diff --git a/Pods b/Pods index 077f345d2..32ca94b0b 160000 --- a/Pods +++ b/Pods @@ -1 +1 @@ -Subproject commit 077f345d2993bf2a7dab8df935986e6495e764c7 +Subproject commit 32ca94b0b45a1f55059ee500c5e217a0f55a0313 diff --git a/Signal/src/Loki/LokiP2PServer.swift b/Signal/src/Loki/LokiP2PServer.swift index 749ef11ff..ab85ca226 100644 --- a/Signal/src/Loki/LokiP2PServer.swift +++ b/Signal/src/Loki/LokiP2PServer.swift @@ -1,6 +1,13 @@ import GCDWebServer // Convenience functions + +fileprivate extension GCDWebServerResponse { + convenience init(statusCode: E) where E.RawValue == Int { + self.init(statusCode: statusCode.rawValue) + } +} + fileprivate extension GCDWebServerDataRequest { var truncatedContentType: String? { guard let contentType = contentType else { return nil } @@ -27,7 +34,7 @@ fileprivate extension GCDWebServerDataRequest { @objc class LokiP2PServer : NSObject { - private enum StatusCode: Int { + fileprivate enum StatusCode: Int { case ok = 200 case badRequest = 400 case notFound = 404 @@ -40,7 +47,7 @@ fileprivate extension GCDWebServerDataRequest { // Don't allow specific methods let invalidMethodProcessBlock: (GCDWebServerRequest) -> GCDWebServerResponse? = { _ in - return GCDWebServerResponse(statusCode: StatusCode.methodNotAllowed.rawValue) + return GCDWebServerResponse(statusCode: StatusCode.methodNotAllowed) } let invalidMethods = ["GET", "PUT", "DELETE"] @@ -50,27 +57,31 @@ fileprivate extension GCDWebServerDataRequest { // By default send 404 for any path webServer.addDefaultHandler(forMethod: "POST", request: GCDWebServerRequest.self, processBlock: { _ in - return GCDWebServerResponse(statusCode: StatusCode.notFound.rawValue) + return GCDWebServerResponse(statusCode: StatusCode.notFound) }) // Handle our specific storage path - webServer.addHandler(forMethod: "POST", path: "/v1/storage_rpc", request: GCDWebServerDataRequest.self, asyncProcessBlock: { (request, completionBlock) in + webServer.addHandler(forMethod: "POST", path: "/v1/storage_rpc", request: GCDWebServerDataRequest.self, processBlock: { request in // Make sure we were sent a good request guard let dataRequest = request as? GCDWebServerDataRequest, let json = dataRequest.jsonObject else { - completionBlock(GCDWebServerResponse(statusCode: StatusCode.badRequest.rawValue)) - return + return GCDWebServerResponse(statusCode: StatusCode.badRequest) } // Only allow the store method guard let method = json["method"] as? String, method == "store" else { - completionBlock(GCDWebServerResponse(statusCode: StatusCode.notFound.rawValue)) - return + return GCDWebServerResponse(statusCode: StatusCode.notFound) + } + + // Make sure we have the data + guard let params = json["params"] as? [String: String], let data = params["data"] else { + return GCDWebServerResponse(statusCode: StatusCode.badRequest) } - // TODO: Decrypt message here + // Pass it off to the message handler + LokiP2PMessageHandler.shared.handleReceivedMessage(base64EncodedData: data) - let response = GCDWebServerResponse(statusCode: StatusCode.ok.rawValue) - completionBlock(response) + // Send a response back + return GCDWebServerResponse(statusCode: StatusCode.ok.rawValue) }) return webServer diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index bc17df0dc..ce2e45d2e 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -38,6 +38,10 @@ message Envelope { optional string serverGuid = 9; // We may eventually want to make this required. optional uint64 serverTimestamp = 10; + + // Loki: This field is only here as a helper + // It shouldn't be set when sending a message + optional bool isPtpMessage = 999; } message TypingMessage { diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift b/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift index 1c3aa29d9..9d146e264 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI+Message.swift @@ -32,7 +32,7 @@ public extension LokiAPI { return Promise { seal in DispatchQueue.global(qos: .default).async { do { - let wrappedMessage = try wrap(message: signalMessage, timestamp: timestamp) + let wrappedMessage = try LokiMessageWrapper.wrap(message: signalMessage, timestamp: timestamp) let data = wrappedMessage.base64EncodedString() let destination = signalMessage["destination"] as! String let ttl = LokiAPI.defaultMessageTTL diff --git a/SignalServiceKit/src/Loki/API/LokiAPI.swift b/SignalServiceKit/src/Loki/API/LokiAPI.swift index 748fcf45d..2fc3cecb3 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiAPI.swift @@ -57,7 +57,7 @@ import PromiseKit Logger.warn("[Loki] Failed to decode data for message: \(message).") return nil } - guard let envelope = try? unwrap(data: data) else { + guard let envelope = try? LokiMessageWrapper.unwrap(data: data) else { Logger.warn("[Loki] Failed to unwrap data for message: \(message).") return nil } diff --git a/SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift b/SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift similarity index 94% rename from SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift rename to SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift index 2a47b47e2..c8fa1e8a8 100644 --- a/SignalServiceKit/src/Loki/API/LokiAPI+Wrapping.swift +++ b/SignalServiceKit/src/Loki/API/LokiMessageWrapper.swift @@ -1,7 +1,8 @@ -extension LokiAPI { +public class LokiMessageWrapper { + private init() {} - enum WrappingError : LocalizedError { + public enum WrappingError : LocalizedError { case failedToWrapData case failedToWrapMessageInEnvelope case failedToWrapEnvelopeInWebSocketMessage @@ -25,7 +26,7 @@ extension LokiAPI { /// - timestamp: The original message timestamp (`TSOutgoingMessage.timestamp`). /// - Returns: The wrapped message data. /// - Throws: A `WrappingError` if something went wrong. - static func wrap(message: SignalMessage, timestamp: UInt64) throws -> Data { + public static func wrap(message: SignalMessage, timestamp: UInt64) throws -> Data { do { let envelope = try createEnvelope(around: message, timestamp: timestamp) let webSocketMessage = try createWebSocketMessage(around: envelope) @@ -40,7 +41,7 @@ extension LokiAPI { /// - Parameter data: The data from the storage server (not base 64 encoded). /// - Returns: An `SSKProtoEnvelope` object. /// - Throws: A `WrappingError` if something went wrong. - static func unwrap(data: Data) throws -> SSKProtoEnvelope { + public static func unwrap(data: Data) throws -> SSKProtoEnvelope { do { let webSocketMessage = try WebSocketProtoWebSocketMessage.parseData(data) let envelope = webSocketMessage.request!.body! diff --git a/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift b/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift new file mode 100644 index 000000000..788edb2f4 --- /dev/null +++ b/SignalServiceKit/src/Loki/API/LokiP2PMessageHandler.swift @@ -0,0 +1,37 @@ + +public class LokiP2PMessageHandler { + public static let shared = LokiP2PMessageHandler() + + private var messageReceiver: OWSMessageReceiver { + return SSKEnvironment.shared.messageReceiver + } + + private init() {} + + public func handleReceivedMessage(base64EncodedData: String) { + guard let data = Data(base64Encoded: base64EncodedData) else { + Logger.warn("[LokiP2PMessageHandler] Failed to decode p2p message data") + return + } + + guard let envelope = try? LokiMessageWrapper.unwrap(data: data) else { + Logger.warn("[LokiP2PMessageHandler] Failed to unwrap p2p data") + return + } + + // We need to set the p2p field on the envelope + let builder = envelope.asBuilder() + builder.setIsPtpMessage(true) + + // Send it to message receiver + do { + let newEnvelope = try builder.build() + let envelopeData = try newEnvelope.serializedData() + messageReceiver.handleReceivedEnvelopeData(envelopeData) + } catch let error { + Logger.warn("[LokiP2PMessageHandler] Something went wrong while converting proto: \(error)") + owsFailDebug("Failed to build envelope") + } + } + +} diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index e01692b52..5fa154cd9 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -80,6 +80,9 @@ public enum SSKProtoError: Error { if hasServerTimestamp { builder.setServerTimestamp(serverTimestamp) } + if hasIsPtpMessage { + builder.setIsPtpMessage(isPtpMessage) + } return builder } @@ -132,6 +135,10 @@ public enum SSKProtoError: Error { proto.serverTimestamp = valueParam } + @objc public func setIsPtpMessage(_ valueParam: Bool) { + proto.isPtpMessage = valueParam + } + @objc public func build() throws -> SSKProtoEnvelope { return try SSKProtoEnvelope.parseProto(proto) } @@ -211,6 +218,13 @@ public enum SSKProtoError: Error { return proto.hasServerTimestamp } + @objc public var isPtpMessage: Bool { + return proto.isPtpMessage + } + @objc public var hasIsPtpMessage: Bool { + return proto.hasIsPtpMessage + } + private init(proto: SignalServiceProtos_Envelope, type: SSKProtoEnvelopeType, timestamp: UInt64) { diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index faa449417..b5e1f4b26 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -119,6 +119,17 @@ struct SignalServiceProtos_Envelope { /// Clears the value of `serverTimestamp`. Subsequent reads from it will return its default value. mutating func clearServerTimestamp() {self._serverTimestamp = nil} + /// Loki: This field is only here as a helper + /// It shouldn't be set when sending a message + var isPtpMessage: Bool { + get {return _isPtpMessage ?? false} + set {_isPtpMessage = newValue} + } + /// Returns true if `isPtpMessage` has been explicitly set. + var hasIsPtpMessage: Bool {return self._isPtpMessage != nil} + /// Clears the value of `isPtpMessage`. Subsequent reads from it will return its default value. + mutating func clearIsPtpMessage() {self._isPtpMessage = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() enum TypeEnum: SwiftProtobuf.Enum { @@ -175,6 +186,7 @@ struct SignalServiceProtos_Envelope { fileprivate var _content: Data? = nil fileprivate var _serverGuid: String? = nil fileprivate var _serverTimestamp: UInt64? = nil + fileprivate var _isPtpMessage: Bool? = nil } #if swift(>=4.2) @@ -437,6 +449,7 @@ struct SignalServiceProtos_LokiAddressMessage { // methods supported on all messages. /// The naming is a bit different from desktop because of swift auto generation + /// It doesn't like p2p much :( var ptpAddress: String { get {return _ptpAddress ?? String()} set {_ptpAddress = newValue} @@ -2473,6 +2486,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me 8: .same(proto: "content"), 9: .same(proto: "serverGuid"), 10: .same(proto: "serverTimestamp"), + 999: .same(proto: "isPtpMessage"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -2487,6 +2501,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me case 8: try decoder.decodeSingularBytesField(value: &self._content) case 9: try decoder.decodeSingularStringField(value: &self._serverGuid) case 10: try decoder.decodeSingularUInt64Field(value: &self._serverTimestamp) + case 999: try decoder.decodeSingularBoolField(value: &self._isPtpMessage) default: break } } @@ -2520,6 +2535,9 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me if let v = self._serverTimestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 10) } + if let v = self._isPtpMessage { + try visitor.visitSingularBoolField(value: v, fieldNumber: 999) + } try unknownFields.traverse(visitor: &visitor) } @@ -2533,6 +2551,7 @@ extension SignalServiceProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._Me if lhs._content != rhs._content {return false} if lhs._serverGuid != rhs._serverGuid {return false} if lhs._serverTimestamp != rhs._serverTimestamp {return false} + if lhs._isPtpMessage != rhs._isPtpMessage {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true }