// // NSHTTPCookieStorage.m // mySTEP // // Created by Dr. H. Nikolaus Schaller on Wed Jan 04 2006. // Copyright (c) 2006 DSITRI. All rights reserved. // #include #import #import #import #import #import #import #import #import #import #import #import #import #define COOKIE_DATABASE @"~/Library/Cookies/Cookies.plist" // Array of Dictionaries #define COOKIE_DATABASE_LOCK COOKIE_DATABASE@".lock" @implementation NSHTTPCookieStorage NSString *NSHTTPCookieStorageCookiesChangedNotification=@"NSHTTPCookieStorageCookiesChangedNotification"; NSString *NSHTTPCookieStorageAcceptPolicyChangedNotification=@"NSHTTPCookieStorageAcceptPolicyChangedNotification"; + (NSHTTPCookieStorage *) sharedHTTPCookieStorage; { static NSHTTPCookieStorage *_shared; if(!_shared) _shared=[[self alloc] init]; return _shared; } - (void) _sync; { BOOL changed=NO; while(![_lock tryLock]) sleep(1); // we could also skip this sync attempt and modify the timer by some random value if(!_cookies || [[[[NSFileManager defaultManager] fileAttributesAtPath:COOKIE_DATABASE traverseLink:YES] fileModificationDate] compare:_lastModification] == NSOrderedDescending) { // update local cookies (if added or changed externally) NSArray *a=[NSArray arrayWithContentsOfFile:COOKIE_DATABASE]; // read all properties NSEnumerator *e=[a objectEnumerator]; NSDictionary *props; if(!_cookies) _cookies=[[NSMutableDictionary alloc] initWithCapacity:100]; while((props=[e nextObject])) { // merge changes NSString *key=[NSString stringWithFormat:@"%@/%@/%@", [props objectForKey:NSHTTPCookieDomain], [props objectForKey:NSHTTPCookiePath], [props objectForKey:NSHTTPCookieName]]; NSHTTPCookie *cookie=[_cookies objectForKey:key]; if(cookie) { // if we already know this cookie and have changed it locally, keep local copy // otherwise apply external change (and set changed) or add // so that the latest version survives - set _touched flag if we have to write any changes } cookie=[NSHTTPCookie cookieWithProperties:props]; // create a new one [_cookies setObject:cookie forKey:key]; // add to database } } if(_touched || !_lastModification) { NSMutableArray *a=[NSMutableArray arrayWithCapacity:[_cookies count]]; NSEnumerator *e=[_cookies objectEnumerator]; NSHTTPCookie *cookie; while((cookie=[e nextObject])) [a addObject:[cookie properties]]; // just save the properties [a writeToFile:COOKIE_DATABASE atomically:YES]; // should we write a binary property list??? _lastModification=[[[[NSFileManager defaultManager] fileAttributesAtPath:COOKIE_DATABASE traverseLink:YES] fileModificationDate] retain]; // modified } [_lock unlock]; if(changed) [[NSNotificationCenter defaultCenter] postNotificationName:NSHTTPCookieStorageCookiesChangedNotification object:self]; } - (NSArray *) cookies; { if(!_cookies) [self _sync]; return [_cookies allValues]; } - (NSArray *) cookiesForURL:(NSURL *) url; { NSMutableArray *r=[NSMutableArray arrayWithCapacity:[[self cookies] count]]; NSEnumerator *c=[_cookies objectEnumerator]; NSHTTPCookie *cookie; while((cookie=[c nextObject])) { NSString *domain=[cookie domain]; NSString *path; NSArray *portList; if([domain hasPrefix:@"."]) { if(![[url host] hasSuffix:domain]) continue; // does not match suffix } else if(![[url host] isEqualToString:domain]) continue; // does not match exact domain path=[cookie path]; if(![path isEqualToString:@"/"] && ![[url path] isEqualToString:path]) continue; // neither "all paths" nor specific path if([cookie isSecure] && ![[url scheme] hasSuffix:@"s"]) continue; // not requested by secure protocol if((portList=[cookie portList]) && ![portList containsObject:[url port]]) continue; // no match with port list [r addObject:cookie]; // exact match } return r; } - (void) deleteCookie:(NSHTTPCookie *) cookie; { // compare cookie and delete if found [_cookies removeObjectForKey:[NSString stringWithFormat:@"%@/%@/%@", [cookie domain], [cookie path], [cookie name]]]; // Hm. what if the cookie was added/deleted locally and added externally before we did sync? _touched=YES; } - (void) setCookie:(NSHTTPCookie *) cookie; { NSString *key; if(_cookieAcceptPolicy == NSHTTPCookieAcceptPolicyNever) return; key=[NSString stringWithFormat:@"%@/%@/%@", [cookie domain], [cookie path], [cookie name]]; if(!_cookies) [self _sync]; [_cookies setObject:cookie forKey:key]; _touched=YES; } - (void) setCookies:(NSArray *) cookies forURL:(NSURL *) url mainDocumentURL:(NSURL *) mainURL; { NSEnumerator *c; NSHTTPCookie *cookie; if(_cookieAcceptPolicy == NSHTTPCookieAcceptPolicyNever) return; c=[_cookies objectEnumerator]; while((cookie=[c nextObject])) { // [cookie setMainDocumentURL:mainURL] [self setCookie:cookie]; } } - (NSHTTPCookieAcceptPolicy) cookieAcceptPolicy; { return _cookieAcceptPolicy; } - (void) setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy) policy; { if(_cookieAcceptPolicy != policy) { _cookieAcceptPolicy=policy; _touched=YES; } } - (id) init; { if((self=[super init])) { _lock=[[NSDistributedLock alloc] initWithPath:COOKIE_DATABASE_LOCK]; _timer=[[NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(_sync) userInfo:nil repeats:YES] retain]; } return self; } - (void) dealloc; { [_timer invalidate]; [_timer release]; if(_touched) [self _sync]; // final sync [_lock release]; [_cookies release]; [_lastModification release]; [super dealloc]; } @end