Merge branch 'dev' into just-prefs

This commit is contained in:
bemusementpark
2024-07-18 12:39:49 +09:30
240 changed files with 495468 additions and 297714 deletions

View File

@@ -2,7 +2,7 @@ package org.session.libsession.messaging
data class BlindedIdMapping(
val blindedId: String,
val sessionId: String?,
val accountId: String?,
val serverUrl: String,
val serverId: String
)

View File

@@ -3,8 +3,11 @@ package org.session.libsession.messaging.file_server
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map
import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import org.session.libsession.snode.OnionRequestAPI
import org.session.libsignal.utilities.HTTP
@@ -37,18 +40,18 @@ object FileServerApi {
)
private fun createBody(body: ByteArray?, parameters: Any?): RequestBody? {
if (body != null) return RequestBody.create(MediaType.get("application/octet-stream"), body)
if (body != null) return RequestBody.create("application/octet-stream".toMediaType(), body)
if (parameters == null) return null
val parametersAsJSON = JsonUtil.toJson(parameters)
return RequestBody.create(MediaType.get("application/json"), parametersAsJSON)
return RequestBody.create("application/json".toMediaType(), parametersAsJSON)
}
private fun send(request: Request): Promise<ByteArray, Exception> {
val url = HttpUrl.parse(server) ?: return Promise.ofFail(Error.InvalidURL)
val url = server.toHttpUrlOrNull() ?: return Promise.ofFail(Error.InvalidURL)
val urlBuilder = HttpUrl.Builder()
.scheme(url.scheme())
.host(url.host())
.port(url.port())
.scheme(url.scheme)
.host(url.host)
.port(url.port)
.addPathSegments(request.endpoint)
if (request.verb == HTTP.Verb.GET) {
for ((key, value) in request.queryParameters) {
@@ -57,7 +60,7 @@ object FileServerApi {
}
val requestBuilder = okhttp3.Request.Builder()
.url(urlBuilder.build())
.headers(Headers.of(request.headers))
.headers(request.headers.toHeaders())
when (request.verb) {
HTTP.Verb.GET -> requestBuilder.get()
HTTP.Verb.PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!)

View File

@@ -1,6 +1,7 @@
package org.session.libsession.messaging.jobs
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.database.MessageDataProvider
import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.MessagingModuleConfiguration
@@ -141,8 +142,8 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
DownloadUtilities.downloadFile(tempFile, attachment.url)
} else {
Log.d("AttachmentDownloadJob", "downloading open group attachment")
val url = HttpUrl.parse(attachment.url)!!
val fileID = url.pathSegments().last()
val url = attachment.url.toHttpUrlOrNull()!!
val fileID = url.pathSegments.last()
OpenGroupApi.download(fileID, openGroup.room, openGroup.server).get().let {
tempFile.writeBytes(it)
}

View File

@@ -176,7 +176,7 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
val kryo = Kryo()
kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096)
val output = Output(serializedMessage, Job.MAX_BUFFER_SIZE)
val output = Output(serializedMessage, Job.MAX_BUFFER_SIZE_BYTES)
kryo.writeClassAndObject(output, message)
output.close()
return Data.Builder()

View File

@@ -1,6 +1,7 @@
package org.session.libsession.messaging.jobs
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.messaging.utilities.Data
@@ -21,9 +22,9 @@ class BackgroundGroupAddJob(val joinUrl: String): Job {
override val maxFailureCount: Int = 1
val openGroupId: String? get() {
val url = HttpUrl.parse(joinUrl) ?: return null
val url = joinUrl.toHttpUrlOrNull() ?: return null
val server = OpenGroup.getServer(joinUrl)?.toString()?.removeSuffix("/") ?: return null
val room = url.pathSegments().firstOrNull() ?: return null
val room = url.pathSegments.firstOrNull() ?: return null
return "$server.$room"
}

View File

@@ -1,5 +1,6 @@
package org.session.libsession.messaging.jobs
import java.util.concurrent.atomic.AtomicBoolean
import network.loki.messenger.libsession_util.ConfigBase.Companion.protoKindFor
import nl.komponents.kovenant.functional.bind
import org.session.libsession.messaging.MessagingModuleConfiguration
@@ -10,7 +11,6 @@ import org.session.libsession.messaging.utilities.Data
import org.session.libsession.snode.RawResponse
import org.session.libsession.snode.SnodeAPI
import org.session.libsignal.utilities.Log
import java.util.concurrent.atomic.AtomicBoolean
// only contact (self) and closed group destinations will be supported
data class ConfigurationSyncJob(val destination: Destination): Job {
@@ -180,7 +180,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
// type mappings
const val CONTACT_TYPE = 1
const val GROUP_TYPE = 2
}
class Factory: Job.Factory<ConfigurationSyncJob> {

View File

@@ -14,7 +14,7 @@ interface Job {
// Keys used for database storage
private val ID_KEY = "id"
private val FAILURE_COUNT_KEY = "failure_count"
internal const val MAX_BUFFER_SIZE = 1_000_000 // bytes
internal const val MAX_BUFFER_SIZE_BYTES = 1_000_000 // ~1MB
}
suspend fun execute(dispatcherName: String)

View File

@@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE
import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE_BYTES
import org.session.libsession.messaging.messages.Destination
import org.session.libsession.messaging.messages.Message
import org.session.libsession.messaging.messages.visible.VisibleMessage
@@ -118,12 +118,12 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
val kryo = Kryo()
kryo.isRegistrationRequired = false
// Message
val messageOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE)
val messageOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE_BYTES)
kryo.writeClassAndObject(messageOutput, message)
messageOutput.close()
val serializedMessage = messageOutput.toBytes()
// Destination
val destinationOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE)
val destinationOutput = Output(ByteArray(4096), MAX_BUFFER_SIZE_BYTES)
kryo.writeClassAndObject(destinationOutput, destination)
destinationOutput.close()
val serializedDestination = destinationOutput.toBytes()

View File

@@ -3,10 +3,10 @@ package org.session.libsession.messaging.jobs
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody
import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE
import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE_BYTES
import org.session.libsession.messaging.sending_receiving.notifications.Server
import org.session.libsession.messaging.utilities.Data
import org.session.libsession.snode.OnionRequestAPI
@@ -33,7 +33,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
val server = Server.LEGACY
val parameters = mapOf( "data" to message.data, "send_to" to message.recipient )
val url = "${server.url}/notify"
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters))
val request = Request.Builder().url(url).post(body).build()
retryIfNeeded(4) {
OnionRequestAPI.sendOnionRequest(
@@ -67,7 +67,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
val kryo = Kryo()
kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096)
val output = Output(serializedMessage, MAX_BUFFER_SIZE)
val output = Output(serializedMessage, MAX_BUFFER_SIZE_BYTES)
kryo.writeObject(output, message)
output.close()
return Data.Builder()

View File

@@ -37,7 +37,7 @@ class RetrieveProfileAvatarJob(private val profileAvatar: String?, val recipient
}
override suspend fun execute(dispatcherName: String) {
val delegate = delegate ?: return
val delegate = delegate ?: return Log.w(TAG, "RetrieveProfileAvatarJob has no delegate method to work with!")
if (profileAvatar in errorUrls) return delegate.handleJobFailed(this, dispatcherName, Exception("Profile URL 404'd this app instance"))
val context = MessagingModuleConfiguration.shared.context
val storage = MessagingModuleConfiguration.shared.storage

View File

@@ -30,8 +30,8 @@ sealed class Endpoint(val value: String) {
data class RoomMessagesSince(val roomToken: String, val seqNo: Long) :
Endpoint("room/$roomToken/messages/since/$seqNo")
data class RoomDeleteMessages(val roomToken: String, val sessionId: String) :
Endpoint("room/$roomToken/all/$sessionId")
data class RoomDeleteMessages(val roomToken: String, val accountId: String) :
Endpoint("room/$roomToken/all/$accountId")
data class Reactors(val roomToken: String, val messageId: Long, val emoji: String):
Endpoint("room/$roomToken/reactors/$messageId/$emoji")
@@ -67,15 +67,15 @@ sealed class Endpoint(val value: String) {
object Inbox : Endpoint("inbox")
data class InboxSince(val id: Long) : Endpoint("inbox/since/$id")
data class InboxFor(val sessionId: String) : Endpoint("inbox/$sessionId")
data class InboxFor(val accountId: String) : Endpoint("inbox/$accountId")
object Outbox : Endpoint("outbox")
data class OutboxSince(val id: Long) : Endpoint("outbox/since/$id")
// Users
data class UserBan(val sessionId: String) : Endpoint("user/$sessionId/ban")
data class UserUnban(val sessionId: String) : Endpoint("user/$sessionId/unban")
data class UserModerator(val sessionId: String) : Endpoint("user/$sessionId/moderator")
data class UserBan(val accountId: String) : Endpoint("user/$accountId/ban")
data class UserUnban(val accountId: String) : Endpoint("user/$accountId/unban")
data class UserModerator(val accountId: String) : Endpoint("user/$accountId/moderator")
}

View File

@@ -1,6 +1,7 @@
package org.session.libsession.messaging.open_groups
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.utilities.Log
import java.util.Locale
@@ -47,11 +48,11 @@ data class OpenGroup(
}
fun getServer(urlAsString: String): HttpUrl? {
val url = HttpUrl.parse(urlAsString) ?: return null
val builder = HttpUrl.Builder().scheme(url.scheme()).host(url.host())
if (url.port() != 80 || url.port() != 443) {
val url = urlAsString.toHttpUrlOrNull() ?: return null
val builder = HttpUrl.Builder().scheme(url.scheme).host(url.host)
if (url.port != 80 || url.port != 443) {
// Non-standard port; add to server
builder.port(url.port())
builder.port(url.port)
}
return builder.build()
}

View File

@@ -6,16 +6,14 @@ import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.databind.annotation.JsonNaming
import com.fasterxml.jackson.databind.type.TypeFactory
import com.goterl.lazysodium.LazySodiumAndroid
import com.goterl.lazysodium.SodiumAndroid
import com.goterl.lazysodium.interfaces.GenericHash
import com.goterl.lazysodium.interfaces.Sign
import kotlinx.coroutines.flow.MutableSharedFlow
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.MediaType
import okhttp3.Headers.Companion.toHeaders
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
@@ -282,10 +280,10 @@ object OpenGroupApi {
)
private fun createBody(body: ByteArray?, parameters: Any?): RequestBody? {
if (body != null) return RequestBody.create(MediaType.get("application/octet-stream"), body)
if (body != null) return RequestBody.create("application/octet-stream".toMediaType(), body)
if (parameters == null) return null
val parametersAsJSON = JsonUtil.toJson(parameters)
return RequestBody.create(MediaType.get("application/json"), parametersAsJSON)
return RequestBody.create("application/json".toMediaType(), parametersAsJSON)
}
private fun getResponseBody(request: Request): Promise<ByteArray, Exception> {
@@ -301,7 +299,7 @@ object OpenGroupApi {
}
private fun send(request: Request): Promise<OnionResponse, Exception> {
HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.InvalidURL)
request.server.toHttpUrlOrNull() ?: return Promise.ofFail(Error.InvalidURL)
val urlBuilder = StringBuilder("${request.server}/${request.endpoint.value}")
if (request.verb == GET && request.queryParameters.isNotEmpty()) {
urlBuilder.append("?")
@@ -387,7 +385,7 @@ object OpenGroupApi {
val requestBuilder = okhttp3.Request.Builder()
.url(urlRequest)
.headers(Headers.of(headers))
.headers(headers.toHeaders())
when (request.verb) {
GET -> requestBuilder.get()
PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!)

View File

@@ -36,7 +36,7 @@ data class OpenGroupMessage(
val base64EncodedData = json["data"] as? String ?: return null
val sentTimestamp = json["posted"] as? Double ?: return null
val serverID = json["id"] as? Int
val sender = json["account_id"] as? String
val sender = json["session_id"] as? String
val base64EncodedSignature = json["signature"] as? String
return OpenGroupMessage(
serverID = serverID?.toLong(),

View File

@@ -72,4 +72,4 @@ class SessionServiceAttachmentStream(val inputStream: InputStream?, contentType:
return null
}
}
}
}

View File

@@ -13,9 +13,9 @@ import kotlinx.serialization.Serializable
@Serializable
data class SubscriptionRequest(
/** the 33-byte account being subscribed to; typically a session ID */
/** the 33-byte account being subscribed to; typically an account ID */
val pubkey: String,
/** when the pubkey starts with 05 (i.e. a session ID) this is the ed25519 32-byte pubkey associated with the session ID */
/** when the pubkey starts with 05 (i.e. an account ID) this is the ed25519 32-byte pubkey associated with the account ID */
val session_ed25519: String?,
/** 32-byte swarm authentication subkey; omitted (or null) when not using subkey auth (new closed groups) */
val subkey_tag: String? = null,
@@ -38,9 +38,9 @@ data class SubscriptionRequest(
@Serializable
data class UnsubscriptionRequest(
/** the 33-byte account being subscribed to; typically a session ID */
/** the 33-byte account being subscribed to; typically a account ID */
val pubkey: String,
/** when the pubkey starts with 05 (i.e. a session ID) this is the ed25519 32-byte pubkey associated with the session ID */
/** when the pubkey starts with 05 (i.e. an account ID) this is the ed25519 32-byte pubkey associated with the account ID */
val session_ed25519: String?,
/** 32-byte swarm authentication subkey; omitted (or null) when not using subkey auth (new closed groups) */
val subkey_tag: String? = null,

View File

@@ -3,6 +3,7 @@ package org.session.libsession.messaging.sending_receiving.notifications
import android.annotation.SuppressLint
import nl.komponents.kovenant.Promise
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody
import org.session.libsession.messaging.MessagingModuleConfiguration
@@ -59,7 +60,7 @@ object PushRegistryV1 {
val url = "${server.url}/register_legacy_groups_only"
val body = RequestBody.create(
MediaType.get("application/json"),
"application/json".toMediaType(),
JsonUtil.toJson(parameters)
)
val request = Request.Builder().url(url).post(body).build()
@@ -84,7 +85,7 @@ object PushRegistryV1 {
return retryIfNeeded(maxRetryCount) {
val parameters = mapOf("token" to token)
val url = "${server.url}/unregister"
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters))
val request = Request.Builder().url(url).post(body).build()
sendOnionRequest(request) success {
@@ -121,7 +122,7 @@ object PushRegistryV1 {
): Promise<*, Exception> {
val parameters = mapOf("closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey)
val url = "${server.url}/$operation"
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
val body = RequestBody.create("application/json".toMediaType(), JsonUtil.toJson(parameters))
val request = Request.Builder().url(url).post(body).build()
return retryIfNeeded(maxRetryCount) {

View File

@@ -272,7 +272,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S
serverPublicKey,
true
)
val syncTarget = mapping.sessionId ?: it.recipient
val syncTarget = mapping.accountId ?: it.recipient
if (message is VisibleMessage) {
message.syncTarget = syncTarget
} else if (message is ExpirationTimerUpdate) {

View File

@@ -159,22 +159,22 @@ object SodiumUtilities {
} else null
}
/* This method should be used to check if a users standard sessionId matches a blinded one */
fun sessionId(
/* This method should be used to check if a users standard accountId matches a blinded one */
fun accountId(
standardAccountId: String,
blindedAccountId: String,
serverPublicKey: String
): Boolean {
// Only support generating blinded keys for standard session ids
val sessionId = AccountId(standardAccountId)
if (sessionId.prefix != IdPrefix.STANDARD) return false
// Only support generating blinded keys for standard account ids
val accountId = AccountId(standardAccountId)
if (accountId.prefix != IdPrefix.STANDARD) return false
val blindedId = AccountId(blindedAccountId)
if (blindedId.prefix != IdPrefix.BLINDED) return false
val k = generateBlindingFactor(serverPublicKey) ?: return false
// From the session id (ignoring 05 prefix) we have two possible ed25519 pubkeys;
// From the account id (ignoring 05 prefix) we have two possible ed25519 pubkeys;
// the first is the positive (which is what Signal's XEd25519 conversion always uses)
val xEd25519Key = curve.convertToEd25519PublicKey(Key.fromHexString(sessionId.publicKey).asBytes)
val xEd25519Key = curve.convertToEd25519PublicKey(Key.fromHexString(accountId.publicKey).asBytes)
// Blind the positive public key
val pk1 = combineKeys(k, xEd25519Key) ?: return false

View File

@@ -467,9 +467,9 @@ object OnionRequestAPI {
x25519PublicKey: String,
version: Version = Version.V4
): Promise<OnionResponse, Exception> {
val url = request.url()
val url = request.url
val payload = generatePayload(request, server, version)
val destination = Destination.Server(url.host(), version.value, x25519PublicKey, url.scheme(), url.port())
val destination = Destination.Server(url.host, version.value, x25519PublicKey, url.scheme, url.port)
return sendOnionRequest(destination, payload, version).recover { exception ->
Log.d("Loki", "Couldn't reach server: $url due to error: $exception.")
throw exception
@@ -478,7 +478,7 @@ object OnionRequestAPI {
private fun generatePayload(request: Request, server: String, version: Version): ByteArray {
val headers = request.getHeadersForOnionRequest().toMutableMap()
val url = request.url()
val url = request.url
val urlAsString = url.toString()
val body = request.getBodyForOnionRequest() ?: "null"
val endpoint = when {
@@ -486,19 +486,19 @@ object OnionRequestAPI {
else -> ""
}
return if (version == Version.V4) {
if (request.body() != null &&
if (request.body != null &&
headers.keys.find { it.equals("Content-Type", true) } == null) {
headers["Content-Type"] = "application/json"
}
val requestPayload = mapOf(
"endpoint" to endpoint,
"method" to request.method(),
"method" to request.method,
"headers" to headers
)
val requestData = JsonUtil.toJson(requestPayload).toByteArray()
val prefixData = "l${requestData.size}:".toByteArray(Charsets.US_ASCII)
val suffixData = "e".toByteArray(Charsets.US_ASCII)
if (request.body() != null) {
if (request.body != null) {
val bodyData = if (body is ByteArray) body else body.toString().toByteArray()
val bodyLengthData = "${bodyData.size}:".toByteArray(Charsets.US_ASCII)
prefixData + requestData + bodyLengthData + bodyData + suffixData
@@ -509,7 +509,7 @@ object OnionRequestAPI {
val payload = mapOf(
"body" to body,
"endpoint" to endpoint.removePrefix("/"),
"method" to request.method(),
"method" to request.method,
"headers" to headers
)
JsonUtil.toJson(payload).toByteArray()

View File

@@ -9,13 +9,13 @@ import java.util.Locale
internal fun Request.getHeadersForOnionRequest(): Map<String, Any> {
val result = mutableMapOf<String, Any>()
val contentType = body()?.contentType()
val contentType = body?.contentType()
if (contentType != null) {
result["content-type"] = contentType.toString()
}
val headers = headers()
val headers = headers
for (name in headers.names()) {
val value = headers.get(name)
val value = headers[name]
if (value != null) {
if (value.toLowerCase(Locale.US) == "true" || value.toLowerCase(Locale.US) == "false") {
result[name] = value.toBoolean()
@@ -33,7 +33,7 @@ internal fun Request.getBodyForOnionRequest(): Any? {
try {
val copyOfThis = newBuilder().build()
val buffer = Buffer()
val body = copyOfThis.body() ?: return null
val body = copyOfThis.body ?: return null
body.writeTo(buffer)
val bodyAsData = buffer.readByteArray()
if (body is MultipartBody) {

View File

@@ -1,6 +1,7 @@
package org.session.libsession.utilities
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.file_server.FileServerApi
import org.session.libsignal.utilities.HTTP
import org.session.libsignal.utilities.Log
@@ -36,8 +37,8 @@ object DownloadUtilities {
*/
@JvmStatic
fun downloadFile(outputStream: OutputStream, urlAsString: String) {
val url = HttpUrl.parse(urlAsString)!!
val fileID = url.pathSegments().last()
val url = urlAsString.toHttpUrlOrNull()!!
val fileID = url.pathSegments.last()
try {
FileServerApi.download(fileID).get().let {
outputStream.write(it)

View File

@@ -17,9 +17,9 @@ object GroupUtil {
}
@JvmStatic
fun getEncodedOpenGroupInboxID(openGroup: OpenGroup, sessionId: AccountId): Address {
fun getEncodedOpenGroupInboxID(openGroup: OpenGroup, accountId: AccountId): Address {
val openGroupInboxId =
"${openGroup.server}!${openGroup.publicKey}!${sessionId.hexString}".toByteArray()
"${openGroup.server}!${openGroup.publicKey}!${accountId.hexString}".toByteArray()
return getEncodedOpenGroupInboxID(openGroupInboxId)
}

View File

@@ -1,6 +1,7 @@
package org.session.libsession.utilities
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.open_groups.migrateLegacyServerUrl
object OpenGroupUrlParser {
@@ -19,14 +20,14 @@ object OpenGroupUrlParser {
// URL has to start with 'http://'
val urlWithPrefix = if (!string.startsWith("http")) "http://$string" else string
// If the URL is malformed, throw an exception
val url = HttpUrl.parse(urlWithPrefix) ?: throw Error.MalformedURL
val url = urlWithPrefix.toHttpUrlOrNull() ?: throw Error.MalformedURL
// Parse components
val server = HttpUrl.Builder().scheme(url.scheme()).host(url.host()).port(url.port()).build().toString().removeSuffix(suffix).migrateLegacyServerUrl()
val room = url.pathSegments().firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoom
val server = HttpUrl.Builder().scheme(url.scheme).host(url.host).port(url.port).build().toString().removeSuffix(suffix).migrateLegacyServerUrl()
val room = url.pathSegments.firstOrNull { !it.isNullOrEmpty() } ?: throw Error.NoRoom
val publicKey = url.queryParameter(queryPrefix) ?: throw Error.NoPublicKey
if (publicKey.length != 64) throw Error.InvalidPublicKey
// Return
return V2OpenGroupInfo(server,room,publicKey)
return V2OpenGroupInfo(server, room, publicKey)
}
fun trimQueryParameter(string: String): String {

View File

@@ -0,0 +1,29 @@
package org.session.libsession.utilities
// String substitution keys for use with the Phrase library.
// Note: The substitution will be to {app_name} etc. in the strings - but do NOT include the curly braces in these keys!
object StringSubstitutionConstants {
const val APP_NAME_KEY = "app_name"
const val COMMUNITY_NAME_KEY = "community_name"
const val CONVERSATION_COUNT_KEY = "conversation_count"
const val CONVERSATION_NAME_KEY = "conversation_name"
const val COUNT_KEY = "count"
const val DATE_TIME_KEY = "date_time"
const val DISAPPEARING_MESSAGES_TYPE_KEY = "disappearing_messages_type"
const val DOWNLOAD_URL_KEY = "download_url" // Used to invite people to download Session
const val EMOJI_KEY = "emoji"
const val FILE_TYPE_KEY = "file_type"
const val GROUP_NAME_KEY = "group_name"
const val MEMBERS_KEY = "members"
const val MESSAGE_COUNT_KEY = "message_count"
const val NAME_KEY = "name"
const val OTHER_NAME_KEY = "other_name"
const val QUERY_KEY = "query"
const val SECONDS_KEY = "seconds"
const val TOTAL_COUNT_KEY = "total_count"
const val TIME_KEY = "time"
const val TIME_LARGE_KEY = "time_large"
const val TIME_SMALL_KEY = "time_small"
const val URL_KEY = "url"
const val VERSION_KEY = "version"
}

View File

@@ -9,7 +9,7 @@ import java.util.concurrent.atomic.AtomicReference
* Not really a 'debouncer' but named to be similar to the current Debouncer
* designed to queue tasks on a window (if not already queued) like a timer
*/
class WindowDebouncer(private val window: Long, private val timer: Timer) {
class WindowDebouncer(private val timeWindowMilliseconds: Long, private val timer: Timer) {
private val atomicRef: AtomicReference<Runnable?> = AtomicReference(null)
private val hasStarted = AtomicBoolean(false)
@@ -23,7 +23,7 @@ class WindowDebouncer(private val window: Long, private val timer: Timer) {
fun publish(runnable: Runnable) {
if (hasStarted.compareAndSet(false, true)) {
timer.scheduleAtFixedRate(recursiveRunnable, 0, window)
timer.scheduleAtFixedRate(recursiveRunnable, 0, timeWindowMilliseconds)
}
atomicRef.compareAndSet(null, runnable)
}

View File

@@ -333,11 +333,11 @@ public class Recipient implements RecipientModifiedListener {
} else if (isOpenGroupInboxRecipient()){
String inboxID = GroupUtil.getDecodedOpenGroupInboxAccountId(accountID);
Contact contact = storage.getContactWithAccountID(inboxID);
if (contact == null) { return accountID; }
if (contact == null) return accountID;
return contact.displayName(Contact.ContactContext.REGULAR);
} else {
Contact contact = storage.getContactWithAccountID(accountID);
if (contact == null) { return null; }
if (contact == null) return null;
return contact.displayName(Contact.ContactContext.REGULAR);
}
}
@@ -513,11 +513,11 @@ public class Recipient implements RecipientModifiedListener {
public synchronized String toShortString() {
String name = getName();
if (name != null) return name;
String sessionId = address.serialize();
if (sessionId.length() < 4) return sessionId; // so substrings don't throw out of bounds exceptions
String accountId = address.serialize();
if (accountId.length() < 4) return accountId; // so substrings don't throw out of bounds exceptions
int takeAmount = 4;
String start = sessionId.substring(0, takeAmount);
String end = sessionId.substring(sessionId.length()-takeAmount);
String start = accountId.substring(0, takeAmount);
String end = accountId.substring(accountId.length()-takeAmount);
return start+"..."+end;
}

View File

@@ -53,8 +53,6 @@
<attr name="emoji_tab_strip_background" format="color" />
<attr name="emoji_tab_indicator" format="color" />
<attr name="emoji_tab_underline" format="color" />
<attr name="emoji_tab_seperator" format="color" />
<attr name="emoji_drawer_background" format="color" />
<attr name="emoji_text_color" format="color" />
@@ -93,7 +91,6 @@
<attr name="dialog_info_icon" format="reference" />
<attr name="dialog_alert_icon" format="reference" />
<attr name="dialog_background_color" format="reference|color" />
<attr name="conversation_icon_attach_audio" format="reference"/>
<attr name="conversation_icon_attach_video" format="reference" />

View File

@@ -40,7 +40,6 @@
<color name="gray27">#ffbbbbbb</color>
<color name="gray50">#ff808080</color>
<color name="gray65">#ff595959</color>
<color name="gray70">#ff4d4d4d</color>
<color name="gray78">#ff383838</color>
<color name="transparent_black_30">#30000000</color>

View File

@@ -80,4 +80,10 @@
<string name="clearDevice">Clear Device</string>
<string name="clearDeviceOnly">Clear device only</string>
<string name="clearDeviceAndNetwork">Clear device and network</string>
<string name="profileDisplayPictureRemoveError">Failed to remove display picture.</string>
<string name="profileErrorUpdate">Failed to update profile.</string>
<!-- Added for SS-40 -->
<string name="attachmentsNotification">{emoji} Attachment</string>
</resources>