diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 39482857e..532179f5a 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -283,8 +283,10 @@ A1C32D5017A06538000A904E /* AddressBookUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4F17A06537000A904E /* AddressBookUI.framework */; }; A1C32D5117A06544000A904E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4D17A0652C000A904E /* AddressBook.framework */; }; AA0C8E498E2046B0B81EEE6E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313AE91B4954215858A5662 /* libPods.a */; }; + B6019E971A2492AB001118DF /* NSDate+millisecondTimeStamp.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6019E961A2492AB001118DF /* NSDate+millisecondTimeStamp.mm */; }; B60C16651988999D00E97A6C /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = B60C16641988999D00E97A6C /* VersionMigrations.m */; }; B60EDE041A05A01700D73516 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B60EDE031A05A01700D73516 /* AudioToolbox.framework */; }; + B62D53F71A23CCAD009AAF82 /* TSMessageAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = B62D53F61A23CCAD009AAF82 /* TSMessageAdapter.m */; }; B633C5801A1D190B0059AC12 /* archive@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B633C4FE1A1D190B0059AC12 /* archive@2x.png */; }; B633C5831A1D190B0059AC12 /* backspace.png in Resources */ = {isa = PBXBuildFile; fileRef = B633C5011A1D190B0059AC12 /* backspace.png */; }; B633C5841A1D190B0059AC12 /* backspace@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B633C5021A1D190B0059AC12 /* backspace@2x.png */; }; @@ -370,7 +372,7 @@ B6B0966E1A1D25ED008BFAA6 /* TSGroupMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960A1A1D25ED008BFAA6 /* TSGroupMessageManager.m */; }; B6B0966F1A1D25ED008BFAA6 /* TSIncomingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960C1A1D25ED008BFAA6 /* TSIncomingMessage.m */; }; B6B096701A1D25ED008BFAA6 /* TSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960E1A1D25ED008BFAA6 /* TSInfoMessage.m */; }; - B6B096711A1D25ED008BFAA6 /* TSInteraction.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6B096101A1D25ED008BFAA6 /* TSInteraction.mm */; }; + B6B096711A1D25ED008BFAA6 /* TSInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096101A1D25ED008BFAA6 /* TSInteraction.m */; }; B6B096721A1D25ED008BFAA6 /* TSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096121A1D25ED008BFAA6 /* TSMessage.m */; }; B6B096731A1D25ED008BFAA6 /* TSMessagesManager+sendMessages.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096141A1D25ED008BFAA6 /* TSMessagesManager+sendMessages.m */; }; B6B096741A1D25ED008BFAA6 /* TSMessagesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096161A1D25ED008BFAA6 /* TSMessagesManager.m */; }; @@ -853,9 +855,13 @@ A1C32D4D17A0652C000A904E /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; A1C32D4F17A06537000A904E /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; A1FDCBEE16DAA6C300868894 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + B6019E951A2492AB001118DF /* NSDate+millisecondTimeStamp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+millisecondTimeStamp.h"; sourceTree = ""; }; + B6019E961A2492AB001118DF /* NSDate+millisecondTimeStamp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+millisecondTimeStamp.mm"; sourceTree = ""; }; B60C16631988999D00E97A6C /* VersionMigrations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VersionMigrations.h; sourceTree = ""; }; B60C16641988999D00E97A6C /* VersionMigrations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VersionMigrations.m; sourceTree = ""; }; B60EDE031A05A01700D73516 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + B62D53F51A23CCAD009AAF82 /* TSMessageAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSMessageAdapter.h; sourceTree = ""; }; + B62D53F61A23CCAD009AAF82 /* TSMessageAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageAdapter.m; sourceTree = ""; }; B633C4FE1A1D190B0059AC12 /* archive@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "archive@2x.png"; sourceTree = ""; }; B633C5011A1D190B0059AC12 /* backspace.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = backspace.png; sourceTree = ""; }; B633C5021A1D190B0059AC12 /* backspace@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backspace@2x.png"; sourceTree = ""; }; @@ -1020,7 +1026,7 @@ B6B0960D1A1D25ED008BFAA6 /* TSInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInfoMessage.h; sourceTree = ""; }; B6B0960E1A1D25ED008BFAA6 /* TSInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInfoMessage.m; sourceTree = ""; }; B6B0960F1A1D25ED008BFAA6 /* TSInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInteraction.h; sourceTree = ""; }; - B6B096101A1D25ED008BFAA6 /* TSInteraction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TSInteraction.mm; sourceTree = ""; }; + B6B096101A1D25ED008BFAA6 /* TSInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSInteraction.m; sourceTree = ""; }; B6B096111A1D25ED008BFAA6 /* TSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSMessage.h; sourceTree = ""; }; B6B096121A1D25ED008BFAA6 /* TSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessage.m; sourceTree = ""; }; B6B096131A1D25ED008BFAA6 /* TSMessagesManager+sendMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TSMessagesManager+sendMessages.h"; sourceTree = ""; }; @@ -2090,6 +2096,15 @@ path = util; sourceTree = ""; }; + B62D53F41A23CC8B009AAF82 /* TSMessageAdapters */ = { + isa = PBXGroup; + children = ( + B62D53F51A23CCAD009AAF82 /* TSMessageAdapter.h */, + B62D53F61A23CCAD009AAF82 /* TSMessageAdapter.m */, + ); + name = TSMessageAdapters; + sourceTree = ""; + }; B633C4FD1A1D190B0059AC12 /* Images */ = { isa = PBXGroup; children = ( @@ -2650,7 +2665,7 @@ B6B0960D1A1D25ED008BFAA6 /* TSInfoMessage.h */, B6B0960E1A1D25ED008BFAA6 /* TSInfoMessage.m */, B6B0960F1A1D25ED008BFAA6 /* TSInteraction.h */, - B6B096101A1D25ED008BFAA6 /* TSInteraction.mm */, + B6B096101A1D25ED008BFAA6 /* TSInteraction.m */, B6B096111A1D25ED008BFAA6 /* TSMessage.h */, B6B096121A1D25ED008BFAA6 /* TSMessage.m */, B6B096131A1D25ED008BFAA6 /* TSMessagesManager+sendMessages.h */, @@ -2723,6 +2738,8 @@ B6B0965E1A1D25ED008BFAA6 /* NSString+escape.m */, B6B0965F1A1D25ED008BFAA6 /* NSURLSessionDataTask+StatusCode.h */, B6B096601A1D25ED008BFAA6 /* NSURLSessionDataTask+StatusCode.m */, + B6019E951A2492AB001118DF /* NSDate+millisecondTimeStamp.h */, + B6019E961A2492AB001118DF /* NSDate+millisecondTimeStamp.mm */, ); path = Util; sourceTree = ""; @@ -2971,6 +2988,7 @@ FC3196321A08142D0094C78E /* Signals */ = { isa = PBXGroup; children = ( + B62D53F41A23CC8B009AAF82 /* TSMessageAdapters */, FC3196281A067D8F0094C78E /* MessageComposeTableViewController.h */, FC3196291A067D8F0094C78E /* MessageComposeTableViewController.m */, FCAC963A19FEF9280046DFC5 /* SignalsViewController.h */, @@ -3445,6 +3463,7 @@ FC4FA0331A1D46AE00DA100A /* InitialViewController.m in Sources */, B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */, 76EB05D618170B33006006FC /* ZrtpResponder.m in Sources */, + B62D53F71A23CCAD009AAF82 /* TSMessageAdapter.m in Sources */, B63AF5C91A1F757900D01AAD /* TSRecipientPrekeyRequest.m in Sources */, 7095B7B018F46D35002C66E2 /* PhoneNumberUtil.m in Sources */, B63AF5D81A1F889500D01AAD /* SubProtocol.pb.m in Sources */, @@ -3513,6 +3532,7 @@ 7038632818F70C0700D4A43F /* EvpSymetricUtil.m in Sources */, 76EB068618170B34006006FC /* ContactTableViewCell.m in Sources */, B6B096921A1D25ED008BFAA6 /* NSData+hexString.m in Sources */, + B6019E971A2492AB001118DF /* NSDate+millisecondTimeStamp.mm in Sources */, B63761ED19E1FBE8005735D1 /* HttpRequestOrResponse.m in Sources */, B63AF5C81A1F757900D01AAD /* TSDeregisterAccountRequest.m in Sources */, 76EB05A018170B33006006FC /* IpAddress.m in Sources */, @@ -3619,7 +3639,7 @@ 76EB05E818170B33006006FC /* CallFailedServerMessage.m in Sources */, 76EB05FA18170B33006006FC /* CallConnectUtil_Responder.m in Sources */, 76EB05AE18170B33006006FC /* SrtpStream.m in Sources */, - B6B096711A1D25ED008BFAA6 /* TSInteraction.mm in Sources */, + B6B096711A1D25ED008BFAA6 /* TSInteraction.m in Sources */, B6B0966D1A1D25ED008BFAA6 /* TSErrorMessage.m in Sources */, B63AF5CF1A1F757900D01AAD /* TSRequestAttachmentId.m in Sources */, E197B61318BBEC1A00F073E5 /* DesiredBufferDepthController.m in Sources */, diff --git a/Signal/src/contact/ContactsManager.m b/Signal/src/contact/ContactsManager.m index 7d15fecb4..722ab768b 100644 --- a/Signal/src/contact/ContactsManager.m +++ b/Signal/src/contact/ContactsManager.m @@ -55,7 +55,7 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*); } -(void) updatedDirectoryHandler:(NSNotification*) notification { - NSArray *currentUsers = [self getRedPhoneUsersFromContactsArray:latestContactsById.allValues]; + NSArray *currentUsers = [self getSignalUsersFromContactsArray:latestContactsById.allValues]; [observableRedPhoneUsersController updateValue:currentUsers]; } @@ -380,9 +380,9 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in #pragma mark - Whisper User Management --(NSArray*) getRedPhoneUsersFromContactsArray:(NSArray*) contacts { +-(NSArray*) getSignalUsersFromContactsArray:(NSArray*)contacts { return [contacts filter:^int(Contact* contact) { - return [self isContactRegisteredWithRedPhone:contact]; + return [self isContactRegisteredWithRedPhone:contact] || contact.isTextSecureContact; }]; } @@ -420,7 +420,7 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in return NO; } -- (BOOL)isPhoneNumberRegisteredWithRedPhone:(PhoneNumber *)phoneNumber { +- (BOOL)isPhoneNumberRegisteredWithRedPhone:(PhoneNumber*)phoneNumber { PhoneNumberDirectoryFilter* directory = Environment.getCurrent.phoneDirectoryManager.getCurrentFilter; return phoneNumber != nil && [directory containsPhoneNumber:phoneNumber]; } diff --git a/Signal/src/network/http/RPAPICall.m b/Signal/src/network/http/RPAPICall.m index b5097a88b..e16546617 100644 --- a/Signal/src/network/http/RPAPICall.m +++ b/Signal/src/network/http/RPAPICall.m @@ -39,13 +39,13 @@ [SignalKeyingStorage generateServerAuthPassword]; RPAPICall *apiCall = [self defaultAPICall]; apiCall.method = HTTP_GET; - apiCall.endPoint = @"/users/verification"; + apiCall.endPoint = @"/users/verification/sms?client=ios"; return apiCall; } + (RPAPICall*)requestVerificationCodeWithVoice { RPAPICall *apiCall = [self requestVerificationCode]; - apiCall.endPoint = [apiCall.endPoint stringByAppendingString:@"/voice"]; + apiCall.endPoint = @"/users/verification/voice?client=ios"; return apiCall; } diff --git a/Signal/src/textsecure/Contacts/Threads/TSContactThread.h b/Signal/src/textsecure/Contacts/Threads/TSContactThread.h index 8a62249ef..d448797c7 100644 --- a/Signal/src/textsecure/Contacts/Threads/TSContactThread.h +++ b/Signal/src/textsecure/Contacts/Threads/TSContactThread.h @@ -15,6 +15,7 @@ + (instancetype)threadWithContactId:(NSString*)contactId transaction:(YapDatabaseReadWriteTransaction*)transaction; -- (TSRecipient*)recipient; +- (NSString*)contactIdentifier; +- (TSRecipient *)recipientWithTransaction:(YapDatabaseReadTransaction*)transaction; @end diff --git a/Signal/src/textsecure/Contacts/Threads/TSContactThread.m b/Signal/src/textsecure/Contacts/Threads/TSContactThread.m index 65f693475..cc10f5b6c 100644 --- a/Signal/src/textsecure/Contacts/Threads/TSContactThread.m +++ b/Signal/src/textsecure/Contacts/Threads/TSContactThread.m @@ -11,6 +11,7 @@ #import "Environment.h" #import "TSStorageManager.h" #import "ContactsManager.h" +#import "TSRecipient.h" #define TSContactThreadPrefix @"c" @@ -37,7 +38,7 @@ return thread; } -- (NSString *)contactIdentifier{ +- (NSString*)contactIdentifier{ return [[self class]contactIdFromThreadId:self.uniqueId]; } @@ -64,4 +65,8 @@ return [threadId substringWithRange:NSMakeRange(1, threadId.length-1)]; } +- (TSRecipient *)recipientWithTransaction:(YapDatabaseReadTransaction*)transaction{ + return [TSRecipient recipientWithTextSecureIdentifier:self.contactIdentifier withTransaction:transaction]; +} + @end diff --git a/Signal/src/textsecure/Messages/TSErrorMessage.m b/Signal/src/textsecure/Messages/TSErrorMessage.m index 1715819de..3d01b7b19 100644 --- a/Signal/src/textsecure/Messages/TSErrorMessage.m +++ b/Signal/src/textsecure/Messages/TSErrorMessage.m @@ -10,4 +10,14 @@ @implementation TSErrorMessage +- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread failedMessageType:(TSErrorMessageType)errorMessageType{ + self = [super initWithTimestamp:timestamp inThread:thread messageBody:@"Error Message" attachements:nil]; + + if (self) { + _errorType = errorMessageType; + } + + return self; +} + @end diff --git a/Signal/src/textsecure/Messages/TSInteraction.mm b/Signal/src/textsecure/Messages/TSInteraction.m similarity index 81% rename from Signal/src/textsecure/Messages/TSInteraction.mm rename to Signal/src/textsecure/Messages/TSInteraction.m index 6d4270d24..83f47bd4d 100644 --- a/Signal/src/textsecure/Messages/TSInteraction.mm +++ b/Signal/src/textsecure/Messages/TSInteraction.m @@ -6,8 +6,6 @@ // Copyright (c) 2014 Open Whisper Systems. All rights reserved. // -#import - #import "TSInteraction.h" const struct TSMessageRelationships TSMessageRelationships = { @@ -31,7 +29,7 @@ const struct TSMessageEdges TSMessageEdges = { } -#pragma - mark YapDatabaseRelationshipNode +#pragma mark YapDatabaseRelationshipNode - (NSArray *)yapDatabaseRelationshipEdges { @@ -53,7 +51,7 @@ const struct TSMessageEdges TSMessageEdges = { #pragma mark Date operations -- (int64_t)identifierToTimestamp{ +- (uint64_t)identifierToTimestamp{ NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; [f setNumberStyle:NSNumberFormatterNoStyle]; NSNumber * myNumber = [f numberFromString:self.uniqueId]; @@ -61,12 +59,12 @@ const struct TSMessageEdges TSMessageEdges = { } - (NSDate*)date{ - int64_t milliseconds = [self identifierToTimestamp]; - int64_t seconds = milliseconds/1000; + uint64_t milliseconds = [self identifierToTimestamp]; + uint64_t seconds = milliseconds/1000; return [NSDate dateWithTimeIntervalSince1970:seconds]; } -- (uint64_t)timeStamp{ +- (UInt64)timeStamp{ return [self identifierToTimestamp]; } @@ -74,9 +72,4 @@ const struct TSMessageEdges TSMessageEdges = { return [[NSNumber numberWithUnsignedLongLong:timestamp] stringValue]; } -- (NSNumber*)nowTimeStamp{ - double milliseconds = std::chrono::system_clock::now().time_since_epoch()/std::chrono::milliseconds(1); - return [NSNumber numberWithDouble:milliseconds]; -} - @end diff --git a/Signal/src/textsecure/Messages/TSMessagesManager+sendMessages.m b/Signal/src/textsecure/Messages/TSMessagesManager+sendMessages.m index 736f0c850..69a941914 100644 --- a/Signal/src/textsecure/Messages/TSMessagesManager+sendMessages.m +++ b/Signal/src/textsecure/Messages/TSMessagesManager+sendMessages.m @@ -9,6 +9,7 @@ #import "TSMessagesManager+sendMessages.h" #import +#import #import "IncomingPushMessageSignal.pb.h" #import "TSStorageManager.h" @@ -32,8 +33,13 @@ NSLog(@"Currently unsupported"); } else if([thread isKindOfClass:[TSContactThread class]]){ TSContactThread *contactThread = (TSContactThread*)thread; + __block TSRecipient *recipient; + [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + recipient = [contactThread recipientWithTransaction:transaction]; + }]; + [self sendMessage:message - toRecipient:contactThread.recipient + toRecipient:recipient inThread:thread withAttemps:3]; } @@ -55,6 +61,8 @@ [[TSNetworkManager sharedManager] queueAuthenticatedRequest:request success:^(NSURLSessionDataTask *task, id responseObject) { [self handleMessageSent:message inThread:thread]; + NSLog(@"Message sent"); + } failure:^(NSURLSessionDataTask *task, NSError *error) { NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; @@ -117,7 +125,9 @@ destination:recipient.uniqueId device:[deviceNumber intValue] body:serializedMessage]; - [messagesArray addObject:serverMessage]; + + + [messagesArray addObject:[MTLJSONAdapter JSONDictionaryFromModel:serverMessage]]; }@catch (NSException *exception) { [self processException:exception outgoingMessage:message]; diff --git a/Signal/src/textsecure/Messages/TSMessagesManager.h b/Signal/src/textsecure/Messages/TSMessagesManager.h index fb37493f8..d34243222 100644 --- a/Signal/src/textsecure/Messages/TSMessagesManager.h +++ b/Signal/src/textsecure/Messages/TSMessagesManager.h @@ -7,7 +7,7 @@ // #import - +#import "IncomingPushMessageSignal.pb.h" #import "TSOutgoingMessage.h" @interface TSMessagesManager : NSObject @@ -16,7 +16,7 @@ @property (readonly) YapDatabaseConnection *dbConnection; -- (void)handleMessageSignal:(NSData*)signalData; +- (void)handleMessageSignal:(IncomingPushMessageSignal*)messageSignal; - (void)processException:(NSException*)exception outgoingMessage:(TSOutgoingMessage*)message; diff --git a/Signal/src/textsecure/Messages/TSMessagesManager.m b/Signal/src/textsecure/Messages/TSMessagesManager.m index 5ba6b0a05..d35849faa 100644 --- a/Signal/src/textsecure/Messages/TSMessagesManager.m +++ b/Signal/src/textsecure/Messages/TSMessagesManager.m @@ -30,7 +30,7 @@ #import -#define ddLogLevel LOG_LEVEL_DEBUG +#define ddLogLevel LOG_LEVEL_VERBOSE @implementation TSMessagesManager @@ -53,21 +53,8 @@ return self; } -- (void)handleMessageSignal:(NSData*)signalData{ - NSString *base64String = [[NSString alloc] initWithData:signalData encoding:NSUTF8StringEncoding]; - - NSData *encryptedSignal = [NSData dataFromBase64String:base64String]; - NSData *decryptedPayload = [Cryptography decryptAppleMessagePayload:encryptedSignal - withSignalingKey:TSStorageManager.signalingKey]; - - if (!decryptedPayload) { - DDLogWarn(@"Failed to decrypt incoming payload or bad HMAC"); - return; - } - +- (void)handleMessageSignal:(IncomingPushMessageSignal*)messageSignal{ @try { - IncomingPushMessageSignal *messageSignal = [IncomingPushMessageSignal parseFromData:decryptedPayload]; - switch (messageSignal.type) { case IncomingPushMessageSignalTypeCiphertext: [self handleSecureMessage:messageSignal]; @@ -230,6 +217,11 @@ - (void)processException:(NSException*)exception pushSignal:(IncomingPushMessageSignal*)signal{ NSLog(@"Got exception: %@", exception.description); + [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + TSErrorMessage *errorMessage = [[TSErrorMessage alloc] initWithTimestamp:signal.timestamp inThread:[TSContactThread threadWithContactId:signal.source transaction:transaction] failedMessageType:TSErrorMessageNoSession]; + [errorMessage saveWithTransaction:transaction]; + }]; + } diff --git a/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m b/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m index 76e02c0c5..99b6d90d3 100644 --- a/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m +++ b/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m @@ -15,6 +15,10 @@ #import "TSStorageManager+keyingMaterial.h" #import +#import "NSData+Base64.h" +#import "Cryptography.h" +#import "IncomingPushMessageSignal.pb.h" + #define kWebSocketHeartBeat 15 NSString * const SocketOpenedNotification = @"SocketOpenedNotification"; @@ -83,7 +87,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; } + (void)resignActivity{ - SRWebSocket *socket =[[self sharedManager] websocket]; + SRWebSocket *socket = [[self sharedManager] websocket]; [socket close]; } @@ -115,8 +119,24 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; - (void)processWebSocketRequestMessage:(WebSocketRequestMessage*)message { DDLogInfo(@"Got message with verb: %@ and path: %@", message.verb, message.path); + [self sendWebSocketMessageAcknowledgement:message]; + if ([message.path isEqualToString:@"/api/v1/message"] && [message.verb isEqualToString:@"PUT"]){ - [[TSMessagesManager sharedManager] handleMessageSignal:message.body]; + + NSString *base64String = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding]; + + NSData *encryptedSignal = [NSData dataFromBase64String:base64String]; + NSData *decryptedPayload = [Cryptography decryptAppleMessagePayload:encryptedSignal + withSignalingKey:TSStorageManager.signalingKey]; + + if (!decryptedPayload) { + DDLogWarn(@"Failed to decrypt incoming payload or bad HMAC"); + return; + } + + IncomingPushMessageSignal *messageSignal = [IncomingPushMessageSignal parseFromData:decryptedPayload]; + + [[TSMessagesManager sharedManager] handleMessageSignal:messageSignal]; } else{ DDLogWarn(@"Unsupported WebSocket Request"); } @@ -126,10 +146,16 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; DDLogWarn(@"Client should not receive WebSocket Respond messages"); } -- (void)sendWebSocketMessageAcknowledgement:(NSString*)messageId { - WebSocketResponseMessageBuilder *message = [WebSocketResponseMessage builder]; - [message setStatus:200]; - [message setMessage:messageId]; +- (void)sendWebSocketMessageAcknowledgement:(WebSocketRequestMessage*)request { + WebSocketResponseMessageBuilder *response = [WebSocketResponseMessage builder]; + [response setStatus:200]; + [response setMessage:@"OK"]; + [response setId:request.id]; + + WebSocketMessageBuilder *message = [WebSocketMessage builder]; + [message setResponse:response.build]; + [message setType:WebSocketMessageTypeResponse]; + [self.websocket send:message.build.data]; } diff --git a/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.h b/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.h new file mode 100644 index 000000000..9f3032162 --- /dev/null +++ b/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.h @@ -0,0 +1,13 @@ +// +// NSDate+millisecondTimeStamp.h +// Signal +// +// Created by Frederic Jacobs on 25/11/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface NSDate (millisecondTimeStamp) ++ (uint64_t)ows_millisecondTimeStamp; +@end diff --git a/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.mm b/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.mm new file mode 100644 index 000000000..ac157c062 --- /dev/null +++ b/Signal/src/textsecure/Util/NSDate+millisecondTimeStamp.mm @@ -0,0 +1,19 @@ +// +// NSDate+millisecondTimeStamp.m +// Signal +// +// Created by Frederic Jacobs on 25/11/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "NSDate+millisecondTimeStamp.h" +#import + +@implementation NSDate (millisecondTimeStamp) + ++ (uint64_t)ows_millisecondTimeStamp{ + uint64_t milliseconds = std::chrono::system_clock::now().time_since_epoch()/std::chrono::milliseconds(1); + return milliseconds; +} + +@end diff --git a/Signal/src/view controllers/DemoDataModel.m b/Signal/src/view controllers/DemoDataModel.m index 44e26d92b..6038c56e0 100644 --- a/Signal/src/view controllers/DemoDataModel.m +++ b/Signal/src/view controllers/DemoDataModel.m @@ -24,10 +24,7 @@ enum {kDemoDataModelCase0, kDemoDataModelCase1,kDemoDataModelCase2, kDemoDataMod { [self loadFakeMessages]; - JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init]; - - self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleBlueColor]]; - self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; + } return self; } diff --git a/Signal/src/view controllers/MessageComposeTableViewController.h b/Signal/src/view controllers/MessageComposeTableViewController.h index b5b2c7edf..985fe05c3 100644 --- a/Signal/src/view controllers/MessageComposeTableViewController.h +++ b/Signal/src/view controllers/MessageComposeTableViewController.h @@ -17,8 +17,6 @@ #import "JSQMessagesKeyboardController.h" - @interface MessageComposeTableViewController : UITableViewController - @end diff --git a/Signal/src/view controllers/MessageComposeTableViewController.m b/Signal/src/view controllers/MessageComposeTableViewController.m index 6ac5b9858..dbac3a0d6 100644 --- a/Signal/src/view controllers/MessageComposeTableViewController.m +++ b/Signal/src/view controllers/MessageComposeTableViewController.m @@ -140,8 +140,6 @@ s.contactFromCompose = [self contactForIndexPath:indexPath]; - [self.searchController dismissViewControllerAnimated:NO completion:nil]; - [self dismissViewControllerAnimated:YES completion:^(){ [s performSegueWithIdentifier:@"showSegue" sender:nil]; }]; diff --git a/Signal/src/view controllers/MessagesViewController.h b/Signal/src/view controllers/MessagesViewController.h index a150c0199..07e6982f1 100644 --- a/Signal/src/view controllers/MessagesViewController.h +++ b/Signal/src/view controllers/MessagesViewController.h @@ -15,8 +15,8 @@ @interface MessagesViewController : JSQMessagesViewController -@property (strong, nonatomic) NSString* _senderTitleString; -@property DemoDataModel *demoData; +@property (strong, nonatomic) NSString* senderTitleString; +@property TSThread *thread; - (void)setupWithThread:(TSThread*)thread; diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 45ba1f7b9..7bc003ad9 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -20,6 +20,20 @@ #import #import +#import "TSContactThread.h" +#import "TSGroupThread.h" + +#import "TSStorageManager.h" +#import "TSDatabaseView.h" +#import +#import "TSInteraction.h" +#import "TSMessageAdapter.h" + +#import "TSMessagesManager+sendMessages.h" +#import "NSDate+millisecondTimeStamp.h" + +static NSTimeInterval const kTSMessageSentDateShowTimeInterval = 5 * 60; + typedef enum : NSUInteger { kMediaTypePicture, kMediaTypeVideo, @@ -30,6 +44,11 @@ typedef enum : NSUInteger { BOOL isGroupConversation; } +@property (nonatomic, strong) YapDatabaseConnection *uiDatabaseConnection; +@property (nonatomic, strong) YapDatabaseViewMappings *messageMappings; +@property (nonatomic, retain) JSQMessagesBubbleImage *outgoingBubbleImageData; +@property (nonatomic, retain) JSQMessagesBubbleImage *incomingBubbleImageData; + @end @implementation MessagesViewController @@ -38,22 +57,32 @@ typedef enum : NSUInteger { [super viewDidLoad]; // Do any additional setup after loading the view. + JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init]; + + self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleBlueColor]]; + self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; + + self.messageMappings = [[YapDatabaseViewMappings alloc] initWithGroups:@[self.thread.uniqueId] view:TSMessageDatabaseViewExtensionName]; + + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + [self.messageMappings updateWithTransaction:transaction]; + NSLog(@"Total messages: %lu", (unsigned long)[self.messageMappings numberOfItemsInAllGroups]); + }]; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"lock.png"] style:UIBarButtonItemStylePlain target:self action:@selector(showFingerprint)]; [self.collectionView.collectionViewLayout setMessageBubbleFont:[UIFont ows_lightFontWithSize:16.0f]]; self.collectionView.showsVerticalScrollIndicator = NO; self.collectionView.showsHorizontalScrollIndicator = NO; - + //DEBUG: isGroupConversation = NO; self.title = self.senderTitleString; - - self.senderId = kJSQDemoAvatarIdDylan; - self.senderDisplayName = kJSQDemoAvatarDisplayNameDylan; - self.demoData = [[DemoDataModel alloc] init]; + self.senderId = @"self"; + self.senderDisplayName = kJSQDemoAvatarDisplayNameDylan; self.automaticallyScrollsToMostRecentMessage = YES; @@ -91,12 +120,6 @@ typedef enum : NSUInteger { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; } --(void)initWithGroup:(NSArray *)group -{ - //Search for an existing group to set self.title & fetch messages for this identifier - //If none found, instantiate new group -} - #pragma mark - Keyboard Handlers -(void)keyboardWillShow:(id)sender @@ -120,7 +143,7 @@ typedef enum : NSUInteger { -(void)updateMessageStatus:(JSQMessage*)message { if ([message.senderId isEqualToString:self.senderId]) - message.status = kMessageReceived; + message.status = kMessageReceived; } #pragma mark - JSQMessagesViewController method overrides @@ -134,16 +157,13 @@ typedef enum : NSUInteger { if ([button.titleLabel.text isEqualToString:@"Call"]) { NSLog(@"Let's call !"); - + } else if (text.length > 0) { [JSQSystemSoundPlayer jsq_playMessageSentSound]; - - JSQMessage *message = [[JSQMessage alloc] initWithSenderId:senderId - senderDisplayName:senderDisplayName - date:date - text:text]; - - [self.demoData.messages addObject:message]; + + TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:self.thread messageBody:text attachements:nil]; + + [[TSMessagesManager sharedManager] sendMessage:message inThread:self.thread]; [self finishSendingMessage]; } } @@ -153,19 +173,18 @@ typedef enum : NSUInteger { - (id)collectionView:(JSQMessagesCollectionView *)collectionView messageDataForItemAtIndexPath:(NSIndexPath *)indexPath { - return [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; + return [TSMessageAdapter messageViewDataWithInteraction:[self messageAtIndexPath:indexPath] inThread:_thread]; } - (id)collectionView:(JSQMessagesCollectionView *)collectionView messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath { - - JSQMessage *message = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; + id message = [TSMessageAdapter messageViewDataWithInteraction:[self messageAtIndexPath:indexPath] inThread:_thread]; if ([message.senderId isEqualToString:self.senderId]) { - return self.demoData.outgoingBubbleImageData; + return self.outgoingBubbleImageData; } - return self.demoData.incomingBubbleImageData; + return self.incomingBubbleImageData; } - (id)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageDataForItemAtIndexPath:(NSIndexPath *)indexPath @@ -173,98 +192,31 @@ typedef enum : NSUInteger { return nil; } -- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath -{ - /** - * This logic should be consistent with what you return from `heightForCellTopLabelAtIndexPath:` - * The other label text delegate methods should follow a similar pattern. - * - * Show a timestamp for every 3rd message - */ - if (indexPath.item % 3 == 0) { - JSQMessage *message = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; - return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date]; - } - - return nil; -} - -- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath -{ - JSQMessage *message = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; - - - /** - * iOS7-style sender name labels - */ - if ([message.senderId isEqualToString:self.senderId]) { - [self updateMessageStatus:message]; - return nil; - } - - if (indexPath.item - 1 > 0) { - JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item - 1]; - if ([[previousMessage senderId] isEqualToString:message.senderId]) { - return nil; - } - } - - /** - * Don't specify attributes to use the defaults. - */ - return [[NSAttributedString alloc] initWithString:message.senderDisplayName]; -} - -- (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath -{ - JSQMessage * message = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; - - if (message.status == kMessageRead){ - return [[NSAttributedString alloc]initWithString:@"Read" attributes:nil]; - } else if (message.status == kMessageSent) { - return [[NSAttributedString alloc]initWithString:@"Sent" attributes:nil]; - } else if (message.status == kMessageReceived) { - return [[NSAttributedString alloc]initWithString:@"Received" attributes:nil]; - } else { - return nil; - } -} - #pragma mark - UICollectionView DataSource -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - return (NSInteger)[self.demoData.messages count]; -} - - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { /** * Override point for customizing cells */ - JSQMessage *msg = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; + id msg = [TSMessageAdapter messageViewDataWithInteraction:[self messageAtIndexPath:indexPath] inThread:_thread]; - if ([msg isKindOfClass:[JSQMessage class]]) - { - JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath]; - if (!msg.isMediaMessage) { - if ([msg.senderId isEqualToString:self.senderId]) { - cell.textView.textColor = [UIColor whiteColor]; - } - else { - cell.textView.textColor = [UIColor blackColor]; - } - - cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) }; + + JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath]; + if (!msg.isMediaMessage) { + if ([msg.senderId isEqualToString:self.senderId]) { + cell.textView.textColor = [UIColor whiteColor]; + } + else { + cell.textView.textColor = [UIColor blackColor]; } - return cell; - - } else { - JSQCallCollectionViewCell *cell = (JSQCallCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath]; - return cell; + cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) }; } + + return cell; + } #pragma mark - Adjusting cell label heights @@ -282,32 +234,29 @@ typedef enum : NSUInteger { * * Show a timestamp for every 3rd message */ - if (indexPath.item % 3 == 0) { + if ([self showDateAtIndexPath:indexPath]) { return kJSQMessagesCollectionViewCellLabelHeightDefault; } return 0.0f; } -- (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView - layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath +- (BOOL)showDateAtIndexPath:(NSIndexPath *)indexPath { - /** - * iOS7-style sender name labels - */ - JSQMessage *currentMessage = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item]; - if ([[currentMessage senderId] isEqualToString:self.senderId]) { - return 0.0f; + BOOL showDate = NO; + if (indexPath.row == 0) { + showDate = YES; } - - if (indexPath.item - 1 > 0) { - JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:(NSUInteger)indexPath.item - 1]; - if ([[previousMessage senderId] isEqualToString:[currentMessage senderId]]) { - return 0.0f; + else { + TSInteraction *currentMessage = [self messageAtIndexPath:indexPath]; + TSInteraction *previousMessage = [self messageAtIndexPath:[NSIndexPath indexPathForItem:indexPath.row-1 inSection:indexPath.section]]; + + NSTimeInterval timeDifference = [currentMessage.date timeIntervalSinceDate:previousMessage.date]; + if (timeDifference > kTSMessageSentDateShowTimeInterval) { + showDate = YES; } } - - return kJSQMessagesCollectionViewCellLabelHeightDefault; + return showDate; } - (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView @@ -319,41 +268,6 @@ typedef enum : NSUInteger { #pragma mark - Actions --(void)didPressAccessoryButton:(UIButton *)sender -{ - [self.inputToolbar.contentView.textView resignFirstResponder]; - - UIView *presenter = self.parentViewController.view; - - [DJWActionSheet showInView:presenter - withTitle:nil - cancelButtonTitle:@"Cancel" - destructiveButtonTitle:nil - otherButtonTitles:@[@"Take Photo or Video", @"Choose existing Photo", @"Choose existing Video", @"Send file"] - tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { - if (tappedButtonIndex == actionSheet.cancelButtonIndex) { - NSLog(@"User Cancelled"); - } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { - NSLog(@"Destructive button tapped"); - }else { - switch (tappedButtonIndex) { - case 0: - [self takePictureOrVideo]; - break; - case 1: - [self chooseFromLibrary:kMediaTypePicture]; - break; - case 2: - [self chooseFromLibrary:kMediaTypeVideo]; - break; - - default: - break; - } - } - }]; -} - - (void)collectionView:(JSQMessagesCollectionView *)collectionView didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath { id messageItem = [collectionView.dataSource collectionView:collectionView messageDataForItemAtIndexPath:indexPath]; @@ -363,12 +277,11 @@ typedef enum : NSUInteger { if (isMediaMessage) { id messageMedia = [messageItem media]; - if ([messageMedia isKindOfClass:JSQPhotoMediaItem.class]) { //is a photo tappedImage = ((JSQPhotoMediaItem*)messageMedia).image ; [self performSegueWithIdentifier:@"fullImage" sender:self]; - + } else if ([messageMedia isKindOfClass:JSQVideoMediaItem.class]) { //is a video } @@ -408,7 +321,7 @@ typedef enum : NSUInteger { picker.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *)kUTTypeMovie, kUTTypeImage, kUTTypeVideo, nil]; [self presentViewController:picker animated:YES completion:NULL]; } - + } -(void)chooseFromLibrary:(kMediaTypes)mediaType @@ -424,7 +337,7 @@ typedef enum : NSUInteger { NSArray* videoTypeArray = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeMovie, (NSString*)kUTTypeVideo, nil]; picker.mediaTypes = (mediaType == kMediaTypePicture) ? pictureTypeArray : videoTypeArray; - + [self presentViewController:picker animated:YES completion:nil]; } } @@ -462,9 +375,9 @@ typedef enum : NSUInteger { JSQVideoMediaItem * videoItem = [[JSQVideoMediaItem alloc] initWithFileURL:videoURL isReadyToPlay:YES]; JSQMessage * videoMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdDylan - displayName:kJSQDemoAvatarDisplayNameDylan - media:videoItem]; - [self.demoData.messages addObject:videoMessage]; + displayName:kJSQDemoAvatarDisplayNameDylan + media:videoItem]; + [self finishSendingMessage]; } else if (picture_camera) { @@ -472,14 +385,110 @@ typedef enum : NSUInteger { JSQPhotoMediaItem *photoItem = [[JSQPhotoMediaItem alloc] initWithImage:picture_camera]; JSQMessage *photoMessage = [JSQMessage messageWithSenderId:kJSQDemoAvatarIdDylan - displayName:kJSQDemoAvatarDisplayNameDylan - media:photoItem]; - [self.demoData.messages addObject:photoMessage]; + displayName:kJSQDemoAvatarDisplayNameDylan + media:photoItem]; [self finishSendingMessage]; } [self dismissViewControllerAnimated:YES completion:nil]; - + } + +#pragma mark Storage access + +- (YapDatabaseConnection *)uiDatabaseConnection +{ + NSAssert([NSThread isMainThread], @"Must access uiDatabaseConnection on main thread!"); + if (!_uiDatabaseConnection) { + _uiDatabaseConnection = [[TSStorageManager sharedManager] newDatabaseConnection]; + [_uiDatabaseConnection beginLongLivedReadTransaction]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(yapDatabaseModified:) + name:YapDatabaseModifiedNotification + object:nil]; + } + return _uiDatabaseConnection; +} + +- (void)yapDatabaseModified:(NSNotification *)notification +{ + // Process the notification(s), + // and get the change-set(s) as applies to my view and mappings configuration. + NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction]; + + NSArray *messageRowChanges = nil; + + [[self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName] getSectionChanges:nil + rowChanges:&messageRowChanges + forNotifications:notifications + withMappings:self.messageMappings]; + [self.collectionView reloadData]; + [self finishReceivingMessage]; +} + +#pragma mark - UICollectionView DataSource +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + NSInteger numberOfMessages = [self.messageMappings numberOfItemsInSection:section]; + return numberOfMessages; +} + +- (TSInteraction*)messageAtIndexPath:(NSIndexPath *)indexPath +{ + __block TSInteraction *message = nil; + [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { + YapDatabaseViewTransaction *viewTransaction = [transaction ext:TSMessageDatabaseViewExtensionName]; + NSParameterAssert(viewTransaction != nil); + NSParameterAssert(self.messageMappings != nil); + NSParameterAssert(indexPath != nil); + NSUInteger row = indexPath.row; + NSUInteger section = indexPath.section; + NSUInteger numberOfItemsInSection = [self.messageMappings numberOfItemsInSection:section]; + + NSAssert(row < numberOfItemsInSection, @"Cannot fetch message because row %d is >= numberOfItemsInSection %d", (int)row, (int)numberOfItemsInSection); + + message = [viewTransaction objectAtRow:row inSection:section withMappings:self.messageMappings]; + NSParameterAssert(message != nil); + }]; + return message; +} + +#pragma mark Accessory View + +-(void)didPressAccessoryButton:(UIButton *)sender +{ + [self.inputToolbar.contentView.textView resignFirstResponder]; + + UIView *presenter = self.parentViewController.view; + + [DJWActionSheet showInView:presenter + withTitle:nil + cancelButtonTitle:@"Cancel" + destructiveButtonTitle:nil + otherButtonTitles:@[@"Take Photo or Video", @"Choose existing Photo", @"Choose existing Video", @"Send file"] + tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { + if (tappedButtonIndex == actionSheet.cancelButtonIndex) { + NSLog(@"User Cancelled"); + } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { + NSLog(@"Destructive button tapped"); + }else { + switch (tappedButtonIndex) { + case 0: + [self takePictureOrVideo]; + break; + case 1: + [self chooseFromLibrary:kMediaTypePicture]; + break; + case 2: + [self chooseFromLibrary:kMediaTypeVideo]; + break; + + default: + break; + } + } + }]; +} + @end diff --git a/Signal/src/view controllers/RegistrationViewController.h b/Signal/src/view controllers/RegistrationViewController.h index 9f01958ec..0c71fe086 100644 --- a/Signal/src/view controllers/RegistrationViewController.h +++ b/Signal/src/view controllers/RegistrationViewController.h @@ -13,13 +13,11 @@ @interface RegistrationViewController : UIViewController // Country code -@property(nonatomic, strong) IBOutlet UIButton* countryCodeButton; -@property(nonatomic, strong) IBOutlet UILabel* countryNameLabel; -@property(nonatomic, strong) IBOutlet UILabel* countryCodeLabel; +@property (nonatomic, strong) IBOutlet UIButton * countryCodeButton; +@property (nonatomic, strong) IBOutlet UILabel * countryNameLabel; +@property (nonatomic, strong) IBOutlet UILabel * countryCodeLabel; //Phone number @property(nonatomic, strong) IBOutlet UITextField* phoneNumberTextField; - - @end diff --git a/Signal/src/view controllers/TSMessageAdapter.h b/Signal/src/view controllers/TSMessageAdapter.h new file mode 100644 index 000000000..ce7d9ebc3 --- /dev/null +++ b/Signal/src/view controllers/TSMessageAdapter.h @@ -0,0 +1,20 @@ +// +// TSMessageAdapter.h +// Signal +// +// Created by Frederic Jacobs on 24/11/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import + +#import "TSMessageAdapter.h" +#import "TSInteraction.h" +#import "TSThread.h" + +@interface TSMessageAdapter : NSObject + ++ (instancetype)messageViewDataWithInteraction:(TSInteraction*)interaction inThread:(TSThread*)thread; + +@end diff --git a/Signal/src/view controllers/TSMessageAdapter.m b/Signal/src/view controllers/TSMessageAdapter.m new file mode 100644 index 000000000..e354d2abc --- /dev/null +++ b/Signal/src/view controllers/TSMessageAdapter.m @@ -0,0 +1,93 @@ +// +// TSMessageAdapter.m +// Signal +// +// Created by Frederic Jacobs on 24/11/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import "TSMessageAdapter.h" +#import "TSIncomingMessage.h" +#import "TSOutgoingMessage.h" +#import "TSCall.h" +#import "TSInfoMessage.h" +#import "TSErrorMessage.h" + +@interface TSMessageAdapter () + +// --- + +@property (nonatomic, retain) TSContactThread *thread; + +// OR for groups + +@property (nonatomic, retain) NSString *senderId; +@property (nonatomic, retain) NSString *senderDisplayName; + +// --- + +@property (nonatomic, copy) NSDate *messageDate; +@property (nonatomic, retain) NSString *messageBody; + +@end + + +@implementation TSMessageAdapter + ++ (instancetype)messageViewDataWithInteraction:(TSInteraction*)interaction inThread:(TSThread*)thread{ + + TSMessageAdapter *adapter = [[TSMessageAdapter alloc] init]; + adapter.messageDate = interaction.date; + + if ([thread isKindOfClass:[TSContactThread class]]) { + adapter.thread = (TSContactThread*)thread; + } else if ([thread isKindOfClass:[TSGroupThread class]]){ + if ([interaction isKindOfClass:[TSIncomingMessage class]]) { + TSIncomingMessage *message = (TSIncomingMessage*)interaction; + adapter.senderId = message.authorId; + adapter.senderDisplayName = message.authorId; + } else{ + adapter.senderId = @"self"; + adapter.senderDisplayName = @"Me"; + } + } + + if ([interaction isKindOfClass:[TSMessage class]]) { + TSMessage *message = (TSMessage*)interaction; + adapter.messageBody = message.body; + } else if ([interaction isKindOfClass:[TSCall class]]){ + adapter.messageBody = @"Placeholder for TSCalls"; + } else if ([interaction isKindOfClass:[TSInfoMessage class]]){ + adapter.messageBody = @"Placeholder for InfoMessage"; + } else{ + adapter.messageBody = @"Placeholder for ErrorMessage"; + } + + return adapter; +} + + +- (NSString*)senderId{ + return self.thread.uniqueId; +} + +- (NSString *)senderDisplayName{ + if (self.thread) { + return _thread.name; + } + return self.senderDisplayName; +} + +- (NSDate *)date{ + return self.messageDate; +} + +- (BOOL)isMediaMessage{ + return NO; +} + +- (NSString *)text{ + return self.messageBody; +} + +@end diff --git a/Signal/src/view controllers/UITests/SignalsViewController.m b/Signal/src/view controllers/UITests/SignalsViewController.m index d80ca3a6c..0d0e1e662 100644 --- a/Signal/src/view controllers/UITests/SignalsViewController.m +++ b/Signal/src/view controllers/UITests/SignalsViewController.m @@ -15,6 +15,7 @@ #import "TSStorageManager.h" #import "TSDatabaseView.h" #import "TSSocketManager.h" +#import "TSContactThread.h" #import #import "YapDatabaseViewTransaction.h" @@ -161,14 +162,15 @@ static NSString *const kSegueIndentifier = @"showSegue"; if ([segue.identifier isEqualToString:kSegueIndentifier]) { - MessagesViewController * vc = [segue destinationViewController]; + MessagesViewController * vc = [segue destinationViewController]; NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow]; - if (selectedIndexPath) { - vc.senderTitleString = ((DemoDataModel*)_dataArray[(NSUInteger)selectedIndexPath.row])._sender; - } else if (_contactFromCompose) { - vc.senderTitleString = _contactFromCompose.fullName; - } else if (_groupFromCompose) { - vc.senderTitleString = _groupFromCompose.groupName; + vc.thread = [self threadForIndexPath:selectedIndexPath]; + + if (!vc.thread) { + [TSStorageManager.sharedManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + vc.thread = [TSContactThread threadWithContactId:[self.contactFromCompose.userTextPhoneNumbers firstObject] transaction:transaction]; + NSLog(@"Thread:%@", vc.thread); + }]; } }