summaryrefslogtreecommitdiff
path: root/QSDeliciousPlugIn_Source.m
diff options
context:
space:
mode:
Diffstat (limited to 'QSDeliciousPlugIn_Source.m')
-rw-r--r--QSDeliciousPlugIn_Source.m485
1 files changed, 207 insertions, 278 deletions
diff --git a/QSDeliciousPlugIn_Source.m b/QSDeliciousPlugIn_Source.m
index 8dcc9b4..43a32cb 100644
--- a/QSDeliciousPlugIn_Source.m
+++ b/QSDeliciousPlugIn_Source.m
@@ -8,126 +8,143 @@
#import "QSDeliciousPlugIn_Source.h"
#import <QSCore/QSCore.h>
-
#import <Security/Security.h>
@implementation QSDeliciousPlugIn_Source
+ (void)initialize {
- [self setKeys:[NSArray arrayWithObject:@"selection"] triggerChangeNotificationsForDependentKey:@"currentPassword"];
+ [self setKeys:[NSArray arrayWithObject:@"selection"] triggerChangeNotificationsForDependentKey:@"currentPassword"];
}
- (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry {
- return -[indexDate timeIntervalSinceNow] < 24 * 60 * 60;
+ return -[indexDate timeIntervalSinceNow] < 24 * 60 * 60;
}
-- (BOOL)isVisibleSource{ return YES; }
+- (BOOL)isVisibleSource{
+ return YES;
+}
- (NSImage *) iconForEntry:(NSDictionary *)dict {
return [[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"];
}
-- (NSString *) mainNibName {
- return @"QSDeliciousPrefPane";
+- (NSView *)settingsView
+{
+ if (![super settingsView]) {
+ [[NSBundle bundleForClass:[self class]] loadNibNamed:NSStringFromClass([self class]) owner:self topLevelObjects:NULL];
+ }
+ return [super settingsView];
+}
+
+#pragma mark - Settings Helpers
+
+- (SocialSite)siteIndex {
+ NSDictionary *settings = self.selectedEntry.sourceSettings;
+ return [settings objectForKey:@"site"] != nil ? [[settings objectForKey:@"site"] integerValue] : SocialSiteDelicious;
+}
+
+- (NSString *)currentUsername {
+ return [self.selectedEntry.sourceSettings objectForKey:@"username"];
+}
+
+- (NSString *)currentHost {
+ return [self.selectedEntry.sourceSettings objectForKey:@"host"];
}
-- (void)populateFields {
- NSLog(@"populating: %@/%@", [self.selectedEntry.sourceSettings objectForKey:@"username"], [self.selectedEntry.sourceSettings objectForKey:@"site"]);
+- (NSString *)currentPassword {
+ return [self.selectedEntry.sourceSettings objectForKey:@"password"];
}
-- (NSView *) settingsView {
- if (![super settingsView]) {
- [[NSBundle bundleForClass:[self class]] loadNibNamed:@"QSDeliciousPlugInSource" owner:self topLevelObjects:NULL];
- }
- return [super settingsView];
+- (BOOL)includeTags {
+ return [[self.selectedEntry.sourceSettings objectForKey:@"includeTags"] boolValue];
}
-// Keychain Access -- The QS Built-in ones seems to be broken
+#pragma mark - Keychain Access
- (SecProtocolType)protocolTypeForString:(NSString *)protocol {
- if ([protocol isEqualToString:@"ftp"]) return kSecProtocolTypeFTP;
- else if ([protocol isEqualToString:@"http"]) return kSecProtocolTypeHTTP;
- else if ([protocol isEqualToString:@"sftp"]) return kSecProtocolTypeFTPS;
- else if ([protocol isEqualToString:@"eppc"]) return kSecProtocolTypeEPPC;
- else if ([protocol isEqualToString:@"afp"]) return kSecProtocolTypeAFP;
- else if ([protocol isEqualToString:@"smb"]) return kSecProtocolTypeSMB;
- else if ([protocol isEqualToString:@"ssh"]) return kSecProtocolTypeSSH;
- else if ([protocol isEqualToString:@"telnet"]) return kSecProtocolTypeTelnet;
- return 0;
+ if ([protocol isEqualToString:@"ftp"]) return kSecProtocolTypeFTP;
+ else if ([protocol isEqualToString:@"http"]) return kSecProtocolTypeHTTP;
+ else if ([protocol isEqualToString:@"sftp"]) return kSecProtocolTypeFTPS;
+ else if ([protocol isEqualToString:@"eppc"]) return kSecProtocolTypeEPPC;
+ else if ([protocol isEqualToString:@"afp"]) return kSecProtocolTypeAFP;
+ else if ([protocol isEqualToString:@"smb"]) return kSecProtocolTypeSMB;
+ else if ([protocol isEqualToString:@"ssh"]) return kSecProtocolTypeSSH;
+ else if ([protocol isEqualToString:@"telnet"]) return kSecProtocolTypeTelnet;
+ return 0;
}
- (NSString *)passwordForHost:(NSString *)host user:(NSString *)user andType:(SecProtocolType)type {
- const char *buffer;
- UInt32 length = 0;
- OSErr err;
-
- err = SecKeychainFindInternetPassword(NULL,
- (UInt32)[host length], [host UTF8String],
- 0,
- NULL,
- (UInt32)[user length], [user UTF8String],
- 0, NULL,
- 0,
- type,
- 0,
- &length, (void**)&buffer,
- NULL);
-
- if (err == noErr) {
- NSString *password = [NSString stringWithUTF8String:buffer];
- SecKeychainItemFreeContent(NULL,(void *)buffer);
- return password;
- }
- return nil;
+ const char *buffer;
+ UInt32 length = 0;
+ OSErr err;
+
+ err = SecKeychainFindInternetPassword(NULL,
+ (UInt32)[host length], [host UTF8String],
+ 0,
+ NULL,
+ (UInt32)[user length], [user UTF8String],
+ 0, NULL,
+ 0,
+ type,
+ 0,
+ &length, (void**)&buffer,
+ NULL);
+
+ if (err == noErr) {
+ NSString *password = [NSString stringWithUTF8String:buffer];
+ SecKeychainItemFreeContent(NULL,(void *)buffer);
+ return password;
+ }
+ return nil;
}
- (NSString *)passwordForHost:(NSString *)host user:(NSString *)user andScheme:(NSString *)scheme {
- NSString *password = nil;
-
- SecProtocolType type = [self protocolTypeForString:scheme];
-
- password = [self passwordForHost:host user:user andType:type];
-
- if (!password && type == kSecProtocolTypeFTP)
- password = [self passwordForHost:host user:user andType:kSecProtocolTypeFTPAccount]; // Workaround for Transmit's old type usage
- if ( !password )
- password = [self passwordForHost:host user:user andType:0];
- if ( !password )
- NSLog(@"Couldn't find password. URL:%@ %@ %@", host, user,scheme );
- return password;
+ NSString *password = nil;
+
+ SecProtocolType type = [self protocolTypeForString:scheme];
+
+ password = [self passwordForHost:host user:user andType:type];
+
+ if (!password && type == kSecProtocolTypeFTP)
+ password = [self passwordForHost:host user:user andType:kSecProtocolTypeFTPAccount]; // Workaround for Transmit's old type usage
+ if ( !password )
+ password = [self passwordForHost:host user:user andType:0];
+ if ( !password )
+ NSLog(@"Couldn't find password. URL:%@ %@ %@", host, user,scheme );
+ return password;
}
- (NSString *)keychainPasswordForURL:(NSURL *)url {
- return [self passwordForHost:[url host] user:[url user] andScheme:[url scheme]];
+ return [self passwordForHost:[url host] user:[url user] andScheme:[url scheme]];
}
- (OSErr)addURLPasswordToKeychain:(NSURL *)url {
- OSErr err;
-
- NSString *host = [url host];
- NSString *user = [url user];
- NSString *pass = [url password];
-
- SecProtocolType type = [self protocolTypeForString:[url scheme]];
-
- SecKeychainItemRef existing = NULL;
-
- err = SecKeychainFindInternetPassword(NULL,
- (UInt32)[host length], [host UTF8String],
- 0, NULL,
- (UInt32)[user length], [user UTF8String],
- 0, NULL,
- 0,
- type,
- 0,
- NULL,NULL,
- &existing);
-
- if ( !err ) {
- err = SecKeychainItemModifyContent( existing, NULL, (UInt32)[pass length], [pass UTF8String] );
- CFRelease( existing );
- } else {
- err = SecKeychainAddInternetPassword(NULL,
+ OSErr err;
+
+ NSString *host = [url host];
+ NSString *user = [url user];
+ NSString *pass = [url password];
+
+ SecProtocolType type = [self protocolTypeForString:[url scheme]];
+
+ SecKeychainItemRef existing = NULL;
+
+ err = SecKeychainFindInternetPassword(NULL,
+ (UInt32)[host length], [host UTF8String],
+ 0, NULL,
+ (UInt32)[user length], [user UTF8String],
+ 0, NULL,
+ 0,
+ type,
+ 0,
+ NULL,NULL,
+ &existing);
+
+ if ( !err ) {
+ err = SecKeychainItemModifyContent( existing, NULL, (UInt32)[pass length], [pass UTF8String] );
+ CFRelease( existing );
+ } else {
+ err = SecKeychainAddInternetPassword(NULL,
(UInt32)[host length], [host UTF8String],
0, NULL,
(UInt32)[user length], [user UTF8String],
@@ -138,218 +155,130 @@
(UInt32)[pass length], [pass UTF8String],
NULL);
}
-
- return err;
-}
-
-// Site Index/API/URL
-
-- (NSInteger)siteIndex {
- NSDictionary *settings = self.selectedEntry.sourceSettings;
- return [settings objectForKey:@"site"] != nil ? [[settings objectForKey:@"site"] integerValue] : 0;
-}
-
-- (NSString *)siteURLForIndex:(NSInteger)siteIndex {
- if (siteIndex == 0) return @"del.icio.us";
- else if (siteIndex == 1) return @"ma.gnolia.com";
- else if (siteIndex == 2) return @"pinboard.in";
- else return nil;
-}
-
-- (NSString *)reversedSiteURLForIndex:(NSInteger)siteIndex {
- if (siteIndex == 0) return @"us.icio.del";
- else if (siteIndex == 1) return @"com.gnolia.ma";
- else if (siteIndex == 2) return @"in.pinboard";
- else return nil;
+
+ return err;
}
-- (NSString *)tagURLForIndex:(NSInteger)siteIndex {
- return [NSString stringWithFormat:@"tag.%@", [self reversedSiteURLForIndex:[self siteIndex]]];
-}
-
-- (NSString *)apiURLForIndex:(NSInteger)siteIndex {
- if (siteIndex == 0) return @"api.del.icio.us/v1";
- else if (siteIndex == 1) return @"ma.gnolia.com/api/mirrord/v1";
- else if (siteIndex == 2) return @"api.pinboard.in/v1";
- else return nil;
-}
-
-// Current Site/API URL
-
-- (NSString *)currentSiteURL {
- return [self siteURLForIndex:[self siteIndex]];
-}
-
-- (NSString *)currentAPIURL {
- return [self apiURLForIndex:[self siteIndex]];
-}
-
-// Useranme
-
-- (NSString *)currentUsername {
- return [self.selectedEntry.sourceSettings objectForKey:@"username"];
-}
-
-// Password Related
-
-- (NSString *)currentPassword {
- NSString *account = [self currentUsername];
- if (!account) return nil;
-
- NSURL *keychainURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@@%@/", account, [self currentSiteURL]]];
- NSString *password = [self keychainPasswordForURL:keychainURL];
-
- return password;
+- (NSString *)oldCurrentPassword {
+ NSString *account = [self currentUsername];
+ if (!account) return nil;
+
+ SocialSite site = [self siteIndex];
+ NSString *host = nil;
+
+ // For Linkding, use the custom host; for others, use the standard site URL
+ if (site == SocialSiteLinkding) {
+ host = [self currentHost];
+ if (!host) return nil;
+ } else {
+ host = [SocialSiteHelper siteURLForSite:site];
+ }
+
+ NSURL *keychainURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@@%@/", account, host]];
+ NSString *password = [self keychainPasswordForURL:keychainURL];
+
+ return password;
}
- (void)setCurrentPassword:(NSString *)newPassword {
- NSString *account = [self currentUsername];
- if (!account) return;
- if ([newPassword length] <= 0) return;
-
- NSURL *keychainURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@%@/", account, newPassword, [self currentSiteURL]]];
-
- [self addURLPasswordToKeychain:keychainURL];
-}
-
-// Bookarmk/Caching
-
-- (NSData *)cachedBookmarkData {
- NSString *cachePath=[QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", [self currentSiteURL]], NO) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml", [self currentUsername]]];
- return [NSData dataWithContentsOfFile:cachePath];
-}
-
-- (NSData *)bookmarkData {
- if (![self currentUsername] || ![self currentPassword]) return nil;
-
- NSString *urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?", [self currentUsername], [self currentPassword], [self currentAPIURL]];
- NSURL *requestURL = [NSURL URLWithString:urlString];
-
- NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:requestURL
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:60.0];
- [theRequest setHTTPMethod:@"POST"];
- [theRequest setValue:@"text/xml" forHTTPHeaderField:@"Content-type"];
- [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"];
-
- NSError *error;
- NSData *data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error];
-
- NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", [self currentSiteURL]], YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml", [self currentUsername]]];
- [data writeToFile:cachePath atomically:NO];
-
- return data;
+ NSString *account = [self currentUsername];
+ if (!account) return;
+ if ([newPassword length] <= 0) return;
+
+ SocialSite site = [self siteIndex];
+ NSString *host = nil;
+
+ // For Linkding, use the custom host; for others, use the standard site URL
+ if (site == SocialSiteLinkding) {
+ host = [self currentHost];
+ if (!host) return;
+ } else {
+ host = [SocialSiteHelper siteURLForSite:site];
+ }
+
+ NSURL *keychainURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@%@/", account, newPassword, host]];
+
+ [self addURLPasswordToKeychain:keychainURL];
}
-- (QSObject *)objectForPost:(NSDictionary *)post {
- QSObject *newObject=[QSObject makeObjectWithIdentifier:[post objectForKey:@"hash"]];
- [newObject setObject:[post objectForKey:@"href"] forType:QSURLType];
- [newObject setName:[post objectForKey:@"description"]];
- [newObject setDetails:[post objectForKey:@"extended"]];
- [newObject setPrimaryType:QSURLType];
- //NSDate *date=[NSCalendarDate dateWithString:[post objectForKey:@"time"]
- // calendarFormat:@"%Y-%m-%dT%H:%M:%SZ"];
- //[date setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
- //[newObject setObject:date forMeta:kQSObjectCreationDate];
- return newObject;
-}
+#pragma mark - Objects For Entry
- (NSArray *)objectsForEntry:(NSDictionary *)theEntry {
- NSData *data = nil;//[self cachedBookmarkData];
- if (![data length]) data = [self bookmarkData];
-
- NSXMLParser *postParser = [[NSXMLParser alloc]initWithData:data];
- [postParser setDelegate:self];
-
- posts = [NSMutableArray arrayWithCapacity:1];
-
- [postParser parse];
-
- NSMutableArray *objects=[NSMutableArray arrayWithCapacity:1];
- QSObject *newObject;
- NSEnumerator *e=[posts objectEnumerator];
- NSDictionary *post;
- NSMutableSet *tagSet=[NSMutableSet set];
- while(post=[e nextObject]){
- newObject=[self objectForPost:post];
- [tagSet addObjectsFromArray:[[post objectForKey:@"tag"]componentsSeparatedByString:@" "]];
- [objects addObject:newObject];
- }
- NSString *tag;
- e=[tagSet objectEnumerator];
-
- if ([[theEntry objectForKey:@"includeTags"]boolValue]){
- while(tag=[e nextObject]){
- newObject=[QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[del.icio.us tag]:%@",tag]];
- [newObject setObject:tag forType:[self tagURLForIndex:[self siteIndex]]];
- [newObject setObject:[self currentUsername] forMeta:@"us.icio.del.username"];
- [newObject setName:tag];
- [newObject setPrimaryType:[self tagURLForIndex:[self siteIndex]]];
- [objects addObject:newObject];
- }
- }
- [postParser release];
-
- return objects;
+ NSLog(@"WE HAVE BEEN REQUESTED");
+ SocialSite site = [self siteIndex];
+ NSString *username = [self currentUsername];
+ NSString *password = [self currentPassword];
+ NSString *host = [self currentHost];
+ BOOL includeTags = [self includeTags];
+ // Get the appropriate provider using the factory
+ QSBookmarkProviderFactory *factory = [QSBookmarkProviderFactory sharedFactory];
+ id<QSBookmarkProvider> provider = [factory providerForSite:site username:username password:password host:host];
+
+ NSLog(@"Checking for %ld, user %@, pass %@, host %@", (long)site, username, password, host);
+ if (!provider) {
+ NSLog(@"No provider available for site %ld with username %@", (long)site, username);
+ return @[];
+ }
+
+ return [provider fetchBookmarksForSite:site username:username password:password host:host includeTags:includeTags];
}
-- (NSArray *)objectsForTag:(NSString *)tag username:(NSString *)username{
- NSData *data=[self cachedBookmarkData];
-
- NSXMLParser *postParser=[[NSXMLParser alloc]initWithData:data];
- [postParser setDelegate:self];
- posts=[NSMutableArray arrayWithCapacity:1];
- [postParser parse];
-
- NSMutableArray *objects=[NSMutableArray arrayWithCapacity:1];
- QSObject *newObject;
- NSEnumerator *e=[posts objectEnumerator];
- NSDictionary *post;
- // NSMutableSet *tagSet=[NSMutableSet set];
- while(post=[e nextObject]){
- if ([[post objectForKey:@"tag"]rangeOfString:tag].location==NSNotFound)continue;
- newObject=[self objectForPost:post];
- [objects addObject:newObject];
- }
- return objects;
-}
-
-// Object Handler Methods
-
-/*
-- (void)setQuickIconForObject:(QSObject *)object {
- [object setIcon:nil]; // An icon that is either already in memory or easy to load
+- (NSArray *)objectsForTag:(NSString *)tag username:(NSString *)username {
+ SocialSite site = [self siteIndex];
+ NSString *password = [self currentPassword];
+ NSString *host = [self currentHost];
+
+ // Get the appropriate provider using the factory
+ QSBookmarkProviderFactory *factory = [QSBookmarkProviderFactory sharedFactory];
+ id<QSBookmarkProvider> provider = [factory providerForSite:site username:username password:password host:host];
+
+ if (!provider) {
+ NSLog(@"No provider available for site %ld with username %@", (long)site, username);
+ return @[];
+ }
+
+ // Check if provider supports tag-based fetching
+ if ([provider respondsToSelector:@selector(fetchBookmarksForTag:site:username:password:host:)]) {
+ return [provider fetchBookmarksForTag:tag site:site username:username password:password host:host];
+ }
+
+ return @[];
}
-- (BOOL)loadIconForObject:(QSObject *)object {
- return NO;
- id data=[object objectForType:QSDeliciousPlugInType];
- [object setIcon:nil];
- return YES;
-}
-*/
+#pragma mark - Object Handler Methods
- (void)setQuickIconForObject:(QSObject *)object {
- [object setIcon:[[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]];
+ [object setIcon:[[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]];
}
- (BOOL)loadChildrenForObject:(QSObject *)object {
- [object setChildren:[self objectsForTag:[object objectForType:[self tagURLForIndex:[self siteIndex]]]
- username:[object objectForMeta:@"us.icio.del.username"]]];
- return YES;
-}
-
-// NSXMLParserDelegate Functions
-
-- (void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
- NSLog(@"XML Parser started %@ %@ %@ %@",elementName,namespaceURI,qName,attributeDict);
- if ([elementName isEqualToString:@"post"] && attributeDict)
- [posts addObject:attributeDict];
-}
-
-- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
- NSLog(@"XML Parser ended %@ %@ %@", elementName, namespaceURI, qName);
+ SocialSite site = [self siteIndex];
+
+ NSString *tagType = nil;
+ if (site == SocialSiteLinkding) {
+ tagType = @"tag.linkding";
+ } else {
+ NSString *reversedURL = [SocialSiteHelper reversedSiteURLForSite:site];
+ tagType = [NSString stringWithFormat:@"tag.%@", reversedURL];
+ }
+
+ NSString *tag = [object objectForType:tagType];
+ if (!tag) return NO;
+
+ NSString *username = nil;
+ if (site == SocialSiteLinkding) {
+ username = [object objectForMeta:@"linkding.username"];
+ } else {
+ NSString *reversedURL = [SocialSiteHelper reversedSiteURLForSite:site];
+ username = [object objectForMeta:[NSString stringWithFormat:@"%@.username", reversedURL]];
+ }
+
+ if (!username) return NO;
+
+ NSArray *children = [self objectsForTag:tag username:username];
+ [object setChildren:children];
+ return YES;
}
@end