浏览代码

PPCL, PPRF, and video player improvements

Billy Barrow 1 年之前
父节点
当前提交
2dba627329
共有 5 个文件被更改,包括 100 次插入44 次删除
  1. 2 1
      .htaccess
  2. 3 3
      config.php
  3. 2 2
      ppcl.php
  4. 92 37
      pprf.php
  5. 1 1
      video_player.php

+ 2 - 1
.htaccess

@@ -1,6 +1,7 @@
 RewriteEngine on
 RewriteRule index.php - [L]
+RewriteRule pprf.php - [L]
 RewriteRule themes/* - [L]
 RewriteRule ppvm_player.js - [L]
 RewriteRule ^(.+?(?=/))/(.*)/?$  index.php?ppub=$1&asset=$2    [L,QSA]
-RewriteRule ^(.+?(?=$|\/))$  index.php?ppub=$1    [L,QSA]
+RewriteRule ^(.+?(?=$|\/))$  index.php?ppub=$1    [L,QSA]

+ 3 - 3
config.php

@@ -11,10 +11,10 @@ define("DATE_FORMAT", "l d F Y, H:i");
 define("USE_PPIX", false);
 define("USE_PPCL", true);
 define("ENABLE_PPRF", true);
-define("ENABLE_PPRF_BSPATCH", true);
-define("BSPATCH_PATH", "/usr/bin/bspatch");
+define("ENABLE_PPRF_VCDIFF", true);
+define("XDELTA3_PATH", "/usr/bin/xdelta3");
 define("PPRF_DATA_DIR", "pprf-data");
-define("PPRF_MAX_UPLOAD_CHUNK_SIZE", 1024);
+define("PPRF_MAX_UPLOAD_CHUNK_SIZE", 10485760);
 define("THEME", "baseline");
 define("PPCL_AGENT_PUBLIC_KEY", "RC0aPTwfOmvdZranz4oDpyZ6uYmWiOS4MvaP2UAWgUw=");
 define("PPCL_AGENT_SECRET_KEY", "Iu+Fewn/KZRftygPA48GOYybFlYqi6tHRhsqvnpXKCM=");

+ 2 - 2
ppcl.php

@@ -19,7 +19,7 @@ class Ppcl {
     public function from_string($str) {
         $lines = explode("\n", $str);
         $header = explode(" ", $lines[0]);
-        $this->authoritative_part = explode("\nMOD ", $str, 2)[0];
+        $this->authoritative_part = explode("\nCST ", $str, 2)[0];
 
         if($header[0] != "PPCL") {
             throw new Exception("Header does not start with PPCL magic number", 1);
@@ -87,7 +87,7 @@ class Ppcl {
         $str = $this->authoritative_part;
         $now = new DateTime();
         $token = random_bytes(256);
-        $str .= "\nCST " . $token . "\nMOD " . $now->format(DateTime::ATOM) . "\n";
+        $str .= "\nCST " . base64_encode($token) . "\nMOD " . $now->format(DateTime::ATOM) . "\n";
         usort($this->publications, function ($a, $b) { return $a->timestamp <=> $b->timestamp; });
         foreach($this->publications as $pub) {
             $str .= "\n" . $pub->raw_data;

+ 92 - 37
pprf.php

@@ -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) {

+ 1 - 1
video_player.php

@@ -353,7 +353,7 @@ function generate_embed($path, $video) {
     }
     ?>
 <div class="ppvm-player" style="position: relative; height:0; background: #2d2d2d; padding-bottom: <?php echo($percent);?>%">
-    <iframe src="<?php echo(SITE_URL);?>/<?php echo($_GET["ppub"]);?>/<?php echo($_GET["asset"]);?>?embed=true/" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></iframe>
+    <iframe src="<?php echo(SITE_URL);?>/<?php echo($_GET["ppub"]);?>/<?php echo($_GET["asset"]);?>?embed=true/" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" allowfullscreen></iframe>
 </div><?php
 }