From 3ba4d0bb36c651fb13ca3b4fd2e780e6d4b9b1ca Mon Sep 17 00:00:00 2001 From: Wesley Hill Date: Fri, 5 Dec 2014 11:05:30 +0000 Subject: [PATCH 1/4] Fix snaptcha with latest code changes --- README.md | 101 +++++++++++++++++++++++++--- src/snapchat.php | 149 ++++++++++++++++++++++++++++++++++------- src/snapchat_agent.php | 37 +++++++++- 3 files changed, 248 insertions(+), 39 deletions(-) mode change 100755 => 100644 README.md diff --git a/README.md b/README.md old mode 100755 new mode 100644 index c9aeefc..dc069a6 --- a/README.md +++ b/README.md @@ -2,17 +2,14 @@ Snapchat for PHP ================ [![Build Status](https://travis-ci.org/JorgenPhi/php-snapchat.png)](https://travis-ci.org/JorgenPhi/php-snapchat) -This library is built to communicate with the Snapchat API. It was nearly -feature complete and lacks some newer functionality available in the latest -versions of the official apps (stories, text messages, and "chat"). +This library is built to communicate with the Snapchat API. It is nearly +feature complete but still lacks some functionality available in the latest +versions of the official apps (namely Stories). It's similar to the [excellent Snaphax library](http://github.com/tlack/snaphax) built by Thomas Lackner <[@tlack](http://twitter.com/tlack)>, but the approach is different enough that I figured it deserved its own repo. -We love it when developers add new features, fix bugs, and submit pull requests. -(We need more pull requests!) - Usage ----- @@ -33,7 +30,7 @@ $snaps = $snapchat->getFriendStories(); // Download a specific snap: $data = $snapchat->getMedia('122FAST2FURIOUS334r'); -file_put_contents('/home/jorgen/snap.jpg', $data); +file_put_contents('/home/dan/snap.jpg', $data); // Download a specific story: $data = $snapchat->getStory('[story_media_id]', '[story_key]', '[story_iv]'); @@ -53,14 +50,14 @@ $snapchat->markSnapShot('122FAST2FURIOUS334r'); // Upload a snap and send it to me for 8 seconds: $id = $snapchat->upload( Snapchat::MEDIA_IMAGE, - file_get_contents('/home/jorgen/whatever.jpg') + file_get_contents('/home/dan/whatever.jpg') ); -$snapchat->send($id, array('jorgenphi'), 8); +$snapchat->send($id, array('stelljes'), 8); // Upload a video story: $id = $snapchat->upload( Snapchat::MEDIA_VIDEO, - file_get_contents('/home/jorgen/whatever.mov') + file_get_contents('/home/dan/whatever.mov') ); $snapchat->setStory($id, Snapchat::MEDIA_VIDEO); @@ -101,7 +98,7 @@ $snapchat->deleteFriend('bart'); $snapchat->updatePrivacy(Snapchat::PRIVACY_FRIENDS); // You want to change your email: -$snapchat->updateEmail('jorgen@example.com'); +$snapchat->updateEmail('dan@example.com'); // Log out: $snapchat->logout(); @@ -109,6 +106,88 @@ $snapchat->logout(); ?> ``` +##Snaptcha + +Below is an example on how to bypass the "Snaptcha" made by Snapchat. +This fork includes two new methods. + +getCaptcha() + +and + +sendCaptcha() + +[The new endpoints are discussed in more detail here](http://www.hakobaito.co.uk/b/bypassing-snaptcha) + +Example: + +```php +register($email,$password,$birthday); // Register an account... +$registration = $s->register_username($email, $username); // Register desired username... + +// registration check... +if(is_int($registration)) { + + if ($registration == 69) { + print "username is too short!\n"; + exit(); + } + + else if ($registration == 70) { + print "username is too long!\n"; + exit(); + } + + else if ($registration == 71) { + print "bad username\n"; + exit(); + } + + else if ($registration == 72) { + print "username is taken!\n"; + exit(); + } +} + +$captcha_id = $s->getCaptcha($username, true); // verify yourself... + +// Ask the user for the captcha, +// (should be replaced with respected ghost images)... +// returns false if unable to get the captcha_id. + +print $captcha_id . "\n"; +echo "captcha: "; +$solution_raw = fgets(STDIN); // Solution is 9 characters long eg. 001010011 +$solution = str_replace("\n", "", $solution_raw); // strip off invisible characters. +$verify = $s->sendCaptcha($solution, $captcha_id, $username); // Send off Snaptcha. + +// Check if Snaptcha is correct... +if($verify == TRUE) { + print "Snaptcha passed, Snapchat account verified."; +} +else if($verify == FALSE) { + print "Incorrect Snaptcha, Snapchat account not verified."; +} + +?> +``` + Documentation ------------ diff --git a/src/snapchat.php b/src/snapchat.php index fe64534..9923bde 100755 --- a/src/snapchat.php +++ b/src/snapchat.php @@ -157,27 +157,26 @@ public function logout() { * @todo * Add better validation. * - * @param string $username - * The desired username. - * @param string $password - * The password to associate with the account. * @param string $email * The email address to associate with the account. + * @param string $password + * The password to associate with the account. * @param $birthday string * The user's birthday (yyyy-mm-dd). * * @return mixed - * The data returned by the service or FALSE if registration failed. - * Generally, returns the same result as calling self::getUpdates(). + * The data returned is TRUE if the registration succeeded or + * FALSE if the registration failed. Generally, returns the + * same result as calling self::getUpdates(). */ - public function register($username, $password, $email, $birthday) { + public function register($email, $password, $birthday) { $timestamp = parent::timestamp(); $result = parent::post( '/register', array( - 'birthday' => $birthday, - 'password' => $password, 'email' => $email, + 'password' => $password, + 'birthday' => $birthday, 'timestamp' => $timestamp, ), array( @@ -186,39 +185,137 @@ public function register($username, $password, $email, $birthday) { ) ); - if (!isset($result->token)) { + if (!isset($result->auth_token)) { return FALSE; } - $timestamp = parent::timestamp(); - $result = parent::post( - '/registeru', - array( - 'email' => $email, - 'username' => $username, - 'timestamp' => $timestamp, - ), - array( - parent::STATIC_TOKEN, - $timestamp, - ) - ); - // If registration is successful, set the username and auth_token. if (isset($result->logged) && $result->logged) { $this->auth_token = $result->auth_token; - $this->username = $result->username; $this->cache = new SnapchatCache(); $this->cache->set('updates', $result); - return $result; + return TRUE; } + else { return FALSE; } } + /** + * Registers a username. + * + * @param string $email + * The email address to associate with the account. + * @param string $username + * The desired username for this account. + * + * @return mixed + * TRUE is returned if the username is accepted. + * status integer is returned if the username is either of the following: + * + * 69 - too short + * 70 - too long + * 71 - bad username + * 72 - taken + */ + public function register_username($email, $username) { + $timestamp = parent::timestamp(); + $result = parent::post( + '/register_username', + array( + 'username' => $email, + 'selected_username' => $username, + 'timestamp' => $timestamp + ), + array( + $this->auth_token, + $timestamp, + ) + ); + + if(!property_exists($result, "status")) + { + return TRUE; + } + else if($result->status == 69 || 70 || 71 || 72) { + return $result->status; + } + } + + /** + * Gets the captcha associated to the username from snapchat's server. + * + * @param string $username + * The username to get the captcha puzzle, or in this case an email. + * + * @param bool $download + * Download the captcha archive from snapchat. (optional) + * + * @return string + * A string which is the captcha_id. FALSE on failure. + * + */ + public function getCaptcha($username, $download = NULL) { + $timestamp = parent::timestamp(); + $result = parent::post( + '/get_captcha', + array( + 'username' => $username, + 'timestamp' => $timestamp, + 'dl' => $download + ), + array( + $this->auth_token, + $timestamp + ) + ); + return $result; + } + + /** + * Sends the captcha solution to snapchat's server. + * + * @param $captcha_solution + * The binary solution of the captcha. must be 8 chars long. + * + * @param $captcha_id + * The ID of the captcha thats being solved. + * + * @param $username + * The username to be verified. + * + * @return mixed + * returns TRUE if captcha is solved, FALSE otherwise. + * + */ + public function sendCaptcha($captcha_solution, $captcha_id, $username) { + $timestamp = parent::timestamp(); + $result = parent::post( + '/solve_captcha', + array( + 'captcha_solution' => $captcha_solution, + 'captcha_id' => $captcha_id, + 'username' => $username, + 'timestamp' => $timestamp, + ), + array( + $this->auth_token, + $timestamp, + + ) + ); + unlink(".headers.txt"); + if(is_null($result)) { + return TRUE; + } + else if($result == FALSE) { + return FALSE; + } + } + /** * Retrieves general user, friend, and snap updates. * diff --git a/src/snapchat_agent.php b/src/snapchat_agent.php index 1dc22a9..2996daa 100644 --- a/src/snapchat_agent.php +++ b/src/snapchat_agent.php @@ -14,14 +14,20 @@ abstract class SnapchatAgent { const VERSION = '4.1.07'; /* - * The API URL. We're using the /bq endpoint, the one that the iPhone + * The API URL. We're using the /loq endpoint, the one that the iPhone * uses. Android clients still seem to be using the /ph endpoint. + * We still support the /bq endpoint for the captcha. * * @todo * Make library capable of using different endpoints (some of the * resource names are different, so they aren't interchangeable). */ - const URL = 'https://feelinsonice-hrd.appspot.com/bq'; + const URL = 'https://feelinsonice-hrd.appspot.com/loq'; + + /* + * The old API URL. + */ + const OLD_URL = "https://feelinsonice-hrd.appspot.com/bq"; /* * The API secret. Used to create access tokens. @@ -291,15 +297,42 @@ public function post($endpoint, $data, $params, $multipart = FALSE) { $data = http_build_query($data); } + if(array_key_exists('dl', $data)){ + $download = $data['dl']; + } + + if($endpoint == "/get_captcha" || $endpoint == "/solve_captcha" ) { + $options = self::$CURL_OPTIONS + array( + CURLOPT_POST => TRUE, + CURLOPT_POSTFIELDS => $data, + CURLOPT_URL => self::OLD_URL . $endpoint, + ); + } else { $options = self::$CURL_OPTIONS + array( CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $data, CURLOPT_URL => self::URL . $endpoint, ); + } curl_setopt_array($ch, $options); $result = curl_exec($ch); + // upon registration, the captcha sends us a header to download. + if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) == "application/zip; $charset=UTF-8") { + $filename = fopen(dirname(__DIR__) . "/.headers.txt", "r+"); + $stream = stream_get_contents($filename); + $file = preg_match("/(=)(\S+).zip/", $stream, $match); + $group_matched = $match[2] . ".zip"; + $captcha_id = str_replace(".zip", "", $group_matched); + if($download == 1) { + file_put_contents($group_matched, $result); + } + unlink(".headers.txt"); + curl_close($ch); + return $captcha_id; + } + // If cURL doesn't have a bundle of root certificates handy, we provide // ours (see http://curl.haxx.se/docs/sslcerts.html). if (curl_errno($ch) == 60) { From fd01ca4c53e15abed13bf244f0aa0ddeffdd17c8 Mon Sep 17 00:00:00 2001 From: Wesley Hill Date: Fri, 5 Dec 2014 14:41:05 +0000 Subject: [PATCH 2/4] Fixed header issues and changed version. --- src/snapchat_agent.php | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/snapchat_agent.php b/src/snapchat_agent.php index 2996daa..21a6a2b 100644 --- a/src/snapchat_agent.php +++ b/src/snapchat_agent.php @@ -8,10 +8,10 @@ abstract class SnapchatAgent { /* - * App version (as of 2013-11-20). Before updating this value, confirm + * App version (as of 2014-12-5). Before updating this value, confirm * that the library requests everything in the same way as the app. */ - const VERSION = '4.1.07'; + const VERSION = '8.0.1.3'; /* * The API URL. We're using the /loq endpoint, the one that the iPhone @@ -59,7 +59,7 @@ abstract class SnapchatAgent { CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_TIMEOUT => 10, - CURLOPT_USERAGENT => 'Snapchat/4.1.07 (Nexus 4; Android 18; gzip)', + CURLOPT_USERAGENT => 'Snapchat/8.0.1.3 (Nexus 4; Android 18; gzip)', ); /** @@ -292,34 +292,42 @@ public function post($endpoint, $data, $params, $multipart = FALSE) { $data['req_token'] = self::hash($params[0], $params[1]); $data['version'] = self::VERSION; + + if(array_key_exists('dl', $data)) { + $download = $data['dl']; + } if (!$multipart) { $data = http_build_query($data); } - if(array_key_exists('dl', $data)){ - $download = $data['dl']; - } - if($endpoint == "/get_captcha" || $endpoint == "/solve_captcha" ) { + $options = self::$CURL_OPTIONS + array( CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $data, CURLOPT_URL => self::OLD_URL . $endpoint, ); + + curl_setopt($ch, CURLOPT_VERBOSE, true); + file_put_contents(".headers.txt", " "); + curl_setopt($ch, CURLOPT_STDERR, fopen(dirname(__DIR__) . "/.headers.txt", "r+")); + } else { + $options = self::$CURL_OPTIONS + array( CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $data, CURLOPT_URL => self::URL . $endpoint, ); + } + curl_setopt_array($ch, $options); - $result = curl_exec($ch); // upon registration, the captcha sends us a header to download. - if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) == "application/zip; $charset=UTF-8") { + if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) == "application/zip; charset=UTF-8") { $filename = fopen(dirname(__DIR__) . "/.headers.txt", "r+"); $stream = stream_get_contents($filename); $file = preg_match("/(=)(\S+).zip/", $stream, $match); From bd53190d862f26c2b39dce4399040983295d2a2c Mon Sep 17 00:00:00 2001 From: Wesley Hill Date: Fri, 5 Dec 2014 15:07:12 +0000 Subject: [PATCH 3/4] Added minimal documentation Added sendCaptcha() and getCaptcha() methods. --- README.md | 92 ++++++++----------------------------------------------- 1 file changed, 13 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index dc069a6..292b2e7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,12 @@ Include src/snapchat.php via require_once or Composer or whatever, then: register('email','password','birthday'); +$snapchat->register_username('email', 'username'); // Get your feed: $snaps = $snapchat->getSnaps(); @@ -106,88 +111,17 @@ $snapchat->logout(); ?> ``` -##Snaptcha - -Below is an example on how to bypass the "Snaptcha" made by Snapchat. -This fork includes two new methods. - -getCaptcha() - -and - -sendCaptcha() - -[The new endpoints are discussed in more detail here](http://www.hakobaito.co.uk/b/bypassing-snaptcha) - -Example: +Snaptcha +----- +For the methods on bypassing Snapchat's captcha system see this [gist](https://gist.github.com/hako/adb2ab9419eda2dca62b): ```php -register($email,$password,$birthday); // Register an account... -$registration = $s->register_username($email, $username); // Register desired username... - -// registration check... -if(is_int($registration)) { - - if ($registration == 69) { - print "username is too short!\n"; - exit(); - } - - else if ($registration == 70) { - print "username is too long!\n"; - exit(); - } - - else if ($registration == 71) { - print "bad username\n"; - exit(); - } - - else if ($registration == 72) { - print "username is taken!\n"; - exit(); - } -} - -$captcha_id = $s->getCaptcha($username, true); // verify yourself... - -// Ask the user for the captcha, -// (should be replaced with respected ghost images)... -// returns false if unable to get the captcha_id. - -print $captcha_id . "\n"; -echo "captcha: "; -$solution_raw = fgets(STDIN); // Solution is 9 characters long eg. 001010011 -$solution = str_replace("\n", "", $solution_raw); // strip off invisible characters. -$verify = $s->sendCaptcha($solution, $captcha_id, $username); // Send off Snaptcha. - -// Check if Snaptcha is correct... -if($verify == TRUE) { - print "Snaptcha passed, Snapchat account verified."; -} -else if($verify == FALSE) { - print "Incorrect Snaptcha, Snapchat account not verified."; -} - -?> +$s->getCaptcha('username', true); ``` +```php +$s->sendCaptcha('solution', 'captcha_id', 'username'); +``` Documentation ------------ From 1597f3860f98fcf71f7283d75d9a7532f1aa91a1 Mon Sep 17 00:00:00 2001 From: Wesley Hill Date: Fri, 5 Dec 2014 15:36:17 +0000 Subject: [PATCH 4/4] change method to camelCase --- README.md | 2 +- src/snapchat.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 292b2e7..9bc9105 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ $snapchat = new Snapchat('username' 'password'); //Register an Account and username: $snapchat = new Snapchat(); $snapchat->register('email','password','birthday'); -$snapchat->register_username('email', 'username'); +$snapchat->registerUsername('email', 'username'); // Get your feed: $snaps = $snapchat->getSnaps(); diff --git a/src/snapchat.php b/src/snapchat.php index 9923bde..8c92ee0 100755 --- a/src/snapchat.php +++ b/src/snapchat.php @@ -221,7 +221,7 @@ public function register($email, $password, $birthday) { * 71 - bad username * 72 - taken */ - public function register_username($email, $username) { + public function registerUsername($email, $username) { $timestamp = parent::timestamp(); $result = parent::post( '/register_username',