From 8c5345cda99f7ff178fed3d51a59f5602372613d Mon Sep 17 00:00:00 2001 From: "Qiushi (Billy) He" Date: Mon, 2 Aug 2010 23:32:53 -0700 Subject: Implemented support for Pinboard.in --- Info.plist | 2 +- QSDeliciousPlugIn.xcodeproj/project.pbxproj | 8 +- QSDeliciousPlugInSource.nib/classes.nib | 45 -- QSDeliciousPlugInSource.nib/designable.nib | 930 +++++++++++++++++++++++++++ QSDeliciousPlugInSource.nib/info.nib | 16 - QSDeliciousPlugInSource.nib/keyedobjects.nib | Bin 7815 -> 9343 bytes QSDeliciousPlugIn_Source.h | 4 +- QSDeliciousPlugIn_Source.m | 351 ++++++---- 8 files changed, 1172 insertions(+), 184 deletions(-) delete mode 100644 QSDeliciousPlugInSource.nib/classes.nib create mode 100644 QSDeliciousPlugInSource.nib/designable.nib delete mode 100644 QSDeliciousPlugInSource.nib/info.nib diff --git a/Info.plist b/Info.plist index dc1afde..239f99e 100644 --- a/Info.plist +++ b/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleVersion - 94 + 130 NSPrincipalClass QSDeliciousPlugIn QSPlugIn diff --git a/QSDeliciousPlugIn.xcodeproj/project.pbxproj b/QSDeliciousPlugIn.xcodeproj/project.pbxproj index 03b9ea3..c6b5bd2 100644 --- a/QSDeliciousPlugIn.xcodeproj/project.pbxproj +++ b/QSDeliciousPlugIn.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 28855EDF1207EA17003DC758 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28855EDE1207EA17003DC758 /* Security.framework */; }; 7F8AD05107F2503600011548 /* QSDeliciousPlugInSource.nib in Resources */ = {isa = PBXBuildFile; fileRef = 7F8AD05007F2503600011548 /* QSDeliciousPlugInSource.nib */; }; 7F9441A10803A9D9007EDC31 /* QSObjectSource.name.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7F9441A00803A9D9007EDC31 /* QSObjectSource.name.strings */; }; 7FB0DC1A0B91FF8600A5B6FF /* bookmark_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7FB0DC190B91FF8600A5B6FF /* bookmark_icon.png */; }; @@ -30,11 +31,12 @@ 14A6EB3E10CFFC79000B05B4 /* QSPlugIn_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = QSPlugIn_Release.xcconfig; sourceTree = ""; }; 14A6EB3F10CFFC79000B05B4 /* QSPlugIn.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = QSPlugIn.xcconfig; sourceTree = ""; }; 14A6EB4010CFFC79000B05B4 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 28855EDE1207EA17003DC758 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 7F8AD05007F2503600011548 /* QSDeliciousPlugInSource.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = QSDeliciousPlugInSource.nib; sourceTree = ""; }; 7F9441A00803A9D9007EDC31 /* QSObjectSource.name.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = QSObjectSource.name.strings; sourceTree = ""; }; 7FB0DC190B91FF8600A5B6FF /* bookmark_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bookmark_icon.png; sourceTree = ""; }; - 7FB0DC2D0B91FFC500A5B6FF /* QSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = QSCore.framework; sourceTree = ""; }; - 7FB0DC2E0B91FFC600A5B6FF /* QSFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = QSFoundation.framework; sourceTree = ""; }; + 7FB0DC2D0B91FFC500A5B6FF /* QSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QSCore.framework; path = /Applications/Quicksilver.app/Contents/Frameworks/QSCore.framework; sourceTree = ""; }; + 7FB0DC2E0B91FFC600A5B6FF /* QSFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QSFoundation.framework; path = /Applications/Quicksilver.app/Contents/Frameworks/QSFoundation.framework; sourceTree = ""; }; 8D1AC9730486D14A00FE50C9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8D1AC9740486D14A00FE50C9 /* Social Bookmarks.qsplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Social Bookmarks.qsplugin"; sourceTree = BUILT_PRODUCTS_DIR; }; DD92D38A0106425D02CA0E72 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; @@ -55,6 +57,7 @@ E182BE5B06FC9C46007BF2C2 /* PreferencePanes.framework in Frameworks */, 7FB0DC2F0B91FFC600A5B6FF /* QSCore.framework in Frameworks */, 7FB0DC300B91FFC600A5B6FF /* QSFoundation.framework in Frameworks */, + 28855EDF1207EA17003DC758 /* Security.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -114,6 +117,7 @@ 7FB0DC2E0B91FFC600A5B6FF /* QSFoundation.framework */, E182BE5A06FC9C46007BF2C2 /* PreferencePanes.framework */, DD92D38A0106425D02CA0E72 /* Cocoa.framework */, + 28855EDE1207EA17003DC758 /* Security.framework */, ); name = Frameworks; sourceTree = ""; diff --git a/QSDeliciousPlugInSource.nib/classes.nib b/QSDeliciousPlugInSource.nib/classes.nib deleted file mode 100644 index 9ea8de6..0000000 --- a/QSDeliciousPlugInSource.nib/classes.nib +++ /dev/null @@ -1,45 +0,0 @@ -{ - IBClasses = ( - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - {CLASS = NSObject; LANGUAGE = ObjC; }, - { - CLASS = NSPreferencePane; - LANGUAGE = ObjC; - OUTLETS = { - "_firstKeyView" = NSView; - "_initialKeyView" = NSView; - "_lastKeyView" = NSView; - "_window" = NSWindow; - }; - SUPERCLASS = NSObject; - }, - { - CLASS = QSAllApplicationsObjectSource; - LANGUAGE = ObjC; - SUPERCLASS = QSObjectSource; - }, - { - ACTIONS = {savePassword = id; }; - CLASS = "QSDeliciousPlugIn_Source"; - LANGUAGE = ObjC; - OUTLETS = {passField = NSTextField; userField = NSTextField; }; - SUPERCLASS = QSObjectSource; - }, - { - ACTIONS = {savePassword = id; }; - CLASS = QSDeliciousPrefPane; - LANGUAGE = ObjC; - OUTLETS = {passField = NSTextField; userField = NSTextField; }; - SUPERCLASS = NSPreferencePane; - }, - {CLASS = QSGroupObjectSource; LANGUAGE = ObjC; SUPERCLASS = QSObjectSource; }, - { - CLASS = QSObjectSource; - LANGUAGE = ObjC; - OUTLETS = {settingsView = NSView; }; - SUPERCLASS = NSObject; - }, - {CLASS = QSVolumesObjectSource; LANGUAGE = ObjC; SUPERCLASS = QSObjectSource; } - ); - IBVersion = 1; -} \ No newline at end of file diff --git a/QSDeliciousPlugInSource.nib/designable.nib b/QSDeliciousPlugInSource.nib/designable.nib new file mode 100644 index 0000000..030965a --- /dev/null +++ b/QSDeliciousPlugInSource.nib/designable.nib @@ -0,0 +1,930 @@ + + + + 1060 + 10F569 + 762 + 1038.29 + 461.00 + + YES + + YES + + + YES + + + + YES + + + + + YES + + + YES + + + + YES + + QSDeliciousPlugIn_Source + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{62, 510}, {270, 151}} + 1886912512 + + Window + + NSWindow + + View + + {1.79769e+308, 1.79769e+308} + {213, 107} + + + 256 + + YES + + + 266 + {{89, 85}, {150, 19}} + + YES + + -1804468671 + 4326400 + + + LucidaGrande + 11 + 3100 + + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + 3 + MAA + + + + + + + 264 + {{6, 88}, {78, 14}} + + YES + + 67239424 + 71434240 + Name: + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + + + + + + 264 + {{6, 61}, {78, 14}} + + YES + + 67239424 + 71434240 + Password: + + + + + + + + + 266 + {{89, 58}, {150, 19}} + + YES + + -1804468671 + 4326400 + + + + YES + + + + + + + 264 + {{86, 34}, {87, 18}} + + YES + + 67239424 + 131072 + Include tags + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 264 + {{86, 113}, {126, 26}} + + YES + + -2076049856 + 2048 + + LucidaGrande + 13 + 1044 + + + 109199615 + 1 + + LucidaGrande + 13 + 16 + + + + + + 400 + 75 + + + del.icio.us + + 1048576 + 2147483647 + 1 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + _popUpItemAction: + + + YES + + + OtherViews + + + YES + + + + ma.gnolia + + 1048576 + 2147483647 + + + _popUpItemAction: + 1 + + + + + pinboard.in + + 1048576 + 2147483647 + + + _popUpItemAction: + 2 + + + + + 3 + YES + YES + 1 + + + + + 264 + {{6, 119}, {78, 14}} + + YES + + 67239424 + 71434240 + Site: + + + + + + + + {270, 151} + + + {{0, 0}, {1024, 746}} + {213, 129} + {1.79769e+308, 1.79769e+308} + + + + YES + username + includeTags + site + + YES + + + + + + YES + + + nextKeyView + + + + 56 + + + + nextKeyView + + + + 57 + + + + userField + + + + 65 + + + + passField + + + + 66 + + + + settingsView + + + + 69 + + + + value: selection.username + + + + + + value: selection.username + value + selection.username + 2 + + + 82 + + + + value: selection.includeTags + + + + + + value: selection.includeTags + value + selection.includeTags + 2 + + + 84 + + + + value: currentPassword + + + + + + value: currentPassword + value + currentPassword + 2 + + + 85 + + + + contentObject: selection.info + + + + + + contentObject: selection.info + contentObject + selection.info + 2 + + + 91 + + + + selectedTag: selection.site + + + + + + selectedTag: selection.site + selectedTag + selection.site + 2 + + + 98 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + 5 + + + YES + + + + Window + + + 6 + + + YES + + + + + + + + + + + + 50 + + + YES + + + + + + 51 + + + YES + + + + + + 52 + + + YES + + + + + + 53 + + + YES + + + + + + 70 + + + YES + + + + + + 92 + + + YES + + + + + + 99 + + + YES + + + + + + 75 + + + Settings + + + 101 + + + + + 102 + + + + + 103 + + + + + 104 + + + + + 105 + + + + + 106 + + + YES + + + + + + 107 + + + + + 93 + + + YES + + + + + + + + 95 + + + + + 94 + + + + + -3 + + + Application + + + 108 + + + + + + + YES + + YES + 108.ImportedFromIB2 + 5.IBEditorWindowLastContentRect + 5.IBWindowTemplateEditedContentRect + 5.ImportedFromIB2 + 5.windowTemplate.hasMinSize + 5.windowTemplate.minSize + 50.ImportedFromIB2 + 51.ImportedFromIB2 + 52.ImportedFromIB2 + 53.CustomClassName + 53.ImportedFromIB2 + 6.ImportedFromIB2 + 70.ImportedFromIB2 + 75.ImportedFromIB2 + 92.ImportedFromIB2 + 93.IBEditorWindowLastContentRect + 93.ImportedFromIB2 + 94.ImportedFromIB2 + 95.ImportedFromIB2 + 99.ImportedFromIB2 + + + YES + + {{0, 994}, {270, 151}} + {{0, 994}, {270, 151}} + + + {213, 107} + + + + NSSecureTextField + + + + + + {{75, 1070}, {145, 63}} + + + + + + + + YES + + + YES + + + + + YES + + + YES + + + + 108 + + + + YES + + FirstResponder + NSObject + + IBUserSource + + + + + NSObject + + IBUserSource + + + + + QSDeliciousPlugIn_Source + QSObjectSource + + YES + + YES + passField + userField + + + YES + NSTextField + NSTextField + + + + IBProjectSource + QSDeliciousPlugIn_Source.h + + + + QSDeliciousPlugIn_Source + QSObjectSource + + savePassword: + id + + + IBUserSource + + + + + QSObjectSource + NSObject + + IBUserSource + + + + + + YES + + NSApplication + + relaunch: + id + + + IBFrameworkSource + QSFoundation.framework/Headers/NSApplication_BLTRExtensions.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSIconLoader.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSObject.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSObjectSource.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSObject_FileHandling.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSPlugIn.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSProxyObject.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSRegistry.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSTask.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSTriggerCenter.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/QSURLDownloadWrapper.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/UKFileWatcher.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/UKKQueue.h + + + + NSObject + + IBFrameworkSource + QSCore.framework/Headers/UKMainThreadProxy.h + + + + NSObject + + IBFrameworkSource + QSFoundation.framework/Headers/NDHotKeyEvent.h + + + + NSObject + + IBFrameworkSource + QSFoundation.framework/Headers/NSArray_BLTRExtensions.h + + + + NSObject + + IBFrameworkSource + QSFoundation.framework/Headers/NSObject+BLTRExtensions.h + + + + NSObject + + IBFrameworkSource + QSFoundation.framework/Headers/NSObject+ReaperExtensions.h + + + + NSView + + IBFrameworkSource + QSFoundation.framework/Headers/NSView_BLTRExtensions.h + + + + NSWindow + + IBFrameworkSource + QSFoundation.framework/Headers/NSWindow_BLTRExtensions.h + + + + QSObjectSource + NSObject + + settingsView + NSView + + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + + + diff --git a/QSDeliciousPlugInSource.nib/info.nib b/QSDeliciousPlugInSource.nib/info.nib deleted file mode 100644 index 06fc562..0000000 --- a/QSDeliciousPlugInSource.nib/info.nib +++ /dev/null @@ -1,16 +0,0 @@ - - - - - IBDocumentLocation - 365 116 356 240 0 0 1024 746 - IBFramework Version - 446.1 - IBOpenObjects - - 5 - - IBSystem Version - 8J135 - - diff --git a/QSDeliciousPlugInSource.nib/keyedobjects.nib b/QSDeliciousPlugInSource.nib/keyedobjects.nib index 6ef063b..a794dc2 100644 Binary files a/QSDeliciousPlugInSource.nib/keyedobjects.nib and b/QSDeliciousPlugInSource.nib/keyedobjects.nib differ diff --git a/QSDeliciousPlugIn_Source.h b/QSDeliciousPlugIn_Source.h index 5ddd9d2..89729de 100644 --- a/QSDeliciousPlugIn_Source.h +++ b/QSDeliciousPlugIn_Source.h @@ -9,7 +9,7 @@ #import "QSDeliciousPlugIn_Source.h" -@interface QSDeliciousPlugIn_Source : QSObjectSource{ +@interface QSDeliciousPlugIn_Source : QSObjectSource { NSMutableArray *posts; NSMutableArray *tags; NSMutableArray *dates; @@ -18,7 +18,5 @@ IBOutlet NSTextField *passField; } -- (IBAction)savePassword:(id)sender; - @end diff --git a/QSDeliciousPlugIn_Source.m b/QSDeliciousPlugIn_Source.m index 1267654..a2d6494 100644 --- a/QSDeliciousPlugIn_Source.m +++ b/QSDeliciousPlugIn_Source.m @@ -9,75 +9,237 @@ #import "QSDeliciousPlugIn_Source.h" #import -#define kQSDeliciousTagType @"us.icio.del.tag" -#define DELICIOUS_API_URL @"api.del.icio.us/v1" -#define MAGNOLIA_API_URL @"ma.gnolia.com/api/mirrord/v1" +#import @implementation QSDeliciousPlugIn_Source -+ (void)initialize{ + ++ (void)initialize { [self setKeys:[NSArray arrayWithObject:@"selection"] triggerChangeNotificationsForDependentKey:@"currentPassword"]; } -- (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry{ - return -[indexDate timeIntervalSinceNow] < 24*60*60; + +- (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry { + return -[indexDate timeIntervalSinceNow] < 24 * 60 * 60; } -- (BOOL)isVisibleSource{return YES;} +- (BOOL)isVisibleSource{ return YES; } -- (NSImage *) iconForEntry:(NSDictionary *)dict{ +- (NSImage *) iconForEntry:(NSDictionary *)dict { return [[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]; } -- (NSString *)identifierForObject:(id )object{ - return nil; +- (NSString *) mainNibName { + return @"QSDeliciousPrefPane"; } -- (NSView *) settingsView{ - if (![super settingsView]){ +- (void)populateFields { + NSLog(@"populating: %@/%@", [[selection info] objectForKey:@"username"], [[selection info] objectForKey:@"site"]); +} + +- (NSView *) settingsView { + if (![super settingsView]) [NSBundle loadNibNamed:@"QSDeliciousPlugInSource" owner:self]; - } return [super settingsView]; } -- (NSData *)cachedBookmarkDataForUser:(NSString *)username{ - NSString *cachePath=[QSApplicationSupportSubPath(@"Caches/del.icio.us/",NO) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml",username]]; - return [NSData dataWithContentsOfFile:cachePath]; +// Keychain Access -- The QS Built-in ones seems to be broken + +- (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; } -- (NSString *)passwordForUser:(NSString *)username{ - NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:@"http://%@@del.icio.us/",username]]; - return [url keychainPassword]; + +- (NSString *)passwordForHost:(NSString *)host user:(NSString *)user andType:(SecProtocolType)type { + const char *buffer; + UInt32 length = 0; + OSErr err; + + err = SecKeychainFindInternetPassword(NULL, + [host length], [host UTF8String], + 0, + NULL, + [user length], [user UTF8String], + 0, NULL, + 0, + type, + 0, + &length, (void**)&buffer, + NULL); + + if (err == noErr) { + NSString *password = [[[NSString alloc] initWithCString:buffer length:length] autorelease]; + SecKeychainItemFreeContent(NULL,(void *)buffer); + return password; + } + return nil; } -- (NSData *)bookmarkDataForUser:(NSString *)username onSite:(int)isMagnolia{ - NSString *password=[self passwordForUser:username]; - if (VERBOSE)NSLog(@"Downloading del.icio.us bookmarks for %@ %d",username, isMagnolia); - //NSString *count=[[self currentEntry] objectForKey:@"QSDeliciousRecentCount"]; +- (NSString *)passwordForHost:(NSString *)host user:(NSString *)user andScheme:(NSString *)scheme { + NSString *password = nil; - if (!username || !password) return nil; - //if (!count)count=@"50"; + 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]]; +} - - NSString *apiurl=isMagnolia?MAGNOLIA_API_URL:DELICIOUS_API_URL; +- (OSErr)addURLPasswordToKeychain:(NSURL *)url { + OSErr err; - NSError *error; - NSURL *url=[NSURL URLWithString: - [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?",username,password,apiurl]]; + NSString *host = [url host]; + NSString *user = [url user]; + NSString *pass = [url password]; - NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:url - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; + SecProtocolType type = [self protocolTypeForString:[url scheme]]; - [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"]; + SecKeychainItemRef existing = NULL; + + err = SecKeychainFindInternetPassword(NULL, + [host length], [host UTF8String], + 0, NULL, + [user length], [user UTF8String], + 0, NULL, + 0, + type, + 0, + NULL,NULL, + &existing); + + if ( !err ) { + err = SecKeychainItemModifyContent( existing, NULL, [pass length], [pass UTF8String] ); + CFRelease( existing ); + } else { + err = SecKeychainAddInternetPassword(NULL, + [host length], [host UTF8String], + 0, NULL, + [user length], [user UTF8String], + 0, NULL, + 0, + type, + 0, + [pass length], [pass UTF8String], + NULL); + } + + return err; +} + +// Site Index/API/URL + +- (int)siteIndex { + return [[selection info] objectForKey:@"site"] != nil ? [[[selection info] objectForKey:@"site"] integerValue] : 0; +} + +- (NSString *)siteURLForIndex:(int)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:(int)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; +} + +- (NSString *)tagURLForIndex:(int)siteIndex { + return [NSString stringWithFormat:@"%2.%2", [self reversedSiteURLForIndex:[self siteIndex]], @"tag"]; +} + +- (NSString *)apiURLForIndex:(int)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 [[selection info] 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; +} + +- (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]; - // NSURLConnection *theConnection=[[[NSURLConnection alloc] initWithRequest:theRequest delegate:nil]; + 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"]; - //NSLog(@"url %@",url); - NSData *data=[NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error]; - NSString *cachePath=[QSApplicationSupportSubPath(@"Caches/del.icio.us/",YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml",username]]; + 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; } -- (QSObject *)objectForPost:(NSDictionary *)post{ + +- (QSObject *)objectForPost:(NSDictionary *)post { QSObject *newObject=[QSObject makeObjectWithIdentifier:[post objectForKey:@"hash"]]; [newObject setObject:[post objectForKey:@"href"] forType:QSURLType]; [newObject setName:[post objectForKey:@"description"]]; @@ -90,18 +252,15 @@ return newObject; } -- (NSArray *) objectsForEntry:(NSDictionary *)theEntry{ - NSString *username=[theEntry objectForKey:@"username"]; - NSData *data=nil;//[self cachedBookmarkDataForUser:username]; - if (![data length]) data=[self bookmarkDataForUser:username onSite:[[theEntry objectForKey:@"site"]intValue]]; +- (NSArray *)objectsForEntry:(NSDictionary *)theEntry { + NSData *data = nil;//[self cachedBookmarkData]; + if (![data length]) data = [self bookmarkData]; - //NSString *string=[[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]autorelease]; - //NSLog(@"data %@",string); + NSXMLParser *postParser = [[NSXMLParser alloc]initWithData:data]; + [postParser setDelegate:self]; - NSXMLParser *postParser=[[NSXMLParser alloc]initWithData:data]; + posts = [NSMutableArray arrayWithCapacity:1]; - [postParser setDelegate:self]; - posts=[NSMutableArray arrayWithCapacity:1]; [postParser parse]; NSMutableArray *objects=[NSMutableArray arrayWithCapacity:1]; @@ -120,10 +279,10 @@ if ([[theEntry objectForKey:@"includeTags"]boolValue]){ while(tag=[e nextObject]){ newObject=[QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[del.icio.us tag]:%@",tag]]; - [newObject setObject:tag forType:kQSDeliciousTagType]; - [newObject setObject:username forMeta:@"us.icio.del.username"]; + [newObject setObject:tag forType:[self tagURLForIndex:[self siteIndex]]]; + [newObject setObject:[self currentUsername] forMeta:@"us.icio.del.username"]; [newObject setName:tag]; - [newObject setPrimaryType:kQSDeliciousTagType]; + [newObject setPrimaryType:[self tagURLForIndex:[self siteIndex]]]; [objects addObject:newObject]; } } @@ -134,7 +293,7 @@ } - (NSArray *)objectsForTag:(NSString *)tag username:(NSString *)username{ - NSData *data=[self cachedBookmarkDataForUser:username]; + NSData *data=[self cachedBookmarkData]; NSXMLParser *postParser=[[NSXMLParser alloc]initWithData:data]; [postParser setDelegate:self]; @@ -145,7 +304,7 @@ QSObject *newObject; NSEnumerator *e=[posts objectEnumerator]; NSDictionary *post; - NSMutableSet *tagSet=[NSMutableSet set]; + // NSMutableSet *tagSet=[NSMutableSet set]; while(post=[e nextObject]){ if ([[post objectForKey:@"tag"]rangeOfString:tag].location==NSNotFound)continue; newObject=[self objectForPost:post]; @@ -154,83 +313,41 @@ return objects; } -// XML Stuff -- (void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { - // NSLog(@"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(@"ended %@ %@ %@ %@",elementName,namespaceURI,qName); -} - - - - - - - // Object Handler Methods /* - - (void)setQuickIconForObject:(QSObject *)object{ - [object setIcon:nil]; // An icon that is either already in memory or easy to load - } - - (BOOL)loadIconForObject:(QSObject *)object{ - return NO; - id data=[object objectForType:QSDeliciousPlugInType]; - [object setIcon:nil]; - return YES; - } - */ - - - - - -- (NSString *) mainNibName{ - return @"QSDeliciousPrefPane"; +- (void)setQuickIconForObject:(QSObject *)object { + [object setIcon:nil]; // An icon that is either already in memory or easy to load } -- (void)populateFields{ -} -- (NSString *)currentPassword{ - NSString *account=[[self currentEntry] objectForKey:@"username"]; - if (!account)return nil; - NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:@"http://%@@del.icio.us/",account]]; - NSString *password=[url keychainPassword]; - return password; -} -- (void)setCurrentPassword:(NSString *)newPassword{ - NSString *account=[[self currentEntry] objectForKey:@"username"]; - NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@del.icio.us/",account,newPassword]]; - if ([newPassword length]) - [url addPasswordToKeychain]; - +- (BOOL)loadIconForObject:(QSObject *)object { + return NO; + id data=[object objectForType:QSDeliciousPlugInType]; + [object setIcon:nil]; + return YES; } +*/ - - - - - - - - -- (void)setQuickIconForObject:(QSObject *)object{ +- (void)setQuickIconForObject:(QSObject *)object { [object setIcon:[[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]]; } -- (BOOL)loadChildrenForObject:(QSObject *)object{ - [object setChildren: - [self objectsForTag:[object objectForType:kQSDeliciousTagType] - username:[object objectForMeta:@"us.icio.del.username"]]]; + +- (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); +} @end -- cgit