Revision d8426ffb pithos-macos/pithos_macosAppDelegate.m

b/pithos-macos/pithos_macosAppDelegate.m
36 36
// or implied, of GRNET S.A.
37 37

  
38 38
#import "pithos_macosAppDelegate.h"
39
#import "PithosAccount.h"
39 40
#import "PithosBrowserController.h"
40 41
#import "PithosPreferencesController.h"
41 42
#import "PithosSyncDaemon.h"
42 43
#import "ASIPithosRequest.h"
43 44
#import "ASIPithos.h"
44 45
#import "ASIDownloadCache.h"
46
#import "LastCompletedSyncTransformer.h"
45 47

  
46 48
@implementation pithos_macosAppDelegate
47
@synthesize pithos, pithosBrowserController, pithosSyncDaemon, alwaysNo, aboutVersion;
49
@synthesize pithosBrowserController, alwaysNo, aboutVersion, activated, currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount;
48 50

  
49 51
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
50
    userDefaults = [[NSUserDefaults standardUserDefaults] retain];
51
    NSString *stringURL;
52
    NSURL *testURL;
52
    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
53
                                                       andSelector:@selector(handleAppleEvent:withReplyEvent:) 
54
                                                     forEventClass:kInternetEventClass 
55
                                                        andEventID:kAEGetURL];
53 56
    
54
    stringURL = [userDefaults stringForKey:@"storageURLPrefix"];
55
    testURL = (stringURL) ? [NSURL URLWithString:stringURL] : nil;
56
    if (!testURL || !testURL.scheme || !testURL.host)
57
        [userDefaults setObject:@"https://plus.pithos.grnet.gr/v1" forKey:@"storageURLPrefix"];
58

  
59
    stringURL = [userDefaults stringForKey:@"publicURLPrefix"];
60
    testURL = (stringURL) ? [NSURL URLWithString:stringURL] : nil;
61
    if (!testURL || !testURL.scheme || !testURL.host)
62
        [userDefaults setObject:@"https://plus.pithos.grnet.gr" forKey:@"publicURLPrefix"];
63
        
64
    stringURL = [userDefaults stringForKey:@"loginURLPrefix"];
65
    testURL = (stringURL) ? [NSURL URLWithString:stringURL] : nil;
66
    if (!testURL || !testURL.scheme || !testURL.host)
67
        [userDefaults setObject:@"https://plus.pithos.grnet.gr/login" forKey:@"loginURLPrefix"];
57
    userDefaults = [[NSUserDefaults standardUserDefaults] retain];
68 58

  
69
    stringURL = [userDefaults stringForKey:@"aboutURL"];
70
    testURL = (stringURL) ? [NSURL URLWithString:stringURL] : nil;
71
    if (!testURL || !testURL.scheme || !testURL.host)
72
        [userDefaults setObject:@"https://pithos.dev.grnet.gr/docs" forKey:@"aboutURL"];
59
    NSString *stringURL = [userDefaults stringForKey:@"aboutURL"];
60
    NSURL *testURL = (stringURL) ? [NSURL URLWithString:stringURL] : nil;
61
    if (!testURL || !testURL.scheme || !testURL.host) {
62
        [userDefaults setObject:@"https://pithos.dev.grnet.gr/docs/pithos" forKey:@"aboutURL"];
63
        [userDefaults synchronize];
64
    }
73 65
    
74
    NSString *syncDirectoryPath = [userDefaults stringForKey:@"syncDirectoryPath"];
75
    if (!syncDirectoryPath || ![syncDirectoryPath length]) {
76
        syncDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"Pithos"];
77
    } else {
78
        NSFileManager *fileManager = [NSFileManager defaultManager];
79
        BOOL isDirectory;
80
        BOOL fileExists = [fileManager fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory];
81
        NSError *error = nil;
82
        if ((fileExists && !isDirectory) || 
83
            (!fileExists && (![fileManager createDirectoryAtPath:syncDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error] || error))) {
84
            syncDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"Pithos"];
85
        }   
66
    syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"];
67
    if (syncTimeInterval <= 0.0) {
68
        syncTimeInterval = 180.0;
69
        [userDefaults setDouble:syncTimeInterval forKey:@"syncTimeInterval"];
70
        [userDefaults synchronize];
86 71
    }
