Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 94d27e3a742f0c486ac096db5536429b5fe477a0 0 parents
@matthiasplappert authored
Showing with 7,067 additions and 0 deletions.
  1. +14 −0 .gitignore
  2. +3 −0  .gitmodules
  3. +565 −0 InstapaperKit.xcodeproj/project.pbxproj
  4. +7 −0 InstapaperKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  5. +39 −0 InstapaperKit/IKBookmark.h
  6. +70 −0 InstapaperKit/IKBookmark.m
  7. +44 −0 InstapaperKit/IKConstants.h
  8. +13 −0 InstapaperKit/IKConstants.m
  9. +17 −0 InstapaperKit/IKDeserializer.h
  10. +168 −0 InstapaperKit/IKDeserializer.m
  11. +90 −0 InstapaperKit/IKEngine.h
  12. +861 −0 InstapaperKit/IKEngine.m
  13. +37 −0 InstapaperKit/IKFolder.h
  14. +74 −0 InstapaperKit/IKFolder.m
  15. +48 −0 InstapaperKit/IKURLConnection+Private.h
  16. +27 −0 InstapaperKit/IKURLConnection.h
  17. +104 −0 InstapaperKit/IKURLConnection.m
  18. +22 −0 InstapaperKit/IKUser.h
  19. +43 −0 InstapaperKit/IKUser.m
  20. +30 −0 InstapaperKit/InstapaperKit-Info.plist
  21. +7 −0 InstapaperKit/InstapaperKit-Prefix.pch
  22. +15 −0 InstapaperKit/InstapaperKit.h
  23. +16 −0 InstapaperKit/NSString+IKEncoding.h
  24. +22 −0 InstapaperKit/NSString+IKEncoding.m
  25. +33 −0 InstapaperKit/Third Party/Base64/NSData+Base64.h
  26. +299 −0 InstapaperKit/Third Party/Base64/NSData+Base64.m
  27. +1 −0  InstapaperKit/Third Party/JSONKit
  28. +2 −0  InstapaperKit/en.lproj/InfoPlist.strings
  29. +32 −0 Test/Test-Info.plist
  30. +7 −0 Test/Test-Prefix.pch
  31. +21 −0 Test/TestAppDelegate.h
  32. +172 −0 Test/TestAppDelegate.m
  33. +29 −0 Test/en.lproj/Credits.rtf
  34. +2 −0  Test/en.lproj/InfoPlist.strings
  35. +4,119 −0 Test/en.lproj/MainMenu.xib
  36. +14 −0 Test/main.m
