Do not use the shared cache in sharing account requests.
[pithos-macos] / pithos-macos / pithos_macosAppDelegate.m
1 //
2 //  pithos_macosAppDelegate.m
3 //  pithos-macos
4 //
5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
15 //   2. Redistributions in binary form must reproduce the above
16 //      copyright notice, this list of conditions and the following
17 //      disclaimer in the documentation and/or other materials
18 //      provided with the distribution.
19 // 
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 // 
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
37
38 #import "pithos_macosAppDelegate.h"
39 #import "PithosAccount.h"
40 #import "PithosBrowserController.h"
41 #import "PithosPreferencesController.h"
42 #import "PithosSyncDaemon.h"
43 #import "ASIPithosRequest.h"
44 #import "ASIPithos.h"
45 #import "LastCompletedSyncTransformer.h"
46
47 @implementation pithos_macosAppDelegate
48 @synthesize pithosBrowserController, pithosPreferencesController, alwaysNo, openAtLoginEnabled, openAtLogin, activated, 
49 currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount, activityFacilityTimeInterval;
50
51 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
52     [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
53                                                        andSelector:@selector(handleAppleEvent:withReplyEvent:) 
54                                                      forEventClass:kInternetEventClass 
55                                                         andEventID:kAEGetURL];
56     
57     // Based on: https://github.com/Mozketo/LaunchAtLoginController
58     // and: http://cocoatutorial.grapewave.com/2010/02/creating-andor-removing-a-login-item/
59     loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
60     if (loginItems) {
61         LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, self);
62         LSSharedFileListChanged(loginItems, self);
63         self.openAtLoginEnabled = YES;
64     }
65     
66     userDefaults = [[NSUserDefaults standardUserDefaults] retain];
67     
68     syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"];
69     if (syncTimeInterval <= 0.0) {
70         syncTimeInterval = 180.0;
71         [userDefaults setDouble:syncTimeInterval forKey:@"syncTimeInterval"];
72         [userDefaults synchronize];
73     }
74
75     activityFacilityTimeInterval = [userDefaults doubleForKey:@"activityFacilityTimeInterval"];
76     if (activityFacilityTimeInterval <= 0.0) {
77         activityFacilityTimeInterval = 0.05;
78         [userDefaults setDouble:activityFacilityTimeInterval forKey:@"activityFacilityTimeInterval"];
79         [userDefaults synchronize];
80     }
81
82     NSData *tmpData = [userDefaults objectForKey:@"pithosAccounts"];
83     NSArray *tmpArray;
84     if (tmpData && (tmpArray = [NSKeyedUnarchiver unarchiveObjectWithData:tmpData]))
85         self.pithosAccounts = [NSMutableArray arrayWithArray:tmpArray];
86     else
87         self.pithosAccounts = [NSMutableArray array];
88     
89     if (![pithosAccounts count]) {
90         [pithosAccounts addObject:[PithosAccount pithosAccount]];
91         self.pithosAccounts = self.pithosAccounts;
92     } else {
93         self.activated = YES;
94     }
95     
96     pithosAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[pithosAccounts count]];
97     for (PithosAccount *pithosAccount in pithosAccounts) {
98         [pithosAccountsDictionary setObject:pithosAccount forKey:pithosAccount.name];
99         if (!currentPithosAccount && pithosAccount.active)
100             currentPithosAccount = [pithosAccount retain];
101     }
102     if (!currentPithosAccount)
103         self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
104     
105     if (currentPithosAccount.active) {
106         [self savePithosAccounts:self];
107         [self showPithosBrowser:self];
108         self.pithosBrowserController.pithos = currentPithosAccount.pithos;
109     } else {
110         // XXX maybe call specifically to go to new account tab
111         [self showPithosPreferences:self];
112     }
113
114     syncTimer = [[NSTimer scheduledTimerWithTimeInterval:syncTimeInterval 
115                                                   target:self 
116                                                 selector:@selector(sync) 
117                                                 userInfo:nil 
118                                                  repeats:YES] retain];
119     [syncTimer fire];
120 }
121
122 // Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/
123 // and: http://www.cocoadev.com/index.pl?ThumbnailImages
124 - (void)awakeFromNib {
125     NSImage *sourceImage = [NSImage imageNamed:@"pithos-large.png"];
126     
127     NSImage *smallImage = [[[NSImage alloc] initWithSize:NSMakeSize(18, 18)] autorelease];
128     [smallImage lockFocus];
129     [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
130     [sourceImage setSize:NSMakeSize(18, 18)];
131     [sourceImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
132     [smallImage unlockFocus];
133     
134     statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
135     [statusItem setMenu:statusMenu];
136     [statusItem setImage:sourceImage];
137     [statusItem setHighlightMode:YES];
138     
139     self.alwaysNo = NO;
140 }
141
142 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent {
143     NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
144     NSString *host = [url host];
145         NSString *query = [url query];
146     PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[url lastPathComponent]];
147     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
148     if ([host isEqualToString:[NSString stringWithFormat:@"%d", [processInfo processIdentifier]]] && pithosAccount && query) {
149         // user=
150         NSString *authUser;
151         NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch];
152         if (userRange.length == 0)
153             // XXX maybe show an error message?
154             return;
155         NSUInteger authUserStartLocation = userRange.location + userRange.length;
156         NSRange userEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch 
157                                               range:NSMakeRange(authUserStartLocation, [query length] - authUserStartLocation)];
158         if (userEndRange.length) {
159             authUser = [[query substringWithRange:NSMakeRange(authUserStartLocation, userEndRange.location - authUserStartLocation)]
160                         stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
161         } else {
162             authUser = [[query substringFromIndex:authUserStartLocation]
163                         stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
164         }
165         // token=
166         NSString *authToken;
167         NSRange tokenRange = [query rangeOfString:@"token=" options:NSCaseInsensitiveSearch];
168         if (tokenRange.length == 0)
169             // XXX maybe show an error message?
170             return;
171         NSUInteger authTokenStartLocation = tokenRange.location + tokenRange.length;
172         NSRange tokenEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch 
173                                               range:NSMakeRange(authTokenStartLocation, [query length] - authTokenStartLocation)];
174         if (tokenEndRange.length) {
175             authToken = [[query substringWithRange:NSMakeRange(authTokenStartLocation, tokenEndRange.location - authTokenStartLocation)]
176                          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
177         } else {
178             authToken = [[query substringFromIndex:authTokenStartLocation]
179                          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
180         }
181         
182         NSLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken);
183         if ([authUser length] && [authToken length]) {
184             [pithosAccount authenticateWithServerURL:nil authUser:authUser authToken:authToken];
185             [self savePithosAccounts:self];
186             if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) {
187                 self.pithosPreferencesController.authUser = pithosAccount.authUser;
188                 self.pithosPreferencesController.authToken = pithosAccount.authToken;
189             }
190             self.activated = YES;
191             if ([pithosAccount isEqualTo:currentPithosAccount]) {
192                 [self showPithosBrowser:self];
193                 self.pithosBrowserController.pithos = pithosAccount.pithos;
194             }
195         }
196         // XXX else maybe show an error message?
197     }
198     // XXX else maybe show an error message?
199 }
200
201 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
202     [self savePithosAccounts:self];
203     if ([self.pithosBrowserController operationsPending]) {
204         NSAlert *alert = [[[NSAlert alloc] init] autorelease];
205         [alert setMessageText:@"Pending Operations"];
206         [alert setInformativeText:@"There are pending operations in the browser, do you want to quit and cancel them?"];
207         [alert addButtonWithTitle:@"OK"];
208         [alert addButtonWithTitle:@"Cancel"];
209         NSInteger choice = [alert runModal];
210         if (choice == NSAlertSecondButtonReturn) 
211             return NSTerminateCancel;
212     }
213     if (loginItems) {
214         LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, self);
215         CFRelease(loginItems);
216     }
217     return NSTerminateNow;
218 }
219
220 #pragma mark -
221 #pragma mark Callbacks
222
223 - (void)loginItemsChanged {
224     NSURL *appURL = [[NSBundle mainBundle] bundleURL];
225     LSSharedFileListItemRef appItem = NULL;
226     NSArray *snapshot = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, NULL)) autorelease];
227     for (id itemObject in snapshot) {
228         LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
229         UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
230         CFURLRef currentItemURL = NULL;
231         LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
232         if (currentItemURL && CFEqual(currentItemURL, appURL)) {
233             CFRelease(currentItemURL);
234             appItem = item;
235             break;
236         }
237         if (currentItemURL)
238             CFRelease(currentItemURL);
239     }
240     
241     if (appItem && (!openAtLogin || !openAtLoginEnabled))
242         self.openAtLogin = YES;
243     else if (!appItem && (openAtLogin || !openAtLoginEnabled))
244         self.openAtLogin = NO;
245 }
246
247 void LSSharedFileListChanged(LSSharedFileListRef inList, void *context) {
248     pithos_macosAppDelegate *self = (id)context;
249     [self loginItemsChanged];
250 }
251
252 #pragma mark -
253 #pragma mark Properties
254
255 - (PithosBrowserController *)pithosBrowserController {
256     if (!pithosBrowserController) {
257         pithosBrowserController = [[PithosBrowserController alloc] init];
258     }
259     return pithosBrowserController;
260 }
261
262 - (PithosPreferencesController *)pithosPreferencesController {
263     if (!pithosPreferencesController) {
264         pithosPreferencesController = [[PithosPreferencesController alloc] init];
265     }
266     return pithosPreferencesController;
267 }
268
269 - (void)setOpenAtLogin:(BOOL)anOpenAtLogin {
270     if (!openAtLoginEnabled) {
271         openAtLogin = anOpenAtLogin;
272     } else if (anOpenAtLogin != openAtLogin) {
273         NSURL *appURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
274         LSSharedFileListItemRef appItem = NULL;
275         NSArray *snapshot = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, NULL)) autorelease];
276         for (id itemObject in snapshot) {
277             LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
278             UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
279             CFURLRef currentItemURL = NULL;
280             LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
281             if (currentItemURL && CFEqual(currentItemURL, appURL)) {
282                 CFRelease(currentItemURL);
283                 appItem = item;
284                 break;
285             }
286             if (currentItemURL)
287                 CFRelease(currentItemURL);
288         }
289         
290         if (anOpenAtLogin) {
291             if (!appItem)
292                 LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, (CFURLRef)appURL, NULL, NULL);
293             openAtLogin = YES;
294         } else {
295             if (appItem)
296                 LSSharedFileListItemRemove(loginItems, appItem);
297             openAtLogin = NO;
298         }
299     }
300 }
301
302 #pragma mark -
303 #pragma mark NSMenuDelegate
304
305 - (void)menuNeedsUpdate:(NSMenu *)menu {
306     NSMenuItem *menuItem;
307     [menu removeAllItems];
308     if ([menu isEqualTo:accountsMenu]) {
309         [menu setAutoenablesItems:NO];
310         for (PithosAccount *pithosAccount in pithosAccounts) {
311             menuItem = [[[NSMenuItem alloc] initWithTitle:pithosAccount.name 
312                                                    action:@selector(menuChangePithosAccount:) 
313                                             keyEquivalent:@""] autorelease];
314             [menuItem setRepresentedObject:pithosAccount];
315             [menuItem setEnabled:pithosAccount.active];
316             [menuItem setState:((pithosAccount.active && [currentPithosAccount isEqualTo:pithosAccount]) ? NSOnState : NSOffState)];
317             [menu addItem:menuItem];
318         }
319     } else if ([menu isEqualTo:lastSyncMenu]) {
320         NSString *menuItemTitle;
321         [menu setAutoenablesItems:NO];
322         for (PithosAccount *pithosAccount in pithosAccounts) {
323             menuItemTitle = [NSString stringWithFormat:@"%@: %@", 
324                              pithosAccount.name, 
325                              [[[[LastCompletedSyncTransformer alloc] init] autorelease] transformedValue:pithosAccount.syncLastCompleted]];
326             if ([pithosAccount isEqualTo:syncPithosAccount] && [pithosAccount.syncDaemon isSyncing])
327                 menuItemTitle = [menuItemTitle stringByAppendingString:@" (syncing)"];
328 //            menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle 
329 //                                                   action:@selector(menuChangeSyncActive:) 
330 //                                            keyEquivalent:@""] autorelease];
331 //            [menuItem setRepresentedObject:pithosAccount];
332 //            [menuItem setEnabled:pithosAccount.active];
333 //            [menuItem setState:((pithosAccount.active && pithosAccount.syncActive) ? NSOnState : NSOffState)];
334             menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle action:nil keyEquivalent:@""] autorelease];
335             [menuItem setEnabled:NO];
336             [menuItem setState:NO];
337             [menu addItem:menuItem];
338         }
339         [menu addItem:[NSMenuItem separatorItem]];
340         [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"Next Sync" 
341                                                   action:@selector(sync) 
342                                            keyEquivalent:@""] autorelease]];
343     }
344 }
345
346 #pragma mark -
347 #pragma mark Actions
348
349 - (IBAction)showPithosBrowser:(id)sender {
350     if (!activated)
351         return;
352     [self.pithosBrowserController showWindow:sender];
353     [[self.pithosBrowserController window] makeKeyAndOrderFront:sender];
354     [NSApp activateIgnoringOtherApps:YES];
355 }
356
357 - (IBAction)showPithosPreferences:(id)sender {
358     [self.pithosPreferencesController showWindow:sender];
359     [[self.pithosPreferencesController window] makeKeyAndOrderFront:sender];
360     [NSApp activateIgnoringOtherApps:YES];
361 }
362
363 - (IBAction)showPithosAbout:(id)sender {
364     [NSApp orderFrontStandardAboutPanel:sender];
365     [NSApp activateIgnoringOtherApps:YES];
366 }
367
368 - (void)sync {
369     if (!activated || ![pithosAccounts count])
370         return;
371     NSUInteger syncIndex;
372     BOOL syncPithosAccountFound = [pithosAccounts containsObject:syncPithosAccount];
373     if (syncPithosAccountFound)
374          syncIndex = [pithosAccounts indexOfObject:syncPithosAccount];
375     
376     PithosAccount *singleSyncPithosAccount = nil;
377     for (PithosAccount *pithosAccount in pithosAccounts) {
378         if (!singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
379             singleSyncPithosAccount = pithosAccount;
380         } else if (singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
381             singleSyncPithosAccount = nil;
382             break;
383         }
384     }
385     
386     if (syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
387         // An active syncDaemon was previously syncing
388         if (singleSyncPithosAccount && [singleSyncPithosAccount isEqualTo:syncPithosAccount]) {
389             // It's the only one, sync again
390             [syncPithosAccount.syncDaemon startDaemon];
391             [syncPithosAccount.syncDaemon sync];
392             return;
393         } else if ([syncPithosAccount.syncDaemon isSyncing]) {
394             // It's still syncing, mark it as late and return
395             [syncPithosAccount.syncDaemon syncLate];
396             return;
397         }
398     }
399     PithosAccount *newSyncPithosAccount = nil;
400     if (syncPithosAccountFound) {
401         for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(syncIndex + 1, [pithosAccounts count] - syncIndex - 1)]]) {
402             if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
403                 newSyncPithosAccount = pithosAccount;
404                 break;
405             }
406         }
407         if (!newSyncPithosAccount) {
408             for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, syncIndex)]]) {
409                 if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
410                     newSyncPithosAccount = pithosAccount;
411                     break;
412                 }
413             }
414         }
415     } else {
416         for (PithosAccount *pithosAccount in pithosAccounts) {
417             if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
418                 newSyncPithosAccount = pithosAccount;
419                 break;
420             }
421         }
422     }        
423     if (newSyncPithosAccount) {
424         // A different syncDaemon is found, sync it
425         self.syncPithosAccount = newSyncPithosAccount;
426         [syncPithosAccount.syncDaemon startDaemon];
427         [syncPithosAccount.syncDaemon sync];
428     } else if (syncPithosAccountFound && syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
429         [syncPithosAccount.syncDaemon startDaemon];
430         [syncPithosAccount.syncDaemon sync];
431     } else {
432         self.syncPithosAccount = nil;
433     }
434 }
435
436 - (void)savePithosAccounts:(id)sender {
437     [userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:pithosAccounts] forKey:@"pithosAccounts"];
438     [userDefaults synchronize];
439 }
440
441 - (void)removedPithosAccount:(PithosAccount *)removedPithosAccount {
442     if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
443         for (PithosAccount *pithosAccount in pithosAccounts) {
444             if (pithosAccount.active) {
445                 self.currentPithosAccount = pithosAccount;
446                 self.pithosBrowserController.pithos = currentPithosAccount.pithos;
447                 break;
448             }
449         }
450         if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
451             self.activated = NO;
452             [self.pithosBrowserController.window close];
453             [self.pithosBrowserController resetBrowser];
454             self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
455         }
456     }
457     if ([self.syncPithosAccount isEqualTo:removedPithosAccount])
458         self.syncPithosAccount = nil;
459 }
460
461 #pragma mark -
462 #pragma mark Menu Actions
463
464 - (void)menuChangePithosAccount:(NSMenuItem *)sender {
465     PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
466     if (!pithosAccount.active)
467         return;
468     if (![currentPithosAccount isEqualTo:pithosAccount] && [pithosAccounts containsObject:pithosAccount]) {
469         if ([self.pithosBrowserController operationsPending]) {
470             NSAlert *alert = [[[NSAlert alloc] init] autorelease];
471             [alert setMessageText:@"Pending Operations"];
472             [alert setInformativeText:@"There are pending operations in the browser, do you want to change accounts and cancel them?"];
473             [alert addButtonWithTitle:@"OK"];
474             [alert addButtonWithTitle:@"Cancel"];
475             NSInteger choice = [alert runModal];
476             if (choice == NSAlertSecondButtonReturn) 
477                 return;
478         }
479         self.currentPithosAccount = pithosAccount;
480         [self showPithosBrowser:self];
481         self.pithosBrowserController.pithos = currentPithosAccount.pithos;
482     }
483 }
484
485 //- (void)menuChangeSyncActive:(NSMenuItem *)sender {
486 //    PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
487 //    if (!pithosAccount.active)
488 //        return;
489 //    pithosAccount.syncActive = !pithosAccount.syncActive;
490 //    if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount])
491 //        self.pithosPreferencesController.syncActive = pithosAccount.syncActive;
492 //    [self savePithosAccounts:self];
493 //}
494
495 @end