87
    [userDefaults setObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
88 72
    
89
    NSString *syncContainerName = [userDefaults stringForKey:@"syncContainerName"];
90
    if (!syncContainerName || ![syncContainerName length] || [syncContainerName isEqualToString:@"trash"])
91
        [userDefaults setObject:@"pithos" forKey:@"syncContainerName"];
92

  
93
    double syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"];
94
    if (syncTimeInterval <= 0)
95
        [userDefaults setDouble:180.0 forKey:@"syncTimeInterval"];
73
    NSData *tmpData = [userDefaults objectForKey:@"pithosAccounts"];
74
    NSArray *tmpArray;
75
    if (tmpData && (tmpArray = [NSKeyedUnarchiver unarchiveObjectWithData:tmpData]))
76
        self.pithosAccounts = [NSMutableArray arrayWithArray:tmpArray];
77
    else
78
        self.pithosAccounts = [NSMutableArray array];
96 79
    
97
    [userDefaults synchronize];
80
    if (![pithosAccounts count]) {
81
        [pithosAccounts addObject:[PithosAccount pithosAccount]];
82
        self.pithosAccounts = self.pithosAccounts;
83
    } else {
84
        self.activated = YES;
85
    }
98 86
    
99
    [userDefaults addObserver:self 
100
                   forKeyPath:@"syncDirectoryPath"
101
                      options:NSKeyValueObservingOptionNew
102
                      context:NULL];
103
     
104
    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
105
                                                       andSelector:@selector(handleAppleEvent:withReplyEvent:) 
106
                                                     forEventClass:kInternetEventClass 
107
                                                        andEventID:kAEGetURL];
87
    pithosAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[pithosAccounts count]];
88
    for (PithosAccount *pithosAccount in pithosAccounts) {
89
        [pithosAccountsDictionary setObject:pithosAccount forKey:pithosAccount.name];
90
        if (!currentPithosAccount && pithosAccount.active)
91
            currentPithosAccount = [pithosAccount retain];
92
    }
93
    if (!currentPithosAccount)
94
        self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
108 95
    
109
    [self showPithosBrowser:self];
110
    self.pithos = [ASIPithos pithos];
111
    [self authenticate];
96
    if (currentPithosAccount.active) {
97
        [self savePithosAccounts:self];
98
        [self showPithosBrowser:self];
99
        pithosBrowserController.pithos = currentPithosAccount.pithos;
100
    } else {
101
        // XXX maybe call specifically to go to new account tab
102
        [self showPithosPreferences:self];
103
    }
104

  
105
    syncTimer = [[NSTimer scheduledTimerWithTimeInterval:syncTimeInterval 
106
                                                  target:self 
107
                                                selector:@selector(sync) 
108
                                                userInfo:nil 
109
                                                 repeats:YES] retain];
110
    [syncTimer fire];
112 111
}
113 112

  
114 113
// Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/
......
135 134
    NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
136 135
    NSString *host = [url host];
137 136
	NSString *query = [url query];
138
    NSLog(@"host : '%@', query: '%@'", host, query);
137
    PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[url lastPathComponent]];
139 138
    NSProcessInfo *processInfo = [NSProcessInfo processInfo];
140
    if ([host isEqualToString:[NSString stringWithFormat:@"%@_%d", [processInfo processName], [processInfo processIdentifier]]] && query) {
139
    if ([host isEqualToString:[NSString stringWithFormat:@"%@_%d", [processInfo processName], [processInfo processIdentifier]]] && 
140
        pithosAccount && query) {
141 141
        // user=
142 142
        NSString *authUser;
143 143
        NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch];
......
173 173
        
174 174
        NSLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken);
