cronapp-cordova-plugin-contentsync 4.4.0-RC7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +11 -0
  3. package/README.md +372 -0
  4. package/package-lock.json +1545 -0
  5. package/package.json +58 -0
  6. package/plugin.xml +93 -0
  7. package/sample/css/index.css +128 -0
  8. package/sample/img/logo.png +0 -0
  9. package/sample/index.html +47 -0
  10. package/sample/js/index.js +152 -0
  11. package/spec/helper/cordova.js +83 -0
  12. package/spec/index.spec.js +334 -0
  13. package/src/android/Sync.java +1102 -0
  14. package/src/browser/Sync.js +20 -0
  15. package/src/ios/ContentSync.h +74 -0
  16. package/src/ios/ContentSync.m +1002 -0
  17. package/src/ios/SSZipArchive.h +52 -0
  18. package/src/ios/SSZipArchive.m +540 -0
  19. package/src/ios/minizip/crypt.h +131 -0
  20. package/src/ios/minizip/ioapi.c +239 -0
  21. package/src/ios/minizip/ioapi.h +201 -0
  22. package/src/ios/minizip/mztools.c +284 -0
  23. package/src/ios/minizip/mztools.h +31 -0
  24. package/src/ios/minizip/unzip.c +2153 -0
  25. package/src/ios/minizip/unzip.h +437 -0
  26. package/src/ios/minizip/zip.c +2022 -0
  27. package/src/ios/minizip/zip.h +362 -0
  28. package/src/windows/SyncProxy.js +279 -0
  29. package/src/windows/ZipWinProj/PGZipInflate.cs +94 -0
  30. package/src/windows/ZipWinProj/Properties/AssemblyInfo.cs +30 -0
  31. package/src/windows/ZipWinProj/ZipWinProj.csproj +57 -0
  32. package/src/wp8/Sync.cs +746 -0
  33. package/src/wp8/Unzip.cs +481 -0
  34. package/tests/anyfile.txt +1 -0
  35. package/tests/archives/www1.zip +0 -0
  36. package/tests/archives/www2.zip +0 -0
  37. package/tests/package.json +11 -0
  38. package/tests/plugin.xml +22 -0
  39. package/tests/scripts/start-server.sh +2 -0
  40. package/tests/scripts/stop-server.sh +1 -0
  41. package/tests/tests.js +255 -0
  42. package/www/index.js +285 -0
