diff --git a/Dumped Classes/IMCore_ClassDump.h b/Dumped Classes/IMCore_ClassDump.h index f74f609..cddcbd0 100644 --- a/Dumped Classes/IMCore_ClassDump.h +++ b/Dumped Classes/IMCore_ClassDump.h @@ -4090,6 +4090,30 @@ typedef NS_ENUM(NSInteger, IMMessageDescriptionType) { @property(readonly) unsigned long long hash; @property(readonly) Class superclass; +- (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 *)existingChatWithGUID:(NSString *)guid; +- ( IMChat *)existingChatWithPinningIdentifier:(NSString *)pinningIdentifier; +- ( IMChat *)existingChatWithDeviceIndependentID:(NSString *)deviceIndependentID; +// - ( IMChat *)existingChatWithChatIdentifier:(NSString *)identifier; +- ( IMChat *)existingChatWithPersonID:(NSString *)personID; +// - ( IMChat *)existingChatWithGroupID:(NSString *)groupID; +// - ( IMChat *)existingChatWithDisplayName:(NSString *)displayName; +- ( IMChat *)existingChatWithAddresses:(NSArray *)addresses allowAlternativeService:(BOOL)allowAlternativeService bestHandles:(NSArray * __autoreleasing*)outBestHandles; +- ( IMChat *)existingChatWithContacts:(NSSet /* */ *)contacts bestHandles:(NSArray * __autoreleasing * )outBestHandles; + @end @interface IMSimulatedChat : IMChat // @@ -4985,3 +5009,66 @@ typedef NS_ENUM(NSInteger, IMMessageDescriptionType) { - (struct __CFArray *)copyDDResultArrayByScanningStringForURLs; @end +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, FZIDType) { + FZUnknownIDType = -1, + FZPhoneNumberBasedID = 0, + FZEmailBasedID = 1, + FZDSBasedID = 2, + FZBusinessBasedID = 3, + FZTemporaryBasedID = 4, + FZPseudonymBasedID = 5, + FZHardwareBasedID = 6, + FZSIPBasedID = 7, +}; + +@interface NSString (FezAdditions) + +- (FZIDType) _FZIDType; +- (FZIDType) _FZBestGuessFZIDType; + +- (NSString *) _IDFromFZIDType:(FZIDType)IDType; +- (NSString *) _stripFZIDPrefix; + +- (NSString *) _URIFromFZIDType:(FZIDType)IDType; +- (NSString *) _bestGuessURI; + +@end + +extern BOOL IMStringIsEmail( NSString * string ); +extern BOOL IMStringIsPhoneNumber( NSString * string ); +extern BOOL IMStringIsBusinessID( NSString * string ); +extern BOOL IMStringIsTemporaryID( NSString * string ); +extern BOOL IMStringIsPseudonymID( NSString * string ); +extern BOOL IMStringIsHardwareID( NSString * string ); +extern BOOL IMStringIsSIPID( NSString * string ); + +enum { + IMChatServiceForSendingAvailabilityErrorNone = 0, + IMChatServiceForSendingAvailabilityErrorTooManyRecipients = 1, + IMChatServiceForSendingAvailabilityErrorIMessageRequired = 2, + IMChatServiceForSendingAvailabilityErrorMMSRequired = 3, + IMChatServiceForSendingAvailabilityErrorNoAvailableServices = 4, + IMChatServiceForSendingAvailabilityErrorSpamFiltered = 5, +}; +typedef int8_t IMChatServiceForSendingAvailabilityError; + +extern void IMChatCalculateServiceForSendingNewComposeMaybeForce(NSArray *canonicalIDSAddresses, + NSString* _Nullable senderLastAddressedHandle, + NSString* _Nullable senderLastAddressedSIMID, + BOOL forceMMS, + BOOL hasEmailRecipients, + BOOL lastSentMessageWasNotDelivered, + BOOL conversationWasDowngraded, + BOOL hasConversationHistory, + IMService * _Nullable previousService, + void(^ _Nonnull completion)(BOOL allAddressesiMessageCapable, + NSDictionary * _Nullable perRecipientAvailability, + BOOL checkedServer, + IMChatServiceForSendingAvailabilityError error)); + +extern NSString *IMCopyIDForPhoneNumber(NSString *phoneNumber, NSString *_Nullable countryCode, BOOL useNetworkCountryCode) NS_RETURNS_RETAINED; +extern NSString *IMCopyIDForEmailAddress(NSString *emailAddress) NS_RETURNS_RETAINED; + +NS_ASSUME_NONNULL_END diff --git a/MessagesBridge.xcodeproj/project.pbxproj b/MessagesBridge.xcodeproj/project.pbxproj index 427a335..faa70c7 100644 --- a/MessagesBridge.xcodeproj/project.pbxproj +++ b/MessagesBridge.xcodeproj/project.pbxproj @@ -94,6 +94,7 @@ CDE455A121A365AD0041F5DD /* MBIMMarkOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE455A021A365AD0041F5DD /* MBIMMarkOperation.m */; }; CDE455A421A5308D0041F5DD /* MBIMFetchAttachmentOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE455A321A5308D0041F5DD /* MBIMFetchAttachmentOperation.m */; }; CDE455A721A531ED0041F5DD /* MBIMDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = CDE455A621A531ED0041F5DD /* MBIMDataResponse.m */; }; + CDEFF9FE2CACC7A700063C52 /* MBIMResolveHandleOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CDEFF9FD2CACC7A700063C52 /* MBIMResolveHandleOperation.m */; }; CDF62335219A895D00690038 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CDF62334219A895D00690038 /* main.m */; }; CDF62339219A8A5600690038 /* MBIMBridge.h in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C4469219A4BC300F2AC00 /* MBIMBridge.h */; }; CDF6233A219A8A5600690038 /* MBIMBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C446A219A4BC300F2AC00 /* MBIMBridge.m */; }; @@ -269,6 +270,8 @@ CDE455A321A5308D0041F5DD /* MBIMFetchAttachmentOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMFetchAttachmentOperation.m; sourceTree = ""; }; CDE455A521A531ED0041F5DD /* MBIMDataResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMDataResponse.h; sourceTree = ""; }; CDE455A621A531ED0041F5DD /* MBIMDataResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMDataResponse.m; sourceTree = ""; }; + CDEFF9FC2CACC7A700063C52 /* MBIMResolveHandleOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMResolveHandleOperation.h; sourceTree = ""; }; + CDEFF9FD2CACC7A700063C52 /* MBIMResolveHandleOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMResolveHandleOperation.m; sourceTree = ""; }; CDF62332219A895D00690038 /* kordophoned */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = kordophoned; sourceTree = BUILT_PRODUCTS_DIR; }; CDF62334219A895D00690038 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -571,6 +574,8 @@ CDE455A021A365AD0041F5DD /* MBIMMarkOperation.m */, CD60205A219B623F0024D9C5 /* MBIMMessagesListOperation.h */, CD60205B219B623F0024D9C5 /* MBIMMessagesListOperation.m */, + CDEFF9FC2CACC7A700063C52 /* MBIMResolveHandleOperation.h */, + CDEFF9FD2CACC7A700063C52 /* MBIMResolveHandleOperation.m */, CD602060219B68950024D9C5 /* MBIMSendMessageOperation.h */, CD602061219B68950024D9C5 /* MBIMSendMessageOperation.m */, CD936A37289B49FC0093A1AC /* MBIMStatusOperation.h */, @@ -910,6 +915,7 @@ CD602062219B68950024D9C5 /* MBIMSendMessageOperation.m in Sources */, CD14F1A1219FE7D600E7DD22 /* MBIMUpdatePollOperation.m in Sources */, CD936A39289B49FC0093A1AC /* MBIMStatusOperation.m in Sources */, + CDEFF9FE2CACC7A700063C52 /* MBIMResolveHandleOperation.m in Sources */, CDE455A121A365AD0041F5DD /* MBIMMarkOperation.m in Sources */, CDE455A721A531ED0041F5DD /* MBIMDataResponse.m in Sources */, CD936A35289B47D60093A1AC /* MBIMVersionOperation.m in Sources */, diff --git a/kordophone/Bridge/Operations/MBIMResolveHandleOperation.h b/kordophone/Bridge/Operations/MBIMResolveHandleOperation.h new file mode 100644 index 0000000..02ecbae --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMResolveHandleOperation.h @@ -0,0 +1,17 @@ +// +// MBIMResolveHandleOperation.h +// kordophoned +// +// Created by James Magahern on 10/1/24. +// Copyright © 2024 James Magahern. All rights reserved. +// + +#import "MBIMBridgeOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MBIMResolveHandleOperation : MBIMBridgeOperation + +@end + +NS_ASSUME_NONNULL_END diff --git a/kordophone/Bridge/Operations/MBIMResolveHandleOperation.m b/kordophone/Bridge/Operations/MBIMResolveHandleOperation.m new file mode 100644 index 0000000..a4f2609 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMResolveHandleOperation.m @@ -0,0 +1,134 @@ +// +// MBIMResolveHandleOperation.m +// kordophoned +// +// Created by James Magahern on 10/1/24. +// Copyright © 2024 James Magahern. All rights reserved. +// + +#import "MBIMResolveHandleOperation.h" + +#import "IMCore_ClassDump.h" +#import "IMMessageItem+Encoded.h" + +/* + # Response: + Dictionary { + "resolvedHandle": + "status": valid | invalid | unknown, + "existingChat": chatGUID | null + } + + # IMHandle: + Dictionary { + "id" : resolvedID + "name" : fullName | null + } + */ + +@interface NSNumber (IDSStatusUtility) +- (NSString *)_idsStatusDescription; +@end + +@implementation NSNumber (IDSStatusUtility) +- (NSString *)_idsStatusDescription +{ + switch ([self unsignedIntegerValue]) { + case 1: return @"valid"; + case 2: return @"invalid"; + default: + return @"unknown"; + } +} +@end + +@interface IMHandle (Encoded) +- (NSDictionary *)mbim_dictionaryRepresentation; +@end + +@implementation IMHandle (Encoded) +- (NSDictionary *)mbim_dictionaryRepresentation +{ + return @{ + @"id" : [self ID], + @"name" : [self name] ?: [NSNull null], + }; +} +@end + +@implementation MBIMResolveHandleOperation + ++ (void)load { [super load]; } + ++ (NSString *)endpointName +{ + return @"resolveHandle"; +} + +- (void)main +{ + NSString *specifiedIdentifier = [[self valueForQueryItemWithName:@"id"] _stripFZIDPrefix]; + if (!specifiedIdentifier) { + MBIMLogError(@"No handle ID provided."); + HTTPErrorResponse *response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; + self.serverCompletionBlock(response); + + return; + } + + if (IMStringIsPhoneNumber(specifiedIdentifier)) { + // Phone numbers will require a country code guess here. + // Passing nil, I presume, will make some kind of guess for the country code (useNetworkCountryCode). + specifiedIdentifier = IMCopyIDForPhoneNumber(specifiedIdentifier, nil, YES); + } + + NSString *canonicalAddress = [specifiedIdentifier _bestGuessURI]; + + IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; + IMHandle *loginHandle = [iMessageAccount loginIMHandle]; + + NSString *lastAddressedHandle = [[[loginHandle ID] _stripFZIDPrefix] _bestGuessURI]; + IMChatCalculateServiceForSendingNewComposeMaybeForce(@[ canonicalAddress ], lastAddressedHandle, nil, NO, IMStringIsEmail(canonicalAddress), NO, NO, NO, [iMessageAccount service], ^(BOOL allAddressesiMessageCapable, NSDictionary * _Nullable perRecipientAvailability, BOOL checkedServer, IMChatServiceForSendingAvailabilityError error) + { + NSError *encodingError = nil; + NSObject *response = nil; + + do { + // Assume we have returned just one key here. + NSString *resolvedHandleID = [[perRecipientAvailability allKeys] firstObject]; + if (!resolvedHandleID) { + MBIMLogError(@"Unexpected missing handle id"); + response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; + break; + } + + IMHandle *resolvedHandle = [iMessageAccount imHandleWithID:resolvedHandleID]; + if (!resolvedHandle) { + MBIMLogError(@"Couldn't resolve handle"); + response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; + break; + } + + NSString *handleStatus = [[perRecipientAvailability objectForKey:resolvedHandleID] _idsStatusDescription]; + + IMChat *existingChat = [[IMChatRegistry sharedInstance] existingChatForIMHandle:resolvedHandle]; + + NSDictionary *responseDict = @{ + @"resolvedHandle" : [resolvedHandle mbim_dictionaryRepresentation], + @"status" : handleStatus, + @"existingChat" : [existingChat guid] ?: [NSNull null], + }; + + NSData *data = [NSJSONSerialization dataWithJSONObject:responseDict options:0 error:&encodingError]; + if (encodingError) { + response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; + } else { + response = [[MBIMDataResponse alloc] initWithData:data contentType:@"application/json"]; + } + } while (0); + + self.serverCompletionBlock(response); + }); +} + +@end