175 175
        if ([authUser length] && [authToken length]) {
176
            [userDefaults setObject:authUser forKey:@"authUser"];
177
            [userDefaults setObject:authToken forKey:@"authToken"];
178
            [userDefaults synchronize];
179

  
180
            [self authenticate];
176
            [pithosAccount authenticateWithServerURL:nil authUser:authUser authToken:authToken];
177
            [self savePithosAccounts:self];
178
            if (pithosPreferencesController && [pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) {
179
                pithosPreferencesController.authUser = pithosAccount.authUser;
180
                pithosPreferencesController.authToken = pithosAccount.authToken;
181
            }
182
            self.activated = YES;
183
            if ([pithosAccount isEqualTo:currentPithosAccount]) {
184
                [self showPithosBrowser:self];
185
                pithosBrowserController.pithos = pithosAccount.pithos;
186
            }
181 187
        }
182 188
        // XXX else maybe show an error message?
183 189
    }
184 190
    // XXX else maybe show an error message?
185 191
}
186 192

  
187
#pragma mark -
188
#pragma Observers
189

  
190
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
191
    if ([object isEqualTo:userDefaults] && [keyPath isEqualToString:@"syncDirectoryPath"]) {
192
        [self authenticate];
193
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
194
    [self savePithosAccounts:self];
195
    if ([self.pithosBrowserController operationsPending]) {
196
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
197
        [alert setMessageText:@"Pending Operations"];
198
        [alert setInformativeText:@"There are pending operations in the browser, do you want to quit and cancel them?"];
199
        [alert addButtonWithTitle:@"OK"];
200
        [alert addButtonWithTitle:@"Cancel"];
201
        NSInteger choice = [alert runModal];
202
        if (choice == NSAlertSecondButtonReturn) 
203
            return NSTerminateCancel;
193 204
    }
205
    return NSTerminateNow;
194 206
}
195 207

  
196 208
#pragma mark -
197
#pragma Properties
209
#pragma mark Properties
198 210

  
199 211
- (NSString *)aboutVersion {
200 212
    return [NSString stringWithFormat:@"About Pithos+ %@ (%@)", 
......
203 215
}
204 216

  
205 217
#pragma mark -
206
#pragma Actions
218
#pragma mark NSMenuDelegate
219

  
220
- (void)menuNeedsUpdate:(NSMenu *)menu {
221
    NSMenuItem *menuItem;
222
    [menu removeAllItems];
223
    if ([menu isEqualTo:accountsMenu]) {
224
        [menu setAutoenablesItems:NO];
225
        for (PithosAccount *pithosAccount in pithosAccounts) {
226
            menuItem = [[[NSMenuItem alloc] initWithTitle:pithosAccount.name 
227
                                                   action:@selector(menuChangePithosAccount:) 
228
                                            keyEquivalent:@""] autorelease];
229
            [menuItem setRepresentedObject:pithosAccount];
230
            [menuItem setEnabled:pithosAccount.active];
231
            [menuItem setState:((pithosAccount.active && [currentPithosAccount isEqualTo:pithosAccount]) ? NSOnState : NSOffState)];
232
            [menu addItem:menuItem];
233
        }
234
    } else if ([menu isEqualTo:lastSyncMenu]) {
235
        NSString *menuItemTitle;
236
        [menu setAutoenablesItems:NO];
237
        for (PithosAccount *pithosAccount in pithosAccounts) {
238
            menuItemTitle = [NSString stringWithFormat:@"%@: %@", 
239
                             pithosAccount.name, 
240
                             [[[[LastCompletedSyncTransformer alloc] init] autorelease] transformedValue:pithosAccount.syncLastCompleted]];
241
            if ([pithosAccount isEqualTo:syncPithosAccount] && [pithosAccount.syncDaemon isSyncing])
242
                menuItemTitle = [menuItemTitle stringByAppendingString:@" (syncing)"];
243
            menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle 
244
                                                   action:@selector(menuChangeSyncActive:) 
245
                                            keyEquivalent:@""] autorelease];
246
            [menuItem setRepresentedObject:pithosAccount];
247
            [menuItem setEnabled:pithosAccount.active];
248
            [menuItem setState:((pithosAccount.active && pithosAccount.syncActive) ? NSOnState : NSOffState)];
249
            [menu addItem:menuItem];
250
        }
251
        [menu addItem:[NSMenuItem separatorItem]];
252
        [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"Next Sync" 
253
                                                  action:@selector(sync) 
254
                                           keyEquivalent:@""] autorelease]];
255
    }