14 .gitignore
@@ -0,0 +1,14 @@
+.DS_Store
+*.swp
+*~.nib
+
+build/*
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspective
+*.perspectivev3
+
+*.xcodeproj/xcuserdata/*
+*.xcodeproj/project.xcworkspace/xcuserdata/*
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "InstapaperKit/Third Party/JSONKit"]
+ path = InstapaperKit/Third Party/JSONKit
+ url = git://github.com/johnezang/JSONKit.git
565 InstapaperKit.xcodeproj/project.pbxproj
@@ -0,0 +1,565 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8C209258133F8E1500561607 /* JSONKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C20923E133F8E1500561607 /* JSONKit.h */; };
+ 8C209259133F8E1500561607 /* JSONKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C20923F133F8E1500561607 /* JSONKit.m */; };
+ 8C880B1D1333BC1600834805 /* IKURLConnection+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C880B1B1333BC1400834805 /* IKURLConnection+Private.h */; };
+ 8CBA8F6B132C26790086AA8F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBA8F6A132C26790086AA8F /* Cocoa.framework */; };
+ 8CBA8F75132C26790086AA8F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA8F73132C26790086AA8F /* InfoPlist.strings */; };
+ 8CBA8F80132C26880086AA8F /* IKURLConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CBA8F7C132C26880086AA8F /* IKURLConnection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CBA8F81132C26880086AA8F /* IKURLConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBA8F7D132C26880086AA8F /* IKURLConnection.m */; };
+ 8CBA8F82132C26880086AA8F /* IKEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CBA8F7E132C26880086AA8F /* IKEngine.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CBA8F83132C26880086AA8F /* IKEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBA8F7F132C26880086AA8F /* IKEngine.m */; };
+ 8CCBC67E132CC419009528C3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBA8F6A132C26790086AA8F /* Cocoa.framework */; };
+ 8CCBC684132CC419009528C3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8CCBC682132CC419009528C3 /* InfoPlist.strings */; };
+ 8CCBC687132CC419009528C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC686132CC419009528C3 /* main.m */; };
+ 8CCBC68A132CC419009528C3 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 8CCBC688132CC419009528C3 /* Credits.rtf */; };
+ 8CCBC68D132CC419009528C3 /* TestAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC68C132CC419009528C3 /* TestAppDelegate.m */; };
+ 8CCBC690132CC419009528C3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8CCBC68E132CC419009528C3 /* MainMenu.xib */; };
+ 8CCBC696132CC4AC009528C3 /* InstapaperKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC695132CC4AC009528C3 /* InstapaperKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CCBC697132CC4D6009528C3 /* InstapaperKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBA8F67132C26790086AA8F /* InstapaperKit.framework */; };
+ 8CCBC699132CC52E009528C3 /* InstapaperKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBA8F67132C26790086AA8F /* InstapaperKit.framework */; };
+ 8CCBC6A5132CE7EA009528C3 /* NSString+IKEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6A3132CE7E9009528C3 /* NSString+IKEncoding.h */; };
+ 8CCBC6A6132CE7EA009528C3 /* NSString+IKEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6A4132CE7E9009528C3 /* NSString+IKEncoding.m */; };
+ 8CCBC6AB132CF2E0009528C3 /* NSData+Base64.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6A9132CF2E0009528C3 /* NSData+Base64.h */; };
+ 8CCBC6AC132CF2E0009528C3 /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6AA132CF2E0009528C3 /* NSData+Base64.m */; };
+ 8CCBC6D7132CF3DE009528C3 /* IKUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6D5132CF3DD009528C3 /* IKUser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CCBC6D8132CF3DE009528C3 /* IKUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6D6132CF3DD009528C3 /* IKUser.m */; };
+ 8CCBC6DB132CF4F7009528C3 /* IKDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6D9132CF4F7009528C3 /* IKDeserializer.h */; };
+ 8CCBC6DC132CF4F7009528C3 /* IKDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6DA132CF4F7009528C3 /* IKDeserializer.m */; };
+ 8CCBC6E0132CFA85009528C3 /* IKBookmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6DE132CFA84009528C3 /* IKBookmark.h */; };
+ 8CCBC6E1132CFA85009528C3 /* IKBookmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6DF132CFA84009528C3 /* IKBookmark.m */; };
+ 8CCBC6E4132CFA8D009528C3 /* IKFolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6E2132CFA8D009528C3 /* IKFolder.h */; };
+ 8CCBC6E5132CFA8D009528C3 /* IKFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6E3132CFA8D009528C3 /* IKFolder.m */; };
+ 8CCBC6EE132CFE02009528C3 /* IKConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CCBC6EC132CFE00009528C3 /* IKConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 8CCBC6EF132CFE02009528C3 /* IKConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CCBC6ED132CFE01009528C3 /* IKConstants.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8CCBC698132CC51E009528C3 /* Copy Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 8CCBC699132CC52E009528C3 /* InstapaperKit.framework in Copy Frameworks */,
+ );
+ name = "Copy Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 8C20923E133F8E1500561607 /* JSONKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONKit.h; sourceTree = "<group>"; };
+ 8C20923F133F8E1500561607 /* JSONKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONKit.m; sourceTree = "<group>"; };
+ 8C880B1B1333BC1400834805 /* IKURLConnection+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IKURLConnection+Private.h"; sourceTree = "<group>"; };
+ 8CBA8F67132C26790086AA8F /* InstapaperKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = InstapaperKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8CBA8F6A132C26790086AA8F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 8CBA8F6D132C26790086AA8F /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 8CBA8F6E132C26790086AA8F /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+ 8CBA8F6F132C26790086AA8F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 8CBA8F72132C26790086AA8F /* InstapaperKit-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "InstapaperKit-Info.plist"; sourceTree = "<group>"; };
+ 8CBA8F74132C26790086AA8F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 8CBA8F76132C26790086AA8F /* InstapaperKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "InstapaperKit-Prefix.pch"; sourceTree = "<group>"; };
+ 8CBA8F7C132C26880086AA8F /* IKURLConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKURLConnection.h; sourceTree = "<group>"; };
+ 8CBA8F7D132C26880086AA8F /* IKURLConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKURLConnection.m; sourceTree = "<group>"; };
+ 8CBA8F7E132C26880086AA8F /* IKEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKEngine.h; sourceTree = "<group>"; };
+ 8CBA8F7F132C26880086AA8F /* IKEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKEngine.m; sourceTree = "<group>"; };
+ 8CCBC67C132CC419009528C3 /* Test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Test.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 8CCBC681132CC419009528C3 /* Test-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Test-Info.plist"; sourceTree = "<group>"; };
+ 8CCBC683132CC419009528C3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 8CCBC685132CC419009528C3 /* Test-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Test-Prefix.pch"; sourceTree = "<group>"; };
+ 8CCBC686132CC419009528C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 8CCBC689132CC419009528C3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
+ 8CCBC68B132CC419009528C3 /* TestAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestAppDelegate.h; sourceTree = "<group>"; };
+ 8CCBC68C132CC419009528C3 /* TestAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestAppDelegate.m; sourceTree = "<group>"; };
+ 8CCBC68F132CC419009528C3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 8CCBC695132CC4AC009528C3 /* InstapaperKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InstapaperKit.h; sourceTree = "<group>"; };
+ 8CCBC6A3132CE7E9009528C3 /* NSString+IKEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+IKEncoding.h"; sourceTree = "<group>"; };
+ 8CCBC6A4132CE7E9009528C3 /* NSString+IKEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+IKEncoding.m"; sourceTree = "<group>"; };
+ 8CCBC6A9132CF2E0009528C3 /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Base64.h"; sourceTree = "<group>"; };
+ 8CCBC6AA132CF2E0009528C3 /* NSData+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Base64.m"; sourceTree = "<group>"; };
+ 8CCBC6D5132CF3DD009528C3 /* IKUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKUser.h; sourceTree = "<group>"; };
+ 8CCBC6D6132CF3DD009528C3 /* IKUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKUser.m; sourceTree = "<group>"; };
+ 8CCBC6D9132CF4F7009528C3 /* IKDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKDeserializer.h; sourceTree = "<group>"; };
+ 8CCBC6DA132CF4F7009528C3 /* IKDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKDeserializer.m; sourceTree = "<group>"; };
+ 8CCBC6DE132CFA84009528C3 /* IKBookmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKBookmark.h; sourceTree = "<group>"; };
+ 8CCBC6DF132CFA84009528C3 /* IKBookmark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKBookmark.m; sourceTree = "<group>"; };
+ 8CCBC6E2132CFA8D009528C3 /* IKFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKFolder.h; sourceTree = "<group>"; };
+ 8CCBC6E3132CFA8D009528C3 /* IKFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKFolder.m; sourceTree = "<group>"; };
+ 8CCBC6EC132CFE00009528C3 /* IKConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IKConstants.h; sourceTree = "<group>"; };
+ 8CCBC6ED132CFE01009528C3 /* IKConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IKConstants.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8CBA8F63132C26790086AA8F /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CBA8F6B132C26790086AA8F /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8CCBC679132CC419009528C3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CCBC697132CC4D6009528C3 /* InstapaperKit.framework in Frameworks */,
+ 8CCBC67E132CC419009528C3 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 8C209217133F8E1500561607 /* JSONKit */ = {
+ isa = PBXGroup;
+ children = (
+ 8C20923E133F8E1500561607 /* JSONKit.h */,
+ 8C20923F133F8E1500561607 /* JSONKit.m */,
+ );
+ path = JSONKit;
+ sourceTree = "<group>";
+ };
+ 8CBA8F5B132C26790086AA8F = {
+ isa = PBXGroup;
+ children = (
+ 8CBA8F70132C26790086AA8F /* InstapaperKit */,
+ 8CCBC67F132CC419009528C3 /* Test */,
+ 8CBA8F69132C26790086AA8F /* Frameworks */,
+ 8CBA8F68132C26790086AA8F /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 8CBA8F68132C26790086AA8F /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC67C132CC419009528C3 /* Test.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 8CBA8F69132C26790086AA8F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8CBA8F67132C26790086AA8F /* InstapaperKit.framework */,
+ 8CBA8F6A132C26790086AA8F /* Cocoa.framework */,
+ 8CBA8F6C132C26790086AA8F /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 8CBA8F6C132C26790086AA8F /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 8CBA8F6D132C26790086AA8F /* AppKit.framework */,
+ 8CBA8F6E132C26790086AA8F /* CoreData.framework */,
+ 8CBA8F6F132C26790086AA8F /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 8CBA8F70132C26790086AA8F /* InstapaperKit */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC695132CC4AC009528C3 /* InstapaperKit.h */,
+ 8CCBC6EC132CFE00009528C3 /* IKConstants.h */,
+ 8CCBC6ED132CFE01009528C3 /* IKConstants.m */,
+ 8CBA8F7C132C26880086AA8F /* IKURLConnection.h */,
+ 8CBA8F7D132C26880086AA8F /* IKURLConnection.m */,
+ 8CBA8F7E132C26880086AA8F /* IKEngine.h */,
+ 8CBA8F7F132C26880086AA8F /* IKEngine.m */,
+ 8CCBC6DD132CFA67009528C3 /* Models */,
+ 8CCBC6D4132CF362009528C3 /* Private */,
+ 8CCBC6A7132CF2E0009528C3 /* Third Party */,
+ 8CBA8F71132C26790086AA8F /* Supporting Files */,
+ );
+ path = InstapaperKit;
+ sourceTree = "<group>";
+ };
+ 8CBA8F71132C26790086AA8F /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 8CBA8F72132C26790086AA8F /* InstapaperKit-Info.plist */,
+ 8CBA8F73132C26790086AA8F /* InfoPlist.strings */,
+ 8CBA8F76132C26790086AA8F /* InstapaperKit-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 8CCBC67F132CC419009528C3 /* Test */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC68B132CC419009528C3 /* TestAppDelegate.h */,
+ 8CCBC68C132CC419009528C3 /* TestAppDelegate.m */,
+ 8CCBC68E132CC419009528C3 /* MainMenu.xib */,
+ 8CCBC680132CC419009528C3 /* Supporting Files */,
+ );
+ path = Test;
+ sourceTree = "<group>";
+ };
+ 8CCBC680132CC419009528C3 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC681132CC419009528C3 /* Test-Info.plist */,
+ 8CCBC682132CC419009528C3 /* InfoPlist.strings */,
+ 8CCBC685132CC419009528C3 /* Test-Prefix.pch */,
+ 8CCBC686132CC419009528C3 /* main.m */,
+ 8CCBC688132CC419009528C3 /* Credits.rtf */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 8CCBC6A7132CF2E0009528C3 /* Third Party */ = {
+ isa = PBXGroup;
+ children = (
+ 8C209217133F8E1500561607 /* JSONKit */,
+ 8CCBC6A8132CF2E0009528C3 /* Base64 */,
+ );
+ path = "Third Party";
+ sourceTree = "<group>";
+ };
+ 8CCBC6A8132CF2E0009528C3 /* Base64 */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC6A9132CF2E0009528C3 /* NSData+Base64.h */,
+ 8CCBC6AA132CF2E0009528C3 /* NSData+Base64.m */,
+ );
+ path = Base64;
+ sourceTree = "<group>";
+ };
+ 8CCBC6D4132CF362009528C3 /* Private */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC6A3132CE7E9009528C3 /* NSString+IKEncoding.h */,
+ 8CCBC6A4132CE7E9009528C3 /* NSString+IKEncoding.m */,
+ 8CCBC6D9132CF4F7009528C3 /* IKDeserializer.h */,
+ 8CCBC6DA132CF4F7009528C3 /* IKDeserializer.m */,
+ 8C880B1B1333BC1400834805 /* IKURLConnection+Private.h */,
+ );
+ name = Private;
+ sourceTree = "<group>";
+ };
+ 8CCBC6DD132CFA67009528C3 /* Models */ = {
+ isa = PBXGroup;
+ children = (
+ 8CCBC6D5132CF3DD009528C3 /* IKUser.h */,
+ 8CCBC6D6132CF3DD009528C3 /* IKUser.m */,
+ 8CCBC6DE132CFA84009528C3 /* IKBookmark.h */,
+ 8CCBC6DF132CFA84009528C3 /* IKBookmark.m */,
+ 8CCBC6E2132CFA8D009528C3 /* IKFolder.h */,
+ 8CCBC6E3132CFA8D009528C3 /* IKFolder.m */,
+ );
+ name = Models;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 8CBA8F64132C26790086AA8F /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CBA8F80132C26880086AA8F /* IKURLConnection.h in Headers */,
+ 8CBA8F82132C26880086AA8F /* IKEngine.h in Headers */,
+ 8CCBC696132CC4AC009528C3 /* InstapaperKit.h in Headers */,
+ 8CCBC6A5132CE7EA009528C3 /* NSString+IKEncoding.h in Headers */,
+ 8CCBC6AB132CF2E0009528C3 /* NSData+Base64.h in Headers */,
+ 8CCBC6D7132CF3DE009528C3 /* IKUser.h in Headers */,
+ 8CCBC6DB132CF4F7009528C3 /* IKDeserializer.h in Headers */,
+ 8CCBC6E0132CFA85009528C3 /* IKBookmark.h in Headers */,
+ 8CCBC6E4132CFA8D009528C3 /* IKFolder.h in Headers */,
+ 8CCBC6EE132CFE02009528C3 /* IKConstants.h in Headers */,
+ 8C880B1D1333BC1600834805 /* IKURLConnection+Private.h in Headers */,
+ 8C209258133F8E1500561607 /* JSONKit.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 8CBA8F66132C26790086AA8F /* InstapaperKit */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8CBA8F79132C26790086AA8F /* Build configuration list for PBXNativeTarget "InstapaperKit" */;
+ buildPhases = (
+ 8CBA8F62132C26790086AA8F /* Sources */,
+ 8CBA8F63132C26790086AA8F /* Frameworks */,
+ 8CBA8F64132C26790086AA8F /* Headers */,
+ 8CBA8F65132C26790086AA8F /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = InstapaperKit;
+ productName = InstapaperKit;
+ productReference = 8CBA8F67132C26790086AA8F /* InstapaperKit.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 8CCBC67B132CC419009528C3 /* Test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 8CCBC693132CC419009528C3 /* Build configuration list for PBXNativeTarget "Test" */;
+ buildPhases = (
+ 8CCBC678132CC419009528C3 /* Sources */,
+ 8CCBC679132CC419009528C3 /* Frameworks */,
+ 8CCBC67A132CC419009528C3 /* Resources */,
+ 8CCBC698132CC51E009528C3 /* Copy Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Test;
+ productName = Test;
+ productReference = 8CCBC67C132CC419009528C3 /* Test.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 8CBA8F5D132C26790086AA8F /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 8CBA8F60132C26790086AA8F /* Build configuration list for PBXProject "InstapaperKit" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 8CBA8F5B132C26790086AA8F;
+ productRefGroup = 8CBA8F68132C26790086AA8F /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8CBA8F66132C26790086AA8F /* InstapaperKit */,
+ 8CCBC67B132CC419009528C3 /* Test */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8CBA8F65132C26790086AA8F /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CBA8F75132C26790086AA8F /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8CCBC67A132CC419009528C3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CCBC684132CC419009528C3 /* InfoPlist.strings in Resources */,
+ 8CCBC68A132CC419009528C3 /* Credits.rtf in Resources */,
+ 8CCBC690132CC419009528C3 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8CBA8F62132C26790086AA8F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CBA8F81132C26880086AA8F /* IKURLConnection.m in Sources */,
+ 8CBA8F83132C26880086AA8F /* IKEngine.m in Sources */,
+ 8CCBC6A6132CE7EA009528C3 /* NSString+IKEncoding.m in Sources */,
+ 8CCBC6AC132CF2E0009528C3 /* NSData+Base64.m in Sources */,
+ 8CCBC6D8132CF3DE009528C3 /* IKUser.m in Sources */,
+ 8CCBC6DC132CF4F7009528C3 /* IKDeserializer.m in Sources */,
+ 8CCBC6E1132CFA85009528C3 /* IKBookmark.m in Sources */,
+ 8CCBC6E5132CFA8D009528C3 /* IKFolder.m in Sources */,
+ 8CCBC6EF132CFE02009528C3 /* IKConstants.m in Sources */,
+ 8C209259133F8E1500561607 /* JSONKit.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 8CCBC678132CC419009528C3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8CCBC687132CC419009528C3 /* main.m in Sources */,
+ 8CCBC68D132CC419009528C3 /* TestAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 8CBA8F73132C26790086AA8F /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8CBA8F74132C26790086AA8F /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 8CCBC682132CC419009528C3 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8CCBC683132CC419009528C3 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 8CCBC688132CC419009528C3 /* Credits.rtf */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8CCBC689132CC419009528C3 /* en */,
+ );
+ name = Credits.rtf;
+ sourceTree = "<group>";
+ };
+ 8CCBC68E132CC419009528C3 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 8CCBC68F132CC419009528C3 /* en */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 8CBA8F77132C26790086AA8F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 8CBA8F78132C26790086AA8F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ 8CBA8F7A132C26790086AA8F /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "InstapaperKit/InstapaperKit-Prefix.pch";
+ INFOPLIST_FILE = "InstapaperKit/InstapaperKit-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+ 8CBA8F7B132C26790086AA8F /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "InstapaperKit/InstapaperKit-Prefix.pch";
+ INFOPLIST_FILE = "InstapaperKit/InstapaperKit-Info.plist";
+ INSTALL_PATH = "@loader_path/../Frameworks";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+ 8CCBC691132CC419009528C3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Test/Test-Prefix.pch";
+ INFOPLIST_FILE = "Test/Test-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 8CCBC692132CC419009528C3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "Test/Test-Prefix.pch";
+ INFOPLIST_FILE = "Test/Test-Info.plist";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 8CBA8F60132C26790086AA8F /* Build configuration list for PBXProject "InstapaperKit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8CBA8F77132C26790086AA8F /* Debug */,
+ 8CBA8F78132C26790086AA8F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8CBA8F79132C26790086AA8F /* Build configuration list for PBXNativeTarget "InstapaperKit" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8CBA8F7A132C26790086AA8F /* Debug */,
+ 8CBA8F7B132C26790086AA8F /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 8CCBC693132CC419009528C3 /* Build configuration list for PBXNativeTarget "Test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 8CCBC691132CC419009528C3 /* Debug */,
+ 8CCBC692132CC419009528C3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 8CBA8F5D132C26790086AA8F /* Project object */;
+}
7 InstapaperKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:InstapaperKit.xcodeproj">
+ </FileRef>
+</Workspace>
39 InstapaperKit/IKBookmark.h
@@ -0,0 +1,39 @@
+//
+// IKBookmark.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface IKBookmark : NSObject {
+ NSInteger _bookmarkID;
+ NSURL *_URL;
+ NSString *_title;
+ NSString *_descr;
+ NSDate *_date;
+ BOOL _starred;
+ NSString *_privateSource;
+ NSString *_hashString;
+ CGFloat _progress;
+ NSDate *_progressDate;
+}
+
+@property (nonatomic, assign) NSInteger bookmarkID;
+@property (nonatomic, retain) NSURL *URL;
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, copy) NSString *descr;
+@property (nonatomic, retain) NSDate *date;
+@property (nonatomic, assign, getter=isStarred) BOOL starred;
+@property (nonatomic, copy) NSString *privateSource;
+@property (nonatomic, copy) NSString *hashString;
+@property (nonatomic, assign) CGFloat progress;
+@property (nonatomic, retain) NSDate *progressDate;
+
++ (IKBookmark *)bookmarkWithBookmarkID:(NSInteger)bookmarkID;
+- (id)initWithBookmarkID:(NSInteger)bookmarkID;
+
+@end
70 InstapaperKit/IKBookmark.m
@@ -0,0 +1,70 @@
+//
+// IKBookmark.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKBookmark.h"
+
+
+@implementation IKBookmark
+
+@synthesize bookmarkID = _bookmarkID, URL = _URL, title = _title, descr = _descr, date = _date,
+ starred = _starred, privateSource = _privateSource, hashString = _hashString,
+ progress = _progress, progressDate = _progressDate;
+
++ (IKBookmark *)bookmarkWithBookmarkID:(NSInteger)bookmarkID
+{
+ IKBookmark *bookmark = [[[IKBookmark alloc] initWithBookmarkID:bookmarkID] autorelease];
+ return bookmark;
+}
+
+- (id)init
+{
+ return [self initWithBookmarkID:NSNotFound];
+}
+
+- (id)initWithBookmarkID:(NSInteger)bookmarkID
+{
+ if ((self = [super init])) {
+ _bookmarkID = bookmarkID;
+ _URL = nil;
+ _title = nil;
+ _descr = nil;
+ _date = nil;
+ _starred = NO;
+ _privateSource = nil;
+ _hashString = nil;
+ _progress = -1.0f;
+ _progressDate = nil;
+ }
+ return self;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %@ (%d), URL:(%@)>", NSStringFromClass([self class]),
+ self.title,
+ self.bookmarkID,
+ self.URL];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc
+{
+ [_URL release];
+ [_title release];
+ [_descr release];
+ [_date release];
+ [_privateSource release];
+ [_hashString release];
+ [_progressDate release];
+
+ [super dealloc];
+}
+
+@end
44 InstapaperKit/IKConstants.h
@@ -0,0 +1,44 @@
+//
+// IKConstants.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+// Error domain
+extern NSString *const IKErrorDomain;
+
+// Error keys
+extern NSString *const IKErrorMessageKey;
+
+// Error keys
+enum {
+ // General errors
+ IKErrorCodeRateLimitExceeded = 1040,
+ IKErrorCodeSubscriptionAccountRequired = 1041,
+ IKErrorCodeApplicationSuspended = 1042,
+
+ // Bookmark errors
+ IKErrorCodeDomainRequiresContent = 1220,
+ IKErrorCodeDomainOptedOut = 1221,
+ IKErrorCodeDomainInvalidURL = 1240,
+ IKErrorCodeInvalidBookmarkID = 1241,
+ IKErrorCodeInvalidFolderID = 1242,
+ IKErrorCodeInvalidProgress = 1243,
+ IKErrorCodeInvalidProgressTimestamp = 1244,
+ IKErrorCodePrivateBookmarkRequiresContent = 1245,
+ IKErrorCodeUnexpectedBookmarkError = 1250,
+
+ // Folder errors
+ IKErrorCodeInvalidTitle = 1250,
+ IKErrorCodeFolderAlreadyExists = 1251,
+ IKErrorCodeCannotAddBookmark = 1252,
+
+ // Operational errors
+ IKErrorCodeUnexpectedServiceError = 1500,
+ IKErrorCodeErrorGeneratingTextOfURL = 1550
+};
13 InstapaperKit/IKConstants.m
@@ -0,0 +1,13 @@
+//
+// IKConstants.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKConstants.h"
+
+
+NSString *const IKErrorDomain = @"com.matthiasplappert.InstapaperKit";
+NSString *const IKErrorMessageKey = @"message";
17 InstapaperKit/IKDeserializer.h
@@ -0,0 +1,17 @@
+//
+// IKDeserializer.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface IKDeserializer : NSObject
+
++ (BOOL)token:(NSString **)token andTokenSecret:(NSString **)secret fromQlineString:(NSString *)qlineString;
++ (id)objectFromJSONString:(NSString *)JSONString;
+
+@end
168 InstapaperKit/IKDeserializer.m
@@ -0,0 +1,168 @@
+//
+// IKDeserializer.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKDeserializer.h"
+#import "IKUser.h"
+#import "IKBookmark.h"
+#import "IKFolder.h"
+#import "IKConstants.h"
+
+#import "JSONKit.h"
+
+
+@interface IKDeserializer ()
+
++ (id)_normalizedObjectForKey:(NSString *)key inDictionary:(NSDictionary *)dict;
++ (NSURL *)_URLForKey:(NSString *)key inDictionary:(NSDictionary *)dict;
++ (NSDate *)_dateForKey:(NSString *)key inDictionary:(NSDictionary *)dict;
+
+@end
+
+
+@implementation IKDeserializer
+
++ (BOOL)token:(NSString **)token andTokenSecret:(NSString **)secret fromQlineString:(NSString *)qlineString
+{
+ // Extract token & secret
+ NSArray *parts = [qlineString componentsSeparatedByString:@"&"];
+ for (NSString *part in parts) {
+ NSArray *subparts = [part componentsSeparatedByString:@"="];
+ if ([subparts count] != 2) {
+ // Invalid, skip
+ continue;
+ }
+
+ NSString *key = [subparts objectAtIndex:0];
+ NSString *value = [subparts objectAtIndex:1];
+ if ([key isEqualToString:@"oauth_token"]) {
+ *token = value;
+ } else if ([key isEqualToString:@"oauth_token_secret"]) {
+ *secret = value;
+ }
+ }
+
+ if (!*token || !*secret) {
+ *token = nil;
+ *secret = nil;
+ return NO;
+ }
+
+ return YES;
+}
+
++ (id)objectFromJSONString:(NSString *)JSONString
+{
+ if (!JSONString) {
+ return nil;
+ }
+
+ // Parse JSON
+ NSArray *JSONArray = [JSONString objectFromJSONString];
+ if (![JSONArray isKindOfClass:[NSArray class]]) {
+ // Invalid JSON
+ return nil;
+ }
+
+ NSMutableArray *result = [NSMutableArray array];
+
+ for (NSDictionary *dict in JSONArray) {
+ if (![dict isKindOfClass:[NSDictionary class]]) {
+ // Skip
+ continue;
+ }
+
+ // Get type
+ NSString *type = [dict objectForKey:@"type"];
+ if ([type isEqualToString:@"error"]) {
+ // Return error object, not array
+ NSInteger code = [[dict objectForKey:@"error_code"] integerValue];
+ NSString *message = [dict objectForKey:@"message"];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:message
+ forKey:IKErrorMessageKey];
+
+ return [NSError errorWithDomain:IKErrorDomain code:code userInfo:userInfo];
+
+ } else if ([type isEqualToString:@"user"]) {
+ // Create user object
+ IKUser *user = [[IKUser alloc] init];
+
+ user.userID = [[self _normalizedObjectForKey:@"user_id" inDictionary:dict] unsignedIntegerValue];
+ user.username = [self _normalizedObjectForKey:@"username" inDictionary:dict];
+ user.subscribed = [[self _normalizedObjectForKey:@"subscription_is_active" inDictionary:dict] boolValue];
+
+ [result addObject:user];
+
+ } else if ([type isEqualToString:@"bookmark"]) {
+ // Create bookmark object
+ IKBookmark *bookmark = [[IKBookmark alloc] init];
+
+ bookmark.bookmarkID = [[self _normalizedObjectForKey:@"bookmark_id" inDictionary:dict] integerValue];
+ bookmark.URL = [self _URLForKey:@"url" inDictionary:dict];
+ bookmark.title = [self _normalizedObjectForKey:@"title" inDictionary:dict];
+ bookmark.descr = [self _normalizedObjectForKey:@"descr" inDictionary:dict];
+ bookmark.date = [self _dateForKey:@"time" inDictionary:dict];
+ bookmark.starred = [[self _normalizedObjectForKey:@"starred" inDictionary:dict] boolValue];
+ bookmark.privateSource = [self _normalizedObjectForKey:@"private_source" inDictionary:dict];
+ bookmark.hashString = [self _normalizedObjectForKey:@"hash" inDictionary:dict];
+ bookmark.progress = [[self _normalizedObjectForKey:@"progress" inDictionary:dict] floatValue];
+ bookmark.progressDate = [self _dateForKey:@"progress_timestamp" inDictionary:dict];
+
+ [result addObject:bookmark];
+
+ } else if ([type isEqualToString:@"folder"]) {
+ // Create folder object
+ IKFolder *folder = [[IKFolder alloc] init];
+
+ folder.folderID = [[self _normalizedObjectForKey:@"folder_id" inDictionary:dict] integerValue];
+ folder.title = [self _normalizedObjectForKey:@"title" inDictionary:dict];
+ folder.syncToMobile = [[self _normalizedObjectForKey:@"sync_to_mobile" inDictionary:dict] boolValue];
+ folder.position = [[self _normalizedObjectForKey:@"position" inDictionary:dict] unsignedIntegerValue];
+
+ [result addObject:folder];
+
+ }
+ }
+
+ return result;
+}
+
+#pragma mark -
+#pragma mark Private methods
+
++ (id)_normalizedObjectForKey:(NSString *)key inDictionary:(NSDictionary *)dict
+{
+ id object = [dict objectForKey:key];
+ if ((NSNull *)object == [NSNull null]) {
+ return nil;
+ }
+
+ return object;
+}
+
++ (NSURL *)_URLForKey:(NSString *)key inDictionary:(NSDictionary *)dict
+{
+ NSString *URLString = [self _normalizedObjectForKey:key inDictionary:dict];
+ if (!URLString || ![URLString isKindOfClass:[NSString class]]) {
+ return nil;
+ }
+
+ return [NSURL URLWithString:URLString];
+}
+
++ (NSDate *)_dateForKey:(NSString *)key inDictionary:(NSDictionary *)dict
+{
+ id object = [self _normalizedObjectForKey:key inDictionary:dict];
+ if (!object || ![object respondsToSelector:@selector(unsignedIntegerValue)]) {
+ return nil;
+ }
+
+ NSUInteger unixTimestamp = [(NSNumber *)object unsignedIntegerValue];
+ return [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)unixTimestamp];
+}
+
+@end
90 InstapaperKit/IKEngine.h
@@ -0,0 +1,90 @@
+//
+// IKEngine.h
+// Instapaper
+//
+// Created by Matthias Plappert on 3/12/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <CommonCrypto/CommonHMAC.h>
+
+
+@class IKEngine, IKURLConnection, IKUser, IKFolder, IKBookmark;
+
+
+@protocol IKEngineDelegate <NSObject>
+
+@optional
+- (void)engine:(IKEngine *)engine willStartConnection:(IKURLConnection *)connection;
+- (void)engine:(IKEngine *)engine didFinishConnection:(IKURLConnection *)connection;
+- (void)engine:(IKEngine *)engine didFailConnection:(IKURLConnection *)connection error:(NSError *)error;
+- (void)engine:(IKEngine *)engine didCancelConnection:(IKURLConnection *)connection;
+
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didReceiveAuthToken:(NSString *)token andTokenSecret:(NSString *)secret;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didVerifyCredentialsForUser:(IKUser *)user;
+
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didReceiveBookmarks:(NSArray *)bookmarks ofUser:(IKUser *)user;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didUpdateReadProgressOfBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didAddBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didDeleteBookmarkWithBookmarkID:(NSInteger)bookmarkID;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didStarBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didUnstarBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didArchiveBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didUnarchiveBookmark:(IKBookmark *)bookmark;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didMoveBookmark:(IKBookmark *)bookmark toFolderWithFolderID:(NSInteger)folderID;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didReceiveText:(NSString *)text ofBookmarkWithBookmarkID:(NSInteger)bookmarkID;
+
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didReceiveFolders:(NSArray *)folders;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didAddFolder:(IKFolder *)folder;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didDeleteFolderWithFolderID:(NSInteger)folderID;
+- (void)engine:(IKEngine *)engine connection:(IKURLConnection *)connection didOrderFolders:(NSArray *)folders;
+
+@end
+
+
+@interface IKEngine : NSObject {
+ id <IKEngineDelegate> _delegate;
+ NSString *_OAuthToken;
+ NSString *_OAuthTokenSecret;
+
+ NSMutableDictionary *_connections;
+}
+
+@property (nonatomic, assign) id <IKEngineDelegate> delegate;
+@property (nonatomic, copy) NSString *OAuthToken;
+@property (nonatomic, copy) NSString *OAuthTokenSecret;
+
++ (void)setOAuthConsumerKey:(NSString *)key andConsumerSecret:(NSString *)secret;
+
+- (id)initWithDelegate:(id <IKEngineDelegate>)delegate;
+
+- (NSString *)authTokenForUsername:(NSString *)username password:(NSString *)password userInfo:(id)userInfo;
+- (NSString *)verifyCredentialsWithUserInfo:(id)userInfo;
+
+- (NSString *)bookmarksWithUserInfo:(id)userInfo;
+- (NSString *)bookmarksInFolder:(IKFolder *)folder limit:(NSUInteger)limit existingBookmarks:(NSArray *)bookmarks userInfo:(id)userInfo;
+- (NSString *)updateReadProgressOfBookmark:(IKBookmark *)bookmark toProgress:(CGFloat)progress userInfo:(id)userInfo;
+- (NSString *)addBookmarkWithURL:(NSURL *)URL userInfo:(id)userInfo;
+- (NSString *)addBookmarkWithURL:(NSURL *)URL title:(NSString *)title description:(NSString *)description folder:(IKFolder *)folder resolveFinalURL:(BOOL)resolveFinalURL userInfo:(id)userInfo;
+- (NSString *)deleteBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+- (NSString *)starBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+- (NSString *)unstarBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+- (NSString *)archiveBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+- (NSString *)unarchiveBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+- (NSString *)moveBookmark:(IKBookmark *)bookmark toFolder:(IKFolder *)folder userInfo:(id)userInfo;
+- (NSString *)textOfBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo;
+
+- (NSString *)foldersWithUserInfo:(id)userInfo;
+- (NSString *)addFolderWithTitle:(NSString *)title userInfo:(id)userInfo;
+- (NSString *)deleteFolder:(IKFolder *)folder userInfo:(id)userInfo;
+- (NSString *)orderFolders:(NSArray *)folders userInfo:(id)userInfo;
+
+- (IKURLConnection *)connectionForIdentifier:(NSString *)identifier;
+- (NSString *)identifierForConnection:(IKURLConnection *)connection;
+- (NSUInteger)numberOfConnections;
+
+- (void)cancelConnection:(IKURLConnection *)connection;
+- (void)cancelAllConnections;
+
+@end
861 InstapaperKit/IKEngine.m
@@ -0,0 +1,861 @@
+//
+// IKEngine.m
+// Instapaper
+//
+// Created by Matthias Plappert on 3/12/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKEngine.h"
+#import "IKURLConnection.h"
+#import "IKDeserializer.h"
+#import "NSString+IKEncoding.h"
+#import "IKUser.h"
+#import "IKFolder.h"
+#import "IKBookmark.h"
+#import "IKConstants.h"
+#import "IKURLConnection+Private.h"
+
+#import "NSData+Base64.h"
+
+
+@interface IKEngine ()
+
+- (NSString *)_startConnectionWithAPIPath:(NSString *)path bodyArguments:(NSDictionary *)arguments type:(IKURLConnectionType)type userInfo:(id)userInfo context:(id)context;
+- (NSString *)_signatureWithKey:(NSString *)key baseString:(NSString *)baseString;
+
+@end
+
+
+@implementation IKEngine
+
+static NSString *_OAuthConsumerKey = nil;
+static NSString *_OAuthConsumerSecret = nil;
+
+@synthesize delegate = _delegate, OAuthToken = _OAuthToken, OAuthTokenSecret = _OAuthTokenSecret;
+
++ (void)setOAuthConsumerKey:(NSString *)key andConsumerSecret:(NSString *)secret
+{
+ [_OAuthConsumerKey release];
+ _OAuthConsumerKey = [key copy];
+
+ [_OAuthConsumerSecret release];
+ _OAuthConsumerSecret = [secret copy];
+}
+
+- (id)initWithDelegate:(id <IKEngineDelegate>)delegate
+{
+ if ((self = [super init])) {
+ // Initial values
+ _delegate = delegate;
+ _OAuthToken = nil;
+ _OAuthTokenSecret = nil;
+
+ _connections = [[NSMutableDictionary alloc] init];
+ }
+ return self;
+}
+
+- (id)init
+{
+ return [self initWithDelegate:nil];
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (IKURLConnection *)connectionForIdentifier:(NSString *)identifier
+{
+ return [_connections objectForKey:identifier];
+}
+
+- (NSString *)identifierForConnection:(IKURLConnection *)connection
+{
+ return [[_connections allKeysForObject:connection] lastObject];
+}
+
+- (NSUInteger)numberOfConnections
+{
+ return [_connections count];
+}
+
+#pragma mark -
+#pragma mark Actions
+
+- (NSString *)authTokenForUsername:(NSString *)username password:(NSString *)password userInfo:(id)userInfo
+{
+ NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:username, @"x_auth_username",
+ password, @"x_auth_password",
+ @"client_auth", @"x_auth_mode", nil];
+ return [self _startConnectionWithAPIPath:@"/api/1/oauth/access_token"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeAuthAccessToken
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)verifyCredentialsWithUserInfo:(id)userInfo
+{
+ return [self _startConnectionWithAPIPath:@"/api/1/account/verify_credentials"
+ bodyArguments:nil
+ type:IKURLConnectionTypeAuthVerifyCredentials
+ userInfo:userInfo
+ context:nil];
+ return nil;
+}
+
+#pragma mark -
+
+- (NSString *)bookmarksWithUserInfo:(id)userInfo
+{
+ return [self bookmarksInFolder:[IKFolder unreadFolder]
+ limit:25
+ existingBookmarks:nil
+ userInfo:userInfo];
+}
+
+- (NSString *)bookmarksInFolder:(IKFolder *)folder limit:(NSUInteger)limit existingBookmarks:(NSArray *)bookmarks userInfo:(id)userInfo
+{
+ NSNumber *limitObj = [NSNumber numberWithUnsignedInteger:limit];
+
+ NSString *folderID;
+ switch (folder.folderID) {
+ case IKUnreadFolderID:
+ folderID = @"unread";
+ break;
+ case IKStarredFolderID:
+ folderID = @"starred";
+ break;
+ case IKArchiveFolderID:
+ folderID = @"archive";
+ break;
+ default:
+ folderID = [NSString stringWithFormat:@"%d", folder.folderID];
+ break;
+ }
+
+ NSMutableString *have = [NSMutableString string];
+ if ([bookmarks count] > 0) {
+ for (IKBookmark *bookmark in bookmarks) {
+ [have appendFormat:@"%d", bookmark.bookmarkID];
+ if (bookmark.hash) {
+ [have appendFormat:@":%@", bookmark.hash];
+ }
+ if (bookmark.progressDate && bookmark.progress != -1.0f) {
+ int timestamp = (int)[bookmark.progressDate timeIntervalSince1970];
+ [have appendFormat:@":%f:%d", bookmark.progress, timestamp];
+ }
+ [have appendFormat:@","];
+ }
+
+ // Replace last ,
+ [have replaceCharactersInRange:NSMakeRange([have length] - 1, 1) withString:@""];
+ }
+
+ NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:limitObj, @"limit",
+ folderID, @"folder_id",
+ have, @"have", nil];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/list"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksList
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)updateReadProgressOfBookmark:(IKBookmark *)bookmark toProgress:(CGFloat)progress userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSNumber *progressObj = [NSNumber numberWithFloat:progress];
+ NSNumber *timestampObj = [NSNumber numberWithInteger:(NSInteger)[[NSDate date] timeIntervalSince1970]];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:bookmarkIDObj, @"bookmark_id",
+ progressObj, @"progress",
+ timestampObj, @"progress_timestamp", nil];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/update_read_progress"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksUpdateReadProgress
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)addBookmarkWithURL:(NSURL *)URL userInfo:(id)userInfo
+{
+ return [self addBookmarkWithURL:URL title:nil description:nil folder:nil resolveFinalURL:YES userInfo:userInfo];
+}
+
+- (NSString *)addBookmarkWithURL:(NSURL *)URL title:(NSString *)title description:(NSString *)description folder:(IKFolder *)folder resolveFinalURL:(BOOL)resolveFinalURL userInfo:(id)userInfo
+{
+ NSMutableDictionary *arguments = [NSMutableDictionary dictionary];
+ [arguments setObject:[URL absoluteString] forKey:@"url"];
+ if (title != nil) {
+ [arguments setObject:title forKey:@"title"];
+ }
+ if (description != nil) {
+ [arguments setObject:description forKey:@"description"];
+ }
+ if (folder != nil) {
+ [arguments setObject:[NSNumber numberWithInteger:folder.folderID] forKey:@"folder_id"];
+ }
+ [arguments setObject:[NSNumber numberWithBool:resolveFinalURL] forKey:@"resolve_final_url"];
+
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/add"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksAdd
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)deleteBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/delete"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksDelete
+ userInfo:userInfo
+ context:bookmarkIDObj];
+}
+
+- (NSString *)starBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/star"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksStar
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)unstarBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/unstar"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksUnstar
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)archiveBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/archive"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksArchive
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)unarchiveBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/unarchive"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksUnarchive
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)moveBookmark:(IKBookmark *)bookmark toFolder:(IKFolder *)folder userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSNumber *folderIDObj = [NSNumber numberWithInteger:folder.folderID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:bookmarkIDObj, @"bookmark_id",
+ folderIDObj, @"folder_id", nil];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/move"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksMove
+ userInfo:userInfo
+ context:folderIDObj];
+}
+
+- (NSString *)textOfBookmark:(IKBookmark *)bookmark userInfo:(id)userInfo
+{
+ NSNumber *bookmarkIDObj = [NSNumber numberWithInteger:bookmark.bookmarkID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:bookmarkIDObj
+ forKey:@"bookmark_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/bookmarks/get_text"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeBookmarksText
+ userInfo:userInfo
+ context:bookmarkIDObj];
+}
+
+#pragma mark -
+
+- (NSString *)foldersWithUserInfo:(id)userInfo
+{
+ return [self _startConnectionWithAPIPath:@"/api/1/folders/list"
+ bodyArguments:nil
+ type:IKURLConnectionTypeFoldersList
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)addFolderWithTitle:(NSString *)title userInfo:(id)userInfo
+{
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:title
+ forKey:@"title"];
+
+ return [self _startConnectionWithAPIPath:@"/api/1/folders/add"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeFoldersAdd
+ userInfo:userInfo
+ context:nil];
+}
+
+- (NSString *)deleteFolder:(IKFolder *)folder userInfo:(id)userInfo
+{
+ NSNumber *folderIDObj = [NSNumber numberWithInteger:folder.folderID];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:folderIDObj
+ forKey:@"folder_id"];
+ return [self _startConnectionWithAPIPath:@"/api/1/folders/delete"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeFoldersDelete
+ userInfo:userInfo
+ context:folderIDObj];
+}
+
+- (NSString *)orderFolders:(NSArray *)folders userInfo:(id)userInfo
+{
+ if ([folders count] == 0) {
+ return nil;
+ }
+
+ NSMutableString *orderString = [NSMutableString string];
+ for (NSUInteger i = 0; i < [folders count]; i++) {
+ IKFolder *folder = [folders objectAtIndex:i];
+ [orderString appendFormat:@"%d:%d,", folder.folderID, i];
+ }
+
+ // Replace last ,
+ [orderString replaceCharactersInRange:NSMakeRange([orderString length] - 1, 1) withString:@""];
+
+ NSDictionary *arguments = [NSDictionary dictionaryWithObject:orderString
+ forKey:@"order"];
+
+ return [self _startConnectionWithAPIPath:@"/api/1/folders/set_order"
+ bodyArguments:arguments
+ type:IKURLConnectionTypeFoldersOrder
+ userInfo:userInfo
+ context:nil];
+}
+
+#pragma mark -
+
+- (void)cancelConnection:(IKURLConnection *)connection
+{
+ NSString *identifier = [self identifierForConnection:connection];
+ if (identifier != nil) {
+ [connection cancel];
+
+ if ([self.delegate respondsToSelector:@selector(engine:didCancelConnection:)]) {
+ [self.delegate engine:self didCancelConnection:connection];
+ }
+
+ [_connections removeObjectForKey:identifier];
+ }
+}
+
+- (void)cancelAllConnections
+{
+ NSDictionary *connectionsCopy = [[NSDictionary alloc] initWithDictionary:_connections];
+ for (NSString *key in connectionsCopy) {
+ IKURLConnection *connection = [connectionsCopy objectForKey:key];
+ [self cancelConnection:connection];
+ }
+ [connectionsCopy release];
+}
+
+#pragma mark -
+#pragma mark NSURLConnectionDelegate
+
+- (void)connection:(IKURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
+{
+ [connection _setResponse:response];
+}
+
+- (void)connection:(IKURLConnection *)connection didReceiveData:(NSData *)data
+{
+ [connection _appendData:data];
+}
+
+- (void)connection:(IKURLConnection *)connection didFailWithError:(NSError *)error
+{
+ if ([self.delegate respondsToSelector:@selector(engine:didFailConnection:error:)]) {
+ [self.delegate engine:self didFailConnection:connection error:error];
+ }
+
+ NSString *identifier = [self identifierForConnection:connection];
+ [_connections removeObjectForKey:identifier];
+}
+
+- (void)connectionDidFinishLoading:(IKURLConnection *)connection
+{
+ NSString *responseString = [[[NSString alloc] initWithData:connection.data
+ encoding:NSUTF8StringEncoding] autorelease];
+
+ NSError *responseError = [NSError errorWithDomain:NSURLErrorDomain
+ code:NSURLErrorBadServerResponse
+ userInfo:nil];
+
+ // TODO: remove
+ NSLog(@"%d: %@", (int)connection.response.statusCode, responseString);
+
+ IKURLConnectionType type = [connection _type];
+ if (type == IKURLConnectionTypeAuthAccessToken) {
+ NSInteger statusCode = connection.response.statusCode;
+ if (statusCode != 200) {
+ // Invalid response
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:responseString
+ forKey:IKErrorMessageKey];
+ NSError *error = [NSError errorWithDomain:IKErrorDomain code:statusCode userInfo:userInfo];
+ [self connection:connection didFailWithError:error];
+ return;
+ }
+
+ // Extract token & secret
+ NSString *token = nil;
+ NSString *secret = nil;
+ if (![IKDeserializer token:&token andTokenSecret:&secret fromQlineString:responseString]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didReceiveAuthToken:andTokenSecret:)]) {
+ [self.delegate engine:self connection:connection didReceiveAuthToken:token andTokenSecret:secret];
+ }
+
+ // Assign
+ self.OAuthToken = token;
+ self.OAuthTokenSecret = secret;
+
+ } else if (type == IKURLConnectionTypeBookmarksText) {
+ if (connection.response.statusCode == 200) {
+ // Received text
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didReceiveText:ofBookmarkWithBookmarkID:)]) {
+ NSInteger bookmarkID = [[connection _context] integerValue];
+ [self.delegate engine:self connection:connection didReceiveText:responseString ofBookmarkWithBookmarkID:bookmarkID];
+ }
+
+ } else {
+ NSError *result = [IKDeserializer objectFromJSONString:responseString];
+ if (result != nil && [result isKindOfClass:[NSError class]]) {
+ // Error object
+ [self connection:connection didFailWithError:result];
+ } else {
+ // Invalid JSON
+ [self connection:connection didFailWithError:responseError];
+ }
+
+ return;
+ }
+
+ } else {
+ // Deserialize
+ NSArray *result = [IKDeserializer objectFromJSONString:responseString];
+ if (!result) {
+ // Invalid JSON
+ [self connection:connection didFailWithError:responseError];
+ return;
+ } else if ([result isKindOfClass:[NSError class]]) {
+ // Error object
+ [self connection:connection didFailWithError:(NSError *)result];
+ return;
+ }
+
+ switch (type) {
+ case IKURLConnectionTypeAuthVerifyCredentials: {
+ // Get user
+ IKUser *user = [result lastObject];
+ if (!user || ![user isKindOfClass:[IKUser class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didVerifyCredentialsForUser:)]) {
+ [self.delegate engine:self connection:connection didVerifyCredentialsForUser:user];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksList: {
+ // Get objects from array
+ IKUser *user = nil;
+ NSMutableArray *bookmarks = [NSMutableArray array];
+ for (id object in result) {
+ if ([object isKindOfClass:[IKUser class]]) {
+ user = (IKUser *)object;
+ } else if ([object isKindOfClass:[IKBookmark class]]) {
+ [bookmarks addObject:object];
+ }
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didReceiveBookmarks:ofUser:)]) {
+ [self.delegate engine:self connection:connection didReceiveBookmarks:bookmarks ofUser:user];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksUpdateReadProgress: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didUpdateReadProgressOfBookmark:)]) {
+ [self.delegate engine:self connection:connection didUpdateReadProgressOfBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksAdd: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didAddBookmark:)]) {
+ [self.delegate engine:self connection:connection didAddBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksDelete: {
+ // Get bookmark id
+ NSInteger bookmarkID = [[connection _context] integerValue];
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didDeleteBookmarkWithBookmarkID:)]) {
+ [self.delegate engine:self connection:connection didDeleteBookmarkWithBookmarkID:bookmarkID];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksStar: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didStarBookmark:)]) {
+ [self.delegate engine:self connection:connection didStarBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksUnstar: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didUnstarBookmark:)]) {
+ [self.delegate engine:self connection:connection didUnstarBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksArchive: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didArchiveBookmark:)]) {
+ [self.delegate engine:self connection:connection didArchiveBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksUnarchive: {
+ // Get bookmark
+ IKBookmark *bookmark = [result lastObject];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didUnarchiveBookmark:)]) {
+ [self.delegate engine:self connection:connection didUnarchiveBookmark:bookmark];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeBookmarksMove: {
+ // Get bookmark and folder ID
+ IKBookmark *bookmark = [result lastObject];
+ NSInteger folderID = [[connection _context] integerValue];
+ if (!bookmark || ![bookmark isKindOfClass:[IKBookmark class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didMoveBookmark:toFolderWithFolderID:)]) {
+ [self.delegate engine:self connection:connection didMoveBookmark:bookmark toFolderWithFolderID:folderID];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeFoldersList: {
+ NSMutableArray *folders = [NSMutableArray array];
+ for (id object in result) {
+ if ([object isKindOfClass:[IKFolder class]]) {
+ [folders addObject:object];
+ }
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didReceiveFolders:)]) {
+ [self.delegate engine:self connection:connection didReceiveFolders:folders];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeFoldersAdd: {
+ // Get folder
+ IKFolder *folder = [result lastObject];
+ if (!folder || ![folder isKindOfClass:[IKFolder class]]) {
+ // Invalid response
+ [self connection:connection didFailWithError:responseError];
+ return;
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didAddFolder:)]) {
+ [self.delegate engine:self connection:connection didAddFolder:folder];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeFoldersDelete: {
+ // Get folder id
+ NSInteger folderID = [[connection _context] integerValue];
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didDeleteFolderWithFolderID:)]) {
+ [self.delegate engine:self connection:connection didDeleteFolderWithFolderID:folderID];
+ }
+ break;
+ }
+
+ case IKURLConnectionTypeFoldersOrder: {
+ NSMutableArray *folders = [NSMutableArray array];
+ for (id object in result) {
+ if ([object isKindOfClass:[IKFolder class]]) {
+ [folders addObject:object];
+ }
+ }
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:connection:didOrderFolders:)]) {
+ [self.delegate engine:self connection:connection didOrderFolders:folders];
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if ([self.delegate respondsToSelector:@selector(engine:didFinishConnection:)]) {
+ [self.delegate engine:self didFinishConnection:connection];
+ }
+
+ NSString *identifier = [self identifierForConnection:connection];
+ [_connections removeObjectForKey:identifier];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc
+{
+ _delegate = nil;
+ [self cancelAllConnections];
+
+ [_OAuthToken release];
+ [_OAuthTokenSecret release];
+
+ [_connections release];
+
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Private methods
+
+- (NSString *)_startConnectionWithAPIPath:(NSString *)path bodyArguments:(NSDictionary *)arguments type:(IKURLConnectionType)type userInfo:(id)userInfo context:(id)context
+{
+ // Create full URL
+ static NSURL *baseURL = nil;
+ if (baseURL == nil) {
+ baseURL = [[NSURL alloc] initWithString:@"http://www.instapaper.com"];
+ }
+ NSURL *URL = [NSURL URLWithString:path relativeToURL:baseURL];
+ if (!URL) {
+ return nil;
+ }
+
+ // Encode body
+ NSMutableDictionary *encodedArguments = [NSMutableDictionary dictionary];
+ for (NSString *key in arguments) {
+ NSString *encodedKey = [key URLEncodedStringUsingEncoding:NSUTF8StringEncoding];
+ id argument = [arguments objectForKey:key];
+ if (![argument isKindOfClass:[NSString class]]) {
+ argument = [argument stringValue];
+ }
+ NSString *encodedValue = [argument URLEncodedStringUsingEncoding:NSUTF8StringEncoding];
+ [encodedArguments setObject:encodedValue forKey:encodedKey];
+ }
+
+ // Create body
+ NSMutableString *bodyString = [NSMutableString string];
+ if ([encodedArguments count] > 0) {
+ for (NSString *key in encodedArguments) {
+ NSString *value = [encodedArguments objectForKey:key];
+ [bodyString appendFormat:@"%@=%@&", key, value];
+ }
+
+ // Remove last &
+ [bodyString replaceCharactersInRange:NSMakeRange([bodyString length] - 1, 1) withString:@""];
+ }
+
+ // Request properties
+ NSString *HTTPMethod = @"POST";
+ NSString *encodedURL = [[URL absoluteString] URLEncodedStringUsingEncoding:NSUTF8StringEncoding];
+
+ // OAuth properties
+ NSString *OAuthNonce = [[NSProcessInfo processInfo] globallyUniqueString];
+ NSString *OAuthSignatureMethod = @"HMAC-SHA1";
+ NSString *OAuthTimestamp = [NSString stringWithFormat:@"%d", (int)[[NSDate date] timeIntervalSince1970]];
+ NSString *OAuthVersion = @"1.0";
+
+ // Create oauth arguments
+ NSMutableDictionary *OAuthArguments = [NSMutableDictionary dictionary];
+ [OAuthArguments setObject:OAuthNonce forKey:@"oauth_nonce"];
+ [OAuthArguments setObject:OAuthSignatureMethod forKey:@"oauth_signature_method"];
+ [OAuthArguments setObject:OAuthTimestamp forKey:@"oauth_timestamp"];
+ [OAuthArguments setObject:_OAuthConsumerKey forKey:@"oauth_consumer_key"];
+ [OAuthArguments setObject:OAuthVersion forKey:@"oauth_version"];
+ if (self.OAuthToken) {
+ [OAuthArguments setObject:self.OAuthToken forKey:@"oauth_token"];
+ }
+
+ // Merge oauth request arguments
+ NSMutableDictionary *mergedArguments = [NSMutableDictionary dictionaryWithDictionary:encodedArguments];
+ [mergedArguments addEntriesFromDictionary:OAuthArguments];
+
+ // Sort keys and generate signature base string
+ NSArray *sortedKeys = [[mergedArguments allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+ NSMutableString *signatureBaseString = [NSMutableString stringWithFormat:@"%@&%@&", HTTPMethod, encodedURL];
+ for (NSString *key in sortedKeys) {
+ NSString *encodedKey = [key URLEncodedStringUsingEncoding:NSUTF8StringEncoding];
+ NSString *encodedValue = [[mergedArguments objectForKey:key] URLEncodedStringUsingEncoding:NSUTF8StringEncoding];
+ [signatureBaseString appendFormat:@"%@%%3D%@%%26", encodedKey, encodedValue];
+ }
+
+ // Replace last %26
+ [signatureBaseString replaceCharactersInRange:NSMakeRange([signatureBaseString length] - 3, 3) withString:@""];
+
+ // Calculate signature
+ NSString *key = [NSString stringWithFormat:@"%@&%@", _OAuthConsumerSecret, (!_OAuthTokenSecret ? @"" : _OAuthTokenSecret)];
+ NSString *signature = [self _signatureWithKey:key baseString:signatureBaseString];
+ [OAuthArguments setObject:signature forKey:@"oauth_signature"];
+
+ // Create request
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
+ [request setHTTPMethod:@"POST"];
+ [request setHTTPBody:[bodyString dataUsingEncoding:NSUTF8StringEncoding]];
+
+ // Authorization header
+ NSMutableString *authHeader = [NSMutableString stringWithString:@"OAuth "];
+ for (NSString *key in OAuthArguments) {
+ NSString *value = [OAuthArguments objectForKey:key];
+ [authHeader appendFormat:@"%@=\"%@\", ", key, value];
+ }
+
+ // Replace last ", " and append to request
+ [authHeader replaceCharactersInRange:NSMakeRange([authHeader length] - 2, 2) withString:@""];
+ [request setValue:authHeader forHTTPHeaderField:@"Authorization"];
+
+ // Create connection
+ IKURLConnection *connection = [[IKURLConnection alloc] initWithRequest:request
+ delegate:self
+ startImmediately:NO];
+ [connection _setType:type];
+ [connection _setUserInfo:userInfo];
+ [connection _setContext:context];
+
+ // Add to dictionary
+ NSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString];
+ [_connections setObject:connection forKey:identifier];
+ [connection release];
+
+ // Inform delegate
+ if ([self.delegate respondsToSelector:@selector(engine:willStartConnection:)]) {
+ [self.delegate engine:self willStartConnection:connection];
+ }
+
+ // Run
+ [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+ [connection start];
+
+ return identifier;
+}
+
+- (NSString *)_signatureWithKey:(NSString *)key baseString:(NSString *)baseString
+{
+ const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
+ const char *cData = [baseString cStringUsingEncoding:NSASCIIStringEncoding];
+
+ unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
+
+ CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
+
+ NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC
+ length:sizeof(cHMAC)];
+ NSString *hash = [HMAC base64EncodedString];
+ [HMAC release];
+
+ return hash;
+}
+
+@end
37 InstapaperKit/IKFolder.h
@@ -0,0 +1,37 @@
+//
+// IKFolder.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+enum {
+ IKUnreadFolderID = -1,
+ IKStarredFolderID = -2,
+ IKArchiveFolderID = -3
+};
+
+
+@interface IKFolder : NSObject {
+ NSInteger _folderID;
+ NSString *_title;
+ BOOL _syncToMobile;
+ NSUInteger _position;
+}
+
+@property (nonatomic, assign) NSInteger folderID;
+@property (nonatomic, copy) NSString *title;
+@property (nonatomic, assign) BOOL syncToMobile;
+@property (nonatomic, assign) NSUInteger position;
+
++ (IKFolder *)unreadFolder;
++ (IKFolder *)starredFolder;
++ (IKFolder *)archiveFolder;
++ (IKFolder *)folderWithFolderID:(NSInteger)folderID;
+- (id)initWithFolderID:(NSInteger)folderID;
+
+@end
74 InstapaperKit/IKFolder.m
@@ -0,0 +1,74 @@
+//
+// IKFolder.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKFolder.h"
+
+
+@implementation IKFolder
+
+@synthesize folderID = _folderID, title = _title, syncToMobile = _syncToMobile, position = _position;
+
+#pragma mark -
+#pragma mark Factory methods
+
++ (IKFolder *)unreadFolder
+{
+ return [self folderWithFolderID:IKUnreadFolderID];
+}
+
++ (IKFolder *)starredFolder
+{
+ return [self folderWithFolderID:IKStarredFolderID];
+}
+
++ (IKFolder *)archiveFolder
+{
+ return [self folderWithFolderID:IKArchiveFolderID];
+}
+
++ (IKFolder *)folderWithFolderID:(NSInteger)folderID
+{
+ IKFolder *folder = [[[IKFolder alloc] initWithFolderID:folderID] autorelease];
+ return folder;
+}
+
+#pragma mark -
+#pragma mark Init
+
+- (id)init
+{
+ return [self initWithFolderID:NSNotFound];
+}
+
+- (id)initWithFolderID:(NSInteger)folderID
+{
+ if ((self = [super init])) {
+ _folderID = folderID;
+ _title = nil;
+ _syncToMobile = NO;
+ _position = 0;
+ }
+ return self;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %@ (%d)>", NSStringFromClass([self class]),
+ self.title, self.folderID];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc
+{
+ [_title release];
+ [super dealloc];
+}
+
+@end
48 InstapaperKit/IKURLConnection+Private.h
@@ -0,0 +1,48 @@
+//
+// IKURLConnection+Private.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/18/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "IKURLConnection.h"
+
+
+typedef enum {
+ IKURLConnectionTypeUnknown = -1,
+
+ IKURLConnectionTypeAuthAccessToken,
+ IKURLConnectionTypeAuthVerifyCredentials,
+
+ IKURLConnectionTypeBookmarksList,
+ IKURLConnectionTypeBookmarksUpdateReadProgress,
+ IKURLConnectionTypeBookmarksAdd,
+ IKURLConnectionTypeBookmarksDelete,
+ IKURLConnectionTypeBookmarksStar,
+ IKURLConnectionTypeBookmarksUnstar,
+ IKURLConnectionTypeBookmarksArchive,
+ IKURLConnectionTypeBookmarksUnarchive,
+ IKURLConnectionTypeBookmarksMove,
+ IKURLConnectionTypeBookmarksText,
+
+ IKURLConnectionTypeFoldersList,
+ IKURLConnectionTypeFoldersAdd,
+ IKURLConnectionTypeFoldersDelete,
+ IKURLConnectionTypeFoldersOrder
+} IKURLConnectionType;
+
+
+@interface IKURLConnection (Private)
+
+- (void)_appendData:(NSData *)data;
+- (void)_setUserInfo:(id)userInfo;
+- (void)_setResponse:(NSHTTPURLResponse *)response;
+
+- (void)_setType:(IKURLConnectionType)type;
+- (IKURLConnectionType)_type;
+- (void)_setContext:(id)context;
+- (id)_context;
+
+@end
27 InstapaperKit/IKURLConnection.h
@@ -0,0 +1,27 @@
+//
+// IKURLConnection.h
+// Instapaper
+//
+// Created by Matthias Plappert on 3/12/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface IKURLConnection : NSURLConnection {
+ NSURLRequest *_request;
+ NSHTTPURLResponse *_response;
+ NSMutableData *_data;
+ id _userInfo;
+
+ NSInteger _type;
+ id _context;
+}
+
+@property (nonatomic, readonly) NSURLRequest *request;
+@property (nonatomic, readonly) NSHTTPURLResponse *response;
+@property (nonatomic, readonly) NSData *data;
+@property (nonatomic, readonly) id userInfo;
+
+@end
104 InstapaperKit/IKURLConnection.m
@@ -0,0 +1,104 @@
+//
+// IKURLConnection.m
+// Instapaper
+//
+// Created by Matthias Plappert on 3/12/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKURLConnection.h"
+#import "IKURLConnection+Private.h"
+
+
+@implementation IKURLConnection
+
+@synthesize request = _request, response = _response, data = _data, userInfo = _userInfo;
+
+- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately
+{
+ if ((self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately])) {
+ // Initial values
+ _request = [request retain];
+ _response = nil;
+ _data = [[NSMutableData alloc] init];
+ _userInfo = nil;
+
+ _type = IKURLConnectionTypeUnknown;
+ _context = nil;
+ }
+ return self;
+}
+
+#pragma mark -
+#pragma mark Accessors
+
+- (NSData *)data
+{
+ return [NSData dataWithData:_data];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc
+{
+ [_request release];
+ [_response release];
+ [_data release];
+ [_userInfo release];
+
+ [_context release];
+
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Private accessors
+
+- (void)_appendData:(NSData *)data
+{
+ [_data appendData:data];
+}
+
+- (void)_setUserInfo:(id)userInfo
+{
+ if (_userInfo != userInfo) {
+ [_userInfo release];
+ _userInfo = [userInfo retain];
+ }
+}
+
+- (void)_setResponse:(NSHTTPURLResponse *)response
+{
+ if (_response != response) {
+ [_response release];
+ _response = [response retain];
+ }
+}
+
+- (void)_setType:(IKURLConnectionType)type
+{
+ if (_type != type) {
+ _type = type;
+ }
+}
+
+- (IKURLConnectionType)_type
+{
+ return _type;
+}
+
+- (void)_setContext:(id)context
+{
+ if (_context != context) {
+ [_context release];
+ _context = [context retain];
+ }
+}
+
+- (id)_context
+{
+ return _context;
+}
+
+@end
22 InstapaperKit/IKUser.h
@@ -0,0 +1,22 @@
+//
+// IKUser.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface IKUser : NSObject {
+ NSUInteger _userID;
+ NSString *_username;
+ BOOL _subscribed;
+}
+
+@property (nonatomic, assign) NSUInteger userID;
+@property (nonatomic, copy) NSString *username;
+@property (nonatomic, assign, getter=isSubscribed) BOOL subscribed;
+
+@end
43 InstapaperKit/IKUser.m
@@ -0,0 +1,43 @@
+//
+// IKUser.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKUser.h"
+
+
+@implementation IKUser
+
+@synthesize userID = _userID, username = _username, subscribed = _subscribed;
+
+- (id)init
+{
+ if ((self = [super init])) {
+ _userID = NSNotFound;
+ _username = nil;
+ _subscribed = NO;
+ }
+ return self;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %@ (%d), subscribed:%d>", NSStringFromClass([self class]),
+ self.username,
+ self.userID,
+ self.subscribed];
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc
+{
+ [_username release];
+ [super dealloc];
+}
+
+@end
30 InstapaperKit/InstapaperKit-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.matthiasplappert.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2011 __MyCompanyName__. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
7 InstapaperKit/InstapaperKit-Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'InstapaperKit' target in the 'InstapaperKit' project
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
15 InstapaperKit/InstapaperKit.h
@@ -0,0 +1,15 @@
+//
+// InstapaperKit.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "IKConstants.h"
+#import "IKURLConnection.h"
+#import "IKEngine.h"
+
+#import "IKUser.h"
+#import "IKBookmark.h"
+#import "IKFolder.h"
16 InstapaperKit/NSString+IKEncoding.h
@@ -0,0 +1,16 @@
+//
+// NSString+IKEncoding.h
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSString (IKEncoding)
+
+- (NSString *)URLEncodedStringUsingEncoding:(NSStringEncoding)stringEncoding;
+
+@end
22 InstapaperKit/NSString+IKEncoding.m
@@ -0,0 +1,22 @@
+//
+// NSString+IKEncoding.m
+// InstapaperKit
+//
+// Created by Matthias Plappert on 3/13/11.
+// Copyright 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "NSString+IKEncoding.h"
+
+
+@implementation NSString (IKEncoding)
+
+- (NSString *)URLEncodedStringUsingEncoding:(NSStringEncoding)stringEncoding
+{
+ CFStringEncoding encoding = CFStringConvertNSStringEncodingToEncoding(stringEncoding);
+ CFStringRef legalChars = (CFStringRef)@"!*'();:@&=+$,/?%#[]";
+ CFStringRef encodedString = CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)self, NULL, legalChars, encoding);
+ return [(NSString *)encodedString autorelease];
+}
+
+@end
33 InstapaperKit/Third Party/Base64/NSData+Base64.h
@@ -0,0 +1,33 @@
+//
+// NSData+Base64.h
+// base64
+//
+// Created by Matt Gallagher on 2009/06/03.
+// Copyright 2009 Matt Gallagher. All rights reserved.
+//
+// Permission is given to use this source code file, free of charge, in any
+// project, commercial or otherwise, entirely at your risk, with the condition
+// that any redistribution (in part or whole) of source code must retain
+// this copyright and permission notice. Attribution in compiled projects is
+// appreciated but not required.
+//
+
+#import <Foundation/Foundation.h>
+
+void *NewBase64Decode(
+ const char *inputBuffer,
+ size_t length,
+ size_t *outputLength);
+
+char *NewBase64Encode(
+ const void *inputBuffer,
+ size_t length,
+ bool separateLines,
+ size_t *outputLength);
+
+@interface NSData (Base64)
+
++ (NSData *)dataFromBase64String:(NSString *)aString;
+- (NSString *)base64EncodedString;
+
+@end
<