|
@@ -70,15 +70,15 @@ function get_upload_session($session_header) {
|
|
|
function verify_upload_auth($session_header, $session, $member, $checksum) {
|
|
|
$decrypted = sodium_crypto_box_seal_open($session_header["auth"], $session["key"]);
|
|
|
if($decrypted == null) {
|
|
|
- send_failure(5, "Could not decrypt upload chunk auth");
|
|
|
+ send_failure(5, "Could not decrypt upload auth");
|
|
|
}
|
|
|
$verified = sodium_crypto_sign_open($decrypted, $member->signing_public_key);
|
|
|
if($verified == null) {
|
|
|
- send_failure(5, "Could not verify upload chunk auth");
|
|
|
+ send_failure(5, "Could not verify upload auth");
|
|
|
}
|
|
|
|
|
|
if($verified != $checksum) {
|
|
|
- send_failure(5, "Invalid checksum on upload chunk auth");
|
|
|
+ send_failure(5, "Invalid checksum on upload auth");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -87,6 +87,15 @@ function cleanup_upload_session($token) {
|
|
|
unlink(PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($token));
|
|
|
}
|
|
|
|
|
|
+function read_authenticated_message($handle, $ppcl) {
|
|
|
+ $data = array();
|
|
|
+ $name_len = unpack("C", fread($handle, 1))[1];
|
|
|
+ $data["member_name"] = fread($handle, $name_len);
|
|
|
+ $auth_len = unpack("v", fread($handle, 2))[1];
|
|
|
+ $data["authentication"] = fread($handle, $auth_len);
|
|
|
+ $data["member"] = get_member($ppcl, $data["member_name"]);
|
|
|
+ return $data;
|
|
|
+}
|
|
|
|
|
|
if(!ENABLE_PPRF) {
|
|
|
send_failure(1, "PPRF has been disabled on this server");
|
|
@@ -114,16 +123,18 @@ if($message_info["type"] == 6) {
|
|
|
if($message_info["type"] == 32) {
|
|
|
$ppcl = get_ppcl();
|
|
|
verify_collection_message($handle, $ppcl);
|
|
|
- $member_name_size = unpack("C", fread($handle, 1))[1];
|
|
|
- $member_name = fread($handle, $member_name_size);
|
|
|
- $member = get_member($ppcl, $member_name);
|
|
|
+ $auth = read_authenticated_message($handle, $ppcl);
|
|
|
+ $member = $auth["member"];
|
|
|
|
|
|
- $signed_name_size = unpack("v", fread($handle, 2))[1];
|
|
|
- $signed_name = fread($handle, $signed_name_size);
|
|
|
+ $name_size = unpack("C", fread($handle, 1))[1];
|
|
|
+ $name = fread($handle, $name_size);
|
|
|
|
|
|
- $name = sodium_crypto_sign_open($signed_name, $member->signing_public_key);
|
|
|
- if($name == null) {
|
|
|
- send_failure(11, "Could not verify signed name");
|
|
|
+ $name_auth = sodium_crypto_sign_open($auth["authentication"], $member->signing_public_key);
|
|
|
+ if($name_auth == null) {
|
|
|
+ send_failure(11, "Could not authenticate register name request");
|
|
|
+ }
|
|
|
+ if($name_auth != hash("sha512", $name, true)) {
|
|
|
+ send_failure(11, "Invalid checksum");
|
|
|
}
|
|
|
|
|
|
if(strtolower(substr($name, strlen($name)-5, 5)) != ".ppub") {
|
|
@@ -177,21 +188,31 @@ if($message_info["type"] == 35) {
|
|
|
$session = get_upload_session($session_header);
|
|
|
$path = PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($session_header["token"]);
|
|
|
$offset = unpack("P", fread($handle, 8))[1];
|
|
|
+ $chunk_size = unpack("P", fread($handle, 8))[1];
|
|
|
+ $chunk_path = PPRF_DATA_DIR . "/upload-session-file-chunk-offset-" . $offset . "-" . bin2hex($session_header["token"]);
|
|
|
|
|
|
$member = get_member($ppcl, $session["member_name"]);
|
|
|
|
|
|
if($offset != filesize($path)) {
|
|
|
- send_failure(6, "Invalid offset, got " . $offset . " expected " . filesize($path));
|
|
|
+ $expected = filesize($path);
|
|
|
+ cleanup_upload_session($session_header["token"]);
|
|
|
+ send_failure(6, "Invalid offset, got " . $offset . " expected " . $expected);
|
|
|
}
|
|
|
|
|
|
- $data_size = $message_info["size"] - 9; // Message size minus headers
|
|
|
- $data = fread($handle, $data_size);
|
|
|
+ $read = 0;
|
|
|
+ while($read < $chunk_size) {
|
|
|
+ $data = fread($handle, $chunk_size);
|
|
|
+ file_put_contents($chunk_path, $data, FILE_APPEND);
|
|
|
+ file_put_contents($path, $data, FILE_APPEND);
|
|
|
+ $read += strlen($data);
|
|
|
+ }
|
|
|
|
|
|
// Verify data
|
|
|
- $checksum = hash("sha512", $data, true);
|
|
|
+ $checksum = hash_file("sha512", $chunk_path, true);
|
|
|
+ unlink($chunk_path);
|
|
|
+ error_log("Received " . strlen($data) . " bytes from " . $message_info["size"] . " byte message");
|
|
|
verify_upload_auth($session_header, $session, $member, $checksum);
|
|
|
|
|
|
- file_put_contents($path, $data, FILE_APPEND);
|
|
|
send_confirmation();
|
|
|
}
|
|
|
|
|
@@ -208,26 +229,24 @@ if($message_info["type"] == 34) {
|
|
|
$dest_len = unpack('v', fread($handle, 2))[1];
|
|
|
$dest_name = fread($handle, $dest_len);
|
|
|
$dest_path = PUBLICATION_DIR . "/" . $dest_name;
|
|
|
- $bsdiff_old_checksum = null;
|
|
|
+
|
|
|
+ $flag_cancel = ($flags & 1 << 0) != 0;
|
|
|
+ $flag_replace = ($flags & 1 << 1) != 0;
|
|
|
+ $flag_vcdiff = ($flags & 1 << 2) != 0;
|
|
|
|
|
|
// If cancel flag set
|
|
|
- if(($flags & 1 << 0) != 0) {
|
|
|
+ if($flag_cancel) {
|
|
|
cleanup_upload_session($session_header["token"]);
|
|
|
send_confirmation();
|
|
|
}
|
|
|
|
|
|
- // If BSDIFF flag set
|
|
|
- if(($flags & 1 << 2) != 0) {
|
|
|
- $bsdiff_old_checksum = fread($handle, 64);
|
|
|
- }
|
|
|
-
|
|
|
if(!file_exists($dest_path)) {
|
|
|
cleanup_upload_session($session_header["token"]);
|
|
|
send_failure(9, "The name \"" . $dest_name . "\" is not registered");
|
|
|
}
|
|
|
|
|
|
// If overwrite flag NOT set, and file is not empty
|
|
|
- if(($flags & 1 << 1) == 0 && filesize($dest_path) != 0) {
|
|
|
+ if(!$flag_replace && filesize($dest_path) != 0) {
|
|
|
cleanup_upload_session($session_header["token"]);
|
|
|
send_failure(10, "The destination \"" . $dest_name . "\" is not empty");
|
|
|
}
|
|
@@ -244,25 +263,21 @@ if($message_info["type"] == 34) {
|
|
|
verify_upload_auth($session_header, $session, $member, $checksum);
|
|
|
|
|
|
// Copy to destination
|
|
|
- if($bsdiff_old_checksum == null) {
|
|
|
+ if(!$flag_vcdiff) {
|
|
|
rename($upload_path, $dest_path);
|
|
|
}
|
|
|
- else if(!ENABLE_PPRF_BSPATCH) {
|
|
|
- send_failure(1, "BSPATCH is not supported by this server");
|
|
|
+ else if(!ENABLE_PPRF_VCDIFF) {
|
|
|
+ send_failure(1, "VCDIFF is not supported by this server");
|
|
|
}
|
|
|
else {
|
|
|
$old_hash = hash_file("sha512", $dest_path, true);
|
|
|
- if($bsdiff_old_checksum != $old_hash) {
|
|
|
- cleanup_upload_session($session_header["token"]);
|
|
|
- send_failure(12, "Destination file does not match provided checksum");
|
|
|
- }
|
|
|
$patched_path = PPRF_DATA_DIR . "/upload-session-patch-" . bin2hex($session_header["token"]);
|
|
|
$output=null;
|
|
|
$retval=null;
|
|
|
- exec(BSPATCH_PATH . " " . escapeshellarg($dest_path) . " " . escapeshellarg($patched_path) . " " . escapeshellarg($upload_path), $output, $retval);
|
|
|
+ exec(XDELTA3_PATH . " -d -s " . escapeshellarg($dest_path) . " " . escapeshellarg($upload_path) . " " . escapeshellarg($patched_path), $output, $retval);
|
|
|
if($retval != 0) {
|
|
|
cleanup_upload_session($session_header["token"]);
|
|
|
- send_failure(13, "bspatch failed with exit code " . $retval);
|
|
|
+ send_failure(13, "xdelta3 failed with exit code " . $retval);
|
|
|
}
|
|
|
|
|
|
rename($patched_path, $dest_path);
|
|
@@ -276,16 +291,14 @@ if($message_info["type"] == 34) {
|
|
|
if($message_info["type"] == 36) {
|
|
|
$ppcl = get_ppcl();
|
|
|
verify_collection_message($handle, $ppcl);
|
|
|
+ $auth = read_authenticated_message($handle, $ppcl);
|
|
|
$str_len = unpack("v", fread($handle, 2))[1];
|
|
|
$pub_str = fread($handle, $str_len);
|
|
|
- $auth_len = unpack("v", fread($handle, 2))[1];
|
|
|
- $authentication = fread($handle, $auth_len);
|
|
|
|
|
|
try {
|
|
|
$pub_entry = new CollectionPublication($pub_str, $ppcl->members);
|
|
|
|
|
|
- $member = get_member($ppcl, $pub_entry->member_name);
|
|
|
- $signature = sodium_crypto_sign_open($authentication, $member->signing_public_key);
|
|
|
+ $signature = sodium_crypto_sign_open($auth["authentication"], $auth["member"]->signing_public_key);
|
|
|
if($signature == null) {
|
|
|
send_failure(5, "Could not verify member signature");
|
|
|
}
|
|
@@ -305,6 +318,48 @@ if($message_info["type"] == 36) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// Unpublish message
|
|
|
+if($message_info["type"] == 37) {
|
|
|
+ $ppcl = get_ppcl();
|
|
|
+ verify_collection_message($handle, $ppcl);
|
|
|
+ $auth = read_authenticated_message($handle, $ppcl);
|
|
|
+ $name_len = unpack("C", fread($handle, 1))[1];
|
|
|
+ $name = fread($handle, $name_len);
|
|
|
+
|
|
|
+ $pub_entry = null;
|
|
|
+ foreach($ppcl->publications as $pub) {
|
|
|
+ if($pub->name == $name) {
|
|
|
+ $pub_entry = $pub;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if($pub_entry == null) {
|
|
|
+ send_failure(16, "Publication \"" . $name . "\" not found");
|
|
|
+ }
|
|
|
+
|
|
|
+ $signature = sodium_crypto_sign_open($auth["authentication"], $auth["member"]->signing_public_key);
|
|
|
+ if($signature == null) {
|
|
|
+ send_failure(5, "Could not verify member signature");
|
|
|
+ }
|
|
|
+
|
|
|
+ $expected_auth = hash("sha512", $name, true);
|
|
|
+ $expected_auth .= $ppcl->current_state_token;
|
|
|
+ if($expected_auth != $signature) {
|
|
|
+ send_failure(5, "Invalid authorisation token");
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ $i = array_search($pub_entry, $ppcl->publications);
|
|
|
+ array_splice($ppcl->publications, $i, 1);
|
|
|
+ file_put_contents(PUBLICATION_DIR . "/collection.ppcl", $ppcl->to_string());
|
|
|
+ send_confirmation();
|
|
|
+ }
|
|
|
+ catch(Exception $e) {
|
|
|
+ send_failure(14, $e->getMessage());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
// // Get identity message
|
|
|
// if($message_info["type"] == 41) {
|