<?php
define('H', dirname(__FILE__)."/");
define('SERVER_ID', 'void');
define('SERVER_PASSWORD', 'void');
define('BLOCK_SIZE', 4 * 1024);
define('GET_NODES_STATE', 0x00);
define('FWUPDATE_STATE', 0x01);
define('CLOSE_STATE', 0x02);
define('LOAD_ENGINE_STATE', 0x03);
define('LOAD_DELTA_STATE', 0x04);
define('CONFIRM_FWUPDATE_STATE', 0x05);
define('ERROR_STATE', 0x06);
define('START_FWUPDATE_STATE', 0x07);
require_once 'wbxml.php';
$empty_document = '<!DOCTYPE SyncML PUBLIC "-//SYNCML//DTD SyncML 1.1//EN" "http://www.syncml.org/docs/syncml_represent_v11_20020213.dtd">'
.'<SyncML xmlns="SYNCML:SYNCML1.1"></SyncML>';
$wbxml = file_get_contents("php://input");
if (empty($wbxml))
die("Empty request!!!");
$errors = array();
$nodes = array('./Diag/Stat/Gsm', './FwUpdate/DeltaFile/State', './FwUpdate/PatchEngine/State');
$xml = new Wbxml(H.'tmp');
$response = new SimpleXMLElement($empty_document);
$headers = array();
$decoded_xml = $xml -> decode($wbxml, Wbxml::SYNCML11);
$msg_id = 1;
$cmd_id = 1;
$decoded_xml = preg_replace_callback("/<Format xmlns\=\"syncml:metinf\">bin<\/Format>\s*<Type xmlns\=\"syncml:metinf\">text\/plain<\/Type>\s*<\/Meta>\s*\<Data\>([^<>]+)\<\/Data\>/", function ($m) {
$out = bin2hex(xml_entities_decode($m[1]));
return '<Format xmlns="syncml:metinf">bin</Format>
<Type xmlns="syncml:metinf">text/plain</Type>
</Meta><Data>'.$out.'</Data>';
}, $decoded_xml);
if (empty($decoded_xml))
die('Invalid XML!');
$request = simplexml_load_string($decoded_xml);
$sid = md5($request -> SyncHdr -> SessionID.$_SERVER['REMOTE_ADDR']);
session_id($sid);
session_start();
if (isset($_SESSION['msg_id'])) {
$msg_id = ++$_SESSION['msg_id'];
} else {
$_SESSION['replaced'] = array();
$_SESSION['msg_id'] = $msg_id;
$_SESSION['state'] = GET_NODES_STATE;
$_SESSION['nonce'] = substr(md5(uniqid('', TRUE)), rand(0, 24), 16);
unlink(H."tmp/log_fw.txt");
}
$log = fopen(H."tmp/log_fw.txt", "a+");
$request_body = $_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI'].' '.$_SERVER['SERVER_PROTOCOL']."\n";
foreach (apache_request_headers() as $header => $value)
$request_body .= $header.": ".$value."\n";
$request_body .= "\n".$decoded_xml;
fwrite($log, "\n>>> [".date("H:i:s d/m/Y", time())."] >>>\n");
fwrite($log, "Session id: ".$sid."\n");
fwrite($log, "Message id: ".$msg_id."\n\n");
fwrite($log, $request_body);
$client = $request -> SyncHdr -> Source -> LocURI;
$server = $request -> SyncHdr -> Target -> LocURI;
/* Заголовок */
$sync_header = $response -> addChild('SyncHdr');
$sync_header -> addChild('VerDTD', $request -> SyncHdr -> VerDTD);
$sync_header -> addChild('VerProto', $request -> SyncHdr -> VerProto);
$sync_header -> addChild('SessionID', $request -> SyncHdr -> SessionID);
$sync_header -> addChild('MsgID', $msg_id);
$sync_header -> addChild('Target') -> addChild('LocURI', $client);
$sync_header -> addChild('Source') -> addChild('LocURI', $server);
/* Тело */
$sync_body = $response -> addChild('SyncBody');
$status = $sync_body -> addChild('Status');
$status -> addChild('CmdID', ++$cmd_id);
$status -> addChild('MsgRef', $msg_id);
$status -> addChild('CmdRef', 0);
$status -> addChild('Cmd', 'SyncHdr');
$status -> addChild('TargetRef', $server);
$status -> addChild('SourceRef', $client);
$status -> addChild('Data', 200);
if (isset($request -> SyncHdr -> Cred)) {
switch ($request -> SyncHdr -> Cred -> Meta -> Type) {
case "syncml:auth-basic":
$user = explode(':', base64_decode($request -> SyncHdr -> Cred -> Data));
fwrite($log, "Auth type: ".$request -> SyncHdr -> Cred -> Meta -> Type."\n");
fwrite($log, "User: ".$user[0]."\n");
fwrite($log, "Password: ".$user[1]."\n");
$response -> SyncBody -> Status -> Data = 212;
break;
case "syncml:auth-md5":
fwrite($log, "Auth type: ".$request -> SyncHdr -> Cred -> Meta -> Type."\n");
fwrite($log, "Digest: ".$request -> SyncHdr -> Cred -> Data."\n");
$response -> SyncBody -> Status -> Data = 212;
break;
default:
fwrite($log, "Unknown auth type: ".$request -> SyncHdr -> Cred -> Meta -> Type."\n");
$response -> SyncBody -> Status -> Data = 200;
break;
}
}
$logn = fopen(H."tmp/nodes_fw.txt", "a+");
$is_result = false;
foreach ($request -> SyncBody -> children() as $item) {
switch ($item -> getName()) {
case "Alert":
$status = $response -> SyncBody -> addChild('Status');
$status -> addChild('CmdID', ++$cmd_id);
$status -> addChild('MsgRef', $msg_id);
$status -> addChild('CmdRef', $item -> CmdID);
$status -> addChild('Cmd', $item -> getName());
$status -> addChild('TargetRef', $server);
$status -> addChild('SourceRef', $client);
$status -> addChild('Data', 200);
break;
case "Replace":
$status = $response -> SyncBody -> addChild('Status');
$status -> addChild('CmdID', ++$cmd_id);
$status -> addChild('MsgRef', $msg_id);
$status -> addChild('CmdRef', $item -> CmdID);
$status -> addChild('Cmd', $item -> getName());
$status -> addChild('TargetRef', $server);
$status -> addChild('SourceRef', $client);
$status -> addChild('Data', 200);
break;
case "Status":
if ($item -> Cmd == 'Replace') {
if ($item -> Data != 200 && $item -> Data != 213) {
$errors[] = array(
'cmd' => $item -> Cmd,
'code' => $item -> Data,
'target' => $item -> TargetRef,
'msgref' => $item -> MsgRef,
'cmdref' => $item -> CmdRef,
);
$_SESSION['state'] = ERROR_STATE;
break;
}
}
if ($item -> Cmd == 'Alert' && isset($_SESSION['confirm_'.$item -> CmdRef])) {
if ($item -> Data == 200) {
$_SESSION['state'] = $_SESSION['confirm_'.$item -> CmdRef]['ok'];
} else {
$_SESSION['state'] = $_SESSION['confirm_'.$item -> CmdRef]['cancel'];
}
unset($_SESSION['confirm_'.$item -> CmdRef]);
} elseif ($item -> Data != 200 && $item -> Cmd == "Get" && isset($item -> TargetRef))
fwrite($logn, $item -> TargetRef.": Error #".$item -> Data."\n");
break;
case "Results":
unset($nodes[array_search($item -> Item -> Source -> LocURI, $nodes)]);
switch ($item -> Item -> Meta -> Format) {
case "node":
if (isset($item -> Item -> Data)) {
$nodes_list = explode('/', $item -> Item -> Data);
foreach ($nodes_list as $node)
if (!empty($node))
$nodes[] = $item -> Item -> Source -> LocURI.'/'.$node;
}
break;
default:
fwrite($logn, "".$item -> Item -> Source -> LocURI." [".$item -> Item -> Meta -> Format."]: ".(isset($item -> Item -> Data) ? $item -> Item -> Data : '')."\n");
break;
}
break;
}
}
if ($_SESSION['state'] == GET_NODES_STATE && empty($nodes))
$_SESSION['state'] = CONFIRM_FWUPDATE_STATE;
SELECT_STATES: switch ($_SESSION['state']) {
case GET_NODES_STATE:
$get_node_values = $response -> SyncBody -> addChild('Get');
$get_node_values -> addChild('CmdID', ++$cmd_id);
foreach ($nodes as $node) {
$item = $get_node_values -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', $node);
}
break;
case CONFIRM_FWUPDATE_STATE:
$size_pe = round(filesize("PatchEngine") / 1024);
$size_df = round(filesize("DeltaFile") / 1024);
confirm (
"Обновить ПО? Будет скачно ".($size_pe + $size_df)." Кб.\n\n".
"PatchEngine: ".$size_pe." Кб\n".
"DeltaFile: ".$size_df." Кб\n\n".
"Внимание! Это с вероятностью 99% убьет Ваш телефон!",
START_FWUPDATE_STATE,
CLOSE_STATE
);
break;
case START_FWUPDATE_STATE:
/* PatchEngine */
if (!in_array('./FwUpdate/PatchEngine/PkgName', $_SESSION['replaced'])) {
$pkg_name = 'PatchEngine';
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'chr') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/PatchEngine/PkgName');
$item -> addChild('Data', $pkg_name);
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($pkg_name)) -> addAttribute('xmlns', 'syncml:metinf');
$_SESSION['replaced'][] = './FwUpdate/PatchEngine/PkgName';
}
if (!in_array('./FwUpdate/PatchEngine/PkgVersion', $_SESSION['replaced'])) {
$pkg_version = '1.4.1';
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'chr') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/PatchEngine/PkgVersion');
$item -> addChild('Data', $pkg_version);
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($pkg_version)) -> addAttribute('xmlns', 'syncml:metinf');
$_SESSION['replaced'][] = './FwUpdate/PatchEngine/PkgVersion';
}
/* DeltaFile */
if (!in_array('./FwUpdate/DeltaFile/PkgName', $_SESSION['replaced'])) {
$pkg_name = 'DeltaFile';
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'chr') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/DeltaFile/PkgName');
$item -> addChild('Data', $pkg_name);
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($pkg_name)) -> addAttribute('xmlns', 'syncml:metinf');
$_SESSION['replaced'][] = './FwUpdate/DeltaFile/PkgName';
}
if (!in_array('./FwUpdate/DeltaFile/PkgVersion', $_SESSION['replaced'])) {
$pkg_version = '31';
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'chr') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/DeltaFile/PkgVersion');
$item -> addChild('Data', $pkg_version);
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($pkg_version)) -> addAttribute('xmlns', 'syncml:metinf');
$_SESSION['replaced'][] = './FwUpdate/DeltaFile/PkgVersion';
}
$_SESSION['state'] = LOAD_ENGINE_STATE;
break;
case LOAD_ENGINE_STATE:
/* PatchEngine */
$is_end_fwupdate = true;
$data = file_get_contents("PatchEngine");
if (!in_array('./FwUpdate/PatchEngine/Update/PkgData', $_SESSION['replaced'])) {
if (!isset($_SESSION['start_PatchEngine']))
$_SESSION['start_PatchEngine'] = 0;
$buff = substr($data, $_SESSION['start_PatchEngine'], BLOCK_SIZE + 1);
$is_end_fwupdate = strlen($buff) > BLOCK_SIZE ? false : true;
$buff = substr($buff, 0, BLOCK_SIZE);
if (!empty($buff)) {
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'bin') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
if (!$_SESSION['start_PatchEngine'] > 0) {
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($data)) -> addAttribute('xmlns', 'syncml:metinf');
}
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/PatchEngine/Update/PkgData');
$item -> addChild('Data', '<![BINARY['.base64_encode($buff).']]>');
}
$_SESSION['start_PatchEngine'] += strlen($buff);
if (!$is_end_fwupdate) {
$item -> addChild('MoreData');
} else
$_SESSION['replaced'][] = './FwUpdate/PatchEngine/Update/PkgData';
fwrite($log, "\n".strlen($buff)." / ".strlen($data)." is_end_fwupdate = ".($is_end_fwupdate ? "TRUE" : "FALSE")."\n");
}
if ($is_end_fwupdate) {
// display("PatchEngine успешно скачан! Сейчас начнется скачка DeltaFile...");
$_SESSION['state'] = LOAD_DELTA_STATE;
goto SELECT_STATES;
}
break;
case LOAD_DELTA_STATE:
/* DeltaFile */
$is_end_fwupdate = true;
$data = file_get_contents("DeltaFile");
if (!in_array('./FwUpdate/DeltaFile/Update/PkgData', $_SESSION['replaced'])) {
if (!isset($_SESSION['start_DeltaFile']))
$_SESSION['start_DeltaFile'] = 0;
$buff = substr($data, $_SESSION['start_DeltaFile'], BLOCK_SIZE + 1);
$is_end_fwupdate = strlen($buff) > BLOCK_SIZE ? false : true;
$buff = substr($buff, 0, BLOCK_SIZE);
if (!empty($buff)) {
$replace = $response -> SyncBody -> addChild('Replace');
$replace -> addChild('CmdID', ++$cmd_id);
$meta = $replace -> addChild('Meta');
$meta -> addChild('Format', 'bin') -> addAttribute('xmlns', 'syncml:metinf');
$meta -> addChild('Type', 'text/plain') -> addAttribute('xmlns', 'syncml:metinf');
$item = $replace -> addChild('Item');
$meta = $item -> addChild('Meta');
$meta -> addChild('Size', strlen($data)) -> addAttribute('xmlns', 'syncml:metinf');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate/DeltaFile/Update/PkgData');
$item -> addChild('Data', '<![BINARY['.base64_encode($buff).']]>');
}
if (!$is_end_fwupdate) {
$item -> addChild('MoreData');
$_SESSION['start_DeltaFile'] += strlen($buff);
} else
$_SESSION['replaced'][] = './FwUpdate/DeltaFile/Update/PkgData';
fwrite($log, "\n".$_SESSION['start_DeltaFile']." / ".strlen($data)." is_end_fwupdate = ".($is_end_fwupdate ? "TRUE" : "FALSE")."\n");
}
if ($is_end_fwupdate) {
display("Для начала обновления перезагрузить телефон!");
$replace = $response -> SyncBody -> addChild('Exec');
$replace -> addChild('CmdID', ++$cmd_id);
$item = $replace -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', './FwUpdate');
$_SESSION['state'] = CLOSE_STATE;
}
break;
case ERROR_STATE:
$errmsg = "Во время сессии произошли ошибки!\n";
$errmsg .= "Попробуйте удалить 0:\\System\\FwUpdate\n\n";
foreach ($errors as $error) {
$errmsg .= "Command: ".$error['cmd']."\n";
$errmsg .= "Code: ".$error['code']."\n";
$errmsg .= "Target: ".$error['target']."\n";
$errmsg .= "---\n";
}
display($errmsg);
$_SESSION['state'] = CLOSE_STATE;
break;
case CLOSE_STATE:
break;
}
fwrite($log, "\nSTATE: ".$_SESSION['state']."\n");
if (!isset($request -> SyncBody -> Final) && (!isset($is_end_fwupdate) || !$is_end_fwupdate)) {
$alert = $sync_body -> addChild('Alert');
$alert -> addChild('CmdID', ++$cmd_id);
$alert -> addChild('Data', 222);
$item = $alert -> addChild('Item');
$item -> addChild('Target') -> addChild('LocURI', $server);
$item -> addChild('Source') -> addChild('LocURI', $client);
} else
$response -> SyncBody -> addChild('Final');
$dom = dom_import_simplexml($response) -> ownerDocument;
$dom -> formatOutput = true;
$out_data = $dom -> saveXML();
$wbxml = $xml -> encode($out_data, Wbxml::WBXML11);
/* Костыль для передачи бинарных данных =) */
preg_match_all("/<\!\[BINARY\[([^<>]+)\]\]>/s", $wbxml, $match);
for ($i = 0; $i < count($match[1]); $i++) {
$len = strlen($match[0][$i]);
$match[1][$i] = base64_decode($match[1][$i]);
$wbxml = str_replace(
get_int($len).$match[0][$i],
get_int(strlen($match[1][$i])).$match[1][$i],
$wbxml
);
}
fwrite($log, "\n<<< [".date("H:i:s d/m/Y", time())."] <<<\n");
if (isset($request -> SyncBody -> Status -> Chal)) {
switch ($request -> SyncBody -> Status -> Chal -> Meta -> Type) {
case "syncml:auth-MAC":
$nonce = base64_decode($request -> SyncBody -> Status -> Chal -> Meta -> NextNonce);
$MAC = get_hmac($wbxml, SERVER_ID, SERVER_PASSWORD, $nonce);
$headers[] = "X-syncml-hmac: algorithm=MD5, username=\"".SERVER_ID."\", mac=".$MAC;
fwrite($log, "Auth type: ".$request -> SyncBody -> Status -> Chal -> Meta -> Type."\n");
fwrite($log, "Nonce: ".$request -> SyncBody -> Status -> Chal -> Meta -> NextNonce."\n");
fwrite($log, "HMAC: ".$MAC."\n");
break;
default:
fwrite($log, "Unknown auth type: ".$request -> SyncHdr -> Cred -> Meta -> Type."\n");
break;
}
}
$headers[] = "Content-type: application/vnd.syncml.dm+wbxml";
$headers[] = "Content-length: ".strlen($wbxml)."";
$headers[] = "Accept: application/vnd.syncml.dm+wbxml, application/vnd.syncml.dm+xml";
$response_body = "\n";
$response_body .= implode("\n", $headers);
$response_body .= "\n\n";
$response_body .= $xml -> decode($wbxml, Wbxml::SYNCML11);
fwrite($log, $response_body);
fclose($log);
foreach ($headers as $header)
header($header);
echo $wbxml;
function display($message, $delay = 15) {
global $cmd_id, $response;
$alert = $response -> SyncBody -> addChild('Alert');
$alert -> addChild('CmdID', ++$cmd_id);
$alert -> addChild('Data', 1100);
$item = $alert -> addChild('Item');
$item -> addChild('Data', 'MINDT='.intval($delay));
$item = $alert -> addChild('Item');
$item -> addChild('Data', $message);
return $alert -> CmdID;
}
function confirm($message, $ok_action, $cancel_action, $delay = 15) {
global $cmd_id, $response;
$alert = $response -> SyncBody -> addChild('Alert');
$alert -> addChild('CmdID', ++$cmd_id);
$alert -> addChild('Data', 1101);
$item = $alert -> addChild('Item');
$item -> addChild('Data', 'MINDT='.intval($delay));
$item = $alert -> addChild('Item');
$item -> addChild('Data', $message);
$_SESSION['confirm_'.$alert -> CmdID] = array(
'ok' => $ok_action,
'cancel' => $cancel_action
);
return $alert -> CmdID;
}
function get_hmac($msg, $username, $password, $nonce) {
$cred = base64_encode(pack('H*', md5($username.":".$password)));
return get_hmac_cred($msg, $cred, $nonce);
}
function get_hmac_cred($msg, $credential, $nonce) {
return base64_encode(pack('H*', md5($credential.':'.$nonce.':'.base64_encode(pack('H*', md5($msg))))));
}
function logger($file, $data, $append = true) {
$fp = fopen(H."tmp/".$file, $append ? "a+" : "w+");
flock($fp, LOCK_EX);
fwrite($fp, $data);
flock($fp, LOCK_UN);
fclose($fp);
}
function xml_entities_decode($data) {
return preg_replace_callback(
"/\&(\#[0-9]+|\#x[a-f0-9]+|[a-z]+)\;/si",
function($m) {
// Character Predeclared Entity
$table = array(
"amp" => "&", // &
"lt" => "<", // <
"gt" => ">", // >
"quot" => '"', // "
"apos" => "'", // '
);
$sym = $m[1];
if (strlen($sym) > 1) {
if ($sym{0} == '#' && $sym{1} == 'x' && isset($sym{2})) {
$sym = chr(hexdec(substr($sym, 2)));
} elseif ($sym{0} == '#') {
$sym = chr(substr($sym, 1));
} else {
$key = strtolower($sym);
$sym = isset($table[$key]) ? $table[$key] : '&'.$sym.';';
}
}
return $sym;
},
$data
);
}
function get_int($i) {
if ($i > 268435455) {
$bytes0 = 0 | ($i & 127);
$bytes1 = 128 | (($i >> 7) & 127);
$bytes2 = 128 | (($i >> 14) & 127);
$bytes3 = 128 | (($i >> 21) & 127);
$bytes4 = 128 | (($i >> 28) & 127);
$out = chr($bytes4).chr($bytes3).chr($bytes2).chr($bytes1).chr($bytes0);
} elseif ($i > 2097151) {
$bytes0 = 0 | ($i & 127);
$bytes1 = 128 | (($i >> 7) & 127);
$bytes2 = 128 | (($i >> 14) & 127);
$bytes3 = 128 | (($i >> 21) & 127);
$out = chr($bytes3).chr($bytes2).chr($bytes1).chr($bytes0);
} elseif ($i > 16383) {
$bytes0 = 0 | ($i & 127);
$bytes1 = 128 | (($i >> 7) & 127);
$bytes2 = 128 | (($i >> 14) & 127);
$out = chr($bytes2).chr($bytes1).chr($bytes0);
} elseif ($i > 127) {
$bytes0 = 0 | ($i & 127);
$bytes1 = 128 | (($i >> 7) & 127);
$out = chr($bytes1).chr($bytes0);
} else {
$bytes0 = 0 | ($i & 127);
$out = chr($bytes0);
}
return $out;
}
function hex2bin($data) {
$out = '';
for ($i = 0; $i < strlen($data); $i += 2)
$out .= chr(hexdec($data{$i}.$data{$i + 1}));
return $out;
}
?>