diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt index daa027576b89380992fd07412c4514d84387ca6f..089f04e6a4d1931eba92dc4d6339c9a6f1c4fc5c 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt @@ -39,6 +39,7 @@ enum class Screen { interface BriarService { @Composable fun start( + contactManager: ContactManager, conversationManager: ConversationManager, messagingManager: MessagingManager ) @@ -46,7 +47,8 @@ interface BriarService { fun stop() } -val CM = compositionLocalOf<ConversationManager> { error("Undefined ConversationManager") } +val CVM = compositionLocalOf<ConversationManager> { error("Undefined ConversationManager") } +val CTM = compositionLocalOf<ContactManager> { error("Undefined ContactManager") } val MM = compositionLocalOf<MessagingManager> { error("Undefined MessagingManager") } @Immutable @@ -70,6 +72,7 @@ constructor( @Composable override fun start( + contactManager: ContactManager, conversationManager: ConversationManager, messagingManager: MessagingManager ) { @@ -115,7 +118,8 @@ constructor( else -> CompositionLocalProvider( - CM provides conversationManager, + CVM provides conversationManager, + CTM provides contactManager, MM provides messagingManager ) { BriarUIStateManager(contacts) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt index a95c4f856a5f7f615639f518a3b69a364dbd8d02..871be40b97446240de7204fa2080f84e228bdb91 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt @@ -34,10 +34,10 @@ constructor( @Composable internal fun startBriar() { - briarService.start(conversationManager, messagingManager) - } - - internal fun getContactManager(): ContactManager { - return contactManager + briarService.start( + contactManager, + conversationManager, + messagingManager + ) } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt index 3d1c8a84d28cf1918e83d30d731ec990dfc1caaf..0810db51b33b2bfd9c2c7cc6d59f43dbeae4e9b0 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/paul/views/PrivateMessageView.kt @@ -74,10 +74,17 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import org.briarproject.bramble.api.FormatException import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.contact.ContactId +import org.briarproject.bramble.api.contact.ContactManager +import org.briarproject.bramble.api.db.ContactExistsException +import org.briarproject.bramble.api.db.PendingContactExistsException +import org.briarproject.bramble.api.identity.AuthorConstants +import org.briarproject.bramble.util.StringUtils import org.briarproject.briar.api.conversation.ConversationMessageHeader -import org.briarproject.briar.desktop.CM +import org.briarproject.briar.desktop.CTM +import org.briarproject.briar.desktop.CVM import org.briarproject.briar.desktop.MM import org.briarproject.briar.desktop.chat.Chat import org.briarproject.briar.desktop.chat.ChatHistoryConversationVisitor @@ -94,6 +101,7 @@ import org.briarproject.briar.desktop.paul.theme.briarGreen import org.briarproject.briar.desktop.paul.theme.darkGray import org.briarproject.briar.desktop.paul.theme.divider import org.briarproject.briar.desktop.paul.theme.lightGray +import java.security.GeneralSecurityException import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId @@ -142,12 +150,16 @@ fun PrivateMessageView( @OptIn(ExperimentalMaterialApi::class) @Composable -fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { +fun AddContactDialog(isVisible: Boolean, setDialogVisibility: (Boolean) -> Unit) { if (!isVisible) { return } + var contactAlias by remember { mutableStateOf("") } + var contactLink by remember { mutableStateOf("") } + val contactManager = CTM.current + val ownLink = CTM.current.handshakeLink AlertDialog( - onDismissRequest = { onCancel(false) }, + onDismissRequest = { setDialogVisibility(false) }, text = { Column(modifier = Modifier.fillMaxWidth()) { Row(Modifier.fillMaxWidth().padding(vertical = 16.dp)) { @@ -164,7 +176,7 @@ fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { Modifier.width(128.dp).align(Alignment.CenterVertically), color = lightGray ) - TextField("", onValueChange = {}, modifier = Modifier.fillMaxWidth()) + TextField(contactLink, onValueChange = { contactLink = it }, modifier = Modifier.fillMaxWidth()) } Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text( @@ -172,7 +184,7 @@ fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { Modifier.width(128.dp).align(Alignment.CenterVertically), color = lightGray ) - TextField("", onValueChange = {}, modifier = Modifier.fillMaxWidth()) + TextField(contactAlias, onValueChange = { contactAlias = it }, modifier = Modifier.fillMaxWidth()) } Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Text( @@ -181,8 +193,7 @@ fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { color = lightGray ) TextField( - // TODO: use real code - "briar://ksdjlfgakslhjgaklsjdhglkasjdlk3j12h4lk2j3tkj4", + ownLink, onValueChange = {}, modifier = Modifier.fillMaxWidth() ) @@ -191,14 +202,23 @@ fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { }, confirmButton = { TextButton( - onClick = { onCancel(false) }, modifier = Modifier.background(briarGreen) + onClick = { + if (ownLink.equals(contactLink)) { + println("Please enter contact's link, not your own") + setDialogVisibility(false) + return@TextButton + } + addPendingContact(contactManager, contactAlias, contactLink) + setDialogVisibility(false) + }, + modifier = Modifier.background(briarGreen) ) { Text("Add") } }, dismissButton = { TextButton( - onClick = { onCancel(false) }, modifier = Modifier.background(briarBlack) + onClick = { setDialogVisibility(false) }, modifier = Modifier.background(briarBlack) ) { Text("Cancel") } @@ -210,6 +230,41 @@ fun AddContactDialog(isVisible: Boolean, onCancel: (Boolean) -> Unit) { ) } +private fun addPendingContact(contactManager: ContactManager, alias: String, link: String) { + if (aliasIsInvalid(alias)) { + println("Alias is invalid") + return + } + try { + contactManager.addPendingContact(link, alias) + } catch (e: FormatException) { + println("Link is invalid") + println(e.stackTrace) + } catch (e: GeneralSecurityException) { + println("Public key is invalid") + println(e.stackTrace) + } + /* + TODO: Warn user that the following two errors might be an attack + + Use `e.pendingContact.id.bytes` and `e.pendingContact.alias` to implement the following logic: + https://code.briarproject.org/briar/briar-gtk/-/merge_requests/97 + + */ + catch (e: ContactExistsException) { + println("Contact already exists") + println(e.stackTrace) + } catch (e: PendingContactExistsException) { + println("Pending Contact already exists") + println(e.stackTrace) + } +} + +private fun aliasIsInvalid(alias: String): Boolean { + val aliasUtf8 = StringUtils.toUtf8(alias) + return aliasUtf8.isEmpty() || aliasUtf8.size > AuthorConstants.MAX_AUTHOR_NAME_LENGTH +} + @Composable fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onContactAdd: (Boolean) -> Unit) { TextField( @@ -279,7 +334,7 @@ fun ContactCard( color = Color.White, modifier = Modifier.align(Alignment.Start).padding(bottom = 2.dp) ) - val latestMsgTime = CM.current.getGroupCount(contact.id).latestMsgTime + val latestMsgTime = CVM.current.getGroupCount(contact.id).latestMsgTime Text( getFormattedTimestamp(latestMsgTime), fontSize = 10.sp, @@ -720,7 +775,7 @@ fun DrawMessageRow( fun ChatState(id: ContactId): MutableState<UiState<Chat>> { val state: MutableState<UiState<Chat>> = remember { mutableStateOf(UiState.Loading) } val messagingManager = MM.current - val conversationManager = CM.current + val conversationManager = CVM.current DisposableEffect(id) { state.value = UiState.Loading