diff --git a/.idea/runConfigurations/Briar_Desktop.xml b/.idea/runConfigurations/Briar_Desktop.xml index 2a3884ba8a55285a39f5910e0dd8787cce559eef..ddf5232dce5cc4d1aec65ea54a1bcb5eaf130412 100644 --- a/.idea/runConfigurations/Briar_Desktop.xml +++ b/.idea/runConfigurations/Briar_Desktop.xml @@ -1,7 +1,8 @@ <component name="ProjectRunConfigurationManager"> <configuration default="false" name="Briar Desktop" type="JetRunConfigurationType"> - <option name="MAIN_CLASS_NAME" value="org.briarproject.briar.compose.MainKt" /> + <option name="MAIN_CLASS_NAME" value="org.briarproject.briar.desktop.MainKt" /> <module name="briar-desktop.main" /> + <option name="PROGRAM_PARAMETERS" value="--debug" /> <method v="2"> <option name="Make" enabled="true" /> </method> diff --git a/build.gradle.kts b/build.gradle.kts index 04454a24543f9cf83a0a6a1ed887a1699465f07b..7c576af444ffed2603f1d3b905e446384f972d7a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,9 +3,9 @@ import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.4.30" + kotlin("jvm") version "1.5.10" kotlin("kapt") version "1.3.72" - id("org.jetbrains.compose") version "0.3.1" + id("org.jetbrains.compose") version "0.4.0" id("java") id("idea") } @@ -20,13 +20,18 @@ repositories { } dependencies { - testImplementation(kotlin("test-testng")) implementation(compose.desktop.currentOs) + implementation("com.fasterxml.jackson.core:jackson-databind:2.10.0") + implementation("com.github.ajalt:clikt:2.2.0") + implementation(project(path = ":briar:briar-core", configuration = "default")) implementation(project(path = ":briar:bramble-java", configuration = "default")) + val daggerVersion = "2.24" kapt("com.google.dagger:dagger-compiler:$daggerVersion") + + testImplementation(kotlin("test-testng")) } tasks.test { diff --git a/src/main/kotlin/org/briarproject/briar/compose/BriarService.kt b/src/main/kotlin/org/briarproject/briar/compose/BriarService.kt deleted file mode 100644 index 5a017de5a52445b191e110682931e3f9b13dc25f..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/compose/BriarService.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.briarproject.briar.compose - -import org.briarproject.bramble.api.account.AccountManager -import org.briarproject.bramble.api.crypto.DecryptionException -import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator -import org.briarproject.bramble.api.lifecycle.LifecycleManager -import javax.annotation.concurrent.Immutable -import javax.inject.Inject -import javax.inject.Singleton -import javax.swing.JOptionPane - -interface BriarService { - fun start() - fun stop() -} - -@Immutable -@Singleton -internal class BriarServiceImpl -@Inject -constructor( - private val accountManager: AccountManager, - private val lifecycleManager: LifecycleManager, - private val passwordStrengthEstimator: PasswordStrengthEstimator -) : BriarService { - - override fun start() { - if (!accountManager.accountExists()) { - createAccount(); - } else { - while (true) { -// val password = PasswordPrompt.promptForPassword(); -// if (!password.isValid) { -// // this happens when dismissing the dialog or clicking 'cancel' -// exitProcess(1) -// } - val password = "sdifjasdjhfksjadf" - try { - accountManager.signIn(password) - break - } catch (e: DecryptionException) { - JOptionPane.showMessageDialog( - null, "Wrong password", - "Error", JOptionPane.ERROR_MESSAGE - ) - } - } - } - val dbKey = accountManager.databaseKey ?: throw AssertionError() - lifecycleManager.startServices(dbKey) - lifecycleManager.waitForStartup() - } - - override fun stop() { - lifecycleManager.stopServices() - lifecycleManager.waitForShutdown() - } - - private fun createAccount() { -// echo("No account found. Let's create one!\n\n") -// val result = NewAccountPrompt.promptForDetails(); -// if (!result.isValid) { -// echo("Error: Please enter a username and password") -// exitProcess(1) -// } -// try { -// check(passwordStrengthEstimator, result) -// } catch (e: UsageError) { -// return; -// } - - accountManager.createAccount("Nico", "sdifjasdjhfksjadf") - } - -} diff --git a/src/main/kotlin/org/briarproject/briar/compose/main.kt b/src/main/kotlin/org/briarproject/briar/compose/main.kt deleted file mode 100644 index 773691a753453ab72b8a99805e8379e7fc00873a..0000000000000000000000000000000000000000 --- a/src/main/kotlin/org/briarproject/briar/compose/main.kt +++ /dev/null @@ -1,46 +0,0 @@ -package org.briarproject.briar.compose - -import org.briarproject.bramble.BrambleCoreEagerSingletons -import org.briarproject.briar.BriarCoreEagerSingletons -import java.io.File -import java.io.File.separator -import java.io.IOException -import java.lang.System.getProperty -import java.nio.file.Files.setPosixFilePermissions -import java.nio.file.attribute.PosixFilePermission -import java.util.logging.Level -import java.util.logging.LogManager - -fun main() { - LogManager.getLogManager().getLogger("").level = Level.INFO - - val dataDir = getDataDir() - val app = - DaggerBriarSwingApp.builder().swingModule( - SwingModule( - dataDir - ) - ).build() - // We need to load the eager singletons directly after making the - // dependency graphs - BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) - BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) - - app.getUI().startBriar() - app.getUI().startUI() -} - -private fun getDataDir(): File { - val file = File(getProperty("user.home") + separator + ".briar") - if (!file.exists() && !file.mkdirs()) { - throw IOException("Could not create directory: ${file.absolutePath}") - } else if (!file.isDirectory) { - throw IOException("Data dir is not a directory: ${file.absolutePath}") - } - val perms = HashSet<PosixFilePermission>() - perms.add(PosixFilePermission.OWNER_READ) - perms.add(PosixFilePermission.OWNER_WRITE) - perms.add(PosixFilePermission.OWNER_EXECUTE) - setPosixFilePermissions(file.toPath(), perms) - return file -} \ No newline at end of file diff --git a/src/main/kotlin/org/briarproject/briar/compose/BriarSwingApp.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarDesktopApp.kt similarity index 74% rename from src/main/kotlin/org/briarproject/briar/compose/BriarSwingApp.kt rename to src/main/kotlin/org/briarproject/briar/desktop/BriarDesktopApp.kt index 74737afbc7c2dea9efb8d94cf437e3309ae5fa00..dfc7a8889d33c255143f6e7546bb364c05c87a63 100644 --- a/src/main/kotlin/org/briarproject/briar/compose/BriarSwingApp.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarDesktopApp.kt @@ -1,4 +1,4 @@ -package org.briarproject.briar.compose +package org.briarproject.briar.desktop import dagger.Component import org.briarproject.bramble.BrambleCoreEagerSingletons @@ -12,11 +12,11 @@ import javax.inject.Singleton modules = [ BrambleCoreModule::class, BriarCoreModule::class, - SwingModule::class + DesktopModule::class ] ) @Singleton -internal interface BriarSwingApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons { +internal interface BriarDesktopApp : BrambleCoreEagerSingletons, BriarCoreEagerSingletons { fun getUI(): UI diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt new file mode 100644 index 0000000000000000000000000000000000000000..aa277f516012bf86ff48aa06a690f7e91a95dabe --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt @@ -0,0 +1,48 @@ +package org.briarproject.briar.desktop + +import org.briarproject.bramble.api.account.AccountManager +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator +import org.briarproject.bramble.api.lifecycle.LifecycleManager +import org.briarproject.briar.desktop.dialogs.Login +import org.briarproject.briar.desktop.dialogs.Registration +import javax.annotation.concurrent.Immutable +import javax.inject.Inject +import javax.inject.Singleton + +interface BriarService { + fun start() + fun stop() +} + +@Immutable +@Singleton +internal class BriarServiceImpl +@Inject +constructor( + private val accountManager: AccountManager, + private val lifecycleManager: LifecycleManager, + private val passwordStrengthEstimator: PasswordStrengthEstimator +) : BriarService { + + override fun start() { + if (!accountManager.accountExists()) { + createAccount() + } else { + login() + } + } + + override fun stop() { + lifecycleManager.stopServices() + lifecycleManager.waitForShutdown() + } + + private fun createAccount() { + print("No account found. Let's create one!\n\n") + Registration("Briar", accountManager, lifecycleManager) + } + + private fun login() { + Login("Briar", accountManager, lifecycleManager) + } +} diff --git a/src/main/kotlin/org/briarproject/briar/compose/SwingDatabaseConfig.kt b/src/main/kotlin/org/briarproject/briar/desktop/DesktopDatabaseConfig.kt similarity index 71% rename from src/main/kotlin/org/briarproject/briar/compose/SwingDatabaseConfig.kt rename to src/main/kotlin/org/briarproject/briar/desktop/DesktopDatabaseConfig.kt index 34154addc0b93817d4a0663b5eaddb1d668cbe31..8b936f047530c77102c45c9e7440292c603ddc0d 100644 --- a/src/main/kotlin/org/briarproject/briar/compose/SwingDatabaseConfig.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/DesktopDatabaseConfig.kt @@ -1,10 +1,10 @@ -package org.briarproject.briar.compose +package org.briarproject.briar.desktop import org.briarproject.bramble.api.crypto.KeyStrengthener import org.briarproject.bramble.api.db.DatabaseConfig import java.io.File -internal class SwingDatabaseConfig(private val dbDir: File, private val keyDir: File) : +internal class DesktopDatabaseConfig(private val dbDir: File, private val keyDir: File) : DatabaseConfig { override fun getDatabaseDirectory() = dbDir diff --git a/src/main/kotlin/org/briarproject/briar/compose/SwingModule.kt b/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt similarity index 95% rename from src/main/kotlin/org/briarproject/briar/compose/SwingModule.kt rename to src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt index cfa67a1e1293c5d816bab8a3b74a30876c19d876..a196e6e1dd1357abf5ea7f4e63059c803a597ef5 100644 --- a/src/main/kotlin/org/briarproject/briar/compose/SwingModule.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/DesktopModule.kt @@ -1,4 +1,4 @@ -package org.briarproject.briar.compose +package org.briarproject.briar.desktop import com.fasterxml.jackson.databind.ObjectMapper import dagger.Module @@ -41,7 +41,7 @@ import javax.inject.Singleton SocksModule::class ] ) -internal class SwingModule(private val appDir: File) { +internal class DesktopModule(private val appDir: File) { @Provides @Singleton @@ -52,7 +52,7 @@ internal class SwingModule(private val appDir: File) { internal fun provideDatabaseConfig(): DatabaseConfig { val dbDir = File(appDir, "db") val keyDir = File(appDir, "key") - return SwingDatabaseConfig(dbDir, keyDir) + return DesktopDatabaseConfig(dbDir, keyDir) } @Provides diff --git a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt new file mode 100644 index 0000000000000000000000000000000000000000..a7006b69f91abe67c0470fb6e070d1628bd57f16 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt @@ -0,0 +1,80 @@ +package org.briarproject.briar.desktop + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.counted +import com.github.ajalt.clikt.parameters.options.default +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import org.briarproject.bramble.BrambleCoreEagerSingletons +import org.briarproject.briar.BriarCoreEagerSingletons +import java.io.File +import java.io.File.separator +import java.io.IOException +import java.lang.System.getProperty +import java.nio.file.Files.setPosixFilePermissions +import java.nio.file.attribute.PosixFilePermission +import java.nio.file.attribute.PosixFilePermission.* +import java.util.logging.Level.* +import java.util.logging.LogManager + +private val DEFAULT_DATA_DIR = getProperty("user.home") + separator + ".briar" + +private class Main : CliktCommand( + name = "briar-desktop", + help = "Briar Desktop Client" +) { + private val debug by option("--debug", "-d", help = "Enable printing of debug messages").flag( + default = false + ) + private val verbosity by option( + "--verbose", + "-v", + help = "Print verbose log messages" + ).counted() + private val dataDir by option( + "--data-dir", + help = "The directory where Briar will store its files. Default: $DEFAULT_DATA_DIR", + metavar = "PATH", + envvar = "BRIAR_DATA_DIR" + ).default(DEFAULT_DATA_DIR) + + override fun run() { + val level = if (debug) ALL else when (verbosity) { + 0 -> WARNING + 1 -> INFO + else -> ALL + } + + LogManager.getLogManager().getLogger("").level = level + + val dataDir = getDataDir() + val app = + DaggerBriarDesktopApp.builder().desktopModule( + DesktopModule(dataDir) + ).build() + // We need to load the eager singletons directly after making the + // dependency graphs + BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) + BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) + + app.getUI().startBriar() + app.getUI().startUI() + } + + private fun getDataDir(): File { + val file = File(dataDir) + if (!file.exists() && !file.mkdirs()) { + throw IOException("Could not create directory: ${file.absolutePath}") + } else if (!file.isDirectory) { + throw IOException("Data dir is not a directory: ${file.absolutePath}") + } + val perms = HashSet<PosixFilePermission>() + perms.add(OWNER_READ) + perms.add(OWNER_WRITE) + perms.add(OWNER_EXECUTE) + setPosixFilePermissions(file.toPath(), perms) + return file + } +} + +fun main(args: Array<String>) = Main().main(args) \ No newline at end of file diff --git a/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt b/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt new file mode 100644 index 0000000000000000000000000000000000000000..1c4e79540233f55da682bbf752d06511a088ca2a --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt @@ -0,0 +1,37 @@ +package org.briarproject.briar.desktop + +import org.briarproject.bramble.api.account.AccountManager +import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator +import org.briarproject.bramble.api.event.EventBus +import org.briarproject.briar.api.conversation.ConversationManager +import org.briarproject.briar.api.introduction.IntroductionManager +import org.briarproject.briar.api.messaging.MessagingManager +import org.briarproject.briar.api.messaging.PrivateMessageFactory + +class MainUI( + private val briarService: BriarService, + val accountManager: AccountManager, + val contactManager: ContactManager, + private val conversationManager: ConversationManager, + private val messagingManager: MessagingManager, + private val introductionManager: IntroductionManager, + private val privateMessageFactory: PrivateMessageFactory, + private val eventBus: EventBus, + val passwordStrengthEstimator: PasswordStrengthEstimator +) { + + init { + // Should be shown only when logged in +// val title = "Briar Desktop" +// Window (title = title) { +// Column( +// modifier = Modifier.padding(16.dp).fillMaxSize(), +// verticalArrangement = Arrangement.Center, +// horizontalAlignment = Alignment.CenterHorizontally +// ) { +// Text("Welcome to Briar") +// } +// } + } +} diff --git a/src/main/kotlin/org/briarproject/briar/compose/UI.kt b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt similarity index 51% rename from src/main/kotlin/org/briarproject/briar/compose/UI.kt rename to src/main/kotlin/org/briarproject/briar/desktop/UI.kt index 791e128bb66d6b5dc8a1220fde2fd3ed5c9f2cb5..957e2d004a6100a36fb83800b95f5d6705ff9e04 100644 --- a/src/main/kotlin/org/briarproject/briar/compose/UI.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt @@ -1,16 +1,5 @@ -package org.briarproject.briar.compose +package org.briarproject.briar.desktop -import androidx.compose.desktop.Window -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Button -import androidx.compose.material.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.svgResource -import androidx.compose.ui.unit.dp import org.briarproject.bramble.api.account.AccountManager import org.briarproject.bramble.api.contact.ContactManager import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator @@ -41,54 +30,27 @@ constructor( ) { private val logger = getLogger(UI::javaClass.name) -// private val configuration = Configuration() internal fun startBriar() { briarService.start(); } internal fun startUI() { - Window { - Column( - modifier = Modifier.padding(16.dp) - ) { - TheImage() - Spacer(Modifier.height(32.dp)) - TheText() - TheButton() - } - } + MainUI( + briarService, + accountManager, + contactManager, + conversationManager, + messagingManager, + introductionManager, + privateMessageFactory, + eventBus, + passwordStrengthEstimator + ) } internal fun getContactManager(): ContactManager { return contactManager } -} - - -@Composable -private fun TheButton() { - var text by remember { mutableStateOf("Start chatting") } - Button(onClick = { - text = "Sorry, not yet available" - }) { - Text(text) - } -} - -@Composable -private fun TheImage() { - Image( - painter = svgResource("images/logo_circle.svg"), - contentDescription = "Briar logo", - modifier = Modifier - .fillMaxWidth() - .clip(shape = RoundedCornerShape(400.dp)) - ) -} - -@Composable -private fun TheText() { - Text("Welcome to Briar") -} +} \ No newline at end of file diff --git a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt new file mode 100644 index 0000000000000000000000000000000000000000..24c8546caa630a47f9bd27b6d2ad99b49ec6373c --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt @@ -0,0 +1,58 @@ +package org.briarproject.briar.desktop.dialogs + +import androidx.compose.desktop.Window +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.svgResource +import androidx.compose.ui.unit.dp +import org.briarproject.bramble.api.account.AccountManager +import org.briarproject.bramble.api.lifecycle.LifecycleManager + +// TODO: Error handling +fun Login(title: String, accountManager: AccountManager, lifecycleManager: LifecycleManager) = + Window(title = title) { + Column( + modifier = Modifier.padding(16.dp).fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + TheImage() + Spacer(Modifier.height(32.dp)) + TheTextField(accountManager, lifecycleManager) + } + } + +@Composable +private fun TheImage() { + Image( + painter = svgResource("images/logo_circle.svg"), + contentDescription = "Briar logo", + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(400.dp)) + ) +} + +@Composable +private fun TheTextField(accountManager: AccountManager, lifecycleManager: LifecycleManager) { + var password by remember { mutableStateOf("") } + OutlinedTextField(password, { password = it }, label = { Text("Password") }) + Spacer(Modifier.height(16.dp)) + Button(onClick = { + accountManager.signIn(password) + + val dbKey = accountManager.databaseKey ?: throw AssertionError() + lifecycleManager.startServices(dbKey) + lifecycleManager.waitForStartup() + }) { + Text("Login") + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Registration.kt b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Registration.kt new file mode 100644 index 0000000000000000000000000000000000000000..aba87a41f5e28cd8a609a6d413de927c89e0a528 --- /dev/null +++ b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Registration.kt @@ -0,0 +1,59 @@ +package org.briarproject.briar.desktop.dialogs + +import androidx.compose.desktop.Window +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.svgResource +import androidx.compose.ui.unit.dp +import org.briarproject.bramble.api.account.AccountManager +import org.briarproject.bramble.api.lifecycle.LifecycleManager + +// TODO: Error handling and password strength +fun Registration(title: String, accountManager: AccountManager, lifecycleManager: LifecycleManager) = + Window(title = title) { + Column( + modifier = Modifier.padding(16.dp).fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + TheImage() + Spacer(Modifier.height(32.dp)) + TheTextFields(accountManager, lifecycleManager) + } + } + +@Composable +private fun TheImage() { + Image( + painter = svgResource("images/logo_circle.svg"), + contentDescription = "Briar logo", + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(400.dp)) + ) +} + +@Composable +private fun TheTextFields(accountManager: AccountManager, lifecycleManager: LifecycleManager) { + var username by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + OutlinedTextField(username, { username = it }, label = { Text("Username") }) + OutlinedTextField(password, { password = it }, label = { Text("Password") }) + Spacer(Modifier.height(16.dp)) + Button(onClick = { + accountManager.createAccount(username, password) + val dbKey = accountManager.databaseKey ?: throw AssertionError() + lifecycleManager.startServices(dbKey) + lifecycleManager.waitForStartup() + }) { + Text("Register") + } +} \ No newline at end of file