mega.class.php
seqno = 0;
$this->f = $this->mega_get_file_info($file_hash);
}
function a32_to_str($hex)
{
return call_user_func_array('pack', array_merge(array('N*'), $hex));
}
function aes_ctr_decrypt($data, $key, $iv)
{
return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, 'ctr', $iv);
}
function base64_to_a32($s)
{
return $this->str_to_a32($this->base64urldecode($s));
}
function base64urldecode($data)
{
$data .= substr('==', (2 - strlen($data) * 3) % 4);
$data = str_replace(array('-', '_', ','), array('+', '/', ''), $data);
return base64_decode($data);
}
function str_to_a32($b)
{
// Add padding, we need a string with a length multiple of 4
$b = str_pad($b, 4 * ceil(strlen($b) / 4), "\0");
return array_values(unpack('N*', $b));
}
/**
* Handles query to mega servers
* @param array $req data to be sent to mega
* @return type
*/
function mega_api_req($req)
{
$ch = curl_init('https://g.api.mega.co.nz/cs?id=' . ($this->seqno++)/* . ($sid ? '&sid=' . $sid : '') */);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
//curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array($req)));
$resp = curl_exec($ch);
curl_close($ch);
$resp = json_decode($resp, true);
return $resp[0];
}
function aes_cbc_decrypt($data, $key)
{
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");
}
function mega_dec_attr($attr, $key)
{
$attr = trim($this->aes_cbc_decrypt($attr, $this->a32_to_str($key)));
if (substr($attr, 0, 6) != 'MEGA{"') {
return false;
}
return json_decode(substr($attr, 4), true);
}
/**
* Downloads file from megaupload
* @param string $as_attachment Download file as attachment, default true
* @param string $local_path Save file to specified by $local_path folder
* @return boolean True
*/
function download($as_attachment = true, $local_path = null)
{
$ch = curl_init($this->f['binary_url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_VERBOSE, true);
$data_enc = curl_exec($ch);
curl_close($ch);
$data = $this->aes_ctr_decrypt($data_enc, $this->a32_to_str($this->f['k']), $this->a32_to_str($this->f['iv']));
if ($as_attachment) {
//die(var_dump($this->f['attr']['n']));
header("Content-Disposition: attachment;filename=\"{$this->f['attr']['n']}\"");
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $this->f['size']);
header('Pragma: no-cache');
header('Expires: 0');
print $data;
return true;
} else {
file_put_contents($local_path . DIRECTORY_SEPARATOR . $this->f['attr']['n'], $data);
return true;
}
/* $file_mac = cbc_mac($data, $k, $iv);
print "\nchecking mac\n";
if (array($file_mac[0] ^ $file_mac[1], $file_mac[2] ^ $file_mac[3]) != $meta_mac) {
echo "MAC mismatch";
} */
}
function get_chunks($size)
{
$chunks = array();
$p = $pp = 0;
for ($i = 1; $i <= 8 && $p < $size - $i * 0x20000; $i++) {
$chunks[$p] = $i * 0x20000;
$pp = $p;
$p += $chunks[$p];
}
while ($p < $size) {
$chunks[$p] = 0x100000;
$pp = $p;
$p += $chunks[$p];
}
$chunks[$pp] = ($size - $pp);
if (!$chunks[$pp]) {
unset($chunks[$pp]);
}
return $chunks;
}
/**
* Downloads file from megaupload as a stream (useful if you want to implement megaupload proxy)
* @param string $as_attachment Download file as attachment, default true
* @param string $local_path Save file to specified by $local_path folder
* @return boolean True
*/
function stream_download($as_attachment = true, $local_path = null)
{
if ($as_attachment) {
header("Content-Disposition: attachment;filename=\"{$this->f['attr']['n']}\"");
header('Content-Description: File Transfer');
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $this->f['size']);
header('Pragma: no-cache');
header('Expires: 0');
} else {
$destfile = fopen($local_path . DIRECTORY_SEPARATOR . $this->f['attr']['n'], 'wb');
}
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', 'ctr', '');
mcrypt_generic_init($cipher, $this->a32_to_str($this->f['k']), $this->a32_to_str($this->f['iv']));
$chunks = $this->get_chunks($this->f['size']);
$protocol = parse_url($this->f['binary_url'], PHP_URL_SCHEME);
$opts = array(
$protocol => array(
'method' => 'GET'
)
);
$context = stream_context_create($opts);
$stream = fopen($this->f['binary_url'], 'rb', false, $context);
$info = stream_get_meta_data($stream);
$end = !$info['eof'];
foreach ($chunks as $length) {
$bytes = strlen($buffer);
while ($bytes < $length && $end) {
$data = fread($stream, min(1024, $length - $bytes));
$buffer .= $data;
$bytes = strlen($buffer);
$info = stream_get_meta_data($stream);
$end = !$info['eof'] && $data;
}
$chunk = substr($buffer, 0, $length);
$buffer = $bytes > $length ? substr($buffer, $length) : '';
$chunk = mdecrypt_generic($cipher, $chunk);
if ($as_attachment) {
print $chunk;
ob_flush();
ob_clean();
} else
fwrite($destfile, $chunk);
}
// Terminate decryption handle and close module
mcrypt_generic_deinit($cipher);
mcrypt_module_close($cipher);
fclose($stream);
if (!$as_attachment)
fclose($destfile);
return true;
/* $file_mac = cbc_mac($data, $k, $iv);
print "\nchecking mac\n";
if (array($file_mac[0] ^ $file_mac[1], $file_mac[2] ^ $file_mac[3]) != $meta_mac) {
echo "MAC mismatch";
} */
}
private function mega_get_file_info($hash)
{
preg_match('/\!(.*?)\!(.*)/', $hash, $matches);
$id = $matches[1];
$key = $matches[2];
$key = $this->base64_to_a32($key);
$k = array($key[0] ^ $key[4], $key[1] ^ $key[5], $key[2] ^ $key[6], $key[3] ^ $key[7]);
$iv = array_merge(array_slice($key, 4, 2), array(0, 0));
$meta_mac = array_slice($key, 6, 2);
$info = $this->mega_api_req(array('a' => 'g', 'g' => 1, 'p' => $id));
if (!$info['g']) die('No such file on mega. Maybe it was deleted.');
return array('id' => $id, 'key' => $key, 'k' => $k, 'iv' => $iv, 'meta_mac' => $meta_mac, 'binary_url' => $info['g'], 'attr' => $this->mega_dec_attr($this->base64urldecode($info['at']), $k), 'size' => $info['s']);
}
/**
* Returns file information
* @return array File information
*/
public function file_info()
{
return $this->f;
}
}
?>