PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/hosts/download/mega_co_nz.php

https://gitlab.com/dkiller1/rapidleech
PHP | 243 lines | 193 code | 34 blank | 16 comment | 69 complexity | 4606b5cae7f526af59f172e8c3dcd913 MD5 | raw file
  1. <?php
  2. if (!defined('RAPIDLEECH')) {
  3. require_once('index.html');
  4. exit;
  5. }
  6. // Using functions from: http://julien-marchand.fr/blog/using-the-mega-api-with-php-examples/
  7. class mega_co_nz extends DownloadClass {
  8. private $seqno;
  9. public function Download($link) {
  10. if (!extension_loaded('mcrypt') || !in_array('rijndael-128', mcrypt_list_algorithms(), true)) html_error("Mcrypt module isn't installed or it doesn't have support for the needed encryption.");
  11. $this->seqno = mt_rand();
  12. $this->changeMesg(lang(300).'<br />Mega.co.nz plugin by Th3-822'); // Please, do not remove or change this line contents. - Th3-822
  13. $fragment = parse_url($link, PHP_URL_FRAGMENT);
  14. if (preg_match('@^F!([^!]{8})!([\w\-\,]{22})(?:!([^!#]{8}))?(!less$)?@i', $fragment, $fid)) return $this->Folder($fid[1], $fid[2], (!empty($fid[3]) && $fid[3] != $fid[1] ? $fid[3] : 0), (empty($fid[4]) ? 1 : 0));
  15. if (!preg_match('@^(T8|N)?!([^!]{8})!([\w\-\,]{43})(?:(?:!|=###n=)([^!#]{8})(?:!|$))?@i', $fragment, $fid)) html_error('FileID or Key not found at link.');
  16. $reply = $this->apiReq(array('a' => 'g', 'g' => '1', (empty($fid[1]) ? 'p' : 'n') => $fid[2], 'ssl'=> '0'), (!empty($fid[1]) && !empty($fid[4]) ? $fid[4] : ''));
  17. $this->CheckErr($reply[0]);
  18. if (!empty($reply[0]['e'])) $this->CheckErr($reply[0]['e']);
  19. $key = $this->base64_to_a32($fid[3]);
  20. //$iv = array_merge(array_slice($key, 4, 2), array(0, 0));
  21. $key = array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]);
  22. $attr = $this->dec_attr($this->base64url_decode($reply[0]['at']), $key);
  23. if (empty($attr)) html_error((!empty($fid[1]) ? 'Folder Error: ' : '').'File\'s key isn\'t correct.');
  24. $this->RedirectDownload($reply[0]['g'], $attr['n'], 0, 0, $link, 0, 0, array('T8[fkey]' => $fid[3]));
  25. }
  26. private function CheckErr($code) {
  27. if (is_numeric($code)) {
  28. switch ($code) {
  29. default: $msg = '*No message for this error*';break;
  30. case -1: $msg = 'An internal error has occurred';break;
  31. case -2: $msg = 'You have passed invalid arguments to this command, your rapidleech is outdated?';break;
  32. case -3: $msg = 'A temporary congestion or server malfunction prevented your request from being processed';break;
  33. case -4: $msg = 'You have exceeded your command weight per time quota. Please wait a few seconds, then try again';break;
  34. case -9: $msg = 'File/Folder not found';break;
  35. case -11: $msg = 'Access violation';break;
  36. case -13: $msg = 'Trying to access an incomplete file';break;
  37. case -14: $msg = 'A decryption operation failed';break;
  38. case -16: $msg = 'File not available, uploader\'s account is banned';break;
  39. case -17: $msg = 'Request over quota';break;
  40. case -18: $msg = 'File temporarily not available, please try again later';break;
  41. // Confirmed at page:
  42. case -6: $msg = 'File not found, account was deleted';break;
  43. }
  44. html_error("[Error: $code] $msg.");
  45. }
  46. }
  47. private function apiReq($atrr, $node = '') {
  48. $try = 0;
  49. do {
  50. if ($try > 0) sleep(2);
  51. $ret = $this->doApiReq($atrr, $node);
  52. $try++;
  53. } while ($try < 6 && $ret[0] == -3);
  54. return $ret;
  55. }
  56. private function doApiReq($atrr, $node='') {
  57. if (!function_exists('json_encode')) html_error('Error: Please enable JSON in php.');
  58. $page = $this->GetPage('https://g.api.mega.co.nz/cs?id=' . ($this->seqno++) . (!empty($node) ? "&n=$node" : ''), 0, json_encode(array($atrr)), "https://mega.co.nz/\r\nContent-Type: application/json");
  59. list ($header, $page) = array_map('trim', explode("\r\n\r\n", $page, 2));
  60. if (is_numeric($page)) return array((int)$page);
  61. if (in_array((int)substr($header, 9, 3), array(500, 503))) return array(-3); // 500 Server Too Busy
  62. return $this->Get_Reply($page);
  63. }
  64. private function Get_Reply($content) {
  65. if (!function_exists('json_decode')) html_error('Error: Please enable JSON in php.');
  66. if (($pos = strpos($content, "\r\n\r\n")) > 0) $content = substr($content, $pos + 4);
  67. $cb_pos = strpos($content, '{');
  68. $sb_pos = strpos($content, '[');
  69. if ($cb_pos === false && $sb_pos === false) html_error('Json start braces not found.');
  70. $sb = ($cb_pos === false || $sb_pos < $cb_pos) ? true : false;
  71. $content = substr($content, strpos($content, ($sb ? '[' : '{')));$content = substr($content, 0, strrpos($content, ($sb ? ']' : '}')) + 1);
  72. if (empty($content)) html_error('No json content.');
  73. $rply = json_decode($content, true);
  74. if (!$rply || count($rply) == 0) html_error('Error reading json.');
  75. return $rply;
  76. }
  77. private function str_to_a32($b) {
  78. // Add padding, we need a string with a length multiple of 4
  79. $b = str_pad($b, 4 * ceil(strlen($b) / 4), "\0");
  80. return array_values(unpack('N*', $b));
  81. }
  82. private function a32_to_str($hex) {
  83. return call_user_func_array('pack', array_merge(array('N*'), $hex));
  84. }
  85. private function base64url_encode($data) {
  86. return strtr(rtrim(base64_encode($data), '='), '+/', '-_');
  87. }
  88. private function a32_to_base64($a) {
  89. return $this->base64url_encode($this->a32_to_str($a));
  90. }
  91. private function base64url_decode($data) {
  92. if (($s = (2 - strlen($data) * 3) % 4) < 2) $data .= substr(',,', $s);
  93. return base64_decode(strtr($data, '-_,', '+/='));
  94. }
  95. private function base64_to_a32($s) {
  96. return $this->str_to_a32($this->base64url_decode($s));
  97. }
  98. private function aes_cbc_decrypt($data, $key) {
  99. return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
  100. }
  101. private function aes_cbc_decrypt_a32($data, $key) {
  102. return $this->str_to_a32($this->aes_cbc_decrypt($this->a32_to_str($data), $this->a32_to_str($key)));
  103. }
  104. private function decrypt_key($a, $key) {
  105. $x = array();
  106. for ($i = 0; $i < count($a); $i += 4) $x = array_merge($x, $this->aes_cbc_decrypt_a32(array_slice($a, $i, 4), $key));
  107. return $x;
  108. }
  109. private function dec_attr($attr, $key) {
  110. $attr = trim($this->aes_cbc_decrypt($attr, $this->a32_to_str($key)));
  111. if (substr($attr, 0, 6) != 'MEGA{"') return false;
  112. $attr = substr($attr, 4);$attr = substr($attr, 0, strrpos($attr, '}') + 1);
  113. return $this->Get_Reply($attr);
  114. }
  115. public function CheckBack($header) {
  116. $statuscode = intval(substr($header, 9, 3));
  117. if ($statuscode != 200) switch ($statuscode) {
  118. case 509: html_error('[Mega_co_nz] Transfer quota exeeded.');
  119. case 503: html_error('[Mega_co_nz] Too many connections for this download.');
  120. case 403: html_error('[Mega_co_nz] Link used/expired.');
  121. case 404: html_error('[Mega_co_nz] Link expired.');
  122. default : html_error('[Mega_co_nz][HTTP] '.trim(substr($header, 9, strpos($header, "\n") - 8)));
  123. }
  124. global $fp, $sFilters;
  125. if (empty($fp) || !is_resource($fp)) html_error("Error: Your rapidleech version is outdated and it doesn't support this plugin.");
  126. if (!empty($_GET['T8']['fkey'])) $key = $this->base64_to_a32(urldecode($_GET['T8']['fkey']));
  127. elseif (preg_match('@^(T8|N)?!([^!]{8})!([\w\-\,]{43})@i', parse_url($_GET['referer'], PHP_URL_FRAGMENT), $dat)) $key = $this->base64_to_a32($dat[2]);
  128. else html_error("[CB] File's key not found.");
  129. $iv = array_merge(array_slice($key, 4, 2), array(0, 0));
  130. $key = array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]);
  131. $opts = array('iv' => $this->a32_to_str($iv), 'key' => $this->a32_to_str($key), 'mode' => 'ctr');
  132. if (!stream_filter_register('MegaDlDecrypt', 'Th3822_MegaDlDecrypt') && !in_array('MegaDlDecrypt', stream_get_filters())) html_error('Error: Cannot register "MegaDlDecrypt" filter.');
  133. if (!isset($sFilters) || !is_array($sFilters)) $sFilters = array();
  134. if (empty($sFilters['MegaDlDecrypt'])) $sFilters['MegaDlDecrypt'] = stream_filter_prepend($fp, 'MegaDlDecrypt', STREAM_FILTER_READ, $opts);
  135. if (!$sFilters['MegaDlDecrypt']) html_error('Error: Unknown error while initializing MegaDlDecrypt filter, cannot continue download.');
  136. }
  137. private function FSort($a, $b) {
  138. $a = $a['n'];$b = $b['n'];
  139. return strcmp($a, $b); // Case Sensitive Sort
  140. //return strcasecmp($a, $b); // Case Insensitive Sort
  141. //return strnatcmp($a, $b); // Case Sensitive "Natural" Sort
  142. //return strnatcasecmp($a, $b); // Case Insensitive "Natural" Sort
  143. }
  144. private function Folder($fnid, $fnk, $sfolder, $recursive) {
  145. $files = $this->apiReq(array('a' => 'f', 'c' => 1, 'r' => (!empty($sfolder) || $recursive ? 1 : 0)), $fnid);
  146. if (is_numeric($files[0])) $this->CheckErr($files[0], 'Cannot get folder contents');
  147. $sfolder = (!empty($sfolder) ? array($sfolder => 1) : array());
  148. foreach ($files[0]['f'] as $file) switch ($file['t']) {
  149. case 0: // File
  150. if (!empty($sfolder) && empty($sfolder[$file['p']])) break;
  151. $keys = array();
  152. foreach (explode('/', $file['k']) as $key) if (strpos($key, ':') !== false && $key = explode(':', $key, 2)) $keys[$key[0]] = $key[1];
  153. if (empty($keys)) {
  154. $key = $this->base64_to_a32($fnk);
  155. $attr = $this->dec_attr($this->base64url_decode($file['a']), array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]));
  156. if (!empty($attr)) textarea($attr);
  157. break;
  158. }
  159. $key = $this->decrypt_key($this->base64_to_a32(reset($keys)), $this->base64_to_a32($fnk));
  160. if (empty($key)) break;
  161. $attr = $this->dec_attr($this->base64url_decode($file['a']), array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]));
  162. if (!empty($attr)) $dfiles[$file['h']] = array('k' => $this->a32_to_base64($key), 'n' => $attr['n'], 'p' => $file['p']);
  163. if (!empty($attr)) $_names[] = $attr['n'];
  164. break;
  165. case 1: // Folder
  166. if (!empty($sfolder) && $recursive && !empty($sfolder[$file['p']])) $sfolder[$file['h']] = 1;
  167. break;
  168. }
  169. if (empty($dfiles)) html_error('Error while decoding folder: Empty'.(!empty($sfolder) ? ' or Inexistent Sub-' : ' ').'Folder? [Subfolders: '.(!empty($sfolder) || $recursive ? 'Yes' : 'No').']');
  170. uasort($dfiles, array($this, 'FSort'));
  171. $files = array();
  172. foreach ($dfiles as $file => $key) $files[] = "https://mega.co.nz/#N!$file!{$key['k']}!$fnid!Rapidleech";
  173. $this->moveToAutoDownloader($files);
  174. }
  175. }
  176. class Th3822_MegaDlDecrypt extends php_user_filter {
  177. private $_td, $_data, $_dlen, $_clen, $bucket;
  178. public function onCreate() {
  179. if (empty($this->params['iv']) || empty($this->params['key']) || empty($this->params['mode'])) return false;
  180. $this->_td = mcrypt_module_open('rijndael-128', '', $this->params['mode'], '');
  181. $init = mcrypt_generic_init($this->_td, $this->params['key'], $this->params['iv']);
  182. if ($init === false || $init < 0) return false;
  183. return true;
  184. }
  185. public function filter($in, $out, &$consumed, $stop) {
  186. while ($bucket = stream_bucket_make_writeable($in)) {
  187. if ($bucket->datalen == 0) continue;
  188. $this->bucket = $bucket;
  189. $this->bucket->data = mdecrypt_generic($this->_td, $this->bucket->data);
  190. $consumed += $bucket->datalen;
  191. stream_bucket_append($out, $this->bucket);
  192. }
  193. return PSFS_PASS_ON;
  194. }
  195. public function onClose() {
  196. mcrypt_generic_deinit($this->_td);
  197. mcrypt_module_close($this->_td);
  198. }
  199. }
  200. //[24-2-2013] Written by Th3-822. (Rapidleech r415 or newer required)
  201. //[02-3-2013] Added "checks" for validating rapidleech version & added 2 error msg. - Th3-822
  202. //[27-3-2013] Simplified Stream decrypt function (The other one was not working well... After many tests looks like it's better now :D). - Th3-822
  203. //[20-7-2013] Fixed link regexp. - Th3-822
  204. //[09-8-2013] Added folder support and small fixes from upload plugin. (Download links that are fetched from a folder link are not public and only can be downloaded with this plugin.) - Th3-822
  205. //[30-1-2014] Fixed download from folders. - Th3-822
  206. //[09-2-2014] Fixed issues at link parsing. - Th3-822
  207. //[29-1-2015] Replaced 'T8' prefix at folder->file links for support on third-party downloaders using links with 'N' as prefix. - Th3-822
  208. //[04-2-2016] Added sub-folders support (fully) and added support for link suffix "!less" to disable recursive sub-folder download. - Th3-822
  209. ?>