├── README.md ├── SnapchatHax.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── alex.xcuserdatad │ └── xcschemes │ ├── SnapchatHax.xcscheme │ └── xcschememanagement.plist ├── SnapchatHax ├── ANAppDelegate.h ├── ANAppDelegate.m ├── ANLoginVC.h ├── ANLoginVC.m ├── ANLoginVC.xib ├── ANSnapVC.h ├── ANSnapVC.m ├── ANSnapVC.xib ├── ANSnapsVC.h ├── ANSnapsVC.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Snapchat │ ├── Data │ │ ├── Blobs │ │ │ ├── SCBlob.h │ │ │ ├── SCBlob.m │ │ │ ├── SCImageBlob.h │ │ │ ├── SCImageBlob.m │ │ │ ├── SCVideoBlob.h │ │ │ └── SCVideoBlob.m │ │ ├── SCSnap.h │ │ └── SCSnap.m │ ├── Internal │ │ ├── SCAPIConfiguration.h │ │ ├── SCAPIConfiguration.m │ │ ├── SCAPIRequest.h │ │ └── SCAPIRequest.m │ ├── Managing │ │ ├── SCSnapManager.h │ │ └── SCSnapManager.m │ ├── SCAPILogin.h │ ├── SCAPILogin.m │ ├── SCAPISession.h │ ├── SCAPISession.m │ ├── SCFetcher.h │ └── SCFetcher.m ├── SnapchatHax-Info.plist ├── SnapchatHax-Prefix.pch ├── en.lproj │ └── InfoPlist.strings └── main.m └── SnapchatHaxTests ├── SnapchatHaxTests-Info.plist ├── SnapchatHaxTests.m └── en.lproj └── InfoPlist.strings /README.md: -------------------------------------------------------------------------------- 1 | # Note 2 | 3 | While I am no longer actively developing this project, others have stepped in to fix it along the way. I have heard that it works as of January 6, 2015. 4 | 5 | # Purpose 6 | 7 | Quick, we don't want them to know that we saved this pic, what shall we do? SnapchatHax to the rescue! The purpose of this project is twofold: 8 | 9 | * to provide an easy-to-use Objective-C platform to interface with the Snapchat backend. 10 | * to provide users with an easy-to-use iOS application for the purpose of bypassing Snapchat's inherent limitations. 11 | 12 | Currently, this bare-bones app does my minimal requirements. Now, however, I have my heart set on beating the guy who wrote [SnapHack](https://itunes.apple.com/us/app/snaphack-pro-for-snapchat/id716560946?mt=8&ign-mpt=uo%3D4). By the end of this, I should have a relatively full-featured Objective-C interface for Snapchat's backend! 13 | 14 | 15 | # To-do 16 | 17 | Here's a list of things I think are pretty much essential: 18 | 19 | * Make the backlog include old snaps that are no longer viewable 20 | * Option to clear feed 21 | * Change nickname and email address 22 | 23 | Here are some features which would ideally be added: 24 | 25 | * Send snaps to friends (pictures & video) 26 | * Add & remove friends 27 | * Block and unblock users 28 | * Do *something* with stories, although I don't even understand them. 29 | * Get "bests" of users 30 | 31 | # References 32 | 33 | See [tlack/snaphax](https://github.com/tlack/snaphax) for my main source of reverse-engineered information. 34 | 35 | Update: I have since started using [dstelljes/php-snapchat/](https://github.com/dstelljes/php-snapchat/) for reference. This is a more complete set of functions. 36 | -------------------------------------------------------------------------------- /SnapchatHax.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FAAF8C00186289680049B692 /* SCBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = FAAF8BFB186289680049B692 /* SCBlob.m */; }; 11 | FAAF8C01186289680049B692 /* SCImageBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = FAAF8BFD186289680049B692 /* SCImageBlob.m */; }; 12 | FAAF8C02186289680049B692 /* SCVideoBlob.m in Sources */ = {isa = PBXBuildFile; fileRef = FAAF8BFF186289680049B692 /* SCVideoBlob.m */; }; 13 | FAF75EAE1860F9CC006C7D11 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EAD1860F9CC006C7D11 /* Foundation.framework */; }; 14 | FAF75EB01860F9CC006C7D11 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EAF1860F9CC006C7D11 /* CoreGraphics.framework */; }; 15 | FAF75EB21860F9CC006C7D11 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EB11860F9CC006C7D11 /* UIKit.framework */; }; 16 | FAF75EB81860F9CC006C7D11 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FAF75EB61860F9CC006C7D11 /* InfoPlist.strings */; }; 17 | FAF75EBA1860F9CC006C7D11 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EB91860F9CC006C7D11 /* main.m */; }; 18 | FAF75EBE1860F9CC006C7D11 /* ANAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EBD1860F9CC006C7D11 /* ANAppDelegate.m */; }; 19 | FAF75EC01860F9CC006C7D11 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FAF75EBF1860F9CC006C7D11 /* Images.xcassets */; }; 20 | FAF75EC71860F9CC006C7D11 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EC61860F9CC006C7D11 /* XCTest.framework */; }; 21 | FAF75EC81860F9CC006C7D11 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EAD1860F9CC006C7D11 /* Foundation.framework */; }; 22 | FAF75EC91860F9CC006C7D11 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EB11860F9CC006C7D11 /* UIKit.framework */; }; 23 | FAF75ED11860F9CC006C7D11 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FAF75ECF1860F9CC006C7D11 /* InfoPlist.strings */; }; 24 | FAF75ED31860F9CC006C7D11 /* SnapchatHaxTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75ED21860F9CC006C7D11 /* SnapchatHaxTests.m */; }; 25 | FAF75EE118610DAE006C7D11 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAF75EE018610DAE006C7D11 /* Security.framework */; }; 26 | FAF75EF61861205F006C7D11 /* SCAPIConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EEF1861205F006C7D11 /* SCAPIConfiguration.m */; }; 27 | FAF75EF71861205F006C7D11 /* SCAPIRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EF11861205F006C7D11 /* SCAPIRequest.m */; }; 28 | FAF75EF81861205F006C7D11 /* SCAPILogin.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EF31861205F006C7D11 /* SCAPILogin.m */; }; 29 | FAF75EF91861205F006C7D11 /* SCAPISession.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EF51861205F006C7D11 /* SCAPISession.m */; }; 30 | FAF75EFC18612069006C7D11 /* SCSnap.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EFB18612069006C7D11 /* SCSnap.m */; }; 31 | FAF75EFF18612E91006C7D11 /* SCFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75EFE18612E91006C7D11 /* SCFetcher.m */; }; 32 | FAF75F0D186241DA006C7D11 /* ANLoginVC.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75F0C186241DA006C7D11 /* ANLoginVC.m */; }; 33 | FAF75F10186241F1006C7D11 /* ANSnapsVC.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75F0F186241F1006C7D11 /* ANSnapsVC.m */; }; 34 | FAF75F1218624836006C7D11 /* ANLoginVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = FAF75F1118624836006C7D11 /* ANLoginVC.xib */; }; 35 | FAF75F1818625274006C7D11 /* SCSnapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75F1718625274006C7D11 /* SCSnapManager.m */; }; 36 | FAF75F1C1862591E006C7D11 /* ANSnapVC.m in Sources */ = {isa = PBXBuildFile; fileRef = FAF75F1A1862591E006C7D11 /* ANSnapVC.m */; }; 37 | FAF75F1D1862591E006C7D11 /* ANSnapVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = FAF75F1B1862591E006C7D11 /* ANSnapVC.xib */; }; 38 | /* End PBXBuildFile section */ 39 | 40 | /* Begin PBXContainerItemProxy section */ 41 | FAF75ECA1860F9CC006C7D11 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = FAF75EA21860F9CC006C7D11 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = FAF75EA91860F9CC006C7D11; 46 | remoteInfo = SnapchatHax; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | FAAF8BFA186289680049B692 /* SCBlob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCBlob.h; sourceTree = ""; }; 52 | FAAF8BFB186289680049B692 /* SCBlob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCBlob.m; sourceTree = ""; }; 53 | FAAF8BFC186289680049B692 /* SCImageBlob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCImageBlob.h; sourceTree = ""; }; 54 | FAAF8BFD186289680049B692 /* SCImageBlob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCImageBlob.m; sourceTree = ""; }; 55 | FAAF8BFE186289680049B692 /* SCVideoBlob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCVideoBlob.h; sourceTree = ""; }; 56 | FAAF8BFF186289680049B692 /* SCVideoBlob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCVideoBlob.m; sourceTree = ""; }; 57 | FAF75EAA1860F9CC006C7D11 /* SnapchatHax.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SnapchatHax.app; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | FAF75EAD1860F9CC006C7D11 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 59 | FAF75EAF1860F9CC006C7D11 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 60 | FAF75EB11860F9CC006C7D11 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 61 | FAF75EB51860F9CC006C7D11 /* SnapchatHax-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SnapchatHax-Info.plist"; sourceTree = ""; }; 62 | FAF75EB71860F9CC006C7D11 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 63 | FAF75EB91860F9CC006C7D11 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 64 | FAF75EBB1860F9CC006C7D11 /* SnapchatHax-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SnapchatHax-Prefix.pch"; sourceTree = ""; }; 65 | FAF75EBC1860F9CC006C7D11 /* ANAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ANAppDelegate.h; sourceTree = ""; }; 66 | FAF75EBD1860F9CC006C7D11 /* ANAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ANAppDelegate.m; sourceTree = ""; }; 67 | FAF75EBF1860F9CC006C7D11 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 68 | FAF75EC51860F9CC006C7D11 /* SnapchatHaxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SnapchatHaxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | FAF75EC61860F9CC006C7D11 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 70 | FAF75ECE1860F9CC006C7D11 /* SnapchatHaxTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SnapchatHaxTests-Info.plist"; sourceTree = ""; }; 71 | FAF75ED01860F9CC006C7D11 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 72 | FAF75ED21860F9CC006C7D11 /* SnapchatHaxTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SnapchatHaxTests.m; sourceTree = ""; }; 73 | FAF75EE018610DAE006C7D11 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 74 | FAF75EEE1861205F006C7D11 /* SCAPIConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAPIConfiguration.h; sourceTree = ""; }; 75 | FAF75EEF1861205F006C7D11 /* SCAPIConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAPIConfiguration.m; sourceTree = ""; }; 76 | FAF75EF01861205F006C7D11 /* SCAPIRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAPIRequest.h; sourceTree = ""; }; 77 | FAF75EF11861205F006C7D11 /* SCAPIRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAPIRequest.m; sourceTree = ""; }; 78 | FAF75EF21861205F006C7D11 /* SCAPILogin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAPILogin.h; sourceTree = ""; }; 79 | FAF75EF31861205F006C7D11 /* SCAPILogin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAPILogin.m; sourceTree = ""; }; 80 | FAF75EF41861205F006C7D11 /* SCAPISession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAPISession.h; sourceTree = ""; }; 81 | FAF75EF51861205F006C7D11 /* SCAPISession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAPISession.m; sourceTree = ""; }; 82 | FAF75EFA18612069006C7D11 /* SCSnap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCSnap.h; sourceTree = ""; }; 83 | FAF75EFB18612069006C7D11 /* SCSnap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCSnap.m; sourceTree = ""; }; 84 | FAF75EFD18612E91006C7D11 /* SCFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCFetcher.h; sourceTree = ""; }; 85 | FAF75EFE18612E91006C7D11 /* SCFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCFetcher.m; sourceTree = ""; }; 86 | FAF75F0B186241DA006C7D11 /* ANLoginVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ANLoginVC.h; sourceTree = ""; }; 87 | FAF75F0C186241DA006C7D11 /* ANLoginVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ANLoginVC.m; sourceTree = ""; }; 88 | FAF75F0E186241F1006C7D11 /* ANSnapsVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ANSnapsVC.h; sourceTree = ""; }; 89 | FAF75F0F186241F1006C7D11 /* ANSnapsVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ANSnapsVC.m; sourceTree = ""; }; 90 | FAF75F1118624836006C7D11 /* ANLoginVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ANLoginVC.xib; sourceTree = ""; }; 91 | FAF75F1618625274006C7D11 /* SCSnapManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCSnapManager.h; sourceTree = ""; }; 92 | FAF75F1718625274006C7D11 /* SCSnapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCSnapManager.m; sourceTree = ""; }; 93 | FAF75F191862591E006C7D11 /* ANSnapVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ANSnapVC.h; sourceTree = ""; }; 94 | FAF75F1A1862591E006C7D11 /* ANSnapVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ANSnapVC.m; sourceTree = ""; }; 95 | FAF75F1B1862591E006C7D11 /* ANSnapVC.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ANSnapVC.xib; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | FAF75EA71860F9CC006C7D11 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | FAF75EE118610DAE006C7D11 /* Security.framework in Frameworks */, 104 | FAF75EB01860F9CC006C7D11 /* CoreGraphics.framework in Frameworks */, 105 | FAF75EB21860F9CC006C7D11 /* UIKit.framework in Frameworks */, 106 | FAF75EAE1860F9CC006C7D11 /* Foundation.framework in Frameworks */, 107 | ); 108 | runOnlyForDeploymentPostprocessing = 0; 109 | }; 110 | FAF75EC21860F9CC006C7D11 /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | FAF75EC71860F9CC006C7D11 /* XCTest.framework in Frameworks */, 115 | FAF75EC91860F9CC006C7D11 /* UIKit.framework in Frameworks */, 116 | FAF75EC81860F9CC006C7D11 /* Foundation.framework in Frameworks */, 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXFrameworksBuildPhase section */ 121 | 122 | /* Begin PBXGroup section */ 123 | FAAF8BF9186289680049B692 /* Blobs */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | FAAF8BFA186289680049B692 /* SCBlob.h */, 127 | FAAF8BFB186289680049B692 /* SCBlob.m */, 128 | FAAF8BFC186289680049B692 /* SCImageBlob.h */, 129 | FAAF8BFD186289680049B692 /* SCImageBlob.m */, 130 | FAAF8BFE186289680049B692 /* SCVideoBlob.h */, 131 | FAAF8BFF186289680049B692 /* SCVideoBlob.m */, 132 | ); 133 | path = Blobs; 134 | sourceTree = ""; 135 | }; 136 | FAF75EA11860F9CC006C7D11 = { 137 | isa = PBXGroup; 138 | children = ( 139 | FAF75EB31860F9CC006C7D11 /* SnapchatHax */, 140 | FAF75ECC1860F9CC006C7D11 /* SnapchatHaxTests */, 141 | FAF75EAC1860F9CC006C7D11 /* Frameworks */, 142 | FAF75EAB1860F9CC006C7D11 /* Products */, 143 | ); 144 | sourceTree = ""; 145 | }; 146 | FAF75EAB1860F9CC006C7D11 /* Products */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | FAF75EAA1860F9CC006C7D11 /* SnapchatHax.app */, 150 | FAF75EC51860F9CC006C7D11 /* SnapchatHaxTests.xctest */, 151 | ); 152 | name = Products; 153 | sourceTree = ""; 154 | }; 155 | FAF75EAC1860F9CC006C7D11 /* Frameworks */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | FAF75EE018610DAE006C7D11 /* Security.framework */, 159 | FAF75EAD1860F9CC006C7D11 /* Foundation.framework */, 160 | FAF75EAF1860F9CC006C7D11 /* CoreGraphics.framework */, 161 | FAF75EB11860F9CC006C7D11 /* UIKit.framework */, 162 | FAF75EC61860F9CC006C7D11 /* XCTest.framework */, 163 | ); 164 | name = Frameworks; 165 | sourceTree = ""; 166 | }; 167 | FAF75EB31860F9CC006C7D11 /* SnapchatHax */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | FAF75EEB1861205F006C7D11 /* Snapchat */, 171 | FAF75EBC1860F9CC006C7D11 /* ANAppDelegate.h */, 172 | FAF75EBD1860F9CC006C7D11 /* ANAppDelegate.m */, 173 | FAF75F0B186241DA006C7D11 /* ANLoginVC.h */, 174 | FAF75F0C186241DA006C7D11 /* ANLoginVC.m */, 175 | FAF75F1118624836006C7D11 /* ANLoginVC.xib */, 176 | FAF75EBF1860F9CC006C7D11 /* Images.xcassets */, 177 | FAF75EB41860F9CC006C7D11 /* Supporting Files */, 178 | FAF75F0E186241F1006C7D11 /* ANSnapsVC.h */, 179 | FAF75F0F186241F1006C7D11 /* ANSnapsVC.m */, 180 | FAF75F191862591E006C7D11 /* ANSnapVC.h */, 181 | FAF75F1A1862591E006C7D11 /* ANSnapVC.m */, 182 | FAF75F1B1862591E006C7D11 /* ANSnapVC.xib */, 183 | ); 184 | path = SnapchatHax; 185 | sourceTree = ""; 186 | }; 187 | FAF75EB41860F9CC006C7D11 /* Supporting Files */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | FAF75EB51860F9CC006C7D11 /* SnapchatHax-Info.plist */, 191 | FAF75EB61860F9CC006C7D11 /* InfoPlist.strings */, 192 | FAF75EB91860F9CC006C7D11 /* main.m */, 193 | FAF75EBB1860F9CC006C7D11 /* SnapchatHax-Prefix.pch */, 194 | ); 195 | name = "Supporting Files"; 196 | sourceTree = ""; 197 | }; 198 | FAF75ECC1860F9CC006C7D11 /* SnapchatHaxTests */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | FAF75ED21860F9CC006C7D11 /* SnapchatHaxTests.m */, 202 | FAF75ECD1860F9CC006C7D11 /* Supporting Files */, 203 | ); 204 | path = SnapchatHaxTests; 205 | sourceTree = ""; 206 | }; 207 | FAF75ECD1860F9CC006C7D11 /* Supporting Files */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | FAF75ECE1860F9CC006C7D11 /* SnapchatHaxTests-Info.plist */, 211 | FAF75ECF1860F9CC006C7D11 /* InfoPlist.strings */, 212 | ); 213 | name = "Supporting Files"; 214 | sourceTree = ""; 215 | }; 216 | FAF75EEB1861205F006C7D11 /* Snapchat */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | FAF75F151862526C006C7D11 /* Managing */, 220 | FAF75EEC1861205F006C7D11 /* Data */, 221 | FAF75EED1861205F006C7D11 /* Internal */, 222 | FAF75EF21861205F006C7D11 /* SCAPILogin.h */, 223 | FAF75EF31861205F006C7D11 /* SCAPILogin.m */, 224 | FAF75EF41861205F006C7D11 /* SCAPISession.h */, 225 | FAF75EF51861205F006C7D11 /* SCAPISession.m */, 226 | FAF75EFD18612E91006C7D11 /* SCFetcher.h */, 227 | FAF75EFE18612E91006C7D11 /* SCFetcher.m */, 228 | ); 229 | path = Snapchat; 230 | sourceTree = ""; 231 | }; 232 | FAF75EEC1861205F006C7D11 /* Data */ = { 233 | isa = PBXGroup; 234 | children = ( 235 | FAAF8BF9186289680049B692 /* Blobs */, 236 | FAF75EFA18612069006C7D11 /* SCSnap.h */, 237 | FAF75EFB18612069006C7D11 /* SCSnap.m */, 238 | ); 239 | path = Data; 240 | sourceTree = ""; 241 | }; 242 | FAF75EED1861205F006C7D11 /* Internal */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | FAF75EEE1861205F006C7D11 /* SCAPIConfiguration.h */, 246 | FAF75EEF1861205F006C7D11 /* SCAPIConfiguration.m */, 247 | FAF75EF01861205F006C7D11 /* SCAPIRequest.h */, 248 | FAF75EF11861205F006C7D11 /* SCAPIRequest.m */, 249 | ); 250 | path = Internal; 251 | sourceTree = ""; 252 | }; 253 | FAF75F151862526C006C7D11 /* Managing */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | FAF75F1618625274006C7D11 /* SCSnapManager.h */, 257 | FAF75F1718625274006C7D11 /* SCSnapManager.m */, 258 | ); 259 | path = Managing; 260 | sourceTree = ""; 261 | }; 262 | /* End PBXGroup section */ 263 | 264 | /* Begin PBXNativeTarget section */ 265 | FAF75EA91860F9CC006C7D11 /* SnapchatHax */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = FAF75ED61860F9CC006C7D11 /* Build configuration list for PBXNativeTarget "SnapchatHax" */; 268 | buildPhases = ( 269 | FAF75EA61860F9CC006C7D11 /* Sources */, 270 | FAF75EA71860F9CC006C7D11 /* Frameworks */, 271 | FAF75EA81860F9CC006C7D11 /* Resources */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | ); 277 | name = SnapchatHax; 278 | productName = SnapchatHax; 279 | productReference = FAF75EAA1860F9CC006C7D11 /* SnapchatHax.app */; 280 | productType = "com.apple.product-type.application"; 281 | }; 282 | FAF75EC41860F9CC006C7D11 /* SnapchatHaxTests */ = { 283 | isa = PBXNativeTarget; 284 | buildConfigurationList = FAF75ED91860F9CC006C7D11 /* Build configuration list for PBXNativeTarget "SnapchatHaxTests" */; 285 | buildPhases = ( 286 | FAF75EC11860F9CC006C7D11 /* Sources */, 287 | FAF75EC21860F9CC006C7D11 /* Frameworks */, 288 | FAF75EC31860F9CC006C7D11 /* Resources */, 289 | ); 290 | buildRules = ( 291 | ); 292 | dependencies = ( 293 | FAF75ECB1860F9CC006C7D11 /* PBXTargetDependency */, 294 | ); 295 | name = SnapchatHaxTests; 296 | productName = SnapchatHaxTests; 297 | productReference = FAF75EC51860F9CC006C7D11 /* SnapchatHaxTests.xctest */; 298 | productType = "com.apple.product-type.bundle.unit-test"; 299 | }; 300 | /* End PBXNativeTarget section */ 301 | 302 | /* Begin PBXProject section */ 303 | FAF75EA21860F9CC006C7D11 /* Project object */ = { 304 | isa = PBXProject; 305 | attributes = { 306 | CLASSPREFIX = AN; 307 | LastUpgradeCheck = 0500; 308 | ORGANIZATIONNAME = "Alex Nichol"; 309 | TargetAttributes = { 310 | FAF75EC41860F9CC006C7D11 = { 311 | TestTargetID = FAF75EA91860F9CC006C7D11; 312 | }; 313 | }; 314 | }; 315 | buildConfigurationList = FAF75EA51860F9CC006C7D11 /* Build configuration list for PBXProject "SnapchatHax" */; 316 | compatibilityVersion = "Xcode 3.2"; 317 | developmentRegion = English; 318 | hasScannedForEncodings = 0; 319 | knownRegions = ( 320 | en, 321 | ); 322 | mainGroup = FAF75EA11860F9CC006C7D11; 323 | productRefGroup = FAF75EAB1860F9CC006C7D11 /* Products */; 324 | projectDirPath = ""; 325 | projectRoot = ""; 326 | targets = ( 327 | FAF75EA91860F9CC006C7D11 /* SnapchatHax */, 328 | FAF75EC41860F9CC006C7D11 /* SnapchatHaxTests */, 329 | ); 330 | }; 331 | /* End PBXProject section */ 332 | 333 | /* Begin PBXResourcesBuildPhase section */ 334 | FAF75EA81860F9CC006C7D11 /* Resources */ = { 335 | isa = PBXResourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | FAF75EB81860F9CC006C7D11 /* InfoPlist.strings in Resources */, 339 | FAF75EC01860F9CC006C7D11 /* Images.xcassets in Resources */, 340 | FAF75F1218624836006C7D11 /* ANLoginVC.xib in Resources */, 341 | FAF75F1D1862591E006C7D11 /* ANSnapVC.xib in Resources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | FAF75EC31860F9CC006C7D11 /* Resources */ = { 346 | isa = PBXResourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | FAF75ED11860F9CC006C7D11 /* InfoPlist.strings in Resources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | /* End PBXResourcesBuildPhase section */ 354 | 355 | /* Begin PBXSourcesBuildPhase section */ 356 | FAF75EA61860F9CC006C7D11 /* Sources */ = { 357 | isa = PBXSourcesBuildPhase; 358 | buildActionMask = 2147483647; 359 | files = ( 360 | FAAF8C02186289680049B692 /* SCVideoBlob.m in Sources */, 361 | FAF75EBA1860F9CC006C7D11 /* main.m in Sources */, 362 | FAF75EFC18612069006C7D11 /* SCSnap.m in Sources */, 363 | FAF75EBE1860F9CC006C7D11 /* ANAppDelegate.m in Sources */, 364 | FAF75F10186241F1006C7D11 /* ANSnapsVC.m in Sources */, 365 | FAF75EF81861205F006C7D11 /* SCAPILogin.m in Sources */, 366 | FAF75EF71861205F006C7D11 /* SCAPIRequest.m in Sources */, 367 | FAAF8C00186289680049B692 /* SCBlob.m in Sources */, 368 | FAF75EFF18612E91006C7D11 /* SCFetcher.m in Sources */, 369 | FAAF8C01186289680049B692 /* SCImageBlob.m in Sources */, 370 | FAF75F0D186241DA006C7D11 /* ANLoginVC.m in Sources */, 371 | FAF75EF61861205F006C7D11 /* SCAPIConfiguration.m in Sources */, 372 | FAF75F1C1862591E006C7D11 /* ANSnapVC.m in Sources */, 373 | FAF75F1818625274006C7D11 /* SCSnapManager.m in Sources */, 374 | FAF75EF91861205F006C7D11 /* SCAPISession.m in Sources */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | FAF75EC11860F9CC006C7D11 /* Sources */ = { 379 | isa = PBXSourcesBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | FAF75ED31860F9CC006C7D11 /* SnapchatHaxTests.m in Sources */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | /* End PBXSourcesBuildPhase section */ 387 | 388 | /* Begin PBXTargetDependency section */ 389 | FAF75ECB1860F9CC006C7D11 /* PBXTargetDependency */ = { 390 | isa = PBXTargetDependency; 391 | target = FAF75EA91860F9CC006C7D11 /* SnapchatHax */; 392 | targetProxy = FAF75ECA1860F9CC006C7D11 /* PBXContainerItemProxy */; 393 | }; 394 | /* End PBXTargetDependency section */ 395 | 396 | /* Begin PBXVariantGroup section */ 397 | FAF75EB61860F9CC006C7D11 /* InfoPlist.strings */ = { 398 | isa = PBXVariantGroup; 399 | children = ( 400 | FAF75EB71860F9CC006C7D11 /* en */, 401 | ); 402 | name = InfoPlist.strings; 403 | sourceTree = ""; 404 | }; 405 | FAF75ECF1860F9CC006C7D11 /* InfoPlist.strings */ = { 406 | isa = PBXVariantGroup; 407 | children = ( 408 | FAF75ED01860F9CC006C7D11 /* en */, 409 | ); 410 | name = InfoPlist.strings; 411 | sourceTree = ""; 412 | }; 413 | /* End PBXVariantGroup section */ 414 | 415 | /* Begin XCBuildConfiguration section */ 416 | FAF75ED41860F9CC006C7D11 /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | ALWAYS_SEARCH_USER_PATHS = NO; 420 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 428 | CLANG_WARN_EMPTY_BODY = YES; 429 | CLANG_WARN_ENUM_CONVERSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 432 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 433 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 434 | COPY_PHASE_STRIP = NO; 435 | GCC_C_LANGUAGE_STANDARD = gnu99; 436 | GCC_DYNAMIC_NO_PIC = NO; 437 | GCC_OPTIMIZATION_LEVEL = 0; 438 | GCC_PREPROCESSOR_DEFINITIONS = ( 439 | "DEBUG=1", 440 | "$(inherited)", 441 | ); 442 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 443 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 444 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 445 | GCC_WARN_UNDECLARED_SELECTOR = YES; 446 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 447 | GCC_WARN_UNUSED_FUNCTION = YES; 448 | GCC_WARN_UNUSED_VARIABLE = YES; 449 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 450 | ONLY_ACTIVE_ARCH = YES; 451 | SDKROOT = iphoneos; 452 | }; 453 | name = Debug; 454 | }; 455 | FAF75ED51860F9CC006C7D11 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 467 | CLANG_WARN_EMPTY_BODY = YES; 468 | CLANG_WARN_ENUM_CONVERSION = YES; 469 | CLANG_WARN_INT_CONVERSION = YES; 470 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = YES; 474 | ENABLE_NS_ASSERTIONS = NO; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 483 | SDKROOT = iphoneos; 484 | VALIDATE_PRODUCT = YES; 485 | }; 486 | name = Release; 487 | }; 488 | FAF75ED71860F9CC006C7D11 /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 493 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 494 | GCC_PREFIX_HEADER = "SnapchatHax/SnapchatHax-Prefix.pch"; 495 | INFOPLIST_FILE = "SnapchatHax/SnapchatHax-Info.plist"; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | WRAPPER_EXTENSION = app; 498 | }; 499 | name = Debug; 500 | }; 501 | FAF75ED81860F9CC006C7D11 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 506 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 507 | GCC_PREFIX_HEADER = "SnapchatHax/SnapchatHax-Prefix.pch"; 508 | INFOPLIST_FILE = "SnapchatHax/SnapchatHax-Info.plist"; 509 | PRODUCT_NAME = "$(TARGET_NAME)"; 510 | WRAPPER_EXTENSION = app; 511 | }; 512 | name = Release; 513 | }; 514 | FAF75EDA1860F9CC006C7D11 /* Debug */ = { 515 | isa = XCBuildConfiguration; 516 | buildSettings = { 517 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 518 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SnapchatHax.app/SnapchatHax"; 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(SDKROOT)/Developer/Library/Frameworks", 521 | "$(inherited)", 522 | "$(DEVELOPER_FRAMEWORKS_DIR)", 523 | ); 524 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 525 | GCC_PREFIX_HEADER = "SnapchatHax/SnapchatHax-Prefix.pch"; 526 | GCC_PREPROCESSOR_DEFINITIONS = ( 527 | "DEBUG=1", 528 | "$(inherited)", 529 | ); 530 | INFOPLIST_FILE = "SnapchatHaxTests/SnapchatHaxTests-Info.plist"; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | TEST_HOST = "$(BUNDLE_LOADER)"; 533 | WRAPPER_EXTENSION = xctest; 534 | }; 535 | name = Debug; 536 | }; 537 | FAF75EDB1860F9CC006C7D11 /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 541 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SnapchatHax.app/SnapchatHax"; 542 | FRAMEWORK_SEARCH_PATHS = ( 543 | "$(SDKROOT)/Developer/Library/Frameworks", 544 | "$(inherited)", 545 | "$(DEVELOPER_FRAMEWORKS_DIR)", 546 | ); 547 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 548 | GCC_PREFIX_HEADER = "SnapchatHax/SnapchatHax-Prefix.pch"; 549 | INFOPLIST_FILE = "SnapchatHaxTests/SnapchatHaxTests-Info.plist"; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | TEST_HOST = "$(BUNDLE_LOADER)"; 552 | WRAPPER_EXTENSION = xctest; 553 | }; 554 | name = Release; 555 | }; 556 | /* End XCBuildConfiguration section */ 557 | 558 | /* Begin XCConfigurationList section */ 559 | FAF75EA51860F9CC006C7D11 /* Build configuration list for PBXProject "SnapchatHax" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | FAF75ED41860F9CC006C7D11 /* Debug */, 563 | FAF75ED51860F9CC006C7D11 /* Release */, 564 | ); 565 | defaultConfigurationIsVisible = 0; 566 | defaultConfigurationName = Release; 567 | }; 568 | FAF75ED61860F9CC006C7D11 /* Build configuration list for PBXNativeTarget "SnapchatHax" */ = { 569 | isa = XCConfigurationList; 570 | buildConfigurations = ( 571 | FAF75ED71860F9CC006C7D11 /* Debug */, 572 | FAF75ED81860F9CC006C7D11 /* Release */, 573 | ); 574 | defaultConfigurationIsVisible = 0; 575 | defaultConfigurationName = Release; 576 | }; 577 | FAF75ED91860F9CC006C7D11 /* Build configuration list for PBXNativeTarget "SnapchatHaxTests" */ = { 578 | isa = XCConfigurationList; 579 | buildConfigurations = ( 580 | FAF75EDA1860F9CC006C7D11 /* Debug */, 581 | FAF75EDB1860F9CC006C7D11 /* Release */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | /* End XCConfigurationList section */ 587 | }; 588 | rootObject = FAF75EA21860F9CC006C7D11 /* Project object */; 589 | } 590 | -------------------------------------------------------------------------------- /SnapchatHax.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SnapchatHax.xcodeproj/xcuserdata/alex.xcuserdatad/xcschemes/SnapchatHax.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /SnapchatHax.xcodeproj/xcuserdata/alex.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SnapchatHax.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | FAF75EA91860F9CC006C7D11 16 | 17 | primary 18 | 19 | 20 | FAF75EC41860F9CC006C7D11 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SnapchatHax/ANAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANAppDelegate.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ANLoginVC.h" 11 | 12 | @interface ANAppDelegate : UIResponder { 13 | UINavigationController * navCont; 14 | } 15 | 16 | @property (strong, nonatomic) UIWindow * window; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /SnapchatHax/ANAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANAppDelegate.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "ANAppDelegate.h" 10 | #import "SCAPIConfiguration.h" 11 | 12 | @implementation ANAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | 18 | ANLoginVC * login = [[ANLoginVC alloc] init]; 19 | navCont = [[UINavigationController alloc] init]; 20 | [navCont pushViewController:login animated:NO]; 21 | self.window.rootViewController = navCont; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | return YES; 26 | } 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application 29 | { 30 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 31 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 32 | } 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application 35 | { 36 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application 41 | { 42 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 43 | } 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application 46 | { 47 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 48 | } 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application 51 | { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /SnapchatHax/ANLoginVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANLoginVC.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ANSnapsVC.h" 11 | #import "SCAPILogin.h" 12 | 13 | @interface ANLoginVC : UIViewController { 14 | IBOutlet UITextField * username, * password; 15 | IBOutlet UIButton * button; 16 | } 17 | 18 | - (IBAction)loginPressed:(id)sender; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SnapchatHax/ANLoginVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANLoginVC.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "ANLoginVC.h" 10 | 11 | @interface ANLoginVC () 12 | 13 | @end 14 | 15 | @implementation ANLoginVC 16 | 17 | - (id)init { 18 | if ((self = [super initWithNibName:NSStringFromClass(self.class) 19 | bundle:[NSBundle mainBundle]])) { 20 | self.title = @"Login"; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; 28 | username.text = [defaults objectForKey:@"username"] ?: @""; 29 | password.text = [defaults objectForKey:@"password"] ?: @""; 30 | } 31 | 32 | - (IBAction)loginPressed:(id)sender { 33 | [button setEnabled:NO]; 34 | SCAPILogin * login = [[SCAPILogin alloc] init]; 35 | login.username = username.text; 36 | login.password = password.text; 37 | login.delegate = self; 38 | [login startLogin]; 39 | } 40 | 41 | - (void)scAPILogin:(id)sender failedWithError:(NSError *)error { 42 | [button setEnabled:YES]; 43 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 44 | message:[error localizedDescription] 45 | delegate:nil 46 | cancelButtonTitle:nil 47 | otherButtonTitles:@"OK", nil]; 48 | [av show]; 49 | } 50 | 51 | - (void)scAPILogin:(id)sender succeededWithSession:(SCAPISession *)session { 52 | [button setEnabled:YES]; 53 | ANSnapsVC * snaps = [[ANSnapsVC alloc] initWithSession:session]; 54 | [self.navigationController pushViewController:snaps animated:YES]; 55 | 56 | NSUserDefaults * defs = [NSUserDefaults standardUserDefaults]; 57 | [defs setObject:username.text forKey:@"username"]; 58 | [defs setObject:password.text forKey:@"password"]; 59 | [defs synchronize]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /SnapchatHax/ANLoginVC.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 43 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /SnapchatHax/ANSnapVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANSnapVC.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCAPISession.h" 11 | #import "SCImageBlob.h" 12 | #import "SCVideoBlob.h" 13 | 14 | @interface ANSnapVC : UIViewController { 15 | IBOutlet UILabel * typeLabel; 16 | IBOutlet UILabel * dateLabel; 17 | IBOutlet UILabel * sizeLabel; 18 | IBOutlet UILabel * senderLabel; 19 | NSString * tempFile; 20 | 21 | IBOutlet UIActivityIndicatorView * loader; 22 | IBOutlet UIView * buttonsView; 23 | SCFetcher * fetcher; 24 | 25 | UIDocumentInteractionController * currentDocument; 26 | } 27 | 28 | @property (nonatomic, retain) SCSnap * snap; 29 | @property (nonatomic, retain) SCBlob * blob; 30 | @property (nonatomic, retain) SCAPISession * session; 31 | 32 | - (IBAction)viewPressed:(id)sender; 33 | - (IBAction)markScreenshotPressed:(id)sender; 34 | - (IBAction)markAsReadPressed:(id)sender; 35 | 36 | - (void)setLoading:(BOOL)flag; 37 | 38 | - (id)initWithSnap:(SCSnap *)snap; 39 | - (id)initWithSnap:(SCSnap *)snap session:(SCAPISession *)session; 40 | - (id)initWithSnap:(SCSnap *)snap blob:(SCBlob *)blob session:(SCAPISession *)session; 41 | - (void)handleBlobLoaded; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /SnapchatHax/ANSnapVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANSnapVC.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "ANSnapVC.h" 10 | 11 | @interface ANSnapVC () 12 | 13 | @end 14 | 15 | @implementation ANSnapVC 16 | 17 | - (id)initWithSnap:(SCSnap *)snap { 18 | if ((self = [super initWithNibName:NSStringFromClass(self.class) 19 | bundle:[NSBundle mainBundle]])) { 20 | self.snap = snap; 21 | self.title = @"Snap"; 22 | } 23 | return self; 24 | } 25 | 26 | - (id)initWithSnap:(SCSnap *)snap session:(SCAPISession *)session { 27 | if ((self = [self initWithSnap:snap])) { 28 | // load the data 29 | self.session = session; 30 | fetcher = [session fetchBlob:self.snap.identifier callback:^(NSError * error, SCBlob * blob) { 31 | if (!blob) { 32 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 33 | message:[error localizedDescription] 34 | delegate:nil 35 | cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 36 | [av show]; 37 | } else { 38 | fetcher = nil; 39 | self.blob = blob; 40 | [self handleBlobLoaded]; 41 | } 42 | }]; 43 | } 44 | return self; 45 | } 46 | 47 | - (id)initWithSnap:(SCSnap *)snap blob:(SCBlob *)blob session:(SCAPISession *)session { 48 | if ((self = [self initWithSnap:snap])) { 49 | self.blob = blob; 50 | self.session = session; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)setLoading:(BOOL)flag { 56 | if (flag) { 57 | [loader startAnimating]; 58 | [buttonsView setHidden:YES]; 59 | } else { 60 | [loader stopAnimating]; 61 | [buttonsView setHidden:NO]; 62 | } 63 | } 64 | 65 | #pragma mark - Events - 66 | 67 | - (IBAction)viewPressed:(id)sender { 68 | NSURL * theURL = [NSURL fileURLWithPath:tempFile]; 69 | currentDocument = [UIDocumentInteractionController interactionControllerWithURL:theURL]; 70 | currentDocument.delegate = self; 71 | 72 | [currentDocument presentOptionsMenuFromRect:CGRectZero 73 | inView:self.view 74 | animated:YES]; 75 | } 76 | 77 | - (IBAction)markScreenshotPressed:(id)sender { 78 | [self setLoading:YES]; 79 | fetcher = [self.session markSnapScreenshot:self.snap.identifier time:1 callback:^(NSError * error) { 80 | if (error) { 81 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 82 | message:[error localizedDescription] 83 | delegate:nil 84 | cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 85 | [av show]; 86 | } 87 | [self setLoading:NO]; 88 | }]; 89 | } 90 | 91 | - (IBAction)markAsReadPressed:(id)sender { 92 | [self setLoading:YES]; 93 | fetcher = [self.session markSnapViewed:self.snap.identifier time:1 callback:^(NSError * error) { 94 | if (error) { 95 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 96 | message:[error localizedDescription] 97 | delegate:nil 98 | cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; 99 | [av show]; 100 | } 101 | [self setLoading:NO]; 102 | }]; 103 | } 104 | 105 | #pragma mark - Loading - 106 | 107 | - (void)viewDidLoad { 108 | [super viewDidLoad]; 109 | 110 | if (self.blob) [self handleBlobLoaded]; 111 | dateLabel.text = [[NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)self.snap.timestamp / 1000.0] description]; 112 | senderLabel.text = self.snap.user; 113 | } 114 | 115 | - (void)handleBlobLoaded { 116 | if (!buttonsView || !typeLabel || !sizeLabel) return; 117 | 118 | NSString * fileName = [NSString stringWithFormat:@"%@.%@", self.snap.identifier, [self.blob blobFileExtension]]; 119 | tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName]; 120 | [self.blob.blobData writeToFile:tempFile atomically:YES]; 121 | 122 | [self setLoading:NO]; 123 | typeLabel.text = [self.blob isKindOfClass:[SCImageBlob class]] ? @"Image" : @"Video"; 124 | sizeLabel.text = [NSString stringWithFormat:@"%d KB", (int)self.blob.blobData.length >> 10]; 125 | } 126 | 127 | - (void)viewDidDisappear:(BOOL)animated { 128 | if (!self.navigationController) { 129 | [fetcher cancel]; 130 | if (tempFile) [[NSFileManager defaultManager] removeItemAtPath:tempFile error:nil]; 131 | } 132 | } 133 | 134 | #pragma mark - Document Interaction - 135 | 136 | - (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller { 137 | return self.navigationController; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /SnapchatHax/ANSnapVC.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 35 | 41 | 47 | 53 | 59 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /SnapchatHax/ANSnapsVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANSnapsVC.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCAPISession.h" 11 | #import "SCSnapManager.h" 12 | #import "ANSnapVC.h" 13 | 14 | @interface ANSnapsVC : UITableViewController { 15 | SCFetcher * reloadFetcher; 16 | SCSnapManager * snaps; 17 | 18 | UIActivityIndicatorView * loader; 19 | UIBarButtonItem * loadButton; 20 | } 21 | 22 | @property (nonatomic, retain) SCAPISession * session; 23 | 24 | - (id)initWithSession:(SCAPISession *)session; 25 | 26 | - (IBAction)reloadPressed:(id)sender; 27 | - (void)setReloading:(BOOL)reloading; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /SnapchatHax/ANSnapsVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANSnapsVC.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "ANSnapsVC.h" 10 | 11 | @interface ANSnapsVC () 12 | 13 | @end 14 | 15 | @implementation ANSnapsVC 16 | 17 | - (id)initWithSession:(SCAPISession *)session { 18 | if ((self = [super init])) { 19 | self.session = session; 20 | self.title = session.username; 21 | snaps = [[SCSnapManager alloc] init]; 22 | snaps.delegate = self; 23 | [snaps startFetchingBlobs:session]; 24 | 25 | loader = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 26 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:loader]; 27 | 28 | loadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 29 | target:self 30 | action:@selector(reloadPressed:)]; 31 | [self setReloading:YES]; 32 | } 33 | return self; 34 | } 35 | 36 | - (IBAction)reloadPressed:(id)sender { 37 | reloadFetcher = [self.session reloadAll:^(NSError * error) { 38 | reloadFetcher = nil; 39 | if (error) { 40 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 41 | message:[error localizedDescription] 42 | delegate:nil cancelButtonTitle:nil 43 | otherButtonTitles:@"OK", nil]; 44 | [av show]; 45 | [self setReloading:NO]; 46 | } else { 47 | [snaps startFetchingBlobs:self.session]; 48 | } 49 | }]; 50 | [self setReloading:YES]; 51 | } 52 | 53 | - (void)setReloading:(BOOL)reloading { 54 | if (reloading) { 55 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:loader]; 56 | [loader startAnimating]; 57 | } else { 58 | [loader stopAnimating]; 59 | self.navigationItem.rightBarButtonItem = loadButton; 60 | } 61 | } 62 | 63 | #pragma mark - View Methods - 64 | 65 | - (void)didReceiveMemoryWarning { 66 | [super didReceiveMemoryWarning]; 67 | [snaps.blobs removeAllObjects]; 68 | } 69 | 70 | - (void)viewDidDisappear:(BOOL)animated { 71 | if (!self.navigationController) { 72 | [snaps cancelAll]; 73 | [reloadFetcher cancel]; 74 | } 75 | } 76 | 77 | #pragma mark - Table View - 78 | 79 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 80 | return 1; 81 | } 82 | 83 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 84 | return snaps.snaps.count; 85 | } 86 | 87 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 88 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 89 | if (!cell) { 90 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 91 | reuseIdentifier:@"Cell"]; 92 | } 93 | 94 | SCSnap * snap = snaps.snaps[indexPath.row]; 95 | cell.textLabel.text = snap.user; 96 | 97 | NSDate * date = [NSDate dateWithTimeIntervalSince1970:(NSTimeInterval)snap.timestamp / 1000.0]; 98 | cell.detailTextLabel.text = [NSString stringWithFormat:@"At %@", [date description]]; 99 | 100 | return cell; 101 | } 102 | 103 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 104 | SCSnap * snap = snaps.snaps[indexPath.row]; 105 | SCBlob * blob = [snaps.blobs objectForKey:snap.identifier]; 106 | ANSnapVC * vc = nil; 107 | if (blob) vc = [[ANSnapVC alloc] initWithSnap:snap blob:blob session:self.session]; 108 | else vc = [[ANSnapVC alloc] initWithSnap:snap session:self.session]; 109 | [self.navigationController pushViewController:vc animated:YES]; 110 | } 111 | 112 | #pragma mark - SCSnapManager - 113 | 114 | - (void)scSnapManager:(id)sender insertedAtIndex:(NSInteger)index { 115 | [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:(index) inSection:0]] 116 | withRowAnimation:UITableViewRowAnimationAutomatic]; 117 | } 118 | 119 | - (void)scSnapManager:(id)sender deletedAtIndex:(NSInteger)index { 120 | [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:(index) inSection:0]] 121 | withRowAnimation:UITableViewRowAnimationAutomatic]; 122 | } 123 | 124 | - (void)scSnapManagerFinishedLoading:(id)sender { 125 | [self setReloading:NO]; 126 | } 127 | 128 | - (void)scSnapManager:(id)sender failedWithError:(NSError *)e { 129 | UIAlertView * av = [[UIAlertView alloc] initWithTitle:@"Error" 130 | message:[e localizedDescription] 131 | delegate:nil 132 | cancelButtonTitle:nil 133 | otherButtonTitles:@"OK", nil]; 134 | [av show]; 135 | [self setReloading:NO]; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /SnapchatHax/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SnapchatHax/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCBlob.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCBlob.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCBlob : NSObject 12 | 13 | @property (nonatomic, retain) NSData * blobData; 14 | 15 | + (SCBlob *)blobWithData:(NSData *)data; 16 | - (id)initWithData:(NSData *)data; 17 | 18 | - (NSString *)blobFileExtension; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCBlob.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCBlob.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCBlob.h" 10 | #import "SCImageBlob.h" 11 | #import "SCVideoBlob.h" 12 | 13 | @implementation SCBlob 14 | 15 | + (SCBlob *)blobWithData:(NSData *)data { 16 | NSArray * blobs = @[SCImageBlob.class, SCVideoBlob.class]; 17 | for (Class c in blobs) { 18 | id val = [[c alloc] initWithData:data]; 19 | if (val) return val; 20 | } 21 | return nil; 22 | } 23 | 24 | - (id)initWithData:(NSData *)data { 25 | return nil; 26 | } 27 | 28 | - (NSString *)blobFileExtension { 29 | return @"txt"; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCImageBlob.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCImageBlob.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCBlob.h" 10 | 11 | @interface SCImageBlob : SCBlob 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCImageBlob.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCImageBlob.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCImageBlob.h" 10 | 11 | @implementation SCImageBlob 12 | 13 | - (id)initWithData:(NSData *)data { 14 | if (data.length < 2) return nil; 15 | if (((const unsigned char *)data.bytes)[0] != 0xff || 16 | ((const unsigned char *)data.bytes)[1] != 0xd8) return nil; 17 | if ((self = [super init])) { 18 | self.blobData = data; 19 | } 20 | return self; 21 | } 22 | 23 | - (NSString *)blobFileExtension { 24 | return @"jpg"; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCVideoBlob.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCVideoBlob.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCBlob.h" 10 | 11 | @interface SCVideoBlob : SCBlob 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/Blobs/SCVideoBlob.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCVideoBlob.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCVideoBlob.h" 10 | 11 | @implementation SCVideoBlob 12 | 13 | - (id)initWithData:(NSData *)data { 14 | if (data.length < 2) return nil; 15 | if (((const char *)data.bytes)[0] != 0 || ((const char *)data.bytes)[1] != 0) return nil; 16 | if ((self = [super init])) { 17 | self.blobData = data; 18 | } 19 | return self; 20 | } 21 | 22 | - (NSString *)blobFileExtension { 23 | return @"mp4"; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/SCSnap.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCSnap.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | SCSnapTypeImage = 0, 13 | SCSnapTypeVideo = 1, 14 | SCSnapTypeSilentVideo = 2, 15 | SCSnapTypeFriendFlag = 4 16 | } SCSnapType; 17 | 18 | @interface SCSnap : NSObject 19 | 20 | @property (readwrite) unsigned long long status; 21 | @property (readwrite) unsigned long long timestamp; 22 | @property (readwrite) unsigned long long media; 23 | @property (readwrite) unsigned long long tField; 24 | @property (nonatomic) NSString * identifier; 25 | @property (nonatomic) NSString * user; 26 | 27 | - (id)initWithDictionary:(NSDictionary *)dictionary; 28 | - (BOOL)isImageOrVideo; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Data/SCSnap.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCSnap.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCSnap.h" 10 | 11 | @implementation SCSnap 12 | 13 | - (id)initWithDictionary:(NSDictionary *)dictionary { 14 | if ((self = [super init])) { 15 | self.user = dictionary[@"sn"]; 16 | self.timestamp = [dictionary[@"ts"] longLongValue]; 17 | self.status = [dictionary[@"st"] longLongValue]; 18 | self.identifier = dictionary[@"id"]; 19 | self.media = [dictionary[@"m"] longLongValue]; 20 | self.tField = [dictionary[@"t"] longLongValue]; 21 | } 22 | return self; 23 | } 24 | 25 | - (BOOL)isImageOrVideo { 26 | return (self.media < 7 && self.media != 3); 27 | } 28 | 29 | #pragma mark - Obj-C Overrides - 30 | 31 | - (NSString *)description { 32 | return [NSString stringWithFormat:@"<%@ user=%@, timestamp=%llu, status=%llu, identifier=%@, media=%llu, t=%llu>", 33 | NSStringFromClass(self.class), self.user, self.timestamp, 34 | self.status, self.identifier, self.media, self.tField]; 35 | } 36 | 37 | - (BOOL)isEqual:(id)object { 38 | if (![object isKindOfClass:[SCSnap class]]) return NO; 39 | return [[((SCSnap *)object) identifier] isEqualToString:self.identifier]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Internal/SCAPIConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPI.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SCAPIConfiguration : NSObject { 13 | NSDictionary * options; 14 | } 15 | 16 | @property (readonly) NSData * encryptionKey; 17 | @property (readonly) NSData * secret; 18 | @property (readonly) NSString * baseURL; 19 | @property (readonly) NSString * staticToken; 20 | @property (readonly) NSString * userAgent; 21 | 22 | + (NSDictionary *)defaultOptions; 23 | + (SCAPIConfiguration *)defaultAPIConfiguration; 24 | 25 | - (id)initWithOptions:(NSDictionary *)opts; 26 | 27 | - (BOOL)isValidBlobHeader:(NSData *)header; 28 | - (NSData *)decrypt:(NSData *)data; 29 | - (NSData *)encrypt:(NSData *)data; 30 | - (NSString *)dualHash:(NSData *)value1 andHash:(NSData *)value2; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Internal/SCAPIConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPI.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPIConfiguration.h" 10 | 11 | static NSString * hexHash(NSData * input); 12 | 13 | @implementation SCAPIConfiguration 14 | 15 | + (NSDictionary *)defaultOptions { 16 | /** 17 | * Graciously taken from https://github.com/tlack/snaphax/blob/master/snaphax.php 18 | */ 19 | return @{@"blob_enc_key": @"M02cnQ51Ji97vwT4", 20 | @"pattern": @"0001110111101110001111010101111011010001001110011000110001000110", 21 | @"secret": @"iEk21fuwZApXlz93750dmW22pw389dPwOk", 22 | @"static_token": @"m198sOkJEn37DjqZ32lpRu76xmw288xSQ9", 23 | @"url": @"https://feelinsonice-hrd.appspot.com", 24 | @"user_agent": @"Snapchat/8.1.1 (iPad; iPhone OS 6.0; en_US; gzip)"}; 25 | } 26 | 27 | + (SCAPIConfiguration *)defaultAPIConfiguration { 28 | static SCAPIConfiguration * api = nil; 29 | static dispatch_once_t onceToken; 30 | dispatch_once(&onceToken, ^{ 31 | api = [[SCAPIConfiguration alloc] initWithOptions:[self defaultOptions]]; 32 | }); 33 | return api; 34 | } 35 | 36 | - (id)initWithOptions:(NSDictionary *)opts { 37 | if ((self = [super init])) { 38 | options = opts; 39 | } 40 | return self; 41 | } 42 | 43 | - (BOOL)isValidBlobHeader:(NSData *)header { 44 | if (header.length < 2) return NO; 45 | unsigned const char * bytes = (unsigned const char *)header.bytes; 46 | return (bytes[0] == 0 && bytes[1] == 0) || 47 | (bytes[0] == 0xff && bytes[1] == 0xd8); 48 | } 49 | 50 | - (NSData *)decrypt:(NSData *)data { 51 | NSData * result = nil; 52 | 53 | // setup key 54 | unsigned char cKey[16]; 55 | bzero(cKey, sizeof(cKey)); 56 | [self.encryptionKey getBytes:cKey length:16]; 57 | 58 | // setup output buffer 59 | size_t bufferSize = [data length]; 60 | void * buffer = malloc(bufferSize); 61 | 62 | // do decrypt 63 | size_t decryptedSize = 0; 64 | CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, 65 | kCCAlgorithmAES128, 66 | kCCOptionECBMode, 67 | cKey, 68 | 16, 69 | NULL, 70 | [data bytes], 71 | [data length], 72 | buffer, 73 | bufferSize, 74 | &decryptedSize); 75 | 76 | if (cryptStatus == kCCSuccess) { 77 | result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize]; 78 | } else { 79 | free(buffer); 80 | NSLog(@"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus); 81 | } 82 | 83 | return result; 84 | } 85 | 86 | - (NSData *)encrypt:(NSData *)data { 87 | NSData * result = nil; 88 | 89 | // setup key 90 | unsigned char cKey[16]; 91 | bzero(cKey, sizeof(cKey)); 92 | [self.encryptionKey getBytes:cKey length:16]; 93 | 94 | // setup output buffer 95 | size_t bufferSize = [data length] + (data.length % 16); 96 | void * buffer = malloc(bufferSize); 97 | 98 | void * strBuffer = malloc(bufferSize); 99 | bzero(strBuffer, bufferSize); 100 | [data getBytes:strBuffer]; 101 | 102 | // do encrypt 103 | size_t encSize = 0; 104 | CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, 105 | kCCAlgorithmAES128, 106 | kCCOptionECBMode, 107 | cKey, 108 | 16, 109 | NULL, 110 | strBuffer, 111 | bufferSize, 112 | buffer, 113 | bufferSize, 114 | &encSize); 115 | free(strBuffer); 116 | if (cryptStatus == kCCSuccess) { 117 | result = [NSData dataWithBytesNoCopy:buffer length:encSize]; 118 | } else { 119 | free(buffer); 120 | NSLog(@"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus); 121 | } 122 | 123 | return result; 124 | } 125 | 126 | - (NSString *)dualHash:(NSData *)value1 andHash:(NSData *)value2 { 127 | NSMutableData * s1 = [self.secret mutableCopy]; 128 | [s1 appendData:value1]; 129 | NSMutableData * s2 = [value2 mutableCopy]; 130 | [s2 appendData:self.secret]; 131 | NSString * s3 = hexHash(s1); 132 | NSString * s4 = hexHash(s2); 133 | 134 | NSString * pattern = options[@"pattern"]; 135 | NSMutableString * result = [NSMutableString string]; 136 | for (int i = 0; i < pattern.length; i++) { 137 | if ([pattern characterAtIndex:i] == '0') { 138 | [result appendFormat:@"%C", [s3 characterAtIndex:i]]; 139 | } else { 140 | [result appendFormat:@"%C", [s4 characterAtIndex:i]]; 141 | } 142 | } 143 | return result; 144 | } 145 | 146 | #pragma mark - Properties - 147 | 148 | - (NSData *)encryptionKey { 149 | return [options[@"blob_enc_key"] dataUsingEncoding:NSASCIIStringEncoding]; 150 | } 151 | 152 | - (NSData *)secret { 153 | return [options[@"secret"] dataUsingEncoding:NSASCIIStringEncoding]; 154 | } 155 | 156 | - (NSString *)baseURL { 157 | return options[@"url"]; 158 | } 159 | 160 | - (NSString *)staticToken { 161 | return options[@"static_token"]; 162 | } 163 | 164 | - (NSString *)userAgent { 165 | return options[@"user_agent"]; 166 | } 167 | 168 | @end 169 | 170 | static NSString * hexHash(NSData * input) { 171 | unsigned char output[CC_SHA256_DIGEST_LENGTH]; 172 | CC_SHA256(input.bytes, (unsigned int)input.length, output); 173 | NSMutableString * str = [NSMutableString string]; 174 | for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { 175 | [str appendFormat:@"%02x", output[i]]; 176 | } 177 | return str; 178 | } 179 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Internal/SCAPIRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPIRequest.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCAPIConfiguration.h" 11 | 12 | @interface SCAPIRequest : NSMutableURLRequest { 13 | SCAPIConfiguration * encryption; 14 | } 15 | 16 | + (NSString *)timestampString; 17 | + (NSString *)encodeQueryParam:(NSString *)param; 18 | + (NSString *)encodeQuery:(NSDictionary *)dict; 19 | 20 | - (id)initWithConfiguration:(SCAPIConfiguration *)enc 21 | path:(NSString *)path 22 | token:(NSString *)token 23 | dictionary:(NSDictionary *)dict; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Internal/SCAPIRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPIRequest.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPIRequest.h" 10 | 11 | @implementation SCAPIRequest 12 | 13 | + (NSString *)timestampString { 14 | NSTimeInterval time = [NSDate date].timeIntervalSince1970; 15 | return [NSString stringWithFormat:@"%llu", (unsigned long long)round(time * 1000.0)]; 16 | } 17 | 18 | + (NSString *)encodeQueryParam:(NSString *)param { 19 | NSMutableString * str = [NSMutableString string]; 20 | for (NSInteger i = 0; i < param.length; i++) { 21 | unichar aChar = [param characterAtIndex:i]; 22 | if (isalnum(aChar)) { 23 | [str appendFormat:@"%C", aChar]; 24 | } else { 25 | [str appendFormat:@"%%%02X", (unsigned char)aChar]; 26 | } 27 | } 28 | return str; 29 | } 30 | 31 | + (NSString *)encodeQuery:(NSDictionary *)dict { 32 | NSMutableString * str = [NSMutableString string]; 33 | 34 | for (NSString * key in dict) { 35 | if (str.length) [str appendString:@"&"]; 36 | [str appendFormat:@"%@=%@", [self encodeQueryParam:key], 37 | [self encodeQueryParam:[dict[key] description]]]; 38 | } 39 | 40 | return str; 41 | } 42 | 43 | - (id)initWithConfiguration:(SCAPIConfiguration *)enc 44 | path:(NSString *)path 45 | token:(NSString *)token 46 | dictionary:(NSDictionary *)dict { 47 | if ((self = [super init])) { 48 | [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", enc.baseURL, path]]]; 49 | [self setHTTPMethod:@"POST"]; 50 | NSMutableDictionary * jsonBody = [dict mutableCopy]; 51 | NSString * timestamp = self.class.timestampString; 52 | jsonBody[@"req_token"] = [enc dualHash:[token dataUsingEncoding:NSUTF8StringEncoding] 53 | andHash:[timestamp dataUsingEncoding:NSUTF8StringEncoding]]; 54 | jsonBody[@"timestamp"] = @([timestamp longLongValue]); 55 | NSData * encoded = [[self.class encodeQuery:jsonBody] dataUsingEncoding:NSASCIIStringEncoding]; 56 | [self setHTTPBody:encoded]; 57 | [self setValue:[NSString stringWithFormat:@"%d", (int)encoded.length] 58 | forHTTPHeaderField:@"Content-Length"]; 59 | [self setValue:enc.userAgent forHTTPHeaderField:@"User-Agent"]; 60 | [self setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 61 | } 62 | return self; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Managing/SCSnapManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCSnapManager.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SCAPISession.h" 11 | 12 | @protocol SCSnapManagerDelegate 13 | 14 | - (void)scSnapManager:(id)sender insertedAtIndex:(NSInteger)index; 15 | - (void)scSnapManager:(id)sender deletedAtIndex:(NSInteger)index; 16 | - (void)scSnapManagerFinishedLoading:(id)sender; 17 | - (void)scSnapManager:(id)sender failedWithError:(NSError *)e; 18 | 19 | @end 20 | 21 | @interface SCSnapManager : NSObject { 22 | NSMutableArray * fetchers; 23 | NSCache * blobs; 24 | NSMutableArray * snaps; 25 | } 26 | 27 | @property (readonly) NSMutableArray * snaps; 28 | @property (readonly) NSCache * blobs; 29 | @property (nonatomic, weak) id delegate; 30 | 31 | - (void)cancelAll; 32 | - (void)startFetchingBlobs:(SCAPISession *)session; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/Managing/SCSnapManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCSnapManager.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/18/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCSnapManager.h" 10 | 11 | @implementation SCSnapManager 12 | 13 | @synthesize blobs; 14 | @synthesize snaps; 15 | 16 | - (id)init { 17 | if ((self = [super init])) { 18 | snaps = [NSMutableArray array]; 19 | fetchers = [NSMutableArray array]; 20 | blobs = [[NSCache alloc] init]; 21 | blobs.totalCostLimit = (1 << 27); // maximum of 2^27 blob bytes 22 | } 23 | return self; 24 | } 25 | 26 | - (void)cancelAll { 27 | while (fetchers.count > 0) { 28 | [fetchers[0] cancel]; 29 | [fetchers removeObjectAtIndex:0]; 30 | } 31 | } 32 | 33 | - (void)startFetchingBlobs:(SCAPISession *)session { 34 | // remove snaps 35 | NSArray * mediaSnaps = session.mediaSnaps; 36 | for (int i = 0; i < self.snaps.count; i++) { 37 | SCSnap * s = self.snaps[i]; 38 | if (![mediaSnaps containsObject:s]) { 39 | [self.snaps removeObject:s]; 40 | [self.delegate scSnapManager:self deletedAtIndex:i]; 41 | i--; 42 | } 43 | } 44 | for (SCSnap * s in mediaSnaps) { 45 | __weak SCSnapManager * weakSelf = self; 46 | __block SCFetcher * f = [session fetchBlob:s.identifier callback:^(NSError * error, SCBlob * blob) { 47 | [fetchers removeObject:f]; 48 | if (error && ![error.domain isEqualToString:@"SCBlob"]) { 49 | [weakSelf.delegate scSnapManager:weakSelf failedWithError:error]; 50 | } else { 51 | if (blob) { 52 | [weakSelf.blobs setObject:blob forKey:s.identifier 53 | cost:blob.blobData.length]; 54 | if (![weakSelf.snaps containsObject:s]) { 55 | [weakSelf.snaps addObject:s]; 56 | 57 | // sort it by date 58 | [weakSelf.snaps sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 59 | SCSnap * s1 = obj1, * s2 = obj2; 60 | if (s1.timestamp < s2.timestamp) return NSOrderedAscending; 61 | if (s1.timestamp > s2.timestamp) return NSOrderedDescending; 62 | return NSOrderedSame; 63 | }]; 64 | 65 | [weakSelf.delegate scSnapManager:weakSelf 66 | insertedAtIndex:[weakSelf.snaps indexOfObject:s]]; 67 | } 68 | } else { 69 | if ([weakSelf.snaps containsObject:s]) { 70 | NSInteger index = [weakSelf.snaps indexOfObject:s]; 71 | [weakSelf.snaps removeObject:s]; 72 | [weakSelf.delegate scSnapManager:self deletedAtIndex:index]; 73 | } 74 | } 75 | if (!fetchers.count) { 76 | [weakSelf.delegate scSnapManagerFinishedLoading:weakSelf]; 77 | } 78 | } 79 | }]; 80 | [fetchers addObject:f]; 81 | } 82 | if (!fetchers.count) { 83 | [(id)self.delegate performSelector:@selector(scSnapManagerFinishedLoading:) 84 | withObject:self afterDelay:0]; 85 | } 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCAPILogin.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPILogin.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPISession.h" 10 | 11 | @protocol SCAPILoginDelegate 12 | 13 | - (void)scAPILogin:(id)sender failedWithError:(NSError *)error; 14 | - (void)scAPILogin:(id)sender succeededWithSession:(SCAPISession *)session; 15 | 16 | @end 17 | 18 | @interface SCAPILogin : NSObject { 19 | NSMutableData * responseData; 20 | NSURLConnection * connection; 21 | } 22 | 23 | @property (nonatomic, weak) id delegate; 24 | @property (nonatomic, weak) SCAPIConfiguration * configuration; 25 | @property (nonatomic, retain) NSString * username; 26 | @property (nonatomic, retain) NSString * password; 27 | 28 | - (id)initWithConfiguration:(SCAPIConfiguration *)configuration; 29 | - (void)startLogin; 30 | - (void)cancel; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCAPILogin.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPILogin.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPILogin.h" 10 | 11 | @implementation SCAPILogin 12 | 13 | - (id)init { 14 | self = [self initWithConfiguration:[SCAPIConfiguration defaultAPIConfiguration]]; 15 | return self; 16 | } 17 | 18 | - (id)initWithConfiguration:(SCAPIConfiguration *)configuration { 19 | if ((self = [super init])) { 20 | self.configuration = configuration; 21 | responseData = [NSMutableData data]; 22 | } 23 | return self; 24 | } 25 | 26 | - (void)startLogin { 27 | NSDictionary * post = @{@"username": self.username, 28 | @"password": self.password}; 29 | SCAPIRequest * req = [[SCAPIRequest alloc] initWithConfiguration:self.configuration 30 | path:@"/ph/login" 31 | token:self.configuration.staticToken 32 | dictionary:post]; 33 | connection = [NSURLConnection connectionWithRequest:req delegate:self]; 34 | } 35 | 36 | - (void)cancel { 37 | [connection cancel]; 38 | connection = nil; 39 | } 40 | 41 | #pragma mark - Connection - 42 | 43 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 44 | [self.delegate scAPILogin:self failedWithError:error]; 45 | } 46 | 47 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 48 | [responseData appendData:data]; 49 | } 50 | 51 | - (void)connectionDidFinishLoading:(NSURLConnection *)conn { 52 | connection = nil; 53 | NSError * error = nil; 54 | NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error]; 55 | if (![dict isKindOfClass:[NSDictionary class]]) { 56 | return [self.delegate scAPILogin:self failedWithError:error]; 57 | } 58 | if (![dict[@"auth_token"] isKindOfClass:[NSString class]]) { 59 | return [self.delegate scAPILogin:self failedWithError:[NSError errorWithDomain:@"SCAPILogin" 60 | code:1 61 | userInfo:@{NSLocalizedDescriptionKey:@"Invalid username or password."}]]; 62 | } 63 | 64 | SCAPISession * session = [[SCAPISession alloc] init]; 65 | session.authToken = dict[@"auth_token"]; 66 | session.configuration = self.configuration; 67 | session.snaps = [SCAPISession parseSnaps:dict[@"snaps"]]; 68 | session.username = self.username; 69 | [self.delegate scAPILogin:self succeededWithSession:session]; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCAPISession.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPISession.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPIRequest.h" 10 | #import "SCFetcher.h" 11 | 12 | #import "SCSnap.h" 13 | #import "SCBlob.h" 14 | 15 | @interface SCAPISession : NSObject { 16 | 17 | } 18 | 19 | @property (nonatomic, retain) SCAPIConfiguration * configuration; 20 | @property (nonatomic, retain) NSString * authToken; 21 | @property (nonatomic, retain) NSString * username; 22 | @property (nonatomic, retain) NSArray * snaps; 23 | 24 | + (NSArray *)parseSnaps:(NSArray *)jsonSnaps; 25 | 26 | - (SCAPIRequest *)requestForBlob:(NSString *)blobIdentifier; 27 | - (SCFetcher *)fetchBlob:(NSString *)blobIdentifier 28 | callback:(void (^)(NSError * error, SCBlob * blob))cb; 29 | 30 | - (SCAPIRequest *)requestForSendingEvents:(NSArray *)list snapInfo:(NSDictionary *)info; 31 | - (SCFetcher *)markSnapViewed:(NSString *)blobId time:(NSTimeInterval)delay callback:(void (^)(NSError * error))cb; 32 | - (SCFetcher *)markSnapScreenshot:(NSString *)blobId time:(NSTimeInterval)delay callback:(void (^)(NSError * error))cb; 33 | 34 | /** 35 | * Asynchronously reload the snaps and authToken. 36 | */ 37 | - (SCFetcher *)reloadAll:(void (^)(NSError * error))callback; 38 | 39 | - (NSArray *)mediaSnaps; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCAPISession.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAPISession.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCAPISession.h" 10 | 11 | @implementation SCAPISession 12 | 13 | + (NSArray *)parseSnaps:(NSArray *)jsonSnaps { 14 | NSMutableArray * snaps = [[NSMutableArray alloc] init]; 15 | for (NSDictionary * snap in jsonSnaps) { 16 | [snaps addObject:[[SCSnap alloc] initWithDictionary:snap]]; 17 | } 18 | return snaps; 19 | } 20 | 21 | - (NSString *)description { 22 | return [NSString stringWithFormat:@"<%@ authToken=%@, snaps=%@>", NSStringFromClass(self.class), self.authToken, self.snaps]; 23 | } 24 | 25 | #pragma mark - Blobs - 26 | 27 | - (SCAPIRequest *)requestForBlob:(NSString *)blobIdentifier { 28 | NSDictionary * params = @{@"username": self.username, 29 | @"id": blobIdentifier}; 30 | return [[SCAPIRequest alloc] initWithConfiguration:self.configuration 31 | path:@"/bq/blob" 32 | token:self.authToken 33 | dictionary:params]; 34 | } 35 | 36 | - (SCFetcher *)fetchBlob:(NSString *)blobIdentifier 37 | callback:(void (^)(NSError * error, SCBlob * blob))cb { 38 | NSURLRequest * req = [self requestForBlob:blobIdentifier]; 39 | return [SCFetcher fetcherStartedForRequestNoJSON:req callback:^(NSError * error, id result) { 40 | if (error) return cb(error, nil); 41 | SCBlob * blob = [SCBlob blobWithData:result]; 42 | if (!blob) { 43 | // decrypt the data 44 | NSData * testData = [self.configuration decrypt:result]; 45 | blob = [SCBlob blobWithData:testData]; 46 | } 47 | if (!blob) return cb([NSError errorWithDomain:@"SCBlob" 48 | code:1 49 | userInfo:@{NSLocalizedDescriptionKey: @"Unknown blob type."}], nil); 50 | cb(nil, blob); 51 | }]; 52 | } 53 | 54 | #pragma mark - Events - 55 | 56 | - (SCAPIRequest *)requestForSendingEvents:(NSArray *)list snapInfo:(NSDictionary *)info { 57 | NSData * jsonData = [NSJSONSerialization dataWithJSONObject:info options:0 error:nil]; 58 | NSData * eventsData = [NSJSONSerialization dataWithJSONObject:list options:0 error:nil]; 59 | NSDictionary * req = @{@"username": self.username, 60 | @"json": [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding], 61 | @"events": [[NSString alloc] initWithData:eventsData encoding:NSUTF8StringEncoding]}; 62 | return [[SCAPIRequest alloc] initWithConfiguration:self.configuration 63 | path:@"/bq/update_snaps" 64 | token:self.authToken 65 | dictionary:req]; 66 | } 67 | 68 | - (SCFetcher *)markSnapViewed:(NSString *)blobId time:(NSTimeInterval)delay callback:(void (^)(NSError * error))cb { 69 | NSTimeInterval ts = [[NSDate date] timeIntervalSince1970]; 70 | NSDictionary * timeInfo = @{@"t": @(ts), 71 | @"sv": @(delay)}; 72 | NSDictionary * snapInfo = @{blobId: timeInfo}; 73 | NSArray * events = @[@{@"eventName": @"SNAP_VIEW", @"params": @{@"id": blobId}, @"ts": @(ts - delay)}, 74 | @{@"eventName": @"SNAP_EXPIRED", @"params": @{@"id": blobId}, @"ts": @(ts)}]; 75 | return [SCFetcher fetcherStartedForRequestNoJSON:[self requestForSendingEvents:events snapInfo:snapInfo] 76 | callback:^(NSError * error, id result) { 77 | cb(error); 78 | }]; 79 | } 80 | 81 | - (SCFetcher *)markSnapScreenshot:(NSString *)blobId time:(NSTimeInterval)delay callback:(void (^)(NSError * error))cb { 82 | NSTimeInterval ts = [[NSDate date] timeIntervalSince1970]; 83 | NSDictionary * timeInfo = @{@"t": @(ts), 84 | @"sv": @(delay), 85 | @"c": @(3)}; 86 | NSDictionary * snapInfo = @{blobId: timeInfo}; 87 | NSArray * events = @[@{@"eventName": @"SNAP_SCREENSHOT", @"params": @{@"id": blobId}, @"ts": @(ts - delay)}]; 88 | return [SCFetcher fetcherStartedForRequestNoJSON:[self requestForSendingEvents:events snapInfo:snapInfo] 89 | callback:^(NSError * error, id result) { 90 | cb(error); 91 | }]; 92 | } 93 | 94 | #pragma mark - Extra - 95 | 96 | - (SCFetcher *)reloadAll:(void (^)(NSError * error))callback { 97 | NSDictionary * reqArgs = @{@"username": self.username}; 98 | NSURLRequest * req = [[SCAPIRequest alloc] initWithConfiguration:self.configuration 99 | path:@"/bq/all_updates" 100 | token:self.authToken 101 | dictionary:reqArgs]; 102 | return [SCFetcher fetcherStartedForRequest:req callback:^(NSError * error, id result) { 103 | if (error) return callback(error); 104 | NSDictionary * dict = result; 105 | if (dict[@"updates_response"]) { 106 | dict = dict[@"updates_response"]; 107 | self.snaps = [self.class parseSnaps:dict[@"snaps"]]; 108 | self.authToken = dict[@"auth_token"]; 109 | } else if (dict[@"snaps"]) { 110 | self.snaps = [self.class parseSnaps:dict[@"snaps"]]; 111 | } 112 | callback(nil); 113 | }]; 114 | } 115 | 116 | - (NSArray *)mediaSnaps { 117 | NSMutableArray * snaps = [NSMutableArray array]; 118 | 119 | for (SCSnap * snap in self.snaps) { 120 | if ([snap isImageOrVideo]) { 121 | [snaps addObject:snap]; 122 | } 123 | } 124 | 125 | return snaps; 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCFetcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCFetcher.h 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^SCFetcherBlock)(NSError * error, id result); 12 | 13 | @interface SCFetcher : NSObject { 14 | NSMutableData * data; 15 | NSURLConnection * connection; 16 | } 17 | 18 | @property (readwrite) BOOL parseJSON; 19 | @property (nonatomic, copy) SCFetcherBlock callback; 20 | 21 | + (SCFetcher *)fetcherStartedForRequest:(NSURLRequest *)req callback:(SCFetcherBlock)block; 22 | + (SCFetcher *)fetcherStartedForRequestNoJSON:(NSURLRequest *)req callback:(SCFetcherBlock)block; 23 | 24 | - (id)initWithRequest:(NSURLRequest *)request; 25 | - (void)start; 26 | - (void)cancel; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /SnapchatHax/Snapchat/SCFetcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCFetcher.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import "SCFetcher.h" 10 | 11 | @implementation SCFetcher 12 | 13 | + (SCFetcher *)fetcherStartedForRequest:(NSURLRequest *)req callback:(SCFetcherBlock)block { 14 | SCFetcher * fetcher = [[SCFetcher alloc] initWithRequest:req]; 15 | fetcher.callback = block; 16 | fetcher.parseJSON = YES; 17 | [fetcher start]; 18 | return fetcher; 19 | } 20 | 21 | + (SCFetcher *)fetcherStartedForRequestNoJSON:(NSURLRequest *)req callback:(SCFetcherBlock)block { 22 | SCFetcher * fetcher = [[SCFetcher alloc] initWithRequest:req]; 23 | fetcher.callback = block; 24 | [fetcher start]; 25 | return fetcher; 26 | } 27 | 28 | - (id)initWithRequest:(NSURLRequest *)request { 29 | if ((self = [super init])) { 30 | connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)start { 36 | data = [NSMutableData data]; 37 | [connection start]; 38 | } 39 | 40 | - (void)cancel { 41 | data = nil; 42 | [connection cancel]; 43 | connection = nil; 44 | self.callback = nil; 45 | } 46 | 47 | #pragma mark - NSURLConnection - 48 | 49 | - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 50 | self.callback(error, nil); 51 | self.callback = nil; 52 | } 53 | 54 | - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)_data { 55 | [data appendData:_data]; 56 | } 57 | 58 | - (void)connectionDidFinishLoading:(NSURLConnection *)conn { 59 | connection = nil; 60 | if (self.parseJSON) { 61 | NSError * error = nil; 62 | NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 63 | data = nil; 64 | if (![dict isKindOfClass:[NSDictionary class]]) { 65 | self.callback(error, nil); 66 | self.callback = nil; 67 | return; 68 | } 69 | self.callback(nil, dict); 70 | } else { 71 | self.callback(nil, [data copy]); 72 | data = nil; 73 | } 74 | self.callback = nil; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /SnapchatHax/SnapchatHax-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Hackchat 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.aqnichol.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /SnapchatHax/SnapchatHax-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /SnapchatHax/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /SnapchatHax/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SnapchatHax 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ANAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ANAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SnapchatHaxTests/SnapchatHaxTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.aqnichol.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SnapchatHaxTests/SnapchatHaxTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SnapchatHaxTests.m 3 | // SnapchatHaxTests 4 | // 5 | // Created by Alex Nichol on 12/17/13. 6 | // Copyright (c) 2013 Alex Nichol. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SnapchatHaxTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation SnapchatHaxTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /SnapchatHaxTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------