adium/adium

Parents a0742a8dbe96
Children f3e70aa36278
Upgrade Twitter to API 1.1 by switching from MGTwitterEngine to STTwitter.
  • +48 -83
    Adium.xcodeproj/project.pbxproj
  • +0 -1
    Frameworks/OAuthConsumer.framework/Headers
  • +0 -1
    Frameworks/OAuthConsumer.framework/OAuthConsumer
  • +0 -1
    Frameworks/OAuthConsumer.framework/Resources
  • +0 -35
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSMutableURLRequest+Parameters.h
  • +0 -34
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSString+URLEncoding.h
  • +0 -34
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSURL+Base.h
  • +0 -45
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAAsynchronousDataFetcher.h
  • +0 -40
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAConsumer.h
  • +0 -45
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OADataFetcher.h
  • +0 -32
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAHMAC_SHA1SignatureProvider.h
  • +0 -68
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAMutableURLRequest.h
  • +0 -31
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAPlaintextSignatureProvider.h
  • +0 -45
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OARequestParameter.h
  • +0 -43
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAServiceTicket.h
  • +0 -34
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OASignatureProviding.h
  • +0 -41
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAToken.h
  • +0 -19
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAToken_KeychainExtensions.h
  • +0 -39
    Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAuthConsumer.h
  • +0 -0
    Frameworks/OAuthConsumer.framework/Versions/A/OAuthConsumer
  • +0 -0
    Frameworks/OAuthConsumer.framework/Versions/A/Resources/English.lproj/InfoPlist.strings
  • +0 -38
    Frameworks/OAuthConsumer.framework/Versions/A/Resources/Info.plist
  • +0 -1
    Frameworks/OAuthConsumer.framework/Versions/Current
  • +8 -11
    Plugins/Twitter Plugin/AITwitterAccount.h
  • +590 -967
    Plugins/Twitter Plugin/AITwitterAccount.m
  • +0 -53
    Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.h
  • +0 -149
    Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.m
  • +3 -4
    Plugins/Twitter Plugin/AITwitterAccountViewController.h
  • +56 -92
    Plugins/Twitter Plugin/AITwitterAccountViewController.m
  • +0 -142
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.h
  • +0 -1612
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m
  • +0 -27
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngineDelegate.h
  • +0 -18
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngineGlobalHeader.h
  • +0 -37
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterHTTPURLConnection.h
  • +0 -114
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterHTTPURLConnection.m
  • +0 -17
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMessagesParser.h
  • +0 -56
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMessagesParser.m
  • +0 -17
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMiscParser.h
  • +0 -48
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMiscParser.m
  • +0 -23
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterParserDelegate.h
  • +0 -37
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterRequestTypes.h
  • +0 -17
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.h
  • +0 -75
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.m
  • +0 -17
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterUsersParser.h
  • +0 -112
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterUsersParser.m
  • +0 -36
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterXMLParser.h
  • +0 -129
    Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterXMLParser.m
  • +0 -36
    Plugins/Twitter Plugin/MGTwitterEngine/NSData+Base64.h
  • +0 -149
    Plugins/Twitter Plugin/MGTwitterEngine/NSData+Base64.m
  • +0 -15
    Plugins/Twitter Plugin/MGTwitterEngine/NSString+UUID.h
  • +0 -27
    Plugins/Twitter Plugin/MGTwitterEngine/NSString+UUID.m
  • +0 -104
    Plugins/Twitter Plugin/MGTwitterEngine/Source Code License.rtf
  • +15 -0
    Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.h
  • +35 -0
    Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.m
  • +420 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.h
  • +760 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.m
  • +25 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.h
  • +261 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.m
  • +70 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.h
  • +650 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.m
  • +45 -0
    Plugins/Twitter Plugin/STTwitter/STTwitterOAuthProtocol.h
  • +90 -0
    Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.h
  • +673 -0
    Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.m
  • --- a/Adium.xcodeproj/project.pbxproj Wed Mar 20 01:55:36 2013 +0100
    +++ b/Adium.xcodeproj/project.pbxproj Wed Mar 20 12:02:05 2013 -0400
    @@ -141,9 +141,6 @@
    1172FBD10CDAA8D400B8E233 /* libpurple.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1172FBC90CDAA8D400B8E233 /* libpurple.framework */; };
    117D6DC00BC5F0C40080D02B /* msg-request-attention.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 117D6DBF0BC5F0C40080D02B /* msg-request-attention.tiff */; };
    11819A1B10D0B95D003E8ECA /* AIMediaControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 11819A0910D0B90E003E8ECA /* AIMediaControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
    - 11879C0B0F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = 11879C0A0F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.m */; };
    - 11879DF80F6FFC0B00CACFB1 /* OAuthConsumer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11879DF70F6FFC0B00CACFB1 /* OAuthConsumer.framework */; };
    - 11879E0A0F6FFC1000CACFB1 /* OAuthConsumer.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 11879DF70F6FFC0B00CACFB1 /* OAuthConsumer.framework */; };
    1192E6D90FD3056F003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 1192E6B10FD30307003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.h */; };
    1192E6DA0FD30578003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 1192E6B20FD30307003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.m */; };
    1197F6710FCF8D180032F19B /* AITwitterStatusFollowup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1197F6700FCF8D180032F19B /* AITwitterStatusFollowup.m */; };
    @@ -187,15 +184,6 @@
    11F738F90F58D18700B3285B /* AITwitterService.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F738F80F58D18700B3285B /* AITwitterService.m */; };
    11F738FC0F58D19B00B3285B /* AITwitterPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F738FB0F58D19B00B3285B /* AITwitterPlugin.m */; };
    11F739020F58D1C400B3285B /* AITwitterAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739010F58D1C400B3285B /* AITwitterAccountViewController.m */; };
    - 11F7397A0F58D4DC00B3285B /* MGTwitterEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F7395B0F58D4DC00B3285B /* MGTwitterEngine.m */; };
    - 11F7397B0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F7395F0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.m */; };
    - 11F7397E0F58D4DD00B3285B /* MGTwitterMessagesParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739650F58D4DC00B3285B /* MGTwitterMessagesParser.m */; };
    - 11F739800F58D4DD00B3285B /* MGTwitterMiscParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739690F58D4DC00B3285B /* MGTwitterMiscParser.m */; };
    - 11F739820F58D4DD00B3285B /* MGTwitterStatusesParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F7396F0F58D4DC00B3285B /* MGTwitterStatusesParser.m */; };
    - 11F739840F58D4DD00B3285B /* MGTwitterUsersParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739730F58D4DC00B3285B /* MGTwitterUsersParser.m */; };
    - 11F739850F58D4DD00B3285B /* MGTwitterXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739750F58D4DC00B3285B /* MGTwitterXMLParser.m */; };
    - 11F739860F58D4DD00B3285B /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739770F58D4DC00B3285B /* NSData+Base64.m */; };
    - 11F739870F58D4DD00B3285B /* NSString+UUID.m in Sources */ = {isa = PBXBuildFile; fileRef = 11F739790F58D4DC00B3285B /* NSString+UUID.m */; };
    11FC23C20F768C1600C1C906 /* AIXMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 11FC23BF0F768C0900C1C906 /* AIXMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
    11FC23C30F768C2900C1C906 /* AIXMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 11FC23C00F768C0900C1C906 /* AIXMLElement.m */; };
    31034EFF0C8142680003F5AA /* TestStringAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 31034EFE0C8142680003F5AA /* TestStringAdditions.m */; };
    @@ -1034,11 +1022,16 @@
    4F1CB6400D640DA40073A1E6 /* get-info-advanced.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4F1CB63C0D640DA40073A1E6 /* get-info-advanced.tiff */; };
    4F1CB6410D640DA40073A1E6 /* get-info-events.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 4F1CB63D0D640DA40073A1E6 /* get-info-events.tiff */; };
    4F1CB64C0D640F4F0073A1E6 /* ContactInfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F1CB64B0D640F4F0073A1E6 /* ContactInfoInspector.xib */; };
    + 5A0D236A16F4C7BC005DF211 /* STTwitterAppOnly.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A0D236816F4C7BC005DF211 /* STTwitterAppOnly.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    5A1781860EC1215D00BA1E04 /* AIAutoScrollTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A1781850EC1215D00BA1E04 /* AIAutoScrollTextView.m */; };
    5A17D65D130F76B4002C852F /* AIGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A17D65C130F76B4002C852F /* AIGradientView.m */; };
    5A1E3A1214DCE60400724574 /* Preferences-Xtras.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5A1E3A1114DCE60400724574 /* Preferences-Xtras.xib */; };
    5A22D6E214834F44004E15F7 /* AIFacebookXMPPAccountView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5A22D6E014834F44004E15F7 /* AIFacebookXMPPAccountView.xib */; };
    5A27FA7E14A272330063489D /* pref-messagestyle.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A27FA7A14A272330063489D /* pref-messagestyle.png */; };
    + 5A3B4D7916D878AC00903E40 /* NSString+STTwitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A3B4D6C16D878AB00903E40 /* NSString+STTwitter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    + 5A3B4D7A16D878AC00903E40 /* STTwitterAPIWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A3B4D6E16D878AB00903E40 /* STTwitterAPIWrapper.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    + 5A3B4D7C16D878AC00903E40 /* STTwitterOAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A3B4D7216D878AB00903E40 /* STTwitterOAuth.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    + 5A3B4D7E16D878AC00903E40 /* STHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A3B4D7816D878AC00903E40 /* STHTTPRequest.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    5A44595E169143130078AB0A /* AIPreferenceCVPrototypeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A445957169118C60078AB0A /* AIPreferenceCVPrototypeView.m */; };
    5A4BD41D13F855B000A4D3F7 /* SearchTerms.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5A4BD41B13F855B000A4D3F7 /* SearchTerms.plist */; };
    5A4BD41E13F8568100A4D3F7 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5ACF27331392C585004B6AEF /* Preferences.xib */; };
    @@ -1679,7 +1672,6 @@
    116E369A10B72934002EDB0F /* Growl.framework in Copy Frameworks */,
    633404710F9C18EF003C77A9 /* AIUtilities.framework in Copy Frameworks */,
    639DF9D80F97E678003C9A32 /* AdiumLibpurple.framework in Copy Frameworks */,
    - 11879E0A0F6FFC1000CACFB1 /* OAuthConsumer.framework in Copy Frameworks */,
    11EE1CCF0CDD01120097F246 /* libglib.framework in Copy Frameworks */,
    11EE1CD00CDD01120097F246 /* libgmodule.framework in Copy Frameworks */,
    11EE1CD10CDD01120097F246 /* libgobject.framework in Copy Frameworks */,
    @@ -1915,9 +1907,6 @@
    11819A0210D0B8BE003E8ECA /* AIMediaController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIMediaController.h; path = Source/AIMediaController.h; sourceTree = "<group>"; };
    11819A0310D0B8BE003E8ECA /* AIMediaController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIMediaController.m; path = Source/AIMediaController.m; sourceTree = "<group>"; };
    11819A0910D0B90E003E8ECA /* AIMediaControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIMediaControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIMediaControllerProtocol.h"; sourceTree = "<group>"; };
    - 11879C090F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AITwitterAccountOAuthSetup.h; path = "Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.h"; sourceTree = "<group>"; };
    - 11879C0A0F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AITwitterAccountOAuthSetup.m; path = "Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.m"; sourceTree = "<group>"; };
    - 11879DF70F6FFC0B00CACFB1 /* OAuthConsumer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OAuthConsumer.framework; path = Frameworks/OAuthConsumer.framework; sourceTree = "<group>"; };
    118A444F0FEEA828008153C0 /* libjson-glib.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "libjson-glib.framework"; path = "Frameworks/libjson-glib.framework"; sourceTree = "<group>"; };
    1192E6B10FD30307003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIAnnoyingIRCMessagesHiderPlugin.h; path = "Plugins/Purple Service/AIAnnoyingIRCMessagesHiderPlugin.h"; sourceTree = "<group>"; };
    1192E6B20FD30307003CAEF5 /* AIAnnoyingIRCMessagesHiderPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIAnnoyingIRCMessagesHiderPlugin.m; path = "Plugins/Purple Service/AIAnnoyingIRCMessagesHiderPlugin.m"; sourceTree = "<group>"; };
    @@ -1994,28 +1983,6 @@
    11F738FB0F58D19B00B3285B /* AITwitterPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AITwitterPlugin.m; path = "Plugins/Twitter Plugin/AITwitterPlugin.m"; sourceTree = "<group>"; };
    11F739000F58D1C300B3285B /* AITwitterAccountViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AITwitterAccountViewController.h; path = "Plugins/Twitter Plugin/AITwitterAccountViewController.h"; sourceTree = "<group>"; };
    11F739010F58D1C400B3285B /* AITwitterAccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AITwitterAccountViewController.m; path = "Plugins/Twitter Plugin/AITwitterAccountViewController.m"; sourceTree = "<group>"; };
    - 11F7395A0F58D4DC00B3285B /* MGTwitterEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterEngine.h; sourceTree = "<group>"; };
    - 11F7395B0F58D4DC00B3285B /* MGTwitterEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterEngine.m; sourceTree = "<group>"; };
    - 11F7395C0F58D4DC00B3285B /* MGTwitterEngineDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterEngineDelegate.h; sourceTree = "<group>"; };
    - 11F7395D0F58D4DC00B3285B /* MGTwitterEngineGlobalHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterEngineGlobalHeader.h; sourceTree = "<group>"; };
    - 11F7395E0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterHTTPURLConnection.h; sourceTree = "<group>"; };
    - 11F7395F0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterHTTPURLConnection.m; sourceTree = "<group>"; };
    - 11F739640F58D4DC00B3285B /* MGTwitterMessagesParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterMessagesParser.h; sourceTree = "<group>"; };
    - 11F739650F58D4DC00B3285B /* MGTwitterMessagesParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterMessagesParser.m; sourceTree = "<group>"; };
    - 11F739680F58D4DC00B3285B /* MGTwitterMiscParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterMiscParser.h; sourceTree = "<group>"; };
    - 11F739690F58D4DC00B3285B /* MGTwitterMiscParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterMiscParser.m; sourceTree = "<group>"; };
    - 11F7396A0F58D4DC00B3285B /* MGTwitterParserDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterParserDelegate.h; sourceTree = "<group>"; };
    - 11F7396B0F58D4DC00B3285B /* MGTwitterRequestTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterRequestTypes.h; sourceTree = "<group>"; };
    - 11F7396E0F58D4DC00B3285B /* MGTwitterStatusesParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterStatusesParser.h; sourceTree = "<group>"; };
    - 11F7396F0F58D4DC00B3285B /* MGTwitterStatusesParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterStatusesParser.m; sourceTree = "<group>"; };
    - 11F739720F58D4DC00B3285B /* MGTwitterUsersParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterUsersParser.h; sourceTree = "<group>"; };
    - 11F739730F58D4DC00B3285B /* MGTwitterUsersParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterUsersParser.m; sourceTree = "<group>"; };
    - 11F739740F58D4DC00B3285B /* MGTwitterXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGTwitterXMLParser.h; sourceTree = "<group>"; };
    - 11F739750F58D4DC00B3285B /* MGTwitterXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGTwitterXMLParser.m; sourceTree = "<group>"; };
    - 11F739760F58D4DC00B3285B /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Base64.h"; sourceTree = "<group>"; };
    - 11F739770F58D4DC00B3285B /* NSData+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Base64.m"; sourceTree = "<group>"; };
    - 11F739780F58D4DC00B3285B /* NSString+UUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+UUID.h"; sourceTree = "<group>"; };
    - 11F739790F58D4DC00B3285B /* NSString+UUID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+UUID.m"; sourceTree = "<group>"; };
    11FC23BF0F768C0900C1C906 /* AIXMLElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIXMLElement.h; path = "Frameworks/Adium Framework/Source/AIXMLElement.h"; sourceTree = "<group>"; };
    11FC23C00F768C0900C1C906 /* AIXMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIXMLElement.m; path = "Frameworks/Adium Framework/Source/AIXMLElement.m"; sourceTree = "<group>"; };
    31034EFD0C8142680003F5AA /* TestStringAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestStringAdditions.h; path = UnitTests/TestStringAdditions.h; sourceTree = "<group>"; };
    @@ -3908,6 +3875,8 @@
    4F1CB63C0D640DA40073A1E6 /* get-info-advanced.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = "get-info-advanced.tiff"; path = "Resources/get-info-advanced.tiff"; sourceTree = "<group>"; };
    4F1CB63D0D640DA40073A1E6 /* get-info-events.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = "get-info-events.tiff"; path = "Resources/get-info-events.tiff"; sourceTree = "<group>"; };
    4F1CB64B0D640F4F0073A1E6 /* ContactInfoInspector.xib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xib; name = ContactInfoInspector.xib; path = Resources/ContactInfoInspector.xib; sourceTree = "<group>"; };
    + 5A0D236816F4C7BC005DF211 /* STTwitterAppOnly.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STTwitterAppOnly.m; path = "Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.m"; sourceTree = "<group>"; };
    + 5A0D236916F4C7BC005DF211 /* STTwitterAppOnly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STTwitterAppOnly.h; path = "Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.h"; sourceTree = "<group>"; };
    5A1781840EC1215D00BA1E04 /* AIAutoScrollTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIAutoScrollTextView.h; path = Source/AIAutoScrollTextView.h; sourceTree = "<group>"; };
    5A1781850EC1215D00BA1E04 /* AIAutoScrollTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIAutoScrollTextView.m; path = Source/AIAutoScrollTextView.m; sourceTree = "<group>"; };
    5A17D65B130F76B4002C852F /* AIGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIGradientView.h; path = Source/AIGradientView.h; sourceTree = "<group>"; };
    @@ -3918,6 +3887,15 @@
    5A1FEA601334549300C14951 /* MessageView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = MessageView.xib; path = "Plugins/Dual Window Interface/MessageView.xib"; sourceTree = "<group>"; };
    5A22D6E114834F44004E15F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xib; name = en; path = "Plugins/Purple Service/Resources/en.lproj/AIFacebookXMPPAccountView.xib"; sourceTree = "<group>"; };
    5A27FA7A14A272330063489D /* pref-messagestyle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "pref-messagestyle.png"; path = "Resources/pref-messagestyle.png"; sourceTree = "<group>"; };
    + 5A3B4D6B16D878AB00903E40 /* NSString+STTwitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+STTwitter.h"; path = "Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.h"; sourceTree = "<group>"; };
    + 5A3B4D6C16D878AB00903E40 /* NSString+STTwitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+STTwitter.m"; path = "Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.m"; sourceTree = "<group>"; };
    + 5A3B4D6D16D878AB00903E40 /* STTwitterAPIWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STTwitterAPIWrapper.h; path = "Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.h"; sourceTree = "<group>"; };
    + 5A3B4D6E16D878AB00903E40 /* STTwitterAPIWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STTwitterAPIWrapper.m; path = "Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.m"; sourceTree = "<group>"; };
    + 5A3B4D7116D878AB00903E40 /* STTwitterOAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STTwitterOAuth.h; path = "Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.h"; sourceTree = "<group>"; };
    + 5A3B4D7216D878AB00903E40 /* STTwitterOAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STTwitterOAuth.m; path = "Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.m"; sourceTree = "<group>"; };
    + 5A3B4D7516D878AB00903E40 /* STTwitterOAuthProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STTwitterOAuthProtocol.h; path = "Plugins/Twitter Plugin/STTwitter/STTwitterOAuthProtocol.h"; sourceTree = "<group>"; };
    + 5A3B4D7716D878AC00903E40 /* STHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = STHTTPRequest.h; path = "Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.h"; sourceTree = "<group>"; };
    + 5A3B4D7816D878AC00903E40 /* STHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = STHTTPRequest.m; path = "Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.m"; sourceTree = "<group>"; };
    5A445956169118C60078AB0A /* AIPreferenceCVPrototypeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIPreferenceCVPrototypeView.h; path = Source/AIPreferenceCVPrototypeView.h; sourceTree = "<group>"; };
    5A445957169118C60078AB0A /* AIPreferenceCVPrototypeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIPreferenceCVPrototypeView.m; path = Source/AIPreferenceCVPrototypeView.m; sourceTree = "<group>"; };
    5A4BD41C13F855B000A4D3F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = en; path = Resources/en.lproj/SearchTerms.plist; sourceTree = "<group>"; };
    @@ -4841,7 +4819,6 @@
    11AA1EFA0BCAE9C3003DDA66 /* Quartz.framework in Frameworks */,
    340C1ABF0BD58FAB00D09235 /* Security.framework in Frameworks */,
    31E0CD810C5EEF5200271DB1 /* CoreAudio.framework in Frameworks */,
    - 11879DF80F6FFC0B00CACFB1 /* OAuthConsumer.framework in Frameworks */,
    34C846AF101E515900140B4B /* QTKit.framework in Frameworks */,
    9719C92C1530EDF700217FBE /* FriBidi.framework in Frameworks */,
    9719C92E1530EE0C00217FBE /* ShortcutRecorder.framework in Frameworks */,
    @@ -5064,7 +5041,7 @@
    11F738F40F58D15500B3285B /* Twitter */ = {
    isa = PBXGroup;
    children = (
    - 11F739590F58D4DC00B3285B /* MGTwitterEngine */,
    + 5A3B4D6A16D878AB00903E40 /* STTwitter */,
    113891850F6B6B0300A7D7DC /* Laconica */,
    1197F66F0FCF8D180032F19B /* AITwitterStatusFollowup.h */,
    1197F6700FCF8D180032F19B /* AITwitterStatusFollowup.m */,
    @@ -5073,9 +5050,7 @@
    110966190F61D3E70064CA0E /* AITwitterReplyWindowController.m */,
    11F739000F58D1C300B3285B /* AITwitterAccountViewController.h */,
    11F739010F58D1C400B3285B /* AITwitterAccountViewController.m */,
    - 11879C090F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.h */,
    11D135D80FBE4C65000B0A5E /* AITwitterAccountView.xib */,
    - 11879C0A0F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.m */,
    11BD73D10F5A54BB007D438A /* twitter-small.png */,
    11BD73D20F5A54BB007D438A /* twitter.png */,
    EFB1C3120DDBDA3100B3973D /* AITwitterIMPlugin.h */,
    @@ -5096,36 +5071,6 @@
    name = Twitter;
    sourceTree = "<group>";
    };
    - 11F739590F58D4DC00B3285B /* MGTwitterEngine */ = {
    - isa = PBXGroup;
    - children = (
    - 11F7395A0F58D4DC00B3285B /* MGTwitterEngine.h */,
    - 11F7395B0F58D4DC00B3285B /* MGTwitterEngine.m */,
    - 11F7395C0F58D4DC00B3285B /* MGTwitterEngineDelegate.h */,
    - 11F7395D0F58D4DC00B3285B /* MGTwitterEngineGlobalHeader.h */,
    - 11F7395E0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.h */,
    - 11F7395F0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.m */,
    - 11F739640F58D4DC00B3285B /* MGTwitterMessagesParser.h */,
    - 11F739650F58D4DC00B3285B /* MGTwitterMessagesParser.m */,
    - 11F739680F58D4DC00B3285B /* MGTwitterMiscParser.h */,
    - 11F739690F58D4DC00B3285B /* MGTwitterMiscParser.m */,
    - 11F7396A0F58D4DC00B3285B /* MGTwitterParserDelegate.h */,
    - 11F7396B0F58D4DC00B3285B /* MGTwitterRequestTypes.h */,
    - 11F7396E0F58D4DC00B3285B /* MGTwitterStatusesParser.h */,
    - 11F7396F0F58D4DC00B3285B /* MGTwitterStatusesParser.m */,
    - 11F739720F58D4DC00B3285B /* MGTwitterUsersParser.h */,
    - 11F739730F58D4DC00B3285B /* MGTwitterUsersParser.m */,
    - 11F739740F58D4DC00B3285B /* MGTwitterXMLParser.h */,
    - 11F739750F58D4DC00B3285B /* MGTwitterXMLParser.m */,
    - 11F739760F58D4DC00B3285B /* NSData+Base64.h */,
    - 11F739770F58D4DC00B3285B /* NSData+Base64.m */,
    - 11F739780F58D4DC00B3285B /* NSString+UUID.h */,
    - 11F739790F58D4DC00B3285B /* NSString+UUID.m */,
    - );
    - name = MGTwitterEngine;
    - path = "Plugins/Twitter Plugin/MGTwitterEngine";
    - sourceTree = "<group>";
    - };
    19C28FACFE9D520D11CA2CBB /* Products */ = {
    isa = PBXGroup;
    children = (
    @@ -7371,6 +7316,32 @@
    name = Growl;
    sourceTree = "<group>";
    };
    + 5A3B4D6A16D878AB00903E40 /* STTwitter */ = {
    + isa = PBXGroup;
    + children = (
    + 5A0D236816F4C7BC005DF211 /* STTwitterAppOnly.m */,
    + 5A0D236916F4C7BC005DF211 /* STTwitterAppOnly.h */,
    + 5A3B4D6B16D878AB00903E40 /* NSString+STTwitter.h */,
    + 5A3B4D6C16D878AB00903E40 /* NSString+STTwitter.m */,
    + 5A3B4D6D16D878AB00903E40 /* STTwitterAPIWrapper.h */,
    + 5A3B4D6E16D878AB00903E40 /* STTwitterAPIWrapper.m */,
    + 5A3B4D7116D878AB00903E40 /* STTwitterOAuth.h */,
    + 5A3B4D7216D878AB00903E40 /* STTwitterOAuth.m */,
    + 5A3B4D7516D878AB00903E40 /* STTwitterOAuthProtocol.h */,
    + 5A3B4D7616D878AC00903E40 /* Vendor */,
    + );
    + name = STTwitter;
    + sourceTree = "<group>";
    + };
    + 5A3B4D7616D878AC00903E40 /* Vendor */ = {
    + isa = PBXGroup;
    + children = (
    + 5A3B4D7716D878AC00903E40 /* STHTTPRequest.h */,
    + 5A3B4D7816D878AC00903E40 /* STHTTPRequest.m */,
    + );
    + name = Vendor;
    + sourceTree = "<group>";
    + };
    5A8A6A46124456B1004965A8 /* Segmented control with menu popup */ = {
    isa = PBXGroup;
    children = (
    @@ -8307,7 +8278,6 @@
    3496A8E707CE6CA30055BBAB /* AutoHyperlinks.framework.xcodeproj */,
    9719C92B1530EDF700217FBE /* FriBidi.framework */,
    7E9A8CB2104DEBC400F210CC /* Growl.framework */,
    - 11879DF70F6FFC0B00CACFB1 /* OAuthConsumer.framework */,
    377EC8930AE9525B00CB7BDF /* PSMTabBarControl.framework */,
    9719C92D1530EE0C00217FBE /* ShortcutRecorder.framework */,
    9E1E1DFC0A96741500E16DFC /* LMX.framework */,
    @@ -10550,15 +10520,6 @@
    11F738F90F58D18700B3285B /* AITwitterService.m in Sources */,
    11F738FC0F58D19B00B3285B /* AITwitterPlugin.m in Sources */,
    11F739020F58D1C400B3285B /* AITwitterAccountViewController.m in Sources */,
    - 11F7397A0F58D4DC00B3285B /* MGTwitterEngine.m in Sources */,
    - 11F7397B0F58D4DC00B3285B /* MGTwitterHTTPURLConnection.m in Sources */,
    - 11F7397E0F58D4DD00B3285B /* MGTwitterMessagesParser.m in Sources */,
    - 11F739800F58D4DD00B3285B /* MGTwitterMiscParser.m in Sources */,
    - 11F739820F58D4DD00B3285B /* MGTwitterStatusesParser.m in Sources */,
    - 11F739840F58D4DD00B3285B /* MGTwitterUsersParser.m in Sources */,
    - 11F739850F58D4DD00B3285B /* MGTwitterXMLParser.m in Sources */,
    - 11F739860F58D4DD00B3285B /* NSData+Base64.m in Sources */,
    - 11F739870F58D4DD00B3285B /* NSString+UUID.m in Sources */,
    113F26A00F5CC03F00954772 /* AITwitterURLParser.m in Sources */,
    112523190F5F7F86003FC58A /* AITwitterURLHandler.m in Sources */,
    1109661A0F61D3E70064CA0E /* AITwitterReplyWindowController.m in Sources */,
    @@ -10567,7 +10528,6 @@
    1138918D0F6B6B3F00A7D7DC /* AILaconicaPlugin.m in Sources */,
    113891950F6B6B9C00A7D7DC /* AILaconicaAccountViewController.m in Sources */,
    1163F0EC0F6C7A8300F12F5D /* AIURLShortenerPlugin.m in Sources */,
    - 11879C0B0F6FF4C400CACFB1 /* AITwitterAccountOAuthSetup.m in Sources */,
    11700A350F7E8BE80078D6AB /* AISpecialPasswordPromptController.m in Sources */,
    112B490A0F82FB1700690E84 /* AIGroupChatStatusTooltipPlugin.m in Sources */,
    112B4A250F83194700690E84 /* AIMentionAdvancedPreferences.m in Sources */,
    @@ -10606,6 +10566,11 @@
    761D58831636EDE100210B12 /* AINewMessageTextFieldCell.m in Sources */,
    761D58861636F94300210B12 /* AINewMessageSearchField.m in Sources */,
    5A44595E169143130078AB0A /* AIPreferenceCVPrototypeView.m in Sources */,
    + 5A3B4D7916D878AC00903E40 /* NSString+STTwitter.m in Sources */,
    + 5A3B4D7A16D878AC00903E40 /* STTwitterAPIWrapper.m in Sources */,
    + 5A3B4D7C16D878AC00903E40 /* STTwitterOAuth.m in Sources */,
    + 5A3B4D7E16D878AC00903E40 /* STHTTPRequest.m in Sources */,
    + 5A0D236A16F4C7BC005DF211 /* STTwitterAppOnly.m in Sources */,
    );
    runOnlyForDeploymentPostprocessing = 0;
    };
    --- a/Frameworks/OAuthConsumer.framework/Headers Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -Versions/Current/Headers
    \ No newline at end of file
    --- a/Frameworks/OAuthConsumer.framework/OAuthConsumer Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -Versions/Current/OAuthConsumer
    \ No newline at end of file
    --- a/Frameworks/OAuthConsumer.framework/Resources Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -Versions/Current/Resources
    \ No newline at end of file
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSMutableURLRequest+Parameters.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,35 +0,0 @@
    -//
    -// NSMutableURLRequest+Parameters.h
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -#import <Foundation/Foundation.h>
    -#import "OARequestParameter.h"
    -#import "NSURL+Base.h"
    -
    -
    -@interface NSMutableURLRequest (OAParameterAdditions)
    -
    -- (NSArray *)parameters;
    -- (void)setParameters:(NSArray *)parameters;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSString+URLEncoding.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -//
    -// NSString+URLEncoding.h
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -
    -
    -@interface NSString (OAURLEncodingAdditions)
    -
    -- (NSString *)URLEncodedString;
    -- (NSString *)URLDecodedString;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/NSURL+Base.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -//
    -// NSURL+Base.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -
    -
    -@interface NSURL (OABaseAdditions)
    -
    -- (NSString *)URLStringWithoutQuery;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAAsynchronousDataFetcher.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,45 +0,0 @@
    -//
    -// OAAsynchronousDataFetcher.h
    -// OAuthConsumer
    -//
    -// Created by Zsombor Szabó on 12/3/08.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -#import <Foundation/Foundation.h>
    -
    -#import "OAMutableURLRequest.h"
    -
    -@interface OAAsynchronousDataFetcher : NSObject {
    - OAMutableURLRequest *request;
    - NSURLResponse *response;
    - NSURLConnection *connection;
    - NSMutableData *responseData;
    - id delegate;
    - SEL didFinishSelector;
    - SEL didFailSelector;
    -}
    -
    -+ (id)asynchronousFetcherWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector;
    -- (id)initWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector;
    -
    -- (void)start;
    -- (void)cancel;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAConsumer.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,40 +0,0 @@
    -//
    -// OAConsumer.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -
    -
    -@interface OAConsumer : NSObject {
    -@protected
    - NSString *key;
    - NSString *secret;
    -}
    -@property(retain) NSString *key;
    -@property(retain) NSString *secret;
    -
    -- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OADataFetcher.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,45 +0,0 @@
    -//
    -// OADataFetcher.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 11/5/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -#import <Foundation/Foundation.h>
    -#import "OAMutableURLRequest.h"
    -#import "OAServiceTicket.h"
    -
    -
    -@interface OADataFetcher : NSObject {
    -@private
    - OAMutableURLRequest *request;
    - NSURLResponse *response;
    - NSURLConnection *connection;
    - NSError *error;
    - NSData *responseData;
    - id delegate;
    - SEL didFinishSelector;
    - SEL didFailSelector;
    -}
    -
    -- (void)fetchDataWithRequest:(OAMutableURLRequest *)aRequest delegate:(id)aDelegate didFinishSelector:(SEL)finishSelector didFailSelector:(SEL)failSelector;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAHMAC_SHA1SignatureProvider.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,32 +0,0 @@
    -//
    -// OAHMAC_SHA1SignatureProvider.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -#import "OASignatureProviding.h"
    -
    -
    -@interface OAHMAC_SHA1SignatureProvider : NSObject <OASignatureProviding>
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAMutableURLRequest.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,68 +0,0 @@
    -//
    -// OAMutableURLRequest.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -#import "OAConsumer.h"
    -#import "OAToken.h"
    -#import "OAHMAC_SHA1SignatureProvider.h"
    -#import "OASignatureProviding.h"
    -#import "NSMutableURLRequest+Parameters.h"
    -#import "NSURL+Base.h"
    -
    -
    -@interface OAMutableURLRequest : NSMutableURLRequest {
    -@protected
    - OAConsumer *consumer;
    - OAToken *token;
    - NSString *realm;
    - NSString *signature;
    - id<OASignatureProviding> signatureProvider;
    - NSString *nonce;
    - NSString *timestamp;
    - NSMutableDictionary *extraOAuthParameters;
    -}
    -@property(readonly) NSString *signature;
    -@property(readonly) NSString *nonce;
    -
    -- (id)initWithURL:(NSURL *)aUrl
    - consumer:(OAConsumer *)aConsumer
    - token:(OAToken *)aToken
    - realm:(NSString *)aRealm
    -signatureProvider:(id<OASignatureProviding, NSObject>)aProvider;
    -
    -- (id)initWithURL:(NSURL *)aUrl
    - consumer:(OAConsumer *)aConsumer
    - token:(OAToken *)aToken
    - realm:(NSString *)aRealm
    -signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
    - nonce:(NSString *)aNonce
    - timestamp:(NSString *)aTimestamp;
    -
    -- (void)prepare;
    -
    -- (void)setOAuthParameterName:(NSString*)parameterName withValue:(NSString*)parameterValue;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAPlaintextSignatureProvider.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,31 +0,0 @@
    -//
    -// OAPlaintextSignatureProvider.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -#import "OASignatureProviding.h"
    -
    -@interface OAPlaintextSignatureProvider : NSObject <OASignatureProviding>
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OARequestParameter.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,45 +0,0 @@
    -//
    -// OARequestParameter.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -#import "NSString+URLEncoding.h"
    -
    -
    -@interface OARequestParameter : NSObject {
    -@protected
    - NSString *name;
    - NSString *value;
    -}
    -@property(retain) NSString *name;
    -@property(retain) NSString *value;
    -
    -+ (id)requestParameterWithName:(NSString *)aName value:(NSString *)aValue;
    -- (id)initWithName:(NSString *)aName value:(NSString *)aValue;
    -- (NSString *)URLEncodedName;
    -- (NSString *)URLEncodedValue;
    -- (NSString *)URLEncodedNameValuePair;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAServiceTicket.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,43 +0,0 @@
    -//
    -// OAServiceTicket.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 11/5/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -#import "OAMutableURLRequest.h"
    -
    -
    -@interface OAServiceTicket : NSObject {
    -@private
    - OAMutableURLRequest *request;
    - NSURLResponse *response;
    - BOOL didSucceed;
    -}
    -@property(retain) OAMutableURLRequest *request;
    -@property(retain) NSURLResponse *response;
    -@property(assign) BOOL didSucceed;
    -
    -- (id)initWithRequest:(OAMutableURLRequest *)aRequest response:(NSURLResponse *)aResponse didSucceed:(BOOL)success;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OASignatureProviding.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,34 +0,0 @@
    -//
    -// OASignatureProviding.h
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -
    -#import <Foundation/Foundation.h>
    -
    -
    -@protocol OASignatureProviding <NSObject>
    -
    -- (NSString *)name;
    -- (NSString *)signClearText:(NSString *)text withSecret:(NSString *)secret;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAToken.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,41 +0,0 @@
    -//
    -// OAToken.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -#import <Foundation/Foundation.h>
    -
    -@interface OAToken : NSObject {
    -@protected
    - NSString *key;
    - NSString *secret;
    -}
    -@property(retain) NSString *key;
    -@property(retain) NSString *secret;
    -
    -- (id)initWithKey:(NSString *)aKey secret:(NSString *)aSecret;
    -- (id)initWithUserDefaultsUsingServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
    -- (id)initWithHTTPResponseBody:(NSString *)body;
    -- (int)storeInUserDefaultsWithServiceProviderName:(NSString *)provider prefix:(NSString *)prefix;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAToken_KeychainExtensions.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,19 +0,0 @@
    -//
    -// OAToken_KeychainExtensions.h
    -// TouchTheFireEagle
    -//
    -// Created by Jonathan Wight on 04/04/08.
    -// Copyright 2008 __MyCompanyName__. All rights reserved.
    -//
    -
    -#import "OAToken.h"
    -
    -#import <Security/Security.h>
    -
    -@interface OAToken (OAToken_KeychainExtensions)
    -
    -- (id)initWithKeychainUsingAppName:(NSString *)name serviceProviderName:(NSString *)provider;
    -- (int)storeInDefaultKeychainWithAppName:(NSString *)name serviceProviderName:(NSString *)provider;
    -- (int)storeInKeychain:(SecKeychainRef)keychain appName:(NSString *)name serviceProviderName:(NSString *)provider;
    -
    -@end
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Headers/OAuthConsumer.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,39 +0,0 @@
    -//
    -// OAuthConsumer.h
    -// OAuthConsumer
    -//
    -// Created by Jon Crosby on 10/19/07.
    -// Copyright 2007 Kaboomerang LLC. All rights reserved.
    -//
    -// Permission is hereby granted, free of charge, to any person obtaining a copy
    -// of this software and associated documentation files (the "Software"), to deal
    -// in the Software without restriction, including without limitation the rights
    -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -// copies of the Software, and to permit persons to whom the Software is
    -// furnished to do so, subject to the following conditions:
    -//
    -// The above copyright notice and this permission notice shall be included in
    -// all copies or substantial portions of the Software.
    -//
    -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -// THE SOFTWARE.
    -
    -#import <Foundation/Foundation.h>
    -#import <OAuthConsumer/OAToken.h>
    -#import <OAuthConsumer/OAConsumer.h>
    -#import <OAuthConsumer/OAMutableURLRequest.h>
    -#import <OAuthConsumer/NSString+URLEncoding.h>
    -#import <OAuthConsumer/NSMutableURLRequest+Parameters.h>
    -#import <OAuthConsumer/NSURL+Base.h>
    -#import <OAuthConsumer/OASignatureProviding.h>
    -#import <OAuthConsumer/OAHMAC_SHA1SignatureProvider.h>
    -#import <OAuthConsumer/OAPlaintextSignatureProvider.h>
    -#import <OAuthConsumer/OARequestParameter.h>
    -#import <OAuthConsumer/OAServiceTicket.h>
    -#import <OAuthConsumer/OADataFetcher.h>
    -#import <OAuthConsumer/OAAsynchronousDataFetcher.h>
    Binary file Frameworks/OAuthConsumer.framework/Versions/A/OAuthConsumer has changed
    Binary file Frameworks/OAuthConsumer.framework/Versions/A/Resources/English.lproj/InfoPlist.strings has changed
    --- a/Frameworks/OAuthConsumer.framework/Versions/A/Resources/Info.plist Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,38 +0,0 @@
    -<?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>BuildMachineOSBuild</key>
    - <string>11C74</string>
    - <key>CFBundleDevelopmentRegion</key>
    - <string>English</string>
    - <key>CFBundleExecutable</key>
    - <string>OAuthConsumer</string>
    - <key>CFBundleIdentifier</key>
    - <string>net.oauth.OAuthConsumer</string>
    - <key>CFBundleInfoDictionaryVersion</key>
    - <string>6.0</string>
    - <key>CFBundleName</key>
    - <string>OAuthConsumer</string>
    - <key>CFBundlePackageType</key>
    - <string>FMWK</string>
    - <key>CFBundleSignature</key>
    - <string>????</string>
    - <key>CFBundleVersion</key>
    - <string>0.1.1</string>
    - <key>DTCompiler</key>
    - <string>com.apple.compilers.llvm.clang.1_0</string>
    - <key>DTPlatformBuild</key>
    - <string>4E71d</string>
    - <key>DTPlatformVersion</key>
    - <string>GM</string>
    - <key>DTSDKBuild</key>
    - <string>10K549</string>
    - <key>DTSDKName</key>
    - <string>macosx10.6</string>
    - <key>DTXcode</key>
    - <string>0430</string>
    - <key>DTXcodeBuild</key>
    - <string>4E71d</string>
    -</dict>
    -</plist>
    --- a/Frameworks/OAuthConsumer.framework/Versions/Current Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1 +0,0 @@
    -A
    \ No newline at end of file
    --- a/Plugins/Twitter Plugin/AITwitterAccount.h Wed Mar 20 01:55:36 2013 +0100
    +++ b/Plugins/Twitter Plugin/AITwitterAccount.h Wed Mar 20 12:02:05 2013 -0400
    @@ -16,7 +16,7 @@
    #import <Adium/AIAccount.h>
    #import <Adium/AIGroupChat.h>
    -#import "MGTwitterEngine/MGTwitterEngine.h"
    +#import "STTwitterAPIWrapper.h"
    typedef enum {
    AITwitterUnknownType = 0,
    @@ -112,9 +112,9 @@
    #define AITwitterNotificationPostedStatus @"AITwitterNotificationPostedStatus"
    // Status Updates
    -#define TWITTER_STATUS_ID @"id"
    +#define TWITTER_STATUS_ID @"id_str"
    #define TWITTER_STATUS_REPLY_UID @"in_reply_to_screen_name"
    -#define TWITTER_STATUS_REPLY_ID @"in_reply_to_status_id"
    +#define TWITTER_STATUS_REPLY_ID @"in_reply_to_status_id_str"
    #define TWITTER_STATUS_CREATED @"created_at"
    #define TWITTER_STATUS_USER @"user"
    #define TWITTER_STATUS_UID @"screen_name"
    @@ -124,7 +124,6 @@
    // Direct Messages
    #define TWITTER_DM_ID @"id"
    #define TWITTER_DM_CREATED @"created_at"
    -#define TWITTER_DM_SENDER @"sender"
    #define TWITTER_DM_SENDER_UID @"sender_screen_name"
    #define TWITTER_DM_RECIPIENT_UID @"recipient_screen_name"
    #define TWITTER_DM_TEXT @"text"
    @@ -135,16 +134,14 @@
    #define TWITTER_INFO_DISPLAY_NAME @"name"
    #define TWITTER_INFO_UID @"screen_name"
    #define TWITTER_INFO_ICON @"profile_image_url"
    -#define TWITTER_INFO_PREVIOUS_CURSOR @"previous_cursor"
    -#define TWITTER_INFO_NEXT_CURSOR @"next_cursor"
    // Rate Limit
    -#define TWITTER_RATE_LIMIT_HOURLY_LIMIT @"hourly-limit"
    -#define TWITTER_RATE_LIMIT_REMAINING @"remaining-hits"
    -#define TWITTER_RATE_LIMIT_RESET_SECONDS @"reset-time-in-seconds"
    +#define TWITTER_RATE_LIMIT @"limit"
    +#define TWITTER_RATE_LIMIT_REMAINING @"remaining"
    +#define TWITTER_RATE_LIMIT_RESET_SECONDS @"reset"
    -@interface AITwitterAccount : AIAccount <MGTwitterEngineDelegate> {
    - MGTwitterEngine *twitterEngine;
    +@interface AITwitterAccount : AIAccount {
    + STTwitterAPIWrapper *twitterEngine;
    NSTimer *updateTimer;
    BOOL updateAfterSend;
    --- a/Plugins/Twitter Plugin/AITwitterAccount.m Wed Mar 20 01:55:36 2013 +0100
    +++ b/Plugins/Twitter Plugin/AITwitterAccount.m Wed Mar 20 12:02:05 2013 -0400
    @@ -36,6 +36,7 @@
    #import <Adium/AIHTMLDecoder.h>
    #import <Adium/AIContentEvent.h>
    #import <AIUtilities/AIApplicationAdditions.h>
    +#import "STTwitterOAuth.h"
    @interface AITwitterAccount()
    - (void)updateUserIcon:(NSString *)url forContact:(AIListContact *)listContact;
    @@ -54,11 +55,6 @@
    linkDestination:(NSString *)destination
    linkClass:(NSString *)attributeName;
    -- (void)setRequestType:(AITwitterRequestType)type forRequestID:(NSString *)requestID withDictionary:(NSDictionary *)info;
    -- (AITwitterRequestType)requestTypeForRequestID:(NSString *)requestID;
    -- (NSDictionary *)dictionaryForRequestID:(NSString *)requestID;
    -- (void)clearRequestTypeForRequestID:(NSString *)requestID;
    -
    - (void)periodicUpdate;
    - (void)displayQueuedUpdatesForRequestType:(AITwitterRequestType)requestType;
    @@ -142,58 +138,67 @@
    {
    [super connect];
    - twitterEngine = [[MGTwitterEngine alloc] initWithDelegate:self];
    -
    - [twitterEngine setClientName:@"Adium"
    - version:[NSApp applicationVersion]
    - URL:@"http://www.adium.im"
    - token:self.sourceToken];
    -
    - [twitterEngine setAPIDomain:[self.host stringByAppendingPathComponent:self.apiPath]];
    -
    - [twitterEngine setUsesSecureConnection:self.useSSL];
    -
    - if (self.useOAuth) {
    - if (!self.passwordWhileConnected.length) {
    - /* If we weren't able to retrieve the 'password', we can't proceed with oauth - we stored the oauth
    - * http response body in the keychain as the password.
    - *
    - * Note that this can happen not only if Adium isn't authorized but also if it *is* authorized but the
    - * keychain was inaccessible - e.g. keychain access wasn't allowed after an upgrade. Hm.
    - */
    - [self setLastDisconnectionError:TWITTER_OAUTH_NOT_AUTHORIZED];
    -
    - [[NSNotificationCenter defaultCenter] postNotificationName:@"AIEditAccount"
    - object:self];
    -
    - [self didDisconnect];
    -
    - // Don't try and connect.
    - return;
    -
    - } else {
    - twitterEngine.useOAuth = YES;
    -
    - OAToken *token = [[OAToken alloc] initWithHTTPResponseBody:self.passwordWhileConnected];
    - OAConsumer *consumer = [[OAConsumer alloc] initWithKey:self.consumerKey secret:self.secretKey];
    -
    - twitterEngine.accessToken = token;
    - twitterEngine.consumer = consumer;
    - }
    - } else {
    - [twitterEngine setUsername:self.UID password:self.passwordWhileConnected];
    + if (!self.passwordWhileConnected.length) {
    + /* If we weren't able to retrieve the 'password', we can't proceed with oauth - we stored the oauth
    + * http response body in the keychain as the password.
    + *
    + * Note that this can happen not only if Adium isn't authorized but also if it *is* authorized but the
    + * keychain was inaccessible - e.g. keychain access wasn't allowed after an upgrade. Hm.
    + */
    + [self setLastDisconnectionError:TWITTER_OAUTH_NOT_AUTHORIZED];
    +
    + [[NSNotificationCenter defaultCenter] postNotificationName:@"AIEditAccount"
    + object:self];
    +
    + [self didDisconnect];
    +
    + // Don't try and connect.
    + return;
    +
    }
    - AILogWithSignature(@"%@ connecting to %@", self, twitterEngine.APIDomain);
    + NSDictionary *oauth = [self.passwordWhileConnected parametersDictionary];
    +
    + NSString *oauthToken = [oauth objectForKey:@"oauth_token"];
    + NSString *oauthSecret = [oauth objectForKey:@"oauth_token_secret"];
    - NSString *requestID = [twitterEngine checkUserCredentials];
    + twitterEngine = [STTwitterAPIWrapper twitterAPIWithOAuthConsumerName:@"Adium"
    + consumerKey:self.consumerKey
    + consumerSecret:self.secretKey
    + oauthToken:oauthToken
    + oauthTokenSecret:oauthSecret];
    +
    + AILogWithSignature(@"%@ connecting to %@", self, twitterEngine.userName);
    - if (requestID) {
    - [self setRequestType:AITwitterValidateCredentials forRequestID:requestID withDictionary:nil];
    - } else {
    - [self setLastDisconnectionError:AILocalizedString(@"Unable to connect to server", nil)];
    - [self didDisconnect];
    - }
    + [twitterEngine verifyCredentialsWithSuccessBlock:^(id response) {
    + if ([response isKindOfClass:[NSDictionary class]])
    + [self userInfoReceived:(NSDictionary *)response forRequest:AITwitterValidateCredentials];
    +
    + if ([[self preferenceForKey:TWITTER_PREFERENCE_LOAD_CONTACTS group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue]) {
    + // If we load our follows as contacts, do so now.
    +
    + // Delay updates on initial login.
    + [self silenceAllContactUpdatesForInterval:18.0];
    + // Grab our user list.
    + [twitterEngine getFriendsForScreenName:self.UID
    + successBlock:^(NSArray *friends) {
    + [self userInfoReceived:@{ @"friends" : friends } forRequest:AITwitterInitialUserInfo];
    +
    + if ([self boolValueForProperty:@"isConnecting"]) {
    + // Trigger our normal update routine.
    + [self didConnect];
    + }
    + } errorBlock:^(NSError *error) {
    + [self setLastDisconnectionError:AILocalizedString(@"Unable to retrieve user list [fail]", "Message when a (vital) twitter request to retrieve the follow list fails")];
    + [self didDisconnect];
    + }];
    + } else {
    + // If we don't load follows as contacts, we've finished connecting (fast, wasn't it?)
    + [self didConnect];
    + }
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterValidateCredentials withError:error userInfo:nil];
    + }];
    }
    /*!
    @@ -333,7 +338,7 @@
    */
    - (BOOL)encrypted
    {
    - return (self.online && [twitterEngine usesSecureConnection]);
    + return self.online;
    }
    /*!
    @@ -464,13 +469,50 @@
    */
    - (void)setSocialNetworkingStatusMessage:(NSAttributedString *)inStatusMessage
    {
    - NSString *requestID = [twitterEngine sendUpdate:[inStatusMessage string]];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterSendUpdate
    - forRequestID:requestID
    - withDictionary:nil];
    - }
    + [self sendUpdate:inStatusMessage.string forChat:nil];
    + AILogWithSignature(@"%@ Sending social networking update %@", self, inStatusMessage);
    +}
    +
    +/*!
    + * @brief Send a tweet
    + */
    +- (void)sendUpdate:(NSString *)inStatusMessage forChat:(AIChat *)chat {
    + [twitterEngine postStatusUpdate:inStatusMessage
    + inReplyToStatusID:[chat valueForProperty:@"TweetInReplyToStatusID"]
    + placeID:nil
    + lat:nil
    + lon:nil
    + successBlock:^(NSDictionary *status) {
    + [adium.contentController displayEvent:AILocalizedString(@"Tweet successfully sent.", nil)
    + ofType:@"tweet"
    + inChat:self.timelineChat];
    +
    + [[NSNotificationCenter defaultCenter] postNotificationName:AITwitterNotificationPostedStatus
    + object:status
    + userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self.timelineChat, @"AIChat", nil]];
    +
    + NSDictionary *retweet = [status valueForKey:TWITTER_STATUS_RETWEET];
    + NSString *text = [[status objectForKey:TWITTER_STATUS_TEXT] stringByEscapingForXMLWithEntities:nil];
    +
    + if (retweet && [retweet isKindOfClass:[NSDictionary class]]) {
    + text = [[NSString stringWithFormat:@"RT @%@: %@",
    + [[retweet objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID],
    + [retweet objectForKey:TWITTER_STATUS_TEXT]] stringByEscapingForXMLWithEntities:nil];
    + }
    +
    + if ([[self preferenceForKey:TWITTER_PREFERENCE_UPDATE_GLOBAL group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue] &&
    + (![text hasPrefix:@"@"] || [[self preferenceForKey:TWITTER_PREFERENCE_UPDATE_GLOBAL_REPLIES group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue])) {
    + AIStatus *availableStatus = [AIStatus statusOfType:AIAvailableStatusType];
    +
    + availableStatus.statusMessage = [NSAttributedString stringWithString:text];
    + [adium.statusController setActiveStatusState:availableStatus];
    + }
    +
    + if (updateAfterSend)
    + [self performSelector:@selector(periodicUpdate) withObject:nil afterDelay:0.0];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterSendUpdate withError:error userInfo:@{ @"Chat" : chat }];
    + }];
    }
    - (NSString *)encodedAttributedString:(NSAttributedString *)inAttributedString forListObject:(AIListObject *)inListObject
    @@ -487,44 +529,27 @@
    */
    - (BOOL)sendMessageObject:(AIContentMessage *)inContentMessage
    {
    - NSString *requestID;
    -
    - if(inContentMessage.chat.isGroupChat) {
    - requestID = [twitterEngine sendUpdate:inContentMessage.encodedMessage
    - inReplyTo:[inContentMessage.chat valueForProperty:@"TweetInReplyToStatusID"]];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterSendUpdate
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:inContentMessage.chat
    - forKey:@"Chat"]];
    -
    - inContentMessage.displayContent = NO;
    + if (inContentMessage.chat.isGroupChat) {
    + [self sendUpdate:inContentMessage.encodedMessage forChat:inContentMessage.chat];
    + inContentMessage.displayContent = NO;
    +
    + AILogWithSignature(@"%@ Sending update [in reply to %@]: %@", self, [inContentMessage.chat valueForProperty:@"TweetInReplyToStatusID"], inContentMessage.encodedMessage);
    + } else {
    + [twitterEngine postDirectMessage:inContentMessage.encodedMessage
    + to:inContentMessage.destination.UID
    + successBlock:^(NSDictionary *dm) {
    + [queuedOutgoingDM addObject:dm];
    + [self displayQueuedUpdatesForRequestType:AITwitterDirectMessageSend];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterDirectMessageSend withError:error userInfo:@{ @"Chat" : inContentMessage.chat }];
    + }];
    - AILogWithSignature(@"%@ Sending update [in reply to %@]: %@", self, [inContentMessage.chat valueForProperty:@"TweetInReplyToStatusID"], inContentMessage.encodedMessage);
    - }
    -
    - } else {
    - requestID = [twitterEngine sendDirectMessage:inContentMessage.encodedMessage
    - to:inContentMessage.destination.UID];
    + inContentMessage.displayContent = NO;
    - if(requestID) {
    - [self setRequestType:AITwitterDirectMessageSend
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:inContentMessage.chat
    - forKey:@"Chat"]];
    -
    - inContentMessage.displayContent = NO;
    -
    - AILogWithSignature(@"%@ Sending DM to %@: %@", self, inContentMessage.destination.UID, inContentMessage.encodedMessage);
    - }
    + AILogWithSignature(@"%@ Sending DM to %@: %@", self, inContentMessage.destination.UID, inContentMessage.encodedMessage);
    }
    - if (!requestID) {
    - AILogWithSignature(@"%@ Message immediate fail.", self);
    - }
    -
    - return (requestID != nil);
    + return YES;
    }
    /*!
    @@ -539,13 +564,82 @@
    return;
    }
    - NSString *requestID = [twitterEngine getUserInformationFor:inContact.UID];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterProfileUserInfo
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:inContact forKey:@"ListContact"]];
    - }
    + [twitterEngine getUserInformationFor:inContact.UID
    + successBlock:^(NSDictionary *thisUserInfo) {
    + if (thisUserInfo) {
    + NSArray *keyNames = [NSArray arrayWithObjects:@"name", @"location", @"description", @"url", @"friends_count", @"followers_count", @"statuses_count", nil];
    + NSArray *readableNames = [NSArray arrayWithObjects:AILocalizedString(@"Name", nil), AILocalizedString(@"Location", nil),
    + AILocalizedString(@"Biography", nil), AILocalizedString(@"Website", nil), AILocalizedString(@"Following", nil),
    + AILocalizedString(@"Followers", nil), AILocalizedString(@"Updates", nil), nil];
    +
    + __block NSMutableArray *profileArray = [NSMutableArray array];
    +
    + for (NSUInteger idx = 0; idx < keyNames.count; idx++) {
    + NSString *keyName = [keyNames objectAtIndex:idx];
    + id unattributedValue = [thisUserInfo objectForKey:keyName];
    + NSString *stringValue = nil;
    + if ([unattributedValue isKindOfClass:[NSNumber class]])
    + stringValue = [(NSNumber *)unattributedValue stringValue];
    + else if ([unattributedValue isKindOfClass:[NSNumber class]])
    + stringValue = unattributedValue;
    +
    + if (stringValue) {
    + NSString *readableName = [readableNames objectAtIndex:idx];
    + NSAttributedString *value;
    +
    + if([keyName isEqualToString:@"friends_count"]) {
    + value = [NSAttributedString attributedStringWithLinkLabel:stringValue
    + linkDestination:[self addressForLinkType:AITwitterLinkFriends userID:inContact.UID statusID:nil context:nil]];
    + } else if ([keyName isEqualToString:@"followers_count"]) {
    + value = [NSAttributedString attributedStringWithLinkLabel:stringValue
    + linkDestination:[self addressForLinkType:AITwitterLinkFollowers userID:inContact.UID statusID:nil context:nil]];
    + } else if ([keyName isEqualToString:@"statuses_count"]) {
    + value = [NSAttributedString attributedStringWithLinkLabel:stringValue
    + linkDestination:[self addressForLinkType:AITwitterLinkUserPage userID:inContact.UID statusID:nil context:nil]];
    + } else {
    + value = [NSAttributedString stringWithString:stringValue];
    + }
    +
    + [profileArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:readableName, KEY_KEY, value, KEY_VALUE, nil]];
    + }
    + }
    +
    + AILogWithSignature(@"%@ Updating profileArray for user %@", self, inContact);
    +
    + // Grab their statuses.
    + [twitterEngine getUserTimelineWithScreenName:inContact.UID
    + count:TWITTER_UPDATE_USER_INFO_COUNT
    + successBlock:^(NSArray *statuses) {
    + AILogWithSignature(@"%@ Updating statuses for profile, user %@", self, inContact);
    +
    + for (NSDictionary *update in statuses) {
    + NSAttributedString *message;
    + NSDictionary *retweet = [update valueForKey:TWITTER_STATUS_RETWEET];
    + NSString *text = [update objectForKey:TWITTER_STATUS_TEXT];
    +
    + if (retweet && [retweet isKindOfClass:[NSDictionary class]]) {
    + text = [NSString stringWithFormat:@"RT @%@: %@",
    + [[retweet objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID],
    + [retweet objectForKey:TWITTER_STATUS_TEXT]];
    + }
    +
    + message = [self parseMessage:text
    + tweetID:[update objectForKey:TWITTER_STATUS_ID]
    + userID:inContact.UID
    + inReplyToUser:[update objectForKey:TWITTER_STATUS_REPLY_UID]
    + inReplyToTweetID:[update objectForKey:TWITTER_STATUS_REPLY_ID]];
    +
    + [profileArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:message, KEY_VALUE, nil]];
    + }
    +
    + [inContact setProfileArray:profileArray notify:NotifyNow];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterProfileUserInfo withError:error userInfo:@{ @"ListContact" : inContact }];
    + }];
    + }
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterProfileUserInfo withError:error userInfo:@{ @"ListContact" : inContact }];
    + }];
    }
    /*!
    @@ -560,21 +654,16 @@
    * @brief Update the Twitter profile
    */
    - (void)setProfileName:(NSString *)name
    - url:(NSString*)url
    + url:(NSString *)url
    location:(NSString *)location
    description:(NSString *)description
    {
    - NSString *requestID = [twitterEngine updateProfileName:name
    - email:nil
    - url:url
    - location:location
    - description:description];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterProfileSelf
    - forRequestID:requestID
    - withDictionary:nil];
    - }
    + [twitterEngine postUpdateProfile:@{ @"name" : name, @"url" : url, @"location" : location, @"description" : description }
    + successBlock:^(NSDictionary *status) {
    + [self userInfoReceived:status forRequest:AITwitterProfileSelf];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterProfileSelf withError:error userInfo:nil];
    + }];
    }
    #pragma mark OAuth
    @@ -663,7 +752,7 @@
    keyEquivalent:@""];
    [menuItem setImage:serviceIcon];
    [menuItem setRepresentedObject:inContact];
    - [menuItemArray addObject:menuItem];
    + [menuItemArray addObject:menuItem];
    menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:AILocalizedString(@"Enable device notifications for %@", "Enable sending Twitter notifications to your phone (device)"), inContact.UID]
    target:self
    @@ -711,39 +800,21 @@
    BOOL enableNotification = menuItem.tag;
    AIListContact *contact = menuItem.representedObject;
    - NSString *requestID = nil;
    -
    - BOOL initialFailure = NO;
    -
    - if (enableNotification) {
    - requestID = [twitterEngine enableNotificationsFor:contact.UID];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterNotificationEnable
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:contact, @"ListContact", nil]];
    - } else {
    - initialFailure = YES;
    - }
    -
    - } else {
    - requestID = [twitterEngine disableNotificationsFor:contact.UID];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterNotificationDisable
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:contact, @"ListContact", nil]];
    - } else {
    - initialFailure = YES;
    - }
    - }
    -
    - if (initialFailure) {
    - [adium.interfaceController handleErrorMessage:(enableNotification ?
    - AILocalizedString(@"Unable to Enable Notifications", nil) :
    - AILocalizedString(@"Unable to Disable Notifications", nil))
    - withDescription:AILocalizedString(@"Unable to connect to the Twitter server.", nil)];
    - }
    + [twitterEngine postUpdateNotifications:enableNotification
    + forScreenName:contact.UID
    + successBlock:^(NSDictionary *relationship) {
    + NSString *status = (enableNotification ?
    + AILocalizedString(@"Notifications Enabled", nil) :
    + AILocalizedString(@"Notifications Disabled", nil));
    + [adium.interfaceController handleMessage:status
    + withDescription:[NSString stringWithFormat:(enableNotification ?
    + AILocalizedString(@"You will now receive device notifications for %@.", nil) :
    + AILocalizedString(@"You will no longer receive device notifications for %@.", nil)),
    + contact.UID]
    + withWindowTitle:status];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:(enableNotification ? AITwitterNotificationEnable : AITwitterNotificationDisable) withError:error userInfo:@{ @"ListContact" : contact }];
    + }];
    }
    /*!
    @@ -830,14 +901,38 @@
    */
    - (void)getRateLimitAmount
    {
    - NSString *requestID = [twitterEngine getRateLimitStatus];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterRateLimitStatus
    - forRequestID:requestID
    - withDictionary:nil];
    - }
    -
    + [twitterEngine getRateLimitsForResources:@[ @"users", @"statuses", @"friendships", @"direct_messages" ]
    + successBlock:^(NSDictionary *rateLimits) {
    + NSMutableString *formattedString = [NSMutableString string];
    + [rateLimits[@"resources"] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + if ([obj isKindOfClass:[NSDictionary class]]) {
    + __block BOOL displayedHeader = NO;
    + [obj enumerateKeysAndObjectsUsingBlock:^(id subKey, id subObj, BOOL *subStop) {
    + NSDate *resetDate = [NSDate dateWithTimeIntervalSince1970:[[subObj objectForKey:TWITTER_RATE_LIMIT_RESET_SECONDS] intValue]];
    +
    + int limit = [[subObj objectForKey:TWITTER_RATE_LIMIT] intValue];
    + int remaining = [[subObj objectForKey:TWITTER_RATE_LIMIT_REMAINING] intValue];
    + NSString *resource = [subKey stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"/%@/", key]
    + withString:@""];
    + if (remaining < limit) {
    + if (!displayedHeader) {
    + [formattedString appendFormat:@"%@:\n", key];
    + displayedHeader = YES;
    + }
    + [formattedString appendFormat:@"\t%@: %d/%d for %@\n", resource,
    + remaining, limit,
    + [NSDateFormatter stringForTimeInterval:[resetDate timeIntervalSinceNow]
    + showingSeconds:YES
    + abbreviated:YES
    + approximated:NO]];
    + }
    + }];
    + }
    + }];
    + [[NSAlert alertWithMessageText:AILocalizedString(@"Current Twitter rate limits", "Message in the rate limits status window") defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", formattedString] beginSheetModalForWindow:nil modalDelegate:nil didEndSelector:nil contextInfo:nil];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterRateLimitStatus withError:error userInfo:nil];
    + }];
    }
    #pragma mark Contact handling
    @@ -929,20 +1024,26 @@
    {
    // If we don't already have an icon for the user...
    if(![listContact boolValueForProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON]) {
    - NSString *fileName = [[url lastPathComponent] stringByReplacingOccurrencesOfString:@"_normal." withString:@"_bigger."];
    -
    - url = [[url stringByDeletingLastPathComponent] stringByAppendingPathComponent:fileName];
    + [listContact setValue:[NSNumber numberWithBool:YES] forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    // Grab the user icon and set it as their serverside icon.
    - NSString *requestID = [twitterEngine getImageAtURL:url];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterUserIconPull
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:listContact forKey:@"ListContact"]];
    - }
    -
    - [listContact setValue:[NSNumber numberWithBool:YES] forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    + NSString *imageURL = [url stringByReplacingOccurrencesOfString:@"_normal." withString:@"_bigger."];
    + NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
    + [NSURLConnection sendAsynchronousRequest:imageRequest
    + queue:[NSOperationQueue currentQueue]
    + completionHandler:^(NSURLResponse *resp, NSData *data, NSError *error) {
    + NSImage *image = [[NSImage alloc] initWithData:data];
    +
    + if (image) {
    + AILogWithSignature(@"%@ Updated user icon for %@", self, listContact);
    + [listContact setServersideIconData:[image TIFFRepresentation]
    + notify:NotifyLater];
    +
    + [listContact setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    + } else {
    + [self requestFailed:AITwitterUserIconPull withError:error userInfo:@{ @"ListContact" : listContact }];
    + }
    + }];
    }
    }
    @@ -950,17 +1051,17 @@
    * @brief Unfollow the requested contacts.
    */
    - (void)removeContacts:(NSArray *)objects fromGroups:(NSArray *)groups
    -{
    +{
    for (AIListContact *object in objects) {
    - NSString *requestID = [twitterEngine disableUpdatesFor:object.UID];
    -
    AILogWithSignature(@"%@ Requesting unfollow for: %@", self, object.UID);
    -
    - if(requestID) {
    - [self setRequestType:AITwitterRemoveFollow
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:object forKey:@"ListContact"]];
    - }
    + [twitterEngine postUnfollow:object.UID
    + successBlock:^(NSDictionary *user) {
    + for (NSString *groupName in object.remoteGroupNames) {
    + [object removeRemoteGroupName:groupName];
    + }
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterRemoveFollow withError:error userInfo:@{ @"ListContact" : UID }];
    + }];
    }
    }
    @@ -997,58 +1098,14 @@
    AILogWithSignature(@"Not adding contact %@ to group %@, it's me!", contact.UID, group.UID);
    return;
    }
    -
    - NSString *requestID = [twitterEngine enableUpdatesFor:contact.UID];
    -
    +
    AILogWithSignature(@"%@ Requesting follow for: %@", self, contact.UID);
    -
    - if(requestID) {
    - NSString *updateRequestID = [twitterEngine getUserInformationFor:contact.UID];
    -
    - if (updateRequestID) {
    - [self setRequestType:AITwitterAddFollow
    - forRequestID:updateRequestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:contact.UID, @"UID", nil]];
    - }
    - }
    -}
    -
    -#pragma mark Request cataloguing
    -/*!
    - * @brief Set the type and optional dictionary for a request ID
    - *
    - * Sets the AITwitterRequestType for a particular request ID, so when the request finishes we can identify what it is for.
    - * Optionally sets a dictionary which can be retrieved in association with the request type.
    - */
    -- (void)setRequestType:(AITwitterRequestType)type forRequestID:(NSString *)requestID withDictionary:(NSDictionary *)info
    -{
    - [pendingRequests setObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:type], @"Type",
    - info, @"Info", nil]
    - forKey:requestID];
    -}
    -
    -/*!
    - * @brief Get the request type for a request ID
    - */
    -- (AITwitterRequestType)requestTypeForRequestID:(NSString *)requestID
    -{
    - return [(NSNumber *)[[pendingRequests objectForKey:requestID] objectForKey:@"Type"] intValue];
    -}
    -
    -/*!
    - * @brief Get the dictionary associated with a request ID
    - */
    -- (NSDictionary *)dictionaryForRequestID:(NSString *)requestID
    -{
    - return (NSDictionary *)[[pendingRequests objectForKey:requestID] objectForKey:@"Info"];
    -}
    -
    -/*!
    - * @brief Remove a request ID's saved information.
    - */
    -- (void)clearRequestTypeForRequestID:(NSString *)requestID
    -{
    - [pendingRequests removeObjectForKey:requestID];
    + [twitterEngine postFollow:contact.UID
    + successBlock:^(NSDictionary *friend) {
    + [self userInfoReceived:@{ @"friends" : friend } forRequest:AITwitterAddFollow];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterAddFollow withError:error userInfo:@{ @"UID" : contact.UID }];
    + }];
    }
    #pragma mark Preference updating
    @@ -1056,7 +1113,7 @@
    preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
    {
    [super preferencesChangedForGroup:group key:key object:object preferenceDict:prefDict firstTime:firstTime];
    -
    +
    // We only care about our changes.
    if (object != self) {
    return;
    @@ -1066,18 +1123,15 @@
    if([key isEqualToString:KEY_USER_ICON]) {
    // Avoid pushing an icon update which we just downloaded.
    if(![self boolValueForProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON]) {
    - NSString *requestID = [twitterEngine updateProfileImage:[prefDict objectForKey:KEY_USER_ICON]];
    -
    - if(requestID) {
    - AILogWithSignature(@"%@ Pushing self icon update", self);
    -
    - [self setRequestType:AITwitterProfileSelf
    - forRequestID:requestID
    - withDictionary:nil];
    - }
    + AILogWithSignature(@"%@ Pushing self icon update", self);
    + [twitterEngine postUpdateProfileImage:[prefDict objectForKey:KEY_USER_ICON]
    + successBlock:^(NSDictionary *myInfo) {
    + [self userInfoReceived:myInfo forRequest:AITwitterProfileSelf];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterProfileSelf withError:error userInfo:nil];
    + }];
    + [self setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    }
    -
    - [self setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    }
    }
    @@ -1106,27 +1160,18 @@
    // Delay updates when loading our contacts list.
    [self silenceAllContactUpdatesForInterval:18.0];
    // Grab our user list.
    - NSString *requestID;
    - NSString *friendRequestType;
    - if (self.supportsCursors) {
    - requestID = [twitterEngine getRecentlyUpdatedFriendsFor:self.UID startingAtCursor:-1];
    - friendRequestType = @"Cursor";
    - } else {
    - requestID = [twitterEngine getRecentlyUpdatedFriendsFor:self.UID startingAtPage:1];
    - friendRequestType = @"Page";
    - }
    -
    - if (requestID) {
    - [self setRequestType:AITwitterInitialUserInfo
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:friendRequestType]];
    - }
    + [twitterEngine getFriendsForScreenName:self.UID
    + successBlock:^(NSArray *friends) {
    + [self userInfoReceived:@{ @"friends" : friends } forRequest:AITwitterInitialUserInfo];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterInitialUserInfo withError:error userInfo:nil];
    + }];
    } else {
    [[self timelineChat] removeAllParticipatingContactsSilently];
    [self removeAllContacts];
    }
    }
    - }
    + }
    }
    #pragma mark Periodic update scheduler
    @@ -1140,68 +1185,58 @@
    return;
    }
    - NSString *requestID;
    - NSString *lastID;
    -
    - // We haven't completed the timeline nor replies. This lets us know if we should display statuses.
    - followedTimelineCompleted = repliesCompleted = NO;
    - futureTimelineLastID = futureRepliesLastID = nil;
    -
    // Prevent triggering this update routine multiple times.
    pendingUpdateCount = 3;
    // We haven't printed error messages for this set.
    timelineErrorMessagePrinted = NO;
    -
    +
    [queuedUpdates removeAllObjects];
    [queuedDM removeAllObjects];
    AILogWithSignature(@"%@ Periodic update fire", self);
    - // Pull direct messages
    + NSString *lastID;
    +
    + // Pull direct messages
    lastID = [self preferenceForKey:TWITTER_PREFERENCE_DM_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    - requestID = [twitterEngine getDirectMessagesSinceID:lastID startingAtPage:1];
    + [twitterEngine getDirectMessagesSinceID:lastID
    + count:TWITTER_UPDATE_DM_COUNT
    + successBlock:^(NSArray *statuses) {
    + [self directMessagesReceived:statuses forRequest:AITwitterUpdateDirectMessage];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterUpdateDirectMessage withError:error userInfo:nil];
    + }];
    - if (requestID) {
    - [self setRequestType:AITwitterUpdateDirectMessage
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"Page", nil]];
    - } else {
    - --pendingUpdateCount;
    - }
    + // We haven't completed the timeline nor replies. This lets us know if we should display statuses.
    + followedTimelineCompleted = repliesCompleted = NO;
    + futureTimelineLastID = futureRepliesLastID = nil;
    // Pull followed timeline
    lastID = [self preferenceForKey:TWITTER_PREFERENCE_TIMELINE_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    - requestID = [twitterEngine getFollowedTimelineFor:nil
    - sinceID:lastID
    - startingAtPage:1
    - count:(lastID ? TWITTER_UPDATE_TIMELINE_COUNT : TWITTER_UPDATE_TIMELINE_COUNT_FIRST_RUN)];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterUpdateFollowedTimeline
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"Page", nil]];
    - } else {
    - --pendingUpdateCount;
    - }
    + [twitterEngine getHomeTimelineSinceID:lastID
    + count:TWITTER_UPDATE_TIMELINE_COUNT
    + successBlock:^(NSArray *statuses) {
    + [self statusesReceived:statuses forRequest:AITwitterUpdateFollowedTimeline];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterUpdateFollowedTimeline withError:error userInfo:nil];
    + }];
    // Pull the replies feed
    lastID = [self preferenceForKey:TWITTER_PREFERENCE_REPLIES_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    - requestID = [twitterEngine getRepliesSinceID:lastID startingAtPage:1];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterUpdateReplies
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], @"Page", nil]];
    - } else {
    - --pendingUpdateCount;
    - }
    + [twitterEngine getMentionsTimelineSinceID:lastID
    + count:TWITTER_UPDATE_REPLIES_COUNT
    + successBlock:^(NSArray *statuses) {
    + [self statusesReceived:statuses forRequest:AITwitterUpdateReplies];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterUpdateReplies withError:error userInfo:nil];
    + }];
    }
    #pragma mark Message Display
    @@ -1213,8 +1248,7 @@
    switch (error.code) {
    case 400:
    // Bad Request: your request is invalid, and we'll return an error message that tells you why.
    - // This is the status code returned if you've exceeded the rate limit.
    - return AILocalizedString(@"You've exceeded the rate limit.", nil);
    + return AILocalizedString(@"The request is invalid.", nil);
    break;
    case 401:
    @@ -1232,6 +1266,11 @@
    return AILocalizedString(@"Requested resource not found.", nil);
    break;
    + case 429:
    + // This is the status code returned if you've exceeded the rate limit.
    + return AILocalizedString(@"You've exceeded the rate limit.", nil);
    + break;
    +
    case 500:
    // Internal Server Error: we did something wrong. Please post to the group about it and the Twitter team will investigate.
    return AILocalizedString(@"The server reported an internal error.", nil);
    @@ -1301,15 +1340,12 @@
    */
    - (BOOL)retweetTweet:(NSString *)tweetID
    {
    - NSString *requestID = [twitterEngine retweetUpdate:tweetID];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterSendUpdate
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:tweetID, @"tweetID", nil]];
    - } else {
    - [self.timelineChat receivedError:[NSNumber numberWithInt:AIChatMessageSendingConnectionError]];
    - }
    + [twitterEngine postStatusRetweetWithID:tweetID
    + successBlock:^(NSDictionary *status) {
    + [self statusesReceived:@[status] forRequest:AITwitterSendUpdate];
    + } errorBlock:^(NSError *error) {
    + [self requestFailed:AITwitterSendUpdate withError:error userInfo:@{ @"Chat" : self.timelineChat }];
    + }];
    return YES;
    }
    @@ -1322,19 +1358,59 @@
    */
    - (void)toggleFavoriteTweet:(NSString *)tweetID
    {
    - NSString *requestID = [twitterEngine markUpdate:tweetID asFavorite:YES];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterFavoriteYes
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:tweetID, @"tweetID", nil]];
    - } else {
    - AIChat *timelineChat = self.timelineChat;
    -
    - [adium.contentController displayEvent:AILocalizedString(@"Attempt to favorite tweet failed to connect.", nil)
    - ofType:@"favorite"
    - inChat:timelineChat];
    - }
    + [self markTweet:tweetID asFavorite:YES];
    +}
    +
    +- (void)markTweet:(NSString *)tweetID asFavorite:(BOOL)favorite
    +{
    + [twitterEngine postFavoriteState:favorite
    + forStatusID:tweetID
    + successBlock:^(NSDictionary *status) {
    + AIChat *timelineChat = self.timelineChat;
    + NSString *message;
    +
    + // Use HTML for the status message since it's just easier to localize that way.
    + if (favorite) {
    + message = AILocalizedString(@"The <a href=\"%@\">requested tweet</a> by <a href=\"%@\">%@</a> is now a favorite.", nil);
    + } else {
    + message = AILocalizedString(@"The <a href=\"%@\">requested tweet</a> by <a href=\"%@\">%@</a> is no longer a favorite.", nil);
    + }
    +
    + NSString *userID = [[status objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID];
    +
    +
    + message = [NSString stringWithFormat:message,
    + [self addressForLinkType:AITwitterLinkStatus
    + userID:userID
    + statusID:[status objectForKey:TWITTER_STATUS_ID]
    + context:nil],
    + [self addressForLinkType:AITwitterLinkUserPage
    + userID:userID
    + statusID:nil
    + context:nil],
    + userID];
    +
    + NSAttributedString *attributedMessage = [[AIHTMLDecoder decoder] decodeHTML:message withDefaultAttributes:nil];
    +
    + AIContentEvent *content = [AIContentEvent eventInChat:timelineChat
    + withSource:nil
    + destination:self
    + date:[NSDate date]
    + message:attributedMessage
    + withType:@"favorite"];
    +
    + content.postProcessContent = NO;
    + content.coalescingKey = @"favorite";
    +
    + [adium.contentController receiveContentObject:content];
    + } errorBlock:^(NSError *error) {
    + if (error.code == 403) {
    + // We've attempted to add or remove when we already have it marked as such. Try the opposite.
    + [self markTweet:tweetID asFavorite:!favorite];
    + } else {
    + [self requestFailed:(favorite ? AITwitterFavoriteYes : AITwitterFavoriteNo) withError:error userInfo:nil];
    + }
    + }];
    }
    /*!
    @@ -1344,19 +1420,16 @@
    */
    - (void)destroyTweet:(NSString *)tweetID
    {
    - NSString *requestID = [twitterEngine deleteUpdate:tweetID];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterDestroyStatus
    - forRequestID:requestID
    - withDictionary:nil];
    - } else {
    - AIChat *timelineChat = self.timelineChat;
    -
    - [adium.contentController displayEvent:AILocalizedString(@"Attempt to delete tweet failed to connect.", nil)
    - ofType:@"delete"
    - inChat:timelineChat];
    - }
    + [twitterEngine postDestroyStatusWithID:tweetID
    + successBlock:^(NSDictionary *status) {
    + [adium.contentController displayEvent:AILocalizedString(@"Your tweet has been successfully deleted.", nil)
    + ofType:@"delete"
    + inChat:self.timelineChat];
    + } errorBlock:^(NSError *error) {
    + [adium.contentController displayEvent:[NSString stringWithFormat:AILocalizedString(@"Your tweet failed to delete. %@", nil), [self errorMessageForError:error]]
    + ofType:@"delete"
    + inChat:self.timelineChat];
    + }];
    }
    /*!
    @@ -1367,20 +1440,22 @@
    - (void)destroyDirectMessage:(NSString *)messageID
    forUser:(NSString *)userID
    {
    - NSString *requestID = [twitterEngine deleteDirectMessage:messageID];
    - AIListContact *contact = [self contactWithUID:userID];
    -
    - if(requestID) {
    - [self setRequestType:AITwitterDestroyDM
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:contact forKey:@"ListContact"]];
    - } else {
    - AIChat *chat = [adium.chatController chatWithContact:contact];
    -
    - [adium.contentController displayEvent:AILocalizedString(@"Attempt to delete tweet failed to connect.", nil)
    - ofType:@"delete"
    - inChat:chat];
    - }
    + [twitterEngine postDestroyDirectMessageWithID:messageID
    + successBlock:^(NSDictionary *dm) {
    + AIListContact *contact = [self contactWithUID:userID];
    + AIChat *chat = [adium.chatController chatWithContact:contact];
    +
    + [adium.contentController displayEvent:AILocalizedString(@"The direct message has been successfully deleted.", nil)
    + ofType:@"delete"
    + inChat:chat];
    + } errorBlock:^(NSError *error) {
    + AIListContact *contact = [self contactWithUID:userID];
    + AIChat *chat = [adium.chatController chatWithContact:contact];
    +
    + [adium.contentController displayEvent:[NSString stringWithFormat:AILocalizedString(@"The direct message failed to delete. %@", nil), [self errorMessageForError:error]]
    + ofType:@"delete"
    + inChat:chat];
    + }];
    }
    /*!
    @@ -1455,7 +1530,7 @@
    message = [self linkifiedAttributedStringFromString:message];
    - BOOL replyTweet = (replyTweetID.length);
    + BOOL replyTweet = (replyTweetID.length > 0);
    BOOL tweetLink = (tweetID.length && userID.length);
    if (replyTweet || tweetLink) {
    @@ -1724,7 +1799,7 @@
    id fromObject = nil;
    - if(![self.UID isCaseInsensitivelyEqualToString:contactUID]) {
    + if (![self.UID isCaseInsensitivelyEqualToString:contactUID]) {
    AIListContact *listContact = [self contactWithUID:contactUID];
    // Update the user's status message
    @@ -1773,7 +1848,7 @@
    NSArray *sortedQueuedDM = [*unsortedArray sortedArrayUsingFunction:queuedDMSort context:nil];
    for (NSDictionary *message in sortedQueuedDM) {
    - NSDate *date = [message objectForKey:TWITTER_DM_CREATED];
    + NSDate *date = [NSDate dateWithNaturalLanguageString:[message objectForKey:TWITTER_DM_CREATED]];
    NSString *text = [message objectForKey:TWITTER_DM_TEXT];
    NSString *fromUID = [message objectForKey:TWITTER_DM_SENDER_UID];
    NSString *toUID = [message objectForKey:TWITTER_DM_RECIPIENT_UID];
    @@ -1810,52 +1885,20 @@
    }
    }
    -#pragma mark MGTwitterEngine Delegate Methods
    -/*!
    - * @brief A request was successful
    - *
    - * We only care about requests succeeding if they aren't specifically handled in another location.
    - */
    -- (void)requestSucceeded:(NSString *)identifier
    -{
    - // If a request succeeds and we think we're offline, call ourselves online.
    - if ([self requestTypeForRequestID:identifier] == AITwitterDisconnect) {
    - [self didDisconnect];
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterRemoveFollow) {
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    -
    - for (NSString *groupName in listContact.remoteGroupNames) {
    - [listContact removeRemoteGroupName:groupName];
    - }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterDestroyStatus) {
    - AIChat *timelineChat = self.timelineChat;
    -
    - [adium.contentController displayEvent:AILocalizedString(@"Your tweet has been successfully deleted.", nil)
    - ofType:@"delete"
    - inChat:timelineChat];
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterDestroyDM) {
    - AIListContact *contact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    - AIChat *chat = [adium.chatController chatWithContact:contact];
    -
    - [adium.contentController displayEvent:AILocalizedString(@"The direct message has been successfully deleted.", nil)
    - ofType:@"delete"
    - inChat:chat];
    - }
    -}
    -
    +#pragma mark Response Handling
    /*!
    * @brief A request failed
    *
    * If it's a fatal error, we need to kill the session and retry. Otherwise, twitter's reliability is
    * pretty terrible, so let's ignore errors for the most part.
    */
    -- (void)requestFailed:(NSString *)identifier withError:(NSError *)error
    -{
    - switch ([self requestTypeForRequestID:identifier]) {
    +- (void)requestFailed:(AITwitterRequestType)identifier withError:(NSError *)error userInfo:(NSDictionary *)userInfo
    +{
    + switch (identifier) {
    case AITwitterDirectMessageSend:
    case AITwitterSendUpdate:
    {
    - AIChat *chat = [[self dictionaryForRequestID:identifier] objectForKey:@"Chat"];
    + AIChat *chat = [userInfo objectForKey:@"Chat"];
    if (chat) {
    [chat receivedError:[NSNumber numberWithInt:AIChatMessageSendingConnectionError]];
    @@ -1869,14 +1912,9 @@
    [self didDisconnect];
    break;
    - case AITwitterInitialUserInfo:
    - [self setLastDisconnectionError:AILocalizedString(@"Unable to retrieve user list [fail]", "Message when a (vital) twitter request to retrieve the follow list fails")];
    - [self didDisconnect];
    - break;
    -
    case AITwitterUserIconPull:
    {
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    + AIListContact *listContact = [userInfo objectForKey:@"ListContact"];
    // Image pull failed, flag ourselves as needing to try again.
    [listContact setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    @@ -1920,12 +1958,12 @@
    if(error.code == 404) {
    [adium.interfaceController handleErrorMessage:AILocalizedString(@"Unable to Add Contact", nil)
    withDescription:[NSString stringWithFormat:AILocalizedString(@"Unable to add %@ to account %@, the user does not exist.", nil),
    - [[self dictionaryForRequestID:identifier] objectForKey:@"UID"],
    + [userInfo objectForKey:@"UID"],
    self.explicitFormattedUID]];
    } else {
    [adium.interfaceController handleErrorMessage:AILocalizedString(@"Unable to Add Contact", nil)
    withDescription:[NSString stringWithFormat:AILocalizedString(@"Unable to add %@ to account %@. %@",nil),
    - [[self dictionaryForRequestID:identifier] objectForKey:@"UID"],
    + [userInfo objectForKey:@"UID"],
    self.explicitFormattedUID,
    [self errorMessageForError:error]]];
    }
    @@ -1934,13 +1972,13 @@
    case AITwitterRemoveFollow:
    [adium.interfaceController handleErrorMessage:AILocalizedString(@"Unable to Remove Contact", nil)
    withDescription:[NSString stringWithFormat:AILocalizedString(@"Unable to remove %@ on account %@. %@", nil),
    - ((AIListContact *)[[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"]).UID,
    + ((AIListContact *)[userInfo objectForKey:@"ListContact"]).UID,
    self.explicitFormattedUID,
    [self errorMessageForError:error]]];
    break;
    case AITwitterValidateCredentials:
    - if(error.code == 401) {
    + if(error.code == 401) {
    if(self.useOAuth) {
    [self setPasswordTemporarily:nil];
    [self setLastDisconnectionError:TWITTER_OAUTH_NOT_AUTHORIZED];
    @@ -1964,29 +2002,9 @@
    case AITwitterFavoriteNo:
    {
    AIChat *timelineChat = self.timelineChat;
    -
    - if (error.code == 403) {
    - // We've attempted to add or remove when we already have it marked as such. Try the opposite.
    - BOOL addAsFavorite = ([self requestTypeForRequestID:identifier] == AITwitterFavoriteNo);
    - NSString *tweetID = [[self dictionaryForRequestID:identifier] objectForKey:@"tweetID"];
    -
    - NSString *requestID = [twitterEngine markUpdate:tweetID
    - asFavorite:addAsFavorite];
    -
    - if (requestID) {
    - [self setRequestType:(addAsFavorite ? AITwitterFavoriteYes : AITwitterFavoriteNo)
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:tweetID, @"tweetID", nil]];
    - } else {
    - [adium.contentController displayEvent:AILocalizedString(@"Attempt to favorite tweet failed to connect.", nil)
    - ofType:@"favorite"
    - inChat:timelineChat];
    - }
    - } else {
    [adium.contentController displayEvent:[NSString stringWithFormat:AILocalizedString(@"Attempt to favorite tweet failed. %@", nil), [self errorMessageForError:error]]
    ofType:@"favorite"
    - inChat:timelineChat];
    - }
    + inChat:timelineChat];
    break;
    }
    @@ -1994,8 +2012,8 @@
    case AITwitterNotificationEnable:
    case AITwitterNotificationDisable:
    {
    - BOOL enableNotification = ([self requestTypeForRequestID:identifier] == AITwitterNotificationEnable);
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    + BOOL enableNotification = (identifier == AITwitterNotificationEnable);
    + AIListContact *listContact = [userInfo objectForKey:@"ListContact"];
    [adium.interfaceController handleErrorMessage:(enableNotification ?
    AILocalizedString(@"Unable to Enable Notifications", nil) :
    @@ -2003,636 +2021,241 @@
    withDescription:[NSString stringWithFormat:AILocalizedString(@"Cannot change notification setting for %@. %@", nil), listContact.UID, [self errorMessageForError:error]]];
    break;
    }
    -
    case AITwitterDestroyStatus:
    - {
    - AIChat *timelineChat = self.timelineChat;
    -
    - [adium.contentController displayEvent:[NSString stringWithFormat:AILocalizedString(@"Your tweet failed to delete. %@", nil), [self errorMessageForError:error]]
    - ofType:@"delete"
    - inChat:timelineChat];
    - break;
    - }
    -
    case AITwitterDestroyDM:
    - {
    - AIListContact *contact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    - AIChat *chat = [adium.chatController chatWithContact:contact];
    -
    - [adium.contentController displayEvent:[NSString stringWithFormat:AILocalizedString(@"The direct message failed to delete. %@", nil), [self errorMessageForError:error]]
    - ofType:@"delete"
    - inChat:chat];
    - break;
    - }
    -
    case AITwitterUnknownType:
    case AITwitterRateLimitStatus:
    case AITwitterProfileSelf:
    case AITwitterSelfUserIconPull:
    case AITwitterProfileUserInfo:
    case AITwitterProfileStatusUpdates:
    + case AITwitterInitialUserInfo:
    // While we don't handle the errors, it's a good idea to not have a "default" just to prevent accidentally letting something
    // we should really handle slip through.
    break;
    -
    }
    - AILogWithSignature(@"%@ Request failed (%@ - %u) - %@", self, identifier, [self requestTypeForRequestID:identifier], error);
    -
    - [self clearRequestTypeForRequestID:identifier];
    + AILogWithSignature(@"%@ Request failed (%u) - %@", self, identifier, error);
    }
    /*!
    * @brief Status updates received
    */
    -- (void)statusesReceived:(NSArray *)statuses forRequest:(NSString *)identifier
    +- (void)statusesReceived:(NSArray *)statuses forRequest:(AITwitterRequestType)identifier
    {
    - if([self requestTypeForRequestID:identifier] == AITwitterUpdateFollowedTimeline ||
    - [self requestTypeForRequestID:identifier] == AITwitterUpdateReplies) {
    + if(identifier == AITwitterUpdateFollowedTimeline ||
    + identifier == AITwitterUpdateReplies) {
    NSString *lastID;
    - BOOL nextPageNecessary = NO;
    -
    - if([self requestTypeForRequestID:identifier] == AITwitterUpdateFollowedTimeline) {
    + if(identifier == AITwitterUpdateFollowedTimeline) {
    lastID = [self preferenceForKey:TWITTER_PREFERENCE_TIMELINE_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    -
    - nextPageNecessary = (lastID && statuses.count >= TWITTER_UPDATE_TIMELINE_COUNT - 5);
    } else {
    lastID = [self preferenceForKey:TWITTER_PREFERENCE_REPLIES_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    -
    - nextPageNecessary = (lastID && statuses.count >= TWITTER_UPDATE_REPLIES_COUNT - 5);
    }
    // Store the largest tweet ID we find; this will be our "last ID" the next time we run.
    - NSString *largestTweet = [[self dictionaryForRequestID:identifier] objectForKey:@"LargestTweet"];
    + NSString *largestTweet = nil;
    +
    + if (statuses.count)
    + largestTweet = [[statuses objectAtIndex:0] objectForKey:TWITTER_STATUS_ID];
    - // The largest ID is first, compare.
    - if (statuses.count) {
    - NSString *tweetID = [[statuses objectAtIndex:0] objectForKey:TWITTER_STATUS_ID];
    - if (!largestTweet || [largestTweet compare:tweetID options:NSNumericSearch] == NSOrderedAscending) {
    - largestTweet = tweetID;
    - }
    + //Convert the TWITTER_STATUS_CREATED datestrings to NSDates
    + NSMutableArray *ms = (__bridge_transfer NSMutableArray *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFArrayRef)statuses, kCFPropertyListMutableContainers);
    + [ms enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    + [obj setObject:[NSDate dateWithNaturalLanguageString:[obj objectForKey:TWITTER_STATUS_CREATED]]
    + forKey:TWITTER_STATUS_CREATED];
    + }];
    +
    + [queuedUpdates addObjectsFromArray:ms];
    +
    + AILogWithSignature(@"%@ Last ID: %@ Largest Tweet: %@", self, lastID, largestTweet);
    +
    + if (identifier == AITwitterUpdateFollowedTimeline) {
    + followedTimelineCompleted = YES;
    + futureTimelineLastID = largestTweet;
    + } else if (identifier == AITwitterUpdateReplies) {
    + repliesCompleted = YES;
    + futureRepliesLastID = largestTweet;
    }
    - [queuedUpdates addObjectsFromArray:statuses];
    + --pendingUpdateCount;
    - AILogWithSignature(@"%@ Last ID: %@ Largest Tweet: %@ Next Page Necessary: %d", self, lastID, largestTweet, nextPageNecessary);
    + AILogWithSignature(@"%@ Followed completed: %d Replies completed: %d", self, followedTimelineCompleted, repliesCompleted);
    - // See if we need to pull more updates.
    - if (nextPageNecessary) {
    - int nextPage = [[[self dictionaryForRequestID:identifier] objectForKey:@"Page"] intValue] + 1;
    - NSString *requestID;
    -
    - if ([self requestTypeForRequestID:identifier] == AITwitterUpdateFollowedTimeline) {
    - requestID = [twitterEngine getFollowedTimelineFor:nil
    - sinceID:lastID
    - startingAtPage:nextPage
    - count:TWITTER_UPDATE_TIMELINE_COUNT];
    -
    - AILogWithSignature(@"%@ Pulling additional timeline page %d", self, nextPage);
    -
    - if (requestID) {
    - [self setRequestType:AITwitterUpdateFollowedTimeline
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:nextPage], @"Page",
    - largestTweet, @"LargestTweet", nil]];
    - } else {
    - // Gracefully fail: remove all stored objects.
    - AILogWithSignature(@"%@ Immediate timeline fail", self);
    - --pendingUpdateCount;
    - [queuedUpdates removeAllObjects];
    + if (followedTimelineCompleted && repliesCompleted) {
    + if (queuedUpdates.count) {
    + // Set the "last pulled" for the timeline and replies, since we've completed both.
    + if(futureRepliesLastID) {
    + AILogWithSignature(@"%@ futureRepliesLastID = %@", self, futureRepliesLastID);
    +
    + [self setPreference:futureRepliesLastID
    + forKey:TWITTER_PREFERENCE_REPLIES_LAST_ID
    + group:TWITTER_PREFERENCE_GROUP_UPDATES];
    +
    + futureRepliesLastID = nil;
    }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterUpdateReplies) {
    - requestID = [twitterEngine getRepliesSinceID:lastID startingAtPage:nextPage];
    -
    - AILogWithSignature(@"%@ Pulling additional replies page %d", self, nextPage);
    + if(futureTimelineLastID) {
    + AILogWithSignature(@"%@ futureTimelineLastID = %@", self, futureTimelineLastID);
    +
    + [self setPreference:futureTimelineLastID
    + forKey:TWITTER_PREFERENCE_TIMELINE_LAST_ID
    + group:TWITTER_PREFERENCE_GROUP_UPDATES];
    +
    + futureTimelineLastID = nil;
    + }
    - if (requestID) {
    - [self setRequestType:AITwitterUpdateReplies
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:nextPage], @"Page",
    - largestTweet, @"LargestTweet", nil]];
    - } else {
    - // Gracefully fail: remove all stored objects.
    - AILogWithSignature(@"%@ Immediate reply fail", self);
    - --pendingUpdateCount;
    - [queuedUpdates removeAllObjects];
    - }
    - }
    - } else {
    - if([self requestTypeForRequestID:identifier] == AITwitterUpdateFollowedTimeline) {
    - followedTimelineCompleted = YES;
    - futureTimelineLastID = largestTweet;
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterUpdateReplies) {
    - repliesCompleted = YES;
    - futureRepliesLastID = largestTweet;
    + [self displayQueuedUpdatesForRequestType:identifier];
    }
    - --pendingUpdateCount;
    -
    - AILogWithSignature(@"%@ Followed completed: %d Replies completed: %d", self, followedTimelineCompleted, repliesCompleted);
    -
    - if (followedTimelineCompleted && repliesCompleted) {
    - if (queuedUpdates.count) {
    - // Set the "last pulled" for the timeline and replies, since we've completed both.
    - if(futureRepliesLastID) {
    - AILogWithSignature(@"%@ futureRepliesLastID = %@", self, futureRepliesLastID);
    -
    - [self setPreference:futureRepliesLastID
    - forKey:TWITTER_PREFERENCE_REPLIES_LAST_ID
    - group:TWITTER_PREFERENCE_GROUP_UPDATES];
    -
    - futureRepliesLastID = nil;
    - }
    -
    - if(futureTimelineLastID) {
    - AILogWithSignature(@"%@ futureTimelineLastID = %@", self, futureTimelineLastID);
    -
    - [self setPreference:futureTimelineLastID
    - forKey:TWITTER_PREFERENCE_TIMELINE_LAST_ID
    - group:TWITTER_PREFERENCE_GROUP_UPDATES];
    -
    - futureTimelineLastID = nil;
    - }
    -
    - [self displayQueuedUpdatesForRequestType:[self requestTypeForRequestID:identifier]];
    - }
    -
    - if (![self preferenceForKey:TWITTER_PREFERENCE_EVER_LOADED_TIMELINE group:TWITTER_PREFERENCE_GROUP_UPDATES]) {
    - [self setPreference:[NSNumber numberWithBool:YES]
    - forKey:TWITTER_PREFERENCE_EVER_LOADED_TIMELINE
    - group:TWITTER_PREFERENCE_GROUP_UPDATES];
    - }
    + if (![self preferenceForKey:TWITTER_PREFERENCE_EVER_LOADED_TIMELINE group:TWITTER_PREFERENCE_GROUP_UPDATES]) {
    + [self setPreference:[NSNumber numberWithBool:YES]
    + forKey:TWITTER_PREFERENCE_EVER_LOADED_TIMELINE
    + group:TWITTER_PREFERENCE_GROUP_UPDATES];
    }
    }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterProfileStatusUpdates) {
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    -
    - NSMutableArray *profileArray = [[listContact profileArray] mutableCopy];
    -
    - AILogWithSignature(@"%@ Updating statuses for profile, user %@", self, listContact);
    -
    - for (NSDictionary *update in statuses) {
    - NSAttributedString *message;
    - NSDictionary *retweet = [update valueForKey:TWITTER_STATUS_RETWEET];
    - NSString *text = [update objectForKey:TWITTER_STATUS_TEXT];
    -
    - if (retweet && [retweet isKindOfClass:[NSDictionary class]]) {
    - text = [NSString stringWithFormat:@"RT @%@: %@",
    - [[retweet objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID],
    - [retweet objectForKey:TWITTER_STATUS_TEXT]];
    - }
    -
    - message = [self parseMessage:text
    - tweetID:[update objectForKey:TWITTER_STATUS_ID]
    - userID:listContact.UID
    - inReplyToUser:[update objectForKey:TWITTER_STATUS_REPLY_UID]
    - inReplyToTweetID:[update objectForKey:TWITTER_STATUS_REPLY_ID]];
    -
    - [profileArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:message, KEY_VALUE, nil]];
    - }
    -
    - [listContact setProfileArray:profileArray notify:NotifyNow];
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterSendUpdate) {
    - if (updateAfterSend) {
    - [self periodicUpdate];
    - }
    -
    - if (statuses.count) {
    - [adium.contentController displayEvent:AILocalizedString(@"Tweet successfully sent.", nil)
    - ofType:@"tweet"
    - inChat:self.timelineChat];
    - }
    -
    - for(NSDictionary *update in statuses) {
    - [[NSNotificationCenter defaultCenter] postNotificationName:AITwitterNotificationPostedStatus
    - object:update
    - userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self.timelineChat, @"AIChat", nil]];
    -
    - NSDictionary *retweet = [update valueForKey:TWITTER_STATUS_RETWEET];
    - NSString *text = [[update objectForKey:TWITTER_STATUS_TEXT] stringByEscapingForXMLWithEntities:nil];
    -
    - if (retweet && [retweet isKindOfClass:[NSDictionary class]]) {
    - text = [[NSString stringWithFormat:@"RT @%@: %@",
    - [[retweet objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID],
    - [retweet objectForKey:TWITTER_STATUS_TEXT]] stringByEscapingForXMLWithEntities:nil];
    - }
    -
    - if([[self preferenceForKey:TWITTER_PREFERENCE_UPDATE_GLOBAL group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue] &&
    - (![text hasPrefix:@"@"] || [[self preferenceForKey:TWITTER_PREFERENCE_UPDATE_GLOBAL_REPLIES group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue])) {
    - AIStatus *availableStatus = [AIStatus statusOfType:AIAvailableStatusType];
    -
    - availableStatus.statusMessage = [NSAttributedString stringWithString:text];
    - [adium.statusController setActiveStatusState:availableStatus];
    - }
    - }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterFavoriteYes ||
    - [self requestTypeForRequestID:identifier] == AITwitterFavoriteNo) {
    - AIChat *timelineChat = self.timelineChat;
    -
    - for (NSDictionary *status in statuses) {
    - NSString *message;
    -
    - // Use HTML for the status message since it's just easier to localize that way.
    -
    - if ([self requestTypeForRequestID:identifier] == AITwitterFavoriteYes) {
    - message = AILocalizedString(@"The <a href=\"%@\">requested tweet</a> by <a href=\"%@\">%@</a> is now a favorite.", nil);
    - } else {
    - message = AILocalizedString(@"The <a href=\"%@\">requested tweet</a> by <a href=\"%@\">%@</a> is no longer a favorite.", nil);
    - }
    -
    - NSString *userID = [[status objectForKey:TWITTER_STATUS_USER] objectForKey:TWITTER_STATUS_UID];
    -
    -
    - message = [NSString stringWithFormat:message,
    - [self addressForLinkType:AITwitterLinkStatus
    - userID:userID
    - statusID:[status objectForKey:TWITTER_STATUS_ID]
    - context:nil],
    - [self addressForLinkType:AITwitterLinkUserPage
    - userID:userID
    - statusID:nil
    - context:nil],
    - userID];
    -
    - NSAttributedString *attributedMessage = [[AIHTMLDecoder decoder] decodeHTML:message withDefaultAttributes:nil];
    -
    - AIContentEvent *content = [AIContentEvent eventInChat:timelineChat
    - withSource:nil
    - destination:self
    - date:[NSDate date]
    - message:attributedMessage
    - withType:@"favorite"];
    -
    - content.postProcessContent = NO;
    - content.coalescingKey = @"favorite";
    -
    - [adium.contentController receiveContentObject:content];
    - }
    }
    -
    - [self clearRequestTypeForRequestID:identifier];
    }
    /*!
    * @brief Direct messages received
    */
    -- (void)directMessagesReceived:(NSArray *)messages forRequest:(NSString *)identifier
    -{
    - if ([self requestTypeForRequestID:identifier] == AITwitterUpdateDirectMessage) {
    +- (void)directMessagesReceived:(NSArray *)messages forRequest:(AITwitterRequestType)identifier
    +{
    + if (identifier == AITwitterUpdateDirectMessage) {
    NSString *lastID = [self preferenceForKey:TWITTER_PREFERENCE_DM_LAST_ID
    group:TWITTER_PREFERENCE_GROUP_UPDATES];
    BOOL nextPageNecessary = (lastID && messages.count >= TWITTER_UPDATE_DM_COUNT);
    // Store the largest tweet ID we find; this will be our "last ID" the next time we run.
    - NSString *largestTweet = [[self dictionaryForRequestID:identifier] objectForKey:@"LargestTweet"];
    + NSString *largestTweet = nil;
    - // The largest ID is first, compare.
    - if (messages.count) {
    - NSString *tweetID = [[messages objectAtIndex:0] objectForKey:TWITTER_DM_ID];
    - if (!largestTweet || [largestTweet compare:tweetID] == NSOrderedAscending) {
    - largestTweet = tweetID;
    - }
    - }
    + if (messages.count)
    + largestTweet = [[messages objectAtIndex:0] objectForKey:TWITTER_DM_ID];
    [queuedDM addObjectsFromArray:messages];
    AILogWithSignature(@"%@ Last ID: %@ Largest Tweet: %@ Next Page Necessary: %d", self, lastID, largestTweet, nextPageNecessary);
    - if(nextPageNecessary) {
    - int nextPage = [[[self dictionaryForRequestID:identifier] objectForKey:@"Page"] intValue] + 1;
    -
    - NSString *requestID = [twitterEngine getDirectMessagesSinceID:lastID
    - startingAtPage:nextPage];
    -
    - AILogWithSignature(@"%@ Pulling additional DM page %d", self, nextPage);
    -
    - if(requestID) {
    - [self setRequestType:AITwitterUpdateDirectMessage
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:nextPage], @"Page",
    - largestTweet, @"LargestTweet", nil]];
    - } else {
    - // Gracefully fail: remove all stored objects.
    - AILogWithSignature(@"%@ Immediate DM pull fail", self);
    - --pendingUpdateCount;
    - [queuedDM removeAllObjects];
    - }
    - } else {
    - --pendingUpdateCount;
    + --pendingUpdateCount;
    +
    + if (largestTweet) {
    + AILogWithSignature(@"%@ Largest DM pulled = %@", self, largestTweet);
    - if (largestTweet) {
    - AILogWithSignature(@"%@ Largest DM pulled = %@", self, largestTweet);
    -
    - [self setPreference:largestTweet
    - forKey:TWITTER_PREFERENCE_DM_LAST_ID
    - group:TWITTER_PREFERENCE_GROUP_UPDATES];
    - }
    -
    - // On first load, don't display any direct messages. Just ge the largest ID.
    - if (queuedDM.count && lastID) {
    - [self displayQueuedUpdatesForRequestType:[self requestTypeForRequestID:identifier]];
    - } else {
    - [queuedDM removeAllObjects];
    - }
    + [self setPreference:largestTweet
    + forKey:TWITTER_PREFERENCE_DM_LAST_ID
    + group:TWITTER_PREFERENCE_GROUP_UPDATES];
    }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterDirectMessageSend) {
    - [queuedOutgoingDM addObjectsFromArray:messages];
    - [self displayQueuedUpdatesForRequestType:AITwitterDirectMessageSend];
    +
    + // On first load, don't display any direct messages. Just ge the largest ID.
    + if (queuedDM.count && lastID) {
    + [self displayQueuedUpdatesForRequestType:identifier];
    + } else {
    + [queuedDM removeAllObjects];
    + }
    }
    -
    - [self clearRequestTypeForRequestID:identifier];
    }
    /*!
    * @brief User information received
    */
    -- (void)userInfoReceived:(NSArray *)userInfo forRequest:(NSString *)identifier
    -{
    - if (([self requestTypeForRequestID:identifier] == AITwitterInitialUserInfo ||
    - [self requestTypeForRequestID:identifier] == AITwitterAddFollow) &&
    - [[self preferenceForKey:TWITTER_PREFERENCE_LOAD_CONTACTS group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue]) {
    +- (void)userInfoReceived:(NSDictionary *)userInfo forRequest:(AITwitterRequestType)identifier
    +{
    + if (identifier == AITwitterInitialUserInfo ||
    + identifier == AITwitterAddFollow) {
    [[AIContactObserverManager sharedManager] delayListObjectNotifications];
    -
    - BOOL nextPageNecessary = NO;
    - long long nextCursor = 0;
    -
    - for (NSDictionary *info in userInfo) {
    - // Iterate users and next_cursor (previous_cursor is not used yet)
    +
    + NSArray *users = [userInfo objectForKey:@"friends"];
    + AILogWithSignature(@"%@ User info pull, Users count: %ld", self, users.count);
    + for (NSDictionary *user in users) {
    + // Iterate users
    + NSString *twitterInfoUID = [user objectForKey:TWITTER_INFO_UID];
    - NSArray *users = [info objectForKey:@"users"];
    -
    - if (users.count > 0) {
    + if (twitterInfoUID) {
    +
    + AIListContact *listContact = [self contactWithUID:twitterInfoUID];
    - AILogWithSignature(@"%@ User info pull, Users count: %ld", self, users.count);
    + // If the user isn't in a group, set them in the Twitter group.
    + if (listContact.countOfRemoteGroupNames == 0) {
    + [listContact addRemoteGroupName:self.timelineGroupName];
    + }
    - for (NSDictionary *user in users) {
    -
    - NSString *twitterInfoUID = [user objectForKey:TWITTER_INFO_UID];
    -
    - if (twitterInfoUID) {
    -
    - AIListContact *listContact = [self contactWithUID:twitterInfoUID];
    -
    - // If the user isn't in a group, set them in the Twitter group.
    - if (listContact.countOfRemoteGroupNames == 0) {
    - [listContact addRemoteGroupName:self.timelineGroupName];
    - }
    -
    - // Grab the Twitter display name and set it as the remote alias.
    - if (![[listContact valueForProperty:@"serverDisplayName"] isEqualToString:[user objectForKey:TWITTER_INFO_DISPLAY_NAME]]) {
    - [listContact setServersideAlias:[user objectForKey:TWITTER_INFO_DISPLAY_NAME]
    - silently:silentAndDelayed];
    - }
    -
    - // Grab the user icon and set it as their serverside icon.
    - [self updateUserIcon:[user objectForKey:TWITTER_INFO_ICON] forContact:listContact];
    -
    - // Set the user as available.
    - [listContact setStatusWithName:nil
    - statusType:AIAvailableStatusType
    - notify:NotifyLater];
    -
    - // Set the user's status message to their current twitter status text
    - NSString *statusText = [[user objectForKey:TWITTER_INFO_STATUS] objectForKey:TWITTER_INFO_STATUS_TEXT];
    - if (!statusText) //nil if they've never tweeted
    - statusText = @"";
    - [listContact setStatusMessage:[NSAttributedString stringWithString:[statusText stringByUnescapingFromXMLWithEntities:nil]] notify:NotifyLater];
    -
    - // Set the user as online.
    - [listContact setOnline:YES notify:NotifyLater silently:silentAndDelayed];
    -
    - [listContact notifyOfChangedPropertiesSilently:silentAndDelayed];
    - AILogWithSignature(@"%@ User info pull, Next page necessary: %d", self, nextPageNecessary);
    - }
    - }
    - }
    -
    - NSString *nextCursorString = [info objectForKey:TWITTER_INFO_NEXT_CURSOR];
    -
    - if (([nextCursorString length] > 0) && self.supportsCursors) {
    - nextCursor = [nextCursorString longLongValue];
    - nextPageNecessary = (nextCursor > 0) ? YES:NO;
    - } else if (!self.supportsCursors) {
    - nextPageNecessary = (userInfo.count >= 100);
    + // Grab the Twitter display name and set it as the remote alias.
    + if (![[listContact valueForProperty:@"serverDisplayName"] isEqualToString:[user objectForKey:TWITTER_INFO_DISPLAY_NAME]]) {
    + [listContact setServersideAlias:[user objectForKey:TWITTER_INFO_DISPLAY_NAME]
    + silently:silentAndDelayed];
    + }
    +
    + // Grab the user icon and set it as their serverside icon.
    + [self updateUserIcon:[user objectForKey:TWITTER_INFO_ICON] forContact:listContact];
    +
    + // Set the user as available.
    + [listContact setStatusWithName:nil
    + statusType:AIAvailableStatusType
    + notify:NotifyLater];
    +
    + // Set the user's status message to their current twitter status text
    + NSString *statusText = [[user objectForKey:TWITTER_INFO_STATUS] objectForKey:TWITTER_INFO_STATUS_TEXT];
    + if (!statusText) //nil if they've never tweeted
    + statusText = @"";
    + [listContact setStatusMessage:[NSAttributedString stringWithString:[statusText stringByUnescapingFromXMLWithEntities:nil]] notify:NotifyLater];
    +
    + // Set the user as online.
    + [listContact setOnline:YES notify:NotifyLater silently:silentAndDelayed];
    +
    + [listContact notifyOfChangedPropertiesSilently:silentAndDelayed];
    }
    }
    [[AIContactObserverManager sharedManager] endListObjectNotificationsDelay];
    -
    - if (nextPageNecessary) {
    - if (self.supportsCursors) {
    - NSString *requestID = [twitterEngine getRecentlyUpdatedFriendsFor:self.UID startingAtCursor:nextCursor];
    - if (requestID) {
    - [self setRequestType:AITwitterInitialUserInfo
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLongLong:nextCursor]
    - forKey:@"Cursor"]];
    - } else {
    - [self setLastDisconnectionError:AILocalizedString(@"Unable to retrieve user list [additional fail]", "Message when a (vital) twitter request to retrieve the follow list fails")];
    - [self didDisconnect];
    - }
    - } else {
    - int nextPage = [[[self dictionaryForRequestID:identifier] objectForKey:@"Page"] intValue] + 1;
    - NSString *requestID = [twitterEngine getRecentlyUpdatedFriendsFor:self.UID startingAtPage:nextPage];
    - if (requestID) {
    - [self setRequestType:AITwitterInitialUserInfo
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:nextPage]
    - forKey:@"Page"]];
    - } else {
    - [self setLastDisconnectionError:AILocalizedString(@"Unable to retrieve user list [additional fail]", "Message when a (vital) twitter request to retrieve the follow list fails")];
    - [self didDisconnect];
    - }
    - }
    - AILogWithSignature(@"%@ Pulling additional user info page", self);
    -
    - } else if ([self boolValueForProperty:@"isConnecting"]) {
    - // Trigger our normal update routine.
    - [self didConnect];
    - }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterProfileUserInfo) {
    - NSDictionary *thisUserInfo = [userInfo objectAtIndex:0];
    + } else if (identifier == AITwitterValidateCredentials ||
    + identifier == AITwitterProfileSelf) {
    + [self filterAndSetUID:[userInfo objectForKey:TWITTER_INFO_UID]];
    - if (thisUserInfo) {
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    -
    - NSArray *keyNames = [NSArray arrayWithObjects:@"name", @"location", @"description", @"url", @"friends_count", @"followers_count", @"statuses_count", nil];
    - NSArray *readableNames = [NSArray arrayWithObjects:AILocalizedString(@"Name", nil), AILocalizedString(@"Location", nil),
    - AILocalizedString(@"Biography", nil), AILocalizedString(@"Website", nil), AILocalizedString(@"Following", nil),
    - AILocalizedString(@"Followers", nil), AILocalizedString(@"Updates", nil), nil];
    -
    - NSMutableArray *profileArray = [NSMutableArray array];
    -
    - for (NSUInteger idx = 0; idx < keyNames.count; idx++) {
    - NSString *keyName = [keyNames objectAtIndex:idx];
    - NSString *unattributedValue = [thisUserInfo objectForKey:keyName];
    -
    - if(![unattributedValue isEqualToString:@""]) {
    - NSString *readableName = [readableNames objectAtIndex:idx];
    - NSAttributedString *value;
    -
    - if([keyName isEqualToString:@"friends_count"]) {
    - value = [NSAttributedString attributedStringWithLinkLabel:unattributedValue
    - linkDestination:[self addressForLinkType:AITwitterLinkFriends userID:listContact.UID statusID:nil context:nil]];
    - } else if ([keyName isEqualToString:@"followers_count"]) {
    - value = [NSAttributedString attributedStringWithLinkLabel:unattributedValue
    - linkDestination:[self addressForLinkType:AITwitterLinkFollowers userID:listContact.UID statusID:nil context:nil]];
    - } else if ([keyName isEqualToString:@"statuses_count"]) {
    - value = [NSAttributedString attributedStringWithLinkLabel:unattributedValue
    - linkDestination:[self addressForLinkType:AITwitterLinkUserPage userID:listContact.UID statusID:nil context:nil]];
    - } else {
    - value = [NSAttributedString stringWithString:unattributedValue];
    - }
    -
    - [profileArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:readableName, KEY_KEY, value, KEY_VALUE, nil]];
    - }
    - }
    -
    - AILogWithSignature(@"%@ Updating profileArray for user %@", self, listContact);
    -
    - [listContact setProfileArray:profileArray notify:NotifyNow];
    -
    - // Grab their statuses.
    - NSString *requestID = [twitterEngine getUserTimelineFor:listContact.UID since:nil startingAtPage:0 count:TWITTER_UPDATE_USER_INFO_COUNT];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterProfileStatusUpdates
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:listContact forKey:@"ListContact"]];
    - }
    - }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterValidateCredentials ||
    - [self requestTypeForRequestID:identifier] == AITwitterProfileSelf) {
    - for (NSDictionary *info in userInfo) {
    - NSString *requestID = [twitterEngine getImageAtURL:[info objectForKey:TWITTER_INFO_ICON]];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterSelfUserIconPull
    - forRequestID:requestID
    - withDictionary:nil];
    - }
    -
    - [self filterAndSetUID:[info objectForKey:TWITTER_INFO_UID]];
    -
    - if ([info objectForKey:@"name"]) {
    - [self setPreference:[[NSAttributedString stringWithString:[info objectForKey:@"name"]] dataRepresentation]
    - forKey:KEY_ACCOUNT_DISPLAY_NAME
    - group:GROUP_ACCOUNT_STATUS];
    - }
    -
    - [self setValue:[info objectForKey:@"name"] forProperty:@"Profile Name" notify:NotifyLater];
    - [self setValue:[info objectForKey:@"url"] forProperty:@"Profile URL" notify:NotifyLater];
    - [self setValue:[info objectForKey:@"location"] forProperty:@"Profile Location" notify:NotifyLater];
    - [self setValue:[info objectForKey:@"description"] forProperty:@"Profile Description" notify:NotifyLater];
    - [self notifyOfChangedPropertiesSilently:NO];
    + if ([userInfo objectForKey:@"name"]) {
    + [self setPreference:[[NSAttributedString stringWithString:[userInfo objectForKey:@"name"]] dataRepresentation]
    + forKey:KEY_ACCOUNT_DISPLAY_NAME
    + group:GROUP_ACCOUNT_STATUS];
    }
    -
    - if([self requestTypeForRequestID:identifier] == AITwitterValidateCredentials) {
    - // Our UID is definitely set; grab our friends.
    -
    - if ([[self preferenceForKey:TWITTER_PREFERENCE_LOAD_CONTACTS group:TWITTER_PREFERENCE_GROUP_UPDATES] boolValue]) {
    - // If we load our follows as contacts, do so now.
    -
    - // Delay updates on initial login.
    - [self silenceAllContactUpdatesForInterval:18.0];
    - // Grab our user list.
    - NSString *requestID = [twitterEngine getRecentlyUpdatedFriendsFor:self.UID startingAtCursor:-1];
    -
    - if (requestID) {
    - [self setRequestType:AITwitterInitialUserInfo
    - forRequestID:requestID
    - withDictionary:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:-1] forKey:@"Cursor"]];
    - } else {
    - [self setLastDisconnectionError:AILocalizedString(@"Unable to retrieve user list", nil)];
    - [self didDisconnect];
    - }
    - } else {
    - // If we don't load follows as contacts, we've finished connecting (fast, wasn't it?)
    - [self didConnect];
    - }
    - }
    - } else if ([self requestTypeForRequestID:identifier] == AITwitterNotificationEnable ||
    - [self requestTypeForRequestID:identifier] == AITwitterNotificationDisable) {
    - BOOL enableNotification = ([self requestTypeForRequestID:identifier] == AITwitterNotificationEnable);
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    -
    - for (NSDictionary *info in userInfo) {
    - [adium.interfaceController handleMessage:(enableNotification ?
    - AILocalizedString(@"Notifications Enabled", nil) :
    - AILocalizedString(@"Notifications Disabled", nil))
    - withDescription:[NSString stringWithFormat:(enableNotification ?
    - AILocalizedString(@"You will now receive device notifications for %@.", nil) :
    - AILocalizedString(@"You will no longer receive device notifications for %@.", nil)),
    - listContact.UID]
    - withWindowTitle:(enableNotification ?
    - AILocalizedString(@"Notifications Enabled", nil) :
    - AILocalizedString(@"Notifications Disabled", nil))];
    - }
    - }
    -
    - [self clearRequestTypeForRequestID:identifier];
    -}
    -
    -/*!
    - * @brief Miscellaneous information received
    - */
    -- (void)miscInfoReceived:(NSArray *)miscInfo forRequest:(NSString *)identifier
    -{
    - if([self requestTypeForRequestID:identifier] == AITwitterRateLimitStatus) {
    - NSDictionary *rateLimit = [miscInfo objectAtIndex:0];
    - NSDate *resetDate = [NSDate dateWithTimeIntervalSince1970:[[rateLimit objectForKey:TWITTER_RATE_LIMIT_RESET_SECONDS] intValue]];
    -
    - [adium.interfaceController handleMessage:AILocalizedString(@"Current Twitter rate limit", "Message in the rate limit status window")
    - withDescription:[NSString stringWithFormat:AILocalizedString(@"You have %d/%d more requests for %@.", "The first %d is the number of requests, the second is the total number of requests per hour. The %@ is the duration of time until the count resets."),
    - [[rateLimit objectForKey:TWITTER_RATE_LIMIT_REMAINING] intValue],
    - [[rateLimit objectForKey:TWITTER_RATE_LIMIT_HOURLY_LIMIT] intValue],
    - [NSDateFormatter stringForTimeInterval:[resetDate timeIntervalSinceNow]
    - showingSeconds:YES
    - abbreviated:YES
    - approximated:NO]]
    - withWindowTitle:AILocalizedString(@"Rate Limit Status", nil)];
    - }
    -
    - [self clearRequestTypeForRequestID:identifier];
    -}
    -
    -/*!
    - * @brief Requested image received
    - */
    -- (void)imageReceived:(NSImage *)image forRequest:(NSString *)identifier
    -{
    - if([self requestTypeForRequestID:identifier] == AITwitterUserIconPull) {
    - AIListContact *listContact = [[self dictionaryForRequestID:identifier] objectForKey:@"ListContact"];
    -
    - AILogWithSignature(@"%@ Updated user icon for %@", self, listContact);
    -
    - [listContact setServersideIconData:[image TIFFRepresentation]
    - notify:NotifyLater];
    -
    - [listContact setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    - } else if([self requestTypeForRequestID:identifier] == AITwitterSelfUserIconPull) {
    - AILogWithSignature(@"Updated self icon for %@", self);
    -
    - // Set a property so we don't re-send thie image we're just now downloading.
    - [self setValue:[NSNumber numberWithBool:YES] forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    -
    - [self setPreference:[NSNumber numberWithBool:YES]
    - forKey:KEY_USE_USER_ICON
    - group:GROUP_ACCOUNT_STATUS];
    + [self setValue:[userInfo objectForKey:@"name"] forProperty:@"Profile Name" notify:NotifyLater];
    + [self setValue:[userInfo objectForKey:@"url"] forProperty:@"Profile URL" notify:NotifyLater];
    + [self setValue:[userInfo objectForKey:@"location"] forProperty:@"Profile Location" notify:NotifyLater];
    + [self setValue:[userInfo objectForKey:@"description"] forProperty:@"Profile Description" notify:NotifyLater];
    - [self setPreference:[image TIFFRepresentation]
    - forKey:KEY_USER_ICON
    - group:GROUP_ACCOUNT_STATUS];
    + NSString *imageURL = [userInfo objectForKey:TWITTER_INFO_ICON];
    + NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
    + [NSURLConnection sendAsynchronousRequest:imageRequest
    + queue:[NSOperationQueue currentQueue]
    + completionHandler:^(NSURLResponse *resp, NSData *data, NSError *error) {
    + NSImage *image = [[NSImage alloc] initWithData:data];
    +
    + if (image) {
    + AILogWithSignature(@"Updated self icon for %@", self);
    +
    + // Set a property so that we don't re-send the image we're just now downloading.
    + [self setValue:[NSNumber numberWithBool:YES] forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    +
    + [self setPreference:[NSNumber numberWithBool:YES]
    + forKey:KEY_USE_USER_ICON
    + group:GROUP_ACCOUNT_STATUS];
    +
    + [self setPreference:[image TIFFRepresentation]
    + forKey:KEY_USER_ICON
    + group:GROUP_ACCOUNT_STATUS];
    +
    + [self notifyOfChangedPropertiesSilently:NO];
    +
    + [self setValue:nil forProperty:TWITTER_PROPERTY_REQUESTED_USER_ICON notify:NotifyNever];
    + } else {
    + [self requestFailed:AITwitterSelfUserIconPull withError:error userInfo:nil];
    + }
    + }];
    }
    -
    - [self clearRequestTypeForRequestID:identifier];
    }
    @end
    --- a/Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,53 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "AITwitterAccount.h"
    -
    -typedef enum {
    - AIOAuthStepFailure = 0,
    -
    - AIOAuthStepStart,
    - AIOAuthStepRequestToken,
    - AIOAuthStepVerifyingRequest,
    - AIOAuthStepAccessToken,
    -} AIOAuthSetupStep;
    -
    -
    -@protocol AITwitterAccountOAuthSetupDelegate;
    -
    -@interface AITwitterAccountOAuthSetup : NSObject {
    - id delegate;
    -
    - AITwitterAccount *account;
    -
    - OAConsumer *consumer;
    - OAToken *requestToken;
    -
    - __unsafe_unretained NSString *verifier;
    -}
    -
    -- (id)initWithDelegate:(id <AITwitterAccountOAuthSetupDelegate>)inDelegate
    - forAccount:(AITwitterAccount *)inAccount;
    -
    -@property (assign, nonatomic) NSString *verifier;
    -
    -- (void)beginSetup;
    -- (void)fetchAccessToken;
    -@end
    -
    -@protocol AITwitterAccountOAuthSetupDelegate
    -- (void)OAuthSetup:(AITwitterAccountOAuthSetup *)setup changedToStep:(AIOAuthSetupStep)setupStep withToken:(OAToken *)token responseBody:(NSString *)body;
    -@end
    --- a/Plugins/Twitter Plugin/AITwitterAccountOAuthSetup.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,149 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "AITwitterAccountOAuthSetup.h"
    -
    -#import <OAuthConsumer/OAuthConsumer.h>
    -
    -@interface AITwitterAccountOAuthSetup ()
    -- (void)requestTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data;
    -- (void)requestTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error;
    -
    -- (void)accessTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data;
    -- (void)accessTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error;
    -@end
    -
    -@implementation AITwitterAccountOAuthSetup
    -
    -- (id)initWithDelegate:(id <AITwitterAccountOAuthSetupDelegate>)inDelegate
    - forAccount:(AITwitterAccount *)inAccount
    -{
    - if ((self = [super init])) {
    - delegate = inDelegate;
    - account = inAccount;
    - }
    -
    - return self;
    -}
    -
    -#pragma mark Requesting
    -- (void)beginSetup
    -{
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepStart withToken:nil responseBody:nil];
    -
    - consumer = [[OAConsumer alloc] initWithKey:account.consumerKey
    - secret:account.secretKey];
    -
    - NSURL *url = [NSURL URLWithString:account.tokenRequestURL];
    -
    - OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url
    - consumer:consumer
    - token:nil
    - realm:nil
    - signatureProvider:nil];
    -
    - [request setHTTPMethod:@"POST"];
    -
    - OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:request
    - delegate:self
    - didFinishSelector:@selector(requestTokenTicket:didFinishWithData:)
    - didFailSelector:@selector(requestTokenTicket:didFailWithError:)];
    -
    - [fetcher start];
    -}
    -
    -@synthesize verifier;
    -
    -- (void)fetchAccessToken
    -{
    - if (!requestToken) {
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepFailure withToken:nil responseBody:nil];
    - }
    -
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepVerifyingRequest withToken:nil responseBody:nil];
    -
    - NSURL *url = [NSURL URLWithString:account.tokenAccessURL];
    -
    - OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url
    - consumer:consumer
    - token:requestToken
    - realm:nil
    - signatureProvider:nil];
    - [request setHTTPMethod:@"POST"];
    -
    - if (verifier) {
    - NSString *verifierString = [NSString stringWithFormat:@"oauth_verifier=%@", verifier];
    -
    - [request setHTTPBody:[verifierString dataUsingEncoding:NSUTF8StringEncoding]];
    - }
    -
    - OAAsynchronousDataFetcher *fetcher = [OAAsynchronousDataFetcher asynchronousFetcherWithRequest:request
    - delegate:self
    - didFinishSelector:@selector(accessTokenTicket:didFinishWithData:)
    - didFailSelector:@selector(accessTokenTicket:didFailWithError:)];
    -
    - [fetcher start];
    -}
    -
    -#pragma mark Request token processing
    -- (void)requestTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data {
    - if (ticket.didSucceed) {
    - NSString *responseBody = [[NSString alloc] initWithData:data
    - encoding:NSUTF8StringEncoding];
    -
    - requestToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody];
    -
    - [delegate OAuthSetup:self
    - changedToStep:AIOAuthStepRequestToken
    - withToken:requestToken
    - responseBody:responseBody];
    - } else {
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepFailure withToken:nil responseBody:nil];
    - AILogWithSignature(@"%@ failure in request token", account);
    - }
    -}
    -
    -- (void)requestTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error {
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepFailure withToken:nil responseBody:nil];
    - AILogWithSignature(@"%@ failure: %@", account, error);
    -}
    -
    -#pragma mark Access token processing
    -- (void)accessTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data {
    - if (ticket.didSucceed) {
    - NSString *responseBody = [[NSString alloc] initWithData:data
    - encoding:NSUTF8StringEncoding];
    -
    - OAToken *accessToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody];
    -
    - [delegate OAuthSetup:self
    - changedToStep:AIOAuthStepAccessToken
    - withToken:accessToken
    - responseBody:responseBody];
    -
    - } else {
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepFailure withToken:nil responseBody:nil];
    - AILogWithSignature(@"%@ failure in access token", account);
    - }
    -}
    -
    -
    -- (void)accessTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error {
    - [delegate OAuthSetup:self changedToStep:AIOAuthStepFailure withToken:nil responseBody:nil];
    - AILogWithSignature(@"%@ failure: %@", account, error);
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/AITwitterAccountViewController.h Wed Mar 20 01:55:36 2013 +0100
    +++ b/Plugins/Twitter Plugin/AITwitterAccountViewController.h Wed Mar 20 12:02:05 2013 -0400
    @@ -15,9 +15,9 @@
    */
    #import <Adium/AIAccountViewController.h>
    -#import "AITwitterAccountOAuthSetup.h"
    +#import "STTwitterOAuth.h"
    -@interface AITwitterAccountViewController : AIAccountViewController <AITwitterAccountOAuthSetupDelegate> {
    +@interface AITwitterAccountViewController : AIAccountViewController {
    // Setup - OAuth
    IBOutlet NSTabView *tabView_authenticationType;
    IBOutlet NSTabViewItem *tabViewItem_basicAuthentication;
    @@ -49,8 +49,7 @@
    IBOutlet NSTextField *textField_description;
    // OAuth setup
    - AITwitterAccountOAuthSetup *OAuthSetup;
    - AIOAuthSetupStep OAuthSetupStep;
    + STTwitterOAuth *OAuthSetup;
    }
    @end
    --- a/Plugins/Twitter Plugin/AITwitterAccountViewController.m Wed Mar 20 01:55:36 2013 +0100
    +++ b/Plugins/Twitter Plugin/AITwitterAccountViewController.m Wed Mar 20 12:02:05 2013 -0400
    @@ -112,17 +112,63 @@
    [checkBox_updateGlobalIncludeReplies setEnabled:[checkBox_updateGlobalStatus state]];
    if(sender == button_OAuthStart) {
    - if (OAuthSetupStep == AIOAuthStepRequestToken) {
    - OAuthSetup.verifier = textField_OAuthVerifier.stringValue;
    - textField_OAuthVerifier.stringValue = @"";
    + void (^errorBlock)(NSError *);
    + errorBlock = ^(NSError *error) {
    + // Failed in some way. sad. :(
    + AILog(@"There was an error authorizing with Twitter: %@", error);
    +
    + [self setStatusText:AILocalizedString(@"An error occured while trying to gain access. Please try again.", nil)
    + withColor:[NSColor redColor]
    + buttonEnabled:YES
    + buttonText:BUTTON_TEXT_ALLOW_ACCESS];
    +
    + [textField_OAuthVerifier setHidden:YES];
    + [progressIndicator setHidden:YES];
    + [progressIndicator stopAnimation:nil];
    - [OAuthSetup fetchAccessToken];
    + [self completedOAuthSetup];
    + };
    +
    + if ([textField_OAuthVerifier.stringValue isEqualToString:@""]) {
    + OAuthSetup = [STTwitterOAuth twitterServiceWithConsumerName:@"Adium"
    + consumerKey:[(AITwitterAccount *)account consumerKey]
    + consumerSecret:[(AITwitterAccount *)account secretKey]];
    + [OAuthSetup postTokenRequest:^(NSURL *url, NSString *oauthToken) {
    + // We have a request token, ask user to authorize.
    + [[NSWorkspace sharedWorkspace] openURL:url];
    +
    + [self setStatusText:AILocalizedString(@"You must allow Adium access to your account in the browser window which just opened. When you have done so, enter the PIN code in the field above.", nil)
    + withColor:nil
    + buttonEnabled:YES
    + buttonText:AILocalizedString(@"I've allowed Adium access", nil)];
    +
    + [textField_OAuthVerifier setHidden:NO];
    + [progressIndicator setHidden:YES];
    + [progressIndicator stopAnimation:nil];
    + }
    + oauthCallback:nil
    + errorBlock:errorBlock];
    } else {
    -
    - OAuthSetup = [[AITwitterAccountOAuthSetup alloc] initWithDelegate:self
    - forAccount:(AITwitterAccount *)account];
    -
    - [OAuthSetup beginSetup];
    + [OAuthSetup postAccessTokenRequestWithPIN:textField_OAuthVerifier.stringValue
    + successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
    + // We have an access token, hoorah!
    +
    + textField_password.stringValue = [NSString stringWithFormat:@"oauth_token=%@&oauth_token_secret=%@", oauthToken, oauthTokenSecret];
    +
    + [self setStatusText:AILocalizedString(@"Success! Adium now has access to your account. Click OK below.", nil)
    + withColor:nil
    + buttonEnabled:NO
    + buttonText:nil];
    +
    + [textField_OAuthVerifier setHidden:YES];
    + [progressIndicator setHidden:YES];
    + [progressIndicator stopAnimation:nil];
    +
    + [account setLastDisconnectionError:nil];
    + [account setValue:[NSNumber numberWithBool:YES] forProperty:@"Reconnect After Edit" notify:NotifyNever];
    +
    + [self completedOAuthSetup];
    + } errorBlock:errorBlock];
    }
    }
    }
    @@ -255,91 +301,9 @@
    }
    }
    -#pragma mark OAuth setup delegate
    -
    -- (void)OAuthSetup:(AITwitterAccountOAuthSetup *)setup
    - changedToStep:(AIOAuthSetupStep)setupStep
    - withToken:(OAToken *)token
    - responseBody:(NSString *)responseBody
    -{
    - AILogWithSignature(@"Step %u", setupStep);
    -
    - OAuthSetupStep = setupStep;
    -
    - switch (OAuthSetupStep) {
    - case AIOAuthStepStart:
    - case AIOAuthStepVerifyingRequest:
    - // Just starting or verifying a token, fetching a request token
    - [self setStatusText:@""
    - withColor:nil
    - buttonEnabled:YES
    - buttonText:nil];
    -
    - [textField_OAuthVerifier setHidden:YES];
    - [progressIndicator setHidden:NO];
    - [progressIndicator startAnimation:nil];
    -
    - break;
    -
    - case AIOAuthStepRequestToken:
    - // We have a request token, ask user to authorize.
    - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?oauth_token=%@",
    - ((AITwitterAccount *)account).tokenAuthorizeURL,
    - token.key]]];
    -
    - [self setStatusText:AILocalizedString(@"You must allow Adium access to your account in the browser window which just opened. When you have done so, enter the PIN code in the field above.", nil)
    - withColor:nil
    - buttonEnabled:YES
    - buttonText:AILocalizedString(@"I've allowed Adium access", nil)];
    -
    - [textField_OAuthVerifier setHidden:NO];
    - [progressIndicator setHidden:YES];
    - [progressIndicator stopAnimation:nil];
    -
    - break;
    -
    - case AIOAuthStepAccessToken:
    - // We have an access token, hoorah!
    - textField_password.stringValue = responseBody;
    -
    - [self setStatusText:AILocalizedString(@"Success! Adium now has access to your account. Click OK below.", nil)
    - withColor:nil
    - buttonEnabled:NO
    - buttonText:nil];
    -
    - [textField_OAuthVerifier setHidden:YES];
    - [progressIndicator setHidden:YES];
    - [progressIndicator stopAnimation:nil];
    -
    - [account setLastDisconnectionError:nil];
    - [account setValue:[NSNumber numberWithBool:YES] forProperty:@"Reconnect After Edit" notify:NotifyNever];
    -
    - [self completedOAuthSetup];
    -
    - break;
    -
    - case AIOAuthStepFailure:
    - // Failed in some way. sad. :(
    -
    - [self setStatusText:AILocalizedString(@"An error occured while trying to gain access. Please try again.", nil)
    - withColor:[NSColor redColor]
    - buttonEnabled:YES
    - buttonText:BUTTON_TEXT_ALLOW_ACCESS];
    -
    - [textField_OAuthVerifier setHidden:YES];
    - [progressIndicator setHidden:YES];
    - [progressIndicator stopAnimation:nil];
    -
    - [self completedOAuthSetup];
    -
    - break;
    - }
    -}
    -
    - (void)completedOAuthSetup
    {
    - OAuthSetup = nil;
    - OAuthSetupStep = AIOAuthStepFailure;
    + OAuthSetup = nil;
    }
    @end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,142 +0,0 @@
    -//
    -// MGTwitterEngine.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 10/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterEngineDelegate.h"
    -#import "MGTwitterParserDelegate.h"
    -
    -#import <OAuthConsumer/OAConsumer.h>
    -#import <OAuthConsumer/OAMutableURLRequest.h>
    -
    -@interface MGTwitterEngine : NSObject <MGTwitterParserDelegate> {
    - __weak NSObject <MGTwitterEngineDelegate> *_delegate;
    - NSString *_username;
    - NSString *_password;
    - NSMutableDictionary *_connections; // MGTwitterHTTPURLConnection objects
    - NSString *_clientName;
    - NSString *_clientVersion;
    - NSString *_clientURL;
    - NSString *_clientSourceToken;
    - NSString *_APIDomain;
    - BOOL _secureConnection;
    - BOOL _clearsCookies;
    -
    - // Adium OAuth Additions
    - BOOL _useOAuth;
    - OAToken *_accessToken;
    - OAConsumer *_consumer;
    -}
    -
    -// Constructors
    -+ (MGTwitterEngine *)twitterEngineWithDelegate:(NSObject *)delegate;
    -- (MGTwitterEngine *)initWithDelegate:(NSObject *)delegate;
    -
    -// Configuration and Accessors
    -+ (NSString *)version; // returns the version of MGTwitterEngine
    -- (NSString *)username;
    -- (NSString *)password;
    -- (void)setUsername:(NSString *)username password:(NSString *)password;
    -- (NSString *)clientName; // see README.txt for info on clientName/Version/URL/SourceToken
    -- (NSString *)clientVersion;
    -- (NSString *)clientURL;
    -- (NSString *)clientSourceToken;
    -- (void)setClientName:(NSString *)name version:(NSString *)version URL:(NSString *)url token:(NSString *)token;
    -- (NSString *)APIDomain;
    -- (void)setAPIDomain:(NSString *)domain;
    -- (BOOL)usesSecureConnection; // YES = uses HTTPS, default is YES
    -- (void)setUsesSecureConnection:(BOOL)flag;
    -- (BOOL)clearsCookies; // YES = deletes twitter.com cookies when setting username/password, default is NO (see README.txt)
    -- (void)setClearsCookies:(BOOL)flag;
    -
    -// Connection methods
    -- (NSUInteger)numberOfConnections;
    -- (NSArray *)connectionIdentifiers;
    -- (void)closeConnection:(NSString *)identifier;
    -- (void)closeAllConnections;
    -
    -// Utility methods
    -/// Note: the -getImageAtURL: method works for any image URL, not just Twitter images.
    -// It does not require authentication, and is provided here for convenience.
    -// As with the Twitter API methods below, it returns a unique connection identifier.
    -// Retrieved images are sent to the delegate via the -imageReceived:forRequest: method.
    -- (NSString *)getImageAtURL:(NSString *)urlString;
    -
    -// ======================================================================================================
    -// Twitter API methods
    -// See Twitter API docs at: http://apiwiki.twitter.com/REST+API+Documentation
    -// All methods below return a unique connection identifier.
    -// ======================================================================================================
    -
    -// Account methods
    -- (NSString *)checkUserCredentials;
    -- (NSString *)endUserSession;
    -- (NSString *)enableUpdatesFor:(NSString *)username; // i.e. follow
    -- (NSString *)disableUpdatesFor:(NSString *)username; // i.e. no longer follow
    -- (NSString *)isUser:(NSString *)username1 receivingUpdatesFor:(NSString *)username2; // i.e. test if username1 follows username2 (not the reverse)
    -- (NSString *)enableNotificationsFor:(NSString *)username;
    -- (NSString *)disableNotificationsFor:(NSString *)username;
    -- (NSString *)getRateLimitStatus;
    -- (NSString *)setLocation:(NSString *)location;
    -- (NSString *)setNotificationsDeliveryMethod:(NSString *)method;
    -- (NSString *)block:(NSString *)username;
    -- (NSString *)unblock:(NSString *)username;
    -- (NSString *)testService;
    -- (NSString *)getDowntimeSchedule;
    -
    -// Retrieving updates
    -- (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum;
    -- (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)count; // max 200
    -- (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)count; // max 200
    -- (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date count:(int)numUpdates; // max 200
    -- (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)numUpdates; // max 200
    -- (NSString *)getUserTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)numUpdates; // max 200
    -- (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum; // 80 per page
    -- (NSString *)getPublicTimelineSinceID:(NSString *)updateID;
    -- (NSString *)getRepliesSinceID:(NSString *)updateID startingAtPage:(int)pageNum;
    -- (NSString *)getRepliesStartingAtPage:(int)pageNum; // sent TO this user
    -- (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pageNum;
    -- (NSString *)getUpdate:(NSString *)updateID;
    -
    -// Retrieving direct messages
    -- (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum; // sent TO this user
    -- (NSString *)getDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum; // sent TO this user
    -- (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum; // sent BY this user
    -- (NSString *)getSentDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum; // sent BY this user
    -
    -// Retrieving user information
    -- (NSString *)getUserInformationFor:(NSString *)username;
    -- (NSString *)getUserInformationForEmail:(NSString *)email;
    -- (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(int)pageNum;
    -- (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtCursor:(long long)cursorNum;
    -- (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag;
    -- (NSString *)getFeaturedUsers;
    -
    -- (NSString *)updateProfileImage:(NSData *)profileImage;
    -- (NSString *)updateProfileName:(NSString *)name
    - email:(NSString *)email
    - url:(NSString *)url
    - location:(NSString *)location
    - description:(NSString *)description;
    -
    -// Sending and editing updates
    -- (NSString *)sendUpdate:(NSString *)status;
    -- (NSString *)sendUpdate:(NSString *)status inReplyTo:(NSString *)updateID;
    -- (NSString *)deleteUpdate:(NSString *)updateID; // this user must be the AUTHOR
    -- (NSString *)markUpdate:(NSString *)updateID asFavorite:(BOOL)flag;
    -- (NSString *)retweetUpdate:(NSString *)updateID;
    -
    -// Sending and editing direct messages
    -- (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username;
    -- (NSString *)deleteDirectMessage:(NSString *)updateID; // this user must be the RECIPIENT
    -
    -// Adium OAuth additions
    -@property (readwrite, nonatomic) BOOL useOAuth;
    -@property (readwrite, nonatomic) OAConsumer *consumer;
    -@property (readwrite, nonatomic) OAToken *accessToken;
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1612 +0,0 @@
    -//
    -// MGTwitterEngine.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 10/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngine.h"
    -#import "MGTwitterHTTPURLConnection.h"
    -
    -#import "NSData+Base64.h"
    -
    -#define USE_LIBXML 0
    -
    -#import "MGTwitterStatusesParser.h"
    -#import "MGTwitterUsersParser.h"
    -#import "MGTwitterMessagesParser.h"
    -#import "MGTwitterMiscParser.h"
    -
    -#define TWITTER_DOMAIN @"api.twitter.com/1"
    -#define HTTP_POST_METHOD @"POST"
    -#define HTTP_MULTIPART_METHOD @"MULTIPART" //adium
    -#define MULTIPART_FORM_BOUNDARY @"bf5faadd239c17e35f91e6dafe1d2f96" //adium
    -#define MAX_MESSAGE_LENGTH 140 // Twitter recommends tweets of max 140 chars
    -#define MAX_LOCATION_LENGTH 31
    -
    -#define DEFAULT_CLIENT_NAME @"MGTwitterEngine"
    -#define DEFAULT_CLIENT_VERSION @"1.0"
    -#define DEFAULT_CLIENT_URL @"http://mattgemmell.com/source"
    -#define DEFAULT_CLIENT_TOKEN @"mgtwitterengine"
    -
    -#define URL_REQUEST_TIMEOUT 50.0 // Twitter usually fails quickly if it's going to fail at all.
    -#define DEFAULT_TWEET_COUNT 20
    -
    -
    -@interface MGTwitterEngine (PrivateMethods)
    -
    -// Utility methods
    -- (NSDateFormatter *)_HTTPDateFormatter;
    -- (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed;
    -- (NSDate *)_HTTPToDate:(NSString *)httpDate;
    -- (NSString *)_dateToHTTP:(NSDate *)date;
    -- (NSString *)_encodeString:(NSString *)string;
    -
    -// Connection/Request methods
    -- (NSString *)_sendRequestWithMethod:(NSString *)method
    - path:(NSString *)path
    - queryParameters:(NSDictionary *)params
    - body:(id)body
    - requestType:(MGTwitterRequestType)requestType
    - responseType:(MGTwitterResponseType)responseType;
    -
    -// Parsing methods
    -- (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection;
    -
    -// Delegate methods
    -- (BOOL) _isValidDelegateForSelector:(SEL)selector;
    -
    -@end
    -
    -
    -@implementation MGTwitterEngine
    -
    -
    -#pragma mark Constructors
    -
    -
    -+ (MGTwitterEngine *)twitterEngineWithDelegate:(NSObject *)theDelegate
    -{
    - return [[MGTwitterEngine alloc] initWithDelegate:theDelegate];
    -}
    -
    -
    -- (MGTwitterEngine *)initWithDelegate:(NSObject <MGTwitterEngineDelegate> *)newDelegate
    -{
    - if ((self = [super init])) {
    - _delegate = newDelegate; // deliberately weak reference
    - _connections = [[NSMutableDictionary alloc] initWithCapacity:0];
    - _clientName = DEFAULT_CLIENT_NAME;
    - _clientVersion = DEFAULT_CLIENT_VERSION;
    - _clientURL = DEFAULT_CLIENT_URL;
    - _clientSourceToken = DEFAULT_CLIENT_TOKEN;
    - _APIDomain = TWITTER_DOMAIN;
    - _secureConnection = YES;
    - _clearsCookies = NO;
    - }
    -
    - return self;
    -}
    -
    -
    -- (void)dealloc
    -{
    - _delegate = nil;
    -
    - [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
    -}
    -
    -
    -#pragma mark Configuration and Accessors
    -
    -
    -+ (NSString *)version
    -{
    - // 1.0.0 = 22 Feb 2008
    - // 1.0.1 = 26 Feb 2008
    - // 1.0.2 = 04 Mar 2008
    - // 1.0.3 = 04 Mar 2008
    - // 1.0.4 = 11 Apr 2008
    - // 1.0.5 = 06 Jun 2008
    - // 1.0.6 = 05 Aug 2008
    - // 1.0.7 = 28 Sep 2008
    - // 1.0.8 = 01 Oct 2008
    - return @"1.0.8";
    -}
    -
    -
    -- (NSString *)username
    -{
    - return _username;
    -}
    -
    -
    -- (NSString *)password
    -{
    - return _password;
    -}
    -
    -
    -- (void)setUsername:(NSString *)newUsername password:(NSString *)newPassword
    -{
    - // Set new credentials.
    - _username = newUsername;
    - _password = newPassword;
    -
    - if ([self clearsCookies]) {
    - // Remove all cookies for twitter, to ensure next connection uses new credentials.
    - NSString *urlString = [NSString stringWithFormat:@"%@://%@",
    - (_secureConnection) ? @"https" : @"http",
    - _APIDomain];
    - NSURL *url = [NSURL URLWithString:urlString];
    -
    - NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    - NSEnumerator *enumerator = [[cookieStorage cookiesForURL:url] objectEnumerator];
    - NSHTTPCookie *cookie = nil;
    - while ((cookie = [enumerator nextObject])) {
    - [cookieStorage deleteCookie:cookie];
    - }
    - }
    -}
    -
    -
    -- (NSString *)clientName
    -{
    - return _clientName;
    -}
    -
    -
    -- (NSString *)clientVersion
    -{
    - return _clientVersion;
    -}
    -
    -
    -- (NSString *)clientURL
    -{
    - return _clientURL;
    -}
    -
    -
    -- (NSString *)clientSourceToken
    -{
    - return _clientSourceToken;
    -}
    -
    -
    -- (void)setClientName:(NSString *)name version:(NSString *)version URL:(NSString *)url token:(NSString *)token;
    -{
    - _clientName = name;
    - _clientVersion = version;
    - _clientURL = url;
    - _clientSourceToken = token;
    -}
    -
    -
    -- (NSString *)APIDomain
    -{
    - return _APIDomain;
    -}
    -
    -
    -- (void)setAPIDomain:(NSString *)domain
    -{
    - if (!domain || [domain length] == 0) {
    - _APIDomain = TWITTER_DOMAIN;
    - } else {
    - _APIDomain = domain;
    - }
    -}
    -
    -
    -- (BOOL)usesSecureConnection
    -{
    - return _secureConnection;
    -}
    -
    -
    -- (void)setUsesSecureConnection:(BOOL)flag
    -{
    - _secureConnection = flag;
    -}
    -
    -
    -- (BOOL)clearsCookies
    -{
    - return _clearsCookies;
    -}
    -
    -
    -- (void)setClearsCookies:(BOOL)flag
    -{
    - _clearsCookies = flag;
    -}
    -
    -
    -#pragma mark Connection methods
    -
    -
    -- (NSUInteger)numberOfConnections
    -{
    - return [_connections count];
    -}
    -
    -
    -- (NSArray *)connectionIdentifiers
    -{
    - return [_connections allKeys];
    -}
    -
    -
    -- (void)closeConnection:(NSString *)identifier
    -{
    - MGTwitterHTTPURLConnection *connection = [_connections objectForKey:identifier];
    - if (connection) {
    - [connection cancel];
    - [_connections removeObjectForKey:identifier];
    - }
    -}
    -
    -
    -- (void)closeAllConnections
    -{
    - [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
    - [_connections removeAllObjects];
    -}
    -
    -
    -#pragma mark Utility methods
    -
    -
    -- (NSDateFormatter *)_HTTPDateFormatter
    -{
    - // Returns a formatter for dates in HTTP format (i.e. RFC 822, updated by RFC 1123).
    - // e.g. "Sun, 06 Nov 1994 08:49:37 GMT"
    - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    - //[dateFormatter setDateFormat:@"%a, %d %b %Y %H:%M:%S GMT"]; // won't work with -init, which uses new (unicode) format behaviour.
    - [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss GMT"];
    - return dateFormatter;
    -}
    -
    -
    -- (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed
    -{
    - // Append base if specified.
    - NSMutableString *str = [NSMutableString stringWithCapacity:0];
    - if (base) {
    - [str appendString:base];
    - }
    -
    - // Append each name-value pair.
    - if (params) {
    - int i;
    - NSArray *names = [params allKeys];
    - for (i = 0; i < [names count]; i++) {
    - if (i == 0 && prefixed) {
    - [str appendString:@"?"];
    - } else if (i > 0) {
    - [str appendString:@"&"];
    - }
    - NSString *name = [names objectAtIndex:i];
    - [str appendString:[NSString stringWithFormat:@"%@=%@",
    - name, [self _encodeString:[params objectForKey:name]]]];
    - }
    - }
    -
    - return str;
    -}
    -
    -
    -- (NSDate *)_HTTPToDate:(NSString *)httpDate
    -{
    - NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
    - return [dateFormatter dateFromString:httpDate];
    -}
    -
    -
    -- (NSString *)_dateToHTTP:(NSDate *)date
    -{
    - NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
    - return [dateFormatter stringFromDate:date];
    -}
    -
    -
    -- (NSString *)_encodeString:(NSString *)string
    -{
    - NSString *result = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
    - (__bridge CFStringRef)string,
    - NULL,
    - (CFStringRef)@";/?:@&=$+{}<>,",
    - kCFStringEncodingUTF8);
    - return result;
    -}
    -
    -
    -- (NSString *)getImageAtURL:(NSString *)urlString
    -{
    - // This is a method implemented for the convenience of the client,
    - // allowing asynchronous downloading of users' Twitter profile images.
    - NSString *encodedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    - NSURL *url = [NSURL URLWithString:encodedUrlString];
    - if (!url) {
    - return nil;
    - }
    -
    - // Construct an NSMutableURLRequest for the URL and set appropriate request method.
    - NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
    - cachePolicy:NSURLRequestReloadIgnoringCacheData
    - timeoutInterval:URL_REQUEST_TIMEOUT];
    - [theRequest setHTTPShouldHandleCookies:NO];
    -
    - // Create a connection using this request, with the default timeout and caching policy,
    - // and appropriate Twitter request and response types for parsing and error reporting.
    - MGTwitterHTTPURLConnection *connection;
    - connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
    - delegate:self
    - requestType:MGTwitterImageRequest
    - responseType:MGTwitterImage];
    -
    - if (!connection) {
    - return nil;
    - } else {
    - [_connections setObject:connection forKey:[connection identifier]];
    - }
    -
    - return [connection identifier];
    -}
    -
    -
    -#pragma mark Request sending methods
    -
    -#define SET_AUTHORIZATION_IN_HEADER 1
    -
    -/* See Adium Additions/Changes below—oauth support */
    -#if 0
    -- (NSString *)_sendRequestWithMethod:(NSString *)method
    - path:(NSString *)path
    - queryParameters:(NSDictionary *)params
    - body:(id)body
    - requestType:(MGTwitterRequestType)requestType
    - responseType:(MGTwitterResponseType)responseType
    -{
    - // Construct appropriate URL string.
    - NSString *fullPath = path;
    - if (params) {
    - fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
    - }
    -
    -#if SET_AUTHORIZATION_IN_HEADER
    - NSString *urlString = [NSString stringWithFormat:@"%@://%@/%@",
    - (_secureConnection) ? @"https" : @"http",
    - _APIDomain, fullPath];
    -#else
    - NSString *urlString = [NSString stringWithFormat:@"%@://%@:%@@%@/%@",
    - (_secureConnection) ? @"https" : @"http",
    - [self _encodeString:_username], [self _encodeString:_password],
    - _APIDomain, fullPath];
    -#endif
    -
    - NSURL *finalURL = [NSURL URLWithString:urlString];
    - if (!finalURL) {
    - return nil;
    - }
    -
    - // Construct an NSMutableURLRequest for the URL and set appropriate request method.
    - NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:finalURL
    - cachePolicy:NSURLRequestReloadIgnoringCacheData
    - timeoutInterval:URL_REQUEST_TIMEOUT];
    - [theRequest setHTTPShouldHandleCookies:NO];
    - if(method && [method isEqualToString:HTTP_MULTIPART_METHOD]) {
    - method = HTTP_POST_METHOD;
    - [theRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY] forHTTPHeaderField:@"Content-type"];
    - }
    -
    - if (method) {
    - [theRequest setHTTPMethod:method];
    - }
    -
    - [theRequest setHTTPShouldHandleCookies:NO];
    -
    - // Set headers for client information, for tracking purposes at Twitter.
    - [theRequest setValue:_clientName forHTTPHeaderField:@"X-Twitter-Client"];
    - [theRequest setValue:_clientVersion forHTTPHeaderField:@"X-Twitter-Client-Version"];
    - [theRequest setValue:_clientURL forHTTPHeaderField:@"X-Twitter-Client-URL"];
    -
    -#if SET_AUTHORIZATION_IN_HEADER
    - if ([self username] && [self password]) {
    - // Set header for HTTP Basic authentication explicitly, to avoid problems with proxies and other intermediaries
    - NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]];
    - NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    - NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];
    - [theRequest setValue:authValue forHTTPHeaderField:@"Authorization"];
    - }
    -#endif
    -
    - // Set the request body if this is a POST request.
    - BOOL isPOST = (method && [method isEqualToString:HTTP_POST_METHOD]);
    -
    - if (isPOST) {
    - // Set request body, if specified (hopefully so), with 'source' parameter if appropriate.
    - if([body isKindOfClass:[NSString class]]) {
    - NSString *finalBody = @"";
    - if (body) {
    - finalBody = [finalBody stringByAppendingString:body];
    - }
    - if (_clientSourceToken) {
    - finalBody = [finalBody stringByAppendingString:[NSString stringWithFormat:@"%@source=%@",
    - (body) ? @"&" : @"?" ,
    - _clientSourceToken]];
    - }
    -
    - if (finalBody) {
    - [theRequest setHTTPBody:[finalBody dataUsingEncoding:NSUTF8StringEncoding]];
    - }
    - } else if ([body isKindOfClass:[NSData class]]) {
    - [theRequest setHTTPBody:body];
    - }
    - }
    -
    -
    - // Create a connection using this request, with the default timeout and caching policy,
    - // and appropriate Twitter request and response types for parsing and error reporting.
    - MGTwitterHTTPURLConnection *connection;
    - connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
    - delegate:self
    - requestType:requestType
    - responseType:responseType];
    -
    - if (!connection) {
    - return nil;
    - } else {
    - [_connections setObject:connection forKey:[connection identifier]];
    - [connection release];
    - }
    -
    - return [connection identifier];
    -}
    -#endif
    -
    -#pragma mark Parsing methods
    -
    -
    -- (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection
    -{
    - NSString *identifier = [[connection identifier] copy];
    - NSData *xmlData = [[connection data] copy];
    - MGTwitterRequestType requestType = [connection requestType];
    - MGTwitterResponseType responseType = [connection responseType];
    -
    -#if USE_LIBXML
    - NSURL *URL = [connection URL];
    -
    - switch (responseType) {
    - case MGTwitterStatuses:
    - case MGTwitterStatus:
    - [MGTwitterStatusesLibXMLParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType URL:URL];
    - break;
    - case MGTwitterUsers:
    - case MGTwitterUser:
    - [MGTwitterUsersLibXMLParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType URL:URL];
    - break;
    - case MGTwitterDirectMessages:
    - case MGTwitterDirectMessage:
    - [MGTwitterMessagesLibXMLParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType URL:URL];
    - break;
    - case MGTwitterMiscellaneous:
    - [MGTwitterMiscLibXMLParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType URL:URL];
    - break;
    - default:
    - break;
    - }
    -#else
    - // Determine which type of parser to use.
    - switch (responseType) {
    - case MGTwitterStatuses:
    - case MGTwitterStatus:
    - [MGTwitterStatusesParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType];
    - break;
    - case MGTwitterUsers:
    - case MGTwitterUser:
    - [MGTwitterUsersParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType];
    - break;
    - case MGTwitterDirectMessages:
    - case MGTwitterDirectMessage:
    - [MGTwitterMessagesParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType];
    - break;
    - case MGTwitterMiscellaneous:
    - [MGTwitterMiscParser parserWithXML:xmlData delegate:self
    - connectionIdentifier:identifier requestType:requestType
    - responseType:responseType];
    - break;
    - default:
    - break;
    - }
    -#endif
    -}
    -
    -#pragma mark Delegate methods
    -
    -- (BOOL) _isValidDelegateForSelector:(SEL)selector
    -{
    - return ((_delegate != nil) && [_delegate respondsToSelector:selector]);
    -}
    -
    -#pragma mark MGTwitterParserDelegate methods
    -
    -
    -- (void)parsingSucceededForRequest:(NSString *)identifier
    - ofResponseType:(MGTwitterResponseType)responseType
    - withParsedObjects:(NSArray *)parsedObjects
    -{
    - // Forward appropriate message to _delegate, depending on responseType.
    - switch (responseType) {
    - case MGTwitterStatuses:
    - case MGTwitterStatus:
    - if ([self _isValidDelegateForSelector:@selector(statusesReceived:forRequest:)])
    - [_delegate statusesReceived:parsedObjects forRequest:identifier];
    - break;
    - case MGTwitterUsers:
    - case MGTwitterUser:
    - if ([self _isValidDelegateForSelector:@selector(userInfoReceived:forRequest:)])
    - [_delegate userInfoReceived:parsedObjects forRequest:identifier];
    - break;
    - case MGTwitterDirectMessages:
    - case MGTwitterDirectMessage:
    - if ([self _isValidDelegateForSelector:@selector(directMessagesReceived:forRequest:)])
    - [_delegate directMessagesReceived:parsedObjects forRequest:identifier];
    - break;
    - case MGTwitterMiscellaneous:
    - if ([self _isValidDelegateForSelector:@selector(miscInfoReceived:forRequest:)])
    - [_delegate miscInfoReceived:parsedObjects forRequest:identifier];
    - break;
    - default:
    - break;
    - }
    -}
    -
    -
    -- (void)parsingFailedForRequest:(NSString *)requestIdentifier
    - ofResponseType:(MGTwitterResponseType)responseType
    - withError:(NSError *)error
    -{
    - if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
    - [_delegate requestFailed:requestIdentifier withError:error];
    -}
    -
    -
    -#pragma mark NSURLConnection delegate methods
    -
    -
    -- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
    -{
    - if ([challenge previousFailureCount] == 0 && ![challenge proposedCredential] && !_useOAuth && _password && _username) {
    - NSURLCredential *credential = [NSURLCredential credentialWithUser:_username password:_password
    - persistence:NSURLCredentialPersistenceForSession];
    - [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    - } else {
    - [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
    - }
    -}
    -
    -
    -- (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    -{
    - // This method is called when the server has determined that it has enough information to create the NSURLResponse.
    - // it can be called multiple times, for example in the case of a redirect, so each time we reset the data.
    - [connection resetDataLength];
    -
    - // Get response code.
    - NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
    - NSInteger statusCode = [resp statusCode];
    -
    - if (statusCode >= 400) {
    - // Assume failure, and report to delegate.
    - NSError *error = [NSError errorWithDomain:@"HTTP" code:statusCode userInfo:nil];
    - if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
    - [_delegate requestFailed:[connection identifier] withError:error];
    -
    - // Destroy the connection.
    - [connection cancel];
    - [_connections removeObjectForKey:[connection identifier]];
    -
    - } else if (statusCode == 304 || [connection responseType] == MGTwitterGeneric) {
    - // Not modified, or generic success.
    - if ([self _isValidDelegateForSelector:@selector(requestSucceeded:)])
    - [_delegate requestSucceeded:[connection identifier]];
    - if (statusCode == 304) {
    - [self parsingSucceededForRequest:[connection identifier]
    - ofResponseType:[connection responseType]
    - withParsedObjects:[NSArray array]];
    - }
    -
    - // Destroy the connection.
    - [connection cancel];
    - [_connections removeObjectForKey:[connection identifier]];
    - }
    -
    - if (NO) {
    - // Display headers for debugging.
    - NSHTTPURLResponse *noResp = (NSHTTPURLResponse *)response;
    - NSLog(@"(%ld) [%@]:\r%@",
    - (long)[noResp statusCode],
    - [NSHTTPURLResponse localizedStringForStatusCode:[noResp statusCode]],
    - [noResp allHeaderFields]);
    - }
    -}
    -
    -
    -- (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveData:(NSData *)data
    -{
    - // Append the new data to the receivedData.
    - [connection appendData:data];
    -}
    -
    -
    -- (void)connection:(MGTwitterHTTPURLConnection *)connection didFailWithError:(NSError *)error
    -{
    - // Inform delegate.
    - if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
    - [_delegate requestFailed:[connection identifier] withError:error];
    -
    - // Release the connection.
    - [_connections removeObjectForKey:[connection identifier]];
    -}
    -
    -
    -- (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
    -{
    - // Inform delegate.
    - if ([self _isValidDelegateForSelector:@selector(requestSucceeded:)])
    - [_delegate requestSucceeded:[connection identifier]];
    -
    - NSData *receivedData = [connection data];
    - if (receivedData) {
    - if (NO) {
    - // Dump data as string for debugging.
    - NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
    - NSLog(@"Succeeded! Received %lu bytes of data:\r\r%@", (unsigned long)[receivedData length], dataString);
    - }
    -
    - if (NO) {
    - // Dump XML to file for debugging.
    - NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
    - [dataString writeToFile:[@"~/Desktop/twitter_messages.xml" stringByExpandingTildeInPath]
    - atomically:NO encoding:NSUnicodeStringEncoding error:NULL];
    - }
    -
    - if ([connection responseType] == MGTwitterImage) {
    - // Create image from data.
    -#if TARGET_OS_IPHONE
    - UIImage *image = [[[UIImage alloc] initWithData:[connection data]] autorelease];
    -#else
    - NSImage *image = [[NSImage alloc] initWithData:[connection data]];
    -#endif
    -
    - // Inform delegate.
    - if ([self _isValidDelegateForSelector:@selector(imageReceived:forRequest:)])
    - [_delegate imageReceived:image forRequest:[connection identifier]];
    - } else {
    - // Parse XML appropriately.
    - [self _parseXMLForConnection:connection];
    - }
    - }
    -
    - // Release the connection.
    - [_connections removeObjectForKey:[connection identifier]];
    -}
    -
    -
    -#pragma mark -
    -#pragma mark Twitter API methods
    -#pragma mark -
    -
    -
    -#pragma mark Account methods
    -
    -- (NSString *)endUserSession
    -{
    - NSString *path = @"account/end_session"; // deliberately no format specified
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -
    -- (NSString *)enableUpdatesFor:(NSString *)username
    -{
    - // i.e. follow
    - if (!username) {
    - return nil;
    - }
    - NSString *path = [NSString stringWithFormat:@"friendships/create/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)disableUpdatesFor:(NSString *)username
    -{
    - // i.e. no longer follow
    - if (!username) {
    - return nil;
    - }
    - NSString *path = [NSString stringWithFormat:@"friendships/destroy/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)isUser:(NSString *)username1 receivingUpdatesFor:(NSString *)username2
    -{
    - if (!username1 || !username2) {
    - return nil;
    - }
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - [params setObject:username1 forKey:@"user_a"];
    - [params setObject:username2 forKey:@"user_b"];
    -
    - NSString *path = @"friendships/exists.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterMiscellaneous];
    -}
    -
    -
    -- (NSString *)enableNotificationsFor:(NSString *)username
    -{
    - if (!username) {
    - return nil;
    - }
    - NSString *path = [NSString stringWithFormat:@"notifications/follow/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)disableNotificationsFor:(NSString *)username
    -{
    - if (!username) {
    - return nil;
    - }
    - NSString *path = [NSString stringWithFormat:@"notifications/leave/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)getRateLimitStatus
    -{
    - NSString *path = @"account/rate_limit_status.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterMiscellaneous];
    -}
    -
    -
    -- (NSString *)setLocation:(NSString *)location
    -{
    - if (!location) {
    - return nil;
    - }
    -
    - NSString *path = @"account/update_location.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - [params setObject:location forKey:@"location"];
    - NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
    - queryParameters:nil body:body
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -
    -- (NSString *)setNotificationsDeliveryMethod:(NSString *)method
    -{
    - NSString *deliveryMethod = method;
    - if (!method || [method length] == 0) {
    - deliveryMethod = @"none";
    - }
    -
    - NSString *path = @"account/update_delivery_device.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (deliveryMethod) {
    - [params setObject:deliveryMethod forKey:@"device"];
    - }
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -
    -- (NSString *)block:(NSString *)username
    -{
    - if (!username) {
    - return nil;
    - }
    -
    - NSString *path = [NSString stringWithFormat:@"blocks/create/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)unblock:(NSString *)username
    -{
    - if (!username) {
    - return nil;
    - }
    -
    - NSString *path = [NSString stringWithFormat:@"blocks/destroy/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)testService
    -{
    - NSString *path = @"help/test.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -
    -- (NSString *)getDowntimeSchedule
    -{
    - NSString *path = @"help/downtime_schedule.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterMiscellaneous];
    -}
    -
    -
    -#pragma mark Retrieving updates
    -
    -
    -- (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum
    -{
    - // Included for backwards-compatibility.
    - return [self getFollowedTimelineFor:username since:date startingAtPage:pageNum count:0]; // zero means default
    -}
    -
    -
    -- (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)count
    -{
    - NSString *path = @"statuses/home_timeline.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (date) {
    - [params setObject:[self _dateToHTTP:date] forKey:@"since"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
    - }
    - int tweetCount = DEFAULT_TWEET_COUNT;
    - if (count > 0) {
    - tweetCount = count;
    - }
    - [params setObject:[NSString stringWithFormat:@"%d", tweetCount] forKey:@"count"];
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)count
    -{
    - NSString *path = @"statuses/home_timeline.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
    - }
    - int tweetCount = DEFAULT_TWEET_COUNT;
    - if (count > 0) {
    - tweetCount = count;
    - }
    - [params setObject:[NSString stringWithFormat:@"%d", tweetCount] forKey:@"count"];
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date count:(int)numUpdates
    -{
    - // Included for backwards-compatibility.
    - return [self getUserTimelineFor:username since:date startingAtPage:0 count:numUpdates];
    -}
    -
    -
    -- (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)numUpdates
    -{
    - NSString *path = @"statuses/user_timeline.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (date) {
    - [params setObject:[self _dateToHTTP:date] forKey:@"since"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    - if (numUpdates > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
    - }
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getUserTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)numUpdates
    -{
    - NSString *path = @"statuses/user_timeline.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    - if (numUpdates > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
    - }
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum
    -{
    - NSString *path = @"account/archive.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getPublicTimelineSinceID:(NSString *)updateID
    -{
    - NSString *path = @"statuses/public_timeline.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -- (NSString *)getRepliesStartingAtPage:(int)pageNum
    -{
    - NSString *path = @"statuses/mentions.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterRepliesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pageNum
    -{
    - NSString *path = @"favorites.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    - if (username) {
    - path = [NSString stringWithFormat:@"favorites/%@.xml", username];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -
    -- (NSString *)getUpdate:(NSString *)updateID
    -{
    - NSString *path = [NSString stringWithFormat:@"statuses/show/%@.xml", updateID];
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterStatusesRequest
    - responseType:MGTwitterStatus];
    -}
    -
    -
    -#pragma mark Retrieving direct messages
    -
    -
    -- (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
    -{
    - NSString *path = @"direct_messages.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (date) {
    - [params setObject:[self _dateToHTTP:date] forKey:@"since"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterDirectMessagesRequest
    - responseType:MGTwitterDirectMessages];
    -}
    -
    -
    -- (NSString *)getDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
    -{
    - NSString *path = @"direct_messages.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterDirectMessagesRequest
    - responseType:MGTwitterDirectMessages];
    -}
    -
    -
    -- (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
    -{
    - NSString *path = @"direct_messages/sent.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (date) {
    - [params setObject:[self _dateToHTTP:date] forKey:@"since"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterDirectMessagesRequest
    - responseType:MGTwitterDirectMessages];
    -}
    -
    -
    -- (NSString *)getSentDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
    -{
    - NSString *path = @"direct_messages/sent.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterDirectMessagesRequest
    - responseType:MGTwitterDirectMessages];
    -}
    -
    -
    -#pragma mark Retrieving user information
    -
    -
    -- (NSString *)getUserInformationFor:(NSString *)username
    -{
    - if (!username) {
    - return nil;
    - }
    - NSString *path = [NSString stringWithFormat:@"users/show/%@.xml", username];
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)getUserInformationForEmail:(NSString *)email
    -{
    - NSString *path = @"users/show.xml";
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (email) {
    - [params setObject:email forKey:@"email"];
    - } else {
    - return nil;
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(int)pageNum
    -{
    - NSString *path = @"statuses/friends.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUsers];
    -}
    -
    -- (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtCursor:(long long)cursorNum
    -{
    - NSString *path = @"statuses/friends.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (username) {
    - path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
    - }
    - if (cursorNum >= -1) {
    - [params setObject:[NSString stringWithFormat:@"%lld", cursorNum] forKey:@"cursor"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUsers];
    -}
    -
    -
    -- (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag
    -{
    - NSString *path = @"statuses/followers.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (!flag) {
    - [params setObject:@"true" forKey:@"lite"]; // slightly bizarre, but correct.
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUsers];
    -}
    -
    -
    -- (NSString *)getFeaturedUsers
    -{
    - NSString *path = @"statuses/featured.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterUserInfoRequest
    - responseType:MGTwitterUsers];
    -}
    -
    -
    -#pragma mark Sending and editing updates
    -
    -
    -- (NSString *)sendUpdate:(NSString *)status
    -{
    - return [self sendUpdate:status inReplyTo:0];
    -}
    -
    -
    -- (NSString *)sendUpdate:(NSString *)status inReplyTo:(NSString *)updateID
    -{
    - if (!status) {
    - return nil;
    - }
    -
    - NSString *path = @"statuses/update.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - [params setObject:status forKey:@"status"];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"in_reply_to_status_id"];
    - }
    - NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
    - queryParameters:nil body:body
    - requestType:MGTwitterStatusSend
    - responseType:MGTwitterStatus];
    -}
    -
    -
    -- (NSString *)deleteUpdate:(NSString *)updateID
    -{
    - NSString *path = [NSString stringWithFormat:@"statuses/destroy/%@.xml", updateID];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -
    -- (NSString *)markUpdate:(NSString *)updateID asFavorite:(BOOL)flag
    -{
    - NSString *path = [NSString stringWithFormat:@"favorites/%@/%@.xml",
    - (flag) ? @"create" : @"destroy" ,
    - updateID];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterStatus];
    -}
    -
    -- (NSString *)retweetUpdate:(NSString *)updateID
    -{
    - NSString *path = [NSString stringWithFormat:@"statuses/retweet/%@.xml", updateID];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterStatus];
    -}
    -
    -#pragma mark Sending and editing direct messages
    -
    -
    -- (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username
    -{
    - if (!message || !username) {
    - return nil;
    - }
    -
    - NSString *path = @"direct_messages/new.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - [params setObject:message forKey:@"text"];
    - [params setObject:username forKey:@"user"];
    - NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
    - queryParameters:nil body:body
    - requestType:MGTwitterDirectMessageSend
    - responseType:MGTwitterDirectMessage];
    -}
    -
    -
    -- (NSString *)deleteDirectMessage:(NSString *)updateID
    -{
    - NSString *path = [NSString stringWithFormat:@"direct_messages/destroy/%@.xml", updateID];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterGeneric];
    -}
    -
    -#pragma mark Adium Additions/Changes
    -
    -#define MAX_NAME_LENGTH 20
    -#define MAX_EMAIL_LENGTH 40
    -#define MAX_URL_LENGTH 100
    -#define MAX_DESCRIPTION_LENGTH 160
    -
    -- (NSString *)checkUserCredentials
    -{
    - NSString *path = @"account/verify_credentials.xml";
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -
    -- (NSString *)getRepliesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
    -{
    - NSString *path = @"statuses/mentions.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    - if (updateID > 0) {
    - [params setObject:updateID forKey:@"since_id"];
    - }
    - if (pageNum > 0) {
    - [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
    - }
    -
    - return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
    - requestType:MGTwitterRepliesRequest
    - responseType:MGTwitterStatuses];
    -}
    -
    -- (NSString *)updateProfileName:(NSString *)name
    - email:(NSString *)email
    - url:(NSString *)url
    - location:(NSString *)location
    - description:(NSString *)description
    -{
    - if (!name && !email && !url && !location && !description) {
    - return nil;
    - }
    -
    - NSString *path = @"account/update_profile.xml";
    -
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    -
    - if (name) {
    - if(name.length > MAX_NAME_LENGTH) {
    - name = [name substringToIndex:MAX_NAME_LENGTH];
    - }
    -
    - [params setObject:name forKey:@"name"];
    - }
    -
    - if (email) {
    - if(email.length > MAX_EMAIL_LENGTH) {
    - email = [email substringToIndex:MAX_EMAIL_LENGTH];
    - }
    -
    - [params setObject:email forKey:@"email"];
    - }
    -
    - if (url) {
    - if(url.length > MAX_URL_LENGTH) {
    - url = [url substringToIndex:MAX_URL_LENGTH];
    - }
    -
    - [params setObject:url forKey:@"url"];
    - }
    -
    - if (location) {
    - if(location.length > MAX_LOCATION_LENGTH) {
    - location = [location substringToIndex:MAX_LOCATION_LENGTH];
    - }
    -
    - [params setObject:location forKey:@"location"];
    - }
    -
    - if (description) {
    - if(description.length > MAX_DESCRIPTION_LENGTH) {
    - description = [description substringToIndex:MAX_DESCRIPTION_LENGTH];
    - }
    -
    - [params setObject:description forKey:@"description"];
    - }
    -
    - NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
    -
    - return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
    - queryParameters:nil body:body
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -- (NSString *)updateProfileImage:(NSData *)profileImage
    -{
    - if (!profileImage || _useOAuth) {
    - return nil;
    - }
    -
    - NSString *path = @"account/update_profile_image.xml";
    -
    - NSMutableData *body = [NSMutableData data];
    - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
    -
    - NSImage *image = [[NSImage alloc] initWithData:profileImage];
    -
    - NSBitmapImageRep *bitmapImageRep = nil;
    - for(NSImageRep *imageRep in image.representations) {
    - if([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
    - bitmapImageRep = (NSBitmapImageRep *)imageRep;
    - }
    - }
    -
    - if(!bitmapImageRep) {
    - return nil;
    - }
    -
    - [body appendData:[[NSString stringWithFormat:@"--%@\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
    - [body appendData:[@"Content-Disposition: form-data; name=\"image\"; filename=\"adium_icon.png\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    - [body appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    - [body appendData:[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]];
    - [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
    -
    - return [self _sendRequestWithMethod:HTTP_MULTIPART_METHOD path:path
    - queryParameters:params body:body
    - requestType:MGTwitterAccountRequest
    - responseType:MGTwitterUser];
    -}
    -
    -#pragma mark Adium OAuth Changes
    -
    -- (NSString *)_sendRequestWithMethod:(NSString *)method
    - path:(NSString *)path
    - queryParameters:(NSDictionary *)params
    - body:(id)body
    - requestType:(MGTwitterRequestType)requestType
    - responseType:(MGTwitterResponseType)responseType
    -{
    - // Construct appropriate URL string.
    - NSString *fullPath = path;
    - if (params) {
    - fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
    - }
    -
    - NSString *urlString = nil;
    -
    - if (!_useOAuth) {
    - #if SET_AUTHORIZATION_IN_HEADER
    - urlString = [NSString stringWithFormat:@"%@://%@/%@",
    - (_secureConnection) ? @"https" : @"http",
    - _APIDomain, fullPath];
    - #else
    - urlString = [NSString stringWithFormat:@"%@://%@:%@@%@/%@",
    - (_secureConnection) ? @"https" : @"http",
    - [self _encodeString:_username], [self _encodeString:_password],
    - _APIDomain, fullPath];
    - #endif
    - } else {
    - urlString = [NSString stringWithFormat:@"%@://%@/%@",
    - (_secureConnection) ? @"https" : @"http",
    - _APIDomain, fullPath];
    - }
    -
    - NSURL *finalURL = [NSURL URLWithString:urlString];
    - if (!finalURL) {
    - return nil;
    - }
    -
    - NSMutableURLRequest *theRequest = nil;
    -
    - if (_useOAuth) {
    - if (!_consumer || !_accessToken) {
    - NSLog(@"No consumer or access token, fail.");
    - return nil;
    - }
    -
    - theRequest = [[OAMutableURLRequest alloc] initWithURL:finalURL
    - consumer:_consumer
    - token:_accessToken
    - realm:nil
    - signatureProvider:nil];
    - } else {
    - // Construct an NSMutableURLRequest for the URL and set appropriate request method.
    - theRequest = [NSMutableURLRequest requestWithURL:finalURL
    - cachePolicy:NSURLRequestReloadIgnoringCacheData
    - timeoutInterval:URL_REQUEST_TIMEOUT];
    - }
    -
    - if(method && [method isEqualToString:HTTP_MULTIPART_METHOD]) {
    - method = HTTP_POST_METHOD;
    - [theRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY] forHTTPHeaderField:@"Content-type"];
    - }
    -
    - if (method) {
    - [theRequest setHTTPMethod:method];
    - }
    -
    - [theRequest setHTTPShouldHandleCookies:NO];
    -
    - // Set headers for client information, for tracking purposes at Twitter.
    - [theRequest setValue:_clientName forHTTPHeaderField:@"X-Twitter-Client"];
    - [theRequest setValue:_clientVersion forHTTPHeaderField:@"X-Twitter-Client-Version"];
    - [theRequest setValue:_clientURL forHTTPHeaderField:@"X-Twitter-Client-URL"];
    -
    -#if SET_AUTHORIZATION_IN_HEADER
    - if (_useOAuth && [self username] && [self password]) {
    - // Set header for HTTP Basic authentication explicitly, to avoid problems with proxies and other intermediaries
    - NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]];
    - NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    - NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];
    - [theRequest setValue:authValue forHTTPHeaderField:@"Authorization"];
    - }
    -#endif
    -
    - // Set the request body if this is a POST request.
    - BOOL isPOST = (method && [method isEqualToString:HTTP_POST_METHOD]);
    -
    - if (isPOST) {
    - // Set request body, if specified (hopefully so), with 'source' parameter if appropriate.
    - if([body isKindOfClass:[NSString class]]) {
    - NSString *finalBody = @"";
    - if (body) {
    - finalBody = [finalBody stringByAppendingString:body];
    - }
    - if (_clientSourceToken) {
    - finalBody = [finalBody stringByAppendingString:[NSString stringWithFormat:@"%@source=%@",
    - (body) ? @"&" : @"?" ,
    - _clientSourceToken]];
    - }
    -
    - if (finalBody) {
    - [theRequest setHTTPBody:[finalBody dataUsingEncoding:NSUTF8StringEncoding]];
    - }
    - } else if ([body isKindOfClass:[NSData class]]) {
    - [theRequest setHTTPBody:body];
    - }
    - }
    -
    - if (_useOAuth) {
    - [(OAMutableURLRequest *)theRequest prepare];
    - }
    -
    - // Create a connection using this request, with the default timeout and caching policy,
    - // and appropriate Twitter request and response types for parsing and error reporting.
    - MGTwitterHTTPURLConnection *connection;
    - connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
    - delegate:self
    - requestType:requestType
    - responseType:responseType];
    -
    - if (!connection) {
    - return nil;
    - } else {
    - [_connections setObject:connection forKey:[connection identifier]];
    - }
    -
    - return [connection identifier];
    -}
    -
    -@synthesize consumer = _consumer, accessToken = _accessToken, useOAuth = _useOAuth;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngineDelegate.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,27 +0,0 @@
    -//
    -// MGTwitterEngineDelegate.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 16/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -@protocol MGTwitterEngineDelegate
    -
    -- (void)requestSucceeded:(NSString *)requestIdentifier;
    -- (void)requestFailed:(NSString *)requestIdentifier withError:(NSError *)error;
    -
    -- (void)statusesReceived:(NSArray *)statuses forRequest:(NSString *)identifier;
    -- (void)directMessagesReceived:(NSArray *)messages forRequest:(NSString *)identifier;
    -- (void)userInfoReceived:(NSArray *)userInfo forRequest:(NSString *)identifier;
    -- (void)miscInfoReceived:(NSArray *)miscInfo forRequest:(NSString *)identifier;
    -
    -#if TARGET_OS_IPHONE
    -- (void)imageReceived:(UIImage *)image forRequest:(NSString *)identifier;
    -#else
    -- (void)imageReceived:(NSImage *)image forRequest:(NSString *)identifier;
    -#endif
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngineGlobalHeader.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,18 +0,0 @@
    -//
    -// MGTwitterEngineGlobalHeader.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 09/08/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -/*
    - This file conditionally includes the correct headers for either Mac OS X or iPhone deployment.
    -*/
    -
    -#if TARGET_OS_IPHONE
    - #import <Foundation/Foundation.h>
    - #import <UIKit/UIKit.h>
    -#else
    - #import <Cocoa/Cocoa.h>
    -#endif
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterHTTPURLConnection.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,37 +0,0 @@
    -//
    -// MGTwitterHTTPURLConnection.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 16/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterRequestTypes.h"
    -
    -@interface MGTwitterHTTPURLConnection : NSURLConnection {
    - NSMutableData *_data; // accumulated data received on this connection
    - MGTwitterRequestType _requestType; // general type of this request, mostly for error handling
    - MGTwitterResponseType _responseType; // type of response data expected (if successful)
    - NSString *_identifier;
    - NSURL *_URL; // the URL used for the connection (needed as a base URL when parsing with libxml)
    -}
    -
    -// Initializer
    -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate
    - requestType:(MGTwitterRequestType)requestType responseType:(MGTwitterResponseType)responseType;
    -
    -// Data helper methods
    -- (void)resetDataLength;
    -- (void)appendData:(NSData *)data;
    -
    -// Accessors
    -- (NSString *)identifier;
    -- (NSData *)data;
    -- (NSURL *)URL;
    -- (MGTwitterRequestType)requestType;
    -- (MGTwitterResponseType)responseType;
    -- (NSString *)description;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterHTTPURLConnection.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,114 +0,0 @@
    -//
    -// MGTwitterHTTPURLConnection.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 16/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterHTTPURLConnection.h"
    -#import "NSString+UUID.h"
    -
    -
    -@implementation MGTwitterHTTPURLConnection
    -
    -
    -#pragma mark Initializer
    -
    -
    -- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate
    - requestType:(MGTwitterRequestType)requestType responseType:(MGTwitterResponseType)responseType
    -{
    - if ((self = [super initWithRequest:request delegate:delegate])) {
    - _data = [[NSMutableData alloc] initWithCapacity:0];
    - _identifier = [NSString stringWithNewUUID];
    - _requestType = requestType;
    - _responseType = responseType;
    - _URL = [request URL];
    - }
    -
    - return self;
    -}
    -
    -
    -#pragma mark Data helper methods
    -
    -
    -- (void)resetDataLength
    -{
    - [_data setLength:0];
    -}
    -
    -
    -- (void)appendData:(NSData *)data
    -{
    - [_data appendData:data];
    -}
    -
    -
    -#pragma mark Accessors
    -
    -
    -- (NSString *)identifier
    -{
    - return _identifier;
    -}
    -
    -
    -- (NSData *)data
    -{
    - return _data;
    -}
    -
    -
    -- (NSURL *)URL
    -{
    - return _URL;
    -}
    -
    -
    -- (MGTwitterRequestType)requestType
    -{
    - return _requestType;
    -}
    -
    -
    -- (MGTwitterResponseType)responseType
    -{
    - return _responseType;
    -}
    -
    -
    -- (NSString *)description
    -{
    - NSString *description = [super description];
    -
    - switch (_requestType) {
    - case MGTwitterStatusesRequest:
    - description = @"Twitter statuses timeline request";
    - break;
    - case MGTwitterDirectMessagesRequest:
    - description = @"Twitter direct messages timeline request";
    - break;
    - case MGTwitterAccountRequest:
    - description = @"Twitter account action request";
    - break;
    - case MGTwitterUserInfoRequest:
    - description = @"Twitter user information request";
    - break;
    - case MGTwitterStatusSend:
    - description = @"Twitter status send";
    - break;
    - case MGTwitterDirectMessageSend:
    - description = @"Twitter direct message send";
    - break;
    - default:
    - description = @"Twitter other action";
    - break;
    - }
    -
    - return [description stringByAppendingFormat:@" (%@)", _identifier];
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMessagesParser.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -//
    -// MGTwitterMessagesParser.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 19/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterStatusesParser.h"
    -
    -@interface MGTwitterMessagesParser : MGTwitterStatusesParser {
    -
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMessagesParser.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,56 +0,0 @@
    -//
    -// MGTwitterMessagesParser.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 19/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterMessagesParser.h"
    -
    -
    -@implementation MGTwitterMessagesParser
    -
    -
    -#pragma mark NSXMLParser delegate methods
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didStartElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
    - attributes:(NSDictionary *)attributeDict
    -{
    - //NSLog(@"Started element: %@ (%@)", elementName, attributeDict);
    - [self setLastOpenedElement:elementName];
    -
    - if ([elementName isEqualToString:@"direct_message"]) {
    - // Make new entry in parsedObjects.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"sender"] || [elementName isEqualToString:@"recipient"]) {
    - // Add an appropriate dictionary to current node.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [currentNode setObject:newNode forKey:elementName];
    - currentNode = newNode;
    - } else if (currentNode) {
    - // Create relevant name-value pair.
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    - }
    -}
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didEndElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    -{
    - [super parser:theParser didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
    -
    - if ([elementName isEqualToString:@"sender"] || [elementName isEqualToString:@"recipient"]) {
    - currentNode = [parsedObjects lastObject];
    - } else if ([elementName isEqualToString:@"direct_message"]) {
    - [self addSource];
    - currentNode = nil;
    - }
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMiscParser.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -//
    -// MGTwitterMiscParser.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 06/06/2008.
    -// Copyright 2008 Instinctive Code. All rights reserved.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterStatusesParser.h"
    -
    -@interface MGTwitterMiscParser : MGTwitterStatusesParser {
    -
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterMiscParser.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,48 +0,0 @@
    -//
    -// MGTwitterMiscParser.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 06/06/2008.
    -// Copyright 2008 Instinctive Code. All rights reserved.
    -//
    -
    -#import "MGTwitterMiscParser.h"
    -
    -
    -@implementation MGTwitterMiscParser
    -
    -
    -#pragma mark NSXMLParser delegate methods
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didStartElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
    - attributes:(NSDictionary *)attributeDict
    -{
    - //NSLog(@"Started element: %@ (%@)", elementName, attributeDict);
    - [self setLastOpenedElement:elementName];
    -
    - if (!currentNode) {
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - }
    -
    - // Create relevant name-value pair.
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    -}
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didEndElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    -{
    - [super parser:theParser didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
    -
    - if ([elementName isEqualToString:@"remaining_hits"]) {
    - NSNumber *hits = [NSNumber numberWithInt:[[currentNode objectForKey:elementName] intValue]];
    - [currentNode setObject:hits forKey:elementName];
    - }
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterParserDelegate.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,23 +0,0 @@
    -//
    -// MGTwitterParserDelegate.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 18/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterRequestTypes.h"
    -
    -@protocol MGTwitterParserDelegate
    -
    -- (void)parsingSucceededForRequest:(NSString *)identifier
    - ofResponseType:(MGTwitterResponseType)responseType
    - withParsedObjects:(NSArray *)parsedObjects;
    -
    -- (void)parsingFailedForRequest:(NSString *)requestIdentifier
    - ofResponseType:(MGTwitterResponseType)responseType
    - withError:(NSError *)error;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterRequestTypes.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,37 +0,0 @@
    -//
    -// MGTwitterEngineDelegate.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 17/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -typedef enum _MGTwitterRequestType {
    - MGTwitterStatusesRequest = 0, // all status requests, excluding replies and direct messages
    - MGTwitterRepliesRequest = 1, // status requests which are specifically for replies
    - MGTwitterDirectMessagesRequest = 2, // all direct message requests, including sent messages
    - MGTwitterAccountRequest = 3, // credentials, session, follow/leave, notifications, favorites, deletions
    - MGTwitterUserInfoRequest = 4, // requests for one or more users' info, including featured users
    - MGTwitterStatusSend = 5, // sending a new status
    - MGTwitterDirectMessageSend = 6, // sending a new direct message
    - MGTwitterImageRequest = 7, // requesting an image
    -} MGTwitterRequestType;
    -
    -typedef enum _MGTwitterResponseType {
    - MGTwitterStatuses = 0, // one or more statuses
    - MGTwitterStatus = 1, // exactly one status
    - MGTwitterUsers = 2, // one or more user's information
    - MGTwitterUser = 3, // info for exactly one user
    - MGTwitterDirectMessages = 4, // one or more direct messages
    - MGTwitterDirectMessage = 5, // exactly one direct message
    - MGTwitterGeneric = 6, // a generic response not requiring parsing
    - MGTwitterMiscellaneous = 8, // a miscellaneous response of key-value pairs
    - MGTwitterImage = 7, // an image
    -} MGTwitterResponseType;
    -
    -// This key is added to each tweet or direct message returned,
    -// with an NSNumber value containing an MGTwitterRequestType.
    -// This is designed to help client applications aggregate updates.
    -#define TWITTER_SOURCE_REQUEST_TYPE @"source_api_request_type"
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -//
    -// MGTwitterStatusesParser.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 11/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterXMLParser.h"
    -
    -@interface MGTwitterStatusesParser : MGTwitterXMLParser {
    -
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterStatusesParser.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,75 +0,0 @@
    -//
    -// MGTwitterStatusesParser.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 11/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterStatusesParser.h"
    -
    -
    -@implementation MGTwitterStatusesParser
    -
    -
    -#pragma mark NSXMLParser delegate methods
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didStartElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
    - attributes:(NSDictionary *)attributeDict
    -{
    - //NSLog(@"Started element: %@ (%@)", elementName, attributeDict);
    - [self setLastOpenedElement:elementName];
    -
    - if ([elementName isEqualToString:@"status"]) {
    - // Make new entry in parsedObjects.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"user"] || [elementName isEqualToString:@"geo"] ||
    - [elementName isEqualToString:@"coordinates"] || [elementName isEqualToString:@"place"] ||
    - [elementName isEqualToString:@"entities"]) {
    - // Add a 'user' dictionary to current node.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [currentNode setObject:newNode forKey:elementName];
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"retweeted_status"]) {
    - // Add a 'retweeted_status' dictionary to current node.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [currentNode setObject:newNode forKey:elementName];
    - currentNode = newNode;
    - } else if (currentNode) {
    - // Create relevant name-value pair.
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    - }
    -}
    -
    -
    -- (void)parser:(NSXMLParser *)theParser foundCharacters:(NSString *)characters
    -{
    - //NSLog(@"Found characters: %@", characters);
    - // Append found characters to value of lastOpenedElement in currentNode.
    - if (lastOpenedElement && currentNode) {
    - [[currentNode objectForKey:lastOpenedElement] appendString:characters];
    - }
    -}
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didEndElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    -{
    - [super parser:theParser didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
    -
    - if ([elementName isEqualToString:@"user"] || [elementName isEqualToString:@"geo"] ||
    - [elementName isEqualToString:@"coordinates"] || [elementName isEqualToString:@"place"] ||
    - [elementName isEqualToString:@"retweeted_status"] || [elementName isEqualToString:@"entities"]) {
    - currentNode = [parsedObjects lastObject];
    - } else if ([elementName isEqualToString:@"status"]) {
    - [self addSource];
    - currentNode = nil;
    - }
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterUsersParser.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,17 +0,0 @@
    -//
    -// MGTwitterUsersParser.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 19/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -#import "MGTwitterStatusesParser.h"
    -
    -@interface MGTwitterUsersParser : MGTwitterStatusesParser {
    - BOOL supportsCursorPaging;
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterUsersParser.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,112 +0,0 @@
    -//
    -// MGTwitterUsersParser.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 19/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterUsersParser.h"
    -
    -
    -@implementation MGTwitterUsersParser
    -
    -
    -#pragma mark NSXMLParser delegate methods
    -
    -
    -- (void)parser:(NSXMLParser *)theParser didStartElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
    - attributes:(NSDictionary *)attributeDict
    -{
    - //NSLog(@"Started element: %@ (%@)", elementName, attributeDict);
    - [self setLastOpenedElement:elementName];
    -
    - if ([elementName isEqualToString:@"users_list"]) {
    - supportsCursorPaging = YES;
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"users"]) {
    - if (!supportsCursorPaging) {
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - }
    - // Create the users mutable array
    - [currentNode setObject:[NSMutableArray arrayWithCapacity:0] forKey:elementName];
    - } else if ([elementName isEqualToString:@"user"]) {
    - // Add the user object into the users array
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    -
    - // Accomodate different type of xml response
    - NSMutableArray *users = [currentNode objectForKey:@"users"];
    -
    - if (users) {
    - [users addObject:newNode];
    - } else {
    - [parsedObjects addObject:newNode];
    - }
    -
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"status"]) {
    - // Add an appropriate dictionary to current node.
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [currentNode setObject:newNode forKey:elementName];
    - currentNode = newNode;
    - } else if ([elementName isEqualToString:@"next_cursor"]) {
    - // Add a new entry for next cursor object
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    - } else if ([elementName isEqualToString:@"previous_cursor"]) {
    - // Add a new entry for previous cursor object
    - NSMutableDictionary *newNode = [NSMutableDictionary dictionaryWithCapacity:0];
    - [parsedObjects addObject:newNode];
    - currentNode = newNode;
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    - } else if (currentNode) {
    - // Create relevant name-value pair.
    - [currentNode setObject:[NSMutableString string] forKey:elementName];
    - }
    -}
    -
    -- (void)parser:(NSXMLParser *)theParser foundCharacters:(NSString *)characters
    -{
    - if (![lastOpenedElement isEqualToString:@"users"] && currentNode) {
    - [[currentNode objectForKey:lastOpenedElement] appendString:characters];
    - }
    -}
    -
    -- (void)parser:(NSXMLParser *)theParser didEndElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    -{
    - [super parser:theParser didEndElement:elementName namespaceURI:namespaceURI qualifiedName:qName];
    -
    - if ([elementName isEqualToString:@"status"]) {
    - NSMutableArray *users = [[parsedObjects lastObject] objectForKey:@"users"];
    -
    - if (users) {
    - currentNode = [users lastObject];
    - } else {
    - currentNode = [parsedObjects lastObject];
    - }
    -
    - currentNode = [parsedObjects lastObject];
    - } else if ([elementName isEqualToString:@"user"]) {
    - [self addSource];
    -
    - NSMutableArray *users = [[parsedObjects lastObject] objectForKey:@"users"];
    -
    - if (users) {
    - currentNode = [parsedObjects lastObject];
    - } else {
    - currentNode = nil;
    - }
    -
    - }
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterXMLParser.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,36 +0,0 @@
    -//
    -// MGTwitterXMLParser.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 18/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -#import "MGTwitterParserDelegate.h"
    -
    -@interface MGTwitterXMLParser : NSObject <NSXMLParserDelegate> {
    - __weak NSObject <MGTwitterParserDelegate> *delegate; // weak ref
    - NSString *identifier;
    - MGTwitterRequestType requestType;
    - MGTwitterResponseType responseType;
    - NSData *xml;
    - NSMutableArray *parsedObjects;
    - NSXMLParser *parser;
    - __weak NSMutableDictionary *currentNode;
    - NSString *lastOpenedElement;
    -}
    -
    -+ (id)parserWithXML:(NSData *)theXML delegate:(NSObject *)theDelegate
    -connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
    - responseType:(MGTwitterResponseType)respType;
    -- (id)initWithXML:(NSData *)theXML delegate:(NSObject *)theDelegate
    -connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
    - responseType:(MGTwitterResponseType)respType;
    -
    -- (NSString *)lastOpenedElement;
    -- (void)setLastOpenedElement:(NSString *)value;
    -
    -- (void)addSource;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterXMLParser.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,129 +0,0 @@
    -//
    -// MGTwitterXMLParser.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 18/02/2008.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterXMLParser.h"
    -
    -
    -@implementation MGTwitterXMLParser
    -
    -
    -#pragma mark Creation and Destruction
    -
    -
    -+ (id)parserWithXML:(NSData *)theXML delegate:(NSObject *)theDelegate
    -connectionIdentifier:(NSString *)identifier requestType:(MGTwitterRequestType)reqType
    - responseType:(MGTwitterResponseType)respType
    -{
    - id parser = [[self alloc] initWithXML:theXML
    - delegate:theDelegate
    - connectionIdentifier:identifier
    - requestType:reqType
    - responseType:respType];
    - return parser;
    -}
    -
    -
    -- (id)initWithXML:(NSData *)theXML delegate:(NSObject <MGTwitterParserDelegate> *)theDelegate
    -connectionIdentifier:(NSString *)theIdentifier requestType:(MGTwitterRequestType)reqType
    - responseType:(MGTwitterResponseType)respType
    -{
    - if ((self = [super init])) {
    - xml = theXML;
    - identifier = theIdentifier;
    - requestType = reqType;
    - responseType = respType;
    - delegate = theDelegate;
    - parsedObjects = [[NSMutableArray alloc] initWithCapacity:0];
    -
    - // Set up the parser object.
    - parser = [[NSXMLParser alloc] initWithData:xml];
    - [parser setDelegate:self];
    - [parser setShouldReportNamespacePrefixes:NO];
    - [parser setShouldProcessNamespaces:NO];
    - [parser setShouldResolveExternalEntities:NO];
    -
    - // Begin parsing.
    - [parser parse];
    - }
    -
    - return self;
    -}
    -
    -#pragma mark NSXMLParser delegate methods
    -
    -
    -- (void)parserDidStartDocument:(NSXMLParser *)theParser
    -{
    - //NSLog(@"Parsing begun");
    -}
    -
    -
    -- (void)parserDidEndDocument:(NSXMLParser *)theParser
    -{
    - //NSLog(@"Parsing complete: %@", parsedObjects);
    - [delegate parsingSucceededForRequest:identifier ofResponseType:responseType
    - withParsedObjects:parsedObjects];
    -}
    -
    -- (void)parser:(NSXMLParser *)theParser didEndElement:(NSString *)elementName
    - namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    -{
    - //NSLog(@"Ended element: %@", elementName);
    - [self setLastOpenedElement:nil];
    -
    - if ([elementName isEqualToString:@"protected"]
    - || [elementName isEqualToString:@"truncated"]
    - || [elementName isEqualToString:@"following"]) {
    - // Change "true"/"false" into an NSNumber with a BOOL value.
    - NSNumber *boolNumber = [NSNumber numberWithBool:[[currentNode objectForKey:elementName] isEqualToString:@"true"]];
    - [currentNode setObject:boolNumber forKey:elementName];
    - } else if ([elementName isEqualToString:@"created_at"]) {
    - // Change date-string into an NSDate.
    - NSDate *creationDate = [NSDate dateWithNaturalLanguageString:[currentNode objectForKey:elementName]];
    - if (creationDate) {
    - [currentNode setObject:creationDate forKey:elementName];
    - }
    - }
    -}
    -
    -- (void)parser:(NSXMLParser *)theParser parseErrorOccurred:(NSError *)parseError
    -{
    - //NSLog(@"Parsing error occurred: %@", parseError);
    - [delegate parsingFailedForRequest:identifier ofResponseType:responseType
    - withError:parseError];
    -}
    -
    -
    -#pragma mark Accessors
    -
    -
    -- (NSString *)lastOpenedElement {
    - return lastOpenedElement;
    -}
    -
    -
    -- (void)setLastOpenedElement:(NSString *)value {
    - if (lastOpenedElement != value) {
    - lastOpenedElement = [value copy];
    - }
    -}
    -
    -
    -#pragma mark Utility methods
    -
    -
    -- (void)addSource
    -{
    - if (![currentNode objectForKey:TWITTER_SOURCE_REQUEST_TYPE]) {
    - [currentNode setObject:[NSNumber numberWithInt:requestType]
    - forKey:TWITTER_SOURCE_REQUEST_TYPE];
    - }
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/NSData+Base64.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,36 +0,0 @@
    -//
    -// NSData+Base64.m
    -//
    -// Derived from http://colloquy.info/project/browser/trunk/NSDataAdditions.h?rev=1576
    -// Created by khammond on Mon Oct 29 2001.
    -// Formatted by Timothy Hatcher on Sun Jul 4 2004.
    -// Copyright (c) 2001 Kyle Hammond. All rights reserved.
    -// Original development by Dave Winer.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -@interface NSData (Base64)
    -
    -/*! @function +dataWithBase64EncodedString:
    - @discussion This method returns an autoreleased NSData object. The NSData object is initialized with the
    - contents of the Base 64 encoded string. This is a convenience method.
    - @param inBase64String An NSString object that contains only Base 64 encoded data.
    - @result The NSData object. */
    -+ (NSData *) dataWithBase64EncodedString:(NSString *) string;
    -
    -/*! @function -initWithBase64EncodedString:
    - @discussion The NSData object is initialized with the contents of the Base 64 encoded string.
    - This method returns self as a convenience.
    - @param inBase64String An NSString object that contains only Base 64 encoded data.
    - @result This method returns self. */
    -- (id) initWithBase64EncodedString:(NSString *) string;
    -
    -/*! @function -base64EncodingWithLineLength:
    - @discussion This method returns a Base 64 encoded string representation of the data object.
    - @param inLineLength A value of zero means no line breaks. This is crunched to a multiple of 4 (the next
    - one greater than inLineLength).
    - @result The base 64 encoded data. */
    -- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/NSData+Base64.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,149 +0,0 @@
    -//
    -// NSData+Base64.m
    -//
    -// Derived from http://colloquy.info/project/browser/trunk/NSDataAdditions.h?rev=1576
    -// Created by khammond on Mon Oct 29 2001.
    -// Formatted by Timothy Hatcher on Sun Jul 4 2004.
    -// Copyright (c) 2001 Kyle Hammond. All rights reserved.
    -// Original development by Dave Winer.
    -//
    -
    -#import "NSData+Base64.h"
    -
    -static char encodingTable[64] = {
    - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
    - 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
    - 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
    - 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
    -
    -@implementation NSData (Base64)
    -
    -+ (NSData *) dataWithBase64EncodedString:(NSString *) string {
    - NSData *result = [[NSData alloc] initWithBase64EncodedString:string];
    - return result;
    -}
    -
    -- (id) initWithBase64EncodedString:(NSString *) string {
    - NSMutableData *mutableData = nil;
    -
    - if( string ) {
    - unsigned long ixtext = 0;
    - unsigned long lentext = 0;
    - unsigned char ch = 0;
    - unsigned char inbuf[4] = {0};
    - unsigned char outbuf[4];
    - short i = 0, ixinbuf = 0;
    - BOOL flignore = NO;
    - BOOL flendtext = NO;
    - NSData *base64Data = nil;
    - const unsigned char *base64Bytes = nil;
    -
    - // Convert the string to ASCII data.
    - base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
    - base64Bytes = [base64Data bytes];
    - mutableData = [NSMutableData dataWithCapacity:[base64Data length]];
    - lentext = [base64Data length];
    -
    - while( YES ) {
    - if( ixtext >= lentext ) break;
    - ch = base64Bytes[ixtext++];
    - flignore = NO;
    -
    - if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
    - else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
    - else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
    - else if( ch == '+' ) ch = 62;
    - else if( ch == '=' ) flendtext = YES;
    - else if( ch == '/' ) ch = 63;
    - else flignore = YES;
    -
    - if( ! flignore ) {
    - short ctcharsinbuf = 3;
    - BOOL flbreak = NO;
    -
    - if( flendtext ) {
    - if( ! ixinbuf ) break;
    - if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
    - else ctcharsinbuf = 2;
    - ixinbuf = 3;
    - flbreak = YES;
    - }
    -
    - inbuf [ixinbuf++] = ch;
    -
    - if( ixinbuf == 4 ) {
    - ixinbuf = 0;
    - outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
    - outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
    - outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
    -
    - for( i = 0; i < ctcharsinbuf; i++ )
    - [mutableData appendBytes:&outbuf[i] length:1];
    - }
    -
    - if( flbreak ) break;
    - }
    - }
    - }
    -
    - self = [self initWithData:mutableData];
    - return self;
    -}
    -
    -- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {
    - const unsigned char *bytes = [self bytes];
    - NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
    - unsigned long ixtext = 0;
    - unsigned long lentext = [self length];
    - long ctremaining = 0;
    - unsigned char inbuf[3], outbuf[4];
    - short i = 0;
    - short charsonline = 0, ctcopy = 0;
    - unsigned long ix = 0;
    -
    - while( YES ) {
    - ctremaining = lentext - ixtext;
    - if( ctremaining <= 0 ) break;
    -
    - for( i = 0; i < 3; i++ ) {
    - ix = ixtext + i;
    - if( ix < lentext ) inbuf[i] = bytes[ix];
    - else inbuf [i] = 0;
    - }
    -
    - outbuf [0] = (inbuf [0] & 0xFC) >> 2;
    - outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
    - outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
    - outbuf [3] = inbuf [2] & 0x3F;
    - ctcopy = 4;
    -
    - switch( ctremaining ) {
    - case 1:
    - ctcopy = 2;
    - break;
    - case 2:
    - ctcopy = 3;
    - break;
    - }
    -
    - for( i = 0; i < ctcopy; i++ )
    - [result appendFormat:@"%c", encodingTable[outbuf[i]]];
    -
    - for( i = ctcopy; i < 4; i++ )
    - [result appendFormat:@"%c",'='];
    -
    - ixtext += 3;
    - charsonline += 4;
    -
    - if( lineLength > 0 ) {
    - if (charsonline >= lineLength) {
    - charsonline = 0;
    - [result appendString:@"\n"];
    - }
    - }
    - }
    -
    - return result;
    -}
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/NSString+UUID.h Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,15 +0,0 @@
    -//
    -// NSString+UUID.h
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 16/09/2007.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "MGTwitterEngineGlobalHeader.h"
    -
    -@interface NSString (UUID)
    -
    -+ (NSString*)stringWithNewUUID;
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/NSString+UUID.m Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,27 +0,0 @@
    -//
    -// NSString+UUID.m
    -// MGTwitterEngine
    -//
    -// Created by Matt Gemmell on 16/09/2007.
    -// Copyright 2008 Instinctive Code.
    -//
    -
    -#import "NSString+UUID.h"
    -
    -
    -@implementation NSString (UUID)
    -
    -
    -+ (NSString*)stringWithNewUUID
    -{
    - // Create a new UUID
    - CFUUIDRef uuidObj = CFUUIDCreate(nil);
    -
    - // Get the string representation of the UUID
    - NSString *newUUID = (__bridge_transfer NSString*)CFUUIDCreateString(nil, uuidObj);
    - CFRelease(uuidObj);
    - return newUUID;
    -}
    -
    -
    -@end
    --- a/Plugins/Twitter Plugin/MGTwitterEngine/Source Code License.rtf Wed Mar 20 01:55:36 2013 +0100
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,104 +0,0 @@
    -{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf270
    -{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
    -{\colortbl;\red255\green255\blue255;\red51\green51\blue51;\red0\green180\blue128;\red255\green0\blue0;
    -\red31\green105\blue199;\red119\green119\blue119;}
    -{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc2\leveljcn2\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{decimal\}.}{\leveltext\leveltemplateid0\'02\'05.;}{\levelnumbers\'01;}}{\listname ;}\listid1}}
    -{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
    -\deftab720
    -\pard\pardeftab720\ql\qnatural
    -
    -\f0\b\fs24 \cf2 Matt Gemmell / Instinctive Code Source Code License\
    -
    -\b0\fs22 Last updated: 19th May 2008
    -\fs24 \
    -\
    -\
    -Thanks for downloading some of our source code!\
    -\
    -This is the license agreement for the source code which this document accompanies (don\'92t worry: you\'92re allowed to use it in your own products, commercial or otherwise).\
    -\
    -The full license text is further down this page, and you should only use the source code if you agree to the terms in that text. For convenience, though, we\'92ve put together a human-readable
    -\b non-authoritative
    -\b0 interpretation of the license which will hopefully answer any questions you have.\
    -\
    -\
    -
    -\b \cf3 Green
    -\b0 \cf2 text shows
    -\b \cf3 what you can do with the code
    -\b0 \cf2 .\
    -
    -\b \cf4 Red
    -\b0 \cf2 text means
    -\b \cf4 restrictions you must abide by
    -\b0 \cf2 .\
    -\
    -Basically, the license says that:\
    -\
    -\pard\tx220\tx720\pardeftab720\li720\fi-720\ql\qnatural
    -\ls1\ilvl0\cf2 {\listtext 1. }You can
    -\b \cf3 use the code in your own products, including commercial and/or closed-source products
    -\b0 \cf2 .\
    -{\listtext 2. }You can
    -\b \cf3 modify the code
    -\b0 \cf0 as you wish\cf2 , and
    -\b \cf3 use the modified code in your products
    -\b0 \cf2 .\
    -{\listtext 3. }You can
    -\b \cf3 redistribute the original, unmodified code
    -\b0 \cf2 , but you
    -\b \cf4 have to include the full license text below
    -\b0 \cf2 .\
    -{\listtext 4. }You can
    -\b \cf3 redistribute the modified code
    -\b0 \cf2 as you wish (
    -\b \cf4 without the full license text below
    -\b0 \cf2 ).\
    -{\listtext 5. }In all cases, you
    -\b \cf4 must include a credit mentioning Matt Gemmell
    -\b0 \cf2 as the original author of the source.\
    -{\listtext 6. }Matt Gemmell is \cf0 not liable for anything you do with the code\cf2 , no matter what. So be sensible.\
    -{\listtext 7. }You
    -\b \cf4 can\'92t use the name Matt Gemmell, the name Instinctive Code, the Instinctive Code logo or any other related marks to promote your products
    -\b0 \cf2 based on the code.\
    -{\listtext 8. }If you agree to all of that, go ahead and use the source. Otherwise, don\'92t!\
    -\pard\pardeftab720\ql\qnatural
    -\cf2 \
    -
    -\b \
    -\
    -Suggested Attribution Format\
    -
    -\b0 \
    -The license requires that you give credit to Matt Gemmell, as the original author of any of our source that you use. The placement and format of the credit is up to you, but we prefer the credit to be in the software\'92s \'93About\'94 window. Alternatively, you could put the credit in a list of acknowledgements within the software, in the software\'92s documentation, or on the web page for the software. The suggested format for the attribution is:\
    -\
    -\pard\pardeftab720\ql\qnatural
    -
    -\b \cf0 Includes <Name of Code> code by {\field{\*\fldinst{HYPERLINK "http://mattgemmell.com/"}}{\fldrslt \cf5 Matt Gemmell}}\cf6 .
    -\b0 \
    -\pard\pardeftab720\ql\qnatural
    -\cf2 \
    -where <Name of Code> would be replaced by the name of the specific source-code package you made use of. Where possible, please link the text \'93Matt Gemmell\'94 to the following URL, or include the URL as plain text: {\field{\*\fldinst{HYPERLINK "http://mattgemmell.com/"}}{\fldrslt \cf5 http://mattgemmell.com/}}\
    -\
    -\
    -
    -\b Full Source Code License Text\
    -\
    -
    -\b0 Below you can find the actual text of the license agreement.
    -\b \
    -\
    -\pard\pardeftab720\ql\qnatural
    -\cf6 \
    -License Agreement for Source Code provided by Matt Gemmell
    -\b0 \
    -\
    -This software is supplied to you by Matt Gemmell in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this software.\
    -\
    -In consideration of your agreement to abide by the following terms, and subject to these terms, Matt Gemmell grants you a personal, non-exclusive license, to use, reproduce, modify and redistribute the software, with or without modifications, in source and/or binary forms; provided that if you redistribute the software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the software, and that in all cases attribution of Matt Gemmell as the original author of the source code shall be included in all such resulting software products or distributions.\uc0\u8232 \
    -Neither the name, trademarks, service marks or logos of Matt Gemmell or Instinctive Code may be used to endorse or promote products derived from the software without specific prior written permission from Matt Gemmell. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Matt Gemmell herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the software may be incorporated.\
    -\
    -The software is provided by Matt Gemmell on an "AS IS" basis. MATT GEMMELL AND INSTINCTIVE CODE MAKE NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.\
    -\
    -IN NO EVENT SHALL MATT GEMMELL OR INSTINCTIVE CODE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF MATT GEMMELL OR INSTINCTIVE CODE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\
    -}
    \ No newline at end of file
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,15 @@
    +//
    +// NSString+STTwitter.h
    +// STTwitter
    +//
    +// Created by Nicolas Seriot on 11/2/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import <Foundation/Foundation.h>
    +
    +@interface NSString (STTwitter)
    +
    +- (NSString *)firstMatchWithRegex:(NSString *)regex error:(NSError **)e;
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/NSString+STTwitter.m Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,35 @@
    +//
    +// NSString+STTwitter.m
    +// STTwitter
    +//
    +// Created by Nicolas Seriot on 11/2/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import "NSString+STTwitter.h"
    +
    +@implementation NSString (STTwitter)
    +
    +- (NSString *)firstMatchWithRegex:(NSString *)regex error:(NSError **)e {
    + NSError *error = nil;
    + NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:regex options:0 error:&error];
    +
    + if(re == nil) {
    + if(e) *e = error;
    + return nil;
    + }
    +
    + NSArray *matches = [re matchesInString:self options:0 range:NSMakeRange(0, [self length])];
    +
    + if([matches count] == 0) {
    + NSString *errorDescription = [NSString stringWithFormat:@"Can't find a match for regex: %@", regex];
    + if(e) *e = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : errorDescription}];
    + return nil;
    + }
    +
    + NSTextCheckingResult *match = [matches lastObject];
    + NSRange matchRange = [match rangeAtIndex:1];
    + return [self substringWithRange:matchRange];
    +}
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,420 @@
    +/*
    + Copyright (c) 2012, Nicolas Seriot
    + All rights reserved.
    +
    + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    +
    + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    + * Neither the name of the Nicolas Seriot nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    +
    + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + */
    +
    +//
    +// STTwitterAPI.h
    +// STTwitterRequests
    +//
    +// Created by Nicolas Seriot on 9/18/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import <Foundation/Foundation.h>
    +
    +/*
    + Partial Objective-C front-end for https://dev.twitter.com/docs/api/1.1
    + */
    +
    +/*
    + FWIW Twitter.app for iOS 5 implements this:
    + https://api.twitter.com/1/statuses/update.json
    + https://upload.twitter.com/1/statuses/update_with_media.json
    + https://api.twitter.com/1/geo/nearby_places.json
    + https://api.twitter.com/1/friendships/show.json
    + https://api.twitter.com/1/statuses/friends.json
    + https://api.twitter.com/1/help/configuration.json
    + https://api.twitter.com/1/apps/configuration.json
    + https://api.twitter.com/1/users/show.json
    + https://api.twitter.com/1/account/verify_credentials.json
    +
    + Tweet fields contents
    + https://dev.twitter.com/docs/platform-objects/tweets
    + https://dev.twitter.com/blog/new-withheld-content-fields-api-responses
    + */
    +
    +@interface STTwitterAPIWrapper : NSObject
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret;
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + username:(NSString *)username
    + password:(NSString *)password;
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + oauthToken:(NSString *)oauthToken
    + oauthTokenSecret:(NSString *)oauthTokenSecret;
    +
    +// https://dev.twitter.com/docs/auth/application-only-auth
    ++ (STTwitterAPIWrapper *)twitterAPIApplicationOnlyWithConsumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret;
    +
    +- (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock
    + oauthCallback:(NSString *)oauthCallback
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)postAccessTokenRequestWithPIN:(NSString *)pin
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +@property (nonatomic, retain) NSString *consumerName;
    +@property (nonatomic, retain) NSString *userName; // available for osx, set after successful connection for STTwitterOAuth
    +
    +@property (nonatomic, readonly) NSString *oauthAccessToken;
    +@property (nonatomic, readonly) NSString *oauthAccessTokenSecret;
    +@property (nonatomic, readonly) NSString *bearerToken;
    +
    +- (void)profileImageFor:(NSString *)screenName
    + successBlock:(void(^)(NSImage *image))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;;
    +
    +#pragma mark Timelines
    +
    +// GET statuses/mentions_timeline
    +// Returns Tweets (*: mentions for the user)
    +- (void)getMentionsTimelineSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET statuses/user_timeline
    +// Returns Tweets (*: tweets for the user)
    +- (void)getUserTimelineWithScreenName:(NSString *)screenName
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)getUserTimelineWithScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET statuses/home_timeline
    +// Returns Tweets (*: tweets from people the user follows)
    +- (void)getHomeTimelineSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark Tweets
    +
    +// GET statuses/retweets/:id
    +
    +// GET statuses/show/:id
    +
    +// POST statuses/destroy/:id
    +// Returns Tweets (1: the destroyed tweet)
    +- (void)postDestroyStatusWithID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST statuses/update
    +// Returns Tweets (1: the new tweet)
    +- (void)postStatusUpdate:(NSString *)status
    + inReplyToStatusID:(NSString *)optionalExistingStatusID
    + placeID:(NSString *)optionalPlaceID // wins over lat/lon
    + lat:(NSString *)optionalLat
    + lon:(NSString *)optionalLon
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST statuses/retweet/:id
    +// Returns Tweets (1: the retweeted tweet)
    +- (void)postStatusRetweetWithID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +
    +// POST statuses/update_with_media
    +// Returns Tweets (1: the new tweet)
    +- (void)postStatusUpdate:(NSString *)status
    + inReplyToStatusID:(NSString *)optionalExistingStatusID
    + mediaURL:(NSURL *)mediaURL
    + placeID:(NSString *)optionalPlaceID // wins over lat/lon
    + lat:(NSString *)optionalLat
    + lon:(NSString *)optionalLon
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET statuses/oembed
    +
    +#pragma mark Search
    +
    +// GET search/tweets
    +// Returns Tweets (*: tweets matching the query)
    +- (void)getSearchTweetsWithQuery:(NSString *)q
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark Streaming
    +
    +// POST statuses/filter
    +
    +// GET statuses/sample
    +
    +// GET statuses/firehose
    +
    +// GET user
    +
    +// GET site
    +
    +#pragma mark Direct Messages
    +
    +// GET direct_messages
    +// Returns Tweets (*: direct messages to the user)
    +- (void)getDirectMessagesSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET direct_messages/sent
    +
    +// GET direct_messages/show
    +
    +// POST direct_messages/destroy
    +// Returns Tweets (1: the destroyed DM)
    +- (void)postDestroyDirectMessageWithID:(NSString *)dmID
    + successBlock:(void(^)(NSDictionary *dm))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST direct_messages/new
    +// Returns Tweets (1: the sent DM)
    +- (void)postDirectMessage:(NSString *)status
    + to:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *dm))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark Friends & Followers
    +
    +// GET friends/ids
    +// Returns Users (*: user IDs for followees)
    +- (void)getFriendsIDsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *friends))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET followers/ids
    +// Returns Users (*: user IDs for followers)
    +- (void)getFollowersIDsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *followers))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET friendships/lookup
    +
    +// GET friendships/incoming
    +
    +// GET friendships/outgoing
    +
    +// POST friendships/create
    +// Returns Users (1: the followed user)
    +- (void)postFollow:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST friendships/destroy
    +// Returns Users (1: the unfollowed user)
    +- (void)postUnfollow:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST friendships/update
    +// Returns ?
    +- (void)postUpdateNotifications:(BOOL)notify
    + forScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *relationship))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET friendships/show
    +
    +// GET friends/list
    +- (void)getFriendsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *friends))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET followers/list
    +- (void)getFollowersForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *followers))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark Users
    +
    +// GET account/settings
    +
    +// GET account/verify_credentials
    +// Returns Users (1: the user)
    +- (void)getAccountVerifyCredentialsSkipStatus:(BOOL)skipStatus
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST account/settings
    +
    +// POST account/update_delivery_device
    +
    +// POST account/update_profile
    +// Returns Users (1: the user)
    +- (void)postUpdateProfile:(NSDictionary *)profileData
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST account/update_profile_background_image
    +
    +// POST account/update_profile_colors
    +
    +// POST account/update_profile_image
    +// Returns Users (1: the user)
    +- (void)postUpdateProfileImage:(NSImage *)newImage
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET blocks/list
    +
    +// GET blocks/ids
    +
    +// POST blocks/create
    +
    +// POST blocks/destroy
    +
    +// GET users/lookup
    +
    +// GET users/show
    +// Returns Users (1: detailed information for the user)
    +- (void)getUserInformationFor:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET users/search
    +
    +// GET users/contributees
    +
    +// GET users/contributors
    +
    +#pragma mark Suggested Users
    +
    +// GET users/suggestions/:slug
    +
    +// GET users/suggestions
    +
    +// GET users/suggestions/:slug/members
    +
    +#pragma mark Favorites
    +
    +// GET favorites/list
    +// Returns Tweets (20: last 20 favorited tweets)
    +- (void)getFavoritesListWithSuccessBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// POST favorites/destroy
    +// POST favorites/create
    +// Returns Tweets (1: the (un)favorited tweet)
    +- (void)postFavoriteState:(BOOL)favoriteState
    + forStatusID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark Lists
    +
    +// GET lists/list
    +
    +// GET lists/statuses
    +
    +// POST lists/members/destroy
    +
    +// GET lists/memberships
    +
    +// GET lists/subscribers
    +
    +// POST lists/subscribers/create
    +
    +// GET lists/subscribers/show
    +
    +// POST lists/subscribers/destroy
    +
    +// POST lists/members/create_all
    +
    +// GET lists/members/show
    +
    +// GET lists/members
    +
    +// POST lists/members/create
    +
    +// POST lists/destroy
    +
    +// POST lists/update
    +
    +// POST lists/create
    +
    +// GET lists/show
    +
    +// GET lists/subscriptions
    +
    +// POST lists/members/destroy_all
    +
    +#pragma mark Saved Searches
    +
    +#pragma mark Places & Geo
    +
    +// GET geo/id/:place_id
    +
    +// GET geo/reverse_geocode
    +// Returns Places (*: up to 20 places that match the lat/lon)
    +- (void)getGeoReverseGeocodeWithLatitude:(NSString *)latitude
    + longitude:(NSString *)longitude
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET geo/search
    +// Returns Places (*: places that match the lat/lon)
    +- (void)getGeoSearchWithLatitude:(NSString *)latitude
    + longitude:(NSString *)longitude
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)getGeoSearchWithIPAddress:(NSString *)ipAddress
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)getGeoSearchWithQuery:(NSString *)query
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +// GET geo/similar_places
    +
    +// POST geo/place
    +
    +#pragma mark Trends
    +
    +#pragma mark Spam Reporting
    +
    +- (void)postReportSpamWithScreenName:(NSString *)screenName
    + orUserID:(NSString *)userID
    + successBlock:(void(^)(id userProfile))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +#pragma mark OAuth
    +
    +#pragma mark Help
    +// GET application/rate_limit_status
    +// Returns ?
    +- (void)getRateLimitsForResources:(NSArray *)resources
    + successBlock:(void(^)(NSDictionary *rateLimits))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterAPIWrapper.m Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,760 @@
    +//
    +// STTwitterAPI.m
    +// STTwitterRequests
    +//
    +// Created by Nicolas Seriot on 9/18/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import "STTwitterAPIWrapper.h"
    +#import "STTwitterOAuth.h"
    +#import "NSString+STTwitter.h"
    +#import "STTwitterAppOnly.h"
    +
    +@interface STTwitterAPIWrapper ()
    +id removeNull(id rootObject);
    +@property (nonatomic, retain) NSObject <STTwitterOAuthProtocol> *oauth;
    +@end
    +
    +@implementation STTwitterAPIWrapper
    +
    +#if TARGET_OS_IPHONE
    +#else
    +
    +- (id)init {
    + self = [super init];
    +
    + return self;
    +}
    +
    +
    +#endif
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + username:(NSString *)username
    + password:(NSString *)password {
    +
    + STTwitterAPIWrapper *twitter = [[STTwitterAPIWrapper alloc] init];
    +
    + twitter.oauth = [STTwitterOAuth twitterServiceWithConsumerName:consumerName
    + consumerKey:consumerKey
    + consumerSecret:consumerSecret
    + username:username
    + password:password];
    + return [twitter autorelease];
    +}
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + oauthToken:(NSString *)oauthToken
    + oauthTokenSecret:(NSString *)oauthTokenSecret {
    +
    + STTwitterAPIWrapper *twitter = [[STTwitterAPIWrapper alloc] init];
    +
    + twitter.oauth = [STTwitterOAuth twitterServiceWithConsumerName:consumerName
    + consumerKey:consumerKey
    + consumerSecret:consumerSecret
    + oauthToken:oauthToken
    + oauthTokenSecret:oauthTokenSecret];
    + return [twitter autorelease];
    +}
    +
    ++ (STTwitterAPIWrapper *)twitterAPIWithOAuthConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret {
    +
    + return [self twitterAPIWithOAuthConsumerName:consumerName
    + consumerKey:consumerKey
    + consumerSecret:consumerSecret
    + username:nil
    + password:nil];
    +}
    +
    ++ (STTwitterAPIWrapper *)twitterAPIApplicationOnlyWithConsumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret {
    +
    + STTwitterAPIWrapper *twitter = [[STTwitterAPIWrapper alloc] init];
    +
    + STTwitterAppOnly *appOnly = [[[STTwitterAppOnly alloc] init] autorelease];
    + appOnly.consumerKey = consumerKey;
    + appOnly.consumerSecret = consumerSecret;
    +
    + twitter.oauth = appOnly;
    + return twitter;
    +}
    +
    +- (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock oauthCallback:(NSString *)oauthCallback errorBlock:(void(^)(NSError *error))errorBlock {
    + [_oauth postTokenRequest:successBlock oauthCallback:oauthCallback errorBlock:errorBlock];
    +}
    +
    +- (void)postAccessTokenRequestWithPIN:(NSString *)pin
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [_oauth postAccessTokenRequestWithPIN:pin
    + successBlock:successBlock
    + errorBlock:errorBlock];
    +}
    +
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + if([_oauth canVerifyCredentials]) {
    + [_oauth verifyCredentialsWithSuccessBlock:^(NSString *username) {
    + self.userName = username;
    + successBlock(_userName);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    + } else {
    + [self getAccountVerifyCredentialsSkipStatus:YES successBlock:^(NSDictionary *myInfo) {
    + self.userName = [myInfo valueForKey:@"screen_name"];
    + successBlock(_userName);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    + }
    +}
    +
    +- (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + if([self.oauth respondsToSelector:@selector(invalidateBearerTokenWithSuccessBlock:errorBlock:)]) {
    + [self.oauth invalidateBearerTokenWithSuccessBlock:successBlock errorBlock:errorBlock];
    + } else {
    + NSLog(@"-- self.oauth does not support tokens invalidation");
    + }
    +}
    +
    +- (NSString *)oauthAccessTokenSecret {
    + return [_oauth oauthAccessTokenSecret];
    +}
    +
    +- (NSString *)oauthAccessToken {
    + return [_oauth oauthAccessToken];
    +}
    +
    +- (NSString *)bearerToken {
    + if([_oauth respondsToSelector:@selector(bearerToken)]) {
    + return [_oauth bearerToken];
    + }
    +
    + return nil;
    +}
    +
    +- (NSString *)userName {
    + return _userName;
    +}
    +
    +- (void)dealloc {
    + [_userName release];
    + [_consumerName release];
    + [_oauth release];
    + [super dealloc];
    +}
    +
    +/**/
    +
    +- (void)profileImageFor:(NSString *)screenName
    + successBlock:(void(^)(NSImage *image))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getUserInformationFor:screenName
    + successBlock:^(NSDictionary *response) {
    + NSString *imageURL = [response objectForKey:@"profile_image_url"];
    +
    + NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
    +
    + NSData *imageData = [NSURLConnection sendSynchronousRequest:imageRequest returningResponse:nil error:nil];
    + successBlock([[NSImage alloc] initWithData:imageData]);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Timelines
    +- (void)getTimeline:(NSString *)timeline
    + withParameters:(NSDictionary *)params
    + sinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSMutableDictionary *mparams = [params mutableCopy];
    + if (!mparams)
    + mparams = [NSMutableDictionary new];
    +
    + if (optionalSinceID) mparams[@"since_id"] = optionalSinceID;
    + if (optionalCount != NSNotFound) mparams[@"count"] = [@(optionalCount) stringValue];
    +
    + __block NSMutableArray *statuses = [NSMutableArray new];
    + __block void (^requestHandler)(id response) = nil;
    + __block int count = 0;
    + requestHandler = [[^(id response) {
    + if ([response isKindOfClass:[NSArray class]] && [response count] > 0)
    + [statuses addObjectsFromArray:response];
    +
    + //Only send another request if we got close to the requested limit, up to a maximum of 4 api calls
    + if (count++ == 0 || (count <= 4 && [response count] >= (optionalCount - 5))) {
    + //Set the max_id so that we don't get statuses we've already received
    + NSString *lastID = [[statuses lastObject] objectForKey:@"id_str"];
    + if (lastID) {
    + NSUInteger maxID = [[NSDecimalNumber decimalNumberWithString:lastID] unsignedIntegerValue];
    + if (maxID != NSNotFound)
    + mparams[@"max_id"] = [@(--maxID) stringValue];
    + }
    +
    + [_oauth getResource:timeline parameters:mparams
    + successBlock:requestHandler
    + errorBlock:errorBlock];
    + } else {
    + successBlock(removeNull(statuses));
    + }
    + } copy] autorelease];
    +
    + //Send the first request
    + requestHandler(nil);
    +}
    +
    +- (void)getMentionsTimelineSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getTimeline:@"statuses/mentions_timeline.json"
    + withParameters:nil
    + sinceID:optionalSinceID
    + count:optionalCount
    + successBlock:successBlock
    + errorBlock:errorBlock];
    +}
    +
    +- (void)getUserTimelineWithScreenName:(NSString *)screenName
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getTimeline:@"statuses/user_timeline.json"
    + withParameters:@{ @"screen_name" : screenName }
    + sinceID:nil
    + count:optionalCount
    + successBlock:successBlock
    + errorBlock:errorBlock];
    +}
    +
    +- (void)getUserTimelineWithScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [self getUserTimelineWithScreenName:screenName count:NSNotFound successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +- (void)getHomeTimelineSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getTimeline:@"statuses/home_timeline.json"
    + withParameters:nil
    + sinceID:optionalSinceID
    + count:optionalCount
    + successBlock:successBlock
    + errorBlock:errorBlock];
    +}
    +
    +#pragma mark Tweets
    +
    +- (void)postDestroyStatusWithID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + // set trim_user to true?
    +
    + NSString *resource = [NSString stringWithFormat:@"statuses/destroy/%@.json", statusID];
    +
    + //Twitter returns an unauthenticated error if parameters is nil.
    + [_oauth postResource:resource parameters:@{ @"id" : statusID } successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postStatusUpdate:(NSString *)status
    + inReplyToStatusID:(NSString *)optionalExistingStatusID
    + placeID:(NSString *)optionalPlaceID // wins over lat/lon
    + lat:(NSString *)optionalLat
    + lon:(NSString *)optionalLon
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + if(status == nil) {
    + NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : @"cannot post empty status"}];
    + errorBlock(error);
    + return;
    + }
    +
    + NSMutableDictionary *md = [NSMutableDictionary dictionaryWithObject:status forKey:@"status"];
    +
    + if(optionalExistingStatusID) {
    + md[@"in_reply_to_status_id"] = optionalExistingStatusID;
    + }
    +
    + if(optionalPlaceID) {
    + md[@"place_id"] = optionalPlaceID;
    + md[@"display_coordinates"] = @"true";
    + } else if(optionalLat && optionalLon) {
    + md[@"lat"] = optionalLat;
    + md[@"lon"] = optionalLon;
    + md[@"display_coordinates"] = @"true";
    + }
    +
    + [_oauth postResource:@"statuses/update.json" parameters:md successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postStatusUpdate:(NSString *)status
    + inReplyToStatusID:(NSString *)optionalExistingStatusID
    + mediaURL:(NSURL *)mediaURL
    + placeID:(NSString *)optionalPlaceID // wins over lat/lon
    + lat:(NSString *)optionalLat
    + lon:(NSString *)optionalLon
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSData *data = [NSData dataWithContentsOfURL:mediaURL];
    +
    + NSMutableDictionary *md = [[ @{ @"status":status, @"media[]":data, @"postDataKey":@"media[]" } mutableCopy] autorelease];
    +
    + if(optionalExistingStatusID) {
    + md[@"in_reply_to_status_id"] = optionalExistingStatusID;
    + }
    +
    + if(optionalPlaceID) {
    + md[@"place_id"] = optionalPlaceID;
    + md[@"display_coordinates"] = @"true";
    + } else if(optionalLat && optionalLon) {
    + md[@"lat"] = optionalLat;
    + md[@"lon"] = optionalLon;
    + md[@"display_coordinates"] = @"true";
    + }
    +
    + [_oauth postResource:@"statuses/update_with_media.json" parameters:md successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postStatusRetweetWithID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSString *resource = [NSString stringWithFormat:@"statuses/retweet/%@.json", statusID];
    +
    + [_oauth postResource:resource parameters:nil successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Search
    +
    +- (void)getSearchTweetsWithQuery:(NSString *)q
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSDictionary *d = @{@"q" : q};
    +
    + [_oauth getResource:@"search/tweets.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Streaming
    +
    +#pragma mark Direct Messages
    +- (void)getDirectMessagesSinceID:(NSString *)optionalSinceID
    + count:(NSUInteger)optionalCount
    + successBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSMutableDictionary *md = [NSMutableDictionary dictionary];
    + if(optionalSinceID) [md setObject:optionalSinceID forKey:@"since_id"];
    + if (optionalCount != NSNotFound) [md setObject:[@(optionalCount) stringValue] forKey:@"count"];
    +
    + [_oauth getResource:@"direct_messages.json" parameters:md successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postDestroyDirectMessageWithID:(NSString *)dmID
    + successBlock:(void(^)(NSDictionary *dm))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSDictionary *d = @{@"id" : dmID};
    +
    + [_oauth postResource:@"direct_messages/destroy.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postDirectMessage:(NSString *)status
    + to:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *dm))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSMutableDictionary *md = [NSMutableDictionary dictionaryWithObject:status forKey:@"text"];
    + [md setObject:screenName forKey:@"screen_name"];
    +
    + [_oauth postResource:@"direct_messages/new.json" parameters:md successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Friends & Followers
    +- (void)getUsersAtResource:(NSString *)resource
    + forScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *friends))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSMutableDictionary *d = [NSMutableDictionary dictionaryWithObject:screenName forKey:@"screen_name"];
    +
    + __block NSMutableArray *ids = [NSMutableArray new];
    + __block void (^requestHandler)(id response) = nil;
    + __block NSString *cursor = @"-1";
    + requestHandler = [[^(id response) {
    + if (response) {
    + [ids addObjectsFromArray:[response objectForKey:@"users"]];
    + cursor = [[response objectForKey:@"next_cursor_str"] copy];
    + d[@"cursor"] = cursor;
    + }
    +
    + if ([cursor isEqualToString:@"0"]) {
    + successBlock(ids);
    + } else {
    + [_oauth getResource:resource parameters:d successBlock:requestHandler
    + errorBlock:errorBlock];
    + }
    + } copy] autorelease];
    +
    + //Send the first request
    + requestHandler(nil);
    +}
    +
    +- (void)getFriendsIDsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *friends))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getUsersAtResource:@"friends/ids.json" forScreenName:screenName successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +- (void)getFollowersIDsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *followers))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getUsersAtResource:@"followers/ids.json" forScreenName:screenName successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +- (void)postFollow:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSDictionary *d = @{@"screen_name" : screenName};
    +
    + [_oauth getResource:@"friendships/create.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postUnfollow:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSDictionary *d = @{@"screen_name" : screenName};
    +
    + [_oauth getResource:@"friendships/destroy.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postUpdateNotifications:(BOOL)notify
    + forScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *relationship))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSMutableDictionary *d = [NSMutableDictionary dictionaryWithObject:screenName forKey:@"screen_name"];
    + d[@"device"] = notify ? @"true" : @"false";
    +
    + [_oauth getResource:@"friendships/update.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getFriendsForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *friends))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getUsersAtResource:@"friends/list.json" forScreenName:screenName successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +- (void)getFollowersForScreenName:(NSString *)screenName
    + successBlock:(void(^)(NSArray *followers))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [self getUsersAtResource:@"followers/list.json" forScreenName:screenName successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +#pragma mark Users
    +
    +- (void)getAccountVerifyCredentialsSkipStatus:(BOOL)skipStatus
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSDictionary *d = @{@"skip_status" : (skipStatus ? @"true" : @"false")};
    +
    + [_oauth getResource:@"account/verify_credentials.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postUpdateProfile:(NSDictionary *)profileData
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + [_oauth postResource:@"account/update_profile.json" parameters:profileData successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postUpdateProfileImage:(NSImage *)newImage
    + successBlock:(void(^)(NSDictionary *myInfo))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSMutableDictionary *md = [NSMutableDictionary dictionaryWithObject:newImage forKey:@"image"];
    + [md setObject:@"image" forKey:@"postDataKey"];
    +
    + [_oauth postResource:@"account/update_profile_image.json" parameters:md successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getUserInformationFor:(NSString *)screenName
    + successBlock:(void(^)(NSDictionary *user))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSDictionary *d = @{@"screen_name" : screenName};
    +
    + [_oauth getResource:@"users/show.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Suggested Users
    +
    +#pragma mark Favorites
    +
    +- (void)getFavoritesListWithSuccessBlock:(void(^)(NSArray *statuses))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [_oauth getResource:@"favorites/list.json" parameters:nil successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postFavoriteState:(BOOL)favoriteState
    + forStatusID:(NSString *)statusID
    + successBlock:(void(^)(NSDictionary *status))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSString *action = favoriteState ? @"create" : @"destroy";
    +
    + NSString *resource = [NSString stringWithFormat:@"favorites/%@.json", action];
    +
    + NSDictionary *d = @{@"id" : statusID};
    +
    + [_oauth postResource:resource parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Lists
    +
    +#pragma mark Saved Searches
    +
    +#pragma mark Places & Geo
    +
    +- (void)getGeoReverseGeocodeWithLatitude:(NSString *)latitude
    + longitude:(NSString *)longitude
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSParameterAssert(latitude);
    + NSParameterAssert(longitude);
    +
    + NSDictionary *d = @{ @"lat":latitude, @"lon":longitude };
    +
    + [_oauth getResource:@"geo/reverse_geocode.json" parameters:d successBlock:^(id response) {
    +
    + NSArray *places = [response valueForKeyPath:@"result.places"];
    +
    + successBlock(places);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getGeoSearchWithLatitude:(NSString *)latitude
    + longitude:(NSString *)longitude
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSParameterAssert(latitude);
    + NSParameterAssert(longitude);
    +
    + NSDictionary *d = @{ @"lat":latitude, @"lon":longitude };
    +
    + [_oauth getResource:@"geo/search.json" parameters:d successBlock:^(id response) {
    +
    + NSArray *places = [response valueForKeyPath:@"result.places"];
    +
    + successBlock(places);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getGeoSearchWithIPAddress:(NSString *)ipAddress
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSParameterAssert(ipAddress);
    +
    + NSDictionary *d = @{ @"ip":ipAddress };
    +
    + [_oauth getResource:@"geo/search.json" parameters:d successBlock:^(id response) {
    +
    + NSArray *places = [response valueForKeyPath:@"result.places"];
    +
    + successBlock(places);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getGeoSearchWithQuery:(NSString *)query
    + successBlock:(void(^)(NSArray *places))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSParameterAssert(query);
    +
    + NSDictionary *d = @{ @"query":query };
    +
    + [_oauth getResource:@"geo/search.json" parameters:d successBlock:^(id response) {
    +
    + NSArray *places = [response valueForKeyPath:@"result.places"];
    +
    + successBlock(places);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark Trends
    +
    +#pragma mark Spam Reporting
    +
    +- (void)postReportSpamWithScreenName:(NSString *)screenName
    + orUserID:(NSString *)userID
    + successBlock:(void(^)(id userProfile))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSParameterAssert(screenName || userID);
    +
    + NSDictionary *d = nil;
    +
    + if(screenName) {
    + d = @{ @"screen_name" : screenName };
    + } else {
    + d = @{ @"user_id" : userID };
    + }
    +
    + [_oauth getResource:@"users/report_spam.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +#pragma mark OAuth
    +
    +#pragma mark Help
    +- (void)getRateLimitsForResources:(NSArray *)resources
    + successBlock:(void(^)(NSDictionary *rateLimits))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    + NSDictionary *d = nil;
    + if (resources)
    + d = @{ @"resources" : [resources componentsJoinedByString:@","] };
    + [_oauth getResource:@"application/rate_limit_status.json" parameters:d successBlock:^(id response) {
    + successBlock(response);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +id removeNull(id rootObject) {
    + if ([rootObject isKindOfClass:[NSDictionary class]]) {
    + NSMutableDictionary *sanitizedDictionary = [NSMutableDictionary dictionaryWithDictionary:rootObject];
    + [rootObject enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + id sanitized = removeNull(obj);
    + if (!sanitized) {
    + [sanitizedDictionary setObject:@"" forKey:key];
    + } else {
    + [sanitizedDictionary setObject:sanitized forKey:key];
    + }
    + }];
    + return [NSDictionary dictionaryWithDictionary:sanitizedDictionary];
    + }
    +
    + if ([rootObject isKindOfClass:[NSArray class]]) {
    + NSMutableArray *sanitizedArray = [NSMutableArray arrayWithArray:rootObject];
    + [rootObject enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    + id sanitized = removeNull(obj);
    + if (!sanitized) {
    + [sanitizedArray replaceObjectAtIndex:[sanitizedArray indexOfObject:obj] withObject:@""];
    + } else {
    + [sanitizedArray replaceObjectAtIndex:[sanitizedArray indexOfObject:obj] withObject:sanitized];
    + }
    + }];
    + return [NSArray arrayWithArray:sanitizedArray];
    + }
    +
    + if ([rootObject isKindOfClass:[NSNull class]]) {
    + return (id)nil;
    + } else {
    + return rootObject;
    + }
    +}
    +
    +@end
    +
    +@implementation NSString (STTwitterAPIWrapper)
    +
    +- (NSString *)htmlLinkName {
    + NSString *ahref = [self firstMatchWithRegex:@"<a href=\".*\">(.*)</a>" error:nil];
    +
    + return ahref ? ahref : self;
    +}
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,25 @@
    +//
    +// STTwitterAppOnly.h
    +// STTwitter
    +//
    +// Created by Nicolas Seriot on 3/13/13.
    +// Copyright (c) 2013 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import <Foundation/Foundation.h>
    +#import "STTwitterOAuthProtocol.h"
    +
    +@interface STTwitterAppOnly : NSObject <STTwitterOAuthProtocol> {
    +
    +}
    +
    +@property (nonatomic, retain) NSString *consumerKey;
    +@property (nonatomic, retain) NSString *consumerSecret;
    +@property (nonatomic, retain) NSString *bearerToken;
    +
    ++ (NSString *)base64EncodedBearerTokenCredentialsWithConsumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret;
    +
    +- (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterAppOnly.m Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,261 @@
    +//
    +// STTwitterAppOnly.m
    +// STTwitter
    +//
    +// Created by Nicolas Seriot on 3/13/13.
    +// Copyright (c) 2013 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import "STTwitterAppOnly.h"
    +#import "STHTTPRequest.h"
    +#import "NSString+STTwitter.h"
    +
    +@interface NSData (Base64)
    +- (NSString *)base64Encoding; // private API
    +@end
    +
    +@implementation STTwitterAppOnly
    +
    +- (void)dealloc {
    + [_consumerKey release];
    + [_consumerSecret release];
    + [_bearerToken release];
    + [super dealloc];
    +}
    +
    +- (id)init {
    + self = [super init];
    +
    + [STHTTPRequest clearSession]; // former cookies may result in mixed-up kind of authentication
    +
    + return self;
    +}
    +
    +#pragma mark STTwitterOAuthProtocol
    +
    +- (BOOL)canVerifyCredentials {
    + return YES;
    +}
    +
    +- (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + if(_bearerToken == nil) {
    + errorBlock(nil);
    + return;
    + }
    +
    + [self postResource:@"oauth2/invalidate_token"
    + baseURLString:@"https://api.twitter.com"
    + parameters:@{ @"access_token" : _bearerToken }
    + useBasicAuth:YES
    + successBlock:^(NSString *body) {
    +
    + NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding];
    +
    + NSError *error = nil;
    + id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
    +
    + if([json isKindOfClass:[NSDictionary class]] == NO) {
    + errorBlock(error);
    + return;
    + }
    +
    + self.bearerToken = [json valueForKey:@"access_token"];
    +
    + NSString *oldToken = self.bearerToken;
    +
    + self.bearerToken = nil;
    +
    + successBlock(oldToken);
    +
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +
    + // POST /oauth2/invalidate_token HTTP/1.1
    + // Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJn
    + // NmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==
    + // User-Agent: My Twitter App v1.0.23
    + // Host: api.twitter.com
    + // Accept: */*
    + //
    + // Content-Length: 119
    + // Content-Type: application/x-www-form-urlencoded
    + //
    + // access_token=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    +
    + // HTTP/1.1 200 OK
    + // Content-Type: application/json; charset=utf-8
    + // Content-Length: 127
    + // ...
    + //
    + // {"access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}
    +}
    +
    ++ (NSString *)base64EncodedBearerTokenCredentialsWithConsumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret {
    + NSString *encodedConsumerToken = [consumerKey st_stringByAddingRFC3986PercentEscapesUsingEncoding:NSUTF8StringEncoding];
    + NSString *encodedConsumerSecret = [consumerSecret st_stringByAddingRFC3986PercentEscapesUsingEncoding:NSUTF8StringEncoding];
    + NSString *bearerTokenCredentials = [NSString stringWithFormat:@"%@:%@", encodedConsumerToken, encodedConsumerSecret];
    + NSData *data = [bearerTokenCredentials dataUsingEncoding:NSUTF8StringEncoding];
    + return [data base64Encoding];
    +}
    +
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    +
    + [self postResource:@"oauth2/token"
    + baseURLString:@"https://api.twitter.com"
    + parameters:@{ @"grant_type" : @"client_credentials" }
    + useBasicAuth:YES
    + successBlock:^(NSString *body) {
    +
    + NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding];
    +
    + NSError *error = nil;
    + id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
    +
    + if([json isKindOfClass:[NSDictionary class]] == NO) {
    + errorBlock(error);
    + return;
    + }
    +
    + NSString *tokenType = [json valueForKey:@"token_type"];
    + if([tokenType isEqualToString:@"bearer"] == NO) {
    + errorBlock(nil);
    + return;
    + }
    +
    + self.bearerToken = [json valueForKey:@"access_token"];
    +
    + successBlock(_bearerToken);
    +
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)getResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id json))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + /*
    + GET /1.1/statuses/user_timeline.json?count=100&screen_name=twitterapi HTTP/1.1
    + Host: api.twitter.com
    + User-Agent: My Twitter App v1.0.23
    + Authorization: Bearer AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAA
    + AAAAAAAA%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    + Accept-Encoding: gzip
    + */
    +
    + NSMutableString *urlString = [NSMutableString stringWithFormat:@"https://api.twitter.com/1.1/%@", resource];
    +
    + NSMutableArray *parameters = [NSMutableArray array];
    +
    + [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + NSString *s = [NSString stringWithFormat:@"%@=%@", key, obj];
    + [parameters addObject:s];
    + }];
    +
    + if([parameters count]) {
    + NSString *parameterString = [parameters componentsJoinedByString:@"&"];
    +
    + [urlString appendFormat:@"?%@", parameterString];
    + }
    +
    + __block STHTTPRequest *r = [STHTTPRequest requestWithURLString:urlString];
    +
    + r.completionBlock = ^(NSDictionary *headers, NSString *body) {
    +
    + NSError *jsonError = nil;
    + id json = [NSJSONSerialization JSONObjectWithData:r.responseData options:NSJSONReadingMutableLeaves error:&jsonError];
    + NSLog(@"-- jsonError: %@", [jsonError localizedDescription]);
    +
    + if(json == nil) {
    + errorBlock(jsonError);
    + return;
    + }
    +
    + NSLog(@"** %@", json);
    +
    + successBlock(json);
    + };
    +
    + r.errorBlock = ^(NSError *error) {
    + NSLog(@"-- body: %@", r.responseString);
    + errorBlock(error);
    + };
    +
    + if(_bearerToken) {
    + [r setHeaderWithName:@"Authorization" value:[NSString stringWithFormat:@"Bearer %@", _bearerToken]];
    + }
    +
    + [r startAsynchronous];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + baseURLString:(NSString *)baseURLString // no trailing slash
    + parameters:(NSDictionary *)params
    + useBasicAuth:(BOOL)useBasicAuth
    + successBlock:(void(^)(NSString *body))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, resource];
    +
    + __block STHTTPRequest *r = [STHTTPRequest requestWithURLString:urlString];
    +
    + r.POSTDictionary = params;
    +
    + NSMutableDictionary *mutableParams = [[params mutableCopy] autorelease];
    +
    + r.encodePOSTDictionary = NO;
    +
    + r.POSTDictionary = mutableParams ? mutableParams : @{};
    +
    + r.completionBlock = ^(NSDictionary *headers, NSString *body) {
    + successBlock(body);
    + };
    +
    + r.errorBlock = ^(NSError *error) {
    +
    + // do our best to extract Twitter error message from responseString
    +
    + NSError *regexError = nil;
    + NSString *errorString = [r.responseString firstMatchWithRegex:@"<error>(.*)</error>" error:&regexError];
    + if(errorString == nil) {
    + NSLog(@"-- regexError: %@", [regexError localizedDescription]);
    + }
    +
    + if(errorString) {
    + error = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : errorString}];
    + } else if ([r.responseString length] > 0 && [r.responseString length] < 64) {
    + error = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : r.responseString}];
    + }
    +
    + NSLog(@"-- body: %@", r.responseString);
    + errorBlock(error);
    + };
    +
    + if(useBasicAuth) {
    + NSString *base64EncodedTokens = [[self class] base64EncodedBearerTokenCredentialsWithConsumerKey:_consumerKey consumerSecret:_consumerSecret];
    +
    + [r setHeaderWithName:@"Authorization" value:[NSString stringWithFormat:@"Basic %@", base64EncodedTokens]];
    + } else if(_bearerToken) {
    + [r setHeaderWithName:@"Authorization" value:[NSString stringWithFormat:@"Bearer %@", _bearerToken]];
    + r.encodePOSTDictionary = YES;
    + }
    +
    + [r startAsynchronous];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id json))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [self postResource:resource baseURLString:@"https://api.twitter.com/1.1/" parameters:params useBasicAuth:NO successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,70 @@
    +//
    +// STTwitterRequest.h
    +// STTwitterRequests
    +//
    +// Created by Nicolas Seriot on 9/5/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import <Foundation/Foundation.h>
    +#import "STTwitterOAuthProtocol.h"
    +
    +/*
    + Based on the following documentation
    + http://oauth.net/core/1.0/
    + https://dev.twitter.com/docs/auth/authorizing-request
    + https://dev.twitter.com/docs/auth/implementing-sign-twitter
    + https://dev.twitter.com/docs/auth/creating-signature
    + https://dev.twitter.com/docs/api/1/post/oauth/request_token
    + https://dev.twitter.com/docs/oauth/xauth
    + ...
    + */
    +
    +@interface STTwitterOAuth : NSObject <STTwitterOAuthProtocol>
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret;
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + oauthToken:(NSString *)oauthToken
    + oauthTokenSecret:(NSString *)oauthTokenSecret;
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + username:(NSString *)username
    + password:(NSString *)password;
    +
    +- (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock
    + oauthCallback:(NSString *)oauthCallback
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)postAccessTokenRequestWithPIN:(NSString *)pin
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)postXAuthAccessTokenRequestWithUsername:(NSString *)username
    + password:(NSString *)password
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (BOOL)canVerifyCredentials;
    +
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +@end
    +
    +@interface NSString (STTwitterOAuth)
    ++ (NSString *)random32Characters;
    +- (NSString *)signHmacSHA1WithKey:(NSString *)key;
    +- (NSDictionary *)parametersDictionary;
    +- (NSString *)urlEncodedString;
    +@end
    +
    +@interface NSURL (STTwitterOAuth)
    +- (NSString *)normalizedForOauthSignatureString;
    +- (NSArray *)getParametersDictionaries;
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterOAuth.m Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,650 @@
    +//
    +// STTwitterRequest.m
    +// STTwitterRequests
    +//
    +// Created by Nicolas Seriot on 9/5/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import "STTwitterOAuth.h"
    +#import "STHTTPRequest.h"
    +#import "NSString+STTwitter.h"
    +
    +#include <CommonCrypto/CommonHMAC.h>
    +
    +@interface NSData (Base64)
    +- (NSString *)base64Encoding; // private API
    +@end
    +
    +@interface STTwitterOAuth ()
    +
    +@property (nonatomic, retain) NSString *username;
    +@property (nonatomic, retain) NSString *password;
    +
    +@property (nonatomic, retain) NSString *oauthConsumerName;
    +@property (nonatomic, retain) NSString *oauthConsumerKey;
    +@property (nonatomic, retain) NSString *oauthConsumerSecret;
    +
    +@property (nonatomic, retain) NSString *oauthRequestToken;
    +@property (nonatomic, retain) NSString *oauthRequestTokenSecret;
    +
    +@property (nonatomic, retain) NSString *oauthAccessToken;
    +@property (nonatomic, retain) NSString *oauthAccessTokenSecret;
    +
    +@property (nonatomic, retain) NSString *testOauthNonce;
    +@property (nonatomic, retain) NSString *testOauthTimestamp;
    +
    +@end
    +
    +@implementation STTwitterOAuth
    +
    +- (void)dealloc {
    + [_username release];
    + [_password release];
    +
    + [_oauthConsumerName release];
    + [_oauthConsumerKey release];
    + [_oauthConsumerSecret release];
    +
    + [_oauthRequestToken release];
    + [_oauthRequestTokenSecret release];
    +
    + [_oauthAccessToken release];
    + [_oauthAccessTokenSecret release];
    +
    + [_testOauthNonce release];
    + [_testOauthTimestamp release];
    +
    + [super dealloc];
    +}
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret {
    +
    + STTwitterOAuth *to = [[STTwitterOAuth alloc] init];
    +
    + to.oauthConsumerName = consumerName;
    + to.oauthConsumerKey = consumerKey;
    + to.oauthConsumerSecret = consumerSecret;
    +
    + return [to autorelease];
    +}
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + oauthToken:(NSString *)oauthToken
    + oauthTokenSecret:(NSString *)oauthTokenSecret {
    +
    + STTwitterOAuth *to = [self twitterServiceWithConsumerName:consumerName consumerKey:consumerKey consumerSecret:consumerSecret];
    +
    + to.oauthAccessToken = oauthToken;
    + to.oauthAccessTokenSecret = oauthTokenSecret;
    +
    + return to;
    +}
    +
    ++ (STTwitterOAuth *)twitterServiceWithConsumerName:(NSString *)consumerName
    + consumerKey:(NSString *)consumerKey
    + consumerSecret:(NSString *)consumerSecret
    + username:(NSString *)username
    + password:(NSString *)password {
    +
    + STTwitterOAuth *to = [self twitterServiceWithConsumerName:consumerName consumerKey:consumerKey consumerSecret:consumerSecret];
    +
    + to.username = username;
    + to.password = password;
    +
    + return to;
    +}
    +
    ++ (NSArray *)encodedParametersDictionaries:(NSArray *)parameters {
    +
    + NSMutableArray *encodedParameters = [NSMutableArray array];
    +
    + for(NSDictionary *d in parameters) {
    +
    + NSString *key = [[d allKeys] lastObject];
    + NSString *value = [[d allValues] lastObject];
    +
    + NSString *encodedKey = [key urlEncodedString];
    + NSString *encodedValue = [value urlEncodedString];
    +
    + [encodedParameters addObject:@{encodedKey : encodedValue}];
    + }
    +
    + return encodedParameters;
    +}
    +
    ++ (NSString *)stringFromParametersDictionaries:(NSArray *)parametersDictionaries {
    +
    + NSMutableArray *parameters = [NSMutableArray array];
    +
    + for(NSDictionary *d in parametersDictionaries) {
    +
    + NSString *encodedKey = [[d allKeys] lastObject];
    + NSString *encodedValue = [[d allValues] lastObject];
    +
    + NSString *s = [NSString stringWithFormat:@"%@=\"%@\"", encodedKey, encodedValue];
    +
    + [parameters addObject:s];
    + }
    +
    + return [parameters componentsJoinedByString:@", "];
    +}
    +
    ++ (NSString *)oauthHeaderValueWithParameters:(NSArray *)parametersDictionaries {
    +
    + NSArray *encodedParametersDictionaries = [self encodedParametersDictionaries:parametersDictionaries];
    +
    + NSString *encodedParametersString = [self stringFromParametersDictionaries:encodedParametersDictionaries];
    +
    + NSString *headerValue = [NSString stringWithFormat:@"OAuth %@", encodedParametersString];
    +
    + return headerValue;
    +}
    +
    ++ (NSArray *)parametersDictionariesSortedByKey:(NSArray *)parametersDictionaries {
    +
    + return [parametersDictionaries sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    + NSDictionary *d1 = (NSDictionary *)obj1;
    + NSDictionary *d2 = (NSDictionary *)obj2;
    +
    + NSString *key1 = [[d1 allKeys] lastObject];
    + NSString *key2 = [[d2 allKeys] lastObject];
    +
    + return [key1 compare:key2];
    + }];
    +
    +}
    +
    +- (NSString *)oauthNonce {
    + if(_testOauthNonce) return _testOauthNonce;
    +
    + return [NSString random32Characters];
    +}
    +
    +- (NSString *)includeEntities {
    + return @"true";
    +}
    +
    ++ (NSString *)signatureBaseStringWithHTTPMethod:(NSString *)httpMethod url:(NSURL *)url allParametersUnsorted:(NSArray *)parameters {
    + NSMutableArray *allParameters = [NSMutableArray arrayWithArray:parameters];
    +
    + NSArray *encodedParametersDictionaries = [self encodedParametersDictionaries:allParameters];
    +
    + NSArray *sortedEncodedParametersDictionaries = [self parametersDictionariesSortedByKey:encodedParametersDictionaries];
    +
    + /**/
    +
    + NSMutableArray *encodedParameters = [NSMutableArray array];
    +
    + for(NSDictionary *d in sortedEncodedParametersDictionaries) {
    + NSString *encodedKey = [[d allKeys] lastObject];
    + NSString *encodedValue = [[d allValues] lastObject];
    +
    + NSString *s = [NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue];
    +
    + [encodedParameters addObject:s];
    + }
    +
    + NSString *encodedParametersString = [encodedParameters componentsJoinedByString:@"&"];
    +
    + NSString *signatureBaseString = [NSString stringWithFormat:@"%@&%@&%@",
    + [httpMethod uppercaseString],
    + [[url normalizedForOauthSignatureString] urlEncodedString],
    + [encodedParametersString urlEncodedString]];
    +
    + return signatureBaseString;
    +}
    +
    ++ (NSString *)oauthSignatureWithHTTPMethod:(NSString *)httpMethod url:(NSURL *)url parameters:(NSArray *)parameters consumerSecret:(NSString *)consumerSecret tokenSecret:(NSString *)tokenSecret {
    + /*
    + The oauth_signature parameter contains a value which is generated by running all of the other request parameters and two secret values through a signing algorithm. The purpose of the signature is so that Twitter can verify that the request has not been modified in transit, verify the application sending the request, and verify that the application has authorization to interact with the user's account.
    + https://dev.twitter.com/docs/auth/creating-signature
    + */
    +
    + NSString *signatureBaseString = [[self class] signatureBaseStringWithHTTPMethod:httpMethod url:url allParametersUnsorted:parameters];
    +
    + /*
    + Note that there are some flows, such as when obtaining a request token, where the token secret is not yet known. In this case, the signing key should consist of the percent encoded consumer secret followed by an ampersand character '&'.
    + */
    +
    + NSString *encodedConsumerSecret = [consumerSecret urlEncodedString];
    + NSString *encodedTokenSecret = [tokenSecret urlEncodedString];
    +
    + NSString *signingKey = [NSString stringWithFormat:@"%@&", encodedConsumerSecret];
    +
    + if(encodedTokenSecret) {
    + signingKey = [signingKey stringByAppendingString:encodedTokenSecret];
    + }
    +
    + NSString *oauthSignature = [signatureBaseString signHmacSHA1WithKey:signingKey];
    +
    + return oauthSignature;
    +}
    +
    +- (BOOL)canVerifyCredentials {
    + return (_username && _password);
    +}
    +
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + if(_username == nil || _password == nil) return;
    +
    + [self postXAuthAccessTokenRequestWithUsername:_username password:_password successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
    + successBlock(screenName);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (NSString *)oauthSignatureMethod {
    + return @"HMAC-SHA1";
    +}
    +
    +- (NSString *)oauthTimestamp {
    + /*
    + The oauth_timestamp parameter indicates when the request was created. This value should be the number of seconds since the Unix epoch at the point the request is generated, and should be easily generated in most programming languages. Twitter will reject requests which were created too far in the past, so it is important to keep the clock of the computer generating requests in sync with NTP.
    + */
    +
    + if(_testOauthTimestamp) return _testOauthTimestamp;
    +
    + NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
    +
    + return [NSString stringWithFormat:@"%d", (int)timeInterval];
    +}
    +
    +- (NSString *)oauthVersion {
    + return @"1.0";
    +}
    +
    +- (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock oauthCallback:(NSString *)oauthCallback errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSString *theOAuthCallback = [oauthCallback length] ? oauthCallback : @"oob"; // out of band, ie PIN instead of redirect
    +
    + [self postResource:@"oauth/request_token"
    + baseURLString:@"https://api.twitter.com"
    + parameters:@{}
    + oauthCallback:theOAuthCallback
    + successBlock:^(id body) {
    +
    + NSDictionary *d = [body parametersDictionary];
    +
    + NSString *s = [NSString stringWithFormat:@"https://api.twitter.com/oauth/authorize?%@", body];
    +
    + NSURL *url = [NSURL URLWithString:s];
    +
    + self.oauthRequestToken = d[@"oauth_token"];
    + self.oauthRequestTokenSecret = d[@"oauth_token_secret"]; // unused
    +
    + successBlock(url, _oauthRequestToken);
    +
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postXAuthAccessTokenRequestWithUsername:(NSString *)username
    + password:(NSString *)password
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSDictionary *d = @{@"x_auth_username" : [username urlEncodedString],
    + @"x_auth_password" : [password urlEncodedString],
    + @"x_auth_mode" : @"client_auth"};
    +
    + [self postResource:@"oauth/access_token"
    + baseURLString:@"https://api.twitter.com"
    + parameters:d
    + successBlock:^(NSString *body) {
    + NSDictionary *dict = [body parametersDictionary];
    +
    + // https://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&user_id=USER_ID&screen_name=SCREEN_NAME
    +
    + self.oauthAccessToken = dict[@"oauth_token"];
    + self.oauthAccessTokenSecret = dict[@"oauth_token_secret"];
    +
    + successBlock(_oauthAccessToken, _oauthAccessTokenSecret, dict[@"user_id"], dict[@"screen_name"]);
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)postAccessTokenRequestWithPIN:(NSString *)pin
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + if([pin length] == 0) {
    + errorBlock([NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : @"PIN needed"}]);
    + return;
    + }
    +
    + //NSParameterAssert(pin);
    +
    + NSDictionary *d = @{@"oauth_verifier" : pin};
    +
    + [self postResource:@"oauth/access_token"
    + baseURLString:@"https://api.twitter.com"
    + parameters:d
    + successBlock:^(NSString *body) {
    + NSDictionary *dict = [body parametersDictionary];
    +
    + // https://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&user_id=USER_ID&screen_name=SCREEN_NAME
    +
    + self.oauthAccessToken = dict[@"oauth_token"];
    + self.oauthAccessTokenSecret = dict[@"oauth_token_secret"];
    +
    + successBlock(_oauthAccessToken, _oauthAccessTokenSecret, dict[@"user_id"], dict[@"screen_name"]);
    +
    + } errorBlock:^(NSError *error) {
    + errorBlock(error);
    + }];
    +}
    +
    +- (void)signRequest:(STHTTPRequest *)r isMediaUpload:(BOOL)isMediaUpload oauthCallback:(NSString *)oauthCallback {
    + NSParameterAssert(_oauthConsumerKey);
    + NSParameterAssert(_oauthConsumerSecret);
    +
    + NSMutableArray *oauthParameters = [NSMutableArray arrayWithObjects:
    + @{@"oauth_consumer_key" : [self oauthConsumerKey]},
    + @{@"oauth_nonce" : [self oauthNonce]},
    + @{@"oauth_signature_method" : [self oauthSignatureMethod]},
    + @{@"oauth_timestamp" : [self oauthTimestamp]},
    + @{@"oauth_version" : [self oauthVersion]}, nil];
    +
    + if([oauthCallback length]) [oauthParameters addObject:@{@"oauth_callback" : oauthCallback}];
    +
    + if(_oauthAccessToken) { // missing while authenticating with XAuth
    + [oauthParameters addObject:@{@"oauth_token" : [self oauthAccessToken]}];
    + } else if(_oauthRequestToken) {
    + [oauthParameters addObject:@{@"oauth_token" : [self oauthRequestToken]}];
    + }
    +
    + NSString *httpMethod = r.POSTDictionary ? @"POST" : @"GET";
    +
    + NSMutableArray *oauthAndPOSTParameters = [oauthParameters mutableCopy];
    +
    + if(r.POSTDictionary) {
    + [r.POSTDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + [oauthAndPOSTParameters addObject:@{ key : obj }];
    + }];
    + }
    +
    + NSMutableArray *oauthAndPOSTandGETParameters = [[r.url getParametersDictionaries] mutableCopy];
    + [oauthAndPOSTandGETParameters addObjectsFromArray:oauthAndPOSTParameters];
    +
    + NSString *signature = [[self class] oauthSignatureWithHTTPMethod:httpMethod
    + url:r.url
    + parameters:isMediaUpload ? oauthParameters : oauthAndPOSTandGETParameters
    + consumerSecret:_oauthConsumerSecret
    + tokenSecret:_oauthAccessTokenSecret];
    +
    + [oauthAndPOSTParameters release];
    + [oauthAndPOSTandGETParameters release];
    +
    + [oauthParameters addObject:@{@"oauth_signature" : signature}];
    +
    + NSString *s = [[self class] oauthHeaderValueWithParameters:oauthParameters];
    +
    + [r setHeaderWithName:@"Authorization" value:s];
    +}
    +
    +- (void)signRequest:(STHTTPRequest *)r isMediaUpload:(BOOL)isMediaUpload {
    + [self signRequest:r isMediaUpload:isMediaUpload oauthCallback:nil];
    +}
    +
    +- (void)signRequest:(STHTTPRequest *)r {
    + [self signRequest:r isMediaUpload:NO];
    +}
    +
    +- (void)getResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSMutableString *urlString = [NSMutableString stringWithFormat:@"https://api.twitter.com/1.1/%@", resource];
    +
    + NSMutableArray *parameters = [NSMutableArray array];
    +
    + [params enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + NSString *s = [NSString stringWithFormat:@"%@=%@", key, obj];
    + [parameters addObject:s];
    + }];
    +
    + if([parameters count]) {
    + NSString *parameterString = [parameters componentsJoinedByString:@"&"];
    +
    + [urlString appendFormat:@"?%@", parameterString];
    + }
    +
    + __block STHTTPRequest *r = [STHTTPRequest requestWithURLString:urlString];
    +
    + [self signRequest:r];
    +
    + r.completionBlock = ^(NSDictionary *headers, NSString *body) {
    +
    + NSError *jsonError = nil;
    + id json = [NSJSONSerialization JSONObjectWithData:r.responseData options:NSJSONReadingMutableLeaves error:&jsonError];
    +
    + if(json == nil) {
    + errorBlock(jsonError);
    + return;
    + }
    +
    + successBlock(json);
    + };
    +
    + r.errorBlock = ^(NSError *error) {
    + errorBlock(error);
    + };
    +
    + [r startAsynchronous];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + baseURLString:(NSString *)baseURLString // no trailing slash
    + parameters:(NSDictionary *)params
    + oauthCallback:(NSString *)oauthCallback
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, resource];
    +
    + __block STHTTPRequest *r = [STHTTPRequest requestWithURLString:urlString];
    +
    + r.POSTDictionary = params;
    +
    + NSString *postKey = [params valueForKey:@"postDataKey"];
    + // https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media
    + NSData *postData = [params valueForKey:postKey];
    +
    + NSMutableDictionary *mutableParams = [[params mutableCopy] autorelease];
    + [mutableParams removeObjectForKey:@"postDataKey"];
    + if(postData) {
    + [mutableParams removeObjectForKey:postKey];
    +
    + [r setDataToUpload:postData parameterName:postKey mimeType:@"application/octet-stream" fileName:@"media.jpg"];
    + }
    +
    + [self signRequest:r isMediaUpload:(postData != nil) oauthCallback:oauthCallback];
    +
    + // POST parameters must not be encoded while posting media, or spaces will appear as %20 in the status
    + r.encodePOSTDictionary = (postData == nil);
    +
    + r.POSTDictionary = mutableParams ? mutableParams : @{};
    +
    + r.completionBlock = ^(NSDictionary *headers, NSString *body) {
    +
    + NSError *jsonError = nil;
    + id json = [NSJSONSerialization JSONObjectWithData:r.responseData options:NSJSONReadingMutableLeaves error:&jsonError];
    +
    + if(json == nil) {
    + errorBlock(jsonError);
    + return;
    + }
    +
    + successBlock(json);
    + };
    +
    + r.errorBlock = ^(NSError *error) {
    +
    + // do our best to extract Twitter error message from responseString
    +
    + NSError *regexError = nil;
    + NSString *errorString = [r.responseString firstMatchWithRegex:@"<error>(.*)</error>" error:&regexError];
    +
    + if(errorString) {
    + error = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : errorString}];
    + } else if(r.responseString && [r.responseString length] < 64) {
    + error = [NSError errorWithDomain:NSStringFromClass([self class]) code:0 userInfo:@{NSLocalizedDescriptionKey : r.responseString}];
    + }
    +
    + errorBlock(error);
    + };
    +
    + [r startAsynchronous];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + baseURLString:(NSString *)baseURLString // no trailing slash
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [self postResource:resource
    + baseURLString:baseURLString
    + parameters:params
    + oauthCallback:nil
    + successBlock:successBlock
    + errorBlock:errorBlock];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + oauthCallback:(NSString *)oauthCallback
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [self postResource:resource baseURLString:@"https://api.twitter.com/1.1" parameters:params oauthCallback:oauthCallback successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +- (void)postResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock {
    +
    + [self postResource:resource parameters:params oauthCallback:nil successBlock:successBlock errorBlock:errorBlock];
    +}
    +
    +@end
    +
    +@implementation NSURL (STTwitterOAuth)
    +
    +- (NSArray *)getParametersDictionaries {
    +
    + NSString *q = [self query];
    +
    + NSArray *getParameters = [q componentsSeparatedByString:@"&"];
    +
    + NSMutableArray *ma = [NSMutableArray array];
    +
    + for(NSString *s in getParameters) {
    + NSArray *kv = [s componentsSeparatedByString:@"="];
    + NSAssert([kv count] == 2, @"-- bad length");
    + if([kv count] != 2) continue;
    + [ma addObject:@{kv[0] : kv[1]}];
    + }
    +
    + return ma;
    +}
    +
    +- (NSString *)normalizedForOauthSignatureString {
    + return [NSString stringWithFormat:@"%@://%@%@", [self scheme], [self host], [self path]];
    +}
    +
    +@end
    +
    +@implementation NSString (STTwitterOAuth)
    +
    ++ (NSString *)randomString {
    + CFUUIDRef cfuuid = CFUUIDCreate (kCFAllocatorDefault);
    + NSString *uuid = (NSString *)CFUUIDCreateString (kCFAllocatorDefault, cfuuid);
    + CFRelease (cfuuid);
    + return [uuid autorelease];
    +}
    +
    ++ (NSString *)random32Characters {
    + NSString *randomString = [self randomString];
    +
    + NSAssert([randomString length] >= 32, @"");
    +
    + return [randomString substringToIndex:32];
    +}
    +
    +- (NSString *)signHmacSHA1WithKey:(NSString *)key {
    +
    + unsigned char buf[CC_SHA1_DIGEST_LENGTH];
    + CCHmac(kCCHmacAlgSHA1, [key UTF8String], [key length], [self UTF8String], [self length], buf);
    + NSData *data = [NSData dataWithBytes:buf length:CC_SHA1_DIGEST_LENGTH];
    + return [data base64Encoding];
    +}
    +
    +- (NSDictionary *)parametersDictionary {
    +
    + NSArray *parameters = [self componentsSeparatedByString:@"&"];
    +
    + NSMutableDictionary *md = [NSMutableDictionary dictionary];
    +
    + for(NSString *parameter in parameters) {
    + NSArray *keyValue = [parameter componentsSeparatedByString:@"="];
    + if([keyValue count] != 2) {
    + continue;
    + }
    +
    + [md setObject:keyValue[1] forKey:keyValue[0]];
    + }
    +
    + return md;
    +}
    +
    +- (NSString *)urlEncodedString {
    + // https://dev.twitter.com/docs/auth/percent-encoding-parameters
    + // http://tools.ietf.org/html/rfc3986#section-2.1
    +
    + return [self st_stringByAddingRFC3986PercentEscapesUsingEncoding:NSUTF8StringEncoding];
    +}
    +
    +@end
    +
    +@implementation NSData (STTwitterOAuth)
    +
    +- (NSString *)base64EncodedString {
    +
    +#if TARGET_OS_IPHONE
    + return [self base64Encoding]; // private API
    +#else
    +
    + CFDataRef retval = NULL;
    + SecTransformRef encodeTrans = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
    + if (encodeTrans == NULL) return nil;
    +
    + if (SecTransformSetAttribute(encodeTrans, kSecTransformInputAttributeName, (CFDataRef)self, NULL)) {
    + retval = SecTransformExecute(encodeTrans, NULL);
    + }
    + CFRelease(encodeTrans);
    +
    + NSString *s = [[NSString alloc] initWithData:(NSData *)retval encoding:NSUTF8StringEncoding];
    +
    + if(retval) {
    + CFRelease(retval);
    + }
    +
    + return [s autorelease];
    +
    +#endif
    +
    +}
    +@end
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/STTwitterOAuthProtocol.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,45 @@
    +//
    +// STOAuthProtocol.h
    +// STTwitterRequests
    +//
    +// Created by Nicolas Seriot on 9/18/12.
    +// Copyright (c) 2012 Nicolas Seriot. All rights reserved.
    +//
    +
    +#import <Foundation/Foundation.h>
    +
    +@protocol STTwitterOAuthProtocol <NSObject>
    +
    +- (BOOL)canVerifyCredentials;
    +- (void)verifyCredentialsWithSuccessBlock:(void(^)(NSString *username))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)getResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)postResource:(NSString *)resource
    + parameters:(NSDictionary *)params
    + successBlock:(void(^)(id response))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +@optional
    +
    +- (void)postTokenRequest:(void(^)(NSURL *url, NSString *oauthToken))successBlock
    + oauthCallback:(NSString *)oauthCallback
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)postAccessTokenRequestWithPIN:(NSString *)pin
    + successBlock:(void(^)(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName))successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (void)invalidateBearerTokenWithSuccessBlock:(void(^)())successBlock
    + errorBlock:(void(^)(NSError *error))errorBlock;
    +
    +- (NSString *)oauthAccessToken;
    +- (NSString *)oauthAccessTokenSecret;
    +
    +- (NSString *)bearerToken;
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.h Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,90 @@
    +/*
    + Copyright (c) 2012, Nicolas Seriot
    + All rights reserved.
    +
    + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    +
    + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    + * Neither the name of the Nicolas Seriot nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    +
    + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    + */
    +
    +#import <Foundation/Foundation.h>
    +
    +extern NSUInteger const kSTHTTPRequestCancellationError;
    +extern NSUInteger const kSTHTTPRequestDefaultTimeout;
    +
    +@class STHTTPRequest;
    +
    +typedef void (^uploadProgressBlock_t)(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite);
    +typedef void (^completionBlock_t)(NSDictionary *headers, NSString *body);
    +typedef void (^errorBlock_t)(NSError *error);
    +
    +@interface STHTTPRequest : NSObject <NSURLConnectionDelegate>
    +
    +@property (copy) uploadProgressBlock_t uploadProgressBlock;
    +@property (copy) completionBlock_t completionBlock;
    +@property (copy) errorBlock_t errorBlock;
    +@property (nonatomic) NSStringEncoding postDataEncoding;
    +@property (nonatomic, retain) NSDictionary *POSTDictionary;
    +@property (nonatomic, retain) NSData *POSTData;
    +@property (nonatomic, retain) NSMutableDictionary *requestHeaders;
    +@property (nonatomic, readonly) NSInteger responseStatus;
    +@property (nonatomic, retain, readonly) NSString *responseStringEncodingName;
    +@property (nonatomic, retain, readonly) NSDictionary *responseHeaders;
    +@property (nonatomic, retain, readonly) NSURL *url;
    +@property (nonatomic, retain, readonly) NSMutableData *responseData;
    +@property (nonatomic, retain, readonly) NSError *error;
    +@property (nonatomic, retain) NSString *responseString;
    +@property (nonatomic) NSStringEncoding forcedResponseEncoding;
    +@property (nonatomic) BOOL encodePOSTDictionary; // default YES
    +@property (nonatomic, assign) NSUInteger timeoutSeconds;
    +@property (nonatomic) BOOL addCredentialsToURL; // default NO
    +//@property (nonatomic, strong) NSString *requestMethod;
    +
    ++ (STHTTPRequest *)requestWithURL:(NSURL *)url;
    ++ (STHTTPRequest *)requestWithURLString:(NSString *)urlString;
    +
    +- (NSString *)startSynchronousWithError:(NSError **)error;
    +- (void)startAsynchronous;
    +- (void)cancel;
    +
    +// Cookies
    ++ (void)addCookieWithName:(NSString *)name value:(NSString *)value url:(NSURL *)url;
    +- (void)addCookieWithName:(NSString *)name value:(NSString *)value;
    +- (NSArray *)requestCookies;
    ++ (NSArray *)sessionCookies;
    ++ (void)deleteSessionCookies;
    +
    +// Credentials
    ++ (NSURLCredential *)sessionAuthenticationCredentialsForURL:(NSURL *)requestURL;
    +- (void)setUsername:(NSString *)username password:(NSString *)password;
    +- (NSString *)username;
    +- (NSString *)password;
    ++ (void)deleteAllCredentials;
    +
    +// Headers
    +- (void)setHeaderWithName:(NSString *)name value:(NSString *)value;
    +- (void)removeHeaderWithName:(NSString *)name;
    +- (NSDictionary *)responseHeaders;
    +
    +// Upload
    +- (void)setFileToUpload:(NSString *)path parameterName:(NSString *)param;
    +- (void)setDataToUpload:(NSData *)data parameterName:(NSString *)param;
    +- (void)setDataToUpload:(NSData *)data parameterName:(NSString *)param mimeType:(NSString *)mimeType fileName:(NSString *)fileName;
    +
    +// Session
    ++ (void)clearSession; // delete all credentials and cookies
    +
    +@end
    +
    +@interface NSError (STHTTPRequest)
    +- (BOOL)st_isAuthenticationError;
    +- (BOOL)st_isCancellationError;
    +@end
    +
    +@interface NSString (RFC3986)
    +- (NSString *)st_stringByAddingRFC3986PercentEscapesUsingEncoding:(NSStringEncoding)encoding;
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Plugins/Twitter Plugin/STTwitter/Vendor/STHTTPRequest.m Wed Mar 20 12:02:05 2013 -0400
    @@ -0,0 +1,673 @@
    +//
    +// STHTTPRequest.m
    +// STHTTPRequest
    +//
    +// Created by Nicolas Seriot on 07.11.11.
    +// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
    +//
    +
    +#import "STHTTPRequest.h"
    +
    +#define DEBUG 0
    +
    +NSUInteger const kSTHTTPRequestCancellationError = 1;
    +NSUInteger const kSTHTTPRequestDefaultTimeout = 5;
    +
    +static NSMutableDictionary *sharedCredentialsStorage = nil;
    +
    +@interface STHTTPRequest ()
    +@property (nonatomic) NSInteger responseStatus;
    +@property (nonatomic, retain) NSURLConnection *connection;
    +@property (nonatomic, retain) NSMutableData *responseData;
    +@property (nonatomic, retain) NSString *responseStringEncodingName;
    +@property (nonatomic, retain) NSDictionary *responseHeaders;
    +@property (nonatomic, retain) NSURL *url;
    +@property (nonatomic, retain) NSError *error;
    +@property (nonatomic, retain) NSString *POSTFilePath;
    +@property (nonatomic, retain) NSData *POSTFileData;
    +@property (nonatomic, retain) NSString *POSTFileMimeType;
    +@property (nonatomic, retain) NSString *POSTFileName;
    +@property (nonatomic, retain) NSString *POSTFileParameter;
    +@end
    +
    +@interface NSData (Base64)
    +- (NSString *)base64Encoding; // private API
    +@end
    +
    +@implementation STHTTPRequest
    +
    +#pragma mark Initializers
    +
    ++ (STHTTPRequest *)requestWithURL:(NSURL *)url {
    + if(url == nil) return nil;
    + return [[(STHTTPRequest *)[self alloc] initWithURL:url] autorelease];
    +}
    +
    ++ (STHTTPRequest *)requestWithURLString:(NSString *)urlString {
    + NSURL *url = [NSURL URLWithString:urlString];
    + return [self requestWithURL:url];
    +}
    +
    +- (STHTTPRequest *)initWithURL:(NSURL *)theURL {
    +
    + if (self = [super init]) {
    + _url = [theURL retain];
    + _responseData = [[NSMutableData alloc] init];
    + _requestHeaders = [[NSMutableDictionary dictionary] retain];
    + _postDataEncoding = NSUTF8StringEncoding;
    + _encodePOSTDictionary = YES;
    + _addCredentialsToURL = NO;
    + _timeoutSeconds = kSTHTTPRequestDefaultTimeout;
    + }
    +
    + return self;
    +}
    +
    ++ (void)clearSession {
    + [self deleteAllCookies];
    + [self deleteAllCredentials];
    +}
    +
    +- (void)dealloc {
    + if(_completionBlock) [_completionBlock release];
    + if(_errorBlock) [_errorBlock release];
    + if(_uploadProgressBlock) [_uploadProgressBlock release];
    +
    + [_connection release];
    + [_responseStringEncodingName release];
    + [_requestHeaders release];
    + [_url release];
    + [_responseData release];
    + [_responseHeaders release];
    + [_responseString release];
    + [_POSTDictionary release];
    + [_POSTData release];
    + [_POSTFilePath release];
    + [_POSTFileData release];
    + [_POSTFileMimeType release];
    + [_POSTFileName release];
    + [_POSTFileParameter release];
    + [_error release];
    +
    + [super dealloc];
    +}
    +
    +#pragma mark Credentials
    +
    ++ (NSMutableDictionary *)sharedCredentialsStorage {
    + if(sharedCredentialsStorage == nil) {
    + sharedCredentialsStorage = [[NSMutableDictionary dictionary] retain];
    + }
    + return sharedCredentialsStorage;
    +}
    +
    ++ (NSURLCredential *)sessionAuthenticationCredentialsForURL:(NSURL *)requestURL {
    + return [[[self class] sharedCredentialsStorage] valueForKey:[requestURL host]];
    +}
    +
    ++ (void)deleteAllCredentials {
    + [sharedCredentialsStorage autorelease];
    + sharedCredentialsStorage = [[NSMutableDictionary dictionary] retain];
    +}
    +
    +- (void)setCredentialForCurrentHost:(NSURLCredential *)c {
    +#if DEBUG
    + NSAssert(_url, @"missing url to set credential");
    +#endif
    + [[[self class] sharedCredentialsStorage] setObject:c forKey:[_url host]];
    +}
    +
    +- (NSURLCredential *)credentialForCurrentHost {
    + return [[[self class] sharedCredentialsStorage] valueForKey:[_url host]];
    +}
    +
    +- (void)setUsername:(NSString *)username password:(NSString *)password {
    + NSURLCredential *c = [NSURLCredential credentialWithUser:username
    + password:password
    + persistence:NSURLCredentialPersistenceNone];
    +
    + [self setCredentialForCurrentHost:c];
    +}
    +
    +- (NSString *)username {
    + return [[self credentialForCurrentHost] user];
    +}
    +
    +- (NSString *)password {
    + return [[self credentialForCurrentHost] password];
    +}
    +
    +#pragma mark Cookies
    +
    ++ (NSArray *)sessionCookies {
    + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    + return [cookieStorage cookies];
    +}
    +
    ++ (void)deleteSessionCookies {
    + for(NSHTTPCookie *cookie in [self sessionCookies]) {
    + [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    + }
    +}
    +
    ++ (void)deleteAllCookies {
    + NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    + NSArray *cookies = [cookieStorage cookies];
    + for (NSHTTPCookie *cookie in cookies) {
    + [cookieStorage deleteCookie:cookie];
    + }
    +}
    +
    ++ (void)addCookie:(NSHTTPCookie *)cookie forURL:(NSURL *)url {
    + NSArray *cookies = [NSArray arrayWithObject:cookie];
    +
    + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:url mainDocumentURL:nil];
    +}
    +
    ++ (void)addCookieWithName:(NSString *)name value:(NSString *)value url:(NSURL *)url {
    +
    + NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    + name, NSHTTPCookieName,
    + value, NSHTTPCookieValue,
    + [url host], NSHTTPCookieDomain,
    + [url host], NSHTTPCookieOriginURL,
    + @"FALSE", NSHTTPCookieDiscard,
    + @"/", NSHTTPCookiePath,
    + @"0", NSHTTPCookieVersion,
    + [[NSDate date] dateByAddingTimeInterval:3600 * 24 * 30], NSHTTPCookieExpires,
    + nil];
    +
    + NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    +
    + [[self class] addCookie:cookie forURL:url];
    +}
    +
    +- (NSArray *)requestCookies {
    + return [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[_url absoluteURL]];
    +}
    +
    +- (void)addCookie:(NSHTTPCookie *)cookie {
    + [[self class] addCookie:cookie forURL:_url];
    +}
    +
    +- (void)addCookieWithName:(NSString *)name value:(NSString *)value {
    + [[self class] addCookieWithName:name value:value url:_url];
    +}
    +
    +#pragma mark Headers
    +
    +- (void)setHeaderWithName:(NSString *)name value:(NSString *)value {
    + if(name == nil || value == nil) return;
    + [[self requestHeaders] setObject:value forKey:name];
    +}
    +
    +- (void)removeHeaderWithName:(NSString *)name {
    + if(name == nil) return;
    + [[self requestHeaders] removeObjectForKey:name];
    +}
    +
    ++ (NSURL *)urlByAddingCredentials:(NSURLCredential *)credentials toURL:(NSURL *)url {
    +
    + if(credentials == nil) return nil; // no credentials to add
    +
    + NSString *scheme = [url scheme];
    + NSString *host = [url host];
    +
    + BOOL hostAlreadyContainsCredentials = [host rangeOfString:@"@"].location != NSNotFound;
    + if(hostAlreadyContainsCredentials) return url;
    +
    + NSMutableString *resourceSpecifier = [[[url resourceSpecifier] mutableCopy] autorelease];
    +
    + if([resourceSpecifier hasPrefix:@"//"] == NO) return nil;
    +
    + NSString *userPassword = [NSString stringWithFormat:@"%@:%@@", credentials.user, credentials.password];
    +
    + [resourceSpecifier insertString:userPassword atIndex:2];
    +
    + NSString *urlString = [NSString stringWithFormat:@"%@:%@", scheme, resourceSpecifier];
    +
    + return [NSURL URLWithString:urlString];
    +}
    +
    +- (NSURLRequest *)requestByAddingCredentialsToURL:(BOOL)useCredentialsInURL {
    +
    + NSURL *theURL = nil;
    +
    + if(useCredentialsInURL) {
    + NSURLCredential *credential = [self credentialForCurrentHost];
    + if(credential == nil) return nil;
    + theURL = [[self class] urlByAddingCredentials:credential toURL:_url];
    + if(theURL == nil) return nil;
    + } else {
    + theURL = _url;
    + }
    +
    + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:theURL];
    +
    + // Set properties
    + request.timeoutInterval = self.timeoutSeconds;
    +// if (self.requestMethod)
    +// request.HTTPMethod = self.requestMethod;
    +
    + // escape POST dictionary keys and values if needed
    + if(_encodePOSTDictionary) {
    + NSMutableDictionary *escapedPOSTDictionary = _POSTDictionary ? [NSMutableDictionary dictionary] : nil;
    + [_POSTDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + NSString *k = [key st_stringByAddingRFC3986PercentEscapesUsingEncoding:_postDataEncoding];
    + NSString *v = [[obj description] st_stringByAddingRFC3986PercentEscapesUsingEncoding:_postDataEncoding];
    + [escapedPOSTDictionary setValue:v forKey:k];
    + }];
    + self.POSTDictionary = escapedPOSTDictionary;
    + }
    +
    + if(_POSTFileParameter && (_POSTFilePath || _POSTFileData)) {
    +
    + if(_POSTDictionary == nil) self.POSTDictionary = @{};
    +
    + NSData *fileData = nil;
    + NSString *mimeType = nil;
    + NSString *fileName = nil;
    +
    + if (_POSTFilePath) {
    + NSError *readingError = nil;
    + fileData = [NSData dataWithContentsOfFile:_POSTFilePath options:0 error:&readingError];
    + if(fileData == nil ) {
    + return nil;
    + }
    +
    + fileName = [_POSTFilePath lastPathComponent];
    + } else {
    + fileData = _POSTFileData;
    + if (_POSTFileName) {
    + fileName = _POSTFileName;
    + }
    + }
    +
    + mimeType = _POSTFileMimeType ? _POSTFileMimeType : @"application/octet-stream";
    +
    + NSString *boundary = @"----------kStHtTpReQuEsTbOuNdArY";
    +
    + NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    + [request addValue:contentType forHTTPHeaderField:@"Content-Type"];
    +
    + NSMutableData *body = [NSMutableData data];
    +
    + [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    +
    + NSString *fileNameContentDisposition = fileName ? [NSString stringWithFormat:@"filename=\"%@\"", fileName] : @"";
    + NSString *contentDisposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; %@\r\n", _POSTFileParameter, fileNameContentDisposition];
    +
    + [body appendData:[contentDisposition dataUsingEncoding:NSUTF8StringEncoding]];
    + [body appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
    + [body appendData:fileData];
    +
    + [_POSTDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    + [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
    + [body appendData:[[obj description] dataUsingEncoding:NSUTF8StringEncoding]];
    + }];
    +
    + [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    +
    + [request setHTTPMethod:@"POST"];
    + [request setValue:[NSString stringWithFormat:@"%lu", [body length]] forHTTPHeaderField:@"Content-Length"];
    + [request setHTTPBody:body];
    +
    + } else if(_POSTDictionary != nil) { // may be empty (POST request without body)
    +
    + if(_encodePOSTDictionary) {
    +
    + CFStringEncoding cfStringEncoding = CFStringConvertNSStringEncodingToEncoding(_postDataEncoding);
    + NSString *encodingName = (NSString *)CFStringConvertEncodingToIANACharSetName(cfStringEncoding);
    +
    + if(encodingName) {
    + NSString *contentTypeValue = [NSString stringWithFormat:@"application/x-www-form-urlencoded ; charset=%@", encodingName];
    + [self setHeaderWithName:@"Content-Type" value:contentTypeValue];
    + }
    + }
    +
    + NSMutableArray *ma = [NSMutableArray arrayWithCapacity:[_POSTDictionary count]];
    +
    + for(NSString *k in _POSTDictionary) {
    + NSString *kv = [NSString stringWithFormat:@"%@=%@", k, [_POSTDictionary objectForKey:k]];
    + [ma addObject:kv];
    + }
    +
    + // we sort POST parameters in order to get deterministric requests, hence unit testable
    + [ma sortUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
    + return [obj1 compare:obj2];
    + }];
    +
    + NSString *s = [ma componentsJoinedByString:@"&"];
    +
    + NSData *data = [s dataUsingEncoding:_postDataEncoding allowLossyConversion:YES];
    +
    + [request setHTTPMethod:@"POST"];
    + [request setValue:[NSString stringWithFormat:@"%lu", [data length]] forHTTPHeaderField:@"Content-Length"];
    + [request setHTTPBody:data];
    + } else if (_POSTData != nil) {
    + [request setHTTPMethod:@"POST"];
    + [request setValue:[NSString stringWithFormat:@"%lu", [_POSTData length]] forHTTPHeaderField:@"Content-Length"];
    + [request setHTTPBody:_POSTData];
    + }
    +
    + [_requestHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + [request addValue:obj forHTTPHeaderField:key];
    + }];
    +
    + NSURLCredential *credentialForHost = [self credentialForCurrentHost];
    +
    + if(credentialForHost) {
    + NSString *authString = [NSString stringWithFormat:@"%@:%@", credentialForHost.user, credentialForHost.password];
    + NSData *authData = [authString dataUsingEncoding:NSASCIIStringEncoding];
    + NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64Encoding]];
    + [request addValue:authValue forHTTPHeaderField:@"Authorization"];
    + }
    +
    + return request;
    +}
    +
    +- (NSURLRequest *)request {
    + return [self requestByAddingCredentialsToURL:NO];
    +}
    +
    +- (NSURLRequest *)requestByAddingCredentialsToURL {
    + return [self requestByAddingCredentialsToURL:YES];
    +}
    +
    +#pragma mark Upload
    +
    +- (void)setFileToUpload:(NSString *)path parameterName:(NSString *)param {
    + self.POSTFilePath = path;
    + self.POSTFileParameter = param;
    +}
    +
    +- (void)setDataToUpload:(NSData *)data parameterName:(NSString *)param {
    + self.POSTFileData = data;
    + self.POSTFileParameter = param;
    +}
    +
    +- (void)setDataToUpload:(NSData *)data parameterName:(NSString *)param mimeType:(NSString *)mimeType fileName:(NSString *)fileName
    +{
    + self.POSTFileData = data;
    + self.POSTFileParameter = param;
    + self.POSTFileMimeType = mimeType;
    + self.POSTFileName = fileName;
    +}
    +
    +#pragma mark Response
    +
    +- (NSString *)stringWithData:(NSData *)data encodingName:(NSString *)encodingName {
    + if(data == nil) return nil;
    +
    + if(_forcedResponseEncoding > 0) {
    + return [[[NSString alloc] initWithData:data encoding:_forcedResponseEncoding] autorelease];
    + }
    +
    + NSStringEncoding encoding = NSUTF8StringEncoding;
    +
    + /* try to use encoding declared in HTTP response headers */
    +
    + if(encodingName != nil) {
    +
    + encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName));
    +
    + if(encoding == kCFStringEncodingInvalidId) {
    + encoding = NSUTF8StringEncoding; // by default
    + }
    + }
    +
    + return [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
    +}
    +
    +- (void)logRequest:(NSURLRequest *)request {
    +
    + NSLog(@"--------------------------------------");
    +
    + NSString *method = _POSTDictionary ? @"POST" : @"GET";
    +
    + NSLog(@"%@ %@", method, [request URL]);
    +
    + NSMutableDictionary *headers = [[[self requestHeaders] mutableCopy] autorelease];
    +
    + [headers addEntriesFromDictionary:[request allHTTPHeaderFields]];
    +
    + if([headers count]) NSLog(@"HEADERS");
    +
    + [headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + NSLog(@"\t %@ = %@", key, obj);
    + }];
    +
    + NSArray *cookies = [self requestCookies];
    +
    + if([cookies count]) NSLog(@"COOKIES");
    +
    + for(NSHTTPCookie *cookie in cookies) {
    + NSLog(@"\t %@ = %@", [cookie name], [cookie value]);
    + }
    +
    + NSDictionary *d = [self POSTDictionary];
    +
    + if([d count]) NSLog(@"POST DATA");
    +
    + [d enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    + NSLog(@"\t %@ = %@", key, obj);
    + }];
    +
    + if (_POSTFileParameter && _POSTFilePath) {
    + NSLog(@"UPLOAD FILE");
    + NSLog(@"\t %@ = %@", _POSTFileParameter, _POSTFilePath);
    + } else if (_POSTFileParameter && _POSTFileData) {
    + NSLog(@"UPLOAD DATA");
    + NSLog(@"\t %@ = [%lu bytes]", _POSTFileParameter, [_POSTFileData length]);
    + } else if (_POSTData) {
    + NSLog(@"UPLOAD DATA");
    + NSLog(@"\t [%lu bytes]", [_POSTData length]);
    + }
    +
    + NSLog(@"--------------------------------------");
    +}
    +
    +#pragma mark Start Request
    +
    +- (void)startAsynchronous {
    +
    + NSURLRequest *request = [self requestByAddingCredentialsToURL:_addCredentialsToURL];
    +
    +#if DEBUG
    + [self logRequest:request];
    +#endif
    +
    + // NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
    + // http://www.pixeldock.com/blog/how-to-avoid-blocked-downloads-during-scrolling/
    + self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
    + [_connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    + [_connection start];
    +
    + if(_connection == nil) {
    + NSString *s = @"can't create connection";
    + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:s forKey:NSLocalizedDescriptionKey];
    + self.error = [NSError errorWithDomain:NSStringFromClass([self class])
    + code:0
    + userInfo:userInfo];
    + _errorBlock(_error);
    + }
    +}
    +
    +- (NSString *)startSynchronousWithError:(NSError **)e {
    +
    + self.responseHeaders = nil;
    + self.responseStatus = 0;
    +
    + NSURLRequest *request = [self requestByAddingCredentialsToURL:_addCredentialsToURL];
    +
    + NSURLResponse *urlResponse = nil;
    +
    + NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:e];
    + if(data == nil) return nil;
    +
    + self.responseData = [NSMutableData dataWithData:data];
    +
    + if([urlResponse isKindOfClass:[NSHTTPURLResponse class]]) {
    +
    + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)urlResponse;
    +
    + self.responseHeaders = [httpResponse allHeaderFields];
    + self.responseStatus = [httpResponse statusCode];
    + self.responseStringEncodingName = [httpResponse textEncodingName];
    + }
    +
    + self.responseString = [self stringWithData:_responseData encodingName:_responseStringEncodingName];
    +
    + if(_responseStatus >= 400) {
    + if(e) *e = [NSError errorWithDomain:NSStringFromClass([self class]) code:_responseStatus userInfo:nil];
    + }
    +
    + return _responseString;
    +}
    +
    +- (void)cancel {
    + [_connection cancel];
    +
    + NSString *s = @"Connection was cancelled.";
    + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:s forKey:NSLocalizedDescriptionKey];
    + self.error = [NSError errorWithDomain:NSStringFromClass([self class])
    + code:kSTHTTPRequestCancellationError
    + userInfo:userInfo];
    + _errorBlock(_error);
    +}
    +
    +#pragma mark NSURLConnectionDelegate
    +
    +-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
    +{
    + //return YES to say that we have the necessary credentials to access the requested resource
    + return YES;
    +}
    +
    +- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    +
    + // Server Trust authentication
    + if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
    + NSURLCredential *serverTrustCredential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    + [challenge.sender useCredential:serverTrustCredential forAuthenticationChallenge:challenge];
    + return;
    + }
    +
    + // Digest authentication
    + else if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
    +
    + if([challenge previousFailureCount] == 0) {
    + NSURLCredential *credential = [self credentialForCurrentHost];
    + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
    + } else {
    + [[[self class] sharedCredentialsStorage] removeObjectForKey:[_url host]];
    + [connection cancel];
    + [[challenge sender] cancelAuthenticationChallenge:challenge];
    + }
    + }
    +
    + // Basic authentication
    + else if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodHTTPBasic]) {
    +
    + // We proactively add authentication into headers.
    + // At this point, the credentials we provided are wrong,
    + // so we delete them and cancel the connection.
    + [[[self class] sharedCredentialsStorage] removeObjectForKey:[_url host]];
    + [connection cancel];
    + [[challenge sender] cancelAuthenticationChallenge:challenge];
    +
    + }
    +
    + // Unhandled
    + else
    + {
    + NSLog(@"Unhandled authentication challenge type - %@", [[challenge protectionSpace] authenticationMethod]);
    + [connection cancel];
    + [[challenge sender] cancelAuthenticationChallenge:challenge];
    + }
    +
    +}
    +
    +- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
    + if (_uploadProgressBlock) {
    + _uploadProgressBlock(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    + }
    +}
    +
    +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    +
    + if([response isKindOfClass:[NSHTTPURLResponse class]]) {
    + NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
    + self.responseHeaders = [r allHeaderFields];
    + self.responseStatus = [r statusCode];
    + self.responseStringEncodingName = [r textEncodingName];
    + }
    +
    + [_responseData setLength:0];
    +}
    +
    +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData {
    + [_responseData appendData:theData];
    +}
    +
    +- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    + self.responseString = [self stringWithData:_responseData encodingName:_responseStringEncodingName];
    +
    + if(_responseStatus >= 400) {
    + self.error = [NSError errorWithDomain:NSStringFromClass([self class]) code:_responseStatus userInfo:nil];
    + _errorBlock(_error);
    + return;
    + }
    +
    + _completionBlock(_responseHeaders, [self responseString]);
    +}
    +
    +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)e {
    + self.error = e;
    + _errorBlock(_error);
    +}
    +
    +@end
    +
    +/**/
    +
    +@implementation NSError (STHTTPRequest)
    +
    +- (BOOL)st_isAuthenticationError {
    + if([[self domain] isEqualToString:NSURLErrorDomain] == NO) return NO;
    +
    + return ([self code] == kCFURLErrorUserCancelledAuthentication || [self code] == kCFURLErrorUserAuthenticationRequired);
    +}
    +
    +- (BOOL)st_isCancellationError {
    + if([[self domain] isEqualToString:@"STHTTPRequest"] == NO) return NO;
    +
    + return ([self code] == kSTHTTPRequestCancellationError);
    +}
    +
    +@end
    +
    +@implementation NSString (RFC3986)
    +- (NSString *)st_stringByAddingRFC3986PercentEscapesUsingEncoding:(NSStringEncoding)encoding {
    +
    + NSString *s = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
    + (CFStringRef)self,
    + NULL,
    + CFSTR("!*'();:@&=+$,/?%#[]"),
    + kCFStringEncodingUTF8);
    + return [s autorelease];
    +}
    +@end
    +
    +/**/
    +
    +#if DEBUG
    +@implementation NSURLRequest (IgnoreSSLValidation)
    +
    ++ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host {
    + return NO;
    +}
    +
    +@end
    +#endif