256
}
257

  
258
#pragma mark -
259
#pragma mark Actions
207 260

  
208 261
- (IBAction)showPithosBrowser:(id)sender {
262
    if (!activated)
263
        return;
209 264
    [pithosBrowserController showWindow:sender];
210 265
    [[pithosBrowserController window] makeKeyAndOrderFront:sender];
211 266
    [NSApp activateIgnoringOtherApps:YES];
......
221 276
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[userDefaults stringForKey:@"aboutURL"]]];
222 277
}
223 278

  
224
- (IBAction)syncNow:(id)sender {
225
    [pithosSyncDaemon sync];
279
- (void)sync {
280
    if (!activated || ![pithosAccounts count])
281
        return;
282
    NSUInteger syncIndex;
283
    BOOL syncPithosAccountFound = [pithosAccounts containsObject:syncPithosAccount];
284
    if (syncPithosAccountFound)
285
         syncIndex = [pithosAccounts indexOfObject:syncPithosAccount];
286
    
287
    PithosAccount *singleSyncPithosAccount = nil;
288
    for (PithosAccount *pithosAccount in pithosAccounts) {
289
        if (!singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
290
            singleSyncPithosAccount = pithosAccount;
291
        } else if (singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
292
            singleSyncPithosAccount = nil;
293
            break;
294
        }
295
    }
296
    
297
    if (syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
298
        // An active syncDaemon was previously syncing
299
        if (singleSyncPithosAccount && [singleSyncPithosAccount isEqualTo:syncPithosAccount]) {
300
            // It's the only one, sync again
301
            [syncPithosAccount.syncDaemon startDaemon];
302
            [syncPithosAccount.syncDaemon sync];
303
            return;
304
        } else if ([syncPithosAccount.syncDaemon isSyncing]) {
305
            // It's still syncing, mark it as late and return
306
            [syncPithosAccount.syncDaemon syncLate];
307
            return;
308
        }
309
    }
310
    PithosAccount *newSyncPithosAccount = nil;
311
    if (syncPithosAccountFound) {
312
        for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(syncIndex + 1, [pithosAccounts count] - syncIndex - 1)]]) {
313
            if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
314
                newSyncPithosAccount = pithosAccount;
315
                break;
316
            }
317
        }
318
        if (!newSyncPithosAccount) {
319
            for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, syncIndex)]]) {
320
                if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
321
                    newSyncPithosAccount = pithosAccount;
322
                    break;
323
                }
324
            }
325
        }
326
    } else {
327
        for (PithosAccount *pithosAccount in pithosAccounts) {
328
            if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
329
                newSyncPithosAccount = pithosAccount;
330
                break;
331
            }
332
        }
333
    }        
334
    if (newSyncPithosAccount) {
335
        // A different syncDaemon is found, sync it
336
        self.syncPithosAccount = newSyncPithosAccount;
337
        [syncPithosAccount.syncDaemon startDaemon];
338
        [syncPithosAccount.syncDaemon sync];
339
    } else if (syncPithosAccountFound && syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
340
        [syncPithosAccount.syncDaemon startDaemon];
341
        [syncPithosAccount.syncDaemon sync];
342
    } else {
343
        self.syncPithosAccount = nil;
344
    }
226 345
}
227 346

  
228
#pragma mark -
229
#pragma Authentication
347
- (void)savePithosAccounts:(id)sender {
348
    [userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:pithosAccounts] forKey:@"pithosAccounts"];
349
    [userDefaults synchronize];
350
}
230 351

  
231
- (void)login {
232
    NSProcessInfo *processInfo = [NSProcessInfo processInfo];
233
    NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%@_%d", 
234
                          [userDefaults stringForKey:@"loginURLPrefix"], [processInfo processName], [processInfo processIdentifier]];
235
    NSLog(@"loginURL: %@", loginURL);
236
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
352
- (void)removedPithosAccount:(PithosAccount *)removedPithosAccount {
353
    if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
354
        for (PithosAccount *pithosAccount in pithosAccounts) {
355
            if (pithosAccount.active) {
356
                self.currentPithosAccount = pithosAccount;
357
                pithosBrowserController.pithos = currentPithosAccount.pithos;
358
                break;
359
            }
360
        }
361
        if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
362
            self.activated = NO;
363
            [pithosBrowserController.window close];
364
            [pithosBrowserController resetBrowser];
365
            self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
366
        }
