This commit is contained in:
陈佳 2023-06-29 17:47:02 +08:00 committed by GitHub
commit 663ced292b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 93 additions and 51 deletions

View file

@ -25,6 +25,7 @@ import androidx.core.view.isVisible
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
@ -284,7 +285,7 @@ class VisibleMessageContentView : ConstraintLayout {
// replace URLSpans with ModalURLSpans // replace URLSpans with ModalURLSpans
body.getSpans<URLSpan>(0, body.length).toList().forEach { urlSpan -> body.getSpans<URLSpan>(0, body.length).toList().forEach { urlSpan ->
val updatedUrl = urlSpan.url.let { HttpUrl.parse(it).toString() } val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() }
val replacementSpan = ModalURLSpan(updatedUrl) { url -> val replacementSpan = ModalURLSpan(updatedUrl) { url ->
val activity = context as AppCompatActivity val activity = context as AppCompatActivity
ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog") ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog")

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups
import android.content.Context import android.content.Context
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.GroupMemberRole import org.session.libsession.messaging.open_groups.GroupMemberRole
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
@ -133,9 +134,9 @@ object OpenGroupManager {
} }
fun addOpenGroup(urlAsString: String, context: Context): OpenGroupApi.RoomInfo? { fun addOpenGroup(urlAsString: String, context: Context): OpenGroupApi.RoomInfo? {
val url = HttpUrl.parse(urlAsString) ?: return null val url = urlAsString.toHttpUrlOrNull() ?: return null
val server = OpenGroup.getServer(urlAsString) val server = OpenGroup.getServer(urlAsString)
val room = url.pathSegments().firstOrNull() ?: return null val room = url.pathSegments.firstOrNull() ?: return null
val publicKey = url.queryParameter("public_key") ?: return null val publicKey = url.queryParameter("public_key") ?: return null
return add(server.toString().removeSuffix("/"), room, publicKey, context) // assume migrated from calling function return add(server.toString().removeSuffix("/"), room, publicKey, context) // assume migrated from calling function

View file

@ -4,8 +4,10 @@ import android.content.Context
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
import org.session.libsession.snode.OnionRequestAPI import org.session.libsession.snode.OnionRequestAPI
import org.session.libsession.snode.Version import org.session.libsession.snode.Version
@ -42,7 +44,7 @@ object LokiPushNotificationManager {
fun unregister(token: String, context: Context) { fun unregister(token: String, context: Context) {
val parameters = mapOf( "token" to token ) val parameters = mapOf( "token" to token )
val url = "$server/unregister" val url = "$server/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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
getResponseBody(request.build()).map { json -> getResponseBody(request.build()).map { json ->
@ -71,7 +73,7 @@ object LokiPushNotificationManager {
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
val parameters = mapOf( "token" to token, "pubKey" to publicKey ) val parameters = mapOf( "token" to token, "pubKey" to publicKey )
val url = "$server/register" val url = "$server/register"
val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) val body = JsonUtil.toJson(parameters).toRequestBody("application/json".toMediaType())
val request = Request.Builder().url(url).post(body) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
getResponseBody(request.build()).map { json -> getResponseBody(request.build()).map { json ->
@ -99,7 +101,7 @@ object LokiPushNotificationManager {
if (!TextSecurePreferences.isUsingFCM(context)) { return } if (!TextSecurePreferences.isUsingFCM(context)) { return }
val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey )
val url = "$server/${operation.rawValue}" val url = "$server/${operation.rawValue}"
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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
getResponseBody(request.build()).map { json -> getResponseBody(request.build()).map { json ->

View file

@ -13,7 +13,7 @@ glideVersion=4.11.0
kovenantVersion=3.3.0 kovenantVersion=3.3.0
curve25519Version=0.6.0 curve25519Version=0.6.0
protobufVersion=2.5.0 protobufVersion=2.5.0
okhttpVersion=3.12.1 okhttpVersion=4.11.0
jacksonDatabindVersion=2.9.8 jacksonDatabindVersion=2.9.8
appcompatVersion=1.5.1 appcompatVersion=1.5.1
materialVersion=1.7.0 materialVersion=1.7.0

View file

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

View file

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

View file

@ -1,6 +1,7 @@
package org.session.libsession.messaging.jobs package org.session.libsession.messaging.jobs
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.open_groups.OpenGroupApi
@ -23,9 +24,9 @@ class BackgroundGroupAddJob(val joinUrl: String): Job {
override val maxFailureCount: Int = 1 override val maxFailureCount: Int = 1
val openGroupId: String? get() { 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 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" return "$server.$room"
} }

View file

@ -5,6 +5,7 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody 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
@ -36,7 +37,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
val server = PushNotificationAPI.server val server = PushNotificationAPI.server
val parameters = mapOf( "data" to message.data, "send_to" to message.recipient ) val parameters = mapOf( "data" to message.data, "send_to" to message.recipient )
val url = "${server}/notify" val url = "${server}/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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(4) { retryIfNeeded(4) {
OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.serverPublicKey, Version.V2).map { response -> OnionRequestAPI.sendOnionRequest(request.build(), server, PushNotificationAPI.serverPublicKey, Version.V2).map { response ->

View file

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

View file

@ -14,8 +14,11 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody import okhttp3.RequestBody
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod
@ -283,10 +286,10 @@ object OpenGroupApi {
) )
private fun createBody(body: ByteArray?, parameters: Any?): RequestBody? { 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 if (parameters == null) return null
val parametersAsJSON = JsonUtil.toJson(parameters) 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> { private fun getResponseBody(request: Request): Promise<ByteArray, Exception> {
@ -302,7 +305,7 @@ object OpenGroupApi {
} }
private fun send(request: Request): Promise<OnionResponse, Exception> { 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}") val urlBuilder = StringBuilder("${request.server}/${request.endpoint.value}")
if (request.verb == GET && request.queryParameters.isNotEmpty()) { if (request.verb == GET && request.queryParameters.isNotEmpty()) {
urlBuilder.append("?") urlBuilder.append("?")
@ -389,7 +392,7 @@ object OpenGroupApi {
val requestBuilder = okhttp3.Request.Builder() val requestBuilder = okhttp3.Request.Builder()
.url(urlRequest) .url(urlRequest)
.headers(Headers.of(headers)) .headers(headers.toHeaders())
when (request.verb) { when (request.verb) {
GET -> requestBuilder.get() GET -> requestBuilder.get()
PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!) PUT -> requestBuilder.put(createBody(request.body, request.parameters)!!)

View file

@ -3,6 +3,7 @@ package org.session.libsession.messaging.sending_receiving.notifications
import android.annotation.SuppressLint import android.annotation.SuppressLint
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
@ -36,7 +37,7 @@ object PushNotificationAPI {
fun unregister(token: String) { fun unregister(token: String) {
val parameters = mapOf( "token" to token ) val parameters = mapOf( "token" to token )
val url = "$server/unregister" val url = "$server/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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response -> OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->
@ -64,7 +65,7 @@ object PushNotificationAPI {
if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
val parameters = mapOf( "token" to token, "pubKey" to publicKey ) val parameters = mapOf( "token" to token, "pubKey" to publicKey )
val url = "$server/register" val url = "$server/register"
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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response -> OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->
@ -91,7 +92,7 @@ object PushNotificationAPI {
if (!TextSecurePreferences.isUsingFCM(context)) { return } if (!TextSecurePreferences.isUsingFCM(context)) { return }
val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey )
val url = "$server/${operation.rawValue}" val url = "$server/${operation.rawValue}"
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) val request = Request.Builder().url(url).post(body)
retryIfNeeded(maxRetryCount) { retryIfNeeded(maxRetryCount) {
OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response -> OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response ->

View file

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

View file

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

View file

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

View file

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

View file

@ -20,6 +20,7 @@ dependencies {
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion" implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
implementation "com.github.oxen-io.session-android-curve-25519:curve25519-java:$curve25519Version" implementation "com.github.oxen-io.session-android-curve-25519:curve25519-java:$curve25519Version"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:$okhttpVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"

View file

@ -1,10 +1,14 @@
package org.session.libsignal.utilities package org.session.libsignal.utilities
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.Response import okhttp3.Response
import okhttp3.dnsoverhttps.DnsOverHttps
import java.net.InetAddress
import java.security.SecureRandom import java.security.SecureRandom
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -15,7 +19,13 @@ object HTTP {
var isConnectedToNetwork: (() -> Boolean) = { false } var isConnectedToNetwork: (() -> Boolean) = { false }
private val seedNodeConnection by lazy { private val seedNodeConnection by lazy {
OkHttpClient().newBuilder() val bootstrapClient = OkHttpClient.Builder().build()
val dns =DnsOverHttps.Builder().client(bootstrapClient)
.url("https://dns.quad9.net/dns-query".toHttpUrl())
.bootstrapDnsHosts(InetAddress.getByName("8.8.4.4"), InetAddress.getByName("8.8.8.8"))
.build()
bootstrapClient.newBuilder()
.dns(dns)
.callTimeout(timeout, TimeUnit.SECONDS) .callTimeout(timeout, TimeUnit.SECONDS)
.connectTimeout(timeout, TimeUnit.SECONDS) .connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS) .readTimeout(timeout, TimeUnit.SECONDS)
@ -33,7 +43,13 @@ object HTTP {
} }
val sslContext = SSLContext.getInstance("SSL") val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf( trustManager ), SecureRandom()) sslContext.init(null, arrayOf( trustManager ), SecureRandom())
OkHttpClient().newBuilder() val bootstrapClient = OkHttpClient.Builder().build()
val dns = DnsOverHttps.Builder().client(bootstrapClient)
.url("https://dns.quad9.net/dns-query".toHttpUrl())
.bootstrapDnsHosts(InetAddress.getByName("8.8.4.4"), InetAddress.getByName("8.8.8.8"))
.build()
bootstrapClient.newBuilder()
.dns(dns)
.sslSocketFactory(sslContext.socketFactory, trustManager) .sslSocketFactory(sslContext.socketFactory, trustManager)
.hostnameVerifier { _, _ -> true } .hostnameVerifier { _, _ -> true }
.callTimeout(timeout, TimeUnit.SECONDS) .callTimeout(timeout, TimeUnit.SECONDS)
@ -53,7 +69,13 @@ object HTTP {
} }
val sslContext = SSLContext.getInstance("SSL") val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf( trustManager ), SecureRandom()) sslContext.init(null, arrayOf( trustManager ), SecureRandom())
return OkHttpClient().newBuilder() val bootstrapClient = OkHttpClient.Builder().build()
val dns =DnsOverHttps.Builder().client(bootstrapClient)
.url("https://dns.quad9.net/dns-query".toHttpUrl())
.bootstrapDnsHosts(InetAddress.getByName("8.8.4.4"), InetAddress.getByName("8.8.8.8"))
.build()
return bootstrapClient.newBuilder()
.dns(dns)
.sslSocketFactory(sslContext.socketFactory, trustManager) .sslSocketFactory(sslContext.socketFactory, trustManager)
.hostnameVerifier { _, _ -> true } .hostnameVerifier { _, _ -> true }
.callTimeout(timeout, TimeUnit.SECONDS) .callTimeout(timeout, TimeUnit.SECONDS)
@ -106,7 +128,7 @@ object HTTP {
Verb.GET -> request.get() Verb.GET -> request.get()
Verb.PUT, Verb.POST -> { Verb.PUT, Verb.POST -> {
if (body == null) { throw Exception("Invalid request body.") } if (body == null) { throw Exception("Invalid request body.") }
val contentType = MediaType.get("application/json; charset=utf-8") val contentType = "application/json; charset=utf-8".toMediaType()
@Suppress("NAME_SHADOWING") val body = RequestBody.create(contentType, body) @Suppress("NAME_SHADOWING") val body = RequestBody.create(contentType, body)
if (verb == Verb.PUT) request.put(body) else request.post(body) if (verb == Verb.PUT) request.put(body) else request.post(body)
} }
@ -131,9 +153,9 @@ object HTTP {
// Override the actual error so that we can correctly catch failed requests in OnionRequestAPI // Override the actual error so that we can correctly catch failed requests in OnionRequestAPI
throw HTTPRequestFailedException(0, null, "HTTP request failed due to: ${exception.message}") throw HTTPRequestFailedException(0, null, "HTTP request failed due to: ${exception.message}")
} }
return when (val statusCode = response.code()) { return when (val statusCode = response.code) {
200 -> { 200 -> {
response.body()?.bytes() ?: throw Exception("An error occurred.") response.body?.bytes() ?: throw Exception("An error occurred.")
} }
else -> { else -> {
Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.") Log.d("Loki", "${verb.rawValue} request to $url failed with status code: $statusCode.")