Advertisement
Guest User

Untitled

a guest
Jun 24th, 2017
3,412
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.30 KB | None | 0 0
  1. <?php
  2. error_reporting(1);
  3.  
  4. /**
  5. * FindMyiPhone - A PHP class for interacting with iCloud's Find My iPhone.
  6. * Developed by Neal, Fixed by Aminetiaret (2017)
  7. */
  8.  
  9. class FindMyiPhone
  10. {
  11. private $username;
  12. private $password;
  13.  
  14. private $loggedIn;
  15. private $cookies;
  16.  
  17. public $devices = array();
  18.  
  19. private $email_updates = true;
  20.  
  21. private $host = 'p45-fmipweb.icloud.com';
  22.  
  23. private $client_context = array(
  24. 'appName' => 'FindMyiPhone',
  25. 'appVersion' => '3.0',
  26. 'buildVersion' => '376',
  27. 'clientTimestamp' => 0,
  28. 'deviceUDID' => null,
  29. 'inactiveTime' => 1,
  30. 'osVersion' => '7.0.3',
  31. 'productType' => 'iPhone6,1'
  32. );
  33.  
  34. private $server_context = array(
  35. 'callbackIntervalInMS' => 10000,
  36. 'classicUser' => false,
  37. 'clientId' => null,
  38. 'cloudUser' => true,
  39. 'deviceLoadStatus' => '200',
  40. 'enableMapStats' => false,
  41. 'isHSA' => false,
  42. 'lastSessionExtensionTime' => null,
  43. 'macCount' => 0,
  44. 'maxDeviceLoadTime' => 60000,
  45. 'maxLocatingTime' => 90000,
  46. 'preferredLanguage' => 'en-us',
  47. 'prefsUpdateTime' => 0,
  48. 'sessionLifespan' => 900000,
  49. 'timezone' => null,
  50. 'trackInfoCacheDurationInSecs' => 86400,
  51. 'validRegion' => true
  52. );
  53.  
  54.  
  55. /**
  56. * Constructor
  57. * Checks requred extensions, sets username/password and gets url host for the user.
  58. * @param $username - iCloud Apple ID
  59. * @param $password - iCloud Password
  60. */
  61. public function __construct($username, $password)
  62. {
  63. if (!extension_loaded('curl')) {
  64. throw new FindMyiPhoneException('PHP extension cURL is not loaded.');
  65. }
  66.  
  67. $this->username = $username;
  68. $this->password = $password;
  69.  
  70. if ( !$this->login() )
  71. {
  72. return $this->loggedIn = false;
  73. }
  74. return $this->loggedIn = true;
  75. }
  76.  
  77. private function login()
  78. {
  79. $url = 'https://setup.icloud.com/setup/ws/1/login';
  80.  
  81. $data = array(
  82. 'apple_id' => $this->username,
  83. 'password' => $this->password,
  84. 'extended_login' => flase
  85. );
  86.  
  87. $response = $this->request($url, json_encode($data));
  88. $result = $response[0];
  89. $result_with_headers = $response[1];
  90.  
  91. $res = json_decode($result, true);
  92.  
  93. if ( isset($res['error']) )
  94. {
  95. return false;
  96. }
  97. else{
  98. $this->host = $res['webservices']['findme']['url'];
  99. $this->cookies = 'Cookie: '. $this->parseCookies($result_with_headers);
  100. $this->refresh_client();
  101.  
  102. return true;
  103. }
  104. }
  105.  
  106. public function parseCookies($headers)
  107. {
  108. preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $headers, $matches);
  109.  
  110. $cookies = '';
  111.  
  112. foreach ( $matches[0] as $value )
  113. {
  114. $value = str_replace('Set-Cookie: ', '', $value);
  115. $cookies .= $value.'; ';
  116. }
  117.  
  118. return $cookies;
  119. }
  120.  
  121. public function loggedIn()
  122. {
  123. return $this->loggedIn;
  124. }
  125.  
  126. /**
  127. * Set email updates
  128. * If false, requests will request to not send email to the user. (doesn't work on all requests)
  129. * True by default. (optional to set)
  130. * $param $email_updates - bool
  131. */
  132. public function set_email_updates($email_updates) {
  133. $this->email_updates = (bool) $email_updates;
  134. }
  135.  
  136.  
  137. /**
  138. * Init Client
  139. *
  140. */
  141. private function init_client() {
  142. $post_data = json_encode(array(
  143. 'clientContext' => $this->client_context
  144. ));
  145.  
  146. $headers = $this->parse_curl_headers($this->make_request('initClient', $post_data, true));
  147.  
  148. $this->host = $headers['X-Apple-MMe-Host'];
  149.  
  150. if ( !empty($this->host) || isset($this->host) )
  151. {
  152. $this->refresh_client();
  153. }
  154. }
  155.  
  156.  
  157. /**
  158. * Refresh Client
  159. *
  160. */
  161. public function refresh_client() {
  162. $post_data = json_encode(array(
  163. 'clientContext' => $this->client_context,
  164. 'serverContext' => $this->server_context
  165. ));
  166.  
  167. foreach (json_decode($this->make_request('refreshClient', $post_data))->content as $id => $device) {
  168. $this->devices[$id] = $device;
  169. }
  170. }
  171.  
  172. /**
  173. * Remove Device
  174. *
  175. */
  176. public function remove_client($device_id)
  177. {
  178. if(!is_string($device_id)) return;
  179.  
  180. $post_data = json_encode(array(
  181. 'clientContext' => $this->client_context,
  182. 'serverContext' => $this->server_context,
  183. 'device' => $device_id,
  184. ));
  185.  
  186. return json_decode($this->make_request('remove', $post_data));
  187. }
  188.  
  189.  
  190. /**
  191. * Play Sound
  192. *
  193. */
  194. public function play_sound($device_id, $subject = 'Find My iPhone Alert') {
  195. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  196. if(!is_string($subject)) throw new FindMyiPhoneException('Expected $subject to be a string');
  197.  
  198. $post_data = json_encode(array(
  199. 'clientContext' => $this->client_context,
  200. 'serverContext' => $this->server_context,
  201. 'device' => $device_id,
  202. 'subject' => $subject
  203. ));
  204.  
  205. return json_decode($this->make_request('playSound', $post_data))->content[0]->snd;
  206. }
  207.  
  208.  
  209. /**
  210. * Send Message
  211. *
  212. */
  213. public function send_message($device_id, $text, $sound = false, $subject = 'Important Message') {
  214. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  215. if(!is_string($text)) throw new FindMyiPhoneException('Expected $text to be a string');
  216. if(!is_bool($sound)) throw new FindMyiPhoneException('Expected $sound to be a bool');
  217. if(!is_string($subject)) throw new FindMyiPhoneException('Expected $subject to be a string');
  218.  
  219. $post_data = json_encode(array(
  220. 'clientContext' => $this->client_context,
  221. 'serverContext' => $this->server_context,
  222. 'device' => $device_id,
  223. 'emailUpdates' => $this->email_updates,
  224. 'sound' => $sound,
  225. 'subject' => $subject,
  226. 'text' => $text,
  227. 'userText' => true
  228. ));
  229.  
  230. return json_decode($this->make_request('sendMessage', $post_data))->content[0]->msg;
  231. }
  232.  
  233.  
  234. /**
  235. * Lock Device
  236. *
  237. */
  238. public function lost_device($device_id, $passcode, $owner_phone_number = '911', $sound = true, $text = 'This iPhone has been lost. Please call me.') {
  239. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  240. if(!is_string($passcode)) throw new FindMyiPhoneException('Expected $passcode to be a string');
  241. if(strlen($passcode) !== 4) throw new FindMyiPhoneException('Expected $passcode to be 4 characters long');
  242. if(!is_string($owner_phone_number)) throw new FindMyiPhoneException('Expected $owner_phone_number to be a string');
  243. if(!is_bool($sound)) throw new FindMyiPhoneException('Expected $sound to be a bool');
  244. if(!is_string($text)) throw new FindMyiPhoneException('Expected $text to be a string');
  245.  
  246. $post_data = json_encode(array(
  247. 'clientContext' => $this->client_context,
  248. 'serverContext' => $this->server_context,
  249. 'device' => $device_id,
  250. 'emailUpdates' => $this->email_updates,
  251. 'lostModeEnabled' => true,
  252. 'ownerNbr' => $owner_phone_number,
  253. 'passcode' => $passcode,
  254. 'sound' => $sound,
  255. 'text' => $text,
  256. 'trackingEnabled' => true,
  257. 'userText' => true
  258. ));
  259.  
  260. return json_decode($this->make_request('lostDevice', $post_data))->content[0]->lostDevice;
  261. }
  262.  
  263.  
  264. /**
  265. * Notify When Found
  266. *
  267. */
  268. public function notify_when_found($device_id, $notify = true) {
  269. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  270. if(!is_string($notify)) throw new FindMyiPhoneException('Expected $notify to be a boolean');
  271.  
  272. $post_data = json_encode(array(
  273. 'clientContext' => $this->client_context,
  274. 'serverContext' => $this->server_context,
  275. 'device' => $device_id,
  276. 'lostModeEnabled' => $notify
  277. ));
  278.  
  279. return json_decode($this->make_request('saveLocFoundPref', $post_data))->content[0]->locFoundEnabled;
  280. }
  281.  
  282.  
  283. /**
  284. * Lock and Message
  285. *
  286. */
  287. public function lock_and_message($device_id, $passcode, $text, $sound = true, $title = 'Find My iPhone Alert') {
  288. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  289. if(!is_string($passcode)) throw new FindMyiPhoneException('Expected $passcode to be a string');
  290. if(strlen($passcode) !== 4) throw new FindMyiPhoneException('Expected $passcode to be 4 characters long');
  291. if(!is_string($text)) throw new FindMyiPhoneException('Expected $text to be a string');
  292. if(!is_bool($sound)) throw new FindMyiPhoneException('Expected $sound to be a bool');
  293. if(!is_string($title)) throw new FindMyiPhoneException('Expected $title to be a string');
  294.  
  295. $post_data = json_encode(array(
  296. 'clientContext' => $this->client_context,
  297. 'serverContext' => $this->server_context,
  298. 'device' => $device_id,
  299. 'emailUpdates' => $this->email_updates,
  300. 'passcode' => $passcode,
  301. 'sound' => $sound,
  302. 'text' => $text,
  303. 'title' => $title,
  304. 'userText' => true
  305. ));
  306.  
  307. return json_decode($this->make_request('lockAndMessage', $post_data))->content[0]->remoteLock;
  308. }
  309.  
  310.  
  311. /**
  312. * Remote Lock
  313. *
  314. */
  315. public function remote_lock($device_id, $passcode) {
  316. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  317. if(!is_string($passcode)) throw new FindMyiPhoneException('Expected $passcode to be a string');
  318. if(strlen($passcode) !== 4) throw new FindMyiPhoneException('Expected $passcode to be 4 characters long');
  319.  
  320. $post_data = json_encode(array(
  321. 'clientContext' => $this->client_context,
  322. 'serverContext' => $this->server_context,
  323. 'device' => $device_id,
  324. 'emailUpdates' => $this->email_updates,
  325. 'passcode' => $passcode
  326. ));
  327.  
  328. return json_decode($this->make_request('remoteLock', $post_data))->content[0]->remoteLock;
  329. }
  330.  
  331.  
  332. /**
  333. * Remote Wipe
  334. *
  335. */
  336. public function remote_wipe($device_id, $passcode, $text)
  337. {
  338. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  339. if(!is_string($passcode)) throw new FindMyiPhoneException('Expected $passcode to be a string');
  340. if(strlen($passcode) !== 4) throw new FindMyiPhoneException('Expected $passcode to be 4 characters long');
  341. if(!is_string($text)) throw new FindMyiPhoneException('Expected $text to be a string');
  342.  
  343. $post_data = json_encode(array(
  344. 'clientContext' => $this->client_context,
  345. 'serverContext' => $this->server_context,
  346. 'device' => $device_id,
  347. 'passcode' => $passcode,
  348. 'text' => $text,
  349. 'ownerNbr' => '444-4',
  350. 'authToken' => $this->get_auth_token($device_id),
  351. 'emailUpdates' => $this->email_updates
  352. ));
  353.  
  354. return json_decode($this->make_request('remoteWipeWithUserAuth', $post_data));
  355. }
  356.  
  357. /**
  358. * Get Wipe Auth Token
  359. *
  360. */
  361. public function get_auth_token($device_id)
  362. {
  363. if(!is_string($device_id)) throw new FindMyiPhoneException('Expected $device_id to be a string');
  364.  
  365. $post_data = json_encode(array(
  366. 'clientContext' => $this->client_context,
  367. 'serverContext' => $this->server_context,
  368. 'device' => $device_id,
  369. 'authToken' => $this->password
  370. ));
  371.  
  372. $json = json_decode($this->make_request('authForUserDevice', $post_data), true);
  373.  
  374. return $json['authToken'];
  375. }
  376.  
  377.  
  378. /**
  379. * Locate Device
  380. *
  381. */
  382. public function locate_device($device, $timeout = 120) {
  383. if(!is_integer($device)) throw new FindMyiPhoneException('Expected $device to be an integer');
  384. if(!isset($this->devices[$device])) $this->refresh_client();
  385.  
  386. $start = time();
  387. while (!$this->devices[$device]->location->locationFinished) {
  388. if ((time() - $start) > intval($timeout)) {
  389. throw new FindMyiPhoneException('Failed to locate device! Request timed out.');
  390. }
  391. sleep(5);
  392. $this->refresh_client();
  393. }
  394.  
  395. return $this->devices[$device]->location;
  396. }
  397.  
  398.  
  399. /**
  400. * Make request to the Find My iPhone server.
  401. * @param $method - the method
  402. * @param $post_data - the POST data
  403. * @param $return_headers - also return headers when true
  404. * @param $headers - optional headers to send
  405. * @return HTTP response
  406. */
  407. private function make_request($method, $post_data, $return_headers = false, $headers = array())
  408. {
  409. if(!is_string($method)) throw new FindMyiPhoneException('Expected $method to be a string');
  410. if(!$this->is_json($post_data)) throw new FindMyiPhoneException('Expected $post_data to be json');
  411. if(!is_array($headers)) throw new FindMyiPhoneException('Expected $headers to be an array');
  412. if(!is_bool($return_headers)) throw new FindMyiPhoneException('Expected $return_headers to be a bool');
  413.  
  414. array_push($headers, 'Origin: https://www.icloud.com');
  415. array_push($headers, 'Accept-Language: en-us');
  416. array_push($headers, 'Content-Type: application/json; charset=utf-8');
  417. array_push($headers, 'X-Apple-Realm-Support: 1.0');
  418. array_push($headers, 'X-Apple-Find-Api-Ver: 3.0');
  419. array_push($headers, 'X-Apple-Authscheme: UserIdGuest');
  420. array_push($headers, $this->cookies);
  421.  
  422. $curl = curl_init();
  423. curl_setopt($curl, CURLOPT_TIMEOUT, 9);
  424. curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
  425. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  426. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
  427. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
  428. curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  429. curl_setopt($curl, CURLOPT_AUTOREFERER, true);
  430. curl_setopt($curl, CURLOPT_VERBOSE, false);
  431. curl_setopt($curl, CURLOPT_POST, true);
  432. curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
  433. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  434. curl_setopt($curl, CURLOPT_HEADER, $return_headers);
  435. curl_setopt($curl, CURLOPT_URL, sprintf("%s/fmipservice/client/web/%s", $this->host, $method));
  436. curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0.2.1 Waterfox/49.0.2.1');
  437.  
  438. $http_result = curl_exec($curl);
  439.  
  440. if ( curl_errno($curl) )
  441. {
  442. echo 'cUrl error: ' . curl_error( $curl );
  443. }
  444.  
  445. curl_close($curl);
  446.  
  447. return $http_result;
  448. }
  449.  
  450.  
  451. /**
  452. * Parse cURL headers
  453. * @param $response - cURL response including the headers
  454. * @return array of headers
  455. */
  456. private function parse_curl_headers($response) {
  457. $headers = array();
  458. foreach (explode("\r\n", substr($response, 0, strpos($response, "\r\n\r\n"))) as $i => $line) {
  459. if ($i === 0) {
  460. $headers['http_code'] = $line;
  461. } else {
  462. list($key, $value) = explode(': ', $line);
  463. $headers[$key] = $value;
  464. }
  465. }
  466. return $headers;
  467. }
  468.  
  469. /**
  470. * Finds whether a variable is json.
  471. */
  472. private function is_json($var) {
  473. json_decode($var);
  474. return (json_last_error() == JSON_ERROR_NONE);
  475. }
  476.  
  477. public function request($url, $data)
  478. {
  479. $headers = array(
  480. 'Origin: https://www.icloud.com ',
  481. 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36 ',
  482. 'Content-Type: text/plain ',
  483. 'Accept: */*',
  484. 'Referer: https://www.icloud.com/ ',
  485. 'Accept-Encoding: gzip, deflate ',
  486. 'Accept-Language: en-US,en;q=0.8 ',
  487. );
  488.  
  489. $ch = curl_init($url);
  490. curl_setopt($ch ,CURLOPT_POST,true);
  491. curl_setopt($ch ,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_1_1);
  492. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  493. curl_setopt($ch, CURLOPT_POST, true);
  494. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  495. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  496. curl_setopt($ch, CURLOPT_ENCODING, 'UTF-8');
  497. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  498. curl_setopt($ch, CURLOPT_VERBOSE, true);
  499.  
  500. $result = curl_exec($ch);
  501.  
  502. $res = json_decode($result, true);
  503. if ( !isset($res['error']) )
  504. {
  505. curl_setopt($ch, CURLOPT_HEADER, true);
  506. $result_with_headers = curl_exec($ch);
  507. return array($result, $result_with_headers);
  508. }
  509.  
  510. return array($result, null);
  511. }
  512. }
  513.  
  514. class FindMyiPhoneException extends Exception {}
  515.  
  516. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement