Using pithos icon for the status bar.
[pithos-macos] / pithos-macos / pithos_macosAppDelegate.m
1 //
2 //  pithos_macosAppDelegate.m
3 //  pithos-macos
4 //
5 // Copyright 2011 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 "PithosBrowserController.h"
40 #import "PithosPreferencesController.h"
41 #import "PithosSyncDaemon.h"
42 #import "ASIPithosRequest.h"
43 #import "ASIDownloadCache.h"
44
45 @implementation pithos_macosAppDelegate
46 @synthesize storageURLPrefix, publicURLPrefix, loginURLPrefix, aboutURL;
47 @synthesize syncContainerName, syncTimeInterval, pithosBrowserController, pithosSyncDaemon, alwaysNo;
48
49 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
50     NSURL *testURL;
51     storageURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosStorageURLPrefix"];
52     if (!storageURLPrefix) {
53         storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
54     } else {
55         testURL = [NSURL URLWithString:storageURLPrefix];
56         if (!testURL || !testURL.scheme || !testURL.host)
57             storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
58     }
59     [storageURLPrefix retain];
60     
61     publicURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosPublicURLPrefix"];
62     if (!publicURLPrefix) {
63         publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
64     } else {
65         testURL = [NSURL URLWithString:publicURLPrefix];
66         if (!testURL || !testURL.scheme || !testURL.host)
67             publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
68     }
69     [publicURLPrefix retain];
70     
71     loginURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosLoginURLPrefix"];
72     if (!loginURLPrefix) {
73         loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
74     } else {
75         testURL = [NSURL URLWithString:loginURLPrefix];
76         if (!testURL || !testURL.scheme || !testURL.host)
77             loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
78     }
79     [loginURLPrefix retain];
80
81     aboutURL = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosAboutURL"];
82     if (!aboutURL) {
83         aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
84     } else {
85         testURL = [NSURL URLWithString:aboutURL];
86         if (!testURL || !testURL.scheme || !testURL.host)
87             aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
88     }
89     [aboutURL retain];
90     
91     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
92     NSString *syncDirectoryPath = [userDefaults stringForKey:@"pithosSyncDirectoryPath"];
93     if (!syncDirectoryPath || ![syncDirectoryPath length]) {
94         syncDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"Pithos"];
95     } else {
96         NSFileManager *fileManager = [NSFileManager defaultManager];
97         BOOL isDirectory;
98         BOOL fileExists = [fileManager fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory];
99         NSError *error = nil;
100         if ((fileExists && !isDirectory) || 
101             (!fileExists && (![fileManager createDirectoryAtPath:syncDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error] || error))) {
102             syncDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"Pithos"];
103         }   
104     }
105     [userDefaults setObject:syncDirectoryPath forKey:@"pithosSyncDirectoryPath"];
106     [userDefaults synchronize];
107     
108     syncContainerName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosSyncContainerName"];
109     if (!syncContainerName || ![syncContainerName length] || [syncContainerName isEqualToString:@"trash"]) {
110         syncContainerName = [NSString stringWithString:@"pithos"];
111     }
112     [syncContainerName retain];
113
114     syncTimeInterval = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosSyncTimeInterval"] doubleValue];
115     if (syncTimeInterval <= 0) {
116         syncTimeInterval = 180.0;
117     }
118     
119     [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
120                                                        andSelector:@selector(handleAppleEvent:withReplyEvent:) 
121                                                      forEventClass:kInternetEventClass 
122                                                         andEventID:kAEGetURL];
123     [self showPithosBrowser:self];
124     
125     [self authenticate];
126 }
127
128 // Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/
129 // and: http://www.cocoadev.com/index.pl?ThumbnailImages
130 - (void)awakeFromNib {
131     NSImage *sourceImage = [NSImage imageNamed:@"pithos-large.png"];
132     
133     NSImage *smallImage = [[[NSImage alloc] initWithSize:NSMakeSize(18, 18)] autorelease];
134     [smallImage lockFocus];
135     [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
136     [sourceImage setSize:NSMakeSize(18, 18)];
137     [sourceImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
138     [smallImage unlockFocus];
139     
140     statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
141     [statusItem setMenu:statusMenu];
142     [statusItem setImage:sourceImage];
143     [statusItem setHighlightMode:YES];
144     
145     self.alwaysNo = NO;
146 }
147
148 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent {
149     NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
150     NSString *host = [url host];
151         NSString *query = [url query];
152     NSLog(@"host : '%@', query: '%@'", host, query);
153     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
154     if ([host isEqualToString:[NSString stringWithFormat:@"%@_%d", [processInfo processName], [processInfo processIdentifier]]] && query) {
155         // user=
156         NSString *authUser;
157         NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch];
158         if (userRange.length == 0)
159             // XXX maybe show an error message?
160             return;
161         NSUInteger authUserStartLocation = userRange.location + userRange.length;
162         NSRange userEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch 
163                                               range:NSMakeRange(authUserStartLocation, [query length] - authUserStartLocation)];
164         if (userEndRange.length) {
165             authUser = [[query substringWithRange:NSMakeRange(authUserStartLocation, userEndRange.location - authUserStartLocation)]
166                         stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
167         } else {
168             authUser = [[query substringFromIndex:authUserStartLocation]
169                         stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
170         }
171         // token=
172         NSString *authToken;
173         NSRange tokenRange = [query rangeOfString:@"token=" options:NSCaseInsensitiveSearch];
174         if (tokenRange.length == 0)
175             // XXX maybe show an error message?
176             return;
177         NSUInteger authTokenStartLocation = tokenRange.location + tokenRange.length;
178         NSRange tokenEndRange = [query rangeOfString:@"&" options:NSCaseInsensitiveSearch 
179                                               range:NSMakeRange(authTokenStartLocation, [query length] - authTokenStartLocation)];
180         if (tokenEndRange.length) {
181             authToken = [[query substringWithRange:NSMakeRange(authTokenStartLocation, tokenEndRange.location - authTokenStartLocation)]
182                          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
183         } else {
184             authToken = [[query substringFromIndex:authTokenStartLocation]
185                          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
186         }
187         
188         NSLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken);
189         if ([authUser length] && [authToken length]) {
190             NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
191             [userDefaults setObject:authUser forKey:@"authUser"];
192             [userDefaults setObject:authToken forKey:@"authToken"];
193             [userDefaults synchronize];
194
195             [self authenticate];
196         }
197         // XXX else maybe show an error message?
198     }
199     // XXX else maybe show an error message?
200 }
201
202 #pragma mark -
203 #pragma Actions
204
205 - (IBAction)showPithosBrowser:(id)sender {
206     [pithosBrowserController showWindow:sender];
207     [[pithosBrowserController window] makeKeyAndOrderFront:sender];
208     [NSApp activateIgnoringOtherApps:YES];
209 }
210
211 - (IBAction)showPithosPreferences:(id)sender {
212     [pithosPreferencesController showWindow:sender];
213     [[pithosPreferencesController window] makeKeyAndOrderFront:sender];
214     [NSApp activateIgnoringOtherApps:YES];
215 }
216
217 - (IBAction)aboutPithos:(id)sender {
218     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:self.aboutURL]];
219 }
220
221 - (IBAction)syncNow:(id)sender {
222     [pithosSyncDaemon sync];
223 }
224
225 #pragma mark -
226 #pragma Authentication
227
228 - (void)authenticate {
229     NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
230     NSString *authUser = [userDefaults stringForKey:@"authUser"];
231     NSString *authToken = [userDefaults stringForKey:@"authToken"];
232     NSString *syncDirectoryPath = [userDefaults stringForKey:@"pithosSyncDirectoryPath"];
233     NSLog(@"Authentication - storageURLPrefix:%@, authUser:%@, authToken:%@", storageURLPrefix, authUser, authToken);
234     if (([authUser length] == 0) || ([authToken length] == 0)) {
235         [self showPithosPreferences:self];
236     } else if ([authUser length] && [authToken length] && 
237                (![[ASIPithosRequest authUser] isEqualToString:authUser] || ![[ASIPithosRequest authToken] isEqualToString:authToken])) {
238         [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
239         [[ASIPithosRequest sharedQueue] cancelAllOperations];
240         
241         [ASIPithosRequest setAuthURL:storageURLPrefix];
242         [ASIPithosRequest setStorageURLPrefix:storageURLPrefix];
243         [ASIPithosRequest setAuthUser:authUser];
244         [ASIPithosRequest setAuthToken:authToken];
245         [ASIPithosRequest setPublicURLPrefix:publicURLPrefix];
246         
247         self.pithosSyncDaemon = [[[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
248                                                                    containerName:syncContainerName 
249                                                                     timeInterval:syncTimeInterval 
250                                                                  resetLocalState:NO] autorelease];
251         
252         [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAuthenticationCredentialsUpdated" object:self];
253     } else if (![pithosSyncDaemon.directoryPath isEqualToString:syncDirectoryPath]) {
254         self.pithosSyncDaemon = [[[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
255                                                                    containerName:syncContainerName 
256                                                                     timeInterval:syncTimeInterval 
257                                                                  resetLocalState:YES] autorelease];
258     }
259 }
260
261 @end