views created + database storage

This commit is contained in:
Brice-W
2021-05-12 08:40:10 +10:00
parent d98cef3c77
commit b2a924ebf8
28 changed files with 1484 additions and 1062 deletions

View File

@@ -1,46 +0,0 @@
package org.session.libsession.messaging.messages.control
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.utilities.logging.Log
class OpenGroupInvitation() : ControlMessage() {
var groupUrl: String? = null;
var groupName: String? = null;
companion object {
const val TAG = "OpenGroupInvitation"
fun fromProto(proto: SignalServiceProtos.Content): OpenGroupInvitation? {
val openGroupInvitationProto = if (proto.hasOpenGroupInvitation()) proto.openGroupInvitation else return null
val serverAddress = openGroupInvitationProto.groupUrl
val serverName = openGroupInvitationProto.groupName
return OpenGroupInvitation(serverAddress, serverName)
}
}
constructor(url: String?, serverName: String?): this() {
this.groupUrl = url
this.groupName = serverName
}
override fun isValid(): Boolean {
if (!super.isValid()) return false
return (groupUrl != null && groupName != null)
}
override fun toProto(): SignalServiceProtos.Content? {
val openGroupInvitationProto = SignalServiceProtos.OpenGroupInvitation.newBuilder()
openGroupInvitationProto.groupUrl = groupUrl
openGroupInvitationProto.groupName = groupName
val proto = SignalServiceProtos.Content.newBuilder()
return try {
proto.openGroupInvitation = openGroupInvitationProto.build()
proto.build()
} catch (e: Exception) {
Log.w(TAG, "Couldn't construct open group invitation proto from: $this")
null
}
}
}

View File