367
    }
368
    if ([self.syncPithosAccount isEqualTo:removedPithosAccount])
369
        self.syncPithosAccount = nil;
237 370
}
238 371

  
239
- (void)authenticate {
240
    NSString *authUser = [userDefaults stringForKey:@"authUser"];
241
    NSString *authToken = [userDefaults stringForKey:@"authToken"];
242
    NSString *storageURLPrefix = [userDefaults stringForKey:@"storageURLPrefix"];
243
    NSString *publicURLPrefix = [userDefaults stringForKey:@"publicURLPrefix"];
244
    NSString *syncDirectoryPath = [userDefaults stringForKey:@"syncDirectoryPath"];
245
    NSString *syncContainerName = [userDefaults stringForKey:@"syncContainerName"];
246
    double syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"];
247
    NSLog(@"Authentication - storageURLPrefix:%@, authUser:%@, authToken:%@", storageURLPrefix, authUser, authToken);
248
    if (([authUser length] == 0) || ([authToken length] == 0)) {
249
        [self showPithosPreferences:self];
250
    } else if ([authUser length] && [authToken length] && 
251
               (![pithos.authUser isEqualToString:authUser] || ![pithos.authToken isEqualToString:authToken])) {
252
        [userDefaults setObject:authUser forKey:@"previousAuthUser"];
253
        [userDefaults setObject:authToken forKey:@"previousAuthToken"];
254
        
255
        [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
256
        [[ASIPithosRequest sharedQueue] cancelAllOperations];
257
        
258
        ASIPithos *newPithos = [ASIPithos pithos];
259
        newPithos.authURL = storageURLPrefix;
260
        newPithos.storageURLPrefix = storageURLPrefix;
261
        newPithos.authUser = authUser;
262
        newPithos.authToken = authToken;
263
        newPithos.publicURLPrefix = publicURLPrefix;
264
        self.pithos = newPithos;
265
        
266
        pithosBrowserController.pithos = pithos;
267
        self.pithosSyncDaemon = [[[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
268
                                                                          pithos:pithos
269
                                                                   containerName:syncContainerName 
270
                                                                    timeInterval:syncTimeInterval 
271
                                                                 resetLocalState:NO] autorelease];
272
    } else if (![pithosSyncDaemon.directoryPath isEqualToString:syncDirectoryPath]) {
273
        self.pithosSyncDaemon = [[[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
274
                                                                          pithos:pithos
275
                                                                   containerName:syncContainerName 
276
                                                                    timeInterval:syncTimeInterval 
277
                                                                 resetLocalState:YES] autorelease];
372
#pragma mark -
373
#pragma mark Menu Actions
374

  
375
- (void)menuChangePithosAccount:(NSMenuItem *)sender {
376
    PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
377
    if (!pithosAccount.active)
378
        return;
379
    if (![currentPithosAccount isEqualTo:pithosAccount] && [pithosAccounts containsObject:pithosAccount]) {
380
        if ([self.pithosBrowserController operationsPending]) {
381
            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
382
            [alert setMessageText:@"Pending Operations"];
383
            [alert setInformativeText:@"There are pending operations in the browser, do you want to change accounts and cancel them?"];
384
            [alert addButtonWithTitle:@"OK"];
385
            [alert addButtonWithTitle:@"Cancel"];
386
            NSInteger choice = [alert runModal];
387
            if (choice == NSAlertSecondButtonReturn) 
388
                return;
389
        }
390
        self.currentPithosAccount = pithosAccount;
391
        [self showPithosBrowser:self];
392
        pithosBrowserController.pithos = currentPithosAccount.pithos;
278 393
    }
279 394
}
280 395

  
396
- (void)menuChangeSyncActive:(NSMenuItem *)sender {
397
    PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
398
    if (!pithosAccount.active)
399
        return;
400
    pithosAccount.syncActive = !pithosAccount.syncActive;
401
    if (pithosPreferencesController && [pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount])
402
        pithosPreferencesController.syncActive = pithosAccount.syncActive;
403
    [self savePithosAccounts:self];
404
}
405

  
281 406
@end

Also available in: Unified diff