From 40fb964cb36ddfb65eb7629785b689e4fb0183b2 Mon Sep 17 00:00:00 2001 From: James Magahern Date: Thu, 8 Jul 2021 15:35:29 -0700 Subject: [PATCH] SendMessage: allows creation of new conversations in addition to replying to guids --- Dumped Classes/IMCore_ClassDump.h | 32 ++++++ Dumped Classes/IMSharedUtilities_ClassDump.h | 24 ---- .../Operations/MBIMAliasValidationOperation.m | 33 ++++-- .../Operations/MBIMSendMessageOperation.m | 106 ++++++++++++++---- kordophone/main.m | 2 +- 5 files changed, 139 insertions(+), 58 deletions(-) diff --git a/Dumped Classes/IMCore_ClassDump.h b/Dumped Classes/IMCore_ClassDump.h index 16bee13..8f9751e 100644 --- a/Dumped Classes/IMCore_ClassDump.h +++ b/Dumped Classes/IMCore_ClassDump.h @@ -4081,6 +4081,38 @@ __attribute__((visibility("default"))) @interface IMService : NSObject { } @end +// Monterey +#if 0 +@interface IMChatRegistry () +- (IMChat *)chatWithHandle:(IMHandle *)handle; +- (IMChat *)chatWithHandle:(IMHandle *)handle lastAddressedHandle:(NSString *)lastAddressedHandle lastAddressedSIMID:(NSString *)lastAddressedSIMID; +- (IMChat *)chatWithHandles:(NSArray *)handles; +- (IMChat *)chatWithHandles:(NSArray *)handles lastAddressedHandle:(NSString *)lastAddressedHandle lastAddressedSIMID:(NSString *)lastAddressedSIMID; +- (IMChat *)chatWithHandles:(NSArray *)handles displayName:(NSString *)displayName joinedChatsOnly:(BOOL)joinedChatsOnly; +- (IMChat *)chatWithHandles:(NSArray *)handles displayName:(NSString *)displayName joinedChatsOnly:(BOOL)joinedChatsOnly lastAddressedHandle:(NSString *)lastAddressedHandle lastAddressedSIMID:(NSString *)lastAddressedSIMID; +- (NSArray *)allGUIDsForChat:(IMChat *)chat; + +- (IMChat *)existingChatWithHandle:(IMHandle *)handle; +- (IMChat *)existingChatWithHandle:(IMHandle *)handle allowAlternativeService:(BOOL)allowAlternativeService; +- (IMChat *)existingChatWithHandles:(NSArray *)handles; +- (IMChat *)existingChatWithHandles:(NSArray *)handles allowAlternativeService:(BOOL)allowAlternativeService; + +- (IMChat *)existingChatWithHandles:(NSArray *)handles allowAlternativeService:(BOOL)allowAlternativeService groupID:(NSString *)groupID; + +- (IMChat *)existingChatWithHandles:(NSArray *)handles allowAlternativeService:(BOOL)allowAlternativeService groupID:(NSString *)groupID displayName:(NSString *)displayName joinedChatsOnly:(BOOL)joinedChatsOnly; + +- (IMChat *)existingChatWithPinningIdentifier:(NSString *)pinningIdentifier; +- (IMChat *)existingChatWithDeviceIndependentID:(NSString *)deviceIndependentID; + +- (IMChat *)existingChatWithPersonID:(NSString *)personID; +- (IMChat *)existingChatWithDisplayName:(NSString *)displayName; +- (IMChat *)existingChatWithAddresses:(NSArray *)addresses allowAlternativeService:(BOOL)allowAlternativeService bestHandles:(NSArray **)outBestHandles; +- (IMChat *)existingChatWithContacts:(NSSet *)contacts bestHandles:(NSArray **)outBestHandles; + +@end +#endif + + @interface IMSimulatedChat : IMChat // { // id _delegate; diff --git a/Dumped Classes/IMSharedUtilities_ClassDump.h b/Dumped Classes/IMSharedUtilities_ClassDump.h index a49ab41..81427f2 100644 --- a/Dumped Classes/IMSharedUtilities_ClassDump.h +++ b/Dumped Classes/IMSharedUtilities_ClassDump.h @@ -745,19 +745,6 @@ struct __va_list_tag { @end -@interface IMImageUtilities : NSObject -{ -} - -+ (struct CGImage *)newThumbnailForTargetSize:(struct CGSize)arg1 imageSize:(struct CGSize)arg2 imageSource:(struct CGImageSource *)arg3 atIndex:(unsigned long long)arg4 mode:(long long)arg5 scale:(double)arg6; -+ (struct CGImage *)newThumbnailForTargetSize:(struct CGSize)arg1 imageSize:(struct CGSize)arg2 imageSource:(struct CGImageSource *)arg3 mode:(long long)arg4 scale:(double)arg5; -+ (BOOL)persistCPBitmapWithImage:(struct CGImage *)arg1 url:(id)arg2; -+ (void)sampleImageEdges:(char *)arg1 usingRect:(struct CGRect)arg2 forMostlyWhitePixels:(unsigned long long *)arg3 otherPixels:(unsigned long long *)arg4 bytesPerRow:(long long)arg5; -+ (struct CGSize)imageRefPxSize:(struct CGImage *)arg1; -+ (struct CGSize)imageSourcePxSize:(struct CGImageSource *)arg1; - -@end - @interface IMOneTimeCodeUtilities : NSObject { } @@ -1426,17 +1413,6 @@ struct __va_list_tag { @end -@interface IMImagePreviewGenerator : IMPreviewGenerator -{ -} - -+ (struct CGImage *)newThumbnailFillToSize:(struct CGSize)arg1 imagePxSize:(struct CGSize)arg2 imageSource:(struct CGImageSource *)arg3 scale:(double)arg4; -+ (struct CGImage *)newPreviewFromSourceURL:(id)arg1 withPreviewConstraints:(struct IMPreviewConstraints)arg2 error:(id *)arg3; -+ (id)UTITypes; -+ (id)fetchUTITypes; - -@end - @interface IMAKAppleIDAuthenticationController : NSObject { diff --git a/kordophone/Bridge/Operations/MBIMAliasValidationOperation.m b/kordophone/Bridge/Operations/MBIMAliasValidationOperation.m index f669921..8c2cee7 100644 --- a/kordophone/Bridge/Operations/MBIMAliasValidationOperation.m +++ b/kordophone/Bridge/Operations/MBIMAliasValidationOperation.m @@ -29,9 +29,6 @@ } NSString *unformattedAddress = IMStripFormattingFromAddress(address); - IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; - IMHandle *handle = [iMessageAccount imHandleWithID:unformattedAddress]; - BOOL isEmailAddress = IMStringIsEmail(unformattedAddress); NSString *IDSaddress = isEmailAddress ? IDSCopyIDForEmailAddress(unformattedAddress) @@ -43,16 +40,30 @@ return; } - IMChatCalculateServiceForSendingNewComposeMaybeForce( - @[ IDSaddress ], nil, nil, NO, isEmailAddress, NO, NO, NO, nil, - ^(BOOL allAddressesiMessageCapable, NSDictionary *availabilityPerRecipient, BOOL checkedServer, NSError *error) { - NSLog(@"Capable: %d", allAddressesiMessageCapable); - NSLog(@"Avail: %@", availabilityPerRecipient); - self.serverCompletionBlock([MBIMJSONDataResponse responseWithJSONObject:@{ - @"capable" : @(allAddressesiMessageCapable), - }]); + __block IMChat *existingChat = nil; + dispatch_sync([[self class] sharedIMAccessQueue], ^{ + IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; + IMHandle *handle = [iMessageAccount imHandleWithID:unformattedAddress]; + + if (handle) { + existingChat = [[IMChatRegistry sharedInstance] existingChatForIMHandle:handle]; + } }); + IMChatCalculateServiceForSendingNewComposeMaybeForce( + @[ IDSaddress ], nil, nil, NO, isEmailAddress, YES, NO, NO, nil, + ^(BOOL allAddressesiMessageCapable, NSDictionary *availabilityPerRecipient, BOOL checkedServer, NSError *error) { + NSMutableDictionary *response = [NSMutableDictionary dictionaryWithDictionary:@{ + @"capable" : @(allAddressesiMessageCapable), + @"idsAddress" : IDSaddress, + }]; + + if ([existingChat guid]) { + [response setObject:[existingChat guid] forKey:@"existingGuid"]; + } + + self.serverCompletionBlock([MBIMJSONDataResponse responseWithJSONObject:response]); + }); } @end diff --git a/kordophone/Bridge/Operations/MBIMSendMessageOperation.m b/kordophone/Bridge/Operations/MBIMSendMessageOperation.m index a1d71cb..ec6922d 100644 --- a/kordophone/Bridge/Operations/MBIMSendMessageOperation.m +++ b/kordophone/Bridge/Operations/MBIMSendMessageOperation.m @@ -19,40 +19,95 @@ return @"sendMessage"; } +- (BOOL)_sendMessage:(NSString *)messageBody toHandles:(NSArray *)handleIDs attachmentGUIDs:(NSArray *)guids +{ + __block BOOL result = YES; + dispatch_sync([[self class] sharedIMAccessQueue], ^{ + IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; + + NSMutableArray *handles = [NSMutableArray array]; + for (NSString *handleID in handleIDs) { + IMHandle *handle = [iMessageAccount imHandleWithID:handleID]; + if (handle) { + [handles addObject:handle]; + } else { + MBIMLogError(@"MBIMSendMessageOperation: Invalid handle ID: %@", handleID); + } + } + + IMChat *chat = nil; + if (handles.count == 1) { + // Single recipient + IMHandle *handle = [handles firstObject]; + chat = [[IMChatRegistry sharedInstance] existingChatForIMHandle:handle]; + if (chat == nil) { + MBIMLogInfo(@"MBIMSendMessageOperation: Creating chat with handle: %@", handle); + chat = [[IMChatRegistry sharedInstance] chatForIMHandle:handle]; + } + } else if (handles.count > 1) { + // Group chat + chat = [[IMChatRegistry sharedInstance] existingChatForIMHandles:handles allowRetargeting:NO groupID:nil displayName:nil joinedChatsOnly:YES]; + if (chat == nil) { + MBIMLogInfo(@"MBIMSendMessageOperation: Creating group chat with handles: %@", handles); + chat = [[IMChatRegistry sharedInstance] chatForIMHandles:handles]; + } + } else { + // No handles? + result = NO; + } + + if (chat) { + result = [self _sendMessage:messageBody toChat:chat attachmentGUIDs:guids]; + } else { + result = NO; + } + }); + + return result; +} + - (BOOL)_sendMessage:(NSString *)messageBody toChatWithGUID:(NSString *)chatGUID attachmentGUIDs:(NSArray *)guids { __block BOOL result = YES; - dispatch_sync([[self class] sharedIMAccessQueue], ^{ IMChat *chat = [[IMChatRegistry sharedInstance] existingChatWithGUID:chatGUID]; - - // TODO: chat might not be an iMessage chat! - IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; - IMHandle *senderHandle = [iMessageAccount loginIMHandle]; - - NSAttributedString *replyAttrString = [[NSAttributedString alloc] initWithString:messageBody]; - NSAttributedString *attrStringWithFileTransfers = IMCreateSuperFormatStringWithAppendedFileTransfers(replyAttrString, guids); - - IMMessage *reply = [IMMessage fromMeIMHandle:senderHandle - withText:attrStringWithFileTransfers - fileTransferGUIDs:guids - flags:(kIMMessageFinished | kIMMessageIsFromMe)]; - - for (NSString *guid in [reply fileTransferGUIDs]) { - [[IMFileTransferCenter sharedInstance] assignTransfer:guid toHandle:chat.recipient]; - } - if (!chat) { MBIMLogInfo(@"Chat does not exist: %@", chatGUID); result = NO; } else { - [chat sendMessage:reply]; + result = [self _sendMessage:messageBody toChat:chat attachmentGUIDs:guids]; } }); return result; } +- (BOOL)_sendMessage:(NSString *)messageBody toChat:(IMChat *)chat attachmentGUIDs:(NSArray *)guids +{ + dispatch_assert_queue([[self class] sharedIMAccessQueue]); + + BOOL result = YES; + + // TODO: chat might not be an iMessage chat! + IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; + IMHandle *senderHandle = [iMessageAccount loginIMHandle]; + + NSAttributedString *replyAttrString = [[NSAttributedString alloc] initWithString:messageBody]; + NSAttributedString *attrStringWithFileTransfers = IMCreateSuperFormatStringWithAppendedFileTransfers(replyAttrString, guids); + + IMMessage *reply = [IMMessage fromMeIMHandle:senderHandle + withText:attrStringWithFileTransfers + fileTransferGUIDs:guids + flags:(kIMMessageFinished | kIMMessageIsFromMe)]; + + for (NSString *guid in [reply fileTransferGUIDs]) { + [[IMFileTransferCenter sharedInstance] assignTransfer:guid toHandle:chat.recipient]; + } + + [chat sendMessage:reply]; + return result; +} + - (void)main { NSObject *response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; @@ -64,9 +119,8 @@ return; } - NSString *guid = [args objectForKey:@"guid"]; NSString *messageBody = [args objectForKey:@"body"]; - if (!guid || !messageBody) { + if (!messageBody) { self.serverCompletionBlock(response); return; } @@ -76,7 +130,15 @@ transferGUIDs = @[]; } - BOOL result = [self _sendMessage:messageBody toChatWithGUID:guid attachmentGUIDs:transferGUIDs]; + BOOL result = NO; + NSString *guid = [args objectForKey:@"guid"]; + NSArray *handles = [args objectForKey:@"handles"]; + if (guid) { + result = [self _sendMessage:messageBody toChatWithGUID:guid attachmentGUIDs:transferGUIDs]; + } else if ([handles count] > 0) { + result = [self _sendMessage:messageBody toHandles:handles attachmentGUIDs:transferGUIDs]; + } + if (result) { response = [[HTTPErrorResponse alloc] initWithErrorCode:200]; } diff --git a/kordophone/main.m b/kordophone/main.m index 169407c..97c5b7c 100644 --- a/kordophone/main.m +++ b/kordophone/main.m @@ -10,7 +10,7 @@ #import "MBIMBridge.h" -void printUsage() +static void printUsage(void) { fprintf(stderr, "Usage: kordophoned [-h] [-s | -c (certificate.p12)] [-a (access control file)\n"); fprintf(stderr, "\t-h \t Show this help message\n");