@@ -5,11 +5,14 @@ import android.os.Parcelable;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.threads.Address;
import org.session.libsession.messaging.utilities.UpdateMessageData;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceGroup;
import org.session.libsignal.utilities.logging.Log;
public class IncomingTextMessage implements Parcelable {
@@ -40,6 +43,8 @@ public class IncomingTextMessage implements Parcelable {
private final long expiresInMillis;
private final boolean unidentified;
private boolean isOpenGroupInvitation = false;
public IncomingTextMessage(Address sender, int senderDeviceId, long sentTimestampMillis,
String encodedBody, Optional<SignalServiceGroup> group,
long expiresInMillis, boolean unidentified)
@@ -94,6 +99,7 @@ public class IncomingTextMessage implements Parcelable {
this.subscriptionId = base.getSubscriptionId();
this.expiresInMillis = base.getExpiresIn();
this.unidentified = base.isUnidentified();
this.isOpenGroupInvitation= base.isOpenGroupInvitation();
}
public static IncomingTextMessage from(VisibleMessage message,
@@ -104,6 +110,14 @@ public class IncomingTextMessage implements Parcelable {
return new IncomingTextMessage(sender, 1, message.getSentTimestamp(), message.getText(), group, expiresInMillis, false);
}
public static IncomingTextMessage fromOpenGroupInvitation(OpenGroupInvitation openGroupInvitation, Address sender, Long sentTimestamp)
{
String body = UpdateMessageData.Companion.buildOpenGroupInvitation(openGroupInvitation.getGroupUrl(), openGroupInvitation.getGroupName()).toJSON();
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(sender, 1, sentTimestamp, body, Optional.absent(), 0, false);
incomingTextMessage.isOpenGroupInvitation = true;
return incomingTextMessage;
}
public int getSubscriptionId() {
return subscriptionId;
}
@@ -163,6 +177,9 @@ public class IncomingTextMessage implements Parcelable {
public boolean isUnidentified() {
return unidentified;
}
public boolean isOpenGroupInvitation() { return isOpenGroupInvitation; }
@Override
public int describeContents() {
return 0;

View File

@@ -1,7 +1,9 @@
package org.session.libsession.messaging.messages.signal;
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.messaging.utilities.UpdateMessageData;
public class OutgoingTextMessage {
@@ -11,6 +13,8 @@ public class OutgoingTextMessage {
private final long expiresIn;
private final long sentTimestampMillis;
private boolean isOpenGroupInvitation = false;
public OutgoingTextMessage(Recipient recipient, String message, long expiresIn, int subscriptionId, long sentTimestampMillis) {
this.recipient = recipient;
this.message = message;
@@ -23,6 +27,13 @@ public class OutgoingTextMessage {
return new OutgoingTextMessage(recipient, message.getText(), recipient.getExpireMessages() * 1000, -1, message.getSentTimestamp());
}
public static OutgoingTextMessage fromOpenGroupInvitation(OpenGroupInvitation openGroupInvitation, Recipient recipient, Long sentTimestamp) {
String body = UpdateMessageData.Companion.buildOpenGroupInvitation(openGroupInvitation.getGroupUrl(), openGroupInvitation.getGroupName()).toJSON();
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, body, 0, -1, sentTimestamp);
outgoingTextMessage.isOpenGroupInvitation = true;
return outgoingTextMessage;
}
public long getExpiresIn() {
return expiresIn;
}
@@ -46,4 +57,6 @@ public class OutgoingTextMessage {
public boolean isSecureMessage() {
return true;
}
public boolean isOpenGroupInvitation() { return isOpenGroupInvitation; }
}

View File

@@ -0,0 +1,43 @@
package org.session.libsession.messaging.messages.visible
import org.session.libsession.messaging.messages.control.ControlMessage
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.utilities.logging.Log
class OpenGroupInvitation() {
var groupUrl: String? = null;
var groupName: String? = null;
companion object {
const val TAG = "OpenGroupInvitation"
fun fromProto(proto: SignalServiceProtos.DataMessage.OpenGroupInvitation): OpenGroupInvitation? {
val groupUrl = proto.url
val groupName = proto.name
return OpenGroupInvitation(groupUrl, groupName)
}
}
constructor(url: String?, serverName: String?): this() {
this.groupUrl = url
this.groupName = serverName
}
fun isValid(): Boolean {
return (groupUrl != null && groupName != null)
}
fun toProto(): SignalServiceProtos.DataMessage.OpenGroupInvitation? {
val openGroupInvitationProto = SignalServiceProtos.DataMessage.OpenGroupInvitation.newBuilder()
openGroupInvitationProto.url = groupUrl
openGroupInvitationProto.name = groupName
return try {
openGroupInvitationProto.build()
} catch (e: Exception) {
Log.w(TAG, "Couldn't construct open group invitation proto from: $this")
null
}
}
}

View File

@@ -18,6 +18,7 @@ class VisibleMessage : Message() {
var quote: Quote? = null
var linkPreview: LinkPreview? = null
var profile: Profile? = null
var openGroupInvitation: OpenGroupInvitation? = null
override val isSelfSendValid: Boolean = true
@@ -42,6 +43,11 @@ class VisibleMessage : Message() {
val linkPreview = LinkPreview.fromProto(linkPreviewProto)
linkPreview?.let { result.linkPreview = linkPreview }
}
val openGroupInvitationProto = if (dataMessage.hasOpenGroupInvitation()) dataMessage.openGroupInvitation else null
openGroupInvitationProto?.let {
val openGroupInvitation = OpenGroupInvitation.fromProto(openGroupInvitationProto)
openGroupInvitation?.let { result.openGroupInvitation = openGroupInvitation}
}
// TODO Contact
val profile = Profile.fromProto(dataMessage)
profile?.let { result.profile = profile }
@@ -66,6 +72,7 @@ class VisibleMessage : Message() {
if (attachmentIDs.isNotEmpty()) return true
val text = text?.trim() ?: return false
if (text.isNotEmpty()) return true
if (openGroupInvitation != null) return true
return false
}
@@ -94,6 +101,11 @@ class VisibleMessage : Message() {
dataMessage.addAllPreview(listOf(linkPreviewProto))
}
}
//Open group invitation
openGroupInvitation?.let {
val openGroupInvitationProto = it.toProto()
if (openGroupInvitationProto != null) dataMessage.openGroupInvitation = openGroupInvitationProto
}
//Attachments
val attachments = attachmentIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getSignalAttachmentPointer(it) }
if (!attachments.all { !it.url.isNullOrEmpty() }) {

View File

@@ -1,13 +1,13 @@
package org.session.libsession.messaging.sending_receiving
import android.text.TextUtils
import okhttp3.HttpUrl
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.messages.Message
import org.session.libsession.messaging.messages.control.*
import org.session.libsession.messaging.messages.visible.Attachment
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation
import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
@@ -49,7 +49,6 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content,
is ExpirationTimerUpdate -> handleExpirationTimerUpdate(message)
is DataExtractionNotification -> handleDataExtractionNotification(message)
is ConfigurationMessage -> handleConfigurationMessage(message)
is OpenGroupInvitation -> handleOpenGroupInvitation(message)
is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID)
}
}
@@ -150,12 +149,6 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
storage.addContacts(message.contacts)
}
// Open group invitation handling
fun handleOpenGroupInvitation(message: OpenGroupInvitation) {
//TODO
}
//endregion
//region VisibleMessage

View File

@@ -6,9 +6,9 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
import org.session.libsession.utilities.ExpirationUtil
object ClosedGroupUpdateMessageBuilder {
object UpdateMessageBuilder {
fun buildGroupUpdateMessage(context: Context, updateMessageData: ClosedGroupUpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String {
fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String {
var message = ""
val updateData = updateMessageData.kind ?: return message
if (!isOutgoing && sender == null) return message
@@ -17,21 +17,21 @@ object ClosedGroupUpdateMessageBuilder {
} else { context.getString(R.string.MessageRecord_you) }
when (updateData) {
is ClosedGroupUpdateMessageData.Kind.GroupCreation -> {
is UpdateMessageData.Kind.GroupCreation -> {
message = if (isOutgoing) {
context.getString(R.string.MessageRecord_you_created_a_new_group)
} else {
context.getString(R.string.MessageRecord_s_added_you_to_the_group, senderName)
}
}
is ClosedGroupUpdateMessageData.Kind.GroupNameChange -> {
is UpdateMessageData.Kind.GroupNameChange -> {
message = if (isOutgoing) {
context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, updateData.name)
} else {
context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, updateData.name)
}
}
is ClosedGroupUpdateMessageData.Kind.GroupMemberAdded -> {
is UpdateMessageData.Kind.GroupMemberAdded -> {
val members = updateData.updatedMembers.joinToString(", ") {
MessagingModuleConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it
}
@@ -41,7 +41,7 @@ object ClosedGroupUpdateMessageBuilder {
context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members)
}
}
is ClosedGroupUpdateMessageData.Kind.GroupMemberRemoved -> {
is UpdateMessageData.Kind.GroupMemberRemoved -> {
val storage = MessagingModuleConfiguration.shared.storage
val userPublicKey = storage.getUserPublicKey()!!
// 1st case: you are part of the removed members
@@ -63,7 +63,7 @@ object ClosedGroupUpdateMessageBuilder {
}
}
}
is ClosedGroupUpdateMessageData.Kind.GroupMemberLeft -> {
is UpdateMessageData.Kind.GroupMemberLeft -> {
message = if (isOutgoing) {
context.getString(R.string.MessageRecord_left_group)
} else {

View File

@@ -9,7 +9,7 @@ import org.session.libsignal.utilities.logging.Log
import java.util.*
// class used to save update messages details
class ClosedGroupUpdateMessageData () {
class UpdateMessageData () {
var kind: Kind? = null
@@ -20,7 +20,8 @@ class ClosedGroupUpdateMessageData () {
JsonSubTypes.Type(Kind.GroupNameChange::class, name = "GroupNameChange"),
JsonSubTypes.Type(Kind.GroupMemberAdded::class, name = "GroupMemberAdded"),
JsonSubTypes.Type(Kind.GroupMemberRemoved::class, name = "GroupMemberRemoved"),
JsonSubTypes.Type(Kind.GroupMemberLeft::class, name = "GroupMemberLeft")
JsonSubTypes.Type(Kind.GroupMemberLeft::class, name = "GroupMemberLeft"),
JsonSubTypes.Type(Kind.OpenGroupInvitation::class, name = "OpenGroupInvitation")
)
sealed class Kind() {
class GroupCreation(): Kind()
@@ -34,6 +35,9 @@ class ClosedGroupUpdateMessageData () {
constructor(): this(Collections.emptyList())
}
class GroupMemberLeft(): Kind()
class OpenGroupInvitation(val groupUrl: String, val groupName: String): Kind() {
constructor(): this("", "")
}
}
constructor(kind: Kind): this() {
@@ -41,22 +45,26 @@ class ClosedGroupUpdateMessageData () {
}
companion object {
val TAG = ClosedGroupUpdateMessageData::class.simpleName
val TAG = UpdateMessageData::class.simpleName
fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection<String>): ClosedGroupUpdateMessageData? {
fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection<String>): UpdateMessageData? {
return when(type) {
SignalServiceGroup.Type.CREATION -> ClosedGroupUpdateMessageData(Kind.GroupCreation())
SignalServiceGroup.Type.NAME_CHANGE -> ClosedGroupUpdateMessageData(Kind.GroupNameChange(name))
SignalServiceGroup.Type.MEMBER_ADDED -> ClosedGroupUpdateMessageData(Kind.GroupMemberAdded(members))
SignalServiceGroup.Type.MEMBER_REMOVED -> ClosedGroupUpdateMessageData(Kind.GroupMemberRemoved(members))
SignalServiceGroup.Type.QUIT -> ClosedGroupUpdateMessageData(Kind.GroupMemberLeft())
SignalServiceGroup.Type.CREATION -> UpdateMessageData(Kind.GroupCreation())
SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(Kind.GroupNameChange(name))
SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(Kind.GroupMemberAdded(members))
SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(Kind.GroupMemberRemoved(members))
SignalServiceGroup.Type.QUIT -> UpdateMessageData(Kind.GroupMemberLeft())
else -> null
}
}
fun fromJSON(json: String): ClosedGroupUpdateMessageData? {
fun buildOpenGroupInvitation(url: String, name: String): UpdateMessageData {
return UpdateMessageData(Kind.OpenGroupInvitation(url, name))
}
fun fromJSON(json: String): UpdateMessageData? {
return try {
JsonUtil.fromJson(json, ClosedGroupUpdateMessageData::class.java)
JsonUtil.fromJson(json, UpdateMessageData::class.java)
} catch (e: JsonParseException) {
Log.e(TAG, "${e.message}")
null

View File

@@ -1,38 +1,39 @@
package org.session.libsession.utilities
import java.net.MalformedURLException
import java.net.URL
import okhttp3.HttpUrl
object OpenGroupUrlParser {
// Error
sealed class Error(val description: String) : Exception(description) {
class MalformedUrl(message: String?) : Error("Malformed URL: $message.")
class MalformedUrl() : Error("Malformed URL.")
object NoRoomSpecified : Error("No room specified in the URL.")
object NoPublicKeySpecified : Error("No public key specified in the URL.")
object WrongQuery : Error("'public_key' argument is missing.")
object InvalidPublicKeyProvided : Error("Invalid public key provided.")
}
private const val pathPrefix = "/"
private const val queryPrefix = "public_key="
private const val queryPrefix = "public_key"
fun parseUrl(url: String): OpenGroupRoom {
fun parseUrl(stringUrl: String): OpenGroupRoom {
// Url have to start with 'http://'
val url = if (!stringUrl.startsWith("http")) "http://$stringUrl" else stringUrl
// If the URL is malformed, it will throw an exception
val url = try { URL(url) } catch (e: MalformedURLException) { throw Error.MalformedUrl(e.message) }
val httpUrl = HttpUrl.parse(url) ?: throw Error.MalformedUrl()
val host = url.host
val host = httpUrl.host()
// Test if the room is specified in the URL
val room = if (!url.path.isNullOrEmpty()) url.path.removePrefix(pathPrefix) else throw Error.NoRoomSpecified
val room = httpUrl.pathSegments().firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoomSpecified
// Test if the query is specified in the URL
val query = if (!url.query.isNullOrEmpty()) url.query else throw Error.NoPublicKeySpecified
// Test if 'public_key' is specified in the URL
val publicKey = if (query.contains(queryPrefix)) url.query.removePrefix(queryPrefix) else throw Error.WrongQuery
val publicKey = httpUrl.queryParameter(queryPrefix) ?: throw Error.NoPublicKeySpecified
// Public key must be 64 characters
if (publicKey.length != 64) throw Error.InvalidPublicKeyProvided
return OpenGroupRoom(host,room,publicKey)
}
fun trimParameter(stringUrl: String): String {
return stringUrl.substringBefore("?$queryPrefix")
}
}
class OpenGroupRoom(val serverHost: String, val room: String, val serverPublicKey: String) {}
class OpenGroupRoom(val serverHost: String, val room: String, val serverPublicKey: String) { }

View File

@@ -19,6 +19,20 @@ class OpenGroupUrlParserTest {
assertEquals(expectedPublicKey, result.serverPublicKey)
}
@Test
fun parseUrlNoHttpTest() {
val inputUrl = "sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
val expectedHost = "sessionopengroup.co"
val expectedRoom = "main"
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
val result = OpenGroupUrlParser.parseUrl(inputUrl)
assertEquals(expectedHost, result.serverHost)
assertEquals(expectedRoom, result.room)
assertEquals(expectedPublicKey, result.serverPublicKey)
}
@Test
fun parseUrlWithIpTest() {
val inputUrl = "https://143.198.213.255:80/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
@@ -33,9 +47,23 @@ class OpenGroupUrlParserTest {
assertEquals(expectedPublicKey, result.serverPublicKey)
}
@Test
fun parseUrlWithIpAndNoHttpTest() {
val inputUrl = "143.198.213.255/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
val expectedHost = "143.198.213.255"
val expectedRoom = "main"
val expectedPublicKey = "658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
val result = OpenGroupUrlParser.parseUrl(inputUrl)
assertEquals(expectedHost, result.serverHost)
assertEquals(expectedRoom, result.room)
assertEquals(expectedPublicKey, result.serverPublicKey)
}
@Test(expected = OpenGroupUrlParser.Error.MalformedUrl::class)
fun parseUrlMalformedUrlTest() {
val inputUrl = "sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
val inputUrl = "file:sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
OpenGroupUrlParser.parseUrl(inputUrl)
}
@@ -51,12 +79,6 @@ class OpenGroupUrlParserTest {
OpenGroupUrlParser.parseUrl(inputUrl)
}
@Test(expected = OpenGroupUrlParser.Error.WrongQuery::class)
fun parseUrlWrongQueryTest() {
val inputUrl = "https://sessionopengroup.co/main?publickey=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"
OpenGroupUrlParser.parseUrl(inputUrl)
}
@Test(expected = OpenGroupUrlParser.Error.InvalidPublicKeyProvided::class)
fun parseUrlInvalidPublicKeyProviedTest() {
val inputUrl = "https://sessionopengroup.co/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adff"