@@ -0,0 +1,1002 @@
1
+ /*
2
+ Licensed to the Apache Software Foundation (ASF) under one
3
+ or more contributor license agreements. See the NOTICE file
4
+ distributed with this work for additional information
5
+ regarding copyright ownership. The ASF licenses this file
6
+ to you under the Apache License, Version 2.0 (the
7
+ "License"); you may not use this file except in compliance
8
+ with the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing,
13
+ software distributed under the License is distributed on an
14
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ KIND, either express or implied. See the License for the
16
+ specific language governing permissions and limitations
17
+ under the License.
18
+ */
19
+ #import "ContentSync.h"
20
+ #if TARGET_OS_IPHONE
21
+ #import <MobileCoreServices/MobileCoreServices.h>
22
+ #endif
23
+
24
+ #ifdef USE_COCOAPODS
25
+ #import <SSZipArchive/SSZipArchive.h>
26
+ #else
27
+ #import "SSZipArchive.h"
28
+ #endif
29
+
30
+ @implementation ContentSyncTask
31
+
32
+ - (ContentSyncTask *)init {
33
+ self = (ContentSyncTask*)[super init];
34
+ if(self) {
35
+ self.appId = nil;
36
+ self.downloadTask = nil;
37
+ self.command = nil;
38
+ self.archivePath = nil;
39
+ self.progress = 0;
40
+ self.extractArchive = YES;
41
+ }
42
+
43
+ return self;
44
+ }
45
+ @end
46
+
47
+ @interface ContentSync () <SSZipArchiveDelegate>
48
+ @end
49
+
50
+ @implementation ContentSync
51
+
52
+ - (void)pluginInitialize {
53
+ [NSURLProtocol registerClass:[NSURLProtocolNoCache class]];
54
+ }
55
+
56
+ - (id)getCordovaPluginInstance:(NSString*)pluginName {
57
+ id plugin = nil;
58
+ SEL getCommandInstanceSelector = NSSelectorFromString(@"getCommandInstance:");
59
+
60
+ if ([self.commandDelegate respondsToSelector:getCommandInstanceSelector]) {
61
+ #pragma clang diagnostic push
62
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
63
+ plugin = [self.commandDelegate performSelector:getCommandInstanceSelector withObject:pluginName];
64
+ #pragma clang diagnostic pop
65
+ }
66
+
67
+ if (plugin == nil) {
68
+ @try {
69
+ NSDictionary* pluginObjects = [self.viewController valueForKey:@"pluginObjects"];
70
+ plugin = [pluginObjects objectForKey:pluginName];
71
+ } @catch (NSException* exception) {
72
+ plugin = nil;
73
+ }
74
+ }
75
+
76
+ return plugin;
77
+ }
78
+
79
+ - (NSString*)resolveBasePathFromUrl:(NSString*)url {
80
+ if (url == nil || ![url hasPrefix:@"file://"]) {
81
+ return nil;
82
+ }
83
+
84
+ NSURL* fileURL = [NSURL URLWithString:url];
85
+ NSString* path = [fileURL path];
86
+
87
+ if (path == nil || [path isEqualToString:@""]) {
88
+ return nil;
89
+ }
90
+
91
+ NSFileManager* fileManager = [NSFileManager defaultManager];
92
+ BOOL isDirectory = NO;
93
+ BOOL exists = [fileManager fileExistsAtPath:path isDirectory:&isDirectory];
94
+ NSString* resolvedPath = path;
95
+
96
+ if (!exists) {
97
+ NSString* parent = [path stringByDeletingLastPathComponent];
98
+ NSString* grandParent = [parent stringByDeletingLastPathComponent];
99
+ NSString* fallbackRootIndex = [grandParent stringByAppendingPathComponent:@"index.html"];
100
+ NSString* fallbackWwwIndex = [parent stringByAppendingPathComponent:@"www/index.html"];
101
+
102
+ if ([fileManager fileExistsAtPath:fallbackRootIndex]) {
103
+ resolvedPath = fallbackRootIndex;
104
+ exists = YES;
105
+ isDirectory = NO;
106
+ } else if ([fileManager fileExistsAtPath:fallbackWwwIndex]) {
107
+ resolvedPath = fallbackWwwIndex;
108
+ exists = YES;
109
+ isDirectory = NO;
110
+ }
111
+ }
112
+
113
+ if (!exists) {
114
+ return nil;
115
+ }
116
+
117
+ return isDirectory ? resolvedPath : [resolvedPath stringByDeletingLastPathComponent];
118
+ }
119
+
120
+ - (BOOL)loadIonicServerBasePath:(NSString*)url command:(CDVInvokedUrlCommand*)command {
121
+ NSString* basePath = [self resolveBasePathFromUrl:url];
122
+
123
+ if (basePath == nil) {
124
+ return NO;
125
+ }
126
+
127
+ id ionicWebView = [self getCordovaPluginInstance:@"IonicWebView"];
128
+ SEL setServerBasePathSelector = NSSelectorFromString(@"setServerBasePath:");
129
+
130
+ if (ionicWebView == nil || ![ionicWebView respondsToSelector:setServerBasePathSelector]) {
131
+ return NO;
132
+ }
133
+
134
+ NSArray* jsonEntry = [NSArray arrayWithObjects:
135
+ command.callbackId,
136
+ @"IonicWebView",
137
+ @"setServerBasePath",
138
+ [NSArray arrayWithObject:basePath],
139
+ nil];
140
+ CDVInvokedUrlCommand* setBasePathCommand = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
141
+
142
+ NSLog(@"Loading Ionic server base path %@", basePath);
143
+ #pragma clang diagnostic push
144
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
145
+ [ionicWebView performSelector:setServerBasePathSelector withObject:setBasePathCommand];
146
+ #pragma clang diagnostic pop
147
+
148
+ return YES;
149
+ }
150
+
151
+ - (CDVPluginResult*) preparePluginResult:(NSInteger)progress status:(NSInteger)status {
152
+ CDVPluginResult *pluginResult = nil;
153
+
154
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
155
+ [message setObject:[NSNumber numberWithInteger:progress] forKey:@"progress"];
156
+ [message setObject:[NSNumber numberWithInteger:status] forKey:@"status"];
157
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
158
+
159
+ return pluginResult;
160
+ }
161
+
162
+ #if TARGET_OS_IOS
163
+ /**
164
+ * Returns the _applicationStorageDirectory_ similar to the one of the file plugin
165
+ */
166
+ + (NSURL*) getStorageDirectory {
167
+ NSFileManager* fm = [NSFileManager defaultManager];
168
+ NSArray *URLs = [fm URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask];
169
+ NSURL *libraryDirectoryUrl = [URLs objectAtIndex:0];
170
+ return [libraryDirectoryUrl URLByAppendingPathComponent:@"NoCloud"];
171
+ }
172
+
173
+ #else // TARGET_OS_MAC
174
+
175
+ /**
176
+ * Returns the _applicationStorageDirectory_ similar to the one of the file plugin
177
+ */
178
+ + (NSURL*) getStorageDirectory {
179
+ NSError* error;
180
+ NSFileManager* fm = [NSFileManager defaultManager];
181
+ NSURL* supportDir = [fm URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
182
+ if (supportDir == nil) {
183
+ NSLog(@"unable to get support directory: %@", error);
184
+ return nil;
185
+ }
186
+
187
+ NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier];
188
+ NSURL *dirPath = [[supportDir URLByAppendingPathComponent:bundleID] URLByAppendingPathComponent:@"files"];
189
+
190
+ if (![fm fileExistsAtPath:dirPath.path]) {
191
+ if (![fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&error]) {
192
+ NSLog(@"unable to create support directory: %@", error);
193
+ return nil;
194
+ }
195
+ }
196
+ return dirPath;
197
+ }
198
+ #endif // TAGET_OS_IOS
199
+
200
+ + (BOOL) hasAppBeenUpdated {
201
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
202
+ NSString* previousVersion = [defaults objectForKey:@"PREVIOUS_VERSION"];
203
+
204
+ NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
205
+ NSString* currentVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
206
+
207
+ NSLog(@"previous version %@", previousVersion);
208
+ NSLog(@"current version %@", currentVersion);
209
+
210
+ // set previous version to current version
211
+ [defaults setObject:currentVersion forKey:@"PREVIOUS_VERSION"];
212
+ [defaults synchronize];
213
+
214
+ if ([currentVersion compare:previousVersion options:NSNumericSearch] == NSOrderedDescending) {
215
+ NSLog(@"current version is newer than previous version");;
216
+ }
217
+
218
+ return ([currentVersion compare:previousVersion options:NSNumericSearch] == NSOrderedDescending);
219
+ }
220
+
221
+ - (void)sync:(CDVInvokedUrlCommand*)command {
222
+ __block NSString* src = [command argumentAtIndex:0 withDefault:nil];
223
+ __block NSString* type = [command argumentAtIndex:2];
224
+ __block BOOL local = [type isEqualToString:@"local"];
225
+
226
+ __block NSFileManager *fileManager = [NSFileManager defaultManager];
227
+ __block NSString* appId = [command argumentAtIndex:1];
228
+ __block NSURL* storageDirectory = [ContentSync getStorageDirectory];
229
+ __block NSURL *appPath = [storageDirectory URLByAppendingPathComponent:appId];
230
+ NSLog(@"appPath %@", appPath);
231
+
232
+ if(local == YES) {
233
+ NSLog(@"Requesting local copy of %@", appId);
234
+ if([fileManager fileExistsAtPath:[appPath path]]) {
235
+ if (![ContentSync hasAppBeenUpdated]) {
236
+ NSLog(@"Found local copy %@", [appPath path]);
237
+ CDVPluginResult *pluginResult = nil;
238
+
239
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
240
+ [message setObject:[appPath path] forKey:@"localPath"];
241
+ [message setObject:@"true" forKey:@"cached"];
242
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
243
+
244
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
245
+ return;
246
+ }
247
+ }
248
+ }
249
+
250
+ BOOL copyRootApp = [[command argumentAtIndex:5 withDefault:@(NO)] boolValue];
251
+
252
+ if(copyRootApp == YES) {
253
+ __block NSError* error = nil;
254
+
255
+ NSLog(@"Creating app directory %@", [appPath path]);
256
+ [fileManager createDirectoryAtPath:[appPath path] withIntermediateDirectories:YES attributes:nil error:&error];
257
+
258
+ __block NSError* errorSetting = nil;
259
+ __block BOOL success = [appPath setResourceValue: [NSNumber numberWithBool: YES]
260
+ forKey: NSURLIsExcludedFromBackupKey error: &errorSetting];
261
+
262
+ if(success == NO) {
263
+ NSLog(@"WARNING: %@ might be backed up to iCloud!", [appPath path]);
264
+ }
265
+
266
+ if(error != nil) {
267
+ CDVPluginResult *pluginResult = nil;
268
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
269
+ [message setObject:[NSNumber numberWithInteger:LOCAL_ERR] forKey:@"type"];
270
+ [message setObject:[NSNumber numberWithInteger:-1] forKey:@"responseCode"];
271
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
272
+ NSLog(@"%@", [error localizedDescription]);
273
+ } else {
274
+ [self.commandDelegate runInBackground:^{
275
+ CDVPluginResult *pluginResult = nil;
276
+ [self copyCordovaAssets:[appPath path] copyRootApp:YES];
277
+ if(src == nil) {
278
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
279
+ [message setObject:[appPath path] forKey:@"localPath"];
280
+ [message setObject:@"true" forKey:@"cached"];
281
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
282
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
283
+ return;
284
+ }
285
+ }];
286
+ }
287
+ }
288
+
289
+
290
+ __weak ContentSync* weakSelf = self;
291
+ if (local != YES && src != nil) {
292
+ [self.commandDelegate runInBackground:^{
293
+ [weakSelf startDownload:command extractArchive:YES];
294
+ }];
295
+ }
296
+ }
297
+
298
+ - (void) download:(CDVInvokedUrlCommand*)command {
299
+ __weak ContentSync* weakSelf = self;
300
+
301
+ [self.commandDelegate runInBackground:^{
302
+ [weakSelf startDownload:command extractArchive:NO];
303
+ }];
304
+ }
305
+
306
+ - (BOOL) isZipArchive:(NSString*)filePath {
307
+ NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:filePath];
308
+ NSData *data = [fh readDataOfLength:4];
309
+ if ([data length] == 4) {
310
+ const char *bytes = [data bytes];
311
+ if (bytes[0] == 'P' && bytes[1] == 'K' && bytes[2] == 3 && bytes[3] == 4) {
312
+ return YES;
313
+ }
314
+ }
315
+ return NO;
316
+ }
317
+
318
+ - (void)startDownload:(CDVInvokedUrlCommand*)command extractArchive:(BOOL)extractArchive {
319
+
320
+ CDVPluginResult* pluginResult = nil;
321
+ NSString* src = [command argumentAtIndex:0 withDefault:nil];
322
+ NSString* appId = [command argumentAtIndex:1];
323
+ NSNumber* timeout = [command argumentAtIndex:6 withDefault:[NSNumber numberWithDouble:15]];
324
+ BOOL validateSrc = [[command argumentAtIndex:9 withDefault:@(YES)] boolValue];
325
+
326
+ self.session = [self backgroundSession:timeout];
327
+ NSURL *srcURL = [NSURL URLWithString:src];
328
+
329
+ // Setting headers (do changes also in download url, or better extract setting the following lines to a function)
330
+ NSDictionary *headers = [command argumentAtIndex:3 withDefault:nil andClass:[NSDictionary class]];
331
+
332
+ // checking if URL is valid
333
+ BOOL srcIsValid = YES;
334
+
335
+ if (validateSrc == YES) {
336
+ NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:srcURL];
337
+ [urlRequest setHTTPMethod:@"HEAD"];
338
+
339
+ [self setHeaders:urlRequest :headers];
340
+
341
+ // request just to check if url is correct and server is available
342
+ NSHTTPURLResponse *response = nil;
343
+ NSError *error = nil;
344
+ [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
345
+
346
+ if (error || response.statusCode >= 400) {
347
+ srcIsValid = false;
348
+ }
349
+ }
350
+
351
+ if(srcURL && srcURL.scheme && srcURL.host && srcIsValid == YES) {
352
+
353
+ BOOL trustHost = (BOOL) [command argumentAtIndex:7 withDefault:@(NO)];
354
+
355
+ if(!self.trustedHosts) {
356
+ self.trustedHosts = [NSMutableArray arrayWithCapacity:1];
357
+ }
358
+
359
+ if(trustHost == YES) {
360
+ NSLog(@"WARNING: Trusting host %@", [srcURL host]);
361
+ [self.trustedHosts addObject:[srcURL host]];
362
+ }
363
+
364
+ NSLog(@"startDownload from %@", src);
365
+ NSURL *downloadURL = [NSURL URLWithString:src];
366
+
367
+ if (appId == nil) {
368
+ appId = [srcURL lastPathComponent];
369
+ }
370
+
371
+ // downloadURL is nil if malformed URL
372
+ if(downloadURL == nil) {
373
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:INVALID_URL_ERR];
374
+
375
+ #if !TARGET_OS_IOS // this is currently not added to ios. see issue-96
376
+ } else if ([self findSyncDataByAppId:appId]) {
377
+ NSLog(@"Download task already started for %@", appId);
378
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:IN_PROGRESS_ERR];
379
+ #endif
380
+ } else {
381
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:downloadURL];
382
+ request.timeoutInterval = 15.0;
383
+
384
+ [self setHeaders:request :headers];
385
+
386
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
387
+
388
+ ContentSyncTask* sData = [[ContentSyncTask alloc] init];
389
+
390
+ sData.appId = appId;
391
+ sData.downloadTask = downloadTask;
392
+ sData.command = command;
393
+ sData.progress = 0;
394
+ sData.extractArchive = extractArchive;
395
+
396
+ [self addSyncTask:sData];
397
+
398
+ [downloadTask resume];
399
+
400
+ pluginResult = [self preparePluginResult:sData.progress status:Downloading];
401
+ [pluginResult setKeepCallbackAsBool:YES];
402
+ }
403
+
404
+ } else {
405
+ NSLog(@"Invalid src URL %@", src);
406
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
407
+ [message setObject:[NSNumber numberWithInteger:INVALID_URL_ERR] forKey:@"type"];
408
+ [message setObject:[NSNumber numberWithInteger:-1] forKey:@"responseCode"];
409
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
410
+ }
411
+
412
+ [pluginResult setKeepCallbackAsBool:YES];
413
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
414
+
415
+ }
416
+
417
+ - (void)setHeaders:(NSMutableURLRequest*)request :(NSDictionary*)headers {
418
+ // Setting headers (do changes also in check if url is valid, or better extract setting the following lines to a function)
419
+ if(headers != nil) {
420
+ for (NSString* header in [headers allKeys]) {
421
+ NSLog(@"Setting header %@ %@", header, [headers objectForKey:header]);
422
+ [request addValue:[headers objectForKey:header] forHTTPHeaderField:header];
423
+ }
424
+ }
425
+ }
426
+
427
+ - (void)cancel:(CDVInvokedUrlCommand *)command {
428
+ NSString* appId = [command argumentAtIndex:0 withDefault:nil];
429
+ NSLog(@"Cancelling download %@", appId);
430
+ if(appId) {
431
+ ContentSyncTask* sTask = [self findSyncDataByAppId:appId];
432
+ if(sTask) {
433
+ CDVPluginResult* pluginResult = nil;
434
+ [[sTask downloadTask] cancel];
435
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
436
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
437
+ }
438
+ }
439
+ }
440
+
441
+ - (void) addSyncTask:(ContentSyncTask*) task {
442
+ @synchronized (self) {
443
+ if (!self.syncTasks) {
444
+ self.syncTasks = [NSMutableArray array];
445
+ }
446
+ [self.syncTasks addObject:task];
447
+ }
448
+ };
449
+
450
+ - (void) removeSyncTask:(ContentSyncTask*) task {
451
+ @synchronized (self) {
452
+ [self.syncTasks removeObject:task];
453
+ }
454
+ }
455
+
456
+ - (ContentSyncTask*) findSyncDataByDownloadTask:(NSURLSessionDownloadTask*) downloadTask {
457
+ @synchronized (self) {
458
+ for (ContentSyncTask* sTask in self.syncTasks) {
459
+ if (sTask.downloadTask == downloadTask) {
460
+ return sTask;
461
+ }
462
+ }
463
+ return nil;
464
+ }
465
+ }
466
+
467
+ - (ContentSyncTask*) findSyncDataByPath {
468
+ @synchronized (self) {
469
+ for (ContentSyncTask* sTask in self.syncTasks) {
470
+ if ([sTask.archivePath isEqualToString:[self currentPath]]) {
471
+ return sTask;
472
+ }
473
+ }
474
+ return nil;
475
+ }
476
+ }
477
+
478
+ - (ContentSyncTask*) findSyncDataByAppId:(NSString*) appId {
479
+ @synchronized (self) {
480
+ for (ContentSyncTask* sTask in self.syncTasks) {
481
+ if ([sTask.appId isEqualToString:appId]) {
482
+ return sTask;
483
+ }
484
+ }
485
+ return nil;
486
+ }
487
+ }
488
+
489
+ - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
490
+ if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
491
+ NSLog(@"Received challenge for host %@", challenge.protectionSpace.host);
492
+ if([self.trustedHosts containsObject:challenge.protectionSpace.host]) {
493
+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
494
+ completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
495
+ } else {
496
+ completionHandler(NSURLSessionAuthChallengeUseCredential,nil);
497
+ // completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
498
+ }
499
+ }
500
+ }
501
+
502
+ - (void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
503
+
504
+ CDVPluginResult* pluginResult = nil;
505
+
506
+ ContentSyncTask* sTask = [self findSyncDataByDownloadTask:(NSURLSessionDownloadTask*)downloadTask];
507
+
508
+ if(sTask) {
509
+ double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
510
+ //NSLog(@"DownloadTask: %@ progress: %lf callbackId: %@", downloadTask, progress, sTask.command.callbackId);
511
+ progress = (sTask.extractArchive == YES ? ((progress / 2) * 100) : progress * 100);
512
+ sTask.progress = progress;
513
+ pluginResult = [self preparePluginResult:sTask.progress status:Downloading];
514
+ [pluginResult setKeepCallbackAsBool:YES];
515
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
516
+ } else {
517
+ NSLog(@"Could not find download task");
518
+ }
519
+ }
520
+
521
+ - (void) URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL {
522
+
523
+
524
+ NSFileManager *fileManager = [NSFileManager defaultManager];
525
+ NSArray *URLs = [fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask];
526
+ NSURL *libraryDirectory = [URLs objectAtIndex:0];
527
+
528
+ NSURL *originalURL = [[downloadTask originalRequest] URL];
529
+ NSURL *sourceURL = [libraryDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
530
+ NSError *errorCopy;
531
+
532
+ [fileManager removeItemAtURL:sourceURL error:NULL];
533
+ BOOL success = [fileManager copyItemAtURL:downloadURL toURL:sourceURL error:&errorCopy];
534
+
535
+ ContentSyncTask* sTask = [self findSyncDataByDownloadTask:downloadTask];
536
+
537
+ if(success) {
538
+ if(sTask) {
539
+ sTask.archivePath = [sourceURL path];
540
+
541
+ NSFileManager *fileManager = [NSFileManager defaultManager];
542
+ NSString* type = [sTask.command argumentAtIndex:2 withDefault:@"replace"];
543
+ BOOL replace = [type isEqualToString:@"replace"];
544
+ NSURL *dstURL = [libraryDirectory URLByAppendingPathComponent:[sTask appId]];
545
+ if([fileManager fileExistsAtPath:[dstURL path]] && replace == YES) {
546
+ NSLog(@"%@ already exists. Deleting it since type is set to `replace`", [dstURL path]);
547
+ [fileManager removeItemAtURL:dstURL error:NULL];
548
+ }
549
+
550
+ if(sTask.extractArchive == YES && [self isZipArchive:[sourceURL path]]) {
551
+ // FIXME there is probably a better way to do this
552
+ NSURL *storageDirectory = [ContentSync getStorageDirectory];
553
+ NSURL *extractURL = [storageDirectory URLByAppendingPathComponent:[sTask appId]];
554
+ NSString* type = [sTask.command argumentAtIndex:2 withDefault:@"replace"];
555
+
556
+ CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:[NSArray arrayWithObjects:sTask.command.callbackId, @"Zip", @"unzip", [NSMutableArray arrayWithObjects:[sourceURL absoluteString], [extractURL absoluteString], type, nil], nil]];
557
+ [self unzip:command];
558
+ } else {
559
+ NSURL *srcURL = [NSURL fileURLWithPath:[sTask archivePath]];
560
+ NSError* error = nil;
561
+ NSError *errorCopy;
562
+ BOOL success;
563
+
564
+ success = [fileManager createDirectoryAtURL:[dstURL URLByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:&error];
565
+
566
+ if(success) {
567
+ NSLog(@"Moving %@ to %@", [srcURL path], [dstURL path]);
568
+
569
+ success = [fileManager moveItemAtURL:srcURL toURL:dstURL error:&errorCopy];
570
+ if(!success) {
571
+ NSLog(@"Error copying. File might already exist %@", [errorCopy description]);
572
+ }
573
+ sTask.archivePath = [dstURL path];
574
+ sTask.extractArchive = NO;
575
+ } else {
576
+ NSLog(@"Unable to create ID :-[ %@", [error description]);
577
+ }
578
+ }
579
+ }
580
+ } else {
581
+ NSLog(@"Sync Failed - Copy Failed - %@", [errorCopy localizedDescription]);
582
+
583
+ CDVPluginResult* pluginResult = nil;
584
+
585
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
586
+ [message setObject:[NSNumber numberWithInteger:CONNECTION_ERR] forKey:@"type"];
587
+ [message setObject:[NSNumber numberWithInteger:-1] forKey:@"responseCode"];
588
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
589
+
590
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
591
+ }
592
+ }
593
+
594
+ - (void) URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(NSError *)error {
595
+
596
+ ContentSyncTask* sTask = [self findSyncDataByDownloadTask:(NSURLSessionDownloadTask*)task];
597
+
598
+ if(sTask) {
599
+ CDVPluginResult* pluginResult = nil;
600
+
601
+ if(error == nil) {
602
+ if([(NSHTTPURLResponse*)[task response] statusCode] != 200) {
603
+ NSLog(@"Task: %@ completed with HTTP Error Code: %ld", task, (long)[(NSHTTPURLResponse*)[task response] statusCode]);
604
+
605
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
606
+ [message setObject:[NSNumber numberWithInteger:CONNECTION_ERR] forKey:@"type"];
607
+ [message setObject:[NSNumber numberWithInteger:[(NSHTTPURLResponse*)[task response] statusCode]] forKey:@"responseCode"];
608
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
609
+
610
+ NSFileManager *fileManager = [NSFileManager defaultManager];
611
+ if([fileManager fileExistsAtPath:[sTask archivePath]]) {
612
+ NSLog(@"Deleting archive. It's probably an HTTP Error Page anyways");
613
+ [fileManager removeItemAtPath:[sTask archivePath] error:NULL];
614
+ }
615
+ [self removeSyncTask:sTask];
616
+ } else {
617
+ double progress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;
618
+ NSLog(@"Task: %@ completed successfully", sTask.archivePath);
619
+ if(sTask.extractArchive) {
620
+ progress = ((progress / 2) * 100);
621
+ pluginResult = [self preparePluginResult:progress status:Downloading];
622
+ [pluginResult setKeepCallbackAsBool:YES];
623
+ }
624
+ else {
625
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
626
+ [message setObject:[NSNumber numberWithInteger:Complete] forKey:@"status"];
627
+ [message setObject:[sTask archivePath] forKey:@"localPath"];
628
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
629
+ [self removeSyncTask:sTask];
630
+ }
631
+ }
632
+ } else {
633
+ NSLog(@"Task: %@ completed with error: %@", task, [error localizedDescription]);
634
+
635
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
636
+ [message setObject:[NSNumber numberWithInteger:CONNECTION_ERR] forKey:@"type"];
637
+ [message setObject:[NSNumber numberWithInteger:[(NSHTTPURLResponse*)[task response] statusCode]] forKey:@"responseCode"];
638
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
639
+
640
+ [self removeSyncTask:sTask];
641
+ }
642
+ if(![[error localizedDescription] isEqual: @"cancelled"]) {
643
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
644
+ }
645
+ }
646
+ }
647
+
648
+ - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
649
+ NSLog(@"All tasks are finished");
650
+ }
651
+
652
+ - (void)unzip:(CDVInvokedUrlCommand*)command {
653
+ __weak ContentSync* weakSelf = self;
654
+ __block NSString* callbackId = command.callbackId;
655
+
656
+ [self.commandDelegate runInBackground:^{
657
+ CDVPluginResult* pluginResult = nil;
658
+
659
+ NSURL* sourceURL = [NSURL URLWithString:[command argumentAtIndex:0]];
660
+ NSURL* destinationURL = [NSURL URLWithString:[command argumentAtIndex:1]];
661
+
662
+ @try {
663
+ NSError *error;
664
+ if(![SSZipArchive unzipFileAtPath:[sourceURL path] toDestination:[destinationURL path] overwrite:YES password:nil error:&error delegate:weakSelf]) {
665
+ NSLog(@"%@ - %@", @"Error occurred during unzipping", [error localizedDescription]);
666
+
667
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
668
+ [message setObject:[NSNumber numberWithInteger:UNZIP_ERR] forKey:@"type"];
669
+ [message setObject:[NSNumber numberWithInteger:-1] forKey:@"responseCode"];
670
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
671
+ } else {
672
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
673
+ // clean up zip archive
674
+ NSFileManager *fileManager = [NSFileManager defaultManager];
675
+ [fileManager removeItemAtURL:sourceURL error:NULL];
676
+
677
+ }
678
+ }
679
+ @catch (NSException *exception) {
680
+ NSLog(@"%@ - %@", @"Error occurred during unzipping", [exception debugDescription]);
681
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
682
+ [message setObject:[NSNumber numberWithInteger:UNZIP_ERR] forKey:@"type"];
683
+ [message setObject:[NSNumber numberWithInteger:-1] forKey:@"responseCode"];
684
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:message];
685
+ }
686
+ [pluginResult setKeepCallbackAsBool:YES];
687
+
688
+ dispatch_async(dispatch_get_main_queue(), ^{
689
+ [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
690
+ });
691
+ }];
692
+ }
693
+
694
+
695
+ - (void)zipArchiveWillUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo {
696
+ self.currentPath = path;
697
+ }
698
+
699
+ - (void)zipArchiveProgressEvent:(unsigned long long)loaded total:(unsigned long long)total {
700
+ ContentSyncTask* sTask = [self findSyncDataByPath];
701
+ if(sTask) {
702
+ //NSLog(@"Extracting %ld / %ld", (long)loaded, (long)total);
703
+ double progress = ((double)loaded / (double)total);
704
+ progress = (sTask.extractArchive == YES ? ((0.5 + progress / 2) * 100) : progress * 100);
705
+ CDVPluginResult* pluginResult = [self preparePluginResult:progress status:Extracting];
706
+ [pluginResult setKeepCallbackAsBool:YES];
707
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
708
+ }
709
+ }
710
+
711
+ - (void) zipArchiveDidUnzipArchiveAtPath:(NSString *)path zipInfo:(unz_global_info)zipInfo unzippedPath:(NSString *)unzippedPath {
712
+ NSLog(@"unzipped path %@", unzippedPath);
713
+ ContentSyncTask* sTask = [self findSyncDataByPath];
714
+ if(sTask) {
715
+
716
+ BOOL copyCordovaAssets = [[sTask.command argumentAtIndex:4 withDefault:@(NO)] boolValue];
717
+
718
+ if(copyCordovaAssets == YES) {
719
+ [self copyCordovaAssets:unzippedPath];
720
+ }
721
+ // XXX this is to match the Android implementation
722
+ CDVPluginResult* pluginResult = [self preparePluginResult:100 status:Complete];
723
+ [pluginResult setKeepCallbackAsBool:YES];
724
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
725
+ // END
726
+
727
+ // Do not BACK UP folder to iCloud
728
+ [self addSkipBackupAttributeToItemAtPath:path];
729
+ [self addSkipBackupAttributeToItemAtPath:unzippedPath];
730
+
731
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
732
+ [message setObject:unzippedPath forKey:@"localPath"];
733
+ [message setObject:@"false" forKey:@"cached"];
734
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
735
+ [pluginResult setKeepCallbackAsBool:NO];
736
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:sTask.command.callbackId];
737
+ [self removeSyncTask:sTask];
738
+ }
739
+ }
740
+
741
+ - (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)path {
742
+ NSURL* appURL = [NSURL fileURLWithPath: path];
743
+ NSError *error = nil;
744
+ BOOL success = [appURL setResourceValue: [NSNumber numberWithBool: YES]
745
+ forKey: NSURLIsExcludedFromBackupKey error: &error];
746
+ if(!success){
747
+ NSLog(@"Error excluding %@ from backup %@", [appURL lastPathComponent], error);
748
+ }
749
+ return success;
750
+ }
751
+
752
+ - (void)mergeFolders:(NSString *)srcDir intoPath:(NSString *)dstDir error:(NSError**)err {
753
+
754
+ NSLog(@"- mergeFolders: %@\n intoPath: %@", srcDir, dstDir);
755
+
756
+ NSFileManager *fm = [NSFileManager defaultManager];
757
+ NSDirectoryEnumerator *srcDirEnum = [fm enumeratorAtPath:srcDir];
758
+ NSString *subPath;
759
+ while ((subPath = [srcDirEnum nextObject])) {
760
+ NSLog(@" subPath: %@", subPath);
761
+ NSString *srcFullPath = [srcDir stringByAppendingPathComponent:subPath];
762
+ NSString *potentialDstPath = [dstDir stringByAppendingPathComponent:subPath];
763
+
764
+ // Need to also check if file exists because if it doesn't, value of `isDirectory` is undefined.
765
+ BOOL isDirectory = ([[NSFileManager defaultManager] fileExistsAtPath:srcFullPath isDirectory:&isDirectory] && isDirectory);
766
+
767
+ // Create directory, or delete existing file and move file to destination
768
+ if (isDirectory) {
769
+ NSLog(@" create directory");
770
+ [fm createDirectoryAtPath:potentialDstPath withIntermediateDirectories:YES attributes:nil error:err];
771
+ if (err && *err) {
772
+ NSLog(@"ERROR: %@", *err);
773
+ return;
774
+ }
775
+ }
776
+ else {
777
+ if ([fm fileExistsAtPath:potentialDstPath]) {
778
+ NSLog(@" removeItemAtPath");
779
+ [fm removeItemAtPath:potentialDstPath error:err];
780
+ if (err && *err) {
781
+ NSLog(@"ERROR: %@", *err);
782
+ return;
783
+ }
784
+ }
785
+
786
+ NSLog(@" copyItemAtPath");
787
+ [fm copyItemAtPath:srcFullPath toPath:potentialDstPath error:err];
788
+ if (err && *err) {
789
+ NSLog(@"ERROR: %@", *err);
790
+ return;
791
+ }
792
+ }
793
+ }
794
+ }
795
+
796
+ - (BOOL) copyCordovaAssets:(NSString*)unzippedPath {
797
+ return [self copyCordovaAssets:unzippedPath copyRootApp:false];
798
+ }
799
+
800
+ // TODO GET RID OF THIS
801
+ // THIS F!@#% BS
802
+ - (BOOL) copyCordovaAssets:(NSString*)unzippedPath copyRootApp:(BOOL)copyRootApp {
803
+ NSLog(@"copyCordovaAssets");
804
+ NSError *errorCopy;
805
+ NSFileManager *fileManager = [NSFileManager defaultManager];
806
+ NSURL* destinationURL = [NSURL fileURLWithPath:unzippedPath];
807
+
808
+ if(copyRootApp == YES) {
809
+ NSLog(@"Copying Root App");
810
+ NSString* suffix = @"/www";
811
+ NSString* destDir = unzippedPath;
812
+
813
+ if([fileManager fileExistsAtPath:[unzippedPath stringByAppendingString:suffix]]) {
814
+ destDir = [unzippedPath stringByAppendingString:suffix];
815
+ NSLog(@"Found %@ folder. Will copy root application to it.", suffix);
816
+ }
817
+ // we use cordova.js as a way to find the root www/
818
+ NSString* root = [[[self commandDelegate] pathForResource:@"cordova.js"] stringByDeletingLastPathComponent];
819
+
820
+ NSError *mergeError = nil;
821
+ [self mergeFolders:root intoPath:destDir error:&mergeError];
822
+ if(mergeError != nil) {
823
+ NSLog(@"An error occurred: %@", [mergeError localizedDescription]);
824
+ return NO;
825
+ }
826
+
827
+ return YES;
828
+ }
829
+ NSLog(@"Copying Cordova Assets");
830
+ NSArray* cordovaAssets = [NSArray arrayWithObjects:@"cordova.js",@"cordova_plugins.js", @"plugins",nil];
831
+ NSString* suffix = @"/www";
832
+
833
+ NSURL* assetsSourceURL = [NSURL fileURLWithPath:[[self commandDelegate] pathForResource:@"plugins"]];
834
+ NSString *assetsSourceURLString = assetsSourceURL.absoluteString;
835
+ NSArray* subp = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:assetsSourceURLString error:Nil];
836
+
837
+ if([fileManager fileExistsAtPath:[unzippedPath stringByAppendingString:suffix]]) {
838
+ destinationURL = [destinationURL URLByAppendingPathComponent:suffix];
839
+ NSLog(@"Found %@ folder. Will copy Cordova assets to it.", suffix);
840
+ }
841
+
842
+ for(NSString* asset in subp) {
843
+ NSURL* assetSourceURL = [NSURL fileURLWithPath:[[self commandDelegate] pathForResource:[NSString stringWithFormat:@"%@/%@", @"plugins", asset]]];
844
+ NSURL* assetDestinationURL = [destinationURL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@/%@", @"plugins", asset]];
845
+
846
+ [fileManager copyItemAtURL:assetSourceURL toURL:assetDestinationURL error:&errorCopy];
847
+ }
848
+
849
+ for(NSString* asset in cordovaAssets) {
850
+ NSURL* assetSourceURL = [NSURL fileURLWithPath:[[self commandDelegate] pathForResource:asset]];
851
+ NSURL* assetDestinationURL = [destinationURL URLByAppendingPathComponent:[assetSourceURL lastPathComponent]];
852
+ //Removido
853
+ //[fileManager removeItemAtURL:assetDestinationURL error:NULL];
854
+ [fileManager copyItemAtURL:assetSourceURL toURL:assetDestinationURL error:&errorCopy];
855
+
856
+ }
857
+
858
+ return YES;
859
+ }
860
+
861
+ #ifdef __CORDOVA_4_0_0
862
+ #if TARGET_OS_IPHONE
863
+ - (void)loadUrl:(CDVInvokedUrlCommand*) command {
864
+ NSString* url = [command argumentAtIndex:0 withDefault:nil];
865
+ if(url != nil) {
866
+ NSLog(@"Loading URL %@", url);
867
+ if ([self loadIonicServerBasePath:url command:command]) {
868
+ return;
869
+ }
870
+ NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
871
+ [self.webViewEngine loadRequest:request];
872
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
873
+ } else {
874
+ NSLog(@"URL IS NIL");
875
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"URL is required."] callbackId:command.callbackId];
876
+ }
877
+ }
878
+ #endif
879
+ #endif
880
+
881
+ - (NSURLSession*) backgroundSession:(NSNumber*) timeout {
882
+ NSString *sessionId = [NSString stringWithFormat:@"%@-download-task", [[NSBundle mainBundle] bundleIdentifier]];
883
+
884
+ static NSURLSession *session = nil;
885
+ static dispatch_once_t onceToken;
886
+ dispatch_once(&onceToken, ^{
887
+ NSURLSessionConfiguration *configuration;
888
+
889
+ #if TARGET_OS_IOS
890
+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >=8.0f)
891
+ {
892
+ configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionId];
893
+ }
894
+ else
895
+ {
896
+ #pragma clang diagnostic push
897
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
898
+ configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionId];
899
+ #pragma clang diagnostic pop
900
+ }
901
+ #else
902
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_0
903
+ #pragma clang diagnostic push
904
+ #pragma ide diagnostic ignored "UnavailableInDeploymentTarget"
905
+ configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionId];
906
+ #pragma clang diagnostic pop
907
+ #else
908
+ configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionId];
909
+ #endif
910
+ #endif
911
+
912
+ configuration.timeoutIntervalForRequest = [timeout doubleValue];
913
+ session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
914
+ });
915
+ return session;
916
+ }
917
+ @end
918
+
919
+ /**
920
+ * NSURLProtocolNoCache
921
+ *
922
+ * URL Protocol handler to prevent caching of local assets.
923
+ */
924
+
925
+ @implementation NSURLProtocolNoCache
926
+
927
+
928
+ /**
929
+ * Should this request be handled by this protocol handler?
930
+ *
931
+ * We disable caching on requests using the file:// protocol and prefixed with the app's Library directory
932
+ * In the future, we may want to limit this or enable it based on configuration or not.
933
+ *
934
+ * @param theRequest is the inbound NSURLRequest.
935
+ * @return YES to handle this request with the this NSURLProtocol handler.
936
+ */
937
+ + (BOOL)canInitWithRequest:(NSURLRequest*)theRequest {
938
+ NSURL* libraryDirectoryUrl = [ContentSync getStorageDirectory];
939
+ return [theRequest.URL.scheme isEqualToString:@"file"] && [theRequest.URL.path hasPrefix:[libraryDirectoryUrl path]];
940
+ }
941
+
942
+ /**
943
+ * Canonical request definition.
944
+ *
945
+ * We keep it simple and map each request directly to itself.
946
+ *
947
+ * @param theRequest is the inbound NSURLRequest.
948
+ * @return the same inbound NSURLRequest object.
949
+ */
950
+
951
+ + (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)theRequest {
952
+ return theRequest;
953
+ }
954
+
955
+ /**
956
+ * Start loading the request.
957
+ *
958
+ * When loading a request, the request headers are altered to prevent browser caching.
959
+ */
960
+
961
+ - (void)startLoading {
962
+ NSData *data = [NSData dataWithContentsOfFile:self.request.URL.path];
963
+
964
+ // Whether as a bug or intentionally, it seems that as of iOS 10 the response's MIME Type is
965
+ // defaulting to 'application/octet-stream' for most files. For now we can set it manually.
966
+ // MIME lookup taken from:
967
+ // http://stackoverflow.com/questions/1363813/how-can-you-read-a-files-mime-type-in-objective-c/21858677#21858677
968
+ NSString *fileExtension = self.request.URL.pathExtension;
969
+ NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
970
+ NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
971
+
972
+ // add the no-cache and MIME HEADERs to the request while preserving the existing HEADER values.
973
+ NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:self.request.allHTTPHeaderFields];
974
+ headers[@"Cache-Control"] = @"no-cache";
975
+ headers[@"Pragma"] = @"no-cache";
976
+ headers[@"Content-Length"] = [NSString stringWithFormat:@"%d", (int)[data length]];
977
+ headers[@"Content-Type"] = contentType;
978
+
979
+ // create a response using the request and our new HEADERs
980
+ NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
981
+ statusCode:200
982
+ HTTPVersion:@"1.1"
983
+ headerFields:headers];
984
+
985
+ // deliver the response and enable in-memory caching (we may want to completely disable this if issues arise)
986
+ [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowedInMemoryOnly];
987
+ [self.client URLProtocol:self didLoadData:data];
988
+ [self.client URLProtocolDidFinishLoading:self];
989
+ }
990
+
991
+ /**
992
+ * Stop loading the request.
993
+ *
994
+ * When the request is cancelled, we have an opportunity to clean up and/or recover. However, for our purpose
995
+ * the ContentSync class will notify the user that the connection failed.
996
+ */
997
+
998
+ - (void)stopLoading {
999
+ NSLog(@"NSURLProtocolNoCache request was cancelled.");
1000
+ }
1001
+
1002
+ @end