mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-11 17:27:42 +00:00
fix closed groups & clean
This commit is contained in:
@@ -1,22 +1,18 @@
|
||||
package org.session.libsession.messaging.fileserver
|
||||
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import okhttp3.Request
|
||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.service.internal.util.Base64
|
||||
import org.session.libsignal.service.internal.util.JsonUtil
|
||||
import org.session.libsignal.service.loki.api.SnodeAPI
|
||||
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
||||
import org.session.libsignal.service.loki.utilities.*
|
||||
import java.net.URL
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.collections.set
|
||||
|
||||
class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() {
|
||||
|
||||
|
@@ -15,7 +15,7 @@ sealed class Destination {
|
||||
if (address.isContact) {
|
||||
return Contact(address.contactIdentifier())
|
||||
} else if (address.isClosedGroup) {
|
||||
val groupID = address.contactIdentifier().toByteArray()
|
||||
val groupID = address.contactIdentifier()
|
||||
val groupPublicKey = GroupUtil.getDecodedGroupID(groupID)
|
||||
return ClosedGroup(groupPublicKey)
|
||||
} else if (address.isOpenGroup) {
|
||||
|
@@ -9,7 +9,7 @@ import org.session.libsession.messaging.MessagingConfiguration
|
||||
|
||||
import org.session.libsession.messaging.utilities.DotNetAPI
|
||||
import org.session.libsession.messaging.fileserver.FileServerAPI
|
||||
import org.session.libsession.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.ThreadUtils
|
||||
import org.session.libsession.utilities.createContext
|
||||
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
|
@@ -1,9 +1,6 @@
|
||||
package org.session.libsession.messaging.sending_receiving
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.annimon.stream.Collectors
|
||||
import com.annimon.stream.Stream
|
||||
import com.annimon.stream.function.Function
|
||||
import org.session.libsession.messaging.MessagingConfiguration
|
||||
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
@@ -22,10 +19,10 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.Hex
|
||||
import org.session.libsession.utilities.SSKEnvironment
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.libsignal.util.guava.Optional
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||
@@ -236,7 +233,7 @@ private fun MessageReceiver.handleNewGroup(message: ClosedGroupUpdate) {
|
||||
MessageSender.requestSenderKey(groupPublicKey, publicKey)
|
||||
}
|
||||
// Create the group
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) //double encoded
|
||||
if (storage.getGroup(groupID) != null) {
|
||||
// Update the group
|
||||
storage.updateTitle(groupID, name)
|
||||
@@ -266,7 +263,7 @@ private fun MessageReceiver.handleGroupUpdate(message: ClosedGroupUpdate) {
|
||||
val members = kind.members.map { it.toHexString() }
|
||||
val admins = kind.admins.map { it.toHexString() }
|
||||
// Get the group
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) //double encoded
|
||||
val group = storage.getGroup(groupID) ?: return Log.d("Loki", "Ignoring closed group info message for nonexistent group.")
|
||||
// Check that the sender is a member of the group (before the update)
|
||||
if (!group.members.contains(Address.fromSerialized(message.sender!!))) { return Log.d("Loki", "Ignoring closed group info message from non-member.") }
|
||||
@@ -326,7 +323,7 @@ private fun MessageReceiver.handleSenderKeyRequest(message: ClosedGroupUpdate) {
|
||||
val sskDatabase = MessagingConfiguration.shared.sskDatabase
|
||||
val userPublicKey = storage.getUserPublicKey()!!
|
||||
val groupPublicKey = kind.groupPublicKey.toHexString()
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) //double encoded
|
||||
val group = storage.getGroup(groupID)
|
||||
if (group == null) {
|
||||
Log.d("Loki", "Ignoring closed group sender key request for nonexistent group.")
|
||||
|
@@ -12,11 +12,9 @@ import org.session.libsession.messaging.sending_receiving.notifications.PushNoti
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
||||
import org.session.libsession.messaging.threads.Address
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment
|
||||
|
||||
import org.session.libsession.utilities.Hex
|
||||
|
||||
import org.session.libsignal.libsignal.ecc.Curve
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey
|
||||
@@ -45,7 +43,7 @@ fun MessageSender.createClosedGroup(name: String, members: Collection<String>):
|
||||
// Create the group
|
||||
val admins = setOf( userPublicKey )
|
||||
val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) //double encoded
|
||||
storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), null, null, LinkedList(admins.map { Address.fromSerialized(it) }))
|
||||
storage.setProfileSharing(Address.fromSerialized(groupID), true)
|
||||
// Send a closed group update message to all members using established channels
|
||||
@@ -79,7 +77,7 @@ fun MessageSender.update(groupPublicKey: String, members: Collection<String>, na
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
val userPublicKey = storage.getUserPublicKey()!!
|
||||
val sskDatabase = MessagingConfiguration.shared.sskDatabase
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) // double encoded
|
||||
val group = storage.getGroup(groupID)
|
||||
if (group == null) {
|
||||
Log.d("Loki", "Can't update nonexistent closed group.")
|
||||
@@ -206,7 +204,7 @@ fun MessageSender.update(groupPublicKey: String, members: Collection<String>, na
|
||||
fun MessageSender.leave(groupPublicKey: String) {
|
||||
val storage = MessagingConfiguration.shared.storage
|
||||
val userPublicKey = storage.getUserPublicKey()!!
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(groupPublicKey)
|
||||
val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) // double encoded
|
||||
val group = storage.getGroup(groupID)
|
||||
if (group == null) {
|
||||
Log.d("Loki", "Can't leave nonexistent closed group.")
|
||||
|
@@ -1,56 +1,11 @@
|
||||
package org.session.libsession.messaging.sending_receiving
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import org.session.libsession.messaging.MessagingConfiguration
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender.Error
|
||||
import org.session.libsession.messaging.utilities.UnidentifiedAccessUtil
|
||||
import org.session.libsession.utilities.AESGCM
|
||||
|
||||
import org.session.libsignal.libsignal.SignalProtocolAddress
|
||||
import org.session.libsignal.libsignal.loki.ClosedGroupCiphertextMessage
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.libsignal.util.guava.Optional
|
||||
import org.session.libsignal.service.api.crypto.SignalServiceCipher
|
||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||
import org.session.libsignal.service.internal.util.Base64
|
||||
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementation
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
|
||||
object MessageSenderEncryption {
|
||||
|
||||
/*internal fun encryptWithSignalProtocol(plaintext: ByteArray, message: Message, recipientPublicKey: String): ByteArray{
|
||||
val storage = MessagingConfiguration.shared.signalStorage
|
||||
val sskDatabase = MessagingConfiguration.shared.sskDatabase
|
||||
val sessionResetImp = MessagingConfiguration.shared.sessionResetImp
|
||||
val localAddress = SignalServiceAddress(recipientPublicKey)
|
||||
val certificateValidator = MessagingConfiguration.shared.certificateValidator
|
||||
val cipher = SignalServiceCipher(localAddress, storage, sskDatabase, sessionResetImp, certificateValidator)
|
||||
val signalProtocolAddress = SignalProtocolAddress(recipientPublicKey, 1)
|
||||
val unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(recipientPublicKey)
|
||||
val unidentifiedAccess = if (unidentifiedAccessPair != null) unidentifiedAccessPair.targetUnidentifiedAccess else Optional.absent()
|
||||
val encryptedMessage = cipher.encrypt(signalProtocolAddress, unidentifiedAccess, plaintext)
|
||||
return Base64.decode(encryptedMessage.content)
|
||||
}*/
|
||||
|
||||
internal fun encryptWithSessionProtocol(plaintext: ByteArray, recipientPublicKey: String): ByteArray{
|
||||
return MessagingConfiguration.shared.sessionProtocol.encrypt(plaintext, recipientPublicKey)
|
||||
}
|
||||
|
||||
/*internal fun encryptWithSharedSenderKeys(plaintext: ByteArray, groupPublicKey: String): ByteArray {
|
||||
// 1. ) Encrypt the data with the user's sender key
|
||||
val userPublicKey = MessagingConfiguration.shared.storage.getUserPublicKey() ?: throw Error.NoUserPublicKey
|
||||
val ciphertextAndKeyIndex = SharedSenderKeysImplementation.shared.encrypt(plaintext, groupPublicKey, userPublicKey)
|
||||
val ivAndCiphertext = ciphertextAndKeyIndex.first
|
||||
val keyIndex = ciphertextAndKeyIndex.second
|
||||
val encryptedMessage = ClosedGroupCiphertextMessage(ivAndCiphertext, Hex.fromStringCondensed(userPublicKey), keyIndex);
|
||||
// 2. ) Encrypt the result for the group's public key to hide the sender public key and key index
|
||||
val intermediate = AESGCM.encrypt(encryptedMessage.serialize(), groupPublicKey.removing05PrefixIfNeeded())
|
||||
// 3. ) Wrap the result
|
||||
return SignalServiceProtos.ClosedGroupCiphertextMessageWrapper.newBuilder()
|
||||
.setCiphertext(ByteString.copyFrom(intermediate.ciphertext))
|
||||
.setEphemeralPublicKey(ByteString.copyFrom(intermediate.ephemeralPublicKey))
|
||||
.build().toByteArray()
|
||||
}*/
|
||||
}
|
@@ -111,7 +111,7 @@ class OpenGroupPoller(private val openGroup: OpenGroup) {
|
||||
return "${rawDisplayName} (${senderPublicKey.takeLast(8)})"
|
||||
}
|
||||
val senderDisplayName = MessagingConfiguration.shared.storage.getOpenGroupDisplayName(senderPublicKey, openGroup.channel, openGroup.server) ?: generateDisplayName("Anonymous")
|
||||
val id = GroupUtil.getEncodedOpenGroupIDAsData(openGroup.id)
|
||||
val id = openGroup.id.toByteArray()
|
||||
// Main message
|
||||
val dataMessageProto = DataMessage.newBuilder()
|
||||
val body = if (message.body == message.timestamp.toString()) { "" } else { message.body }
|
||||
|
@@ -5,9 +5,7 @@ import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.Pair
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import org.session.libsession.utilities.DelimiterUtil.escape
|
||||
import org.session.libsession.utilities.DelimiterUtil.split
|
||||
import org.session.libsession.utilities.DelimiterUtil.unescape
|
||||
import org.session.libsession.utilities.DelimiterUtil
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsignal.libsignal.util.guava.Optional
|
||||
import org.session.libsignal.service.internal.util.Util
|
||||
@@ -163,10 +161,10 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
|
||||
}
|
||||
|
||||
fun fromSerializedList(serialized: String, delimiter: Char): List<Address> {
|
||||
val escapedAddresses = split(serialized, delimiter)
|
||||
val escapedAddresses = DelimiterUtil.split(serialized, delimiter)
|
||||
val addresses: MutableList<Address> = LinkedList()
|
||||
for (escapedAddress in escapedAddresses) {
|
||||
addresses.add(fromSerialized(unescape(escapedAddress, delimiter)))
|
||||
addresses.add(fromSerialized(DelimiterUtil.unescape(escapedAddress, delimiter)))
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
@@ -175,7 +173,7 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
|
||||
Collections.sort(addresses)
|
||||
val escapedAddresses: MutableList<String> = LinkedList()
|
||||
for (address in addresses) {
|
||||
escapedAddresses.add(escape(address.serialize(), delimiter))
|
||||
escapedAddresses.add(DelimiterUtil.escape(address.serialize(), delimiter))
|
||||
}
|
||||
return Util.join(escapedAddresses, delimiter.toString() + "")
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import org.session.libsignal.service.loki.api.*
|
||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||
import org.session.libsignal.service.loki.api.utilities.*
|
||||
import org.session.libsession.utilities.AESGCM.EncryptionResult
|
||||
import org.session.libsession.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.ThreadUtils
|
||||
import org.session.libsession.utilities.getBodyForOnionRequest
|
||||
import org.session.libsession.utilities.getHeadersForOnionRequest
|
||||
import org.session.libsignal.service.loki.utilities.*
|
||||
|
@@ -5,7 +5,7 @@ import nl.komponents.kovenant.deferred
|
||||
import org.session.libsignal.service.internal.util.JsonUtil
|
||||
import org.session.libsession.utilities.AESGCM.EncryptionResult
|
||||
import org.session.libsession.utilities.AESGCM
|
||||
import org.session.libsession.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.toHexString
|
||||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
|
@@ -7,7 +7,7 @@ import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
|
||||
import org.session.libsession.snode.utilities.getRandomElement
|
||||
import org.session.libsession.utilities.ThreadUtils
|
||||
import org.session.libsignal.service.loki.utilities.ThreadUtils
|
||||
import org.session.libsession.utilities.createContext
|
||||
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
|
@@ -2,7 +2,6 @@ package org.session.libsession.utilities
|
||||
|
||||
import org.whispersystems.curve25519.Curve25519
|
||||
import org.session.libsignal.libsignal.util.ByteUtil
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.service.internal.util.Util
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.Mac
|
||||
|
@@ -14,8 +14,8 @@ object DelimiterUtil {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun split(value: String, delimiter: Char): Array<String> {
|
||||
val regex = "(?<!\\\\)" + Pattern.quote(delimiter.toString() + "")
|
||||
return value.split(regex).toTypedArray()
|
||||
fun split(value: String, delimiter: Char): List<String> {
|
||||
val regex = Regex("(?<!\\\\)" + Pattern.quote(delimiter + ""))
|
||||
return value.split(regex)
|
||||
}
|
||||
}
|
@@ -1,15 +1,6 @@
|
||||
package org.session.libsession.utilities
|
||||
|
||||
import android.content.Context
|
||||
import org.session.libsession.messaging.threads.Address.Companion.fromSerialized
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
import org.session.libsignal.libsignal.util.Hex
|
||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
object GroupUtil {
|
||||
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
|
||||
@@ -17,66 +8,44 @@ object GroupUtil {
|
||||
const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"
|
||||
|
||||
@JvmStatic
|
||||
fun getEncodedOpenGroupID(groupID: String): String {
|
||||
return OPEN_GROUP_PREFIX + groupID
|
||||
}
|
||||
|
||||
fun getEncodedOpenGroupIDAsData(groupID: String): ByteArray {
|
||||
return Hex.fromStringCondensed(getEncodedOpenGroupID(groupID))
|
||||
fun getEncodedOpenGroupID(groupID: ByteArray): String {
|
||||
return OPEN_GROUP_PREFIX + Hex.toStringCondensed(groupID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getEncodedClosedGroupID(groupID: ByteArray): String {
|
||||
return getEncodedClosedGroupID(Hex.toStringCondensed(groupID))
|
||||
}
|
||||
|
||||
fun getEncodedClosedGroupID(groupID: String): String {
|
||||
return CLOSED_GROUP_PREFIX + groupID
|
||||
}
|
||||
|
||||
fun getEncodedClosedGroupIDAsData(groupID: String): ByteArray {
|
||||
return Hex.fromStringCondensed(getEncodedClosedGroupID(groupID))
|
||||
return CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getEncodedMMSGroupID(groupID: ByteArray): String {
|
||||
return getEncodedMMSGroupID(Hex.toStringCondensed(groupID))
|
||||
}
|
||||
|
||||
fun getEncodedMMSGroupID(groupID: String): String {
|
||||
return MMS_GROUP_PREFIX + groupID
|
||||
}
|
||||
|
||||
fun getEncodedMMSGroupIDAsData(groupID: String): ByteArray {
|
||||
return (MMS_GROUP_PREFIX + groupID).toByteArray()
|
||||
return MMS_GROUP_PREFIX + Hex.toStringCondensed(groupID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getEncodedId(group: SignalServiceGroup): String {
|
||||
val groupId = group.groupId
|
||||
if (group.groupType == SignalServiceGroup.GroupType.PUBLIC_CHAT) {
|
||||
return getEncodedOpenGroupID(String(groupId))
|
||||
return getEncodedOpenGroupID(groupId)
|
||||
}
|
||||
return getEncodedClosedGroupID(Hex.toStringCondensed(groupId))
|
||||
return getEncodedClosedGroupID(groupId)
|
||||
}
|
||||
|
||||
private fun splitEncodedGroupID(groupID: String): String {
|
||||
if (groupID.split("!").count() > 1) {
|
||||
return groupID.split("!", limit = 2)[1]
|
||||
}
|
||||
return groupID
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDecodedGroupID(groupID: String): String {
|
||||
if (groupID.split("!").count() > 1) {
|
||||
return groupID.split("!")[1]
|
||||
}
|
||||
return groupID.split("!")[0]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDecodedGroupID(groupID: ByteArray): String {
|
||||
val encodedGroupID = Hex.toStringCondensed(groupID)
|
||||
return getDecodedGroupID(encodedGroupID)
|
||||
return String(getDecodedGroupIDAsData(groupID))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDecodedGroupIDAsData(groupID: String): ByteArray {
|
||||
return Hex.fromStringCondensed(getDecodedGroupID(groupID))
|
||||
return Hex.fromStringCondensed(splitEncodedGroupID(groupID))
|
||||
}
|
||||
|
||||
fun isEncodedGroup(groupId: String): Boolean {
|
||||
|
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.session.libsession.utilities;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Utility for generating hex dumps.
|
||||
*/
|
||||
public class Hex {
|
||||
|
||||
private final static int HEX_DIGITS_START = 10;
|
||||
private final static int ASCII_TEXT_START = HEX_DIGITS_START + (16*2 + (16/2));
|
||||
|
||||
final static String EOL = System.getProperty("line.separator");
|
||||
|
||||
private final static char[] HEX_DIGITS = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
|
||||
public static String toString(byte[] bytes) {
|
||||
return toString(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public static String toString(byte[] bytes, int offset, int length) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
appendHexChar(buf, bytes[offset + i]);
|
||||
buf.append(' ');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String toStringCondensed(byte[] bytes) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (int i=0;i<bytes.length;i++) {
|
||||
appendHexChar(buf, bytes[i]);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static byte[] fromStringCondensed(String encoded) throws IOException {
|
||||
final char[] data = encoded.toCharArray();
|
||||
final int len = data.length;
|
||||
|
||||
if ((len & 0x01) != 0) {
|
||||
throw new IOException("Odd number of characters.");
|
||||
}
|
||||
|
||||
final byte[] out = new byte[len >> 1];
|
||||
|
||||
// two characters form the hex value.
|
||||
for (int i = 0, j = 0; j < len; i++) {
|
||||
int f = Character.digit(data[j], 16) << 4;
|
||||
j++;
|
||||
f = f | Character.digit(data[j], 16);
|
||||
j++;
|
||||
out[i] = (byte) (f & 0xFF);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static String dump(byte[] bytes) {
|
||||
return dump(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public static String dump(byte[] bytes, int offset, int length) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
int lines = ((length - 1) / 16) + 1;
|
||||
int lineOffset;
|
||||
int lineLength;
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
lineOffset = (i * 16) + offset;
|
||||
lineLength = Math.min(16, (length - (i * 16)));
|
||||
appendDumpLine(buf, i, bytes, lineOffset, lineLength);
|
||||
buf.append(EOL);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void appendDumpLine(StringBuffer buf, int line, byte[] bytes, int lineOffset, int lineLength) {
|
||||
buf.append(HEX_DIGITS[(line >> 28) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 24) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 20) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 16) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 12) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 8) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line >> 4) & 0xf]);
|
||||
buf.append(HEX_DIGITS[(line ) & 0xf]);
|
||||
buf.append(": ");
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int idx = i + lineOffset;
|
||||
if (i < lineLength) {
|
||||
int b = bytes[idx];
|
||||
appendHexChar(buf, b);
|
||||
} else {
|
||||
buf.append(" ");
|
||||
}
|
||||
if ((i % 2) == 1) {
|
||||
buf.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16 && i < lineLength; i++) {
|
||||
int idx = i + lineOffset;
|
||||
int b = bytes[idx];
|
||||
if (b >= 0x20 && b <= 0x7e) {
|
||||
buf.append((char)b);
|
||||
} else {
|
||||
buf.append('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendHexChar(StringBuffer buf, int b) {
|
||||
buf.append(HEX_DIGITS[(b >> 4) & 0xf]);
|
||||
buf.append(HEX_DIGITS[b & 0xf]);
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@ import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.deferred
|
||||
import nl.komponents.kovenant.jvm.asDispatcher
|
||||
import org.session.libsignal.libsignal.logging.Log
|
||||
import org.session.libsignal.service.loki.utilities.ThreadUtils
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
|
@@ -1,26 +0,0 @@
|
||||
package org.session.libsession.utilities
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
object ThreadUtils {
|
||||
|
||||
internal val executorPool = Executors.newCachedThreadPool()
|
||||
|
||||
@JvmStatic
|
||||
fun queue(target: Runnable) {
|
||||
executorPool.execute(target)
|
||||
}
|
||||
|
||||
fun queue(target: () -> Unit) {
|
||||
executorPool.execute(target)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newDynamicSingleThreadedExecutor(): ExecutorService {
|
||||
val executor = ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
|
||||
LinkedBlockingQueue())
|
||||
executor.allowCoreThreadTimeOut(true)
|
||||
return executor
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user