2 // pithos_macosAppDelegate.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
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.
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.
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.
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"
45 @implementation pithos_macosAppDelegate
46 @synthesize storageURLPrefix, publicURLPrefix, loginURLPrefix, aboutURL;
47 @synthesize syncContainerName, syncTimeInterval, pithosBrowserController, pithosSyncDaemon, alwaysNo;
49 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
51 storageURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosStorageURLPrefix"];
52 if (!storageURLPrefix) {
53 storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
55 testURL = [NSURL URLWithString:storageURLPrefix];
56 if (!testURL || !testURL.scheme || !testURL.host)
57 storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
59 [storageURLPrefix retain];
61 publicURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosPublicURLPrefix"];
62 if (!publicURLPrefix) {
63 publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
65 testURL = [NSURL URLWithString:publicURLPrefix];
66 if (!testURL || !testURL.scheme || !testURL.host)
67 publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
69 [publicURLPrefix retain];
71 loginURLPrefix = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosLoginURLPrefix"];
72 if (!loginURLPrefix) {
73 loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
75 testURL = [NSURL URLWithString:loginURLPrefix];
76 if (!testURL || !testURL.scheme || !testURL.host)
77 loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
79 [loginURLPrefix retain];
81 aboutURL = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosAboutURL"];
83 aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
85 testURL = [NSURL URLWithString:aboutURL];
86 if (!testURL || !testURL.scheme || !testURL.host)
87 aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
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"];
96 NSFileManager *fileManager = [NSFileManager defaultManager];
98 BOOL fileExists = [fileManager fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory];
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"];
105 [userDefaults setObject:syncDirectoryPath forKey:@"pithosSyncDirectoryPath"];
106 [userDefaults synchronize];
108 syncContainerName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosSyncContainerName"];
109 if (!syncContainerName || ![syncContainerName length] || [syncContainerName isEqualToString:@"trash"]) {
110 syncContainerName = [NSString stringWithString:@"pithos"];
112 [syncContainerName retain];
114 syncTimeInterval = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosSyncTimeInterval"] doubleValue];
115 if (syncTimeInterval <= 0) {
116 syncTimeInterval = 180.0;
119 [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
120 andSelector:@selector(handleAppleEvent:withReplyEvent:)
121 forEventClass:kInternetEventClass
122 andEventID:kAEGetURL];
123 [self showPithosBrowser:self];
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"];
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];
140 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
141 [statusItem setMenu:statusMenu];
142 [statusItem setImage:sourceImage];
143 [statusItem setHighlightMode:YES];
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) {
157 NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch];
158 if (userRange.length == 0)
159 // XXX maybe show an error message?
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];
168 authUser = [[query substringFromIndex:authUserStartLocation]
169 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
173 NSRange tokenRange = [query rangeOfString:@"token=" options:NSCaseInsensitiveSearch];
174 if (tokenRange.length == 0)
175 // XXX maybe show an error message?
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];
184 authToken = [[query substringFromIndex:authTokenStartLocation]
185 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
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];
197 // XXX else maybe show an error message?
199 // XXX else maybe show an error message?
205 - (IBAction)showPithosBrowser:(id)sender {
206 [pithosBrowserController showWindow:sender];
207 [[pithosBrowserController window] makeKeyAndOrderFront:sender];
208 [NSApp activateIgnoringOtherApps:YES];
211 - (IBAction)showPithosPreferences:(id)sender {
212 [pithosPreferencesController showWindow:sender];
213 [[pithosPreferencesController window] makeKeyAndOrderFront:sender];
214 [NSApp activateIgnoringOtherApps:YES];
217 - (IBAction)aboutPithos:(id)sender {
218 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:self.aboutURL]];
221 - (IBAction)syncNow:(id)sender {
222 [pithosSyncDaemon sync];
226 #pragma Authentication
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];
241 [ASIPithosRequest setAuthURL:storageURLPrefix];
242 [ASIPithosRequest setStorageURLPrefix:storageURLPrefix];
243 [ASIPithosRequest setAuthUser:authUser];
244 [ASIPithosRequest setAuthToken:authToken];
245 [ASIPithosRequest setPublicURLPrefix:publicURLPrefix];
247 self.pithosSyncDaemon = [[[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath
248 containerName:syncContainerName
249 timeInterval:syncTimeInterval
250 resetLocalState:NO] autorelease];
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];