// // MBIMSendMessageOperation.m // kordophoned // // Created by James Magahern on 11/13/18. // Copyright © 2018 James Magahern. All rights reserved. // #import "MBIMSendMessageOperation.h" #import "IMCore_ClassDump.h" #import "IMMessageItem+Encoded.h" #import "MBIMErrorResponse.h" @implementation MBIMSendMessageOperation + (void)load { [super load]; } + (NSString *)endpointName { return @"sendMessage"; } - (nullable IMChat *)_chatForHandleIDs:(NSArray *)handleIDs registry:(IMChatRegistry *)registry { IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; if (!iMessageAccount) { MBIMLogError(@"Unable to find an iMessage account for message send."); return nil; } NSMutableArray *handles = [NSMutableArray arrayWithCapacity:[handleIDs count]]; for (NSString *handleID in handleIDs) { IMHandle *handle = [iMessageAccount imHandleWithID:handleID]; if (!handle) { MBIMLogError(@"Couldn't resolve IMHandle for id %@", handleID); return nil; } [handles addObject:handle]; } if ([handles count] == 1) { IMHandle *handle = [handles firstObject]; IMChat *chat = [registry existingChatWithHandle:handle allowAlternativeService:NO]; if (!chat) { chat = [registry chatWithHandle:handle]; } return chat; } IMChat *chat = [registry existingChatWithHandles:handles allowAlternativeService:NO groupID:nil displayName:nil joinedChatsOnly:YES]; if (!chat) { chat = [registry chatWithHandles:handles displayName:nil joinedChatsOnly:YES]; } return chat; } - (nullable NSDictionary *)_sendMessage:(NSString *)messageBody toChat:(IMChat *)chat attachmentGUIDs:(NSArray *)guids includeConversationGUID:(BOOL)includeConversationGUID { if (!chat) { return nil; } IMAccount *sendingAccount = [chat account]; if (!sendingAccount) { sendingAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; } IMHandle *senderHandle = [sendingAccount loginIMHandle]; if (!senderHandle) { MBIMLogError(@"Unable to determine sender handle for message send."); return nil; } 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 toMessage:reply account:sendingAccount]; } NSMutableDictionary *result = [[reply mbim_dictionaryRepresentation] mutableCopy]; if (includeConversationGUID) { NSString *conversationGUID = [chat guid]; if (!conversationGUID) { conversationGUID = [[[IMChatRegistry sharedInstance] allGUIDsForChat:chat] firstObject]; } if (conversationGUID) { result[@"conversationGUID"] = conversationGUID; } } dispatch_async(dispatch_get_main_queue(), ^{ [chat sendMessage:reply]; }); return result; } #if 0 - (NSDictionary *)adjustMessageSummaryInfoForSending:(NSDictionary *)messageSummaryInfo { NSMutableDictionary *adjustedInfo = [messageSummaryInfo mutableCopy]; if (!adjustedInfo) { adjustedInfo = [NSMutableDictionary dictionary]; } if ([fullText length] > 50) { summary = [[summary substringToIndex:[summary rangeOfComposedCharacterSequenceAtIndex:kMaxSummaryLength].location] stringByAppendingString:@"…"]; adjustedInfo[IMMessageSummaryInfoSummary] = summary; } adjustedInfo[IMMessageSummaryInfoTapbackRepresentationKey] = @"Loved"; return adjustedInfo; } #endif - (void)main { __block NSObject *response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; NSError *error = nil; NSDictionary *args = [NSJSONSerialization JSONObjectWithData:self.requestBodyData options:0 error:&error]; if (error || args.count == 0) { self.serverCompletionBlock(response); return; } NSString *guid = [args objectForKey:@"guid"]; NSString *messageBody = [args objectForKey:@"body"]; NSArray *rawHandleIDs = [args objectForKey:@"handleIDs"]; BOOL hasGUID = [guid isKindOfClass:[NSString class]] && [guid length] > 0; BOOL hasHandleIDs = [rawHandleIDs isKindOfClass:[NSArray class]] && [rawHandleIDs count] > 0; if (![messageBody isKindOfClass:[NSString class]] || (!hasGUID && !hasHandleIDs) || (hasGUID && hasHandleIDs)) { response = [[MBIMErrorResponse alloc] initWithErrorCode:500 message:@"sendMessage requires body and exactly one of guid or handleIDs."]; self.serverCompletionBlock(response); return; } NSMutableArray *handleIDs = [NSMutableArray array]; if (hasHandleIDs) { for (id handleID in rawHandleIDs) { if ([handleID isKindOfClass:[NSString class]] && [handleID length] > 0) { [handleIDs addObject:handleID]; } } handleIDs = [[[NSOrderedSet orderedSetWithArray:handleIDs] array] mutableCopy]; if ([handleIDs count] == 0) { response = [[MBIMErrorResponse alloc] initWithErrorCode:500 message:@"No valid handle IDs provided."]; self.serverCompletionBlock(response); return; } } NSArray *rawTransferGUIDs = [args objectForKey:@"fileTransferGUIDs"]; NSMutableArray *transferGUIDs = [NSMutableArray array]; if ([rawTransferGUIDs isKindOfClass:[NSArray class]]) { for (id transferGUID in rawTransferGUIDs) { if ([transferGUID isKindOfClass:[NSString class]] && [transferGUID length] > 0) { [transferGUIDs addObject:transferGUID]; } } } dispatch_sync([[self class] sharedIMAccessQueue], ^{ IMChatRegistry *registry = [IMChatRegistry sharedInstance]; IMChat *chat = nil; BOOL includeConversationGUID = NO; if (hasGUID) { chat = [registry existingChatWithGUID:guid]; if (!chat) { response = [[MBIMErrorResponse alloc] initWithErrorCode:500 message:@"Chat does not exist for the provided guid."]; return; } } else { chat = [self _chatForHandleIDs:handleIDs registry:registry]; includeConversationGUID = YES; if (!chat) { response = [[MBIMErrorResponse alloc] initWithErrorCode:500 message:@"Unable to create or locate a chat for the provided handles."]; return; } } NSDictionary *result = [self _sendMessage:messageBody toChat:chat attachmentGUIDs:transferGUIDs includeConversationGUID:includeConversationGUID]; if (result) { response = [MBIMJSONDataResponse responseWithJSONObject:result]; } }); self.serverCompletionBlock(response); } @end