github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/clients/ios-objc/photobackup/LACamliClient/LACamliClient.m (about) 1 // 2 // LACamliClient.m 3 // 4 // Created by Nick O'Neill on 1/10/13. 5 // Copyright (c) 2013 The Camlistore Authors. All rights reserved. 6 // 7 8 #import "LACamliClient.h" 9 #import "LACamliUploadOperation.h" 10 #import "LACamliFile.h" 11 #import "LACamliUtil.h" 12 13 @implementation LACamliClient 14 15 NSString *const CamliNotificationUploadStart = @"camli-upload-start"; 16 NSString *const CamliNotificationUploadProgress = @"camli-upload-progress"; 17 NSString *const CamliNotificationUploadEnd = @"camli-upload-end"; 18 19 - (id)initWithServer:(NSURL *)server username:(NSString *)username andPassword:(NSString *)password 20 { 21 NSParameterAssert(server); 22 NSParameterAssert(username); 23 NSParameterAssert(password); 24 25 if (self = [super init]) { 26 self.serverURL = server; 27 self.username = username; 28 self.password = password; 29 30 if ([[NSFileManager defaultManager] fileExistsAtPath:[self uploadedBlobRefArchivePath]]) { 31 self.uploadedBlobRefs = [NSMutableArray arrayWithContentsOfFile:[self uploadedBlobRefArchivePath]]; 32 } 33 if (!self.uploadedBlobRefs) { 34 self.uploadedBlobRefs = [NSMutableArray array]; 35 } 36 [LACamliUtil logText:@[@"uploads in cache: ",[NSString stringWithFormat:@"%d",[_uploadedBlobRefs count]]]]; 37 38 self.uploadQueue = [[NSOperationQueue alloc] init]; 39 self.uploadQueue.maxConcurrentOperationCount = 1; 40 self.totalUploads = 0; 41 42 self.isAuthorized = false; 43 self.authorizing = false; 44 45 _sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 46 _sessionConfig.HTTPAdditionalHeaders = @{@"Authorization": [NSString stringWithFormat:@"Basic %@",[self encodedAuth]]}; 47 } 48 49 return self; 50 } 51 52 #pragma mark - ready state 53 54 - (BOOL)readyToUpload 55 { 56 // can't upload if we don't have credentials 57 if (!self.username || !self.password || !self.serverURL) { 58 [LACamliUtil logText:@[@"not ready: no u/p/s"]]; 59 return NO; 60 } 61 62 // don't want to start a new upload if we're already going 63 if ([self.uploadQueue operationCount] > 0) { 64 [LACamliUtil logText:@[@"not ready: already uploading"]]; 65 return NO; 66 } 67 68 [LACamliUtil logText:@[@"starting upload"]]; 69 return YES; 70 } 71 72 #pragma mark - discovery 73 74 - (void)discoveryWithUsername:(NSString *)user andPassword:(NSString *)pass 75 { 76 [LACamliUtil statusText:@[@"discovering..."]]; 77 self.authorizing = YES; 78 79 NSURLSessionConfiguration *discoverConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; 80 discoverConfig.HTTPAdditionalHeaders = @{@"Accept": @"text/x-camli-configuration", @"Authorization": [NSString stringWithFormat:@"Basic %@",[self encodedAuth]]}; 81 NSURLSession *discoverSession = [NSURLSession sessionWithConfiguration:discoverConfig delegate:self delegateQueue:nil]; 82 83 NSURLSessionDataTask *data = [discoverSession dataTaskWithURL:self.serverURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 84 85 if (error) { 86 LALog(@"error discovery: %@",error); 87 [LACamliUtil errorText:@[@"discovery error: ",[error description]]]; 88 } else { 89 NSHTTPURLResponse *res = (NSHTTPURLResponse *)response; 90 91 if (res.statusCode != 200) { 92 LALog(@"error with discovery: %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); 93 [LACamliUtil errorText:@[@"error discovery: ",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]]; 94 [LACamliUtil logText:@[[NSString stringWithFormat:@"server said: %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]]]; 95 96 [[NSNotificationCenter defaultCenter] postNotificationName:CamliNotificationUploadEnd object:nil]; 97 } else { 98 NSError *err; 99 NSDictionary *config = [NSJSONSerialization JSONObjectWithData:data options:0 error:&err]; 100 if (!err) { 101 self.blobRootComponent = config[@"blobRoot"]; 102 [LACamliUtil logText:@[[NSString stringWithFormat:@"Welcome to %@'s camlistore",config[@"ownerName"]]]]; 103 104 self.isAuthorized = YES; 105 [self.uploadQueue setSuspended:NO]; 106 107 LALog(@"good discovery: %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); 108 [LACamliUtil statusText:@[@"discovery OK"]]; 109 } else { 110 111 LALog(@"couldn't deserialize discovery json"); 112 [LACamliUtil errorText:@[@"bad json from discovery", [err description]]]; 113 [LACamliUtil logText:@[@"json from discovery: ",[err description]]]; 114 } 115 } 116 } 117 }]; 118 119 [data resume]; 120 } 121 122 #pragma mark - upload methods 123 124 - (BOOL)fileAlreadyUploaded:(LACamliFile *)file 125 { 126 NSParameterAssert(file); 127 128 if ([self.uploadedBlobRefs containsObject:file.blobRef]) { 129 return YES; 130 } 131 132 return NO; 133 } 134 135 // starts uploading immediately 136 - (void)addFile:(LACamliFile *)file withCompletion:(void (^)())completion 137 { 138 NSParameterAssert(file); 139 140 self.totalUploads++; 141 142 if (![self isAuthorized]) { 143 [self.uploadQueue setSuspended:YES]; 144 145 if (!self.authorizing) { 146 [self discoveryWithUsername:self.username andPassword:self.password]; 147 } 148 } 149 150 LACamliUploadOperation *op = [[LACamliUploadOperation alloc] initWithFile:file andClient:self]; 151 __block LACamliUploadOperation *weakOp = op; 152 op.completionBlock = ^{ 153 LALog(@"finished op %@",file.blobRef); 154 if ([_delegate respondsToSelector:@selector(finishedUploadOperation:)]) { 155 [_delegate performSelector:@selector(finishedUploadOperation:) onThread:[NSThread mainThread] withObject:weakOp waitUntilDone:NO]; 156 } 157 158 if (weakOp.failedTransfer) { 159 LALog(@"failed transfer"); 160 } else { 161 [self.uploadedBlobRefs addObject:file.blobRef]; 162 [self.uploadedBlobRefs writeToFile:[self uploadedBlobRefArchivePath] atomically:YES]; 163 } 164 weakOp = nil; 165 166 if (![self.uploadQueue operationCount]) { 167 self.totalUploads = 0; 168 } 169 if (completion) { 170 completion(); 171 } 172 }; 173 174 if ([_delegate respondsToSelector:@selector(addedUploadOperation:)]) { 175 [_delegate performSelector:@selector(addedUploadOperation:) onThread:[NSThread mainThread] withObject:weakOp waitUntilDone:NO]; 176 } 177 178 [self.uploadQueue addOperation:op]; 179 } 180 181 #pragma mark - utility 182 183 - (NSURL *)blobRoot 184 { 185 return [self.serverURL URLByAppendingPathComponent:self.blobRootComponent]; 186 } 187 188 - (NSURL *)statUrl 189 { 190 return [[self blobRoot] URLByAppendingPathComponent:@"camli/stat"]; 191 } 192 193 - (NSURL *)uploadUrl 194 { 195 return [[self blobRoot] URLByAppendingPathComponent:@"camli/upload"]; 196 } 197 198 - (NSString *)encodedAuth 199 { 200 NSString *auth = [NSString stringWithFormat:@"%@:%@",self.username,self.password]; 201 202 return [LACamliUtil base64EncodedStringFromString:auth]; 203 } 204 205 - (NSString *)uploadedBlobRefArchivePath 206 { 207 NSString *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; 208 209 return [documents stringByAppendingPathComponent:@"uploadedRefs.plist"]; 210 } 211 212 @end