NCAPIController.m 168 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "NCAPIController.h"
  6. @import NextcloudKit;
  7. #import <SDWebImage/SDWebImageManager.h>
  8. #import <SDWebImageSVGKitDefine.h>
  9. #import <SDWebImage/SDImageCache.h>
  10. #import "CCCertificate.h"
  11. #import "NCAppBranding.h"
  12. #import "NCDatabaseManager.h"
  13. #import "NCKeyChainController.h"
  14. #import "NotificationCenterNotifications.h"
  15. #import "NextcloudTalk-Swift.h"
  16. NSInteger const APIv1 = 1;
  17. NSInteger const APIv2 = 2;
  18. NSInteger const APIv3 = 3;
  19. NSInteger const APIv4 = 4;
  20. NSString * const kDavEndpoint = @"/remote.php/dav";
  21. NSString * const kNCOCSAPIVersion = @"/ocs/v2.php";
  22. NSString * const kNCSpreedAPIVersionBase = @"/apps/spreed/api/v";
  23. NSInteger const kReceivedChatMessagesLimit = 100;
  24. @interface NCAPIController () <NSURLSessionTaskDelegate, NSURLSessionDelegate, NKCommonDelegate>
  25. @property (nonatomic, strong) NCAPISessionManager *defaultAPISessionManager;
  26. @end
  27. @implementation NCAPIController
  28. + (NCAPIController *)sharedInstance
  29. {
  30. static dispatch_once_t once;
  31. static NCAPIController *sharedInstance;
  32. dispatch_once(&once, ^{
  33. sharedInstance = [[self alloc] init];
  34. });
  35. return sharedInstance;
  36. }
  37. - (id)init
  38. {
  39. self = [super init];
  40. if (self) {
  41. [self initSessionManagers];
  42. [self initImageDownloaders];
  43. }
  44. return self;
  45. }
  46. - (void)initSessionManagers
  47. {
  48. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  49. configuration.HTTPCookieStorage = nil;
  50. _defaultAPISessionManager = [[NCAPISessionManager alloc] initWithConfiguration:configuration];
  51. _apiSessionManagers = [NSMutableDictionary new];
  52. _longPollingApiSessionManagers = [NSMutableDictionary new];
  53. for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) {
  54. [self createAPISessionManagerForAccount:account];
  55. }
  56. }
  57. - (void)createAPISessionManagerForAccount:(TalkAccount *)account
  58. {
  59. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  60. NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:account.accountId];
  61. configuration.HTTPCookieStorage = cookieStorage;
  62. NCAPISessionManager *apiSessionManager = [[NCAPISessionManager alloc] initWithConfiguration:configuration];
  63. [apiSessionManager.requestSerializer setValue:[self authHeaderForAccount:account] forHTTPHeaderField:@"Authorization"];
  64. // As we can run max. 30s in the background, the default timeout should be lower than 30 to avoid being killed by the OS
  65. [apiSessionManager.requestSerializer setTimeoutInterval:25];
  66. [_apiSessionManagers setObject:apiSessionManager forKey:account.accountId];
  67. configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  68. cookieStorage = [NSHTTPCookieStorage sharedCookieStorageForGroupContainerIdentifier:account.accountId];
  69. configuration.HTTPCookieStorage = cookieStorage;
  70. apiSessionManager = [[NCAPISessionManager alloc] initWithConfiguration:configuration];
  71. [apiSessionManager.requestSerializer setValue:[self authHeaderForAccount:account] forHTTPHeaderField:@"Authorization"];
  72. [_longPollingApiSessionManagers setObject:apiSessionManager forKey:account.accountId];
  73. }
  74. - (void)setupNCCommunicationForAccount:(TalkAccount *)account
  75. {
  76. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  77. NSString *userToken = [[NCKeyChainController sharedInstance] tokenForAccountId:account.accountId];
  78. NSString *userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (iOS) Nextcloud-Talk v%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]];
  79. [[NextcloudKit shared] setupWithAccount:account.accountId user:account.user userId:account.userId password:userToken urlBase:account.server userAgent:userAgent nextcloudVersion:serverCapabilities.versionMajor delegate:self];
  80. }
  81. - (void)initImageDownloaders
  82. {
  83. _imageDownloader = [[AFImageDownloader alloc]
  84. initWithSessionManager:[NCImageSessionManager shared]
  85. downloadPrioritization:AFImageDownloadPrioritizationFIFO
  86. maximumActiveDownloads:4
  87. imageCache:[[AFAutoPurgingImageCache alloc] init]];
  88. _imageDownloaderNoCache = [[AFImageDownloader alloc]
  89. initWithSessionManager:[NCImageSessionManager shared]
  90. downloadPrioritization:AFImageDownloadPrioritizationFIFO
  91. maximumActiveDownloads:4
  92. imageCache:nil];
  93. // By default SDWebImageDownloader defaults to 6 concurrent downloads (see SDWebImageDownloaderConfig)
  94. // Make sure we support download SVGs with SDImageDownloader
  95. [[SDImageCodersManager sharedManager] addCoder:[SDImageSVGKCoder sharedCoder]];
  96. // Make sure we support self-signed certificates we trusted before
  97. [[SDWebImageDownloader sharedDownloader].config setOperationClass:[NCWebImageDownloaderOperation class]];
  98. // Try to remove legacy avatar cache in app group
  99. NSURL *legacyCacheURL = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupIdentifier] URLByAppendingPathComponent:@"AvatarCache"];
  100. if (legacyCacheURL != nil) {
  101. [[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:nil];
  102. }
  103. // Limit the cache size to 100 MB and prevent uploading to iCloud
  104. // Don't set the path to an app group in order to prevent crashes
  105. [SDImageCache sharedImageCache].config.shouldDisableiCloud = YES;
  106. [SDImageCache sharedImageCache].config.maxDiskSize = 100 * 1024 * 1024;
  107. NSString *userAgent = [NSString stringWithFormat:@"Mozilla/5.0 (iOS) Nextcloud-Talk v%@",
  108. [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]];
  109. [[SDWebImageDownloader sharedDownloader] setValue:userAgent forHTTPHeaderField:@"User-Agent"];
  110. }
  111. - (NSString *)authHeaderForAccount:(TalkAccount *)account
  112. {
  113. NSString *userTokenString = [NSString stringWithFormat:@"%@:%@", account.user, [[NCKeyChainController sharedInstance] tokenForAccountId:account.accountId]];
  114. NSData *data = [userTokenString dataUsingEncoding:NSUTF8StringEncoding];
  115. NSString *base64Encoded = [data base64EncodedStringWithOptions:0];
  116. return [[NSString alloc]initWithFormat:@"Basic %@",base64Encoded];
  117. }
  118. - (SDWebImageDownloaderRequestModifier *)getRequestModifierForAccount:(TalkAccount *)account
  119. {
  120. NSMutableDictionary *headerDictionary = [[NSMutableDictionary alloc] init];
  121. [headerDictionary setObject:[self authHeaderForAccount:account] forKey:@"Authorization"];
  122. return [[SDWebImageDownloaderRequestModifier alloc] initWithHeaders:headerDictionary];
  123. }
  124. - (NSInteger)conversationAPIVersionForAccount:(TalkAccount *)account
  125. {
  126. NSInteger conversationAPIVersion = APIv1;
  127. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityChatReadStatus forAccountId:account.accountId]) {
  128. conversationAPIVersion = APIv3;
  129. }
  130. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityConversationV4 forAccountId:account.accountId]) {
  131. conversationAPIVersion = APIv4;
  132. }
  133. return conversationAPIVersion;
  134. }
  135. - (NSInteger)callAPIVersionForAccount:(TalkAccount *)account
  136. {
  137. return [self conversationAPIVersionForAccount:account];
  138. }
  139. - (NSInteger)chatAPIVersionForAccount:(TalkAccount *)account
  140. {
  141. return APIv1;
  142. }
  143. - (NSInteger)reactionsAPIVersionForAccount:(TalkAccount *)account
  144. {
  145. return APIv1;
  146. }
  147. - (NSInteger)pollsAPIVersionForAccount:(TalkAccount *)account
  148. {
  149. return APIv1;
  150. }
  151. - (NSInteger)breakoutRoomsAPIVersionForAccount:(TalkAccount *)account
  152. {
  153. return APIv1;
  154. }
  155. - (NSInteger)federationAPIVersionForAccount:(TalkAccount *)account
  156. {
  157. return APIv1;
  158. }
  159. - (NSInteger)banAPIVersionForAccount:(TalkAccount *)account
  160. {
  161. return APIv1;
  162. }
  163. - (NSInteger)signalingAPIVersionForAccount:(TalkAccount *)account
  164. {
  165. NSInteger signalingAPIVersion = APIv1;
  166. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilitySIPSupport forAccountId:account.accountId]) {
  167. signalingAPIVersion = APIv2;
  168. }
  169. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilitySignalingV3 forAccountId:account.accountId]) {
  170. signalingAPIVersion = APIv3;
  171. }
  172. return signalingAPIVersion;
  173. }
  174. - (NSString *)filesPathForAccount:(TalkAccount *)account
  175. {
  176. return [NSString stringWithFormat:@"%@/files/%@", kDavEndpoint, account.userId];
  177. }
  178. - (NSString *)getRequestURLForEndpoint:(NSString *)endpoint withAPIVersion:(NSInteger)apiVersion forAccount:(TalkAccount *)account
  179. {
  180. return [NSString stringWithFormat:@"%@%@%@%ld/%@", account.server, kNCOCSAPIVersion, kNCSpreedAPIVersionBase, (long)apiVersion, endpoint];
  181. }
  182. #pragma mark - Contacts Controller
  183. - (NSURLSessionDataTask *)searchContactsForAccount:(TalkAccount *)account withPhoneNumbers:(NSDictionary *)phoneNumbers andCompletionBlock:(GetContactsWithPhoneNumbersCompletionBlock)block
  184. {
  185. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/cloud/users/search/by-phone", account.server];
  186. NSString *location = [[NSLocale currentLocale] countryCode];
  187. NSDictionary *parameters = @{@"location" : location,
  188. @"search" : phoneNumbers};
  189. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  190. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  191. NSDictionary *responseContacts = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  192. if (block) {
  193. block(responseContacts, nil);
  194. }
  195. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  196. // NSInteger statusCode = [self getResponseStatusCode:task.response];
  197. // Ignore status code for now https://github.com/nextcloud/server/pull/26679
  198. // [self checkResponseStatusCode:statusCode forAccount:account];
  199. if (block) {
  200. block(nil, error);
  201. }
  202. }];
  203. return task;
  204. }
  205. - (NSURLSessionDataTask *)getContactsForAccount:(TalkAccount *)account forRoom:(NSString *)room groupRoom:(BOOL)groupRoom withSearchParam:(NSString *)search andCompletionBlock:(GetContactsCompletionBlock)block
  206. {
  207. NSMutableArray *shareTypes = [[NSMutableArray alloc] initWithObjects:@(NCShareTypeUser), nil];
  208. if (groupRoom && [[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityInviteGroupsAndMails]) {
  209. [shareTypes addObject:@(NCShareTypeGroup)];
  210. [shareTypes addObject:@(NCShareTypeEmail)];
  211. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityCirclesSupport]) {
  212. [shareTypes addObject:@(NCShareTypeCircle)];
  213. }
  214. if ([[NCDatabaseManager sharedInstance] serverCanInviteFederatedUsersforAccountId:account.accountId]) {
  215. [shareTypes addObject:@(NCShareTypeRemote)];
  216. }
  217. }
  218. NSString *URLString = [NSString stringWithFormat:@"%@%@/core/autocomplete/get", account.server, kNCOCSAPIVersion];
  219. NSDictionary *parameters = @{@"format" : @"json",
  220. @"search" : search ? search : @"",
  221. @"limit" : @"50",
  222. @"itemType" : @"call",
  223. @"itemId" : room ? room : @"new",
  224. @"shareTypes" : shareTypes
  225. };
  226. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  227. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  228. NSArray *responseContacts = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  229. NSMutableArray *users = [[NSMutableArray alloc] initWithCapacity:responseContacts.count];
  230. for (NSDictionary *user in responseContacts) {
  231. NCUser *ncUser = [NCUser userWithDictionary:user];
  232. TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
  233. if (ncUser && !([ncUser.userId isEqualToString:activeAccount.userId] && [ncUser.source isEqualToString:kParticipantTypeUser])) {
  234. [users addObject:ncUser];
  235. }
  236. }
  237. NSMutableDictionary *indexedContacts = [NCUser indexedUsersFromUsersArray:users];
  238. NSArray *indexes = [[indexedContacts allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
  239. if (block) {
  240. block(indexes, indexedContacts, users, nil);
  241. }
  242. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  243. NSInteger statusCode = [self getResponseStatusCode:task.response];
  244. [self checkResponseStatusCode:statusCode forAccount:account];
  245. if (block) {
  246. block(nil, nil, nil, error);
  247. }
  248. }];
  249. return task;
  250. }
  251. #pragma mark - Rooms Controller
  252. - (NSURLSessionDataTask *)makeRoomPublic:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MakeRoomPublicCompletionBlock)block
  253. {
  254. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  255. NSString *endpoint = [NSString stringWithFormat:@"room/%@/public", encodedToken];
  256. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  257. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  258. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  259. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  260. if (block) {
  261. block(nil);
  262. }
  263. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  264. NSInteger statusCode = [self getResponseStatusCode:task.response];
  265. [self checkResponseStatusCode:statusCode forAccount:account];
  266. if (block) {
  267. block(error);
  268. }
  269. }];
  270. return task;
  271. }
  272. - (NSURLSessionDataTask *)makeRoomPrivate:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MakeRoomPrivateCompletionBlock)block
  273. {
  274. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  275. NSString *endpoint = [NSString stringWithFormat:@"room/%@/public", encodedToken];
  276. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  277. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  278. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  279. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  280. if (block) {
  281. block(nil);
  282. }
  283. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  284. NSInteger statusCode = [self getResponseStatusCode:task.response];
  285. [self checkResponseStatusCode:statusCode forAccount:account];
  286. if (block) {
  287. block(error);
  288. }
  289. }];
  290. return task;
  291. }
  292. - (NSURLSessionDataTask *)deleteRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(DeleteRoomCompletionBlock)block
  293. {
  294. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  295. NSString *endpoint = [NSString stringWithFormat:@"room/%@", encodedToken];
  296. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  297. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  298. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  299. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  300. if (block) {
  301. block(nil);
  302. }
  303. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  304. NSInteger statusCode = [self getResponseStatusCode:task.response];
  305. [self checkResponseStatusCode:statusCode forAccount:account];
  306. if (block) {
  307. block(error);
  308. }
  309. }];
  310. return task;
  311. }
  312. - (NSURLSessionDataTask *)setPassword:(NSString *)password toRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SetPasswordCompletionBlock)block
  313. {
  314. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  315. NSString *endpoint = [NSString stringWithFormat:@"room/%@/password", encodedToken];
  316. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  317. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  318. NSDictionary *parameters = @{@"password" : password};
  319. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  320. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  321. if (block) {
  322. block(nil, nil);
  323. }
  324. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  325. NSInteger statusCode = [self getResponseStatusCode:task.response];
  326. [self checkResponseStatusCode:statusCode forAccount:account];
  327. if (block) {
  328. if (statusCode == 400) {
  329. NSData *errorData = (NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
  330. NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:errorData
  331. options:0
  332. error:&error];
  333. // message is already translated server-side
  334. NSString *errorDescription = [[[jsonData objectForKey:@"ocs"] objectForKey:@"data"] objectForKey:@"message"];
  335. block(error, errorDescription);
  336. } else {
  337. block(error, nil);
  338. }
  339. }
  340. }];
  341. return task;
  342. }
  343. - (NSURLSessionDataTask *)joinRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(JoinRoomCompletionBlock)block
  344. {
  345. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  346. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants/active", encodedToken];
  347. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  348. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  349. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  350. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  351. NSDictionary *dataDictionary = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  352. NSString *sessionId = [dataDictionary objectForKey:@"sessionId"];
  353. NSHTTPURLResponse *response = ((NSHTTPURLResponse *)[task response]);
  354. NSDictionary *headers = [self getResponseHeaders:response];
  355. [self checkProxyResponseHeaders:headers forAccount:account forRoom:token];
  356. // Room object is returned only since Talk 11
  357. NCRoom *room = nil;
  358. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityListableRooms forAccountId:account.accountId]) {
  359. room = [NCRoom roomWithDictionary:dataDictionary andAccountId:account.accountId];
  360. }
  361. if (block) {
  362. block(sessionId, room, nil, 0, nil);
  363. }
  364. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  365. NSInteger statusCode = [self getResponseStatusCode:task.response];
  366. [self checkResponseStatusCode:statusCode forAccount:account];
  367. NSDictionary *errorDict = [[[self getFailureResponseObjectFromError:error] objectForKey:@"ocs"] objectForKey:@"data"];
  368. NSString *statusReason = nil;
  369. // Depending on the error, an empty array instead of a dictionary is returned by the server
  370. if (errorDict && [errorDict isKindOfClass:[NSDictionary class]]) {
  371. statusReason = [errorDict objectForKey:@"error"];
  372. }
  373. if (block) {
  374. block(nil, nil, error, statusCode, statusReason);
  375. }
  376. }];
  377. return task;
  378. }
  379. - (NSURLSessionDataTask *)exitRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ExitRoomCompletionBlock)block
  380. {
  381. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  382. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants/active", encodedToken];
  383. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  384. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  385. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  386. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  387. if (block) {
  388. block(nil);
  389. }
  390. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  391. NSInteger statusCode = [self getResponseStatusCode:task.response];
  392. [self checkResponseStatusCode:statusCode forAccount:account];
  393. if (block) {
  394. block(error);
  395. }
  396. }];
  397. return task;
  398. }
  399. - (NSURLSessionDataTask *)addRoomToFavorites:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(FavoriteRoomCompletionBlock)block
  400. {
  401. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  402. NSString *endpoint = [NSString stringWithFormat:@"room/%@/favorite", encodedToken];
  403. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  404. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  405. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  406. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  407. if (block) {
  408. block(nil);
  409. }
  410. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  411. NSInteger statusCode = [self getResponseStatusCode:task.response];
  412. [self checkResponseStatusCode:statusCode forAccount:account];
  413. if (block) {
  414. block(error);
  415. }
  416. }];
  417. return task;
  418. }
  419. - (NSURLSessionDataTask *)removeRoomFromFavorites:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(FavoriteRoomCompletionBlock)block
  420. {
  421. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  422. NSString *endpoint = [NSString stringWithFormat:@"room/%@/favorite", encodedToken];
  423. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  424. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  425. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  426. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  427. if (block) {
  428. block(nil);
  429. }
  430. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  431. NSInteger statusCode = [self getResponseStatusCode:task.response];
  432. [self checkResponseStatusCode:statusCode forAccount:account];
  433. if (block) {
  434. block(error);
  435. }
  436. }];
  437. return task;
  438. }
  439. - (NSURLSessionDataTask *)setNotificationLevel:(NCRoomNotificationLevel)level forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(NotificationLevelCompletionBlock)block
  440. {
  441. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  442. NSString *endpoint = [NSString stringWithFormat:@"room/%@/notify", encodedToken];
  443. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  444. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  445. NSDictionary *parameters = @{@"level" : @(level)};
  446. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  447. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  448. if (block) {
  449. block(nil);
  450. }
  451. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  452. NSInteger statusCode = [self getResponseStatusCode:task.response];
  453. [self checkResponseStatusCode:statusCode forAccount:account];
  454. if (block) {
  455. block(error);
  456. }
  457. }];
  458. return task;
  459. }
  460. - (NSURLSessionDataTask *)setCallNotificationEnabled:(BOOL)enabled forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(NotificationLevelCompletionBlock)block
  461. {
  462. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  463. NSString *endpoint = [NSString stringWithFormat:@"room/%@/notify-calls", encodedToken];
  464. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  465. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  466. NSDictionary *parameters = @{@"level" : @(enabled)};
  467. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  468. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  469. if (block) {
  470. block(nil);
  471. }
  472. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  473. NSInteger statusCode = [self getResponseStatusCode:task.response];
  474. [self checkResponseStatusCode:statusCode forAccount:account];
  475. if (block) {
  476. block(error);
  477. }
  478. }];
  479. return task;
  480. }
  481. - (NSURLSessionDataTask *)setReadOnlyState:(NCRoomReadOnlyState)state forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ReadOnlyCompletionBlock)block
  482. {
  483. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  484. NSString *endpoint = [NSString stringWithFormat:@"room/%@/read-only", encodedToken];
  485. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  486. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  487. NSDictionary *parameters = @{@"state" : @(state)};
  488. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  489. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  490. if (block) {
  491. block(nil);
  492. }
  493. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  494. NSInteger statusCode = [self getResponseStatusCode:task.response];
  495. [self checkResponseStatusCode:statusCode forAccount:account];
  496. if (block) {
  497. block(error);
  498. }
  499. }];
  500. return task;
  501. }
  502. - (NSURLSessionDataTask *)setLobbyState:(NCRoomLobbyState)state withTimer:(NSInteger)timer forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SetLobbyStateCompletionBlock)block
  503. {
  504. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  505. NSString *endpoint = [NSString stringWithFormat:@"room/%@/webinary/lobby", encodedToken];
  506. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  507. if (conversationAPIVersion >= APIv4) {
  508. endpoint = [NSString stringWithFormat:@"room/%@/webinar/lobby", encodedToken];
  509. }
  510. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  511. NSMutableDictionary *parameters = [NSMutableDictionary new];
  512. [parameters setObject:@(state) forKey:@"state"];
  513. if (timer > 0) {
  514. [parameters setObject:@(timer) forKey:@"timer"];
  515. }
  516. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  517. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  518. if (block) {
  519. block(nil);
  520. }
  521. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  522. NSInteger statusCode = [self getResponseStatusCode:task.response];
  523. [self checkResponseStatusCode:statusCode forAccount:account];
  524. if (block) {
  525. block(error);
  526. }
  527. }];
  528. return task;
  529. }
  530. - (NSURLSessionDataTask *)setSIPState:(NCRoomSIPState)state forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SetSIPStateCompletionBlock)block
  531. {
  532. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  533. NSString *endpoint = [NSString stringWithFormat:@"room/%@/webinar/sip", encodedToken];
  534. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  535. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  536. NSDictionary *parameters = @{@"state" : @(state)};
  537. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  538. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  539. if (block) {
  540. block(nil);
  541. }
  542. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  543. NSInteger statusCode = [self getResponseStatusCode:task.response];
  544. [self checkResponseStatusCode:statusCode forAccount:account];
  545. if (block) {
  546. block(error);
  547. }
  548. }];
  549. return task;
  550. }
  551. - (NSURLSessionDataTask *)setListableScope:(NCRoomListableScope)scope forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ListableCompletionBlock)block
  552. {
  553. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  554. NSString *endpoint = [NSString stringWithFormat:@"room/%@/listable", encodedToken];
  555. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  556. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  557. NSDictionary *parameters = @{@"scope" : @(scope)};
  558. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  559. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  560. if (block) {
  561. block(nil);
  562. }
  563. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  564. NSInteger statusCode = [self getResponseStatusCode:task.response];
  565. [self checkResponseStatusCode:statusCode forAccount:account];
  566. if (block) {
  567. block(error);
  568. }
  569. }];
  570. return task;
  571. }
  572. - (NSURLSessionDataTask *)setMessageExpiration:(NCMessageExpiration)messageExpiration forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MessageExpirationCompletionBlock)block
  573. {
  574. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  575. NSString *endpoint = [NSString stringWithFormat:@"room/%@/message-expiration", encodedToken];
  576. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  577. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  578. NSDictionary *parameters = @{@"seconds" : @(messageExpiration)};
  579. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  580. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  581. if (block) {
  582. block(nil);
  583. }
  584. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  585. NSInteger statusCode = [self getResponseStatusCode:task.response];
  586. [self checkResponseStatusCode:statusCode forAccount:account];
  587. if (block) {
  588. block(error);
  589. }
  590. }];
  591. return task;
  592. }
  593. #pragma mark - Breakout Rooms Controller
  594. - (NSURLSessionDataTask *)requestAssistanceInRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(RequestAssistanceCompletionBlock)block
  595. {
  596. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  597. NSString *endpoint = [NSString stringWithFormat:@"breakout-rooms/%@/request-assistance", encodedToken];
  598. NSInteger breakoutRoomsAPIVersion = [self breakoutRoomsAPIVersionForAccount:account];
  599. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:breakoutRoomsAPIVersion forAccount:account];
  600. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  601. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  602. if (block) {
  603. block(nil);
  604. }
  605. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  606. NSInteger statusCode = [self getResponseStatusCode:task.response];
  607. [self checkResponseStatusCode:statusCode forAccount:account];
  608. if (block) {
  609. block(error);
  610. }
  611. }];
  612. return task;
  613. }
  614. - (NSURLSessionDataTask *)stopRequestingAssistanceInRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(RequestAssistanceCompletionBlock)block
  615. {
  616. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  617. NSString *endpoint = [NSString stringWithFormat:@"breakout-rooms/%@/request-assistance", encodedToken];
  618. NSInteger breakoutRoomsAPIVersion = [self breakoutRoomsAPIVersionForAccount:account];
  619. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:breakoutRoomsAPIVersion forAccount:account];
  620. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  621. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  622. if (block) {
  623. block(nil);
  624. }
  625. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  626. NSInteger statusCode = [self getResponseStatusCode:task.response];
  627. [self checkResponseStatusCode:statusCode forAccount:account];
  628. if (block) {
  629. block(error);
  630. }
  631. }];
  632. return task;
  633. }
  634. #pragma mark - Participants Controller
  635. - (NSURLSessionDataTask *)getParticipantsFromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(GetParticipantsFromRoomCompletionBlock)block
  636. {
  637. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  638. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants", encodedToken];
  639. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  640. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  641. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  642. if (serverCapabilities.userStatus) {
  643. URLString = [URLString stringByAppendingString:@"?includeStatus=true"];
  644. }
  645. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  646. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  647. NSArray *responseParticipants = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  648. NSMutableArray *participants = [[NSMutableArray alloc] initWithCapacity:responseParticipants.count];
  649. for (NSDictionary *participantDict in responseParticipants) {
  650. NCRoomParticipant *participant = [NCRoomParticipant participantWithDictionary:participantDict];
  651. [participants addObject:participant];
  652. }
  653. // Sort participants by:
  654. // - Participants before groups
  655. // - Online status
  656. // - In call
  657. // - Type (moderators before normal participants)
  658. // - Alphabetic
  659. NSSortDescriptor *alphabeticSorting = [[NSSortDescriptor alloc] initWithKey:@"displayName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
  660. NSSortDescriptor *customSorting = [NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES comparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
  661. NCRoomParticipant *first = (NCRoomParticipant*)obj1;
  662. NCRoomParticipant *second = (NCRoomParticipant*)obj2;
  663. BOOL group1 = first.isGroup;
  664. BOOL group2 = second.isGroup;
  665. if (group1 != group2) {
  666. return group1 - group2;
  667. }
  668. BOOL online1 = !first.isOffline;
  669. BOOL online2 = !second.isOffline;
  670. if (online1 != online2) {
  671. return online2 - online1;
  672. }
  673. BOOL inCall1 = first.inCall > 0;
  674. BOOL inCall2 = second.inCall > 0;
  675. if (inCall1 != inCall2) {
  676. return inCall2 - inCall1;
  677. }
  678. BOOL moderator1 = first.canModerate;
  679. BOOL moderator2 = second.canModerate;
  680. if (moderator1 != moderator2) {
  681. return moderator2 - moderator1;
  682. }
  683. return NSOrderedSame;
  684. }];
  685. NSArray *descriptors = [NSArray arrayWithObjects:customSorting, alphabeticSorting, nil];
  686. [participants sortUsingDescriptors:descriptors];
  687. if (block) {
  688. block(participants, nil);
  689. }
  690. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  691. NSInteger statusCode = [self getResponseStatusCode:task.response];
  692. [self checkResponseStatusCode:statusCode forAccount:account];
  693. if (block) {
  694. block(nil, error);
  695. }
  696. }];
  697. return task;
  698. }
  699. - (NSURLSessionDataTask *)addParticipant:(NSString *)participant ofType:(NSString *)type toRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  700. {
  701. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  702. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants", encodedToken];
  703. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  704. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  705. NSMutableDictionary *parameters = [NSMutableDictionary new];
  706. [parameters setObject:participant forKey:@"newParticipant"];
  707. if (type && ![type isEqualToString:@""]) {
  708. [parameters setObject:type forKey:@"source"];
  709. }
  710. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  711. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  712. if (block) {
  713. block(nil);
  714. }
  715. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  716. NSInteger statusCode = [self getResponseStatusCode:task.response];
  717. [self checkResponseStatusCode:statusCode forAccount:account];
  718. if (block) {
  719. block(error);
  720. }
  721. }];
  722. return task;
  723. }
  724. - (NSURLSessionDataTask *)removeAttendee:(NSInteger)attendeeId fromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  725. {
  726. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  727. NSString *endpoint = [NSString stringWithFormat:@"room/%@/attendees", encodedToken];
  728. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  729. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  730. NSDictionary *parameters = @{@"attendeeId" : @(attendeeId)};
  731. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  732. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  733. if (block) {
  734. block(nil);
  735. }
  736. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  737. NSInteger statusCode = [self getResponseStatusCode:task.response];
  738. [self checkResponseStatusCode:statusCode forAccount:account];
  739. if (block) {
  740. block(error);
  741. }
  742. }];
  743. return task;
  744. }
  745. - (NSURLSessionDataTask *)removeParticipant:(NSString *)user fromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  746. {
  747. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  748. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants", encodedToken];
  749. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  750. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  751. NSDictionary *parameters = @{@"participant" : user};
  752. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  753. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  754. if (block) {
  755. block(nil);
  756. }
  757. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  758. NSInteger statusCode = [self getResponseStatusCode:task.response];
  759. [self checkResponseStatusCode:statusCode forAccount:account];
  760. if (block) {
  761. block(error);
  762. }
  763. }];
  764. return task;
  765. }
  766. - (NSURLSessionDataTask *)removeGuest:(NSString *)guest fromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  767. {
  768. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  769. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants/guests", encodedToken];
  770. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  771. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  772. NSDictionary *parameters = @{@"participant" : guest};
  773. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  774. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  775. if (block) {
  776. block(nil);
  777. }
  778. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  779. NSInteger statusCode = [self getResponseStatusCode:task.response];
  780. [self checkResponseStatusCode:statusCode forAccount:account];
  781. if (block) {
  782. block(error);
  783. }
  784. }];
  785. return task;
  786. }
  787. - (NSURLSessionDataTask *)removeSelfFromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(LeaveRoomCompletionBlock)block
  788. {
  789. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  790. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants/self", encodedToken];
  791. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  792. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  793. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  794. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  795. if (block) {
  796. block(0, nil);
  797. }
  798. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  799. NSInteger statusCode = [self getResponseStatusCode:task.response];
  800. [self checkResponseStatusCode:statusCode forAccount:account];
  801. if (block) {
  802. block(statusCode, error);
  803. }
  804. }];
  805. return task;
  806. }
  807. - (NSURLSessionDataTask *)promoteParticipant:(NSString *)user toModeratorOfRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  808. {
  809. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  810. NSString *endpoint = [NSString stringWithFormat:@"room/%@/moderators", encodedToken];
  811. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  812. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  813. NSDictionary *parameters = @{@"participant" : user};
  814. if (conversationAPIVersion >= APIv3) {
  815. parameters = @{@"attendeeId" : user};
  816. }
  817. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  818. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  819. if (block) {
  820. block(nil);
  821. }
  822. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  823. NSInteger statusCode = [self getResponseStatusCode:task.response];
  824. [self checkResponseStatusCode:statusCode forAccount:account];
  825. if (block) {
  826. block(error);
  827. }
  828. }];
  829. return task;
  830. }
  831. - (NSURLSessionDataTask *)demoteModerator:(NSString *)moderator toParticipantOfRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  832. {
  833. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  834. NSString *endpoint = [NSString stringWithFormat:@"room/%@/moderators", encodedToken];
  835. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  836. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  837. NSDictionary *parameters = @{@"participant" : moderator};
  838. if (conversationAPIVersion >= APIv3) {
  839. parameters = @{@"attendeeId" : moderator};
  840. }
  841. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  842. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  843. if (block) {
  844. block(nil);
  845. }
  846. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  847. NSInteger statusCode = [self getResponseStatusCode:task.response];
  848. [self checkResponseStatusCode:statusCode forAccount:account];
  849. if (block) {
  850. block(error);
  851. }
  852. }];
  853. return task;
  854. }
  855. - (NSURLSessionDataTask *)resendInvitationToParticipant:(NSString *)participant inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  856. {
  857. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  858. NSString *endpoint = [NSString stringWithFormat:@"room/%@/participants/resend-invitations", encodedToken];
  859. NSInteger conversationAPIVersion = [self conversationAPIVersionForAccount:account];
  860. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:conversationAPIVersion forAccount:account];
  861. NSDictionary *parameters = nil;
  862. if (participant) {
  863. parameters = @{@"attendeeId" : participant};
  864. }
  865. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  866. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  867. if (block) {
  868. block(nil);
  869. }
  870. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  871. NSInteger statusCode = [self getResponseStatusCode:task.response];
  872. [self checkResponseStatusCode:statusCode forAccount:account];
  873. if (block) {
  874. block(error);
  875. }
  876. }];
  877. return task;
  878. }
  879. #pragma mark - Call Controller
  880. - (NSURLSessionDataTask *)getPeersForCall:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(GetPeersForCallCompletionBlock)block
  881. {
  882. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  883. NSString *endpoint = [NSString stringWithFormat:@"call/%@", encodedToken];
  884. NSInteger callAPIVersion = [self callAPIVersionForAccount:account];
  885. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:callAPIVersion forAccount:account];
  886. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  887. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  888. NSArray *responsePeers = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  889. NSMutableArray *peers = [[NSMutableArray alloc] initWithArray:responsePeers];
  890. if (block) {
  891. block(peers, nil, 0);
  892. }
  893. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  894. NSInteger statusCode = [self getResponseStatusCode:task.response];
  895. [self checkResponseStatusCode:statusCode forAccount:account];
  896. if (block) {
  897. block(nil, error, statusCode);
  898. }
  899. }];
  900. return task;
  901. }
  902. - (NSURLSessionDataTask *)joinCall:(NSString *)token withCallFlags:(NSInteger)flags silently:(BOOL)silently recordingConsent:(BOOL)recordingConsent forAccount:(TalkAccount *)account withCompletionBlock:(JoinCallCompletionBlock)block
  903. {
  904. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  905. NSString *endpoint = [NSString stringWithFormat:@"call/%@", encodedToken];
  906. NSInteger callAPIVersion = [self callAPIVersionForAccount:account];
  907. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:callAPIVersion forAccount:account];
  908. NSMutableDictionary *parameters = [NSMutableDictionary new];
  909. [parameters setObject:@(flags) forKey:@"flags"];
  910. [parameters setObject:@(recordingConsent) forKey:@"recordingConsent"];
  911. if (silently) {
  912. [parameters setObject:@(silently) forKey:@"silent"];
  913. }
  914. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  915. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  916. if (block) {
  917. block(nil, 0);
  918. }
  919. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  920. NSInteger statusCode = [self getResponseStatusCode:task.response];
  921. [self checkResponseStatusCode:statusCode forAccount:account];
  922. if (block) {
  923. block(error, statusCode);
  924. }
  925. }];
  926. return task;
  927. }
  928. - (NSURLSessionDataTask *)leaveCall:(NSString *)token forAllParticipants:(BOOL)allParticipants forAccount:(TalkAccount *)account withCompletionBlock:(LeaveCallCompletionBlock)block
  929. {
  930. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  931. NSString *endpoint = [NSString stringWithFormat:@"call/%@", encodedToken];
  932. NSInteger callAPIVersion = [self callAPIVersionForAccount:account];
  933. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:callAPIVersion forAccount:account];
  934. NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
  935. if (allParticipants) {
  936. [parameters setObject:@(1) forKey:@"all"];
  937. }
  938. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  939. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  940. if (block) {
  941. block(nil);
  942. }
  943. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  944. NSInteger statusCode = [self getResponseStatusCode:task.response];
  945. [self checkResponseStatusCode:statusCode forAccount:account];
  946. if (block) {
  947. block(error);
  948. }
  949. }];
  950. return task;
  951. }
  952. - (NSURLSessionDataTask *)sendCallNotificationToParticipant:(NSString *)participant inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ParticipantModificationCompletionBlock)block
  953. {
  954. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  955. NSString *endpoint = [NSString stringWithFormat:@"/call/%@/ring/%@", encodedToken, participant];
  956. NSInteger callAPIVersion = [self callAPIVersionForAccount:account];
  957. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:callAPIVersion forAccount:account];
  958. NSDictionary *parameters = nil;
  959. if (participant) {
  960. parameters = @{@"attendeeId" : participant};
  961. }
  962. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  963. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  964. if (block) {
  965. block(nil);
  966. }
  967. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  968. NSInteger statusCode = [self getResponseStatusCode:task.response];
  969. [self checkResponseStatusCode:statusCode forAccount:account];
  970. if (block) {
  971. block(error);
  972. }
  973. }];
  974. return task;
  975. }
  976. #pragma mark - Chat Controller
  977. - (NSURLSessionDataTask *)receiveChatMessagesOfRoom:(NSString *)token fromLastMessageId:(NSInteger)messageId history:(BOOL)history includeLastMessage:(BOOL)include timeout:(BOOL)timeout lastCommonReadMessage:(NSInteger)lastCommonReadMessage setReadMarker:(BOOL)setReadMarker markNotificationsAsRead:(BOOL)markNotificationsAsRead forAccount:(TalkAccount *)account withCompletionBlock:(GetChatMessagesCompletionBlock)block
  978. {
  979. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  980. NSString *endpoint = [NSString stringWithFormat:@"chat/%@", encodedToken];
  981. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  982. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  983. NSDictionary *parameters = @{@"lookIntoFuture" : history ? @(0) : @(1),
  984. @"limit" : @(kReceivedChatMessagesLimit),
  985. @"timeout" : timeout ? @(30) : @(0),
  986. @"lastKnownMessageId" : @(messageId),
  987. @"lastCommonReadId" : @(lastCommonReadMessage),
  988. @"setReadMarker" : setReadMarker ? @(1) : @(0),
  989. @"includeLastKnown" : include ? @(1) : @(0),
  990. @"markNotificationsAsRead" : markNotificationsAsRead ? @(1) : @(0)};
  991. NCAPISessionManager *apiSessionManager;
  992. if (timeout) {
  993. apiSessionManager = [_longPollingApiSessionManagers objectForKey:account.accountId];
  994. } else {
  995. apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  996. }
  997. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  998. NSArray *responseMessages = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  999. // Get X-Chat-Last-Given and X-Chat-Last-Common-Read headers
  1000. NSHTTPURLResponse *response = ((NSHTTPURLResponse *)[task response]);
  1001. NSDictionary *headers = [response allHeaderFields];
  1002. NSString *lastKnowMessageHeader = [headers objectForKey:@"X-Chat-Last-Given"];
  1003. NSInteger lastKnownMessage = -1;
  1004. if (lastKnowMessageHeader) {
  1005. lastKnownMessage = [lastKnowMessageHeader integerValue];
  1006. }
  1007. NSString *lastCommonReadMessageHeader = [headers objectForKey:@"X-Chat-Last-Common-Read"];
  1008. NSInteger lastCommonReadMessage = -1;
  1009. if (lastCommonReadMessageHeader) {
  1010. lastCommonReadMessage = [lastCommonReadMessageHeader integerValue];
  1011. }
  1012. if (block) {
  1013. block(responseMessages, lastKnownMessage, lastCommonReadMessage, nil, 0);
  1014. }
  1015. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1016. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1017. [self checkResponseStatusCode:statusCode forAccount:account];
  1018. if (block) {
  1019. block(nil, -1, -1, error, statusCode);
  1020. }
  1021. }];
  1022. return task;
  1023. }
  1024. - (NSURLSessionDataTask *)sendChatMessage:(NSString *)message toRoom:(NSString *)token displayName:(NSString *)displayName replyTo:(NSInteger)replyTo referenceId:(NSString *)referenceId silently:(BOOL)silently forAccount:(TalkAccount *)account withCompletionBlock:(SendChatMessagesCompletionBlock)block
  1025. {
  1026. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1027. NSString *endpoint = [NSString stringWithFormat:@"chat/%@", encodedToken];
  1028. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1029. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1030. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1031. [parameters setObject:message forKey:@"message"];
  1032. if (replyTo > -1) {
  1033. [parameters setObject:@(replyTo) forKey:@"replyTo"];
  1034. }
  1035. if (referenceId) {
  1036. [parameters setObject:referenceId forKey:@"referenceId"];
  1037. }
  1038. if (silently) {
  1039. [parameters setObject:@(silently) forKey:@"silent"];
  1040. }
  1041. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1042. // Workaround: When sendChatMessage is called from Share Extension session managers are not initialized.
  1043. if (!apiSessionManager) {
  1044. [self initSessionManagers];
  1045. apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1046. }
  1047. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1048. if (block) {
  1049. block(nil);
  1050. }
  1051. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1052. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1053. [self checkResponseStatusCode:statusCode forAccount:account];
  1054. if (block) {
  1055. block(error);
  1056. }
  1057. }];
  1058. return task;
  1059. }
  1060. - (NSURLSessionDataTask *)deleteChatMessageInRoom:(NSString *)token withMessageId:(NSInteger)messageId forAccount:(TalkAccount *)account withCompletionBlock:(DeleteChatMessageCompletionBlock)block
  1061. {
  1062. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1063. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld", encodedToken, (long)messageId];
  1064. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1065. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1066. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1067. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1068. NSDictionary *messageDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1069. if (block) {
  1070. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1071. block(messageDict, nil, httpResponse.statusCode);
  1072. }
  1073. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1074. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1075. [self checkResponseStatusCode:statusCode forAccount:account];
  1076. if (block) {
  1077. block(nil, error, statusCode);
  1078. }
  1079. }];
  1080. return task;
  1081. }
  1082. - (NSURLSessionDataTask *)editChatMessageInRoom:(NSString *)token withMessageId:(NSInteger)messageId withMessage:(NSString *)message forAccount:(TalkAccount *)account withCompletionBlock:(EditChatMessageCompletionBlock)block
  1083. {
  1084. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1085. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld", encodedToken, (long)messageId];
  1086. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1087. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1088. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1089. [parameters setObject:message forKey:@"message"];
  1090. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1091. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1092. NSDictionary *messageDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1093. if (block) {
  1094. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1095. block(messageDict, nil, httpResponse.statusCode);
  1096. }
  1097. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1098. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1099. [self checkResponseStatusCode:statusCode forAccount:account];
  1100. if (block) {
  1101. block(nil, error, statusCode);
  1102. }
  1103. }];
  1104. return task;
  1105. }
  1106. - (NSURLSessionDataTask *)clearChatHistoryInRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ClearChatHistoryCompletionBlock)block
  1107. {
  1108. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1109. NSString *endpoint = [NSString stringWithFormat:@"chat/%@", encodedToken];
  1110. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1111. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1112. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1113. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1114. NSDictionary *messageDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1115. if (block) {
  1116. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1117. block(messageDict, nil, httpResponse.statusCode);
  1118. }
  1119. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1120. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1121. [self checkResponseStatusCode:statusCode forAccount:account];
  1122. if (block) {
  1123. block(nil, error, statusCode);
  1124. }
  1125. }];
  1126. return task;
  1127. }
  1128. - (NSURLSessionDataTask *)shareRichObject:(NSDictionary *)richObject inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SendChatMessagesCompletionBlock)block
  1129. {
  1130. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1131. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/share", encodedToken];
  1132. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1133. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1134. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1135. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:richObject progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1136. if (block) {
  1137. block(nil);
  1138. }
  1139. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1140. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1141. [self checkResponseStatusCode:statusCode forAccount:account];
  1142. if (block) {
  1143. block(error);
  1144. }
  1145. }];
  1146. return task;
  1147. }
  1148. - (NSURLSessionDataTask *)setChatReadMarker:(NSInteger)lastReadMessage inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SendChatMessagesCompletionBlock)block
  1149. {
  1150. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1151. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/read", encodedToken];
  1152. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1153. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1154. NSDictionary *parameters = nil;
  1155. if (lastReadMessage > 0) {
  1156. parameters = @{@"lastReadMessage" : @(lastReadMessage)};
  1157. }
  1158. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1159. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1160. if (block) {
  1161. block(nil);
  1162. }
  1163. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1164. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1165. [self checkResponseStatusCode:statusCode forAccount:account];
  1166. if (block) {
  1167. block(error);
  1168. }
  1169. }];
  1170. return task;
  1171. }
  1172. - (NSURLSessionDataTask *)markChatAsUnreadInRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SendChatMessagesCompletionBlock)block
  1173. {
  1174. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1175. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/read", encodedToken];
  1176. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1177. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1178. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1179. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1180. if (block) {
  1181. block(nil);
  1182. }
  1183. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1184. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1185. [self checkResponseStatusCode:statusCode forAccount:account];
  1186. if (block) {
  1187. block(error);
  1188. }
  1189. }];
  1190. return task;
  1191. }
  1192. - (NSURLSessionDataTask *)getSharedItemsOverviewInRoom:(NSString *)token withLimit:(NSInteger)limit forAccount:(TalkAccount *)account withCompletionBlock:(GetSharedItemsOverviewCompletionBlock)block
  1193. {
  1194. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1195. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/share/overview", encodedToken];
  1196. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1197. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1198. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1199. if (limit > -1) {
  1200. [parameters setObject:@(limit) forKey:@"limit"];
  1201. }
  1202. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1203. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1204. NSDictionary *responseSharedItems = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1205. // Create dictionary [String: [NCChatMessage]]
  1206. NSMutableDictionary *sharedItems = [NSMutableDictionary new];
  1207. for (NSString *key in responseSharedItems.allKeys) {
  1208. NSArray *responseMessages = [responseSharedItems objectForKey:key];
  1209. NSMutableArray *messages = [NSMutableArray new];
  1210. for (NSDictionary *messageDict in responseMessages) {
  1211. NCChatMessage *message = [NCChatMessage messageWithDictionary:messageDict];
  1212. if (message) {
  1213. [messages addObject:message];
  1214. }
  1215. }
  1216. [sharedItems setObject:messages forKey:key];
  1217. }
  1218. if (block) {
  1219. block(sharedItems, nil, 0);
  1220. }
  1221. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1222. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1223. [self checkResponseStatusCode:statusCode forAccount:account];
  1224. if (block) {
  1225. block(nil, error, statusCode);
  1226. }
  1227. }];
  1228. return task;
  1229. }
  1230. - (NSURLSessionDataTask *)getSharedItemsOfType:(NSString *)objectType fromLastMessageId:(NSInteger)messageId withLimit:(NSInteger)limit inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(GetSharedItemsCompletionBlock)block
  1231. {
  1232. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1233. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/share", encodedToken];
  1234. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1235. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1236. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1237. [parameters setObject:objectType forKey:@"objectType"];
  1238. if (messageId > -1) {
  1239. [parameters setObject:@(messageId) forKey:@"lastKnownMessageId"];
  1240. }
  1241. if (limit > -1) {
  1242. [parameters setObject:@(limit) forKey:@"limit"];
  1243. }
  1244. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1245. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1246. id responseData = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1247. // Create array [NCChatMessage]
  1248. NSMutableArray *sharedItems = [NSMutableArray new];
  1249. if ([responseData isKindOfClass:[NSDictionary class]]) {
  1250. NSDictionary *responseSharedItems = responseData;
  1251. for (NSDictionary *messageDict in responseSharedItems.allValues) {
  1252. NCChatMessage *message = [NCChatMessage messageWithDictionary:messageDict];
  1253. [sharedItems addObject:message];
  1254. }
  1255. }
  1256. // Get X-Chat-Last-Given
  1257. NSHTTPURLResponse *response = ((NSHTTPURLResponse *)[task response]);
  1258. NSDictionary *headers = [response allHeaderFields];
  1259. NSString *lastKnowMessageHeader = [headers objectForKey:@"X-Chat-Last-Given"];
  1260. NSInteger lastKnownMessage = -1;
  1261. if (lastKnowMessageHeader) {
  1262. lastKnownMessage = [lastKnowMessageHeader integerValue];
  1263. }
  1264. if (block) {
  1265. block(sharedItems, lastKnownMessage, nil, 0);
  1266. }
  1267. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1268. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1269. [self checkResponseStatusCode:statusCode forAccount:account];
  1270. if (block) {
  1271. block(nil, -1, error, statusCode);
  1272. }
  1273. }];
  1274. return task;
  1275. }
  1276. - (NSURLSessionDataTask *)getMessageContextInRoom:(NSString *)token forMessageId:(NSInteger)messageId withLimit:(NSInteger)limit forAccount:(TalkAccount *)account withCompletionBlock:(GetMessageContextInRoomCompletionBlock)block
  1277. {
  1278. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1279. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld/context", encodedToken, (long)messageId];
  1280. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  1281. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  1282. NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
  1283. if (limit && limit > 0) {
  1284. // Limit is optional server-side and defaults to 50, maximum is 100
  1285. [parameters setObject:@(limit) forKey:@"limit"];
  1286. }
  1287. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1288. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1289. NSArray *responseMessages = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1290. if (block) {
  1291. block(responseMessages, nil, 0);
  1292. }
  1293. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1294. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1295. [self checkResponseStatusCode:statusCode forAccount:account];
  1296. if (block) {
  1297. block(nil, error, statusCode);
  1298. }
  1299. }];
  1300. return task;
  1301. }
  1302. #pragma mark - Translations Controller
  1303. - (NSURLSessionDataTask *)getAvailableTranslationsForAccount:(TalkAccount *)account withCompletionBlock:(GetTranslationsCompletionBlock)block
  1304. {
  1305. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/translation/languages", account.server];
  1306. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1307. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1308. NSDictionary *translationDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1309. if (block) {
  1310. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1311. NSArray *translations = [[NCDatabaseManager sharedInstance] translationsFromTranslationsArray:[translationDict objectForKey:@"languages"]];
  1312. BOOL languageDetection = [translationDict objectForKey:@"languageDetection"];
  1313. block(translations, languageDetection, nil, httpResponse.statusCode);
  1314. }
  1315. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1316. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1317. [self checkResponseStatusCode:statusCode forAccount:account];
  1318. if (block) {
  1319. block(@[], NO, error, statusCode);
  1320. }
  1321. }];
  1322. return task;
  1323. }
  1324. - (NSURLSessionDataTask *)translateMessage:(NSString *)message from:(NSString *)from to:(NSString *)to forAccount:(TalkAccount *)account withCompletionBlock:(MessageTranslationCompletionBlock)block
  1325. {
  1326. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/translation/translate", account.server];
  1327. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1328. [parameters setValue:message forKey:@"text"];
  1329. [parameters setValue:to forKey:@"toLanguage"];
  1330. if (from.length > 0) {
  1331. [parameters setValue:from forKey:@"fromLanguage"];
  1332. }
  1333. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1334. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1335. NSDictionary *translationDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1336. if (block) {
  1337. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1338. block(translationDict, nil, httpResponse.statusCode);
  1339. }
  1340. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1341. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1342. [self checkResponseStatusCode:statusCode forAccount:account];
  1343. NSDictionary *failureDict = [[[self getFailureResponseObjectFromError:error] objectForKey:@"ocs"] objectForKey:@"data"];
  1344. if (block) {
  1345. block(failureDict, error, statusCode);
  1346. }
  1347. }];
  1348. return task;
  1349. }
  1350. #pragma mark - Reactions Controller
  1351. - (NSURLSessionDataTask *)addReaction:(NSString *)reaction toMessage:(NSInteger)messageId inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MessageReactionCompletionBlock)block
  1352. {
  1353. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1354. NSString *endpoint = [NSString stringWithFormat:@"reaction/%@/%ld", encodedToken, (long)messageId];
  1355. NSInteger reactionsAPIVersion = [self reactionsAPIVersionForAccount:account];
  1356. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:reactionsAPIVersion forAccount:account];
  1357. NSDictionary *parameters = @{@"reaction" : reaction};
  1358. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1359. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1360. NSDictionary *reactionsDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1361. if (block) {
  1362. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1363. // When there are no elements, the server returns an empty array instead of an empty dictionary
  1364. if (![reactionsDict isKindOfClass:[NSDictionary class]]) {
  1365. block(@{}, nil, httpResponse.statusCode);
  1366. } else {
  1367. block(reactionsDict, nil, httpResponse.statusCode);
  1368. }
  1369. }
  1370. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1371. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1372. [self checkResponseStatusCode:statusCode forAccount:account];
  1373. if (block) {
  1374. block(nil, error, statusCode);
  1375. }
  1376. }];
  1377. return task;
  1378. }
  1379. - (NSURLSessionDataTask *)removeReaction:(NSString *)reaction fromMessage:(NSInteger)messageId inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MessageReactionCompletionBlock)block
  1380. {
  1381. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1382. NSString *endpoint = [NSString stringWithFormat:@"reaction/%@/%ld", encodedToken, (long)messageId];
  1383. NSInteger reactionsAPIVersion = [self reactionsAPIVersionForAccount:account];
  1384. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:reactionsAPIVersion forAccount:account];
  1385. NSDictionary *parameters = @{@"reaction" : reaction};
  1386. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1387. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1388. NSDictionary *reactionsDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1389. if (block) {
  1390. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1391. // When there are no elements, the server returns an empty array instead of an empty dictionary
  1392. if (![reactionsDict isKindOfClass:[NSDictionary class]]) {
  1393. block(@{}, nil, httpResponse.statusCode);
  1394. } else {
  1395. block(reactionsDict, nil, httpResponse.statusCode);
  1396. }
  1397. }
  1398. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1399. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1400. [self checkResponseStatusCode:statusCode forAccount:account];
  1401. if (block) {
  1402. block(nil, error, statusCode);
  1403. }
  1404. }];
  1405. return task;
  1406. }
  1407. - (NSURLSessionDataTask *)getReactions:(NSString *)reaction fromMessage:(NSInteger)messageId inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(MessageReactionCompletionBlock)block
  1408. {
  1409. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1410. NSString *endpoint = [NSString stringWithFormat:@"reaction/%@/%ld", encodedToken, (long)messageId];
  1411. NSInteger reactionsAPIVersion = [self reactionsAPIVersionForAccount:account];
  1412. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:reactionsAPIVersion forAccount:account];
  1413. NSDictionary *parameters = nil;
  1414. if (reaction) {
  1415. parameters = @{@"reaction" : reaction};
  1416. }
  1417. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1418. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1419. NSDictionary *reactionsDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1420. if (block) {
  1421. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1422. // When there are no elements, the server returns an empty array instead of an empty dictionary
  1423. if (![reactionsDict isKindOfClass:[NSDictionary class]]) {
  1424. block(@{}, nil, httpResponse.statusCode);
  1425. } else {
  1426. block(reactionsDict, nil, httpResponse.statusCode);
  1427. }
  1428. }
  1429. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1430. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1431. [self checkResponseStatusCode:statusCode forAccount:account];
  1432. if (block) {
  1433. block(nil, error, statusCode);
  1434. }
  1435. }];
  1436. return task;
  1437. }
  1438. #pragma mark - Polls Controller
  1439. - (NSURLSessionDataTask *)createPollWithQuestion:(NSString *)question options:(NSArray *)options resultMode:(NCPollResultMode)resultMode maxVotes:(NSInteger)maxVotes inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(PollCompletionBlock)block
  1440. {
  1441. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1442. NSString *endpoint = [NSString stringWithFormat:@"poll/%@", encodedToken];
  1443. NSInteger pollsAPIVersion = [self pollsAPIVersionForAccount:account];
  1444. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:pollsAPIVersion forAccount:account];
  1445. NSDictionary *parameters = @{@"question" : question,
  1446. @"options" : options,
  1447. @"resultMode" : @(resultMode),
  1448. @"maxVotes" : @(maxVotes)
  1449. };
  1450. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1451. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1452. NSDictionary *pollDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1453. NCPoll *poll = [NCPoll initWithPollDictionary:pollDict];
  1454. if (block) {
  1455. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1456. block(poll, nil, httpResponse.statusCode);
  1457. }
  1458. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1459. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1460. [self checkResponseStatusCode:statusCode forAccount:account];
  1461. if (block) {
  1462. block(nil, error, statusCode);
  1463. }
  1464. }];
  1465. return task;
  1466. }
  1467. - (NSURLSessionDataTask *)getPollWithId:(NSInteger)pollId inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(PollCompletionBlock)block
  1468. {
  1469. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1470. NSString *endpoint = [NSString stringWithFormat:@"poll/%@/%ld", encodedToken, (long)pollId];
  1471. NSInteger pollsAPIVersion = [self pollsAPIVersionForAccount:account];
  1472. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:pollsAPIVersion forAccount:account];
  1473. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1474. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1475. NSDictionary *pollDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1476. NCPoll *poll = [NCPoll initWithPollDictionary:pollDict];
  1477. if (block) {
  1478. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1479. block(poll, nil, httpResponse.statusCode);
  1480. }
  1481. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1482. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1483. [self checkResponseStatusCode:statusCode forAccount:account];
  1484. if (block) {
  1485. block(nil, error, statusCode);
  1486. }
  1487. }];
  1488. return task;
  1489. }
  1490. - (NSURLSessionDataTask *)voteOnPollWithId:(NSInteger)pollId inRoom:(NSString *)token withOptions:(NSArray *)options forAccount:(TalkAccount *)account withCompletionBlock:(PollCompletionBlock)block
  1491. {
  1492. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1493. NSString *endpoint = [NSString stringWithFormat:@"poll/%@/%ld", encodedToken, (long)pollId];
  1494. NSInteger pollsAPIVersion = [self pollsAPIVersionForAccount:account];
  1495. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:pollsAPIVersion forAccount:account];
  1496. NSDictionary *parameters = @{@"optionIds" : options};
  1497. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1498. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1499. NSDictionary *pollDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1500. NCPoll *poll = [NCPoll initWithPollDictionary:pollDict];
  1501. if (block) {
  1502. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1503. block(poll, nil, httpResponse.statusCode);
  1504. }
  1505. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1506. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1507. [self checkResponseStatusCode:statusCode forAccount:account];
  1508. if (block) {
  1509. block(nil, error, statusCode);
  1510. }
  1511. }];
  1512. return task;
  1513. }
  1514. - (NSURLSessionDataTask *)closePollWithId:(NSInteger)pollId inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(PollCompletionBlock)block
  1515. {
  1516. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1517. NSString *endpoint = [NSString stringWithFormat:@"poll/%@/%ld", encodedToken, (long)pollId];
  1518. NSInteger pollsAPIVersion = [self pollsAPIVersionForAccount:account];
  1519. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:pollsAPIVersion forAccount:account];
  1520. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1521. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1522. NSDictionary *pollDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  1523. NCPoll *poll = [NCPoll initWithPollDictionary:pollDict];
  1524. if (block) {
  1525. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1526. block(poll, nil, httpResponse.statusCode);
  1527. }
  1528. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1529. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1530. [self checkResponseStatusCode:statusCode forAccount:account];
  1531. if (block) {
  1532. block(nil, error, statusCode);
  1533. }
  1534. }];
  1535. return task;
  1536. }
  1537. #pragma mark - Signaling Controller
  1538. - (NSURLSessionDataTask *)sendSignalingMessages:(NSString *)messages toRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SendSignalingMessagesCompletionBlock)block;
  1539. {
  1540. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1541. NSString *endpoint = (token) ? [NSString stringWithFormat:@"signaling/%@", encodedToken] : @"signaling";
  1542. NSInteger signalingAPIVersion = [self signalingAPIVersionForAccount:account];
  1543. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:signalingAPIVersion forAccount:account];
  1544. NSDictionary *parameters = @{@"messages" : messages};
  1545. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1546. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1547. if (block) {
  1548. block(nil);
  1549. }
  1550. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1551. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1552. [self checkResponseStatusCode:statusCode forAccount:account];
  1553. if (block) {
  1554. block(error);
  1555. }
  1556. }];
  1557. return task;
  1558. }
  1559. - (NSURLSessionDataTask *)pullSignalingMessagesFromRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(PullSignalingMessagesCompletionBlock)block
  1560. {
  1561. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1562. NSString *endpoint = (token) ? [NSString stringWithFormat:@"signaling/%@", encodedToken] : @"signaling";
  1563. NSInteger signalingAPIVersion = [self signalingAPIVersionForAccount:account];
  1564. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:signalingAPIVersion forAccount:account];
  1565. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1566. NSURLSessionDataTask *task = [apiSessionManager GET:URLString
  1567. parameters:nil progress:nil
  1568. success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1569. NSDictionary *responseDict = responseObject;
  1570. if (block) {
  1571. block(responseDict, nil);
  1572. }
  1573. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1574. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1575. [self checkResponseStatusCode:statusCode forAccount:account];
  1576. if (block) {
  1577. block(nil, error);
  1578. }
  1579. }];
  1580. return task;
  1581. }
  1582. - (NSString *)authenticationBackendUrlForAccount:(TalkAccount *)account
  1583. {
  1584. NSString *endpoint = @"signaling/backend";
  1585. NSInteger signalingAPIVersion = [self signalingAPIVersionForAccount:account];
  1586. return [self getRequestURLForEndpoint:endpoint withAPIVersion:signalingAPIVersion forAccount:account];
  1587. }
  1588. #pragma mark - Settings
  1589. - (NSURLSessionDataTask *)setReadStatusPrivacySettingEnabled:(BOOL)enabled forAccount:(TalkAccount *)account withCompletionBlock:(SetReadStatusPrivacySettingCompletionBlock)block
  1590. {
  1591. NSString *URLString = [self getRequestURLForEndpoint:@"settings/user" withAPIVersion:APIv1 forAccount:account];
  1592. NSDictionary *parameters = @{@"key" : @"read_status_privacy",
  1593. @"value" : @(enabled)};
  1594. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1595. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1596. if (block) {
  1597. block(nil);
  1598. }
  1599. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1600. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1601. [self checkResponseStatusCode:statusCode forAccount:account];
  1602. if (block) {
  1603. block(error);
  1604. }
  1605. }];
  1606. return task;
  1607. }
  1608. - (NSURLSessionDataTask *)setTypingPrivacySettingEnabled:(BOOL)enabled forAccount:(TalkAccount *)account withCompletionBlock:(SetTypingPrivacySettingCompletionBlock)block
  1609. {
  1610. NSString *URLString = [self getRequestURLForEndpoint:@"settings/user" withAPIVersion:APIv1 forAccount:account];
  1611. NSDictionary *parameters = @{@"key" : @"typing_privacy",
  1612. @"value" : @(enabled)};
  1613. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1614. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1615. if (block) {
  1616. block(nil);
  1617. }
  1618. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1619. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1620. [self checkResponseStatusCode:statusCode forAccount:account];
  1621. if (block) {
  1622. block(error);
  1623. }
  1624. }];
  1625. return task;
  1626. }
  1627. #pragma mark - Files
  1628. - (void)readFolderForAccount:(TalkAccount *)account atPath:(NSString *)path depth:(NSString *)depth withCompletionBlock:(ReadFolderCompletionBlock)block
  1629. {
  1630. [self setupNCCommunicationForAccount:account];
  1631. NSString *serverUrlString = [NSString stringWithFormat:@"%@%@/%@", account.server, [self filesPathForAccount:account], path ? path : @""];
  1632. // We don't need all properties, so we limit the request to the needed ones to reduce size and processing time
  1633. NSString *body = @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
  1634. <d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\
  1635. <d:prop>\
  1636. <d:getlastmodified />\
  1637. <d:getcontenttype />\
  1638. <d:resourcetype />\
  1639. <fileid xmlns=\"http://owncloud.org/ns\"/>\
  1640. <is-encrypted xmlns=\"http://nextcloud.org/ns\"/>\
  1641. <has-preview xmlns=\"http://nextcloud.org/ns\"/>\
  1642. </d:prop>\
  1643. </d:propfind>";
  1644. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  1645. [[NextcloudKit shared] readFileOrFolderWithServerUrlFileName:serverUrlString depth:depth showHiddenFiles:NO includeHiddenFiles:@[] requestBody:[body dataUsingEncoding:NSUTF8StringEncoding] options:options completion:^(NSString *account, NSArray<NKFile *> *files, NSData *responseDates, NKError *error) {
  1646. if (error.errorCode == 0 && block) {
  1647. block(files, nil);
  1648. } else if (block) {
  1649. NSError *nsError = [NSError errorWithDomain:NSURLErrorDomain code:error.errorCode userInfo:nil];
  1650. block(nil, nsError);
  1651. }
  1652. }];
  1653. }
  1654. - (void)shareFileOrFolderForAccount:(TalkAccount *)account atPath:(NSString *)path toRoom:(NSString *)token talkMetaData:(NSDictionary *)talkMetaData withCompletionBlock:(ShareFileOrFolderCompletionBlock)block
  1655. {
  1656. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/files_sharing/api/v1/shares", account.server];
  1657. NSMutableDictionary *parameters = [NSMutableDictionary new];
  1658. [parameters setObject:path forKey:@"path"];
  1659. [parameters setObject:@(10) forKey:@"shareType"];
  1660. [parameters setObject:token forKey:@"shareWith"];
  1661. if (talkMetaData) {
  1662. NSError *error = nil;
  1663. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:talkMetaData
  1664. options:0
  1665. error:&error];
  1666. if (error) {
  1667. NSLog(@"Error serializing JSON: %@", error);
  1668. } else {
  1669. [parameters setObject:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] forKey:@"talkMetaData"];
  1670. }
  1671. }
  1672. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1673. // Workaround: When shareFileOrFolderForAccount is called from Share Extension session managers are not initialized.
  1674. if (!apiSessionManager) {
  1675. [self initSessionManagers];
  1676. apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1677. }
  1678. [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  1679. if (block) {
  1680. block(nil);
  1681. }
  1682. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  1683. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
  1684. // Do not return error when re-sharing a file or folder.
  1685. if (httpResponse.statusCode == 403 && block) {
  1686. block(nil);
  1687. } else if (block) {
  1688. block(error);
  1689. }
  1690. }];
  1691. }
  1692. - (void)getFileByFileId:(TalkAccount *)account fileId:(NSString *)fileId withCompletionBlock:(GetFileByFileIdCompletionBlock)block
  1693. {
  1694. [self setupNCCommunicationForAccount:account];
  1695. NSString *body = @"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
  1696. <d:searchrequest xmlns:d=\"DAV:\" xmlns:oc=\"http://nextcloud.com/ns\">\
  1697. <d:basicsearch>\
  1698. <d:select>\
  1699. <d:prop>\
  1700. <d:displayname />\
  1701. <d:getcontenttype />\
  1702. <d:resourcetype />\
  1703. <d:getcontentlength />\
  1704. <d:getlastmodified />\
  1705. <d:creationdate />\
  1706. <d:getetag />\
  1707. <d:quota-used-bytes />\
  1708. <d:quota-available-bytes />\
  1709. <oc:permissions xmlns:oc=\"http://owncloud.org/ns\" />\
  1710. <oc:id xmlns:oc=\"http://owncloud.org/ns\" />\
  1711. <oc:size xmlns:oc=\"http://owncloud.org/ns\" />\
  1712. <oc:favorite xmlns:oc=\"http://owncloud.org/ns\" />\
  1713. </d:prop>\
  1714. </d:select>\
  1715. <d:from>\
  1716. <d:scope>\
  1717. <d:href>/files/%@</d:href>\
  1718. <d:depth>infinity</d:depth>\
  1719. </d:scope>\
  1720. </d:from>\
  1721. <d:where>\
  1722. <d:eq>\
  1723. <d:prop>\
  1724. <oc:fileid xmlns:oc=\"http://owncloud.org/ns\" />\
  1725. </d:prop>\
  1726. <d:literal>%@</d:literal>\
  1727. </d:eq>\
  1728. </d:where>\
  1729. <d:orderby />\
  1730. </d:basicsearch>\
  1731. </d:searchrequest>";
  1732. NSString *bodyRequest = [NSString stringWithFormat:body, account.userId, fileId];
  1733. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  1734. [[NextcloudKit shared] searchBodyRequestWithServerUrl:account.server requestBody:bodyRequest showHiddenFiles:YES includeHiddenFiles:@[] options:options completion:^(NSString *account, NSArray<NKFile *> *files, NSData *data, NKError *error) {
  1735. if (block) {
  1736. if ([files count] > 0) {
  1737. block([files objectAtIndex:0], error.errorCode, error.errorDescription);
  1738. } else {
  1739. block(nil, error.errorCode, error.errorDescription);
  1740. }
  1741. }
  1742. }];
  1743. }
  1744. - (void)uniqueNameForFileUploadWithName:(NSString *)fileName originalName:(BOOL)isOriginalName forAccount:(TalkAccount *)account withCompletionBlock:(GetFileUniqueNameCompletionBlock)block
  1745. {
  1746. [self setupNCCommunicationForAccount:account];
  1747. NSString *fileServerPath = [self serverFilePathForFileName:fileName andAccountId:account.accountId];
  1748. NSString *fileServerURL = [self serverFileURLForFilePath:fileServerPath andAccountId:account.accountId];
  1749. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  1750. [[NextcloudKit shared] readFileOrFolderWithServerUrlFileName:fileServerURL depth:@"0" showHiddenFiles:NO includeHiddenFiles:@[] requestBody:nil options:options completion:^(NSString *accountId, NSArray<NKFile *> *files, NSData *data, NKError *error) {
  1751. // File already exists
  1752. if (error.errorCode == 0 && files.count == 1) {
  1753. NSString *alternativeName = [self alternativeNameForFileName:fileName original:isOriginalName];
  1754. [self uniqueNameForFileUploadWithName:alternativeName originalName:NO forAccount:account withCompletionBlock:block];
  1755. // File does not exist
  1756. } else if (error.errorCode == 404) {
  1757. if (block) {
  1758. block(fileServerURL, fileServerPath, 0, nil);
  1759. }
  1760. } else {
  1761. NSLog(@"Error checking file name: %@", error.errorDescription);
  1762. if (block) {
  1763. block(nil, nil, error.errorCode, error.errorDescription);
  1764. }
  1765. }
  1766. }];
  1767. }
  1768. - (void)checkOrCreateAttachmentFolderForAccount:(TalkAccount *)account withCompletionBlock:(CheckAttachmentFolderCompletionBlock)block
  1769. {
  1770. [self setupNCCommunicationForAccount:account];
  1771. NSString *attachmentFolderServerURL = [self attachmentFolderServerURLForAccountId:account.accountId];
  1772. NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken:nil timeout:60 queue:dispatch_get_main_queue()];
  1773. [[NextcloudKit shared] readFileOrFolderWithServerUrlFileName:attachmentFolderServerURL depth:@"0" showHiddenFiles:NO includeHiddenFiles:@[] requestBody:nil options:options completion:^(NSString *accountId, NSArray<NKFile *> *files, NSData *data, NKError *error) {
  1774. // Attachment folder do not exist
  1775. if (error.errorCode == 404) {
  1776. [[NextcloudKit shared] createFolderWithServerUrlFileName:attachmentFolderServerURL options:options completion:^(NSString *accountId, NSString *ocId, NSDate *data, NKError *error) {
  1777. if (block) {
  1778. block(error.errorCode == 0, error.errorCode);
  1779. }
  1780. }];
  1781. } else {
  1782. NSLog(@"Error checking attachment folder: %@", error.errorDescription);
  1783. if (block) {
  1784. block(NO, error.errorCode);
  1785. }
  1786. }
  1787. }];
  1788. }
  1789. - (NSString *)serverFilePathForFileName:(NSString *)fileName andAccountId:(NSString *)accountId;
  1790. {
  1791. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:accountId];
  1792. NSString *attachmentsFolder = serverCapabilities.attachmentsFolder ? serverCapabilities.attachmentsFolder : @"";
  1793. return [NSString stringWithFormat:@"%@/%@", attachmentsFolder, fileName];
  1794. }
  1795. - (NSString *)attachmentFolderServerURLForAccountId:(NSString *)accountId;
  1796. {
  1797. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
  1798. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:accountId];
  1799. NSString *attachmentsFolder = serverCapabilities.attachmentsFolder ? serverCapabilities.attachmentsFolder : @"";
  1800. return [NSString stringWithFormat:@"%@%@%@", account.server, [self filesPathForAccount:account], attachmentsFolder];
  1801. }
  1802. - (NSString *)serverFileURLForFilePath:(NSString *)filePath andAccountId:(NSString *)accountId;
  1803. {
  1804. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:accountId];
  1805. return [NSString stringWithFormat:@"%@%@%@", account.server, [self filesPathForAccount:account], filePath];
  1806. }
  1807. - (NSString *)alternativeNameForFileName:(NSString *)fileName original:(BOOL)isOriginal
  1808. {
  1809. NSString *extension = [fileName pathExtension];
  1810. NSString *nameWithoutExtension = [fileName stringByDeletingPathExtension];
  1811. NSString *alternativeName = nameWithoutExtension;
  1812. NSString *newSuffix = @" (1)";
  1813. if (!isOriginal) {
  1814. // Check if the name ends with ` (n)`
  1815. NSError *error = nil;
  1816. NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@" \\((\\d+)\\)$" options:NSRegularExpressionCaseInsensitive error:&error];
  1817. NSTextCheckingResult *match = [regex firstMatchInString:nameWithoutExtension options:0 range:NSMakeRange(0, nameWithoutExtension.length)];
  1818. if ([match numberOfRanges] > 1) {
  1819. NSRange suffixRange = [match rangeAtIndex: 0];
  1820. NSInteger suffixNumber = [[nameWithoutExtension substringWithRange:[match rangeAtIndex: 1]] intValue];
  1821. newSuffix = [NSString stringWithFormat:@" (%ld)", suffixNumber + 1];
  1822. alternativeName = [nameWithoutExtension stringByReplacingCharactersInRange:suffixRange withString:@""];
  1823. }
  1824. }
  1825. alternativeName = [alternativeName stringByAppendingString:newSuffix];
  1826. alternativeName = [alternativeName stringByAppendingPathExtension:extension];
  1827. return alternativeName;
  1828. }
  1829. #pragma mark - User avatars
  1830. - (SDWebImageCombinedOperation *)getUserAvatarForUser:(NSString *)userId usingAccount:(TalkAccount *)account withStyle:(UIUserInterfaceStyle)style withCompletionBlock:(GetUserAvatarImageForUserCompletionBlock)block
  1831. {
  1832. return [self getUserAvatarForUser:userId usingAccount:account withStyle:style ignoreCache:NO withCompletionBlock:block];
  1833. }
  1834. - (SDWebImageCombinedOperation *)getUserAvatarForUser:(NSString *)userId usingAccount:(TalkAccount *)account withStyle:(UIUserInterfaceStyle)style ignoreCache:(BOOL)ignoreCache withCompletionBlock:(GetUserAvatarImageForUserCompletionBlock)block
  1835. {
  1836. NSString *encodedUser = [userId stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1837. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  1838. // Since https://github.com/nextcloud/server/pull/31010 we can only request avatars in 64px or 512px
  1839. // As we never request lower than 96px, we always get 512px anyway
  1840. long avatarSize = 512;
  1841. NSString *urlString = [NSString stringWithFormat:@"%@/index.php/avatar/%@/%ld", account.server, encodedUser, avatarSize];
  1842. if (style == UIUserInterfaceStyleDark && serverCapabilities.versionMajor >= 25) {
  1843. urlString = [NSString stringWithFormat:@"%@/dark", urlString];
  1844. }
  1845. NSURL *url = [NSURL URLWithString:urlString];
  1846. SDWebImageOptions options = SDWebImageRetryFailed;
  1847. if (ignoreCache) {
  1848. // In case we want to ignore our local caches, we can't provide SDWebImageRefreshCached, as this will
  1849. // always use NSURLCache and could still return a cached value here
  1850. options |= SDWebImageFromLoaderOnly;
  1851. } else {
  1852. // We want to refresh our cache when the NSURLCache determines that the resource is not fresh anymore
  1853. // see: https://github.com/SDWebImage/SDWebImage/wiki/Common-Problems#handle-image-refresh
  1854. // Could be removed when all conversations have a avatarVersion, see https://github.com/nextcloud/spreed/issues/9320
  1855. options |= SDWebImageRefreshCached;
  1856. }
  1857. SDWebImageDownloaderRequestModifier *requestModifier = [self getRequestModifierForAccount:account];
  1858. return [[SDWebImageManager sharedManager] loadImageWithURL:url options:options context:@{SDWebImageContextDownloadRequestModifier : requestModifier} progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
  1859. if (error) {
  1860. // When the request was cancelled before completing, we expect no completion handler to be called
  1861. if (block && error.code != SDWebImageErrorCancelled) {
  1862. block(nil, error);
  1863. }
  1864. return;
  1865. }
  1866. if (image && block) {
  1867. block(image, nil);
  1868. }
  1869. }];
  1870. }
  1871. - (SDWebImageCombinedOperation *)getFederatedUserAvatarForUser:(NSString *)userId inRoom:(NSString *)token usingAccount:(TalkAccount *)account withStyle:(UIUserInterfaceStyle)style withCompletionBlock:(GetFederatedUserAvatarImageForUserCompletionBlock)block
  1872. {
  1873. NSString *encodedToken = @"new";
  1874. if (token.length > 0) {
  1875. encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1876. }
  1877. NSString *endpoint = [NSString stringWithFormat:@"proxy/%@/user-avatar/512", encodedToken];
  1878. if (style == UIUserInterfaceStyleDark) {
  1879. endpoint = [NSString stringWithFormat:@"%@/dark", endpoint];
  1880. }
  1881. NSString *encodedUserId = [userId stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
  1882. endpoint = [NSString stringWithFormat:@"%@?cloudId=%@", endpoint, encodedUserId];
  1883. NSInteger avatarAPIVersion = 1;
  1884. NSString *urlString = [self getRequestURLForEndpoint:endpoint withAPIVersion:avatarAPIVersion forAccount:account];
  1885. NSURL *url = [NSURL URLWithString:urlString];
  1886. // See getAvatarForRoom for explanation
  1887. SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageRefreshCached | SDWebImageQueryDiskDataSync;
  1888. SDWebImageDownloaderRequestModifier *requestModifier = [self getRequestModifierForAccount:account];
  1889. // Make sure we get at least a 120x120 image when retrieving an SVG with SVGKit
  1890. SDWebImageContext *context = @{
  1891. SDWebImageContextDownloadRequestModifier : requestModifier,
  1892. SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(120, 120))
  1893. };
  1894. return [[SDWebImageManager sharedManager] loadImageWithURL:url options:options context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
  1895. if (error) {
  1896. // When the request was cancelled before completing, we expect no completion handler to be called
  1897. if (block && error.code != SDWebImageErrorCancelled) {
  1898. block(nil, error);
  1899. }
  1900. return;
  1901. }
  1902. if (image && block) {
  1903. block(image, nil);
  1904. }
  1905. }];
  1906. }
  1907. #pragma mark - Conversation avatars
  1908. - (SDWebImageCombinedOperation *)getAvatarForRoom:(NCRoom *)room withStyle:(UIUserInterfaceStyle)style withCompletionBlock:(GetAvatarForConversationWithImageCompletionBlock)block
  1909. {
  1910. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:room.accountId];
  1911. NSString *encodedToken = [room.token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1912. NSString *endpoint = [NSString stringWithFormat:@"room/%@/avatar", encodedToken];
  1913. if (style == UIUserInterfaceStyleDark) {
  1914. endpoint = [NSString stringWithFormat:@"%@/dark", endpoint];
  1915. }
  1916. // For non-one-to-one conversation we do have a valid avatarVersion which we can use to cache the avatar
  1917. // For one-to-one conversations we rely on the caching that is specified by the server via cache-control header
  1918. if (room.type != kNCRoomTypeOneToOne) {
  1919. endpoint = [NSString stringWithFormat:@"%@?avatarVersion=%@", endpoint, room.avatarVersion];
  1920. }
  1921. NSInteger avatarAPIVersion = 1;
  1922. NSString *urlString = [self getRequestURLForEndpoint:endpoint withAPIVersion:avatarAPIVersion forAccount:account];
  1923. NSURL *url = [NSURL URLWithString:urlString];
  1924. /*
  1925. SDWebImageRetryFailed: By default SDWebImage blacklists URLs that failed to load and does not try to
  1926. load these URLs again, but we want to retry these.
  1927. Also see https://github.com/SDWebImage/SDWebImage/wiki/Common-Problems#handle-image-refresh
  1928. SDWebImageRefreshCached: By default the cache-control header returned by the webserver is ignored and
  1929. images are cached forever. With this parameter we let NSURLCache determine
  1930. if a resource needs to be reloaded from the server again.
  1931. Could be removed if this endpoint returns an avatar version for all calls.
  1932. Also see https://github.com/nextcloud/spreed/issues/9320
  1933. SDWebImageQueryDiskDataSync: SDImage loads data from the disk cache on a separate (async) queue. This leads
  1934. to 2 problems: 1. It can cause some flickering on a reload, 2. It causes UIImage methods
  1935. being called to leak memory. This is noticeable in NSE with a tight memory constraint.
  1936. SVG images rendered to UIImage with SVGKit will leak data and make NSE crash.
  1937. */
  1938. SDWebImageOptions options = SDWebImageRetryFailed | SDWebImageQueryDiskDataSync;
  1939. SDWebImageDownloaderRequestModifier *requestModifier = [self getRequestModifierForAccount:account];
  1940. // Since we do not have a valid avatarVersion for one-to-one conversations, we need to rely on the
  1941. // cache-control header by the server and therefore on NSURLCache
  1942. // Note: There seems to be an issue with NSURLCache to correctly cache URLs that contain a query parameter
  1943. // so it's currently only suiteable for one-to-ones that don't have a correct avatarVersion anyway
  1944. if (room.type == kNCRoomTypeOneToOne) {
  1945. options |= SDWebImageRefreshCached;
  1946. }
  1947. // Make sure we get at least a 120x120 image when retrieving an SVG with SVGKit
  1948. SDWebImageContext *context = @{
  1949. SDWebImageContextDownloadRequestModifier : requestModifier,
  1950. SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(120, 120))
  1951. };
  1952. return [[SDWebImageManager sharedManager] loadImageWithURL:url options:options context:context progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
  1953. if (error) {
  1954. // When the request was cancelled before completing, we expect no completion handler to be called
  1955. if (block && error.code != SDWebImageErrorCancelled) {
  1956. block(nil, error);
  1957. }
  1958. return;
  1959. }
  1960. if (image && block) {
  1961. block(image, nil);
  1962. }
  1963. }];
  1964. }
  1965. - (NSURLSessionDataTask *)setAvatarForRoom:(NCRoom *)room withImage:(UIImage *)image withCompletionBlock:(SetAvatarForConversationWithImageCompletionBlock)block
  1966. {
  1967. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:room.accountId];
  1968. return [self setAvatarForRoomWithToken:room.token image:image account:account withCompletionBlock:block];
  1969. }
  1970. - (NSURLSessionDataTask *)setAvatarForRoomWithToken:(NSString *)token image:(UIImage *)image account:(TalkAccount *)account withCompletionBlock:(SetAvatarForConversationWithImageCompletionBlock)block
  1971. {
  1972. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  1973. NSString *endpoint = [NSString stringWithFormat:@"room/%@/avatar", encodedToken];
  1974. NSInteger avatarAPIVersion = 1;
  1975. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:avatarAPIVersion forAccount:account];
  1976. NSData *imageData = UIImageJPEGRepresentation(image, 0.7);
  1977. if (!imageData) {
  1978. if (block) {
  1979. NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil];
  1980. block(error);
  1981. }
  1982. return nil;
  1983. }
  1984. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  1985. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
  1986. [formData appendPartWithFileData:imageData name:@"file" fileName:@"avatar.jpg" mimeType:@"image/jpeg"];
  1987. } progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
  1988. if (block) {
  1989. block(nil);
  1990. }
  1991. } failure:^(NSURLSessionDataTask *task, NSError *error) {
  1992. NSInteger statusCode = [self getResponseStatusCode:task.response];
  1993. [self checkResponseStatusCode:statusCode forAccount:account];
  1994. if (block) {
  1995. block(error);
  1996. }
  1997. }];
  1998. return task;
  1999. }
  2000. - (NSURLSessionDataTask *)setEmojiAvatarForRoom:(NCRoom *)room withEmoji:(NSString *)emoji andColor:(NSString *)color withCompletionBlock:(SetAvatarForConversationWithImageCompletionBlock)block
  2001. {
  2002. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:room.accountId];
  2003. return [self setEmojiAvatarForRoomWithToken:room.token withEmoji:emoji andColor:color account:account withCompletionBlock:block];
  2004. }
  2005. - (NSURLSessionDataTask *)setEmojiAvatarForRoomWithToken:(NSString *)token withEmoji:(NSString *)emoji andColor:(NSString *)color account:(TalkAccount *)account withCompletionBlock:(SetAvatarForConversationWithImageCompletionBlock)block
  2006. {
  2007. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2008. NSString *endpoint = [NSString stringWithFormat:@"room/%@/avatar/emoji", encodedToken];
  2009. NSInteger avatarAPIVersion = 1;
  2010. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:avatarAPIVersion forAccount:account];
  2011. NSMutableDictionary *parameters = [NSMutableDictionary new];
  2012. [parameters setValue:emoji forKey:@"emoji"];
  2013. color = [color stringByReplacingOccurrencesOfString:@"#" withString:@""];
  2014. if (color.length > 0) {
  2015. [parameters setValue:color forKey:@"color"];
  2016. }
  2017. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2018. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2019. if (block) {
  2020. block(nil);
  2021. }
  2022. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2023. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2024. [self checkResponseStatusCode:statusCode forAccount:account];
  2025. if (block) {
  2026. block(error);
  2027. }
  2028. }];
  2029. return task;
  2030. }
  2031. - (NSURLSessionDataTask *)removeAvatarForRoom:(NCRoom *)room withCompletionBlock:(RemoveAvatarForConversationWithImageCompletionBlock)block
  2032. {
  2033. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:room.accountId];
  2034. NSString *encodedToken = [room.token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2035. NSString *endpoint = [NSString stringWithFormat:@"room/%@/avatar", encodedToken];
  2036. NSInteger avatarAPIVersion = 1;
  2037. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:avatarAPIVersion forAccount:account];
  2038. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2039. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2040. if (block) {
  2041. block(nil);
  2042. }
  2043. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2044. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2045. [self checkResponseStatusCode:statusCode forAccount:account];
  2046. if (block) {
  2047. block(error);
  2048. }
  2049. }];
  2050. return task;
  2051. }
  2052. #pragma mark - User actions
  2053. - (NSURLSessionDataTask *)getUserActionsForUser:(NSString *)userId usingAccount:(TalkAccount *)account withCompletionBlock:(GetUserActionsCompletionBlock)block
  2054. {
  2055. NSString *encodedUser = [userId stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2056. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/hovercard/v1/%@", account.server, encodedUser];
  2057. NSDictionary *parameters = @{@"format" : @"json"};
  2058. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2059. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2060. NSDictionary *actions = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2061. if (block) {
  2062. block(actions, nil);
  2063. }
  2064. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2065. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2066. [self checkResponseStatusCode:statusCode forAccount:account];
  2067. if (block) {
  2068. block(nil, error);
  2069. }
  2070. }];
  2071. return task;
  2072. }
  2073. #pragma mark - File previews
  2074. - (NSURLRequest *)createPreviewRequestForFile:(NSString *)fileId width:(NSInteger)width height:(NSInteger)height usingAccount:(TalkAccount *)account
  2075. {
  2076. NSString *urlString = [NSString stringWithFormat:@"%@/index.php/core/preview?fileId=%@&x=%ld&y=%ld&forceIcon=1", account.server, fileId, (long)width, (long)height];
  2077. NSMutableURLRequest *previewRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60];
  2078. [previewRequest setValue:[self authHeaderForAccount:account] forHTTPHeaderField:@"Authorization"];
  2079. return previewRequest;
  2080. }
  2081. - (NSURLRequest *)createPreviewRequestForFile:(NSString *)fileId withMaxHeight:(NSInteger) height usingAccount:(TalkAccount *)account
  2082. {
  2083. NSString *urlString = [NSString stringWithFormat:@"%@/index.php/core/preview?fileId=%@&x=-1&y=%ld&a=1&forceIcon=1", account.server, fileId, (long)height];
  2084. NSMutableURLRequest *previewRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60];
  2085. [previewRequest setValue:[self authHeaderForAccount:account] forHTTPHeaderField:@"Authorization"];
  2086. return previewRequest;
  2087. }
  2088. #pragma mark - User profile
  2089. - (NSURLSessionDataTask *)getUserProfileForAccount:(TalkAccount *)account withCompletionBlock:(GetUserProfileCompletionBlock)block
  2090. {
  2091. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/cloud/user", account.server];
  2092. NSDictionary *parameters = @{@"format" : @"json"};
  2093. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2094. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2095. NSDictionary *profile = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2096. if (block) {
  2097. block(profile, nil);
  2098. }
  2099. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2100. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2101. [self checkResponseStatusCode:statusCode forAccount:account];
  2102. if (block) {
  2103. block(nil, error);
  2104. }
  2105. }];
  2106. return task;
  2107. }
  2108. - (NSURLSessionDataTask *)getUserProfileEditableFieldsForAccount:(TalkAccount *)account withCompletionBlock:(GetUserProfileEditableFieldsCompletionBlock)block
  2109. {
  2110. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/cloud/user/fields", account.server];
  2111. NSDictionary *parameters = @{@"format" : @"json"};
  2112. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2113. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2114. NSArray *editableFields = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2115. if (block) {
  2116. block(editableFields, nil);
  2117. }
  2118. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2119. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2120. [self checkResponseStatusCode:statusCode forAccount:account];
  2121. if (block) {
  2122. block(nil, error);
  2123. }
  2124. }];
  2125. return task;
  2126. }
  2127. - (NSURLSessionDataTask *)setUserProfileField:(NSString *)field withValue:(NSString*)value forAccount:(TalkAccount *)account withCompletionBlock:(SetUserProfileFieldCompletionBlock)block
  2128. {
  2129. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/cloud/users/%@", account.server, account.userId];
  2130. NSDictionary *parameters = @{@"format" : @"json",
  2131. @"key" : field,
  2132. @"value" : value};
  2133. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2134. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2135. if (block) {
  2136. block(nil, 0);
  2137. }
  2138. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2139. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2140. // Ignore status code for now https://github.com/nextcloud/server/pull/26679
  2141. // [self checkResponseStatusCode:statusCode forAccount:account];
  2142. if (block) {
  2143. block(error, statusCode);
  2144. }
  2145. }];
  2146. return task;
  2147. }
  2148. - (NSURLSessionDataTask *)setUserProfileImage:(UIImage *)image forAccount:(TalkAccount *)account withCompletionBlock:(SetUserProfileFieldCompletionBlock)block
  2149. {
  2150. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/spreed/temp-user-avatar", account.server];
  2151. NSData *imageData= UIImageJPEGRepresentation(image, 0.7);
  2152. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2153. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
  2154. [formData appendPartWithFileData:imageData name:@"files[]" fileName:@"avatar.jpg" mimeType:@"image/jpeg"];
  2155. } progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
  2156. if (block) {
  2157. block(nil, 0);
  2158. }
  2159. } failure:^(NSURLSessionDataTask *task, NSError *error) {
  2160. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2161. [self checkResponseStatusCode:statusCode forAccount:account];
  2162. if (block) {
  2163. block(error, statusCode);
  2164. }
  2165. }];
  2166. return task;
  2167. }
  2168. - (NSURLSessionDataTask *)removeUserProfileImageForAccount:(TalkAccount *)account withCompletionBlock:(SetUserProfileFieldCompletionBlock)block
  2169. {
  2170. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/spreed/temp-user-avatar", account.server];
  2171. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2172. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2173. if (block) {
  2174. block(nil, 0);
  2175. }
  2176. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2177. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2178. [self checkResponseStatusCode:statusCode forAccount:account];
  2179. if (block) {
  2180. block(error, statusCode);
  2181. }
  2182. }];
  2183. return task;
  2184. }
  2185. - (void)saveProfileImageForAccount:(TalkAccount *)account
  2186. {
  2187. [self getAndStoreProfileImageForAccount:account withStyle:UIUserInterfaceStyleLight];
  2188. }
  2189. - (void)getAndStoreProfileImageForAccount:(TalkAccount *)account withStyle:(UIUserInterfaceStyle)style
  2190. {
  2191. __block SDWebImageCombinedOperation *operation;
  2192. // When getting our own profile image, we need to ignore any cache to always get the latest version
  2193. operation = [self getUserAvatarForUser:account.userId usingAccount:account withStyle:style ignoreCache:YES withCompletionBlock:^(UIImage *image, NSError *error) {
  2194. SDWebImageDownloadToken *token = operation.loaderOperation;
  2195. if (![token isKindOfClass:[SDWebImageDownloadToken class]]) {
  2196. return;
  2197. }
  2198. NSURLResponse *response = token.response;
  2199. NSDictionary *headers = ((NSHTTPURLResponse *)response).allHeaderFields;
  2200. RLMRealm *realm = [RLMRealm defaultRealm];
  2201. [realm beginWriteTransaction];
  2202. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", account.accountId];
  2203. TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  2204. managedAccount.hasCustomAvatar = [[headers objectForKey:@"X-NC-IsCustomAvatar"] boolValue];
  2205. [realm commitWriteTransaction];
  2206. NSData *pngData = UIImagePNGRepresentation(image);
  2207. NSString *documentsPath = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupIdentifier] path];
  2208. NSString *fileName;
  2209. if (style == UIUserInterfaceStyleDark) {
  2210. fileName = [NSString stringWithFormat:@"%@-%@-dark.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2211. } else {
  2212. fileName = [NSString stringWithFormat:@"%@-%@.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2213. }
  2214. NSString *filePath = [documentsPath stringByAppendingPathComponent:fileName];
  2215. [pngData writeToFile:filePath atomically:YES];
  2216. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  2217. if (style == UIUserInterfaceStyleLight && !managedAccount.hasCustomAvatar && serverCapabilities.versionMajor >= 25) {
  2218. [self getAndStoreProfileImageForAccount:account withStyle:UIUserInterfaceStyleDark];
  2219. return;
  2220. }
  2221. [[NSNotificationCenter defaultCenter] postNotificationName:NCUserProfileImageUpdatedNotification object:self userInfo:nil];
  2222. }];
  2223. }
  2224. - (UIImage *)userProfileImageForAccount:(TalkAccount *)account withStyle:(UIUserInterfaceStyle)style
  2225. {
  2226. NSFileManager *fileManager = [NSFileManager defaultManager];
  2227. NSString *documentsPath = [[fileManager containerURLForSecurityApplicationGroupIdentifier:groupIdentifier] path];
  2228. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  2229. NSString *fileName;
  2230. if (style == UIUserInterfaceStyleDark && !account.hasCustomAvatar && serverCapabilities.versionMajor >= 25) {
  2231. fileName = [NSString stringWithFormat:@"%@-%@-dark.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2232. } else {
  2233. fileName = [NSString stringWithFormat:@"%@-%@.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2234. }
  2235. NSString *filePath = [documentsPath stringByAppendingPathComponent:fileName];
  2236. // Migrate to app group directory
  2237. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2238. NSString *oldDocumentsPath = [paths objectAtIndex:0];
  2239. NSString *oldPath = [oldDocumentsPath stringByAppendingPathComponent:fileName];
  2240. if ([fileManager fileExistsAtPath:oldPath]) {
  2241. NSError *error = nil;
  2242. [fileManager moveItemAtPath:oldPath toPath:filePath error:&error];
  2243. NSLog(@"Migrating profile picture. Error: %@", error);
  2244. }
  2245. return [UIImage imageWithContentsOfFile:filePath];
  2246. }
  2247. - (void)removeProfileImageForAccount:(TalkAccount *)account
  2248. {
  2249. NSFileManager *fileManager = [NSFileManager defaultManager];
  2250. NSString *documentsPath = [[fileManager containerURLForSecurityApplicationGroupIdentifier:groupIdentifier] path];
  2251. NSString *fileName = [NSString stringWithFormat:@"%@-%@.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2252. NSString *filePath = [documentsPath stringByAppendingPathComponent:fileName];
  2253. [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
  2254. fileName = [NSString stringWithFormat:@"%@-%@-dark.png", account.userId, [[NSURL URLWithString:account.server] host]];
  2255. filePath = [documentsPath stringByAppendingPathComponent:fileName];
  2256. [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
  2257. // Legacy
  2258. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2259. NSString *oldDocumentsPath = [paths objectAtIndex:0];
  2260. NSString *oldPath = [oldDocumentsPath stringByAppendingPathComponent:fileName];
  2261. [[NSFileManager defaultManager] removeItemAtPath:oldPath error:nil];
  2262. }
  2263. #pragma mark - User Status
  2264. - (NSURLSessionDataTask *)getUserStatusForAccount:(TalkAccount *)account withCompletionBlock:(GetUserStatusCompletionBlock)block
  2265. {
  2266. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/user_status/api/v1/user_status", account.server];
  2267. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2268. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2269. NSDictionary *userStatus = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2270. if (block) {
  2271. block(userStatus, nil);
  2272. }
  2273. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2274. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2275. [self checkResponseStatusCode:statusCode forAccount:account];
  2276. if (block) {
  2277. block(nil, error);
  2278. }
  2279. }];
  2280. return task;
  2281. }
  2282. - (NSURLSessionDataTask *)setUserStatus:(NSString *)status forAccount:(TalkAccount *)account withCompletionBlock:(SetUserStatusCompletionBlock)block
  2283. {
  2284. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/user_status/api/v1/user_status/status", account.server];
  2285. NSDictionary *parameters = @{@"statusType" : status};
  2286. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2287. NSURLSessionDataTask *task = [apiSessionManager PUT:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2288. if (block) {
  2289. block(nil);
  2290. }
  2291. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2292. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2293. [self checkResponseStatusCode:statusCode forAccount:account];
  2294. if (block) {
  2295. block(error);
  2296. }
  2297. }];
  2298. return task;
  2299. }
  2300. #pragma mark - App Store info
  2301. - (NSURLSessionDataTask *)getAppStoreAppIdWithCompletionBlock:(GetAppIdCompletionBlock)block
  2302. {
  2303. NSString *URLString = [NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", bundleIdentifier];
  2304. NSURLSessionDataTask *task = [_defaultAPISessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2305. NSString *appId = nil;
  2306. NSArray *results = [responseObject objectForKey:@"results"];
  2307. if (results.count > 0) {
  2308. NSDictionary *appInfo = [results objectAtIndex:0];
  2309. appId = [[appInfo objectForKey:@"trackId"] stringValue];
  2310. }
  2311. if (block) {
  2312. block(appId, nil);
  2313. }
  2314. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2315. if (block) {
  2316. block(nil, error);
  2317. }
  2318. }];
  2319. return task;
  2320. }
  2321. #pragma mark - Server capabilities
  2322. - (NSURLSessionDataTask *)getServerCapabilitiesForServer:(NSString *)server withCompletionBlock:(GetServerCapabilitiesCompletionBlock)block
  2323. {
  2324. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v1.php/cloud/capabilities", server];
  2325. NSDictionary *parameters = @{@"format" : @"json"};
  2326. NSURLSessionDataTask *task = [_defaultAPISessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2327. NSDictionary *capabilities = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2328. if (block) {
  2329. block(capabilities, nil);
  2330. }
  2331. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2332. if (block) {
  2333. block(nil, error);
  2334. }
  2335. }];
  2336. return task;
  2337. }
  2338. - (NSURLSessionDataTask *)getServerCapabilitiesForAccount:(TalkAccount *)account withCompletionBlock:(GetServerCapabilitiesCompletionBlock)block
  2339. {
  2340. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v1.php/cloud/capabilities", account.server];
  2341. NSDictionary *parameters = @{@"format" : @"json"};
  2342. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2343. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2344. NSDictionary *capabilities = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2345. if (block) {
  2346. block(capabilities, nil);
  2347. }
  2348. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2349. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2350. [self checkResponseStatusCode:statusCode forAccount:account];
  2351. if (block) {
  2352. block(nil, error);
  2353. }
  2354. }];
  2355. return task;
  2356. }
  2357. #pragma mark - Server notifications
  2358. - (NSURLSessionDataTask *)getServerNotification:(NSInteger)notificationId forAccount:(TalkAccount *)account withCompletionBlock:(GetServerNotificationCompletionBlock)block
  2359. {
  2360. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/notifications/%ld", account.server, (long)notificationId];
  2361. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2362. // Workaround: Just in case session managers are not initialized when called from NotificationService extension.
  2363. if (!apiSessionManager) {
  2364. [self initSessionManagers];
  2365. apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2366. }
  2367. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2368. NSDictionary *notification = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2369. if (block) {
  2370. block(notification, nil, 0);
  2371. }
  2372. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2373. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2374. [self checkResponseStatusCode:statusCode forAccount:account];
  2375. if (block) {
  2376. block(nil, error, statusCode);
  2377. }
  2378. }];
  2379. return task;
  2380. }
  2381. - (NSURLSessionDataTask *)getServerNotificationsForAccount:(TalkAccount *)account withLastETag:(NSString *)lastETag withCompletionBlock:(GetServerNotificationsCompletionBlock)block
  2382. {
  2383. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/notifications", account.server];
  2384. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:URLString]];
  2385. [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
  2386. if (lastETag) {
  2387. [request addValue:lastETag forHTTPHeaderField:@"If-None-Match"];
  2388. }
  2389. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2390. NSURLSessionDataTask *task = [apiSessionManager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
  2391. if (!error) {
  2392. NSArray *notifications = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2393. NSDictionary *headers = [self getResponseHeaders:response];
  2394. if (block) {
  2395. block(notifications, [headers objectForKey:@"ETag"], [headers objectForKey:@"x-nextcloud-user-status"], nil);
  2396. }
  2397. } else {
  2398. if (block) {
  2399. block(nil, nil, nil, error);
  2400. }
  2401. }
  2402. }];
  2403. [task resume];
  2404. return task;
  2405. }
  2406. - (void)executeNotificationAction:(NCNotificationAction *)action forAccount:(TalkAccount *)account withCompletionBlock:(ExecuteNotificationActionCompletionBlock)block
  2407. {
  2408. void (^success)(NSURLSessionDataTask *task, id responseObject) = ^void(NSURLSessionDataTask *task, id responseObject) {
  2409. if (block) {
  2410. block(nil);
  2411. }
  2412. };
  2413. void (^failure)(NSURLSessionDataTask *task, NSError *error) = ^void(NSURLSessionDataTask *task, NSError *error) {
  2414. if (block) {
  2415. block(error);
  2416. }
  2417. };
  2418. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2419. if (action.actionType == NCNotificationActionTypeKNotificationActionTypeGet) {
  2420. [apiSessionManager GET:action.actionLink parameters:nil progress:nil success:success failure:failure];
  2421. } else if (action.actionType == NCNotificationActionTypeKNotificationActionTypePut) {
  2422. [apiSessionManager PUT:action.actionLink parameters:nil success:success failure:failure];
  2423. } else if (action.actionType == NCNotificationActionTypeKNotificationActionTypePost) {
  2424. [apiSessionManager POST:action.actionLink parameters:nil progress:nil success:success failure:failure];
  2425. } else if (action.actionType == NCNotificationActionTypeKNotificationActionTypeDelete) {
  2426. [apiSessionManager DELETE:action.actionLink parameters:nil success:success failure:failure];
  2427. } else {
  2428. NSLog(@"Trying to execute non-supported notification action type");
  2429. if (block) {
  2430. NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil];
  2431. block(error);
  2432. }
  2433. }
  2434. }
  2435. - (NSURLSessionDataTask *)checkNotificationExistance:(NSArray *)notificationIds forAccount:(TalkAccount *)account withCompletionBlock:(CheckNotificationExistanceBlock)block
  2436. {
  2437. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/notifications/exists", account.server];
  2438. NSDictionary *parameters = @{
  2439. @"ids" : notificationIds,
  2440. };
  2441. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2442. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2443. NSArray *responseArray = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2444. if (block) {
  2445. block(responseArray, nil);
  2446. }
  2447. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2448. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2449. [self checkResponseStatusCode:statusCode forAccount:account];
  2450. if (block) {
  2451. block(nil, error);
  2452. }
  2453. }];
  2454. return task;
  2455. }
  2456. #pragma mark - Push Notifications
  2457. - (NSURLSessionDataTask *)subscribeAccount:(TalkAccount *)account withPublicKey:(NSData *)publicKey toNextcloudServerWithCompletionBlock:(SubscribeToNextcloudServerCompletionBlock)block
  2458. {
  2459. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/push", account.server];
  2460. NSString *devicePublicKey = [[NSString alloc] initWithData:publicKey encoding:NSUTF8StringEncoding];
  2461. NSDictionary *parameters = @{@"pushTokenHash" : [[NCKeyChainController sharedInstance] pushTokenSHA512],
  2462. @"devicePublicKey" : devicePublicKey,
  2463. @"proxyServer" : pushNotificationServer
  2464. };
  2465. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2466. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2467. NSDictionary *responseDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2468. if (block) {
  2469. block(responseDict, nil);
  2470. }
  2471. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2472. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2473. [self checkResponseStatusCode:statusCode forAccount:account];
  2474. if (block) {
  2475. block(nil, error);
  2476. }
  2477. }];
  2478. return task;
  2479. }
  2480. - (NSURLSessionDataTask *)unsubscribeAccount:(TalkAccount *)account fromNextcloudServerWithCompletionBlock:(UnsubscribeToNextcloudServerCompletionBlock)block
  2481. {
  2482. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/apps/notifications/api/v2/push", account.server];
  2483. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2484. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2485. if (block) {
  2486. block(nil);
  2487. }
  2488. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2489. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2490. [self checkResponseStatusCode:statusCode forAccount:account];
  2491. if (block) {
  2492. block(error);
  2493. }
  2494. }];
  2495. return task;
  2496. }
  2497. - (NSURLSessionDataTask *)subscribeAccount:(TalkAccount *)account toPushServerWithCompletionBlock:(SubscribeToPushProxyCompletionBlock)block
  2498. {
  2499. NSString *URLString = [NSString stringWithFormat:@"%@/devices", pushNotificationServer];
  2500. NSDictionary *parameters = @{@"pushToken" : [[NCKeyChainController sharedInstance] combinedPushToken],
  2501. @"deviceIdentifier" : account.deviceIdentifier,
  2502. @"deviceIdentifierSignature" : account.deviceSignature,
  2503. @"userPublicKey" : account.userPublicKey
  2504. };
  2505. NSURLSessionDataTask *task = [[NCPushProxySessionManager shared] POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2506. if (block) {
  2507. block(nil);
  2508. }
  2509. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2510. if (block) {
  2511. block(error);
  2512. }
  2513. }];
  2514. return task;
  2515. }
  2516. - (NSURLSessionDataTask *)unsubscribeAccount:(TalkAccount *)account fromPushServerWithCompletionBlock:(UnsubscribeToPushProxyCompletionBlock)block
  2517. {
  2518. NSString *URLString = [NSString stringWithFormat:@"%@/devices", pushNotificationServer];
  2519. NSDictionary *parameters = @{@"deviceIdentifier" : account.deviceIdentifier,
  2520. @"deviceIdentifierSignature" : account.deviceSignature,
  2521. @"userPublicKey" : account.userPublicKey
  2522. };
  2523. NSURLSessionDataTask *task = [[NCPushProxySessionManager shared] DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2524. if (block) {
  2525. block(nil);
  2526. }
  2527. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2528. if (block) {
  2529. block(error);
  2530. }
  2531. }];
  2532. return task;
  2533. }
  2534. #pragma mark - Reference handling
  2535. - (NSURLSessionDataTask *)getReferenceForUrlString:(NSString *)url forAccount:(TalkAccount *)account withCompletionBlock:(GetReferenceForUrlStringCompletionBlock)block
  2536. {
  2537. NSString *URLString = [NSString stringWithFormat:@"%@/ocs/v2.php/references/resolve", account.server];
  2538. NSDictionary *parameters = @{@"reference" : url};
  2539. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2540. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2541. NSDictionary *responseReferences = [[[responseObject objectForKey:@"ocs"] objectForKey:@"data"] objectForKey:@"references"];
  2542. if (block) {
  2543. // When there's no data, the server returns an empty array instead of a dictionary
  2544. // Also we don't want to have a dictionary with NSNull values in it
  2545. if (![responseReferences isKindOfClass:[NSDictionary class]] || [[responseReferences objectForKey:url] isKindOfClass:[NSNull class]]) {
  2546. block(@{}, nil);
  2547. } else {
  2548. block(responseReferences, nil);
  2549. }
  2550. }
  2551. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2552. if (block) {
  2553. block(nil, error);
  2554. }
  2555. }];
  2556. return task;
  2557. }
  2558. - (NSURLRequest *)createReferenceThumbnailRequestForUrl:(NSString *)url
  2559. {
  2560. NSMutableURLRequest *thumbnailRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60];
  2561. return thumbnailRequest;
  2562. }
  2563. #pragma - Recording
  2564. - (NSURLSessionDataTask *)startRecording:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(StartRecordingCompletionBlock)block
  2565. {
  2566. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2567. NSString *endpoint = [NSString stringWithFormat:@"recording/%@", encodedToken];
  2568. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:1 forAccount:account];
  2569. // Status 1 -> Video recording
  2570. // Status 2 -> Audio recording (not supported for now)
  2571. NSDictionary *parameters = @{
  2572. @"status" : @(1)
  2573. };
  2574. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2575. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2576. if (block) {
  2577. block(nil);
  2578. }
  2579. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2580. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2581. [self checkResponseStatusCode:statusCode forAccount:account];
  2582. if (block) {
  2583. block(error);
  2584. }
  2585. }];
  2586. return task;
  2587. }
  2588. - (NSURLSessionDataTask *)stopRecording:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(StopRecordingCompletionBlock)block
  2589. {
  2590. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2591. NSString *endpoint = [NSString stringWithFormat:@"recording/%@", encodedToken];
  2592. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:1 forAccount:account];
  2593. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2594. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2595. if (block) {
  2596. block(nil);
  2597. }
  2598. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2599. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2600. [self checkResponseStatusCode:statusCode forAccount:account];
  2601. if (block) {
  2602. block(error);
  2603. }
  2604. }];
  2605. return task;
  2606. }
  2607. - (NSURLSessionDataTask *)dismissStoredRecordingNotificationWithTimestamp:(NSString *)timestamp forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(DismissStoredRecordingNotificationCompletionBlock)block
  2608. {
  2609. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2610. NSString *endpoint = [NSString stringWithFormat:@"recording/%@/notification", encodedToken];
  2611. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:1 forAccount:account];
  2612. NSDictionary *parameters = @{
  2613. @"timestamp" : timestamp
  2614. };
  2615. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2616. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2617. if (block) {
  2618. block(nil);
  2619. }
  2620. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2621. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2622. [self checkResponseStatusCode:statusCode forAccount:account];
  2623. if (block) {
  2624. block(error);
  2625. }
  2626. }];
  2627. return task;
  2628. }
  2629. - (NSURLSessionDataTask *)shareStoredRecordingWithTimestamp:(NSString *)timestamp withFileId:(NSString *)fileId forRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(ShareStoredRecordingCompletionBlock)block
  2630. {
  2631. NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2632. NSString *endpoint = [NSString stringWithFormat:@"recording/%@/share-chat", encodedToken];
  2633. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:1 forAccount:account];
  2634. NSDictionary *parameters = @{
  2635. @"timestamp" : timestamp,
  2636. @"fileId" : fileId
  2637. };
  2638. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2639. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2640. if (block) {
  2641. block(nil);
  2642. }
  2643. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2644. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2645. [self checkResponseStatusCode:statusCode forAccount:account];
  2646. if (block) {
  2647. block(error);
  2648. }
  2649. }];
  2650. return task;
  2651. }
  2652. #pragma mark - Remind me later
  2653. - (NSURLSessionDataTask *)setReminderForMessage:(NCChatMessage *)message withTimestamp:(NSString *)timestamp withCompletionBlock:(SetReminderForMessage)block
  2654. {
  2655. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:message.accountId];
  2656. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  2657. NSString *encodedToken = [message.token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2658. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld/reminder", encodedToken, (long)message.messageId];
  2659. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  2660. NSDictionary *parameters = @{
  2661. @"timestamp" : timestamp
  2662. };
  2663. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2664. NSURLSessionDataTask *task = [apiSessionManager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2665. if (block) {
  2666. block(nil);
  2667. }
  2668. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2669. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2670. [self checkResponseStatusCode:statusCode forAccount:account];
  2671. if (block) {
  2672. block(error);
  2673. }
  2674. }];
  2675. return task;
  2676. }
  2677. - (NSURLSessionDataTask *)deleteReminderForMessage:(NCChatMessage *)message withCompletionBlock:(DeleteReminderForMessage)block
  2678. {
  2679. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:message.accountId];
  2680. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  2681. NSString *encodedToken = [message.token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2682. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld/reminder", encodedToken, (long)message.messageId];
  2683. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  2684. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2685. NSURLSessionDataTask *task = [apiSessionManager DELETE:URLString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2686. if (block) {
  2687. block(nil);
  2688. }
  2689. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2690. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2691. [self checkResponseStatusCode:statusCode forAccount:account];
  2692. if (block) {
  2693. block(error);
  2694. }
  2695. }];
  2696. return task;
  2697. }
  2698. - (NSURLSessionDataTask *)getReminderForMessage:(NCChatMessage *)message withCompletionBlock:(GetReminderForMessage)block
  2699. {
  2700. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:message.accountId];
  2701. NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
  2702. NSString *encodedToken = [message.token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
  2703. NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld/reminder", encodedToken, (long)message.messageId];
  2704. NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];
  2705. NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];
  2706. NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
  2707. if (block) {
  2708. NSDictionary *responseDict = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];
  2709. block(responseDict, nil);
  2710. }
  2711. } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
  2712. NSInteger statusCode = [self getResponseStatusCode:task.response];
  2713. [self checkResponseStatusCode:statusCode forAccount:account];
  2714. if (block) {
  2715. block(nil, error);
  2716. }
  2717. }];
  2718. return task;
  2719. }
  2720. #pragma mark - Error handling
  2721. - (NSInteger)getResponseStatusCode:(NSURLResponse *)response
  2722. {
  2723. NSInteger statusCode = 0;
  2724. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  2725. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  2726. statusCode = httpResponse.statusCode;
  2727. }
  2728. return statusCode;
  2729. }
  2730. - (NSDictionary *)getFailureResponseObjectFromError:(NSError *)error
  2731. {
  2732. NSDictionary *responseDict = @{};
  2733. NSString* errorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
  2734. if (errorResponse.length == 0) {
  2735. return nil;
  2736. }
  2737. NSData *data = [errorResponse dataUsingEncoding:NSUTF8StringEncoding];
  2738. if (data) {
  2739. NSError* error;
  2740. NSDictionary* jsonData = [NSJSONSerialization JSONObjectWithData:data
  2741. options:0
  2742. error:&error];
  2743. if (jsonData) {
  2744. responseDict = jsonData;
  2745. } else {
  2746. NSLog(@"Error retrieving failure response object JSON data: %@", error);
  2747. }
  2748. }
  2749. return responseDict;
  2750. }
  2751. - (void)checkResponseStatusCode:(NSInteger)statusCode forAccount:(TalkAccount *)account
  2752. {
  2753. if (statusCode == 401) {
  2754. // App token has been revoked
  2755. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:account.accountId forKey:@"accountId"];
  2756. [[NSNotificationCenter defaultCenter] postNotificationName:NCTokenRevokedResponseReceivedNotification
  2757. object:self
  2758. userInfo:userInfo];
  2759. } else if (statusCode == 426) {
  2760. // Upgrade required
  2761. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:account.accountId forKey:@"accountId"];
  2762. [[NSNotificationCenter defaultCenter] postNotificationName:NCUpgradeRequiredResponseReceivedNotification
  2763. object:self
  2764. userInfo:userInfo];
  2765. } else if (statusCode == 503) {
  2766. // Server is in maintenance mode
  2767. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:account.accountId forKey:@"accountId"];
  2768. [[NSNotificationCenter defaultCenter] postNotificationName:NCServerMaintenanceModeNotification
  2769. object:self
  2770. userInfo:userInfo];
  2771. }
  2772. }
  2773. #pragma mark - Header handling
  2774. - (NSDictionary *)getResponseHeaders:(NSURLResponse *)response
  2775. {
  2776. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  2777. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  2778. return [httpResponse allHeaderFields];
  2779. }
  2780. return nil;
  2781. }
  2782. - (void)checkResponseHeaders:(NSDictionary *)headers forAccount:(TalkAccount *)account
  2783. {
  2784. NSString *modifiedSince = [headers objectForKey:@"X-Nextcloud-Talk-Modified-Before"];
  2785. NSString *configurationHash = [headers objectForKey:@"X-Nextcloud-Talk-Hash"];
  2786. if (modifiedSince.length > 0) {
  2787. [[NCDatabaseManager sharedInstance] updateLastModifiedSinceForAccountId:account.accountId with:modifiedSince];
  2788. }
  2789. if (!configurationHash) {
  2790. return;
  2791. }
  2792. if (![configurationHash isEqualToString:account.lastReceivedConfigurationHash]) {
  2793. if (account.lastReceivedConfigurationHash) {
  2794. // We previously stored a configuration hash which now changed -> Update settings and capabilities
  2795. NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
  2796. [userInfo setObject:account.accountId forKey:@"accountId"];
  2797. [userInfo setObject:configurationHash forKey:@"configurationHash"];
  2798. [[NSNotificationCenter defaultCenter] postNotificationName:NCTalkConfigurationHashChangedNotification
  2799. object:self
  2800. userInfo:userInfo];
  2801. } else {
  2802. [[NCDatabaseManager sharedInstance] updateTalkConfigurationHashForAccountId:account.accountId withHash:configurationHash];
  2803. }
  2804. }
  2805. }
  2806. - (void)checkProxyResponseHeaders:(NSDictionary *)headers forAccount:(TalkAccount *)account forRoom:(NSString *)token
  2807. {
  2808. NSString *proxyHash = [headers objectForKey:@"X-Nextcloud-Talk-Proxy-Hash"];
  2809. if (!proxyHash) {
  2810. return;
  2811. }
  2812. NSPredicate *query = [NSPredicate predicateWithFormat:@"token = %@ AND accountId = %@", token, account.accountId];
  2813. NCRoom *managedRoom = [NCRoom objectsWithPredicate:query].firstObject;
  2814. if (!managedRoom) {
  2815. // The room is not known to us locally, don't try to fetch room capabilities
  2816. return;
  2817. }
  2818. FederatedCapabilities *federatedCapabilities = [[NCDatabaseManager sharedInstance] federatedCapabilitiesForAccountId:managedRoom.accountId remoteServer:managedRoom.remoteServer roomToken:managedRoom.token];
  2819. if ([proxyHash isEqualToString:managedRoom.lastReceivedProxyHash] && federatedCapabilities != nil) {
  2820. // The proxy hash is equal to our last known proxy hash and we are also able to retrieve capabilities locally -> skip fetching capabilities
  2821. return;
  2822. }
  2823. [self getRoomCapabilitiesFor:account.accountId token:token completionBlock:^(NSDictionary<NSString *,id> * _Nullable capabilities, NSString * _Nullable proxyHash) {
  2824. if (capabilities && proxyHash) {
  2825. [[NCDatabaseManager sharedInstance] setFederatedCapabilities:capabilities forAccountId:account.accountId remoteServer:managedRoom.remoteServer roomToken:token withProxyHash:proxyHash];
  2826. }
  2827. }];
  2828. }
  2829. #pragma mark - NKCommon Delegate
  2830. - (void)authenticationChallenge:(NSURLSession *)session didReceive:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
  2831. {
  2832. // The pinnning check
  2833. if ([[CCCertificate sharedManager] checkTrustedChallenge:challenge]) {
  2834. completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
  2835. } else {
  2836. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  2837. }
  2838. }
  2839. @end
  2840. #pragma mark - OCURLSessionManager
  2841. @implementation OCURLSessionManager
  2842. - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
  2843. {
  2844. // The pinnning check
  2845. if ([[CCCertificate sharedManager] checkTrustedChallenge:challenge]) {
  2846. completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
  2847. } else {
  2848. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  2849. }
  2850. }
  2851. @end