pprf.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. <?php
  2. include("config.php");
  3. include_once("ppcl.php");
  4. function send_message($type, $message) {
  5. send_partial_message($type, $message);
  6. exit();
  7. }
  8. function send_partial_message($type, $message) {
  9. header("Content-type: application/pprf");
  10. echo("PPRF\x00");
  11. $len = strlen($message) + 1;
  12. echo(pack("PC", $len, $type));
  13. echo($message);
  14. }
  15. function send_failure($code, $message) {
  16. $len = strlen($message);
  17. $msg = pack("vv", $code, $len);
  18. $msg .= $message;
  19. send_message(193, $msg);
  20. }
  21. function send_confirmation() {
  22. send_message(192, "");
  23. }
  24. function get_ppcl() {
  25. $ppcl = new Ppcl();
  26. $ppcl->from_string(file_get_contents(PUBLICATION_DIR . "/collection.ppcl"));
  27. return $ppcl;
  28. }
  29. function get_member($ppcl, $name) {
  30. foreach($ppcl->members as $m) {
  31. if($m->name == $name) {
  32. return $m;
  33. }
  34. }
  35. send_failure(3, "No member \"" . $member_name . "\" in collection");
  36. }
  37. function verify_collection_message($handle, $ppcl) {
  38. $id = fread($handle, SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES);
  39. if($ppcl->identifier != $id) {
  40. send_failure(2, "Requested collection not hosted by this server");
  41. }
  42. }
  43. function read_upload_session_message($handle) {
  44. $data = array();
  45. $tsize = unpack("v", fread($handle, 2))[1];
  46. $data["token"] = fread($handle, $tsize);
  47. $asize = unpack("v", fread($handle, 2))[1];
  48. $data["auth"] = fread($handle, $asize);
  49. return $data;
  50. }
  51. function get_upload_session($session_header) {
  52. $file = fopen(PPRF_DATA_DIR . "/upload-session-info-" . bin2hex($session_header["token"]), 'rb');
  53. if($file == null) {
  54. send_failure(4, "Invalid upload session token");
  55. }
  56. $session = array();
  57. $session["file_size"] = unpack("P", fread($file, 8))[1];
  58. $session["key"] = fread($file, SODIUM_CRYPTO_BOX_KEYPAIRBYTES);
  59. $name_size = unpack("C", fread($file, 1))[1];
  60. $session["member_name"] = fread($file, $name_size);
  61. return $session;
  62. }
  63. function verify_upload_auth($session_header, $session, $member, $checksum) {
  64. $decrypted = sodium_crypto_box_seal_open($session_header["auth"], $session["key"]);
  65. if($decrypted == null) {
  66. send_failure(5, "Could not decrypt upload auth");
  67. }
  68. $verified = sodium_crypto_sign_open($decrypted, $member->signing_public_key);
  69. if($verified == null) {
  70. send_failure(5, "Could not verify upload auth");
  71. }
  72. if($verified != $checksum) {
  73. send_failure(5, "Invalid checksum on upload auth");
  74. }
  75. }
  76. function cleanup_upload_session($token) {
  77. unlink(PPRF_DATA_DIR . "/upload-session-info-" . bin2hex($token));
  78. unlink(PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($token));
  79. }
  80. function read_authenticated_message($handle, $ppcl) {
  81. $data = array();
  82. $name_len = unpack("C", fread($handle, 1))[1];
  83. $data["member_name"] = fread($handle, $name_len);
  84. $auth_len = unpack("v", fread($handle, 2))[1];
  85. $data["authentication"] = fread($handle, $auth_len);
  86. $data["member"] = get_member($ppcl, $data["member_name"]);
  87. return $data;
  88. }
  89. if(!ENABLE_PPRF) {
  90. send_failure(1, "PPRF has been disabled on this server");
  91. }
  92. $handle = fopen("php://input", "rb");
  93. if(fread($handle, 5) != "PPRF\x00") {
  94. send_failure(0, "Stream did not begin with PPRF magic number.");
  95. }
  96. $message_info = unpack("Psize/Ctype", fread($handle, 9));
  97. // Get listing
  98. if($message_info["type"] == 1) {
  99. error_log("Get listing!!!!");
  100. $ppcl = get_ppcl();
  101. verify_collection_message($handle, $ppcl);
  102. $data = unpack("vflags/vcols/Vskip/Ctake", fread($handle, 9));
  103. $flag_tag = ($data["flags"] & (1 << 0)) != 0;
  104. $flag_search = ($data["flags"] & (1 << 1)) != 0;
  105. $flag_since = ($data["flags"] & (1 << 2)) != 0;
  106. $flag_hidden = ($data["flags"] & (1 << 3)) != 0;
  107. $col_title = ($data["cols"] & (1 << 0)) != 0;
  108. $col_author = ($data["cols"] & (1 << 1)) != 0;
  109. $col_description = ($data["cols"] & (1 << 2)) != 0;
  110. $col_timestamp = ($data["cols"] & (1 << 3)) != 0;
  111. $col_tags = ($data["cols"] & (1 << 4)) != 0;
  112. $col_poster = ($data["cols"] & (1 << 5)) != 0;
  113. $col_copyright = ($data["cols"] & (1 << 6)) != 0;
  114. $col_checksum = ($data["cols"] & (1 << 7)) != 0;
  115. $tag = null;
  116. $search = null;
  117. $since = null;
  118. if($flag_tag && $flag_search) {
  119. send_failure(1, "Listing by tag and search at the same time is not supported");
  120. }
  121. if($flag_tag) {
  122. $size = unpack("v", fread($handle, 2))[1];
  123. $tag = fread($handle, $size);
  124. }
  125. if($flag_search) {
  126. $size = unpack("v", fread($handle, 2))[1];
  127. $search = fread($handle, $size);
  128. }
  129. if($flag_since) {
  130. $size = unpack("C", fread($handle, 2))[1];
  131. $since = new DateTime(fread($handle, $size));
  132. }
  133. $results = array();
  134. if(USE_PPIX) {
  135. include_once("ppix.php");
  136. $ppix = new Ppix(fopen(PUBLICATION_DIR . "/lib.ppix", 'rb'));
  137. if($flag_search) {
  138. $ids = $ppix->do_search(strtolower($search));
  139. $list = array();
  140. for ($i=0; $i < count($ids); $i++) {
  141. $list[$i] = $ppix->get_publication_by_id($ids[$i]);
  142. }
  143. $results = $list;
  144. } else if($flag_tag) {
  145. $tags = $ppix->get_tags();
  146. $col = $tags[$tag];
  147. if($col != null) {
  148. $ids = $ppix->get_collection_by_id($col);
  149. $list = array();
  150. for ($i=0; $i < count($ids); $i++) {
  151. $list[$i] = $ppix->get_publication_by_id($ids[$i]);
  152. }
  153. $results = $list;
  154. }
  155. } else {
  156. $count = $ppix->get_publication_count();
  157. $list = array();
  158. for ($i=0; $i < $count; $i++) {
  159. $list[$i] = $ppix->get_publication_by_id($i);
  160. }
  161. $results = $list;
  162. }
  163. }
  164. else {
  165. if($flag_tag || $flag_search) {
  166. send_failure(1, "Indexed listings are not enabled on this server");
  167. }
  168. $count = count($ppcl->publications);
  169. $list = array();
  170. for ($i=0; $i < $count; $i++) {
  171. $list[$i] = $ppcl->publications[$i]->name;
  172. }
  173. $results = $list;
  174. }
  175. $publications = array();
  176. foreach($results as $result) {
  177. foreach($ppcl->publications as $pub) {
  178. if($pub->name == $result && (!$flag_since || $pub->timestamp > $since)) {
  179. array_push($publications, $pub);
  180. }
  181. }
  182. }
  183. $publications = array_slice($publications, $data["skip"], $data["take"]);
  184. $res_cols = 0;
  185. if($col_title) {
  186. $res_cols |= (1 << 0);
  187. }
  188. if($col_author) {
  189. $res_cols |= (1 << 1);
  190. }
  191. if($col_description) {
  192. $res_cols |= (1 << 2);
  193. }
  194. if($col_timestamp) {
  195. $res_cols |= (1 << 3);
  196. }
  197. if($col_tags) {
  198. $res_cols |= (1 << 4);
  199. }
  200. if($col_poster) {
  201. $res_cols |= (1 << 5);
  202. }
  203. if($col_copyright) {
  204. $res_cols |= (1 << 6);
  205. }
  206. if($col_checksum) {
  207. $res_cols |= (1 << 7);
  208. }
  209. $message = pack("vC", $res_cols, count($publications));
  210. include_once("ppub.php");
  211. foreach($publications as $pub) {
  212. $message .= pack("v", strlen($pub->name));
  213. $message .= $pub->name;
  214. $ppub = new Ppub();
  215. $ppub->read_file(PUBLICATION_DIR . "/".$pub->name);
  216. if($col_title) {
  217. if(!isset($ppub->metadata["title"])) {
  218. $message .= "\x00\x00";
  219. }
  220. else {
  221. $message .= pack("v", strlen($ppub->metadata["title"]));
  222. $message .= $ppub->metadata["title"];
  223. }
  224. }
  225. if($col_author) {
  226. if(!isset($ppub->metadata["author"])) {
  227. $message .= "\x00\x00";
  228. }
  229. else {
  230. $message .= pack("v", strlen($ppub->metadata["author"]));
  231. $message .= $ppub->metadata["author"];
  232. }
  233. }
  234. if($col_description) {
  235. if(!isset($ppub->metadata["description"])) {
  236. $message .= "\x00\x00";
  237. }
  238. else {
  239. $message .= pack("v", strlen($ppub->metadata["description"]));
  240. $message .= $ppub->metadata["description"];
  241. }
  242. }
  243. if($col_timestamp) {
  244. if(!isset($ppub->metadata["date"])) {
  245. $message .= "\x00\x00";
  246. }
  247. else {
  248. $message .= pack("C", strlen($ppub->metadata["date"]));
  249. $message .= $ppub->metadata["date"];
  250. }
  251. }
  252. if($col_tags) {
  253. if(!isset($ppub->metadata["tags"])) {
  254. $message .= "\x00\x00";
  255. }
  256. else {
  257. $message .= pack("v", strlen($ppub->metadata["tags"]));
  258. $message .= $ppub->metadata["tags"];
  259. }
  260. }
  261. if($col_poster) {
  262. if(!isset($ppub->metadata["poster"])) {
  263. $message .= "\x00\x00";
  264. }
  265. else {
  266. $message .= pack("v", strlen($ppub->metadata["poster"]));
  267. $message .= $ppub->metadata["poster"];
  268. }
  269. }
  270. if($col_copyright) {
  271. if(!isset($ppub->metadata["copyright"])) {
  272. $message .= "\x00\x00";
  273. }
  274. else {
  275. $message .= pack("v", strlen($ppub->metadata["copyright"]));
  276. $message .= $ppub->metadata["copyright"];
  277. }
  278. }
  279. if($col_checksum) {
  280. $message .= $pub->checksum;
  281. }
  282. }
  283. send_message(130, $message);
  284. }
  285. // Get asset
  286. if($message_info["type"] == 3) {
  287. include_once("ppub.php");
  288. $ppcl = get_ppcl();
  289. verify_collection_message($handle, $ppcl);
  290. $flags = unpack("v", fread($handle, 2))[1];
  291. $name_len = unpack("C", fread($handle, 1))[1];
  292. $name = fread($handle, $name_len);
  293. $asset_len = unpack("v", fread($handle, 2))[1];
  294. $asset_name = "";
  295. if($asset_len > 0) {
  296. $asset_name = fread($handle, $asset_len);
  297. }
  298. $skip = unpack("P", fread($handle, 8))[1];
  299. $take = unpack("P", fread($handle, 8))[1];
  300. error_log("Get asset " . $asset_name . " from publication " . $name);
  301. $pub_entry = null;
  302. foreach($ppcl->publications as $pub) {
  303. if($pub->name == $name) {
  304. $pub_entry = $pub;
  305. break;
  306. }
  307. }
  308. if($pub_entry == null) {
  309. send_failure(16, "Publication \"" . $name . "\" not found");
  310. }
  311. $ppub = new Ppub();
  312. $ppub->read_file(PUBLICATION_DIR . "/" . $name);
  313. $asset = null;
  314. if($asset_name == "") {
  315. $asset = $ppub->asset_list[1];
  316. }
  317. else {
  318. $asset = $ppub->asset_index[$asset_name];
  319. }
  320. if($asset == null) {
  321. send_failure(17, "Asset \"" . $asset_name . "\" not found");
  322. }
  323. // Reserved flags
  324. $message = "\x00\x00";
  325. $message .= pack("C", strlen($asset->mimetype));
  326. $message .= $asset->mimetype;
  327. if($skip == 0 && $take == 0) {
  328. $data = $ppub->read_asset($asset);
  329. $message .= pack("P", strlen($data));
  330. $message .= $data;
  331. send_message(132, $message);
  332. }
  333. else if(!$ppub->can_stream_asset($asset)) {
  334. send_failure(18, "Asset is not streamable");
  335. }
  336. $file_size = $ppub->get_asset_size($asset);
  337. if($skip > $file_size) {
  338. $message .= pack("P", 0);
  339. send_message(132, $message);
  340. }
  341. if($take + $skip > $file_size) {
  342. $take = $file_size - $skip;
  343. }
  344. $message .= pack("P", strlen($take));
  345. send_partial_message(132, $message);
  346. $ppub->stream_asset($asset, $skip, $take);
  347. exit();
  348. }
  349. // Get publication
  350. if($message_info["type"] == 4) {
  351. $ppcl = get_ppcl();
  352. verify_collection_message($handle, $ppcl);
  353. $flags = unpack("v", fread($handle, 2))[1];
  354. $name_len = unpack("C", fread($handle, 1))[1];
  355. $name = fread($handle, $name_len);
  356. $pub_entry = null;
  357. foreach($ppcl->publications as $pub) {
  358. if($pub->name == $name) {
  359. $pub_entry = $pub;
  360. break;
  361. }
  362. }
  363. if($pub_entry == null) {
  364. send_failure(16, "Publication \"" . $name . "\" not found");
  365. }
  366. $path = PUBLICATION_DIR . "/" . $name;
  367. $size = filesize($path);
  368. $ppub = fopen($path, 'rb');
  369. $message = "\x00\x00";
  370. $message .= pack("P", $size);
  371. send_partial_message(133, $message);
  372. $pos = 0;
  373. while($pos < $size) {
  374. $chunksize = min(1024 * 1024, $size - $pos);
  375. echo(fread($ppub, $chunksize));
  376. flush();
  377. $pos += $chunksize;
  378. }
  379. exit();
  380. }
  381. // Get collection
  382. if($message_info["type"] == 6) {
  383. $ppcl = get_ppcl();
  384. verify_collection_message($handle, $ppcl);
  385. $data = file_get_contents(PUBLICATION_DIR . "/collection.ppcl");
  386. $message = pack("V", strlen($data));
  387. $message .= $data;
  388. send_message(135, $message);
  389. }
  390. // Register name
  391. if($message_info["type"] == 32) {
  392. $ppcl = get_ppcl();
  393. verify_collection_message($handle, $ppcl);
  394. $auth = read_authenticated_message($handle, $ppcl);
  395. $member = $auth["member"];
  396. $name_size = unpack("C", fread($handle, 1))[1];
  397. $name = fread($handle, $name_size);
  398. $name_auth = sodium_crypto_sign_open($auth["authentication"], $member->signing_public_key);
  399. if($name_auth == null) {
  400. send_failure(11, "Could not authenticate register name request");
  401. }
  402. if($name_auth != hash("sha512", $name, true)) {
  403. send_failure(11, "Invalid checksum");
  404. }
  405. if(strtolower(substr($name, strlen($name)-5, 5)) != ".ppub") {
  406. send_failure(7, "Name must end in \".ppub\"");
  407. }
  408. $path = PUBLICATION_DIR . "/" . $name;
  409. if(file_exists($path)) {
  410. send_failure(8, "Name already exists");
  411. }
  412. file_put_contents($path, "");
  413. send_confirmation();
  414. }
  415. // Begin upload
  416. if($message_info["type"] == 33) {
  417. $ppcl = get_ppcl();
  418. verify_collection_message($handle, $ppcl);
  419. $details = unpack("Pfile_size/Cname_size", fread($handle, 9));
  420. $member_name = fread($handle, $details["name_size"]);
  421. $member = get_member($ppcl, $member_name);
  422. // Create upload session
  423. $token = random_bytes(16);
  424. $key = sodium_crypto_box_keypair();
  425. $session_data = pack("P", $details["file_size"]);
  426. $session_data .= $key;
  427. $session_data .= pack("C", $details["name_size"]);
  428. $session_data .= $member_name;
  429. file_put_contents(PPRF_DATA_DIR . "/upload-session-info-" . bin2hex($token), $session_data);
  430. file_put_contents(PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($token), "");
  431. $session_auth = sodium_crypto_box_publickey($key);
  432. $session_auth .= $token;
  433. // Build and send reply
  434. $challenge = sodium_crypto_box_seal($session_auth, $member->sealing_public_key);
  435. $reply = pack("v", strlen($challenge));
  436. $reply .= $challenge;
  437. $reply .= pack("V", PPRF_MAX_UPLOAD_CHUNK_SIZE);
  438. send_message(160, $reply);
  439. }
  440. // Upload message
  441. if($message_info["type"] == 35) {
  442. $ppcl = get_ppcl();
  443. verify_collection_message($handle, $ppcl);
  444. $session_header = read_upload_session_message($handle);
  445. $session = get_upload_session($session_header);
  446. $path = PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($session_header["token"]);
  447. $offset = unpack("P", fread($handle, 8))[1];
  448. $chunk_size = unpack("P", fread($handle, 8))[1];
  449. $chunk_path = PPRF_DATA_DIR . "/upload-session-file-chunk-offset-" . $offset . "-" . bin2hex($session_header["token"]);
  450. $member = get_member($ppcl, $session["member_name"]);
  451. if($offset != filesize($path)) {
  452. $expected = filesize($path);
  453. cleanup_upload_session($session_header["token"]);
  454. send_failure(6, "Invalid offset, got " . $offset . " expected " . $expected);
  455. }
  456. $read = 0;
  457. while($read < $chunk_size) {
  458. $data = fread($handle, $chunk_size);
  459. file_put_contents($chunk_path, $data, FILE_APPEND);
  460. file_put_contents($path, $data, FILE_APPEND);
  461. $read += strlen($data);
  462. }
  463. // Verify data
  464. $checksum = hash_file("sha512", $chunk_path, true);
  465. unlink($chunk_path);
  466. error_log("Received " . strlen($data) . " bytes from " . $message_info["size"] . " byte message");
  467. verify_upload_auth($session_header, $session, $member, $checksum);
  468. send_confirmation();
  469. }
  470. // Finalise upload message
  471. if($message_info["type"] == 34) {
  472. $ppcl = get_ppcl();
  473. verify_collection_message($handle, $ppcl);
  474. $session_header = read_upload_session_message($handle);
  475. $session = get_upload_session($session_header);
  476. $upload_path = PPRF_DATA_DIR . "/upload-session-file-" . bin2hex($session_header["token"]);
  477. $member = get_member($ppcl, $session["member_name"]);
  478. $flags = unpack('C', fread($handle, 1))[1];
  479. $dest_len = unpack('v', fread($handle, 2))[1];
  480. $dest_name = fread($handle, $dest_len);
  481. $dest_path = PUBLICATION_DIR . "/" . $dest_name;
  482. $flag_cancel = ($flags & 1 << 0) != 0;
  483. $flag_replace = ($flags & 1 << 1) != 0;
  484. $flag_vcdiff = ($flags & 1 << 2) != 0;
  485. // If cancel flag set
  486. if($flag_cancel) {
  487. cleanup_upload_session($session_header["token"]);
  488. send_confirmation();
  489. }
  490. if(!file_exists($dest_path)) {
  491. cleanup_upload_session($session_header["token"]);
  492. send_failure(9, "The name \"" . $dest_name . "\" is not registered");
  493. }
  494. // If overwrite flag NOT set, and file is not empty
  495. if(!$flag_replace && filesize($dest_path) != 0) {
  496. cleanup_upload_session($session_header["token"]);
  497. send_failure(10, "The destination \"" . $dest_name . "\" is not empty");
  498. }
  499. // If destination is already published
  500. foreach($ppcl->publications as $pub) {
  501. if($pub->name == $dest_name) {
  502. send_failure(15, "The destination \"" . $dest_name . "\" cannot be overwritten as it is currently published");
  503. }
  504. }
  505. // Verify data
  506. $checksum = hash_file("sha512", $upload_path, true);
  507. verify_upload_auth($session_header, $session, $member, $checksum);
  508. // Copy to destination
  509. if(!$flag_vcdiff) {
  510. rename($upload_path, $dest_path);
  511. }
  512. else if(!ENABLE_PPRF_VCDIFF) {
  513. send_failure(1, "VCDIFF is not supported by this server");
  514. }
  515. else {
  516. $old_hash = hash_file("sha512", $dest_path, true);
  517. $patched_path = PPRF_DATA_DIR . "/upload-session-patch-" . bin2hex($session_header["token"]);
  518. $output=null;
  519. $retval=null;
  520. exec(XDELTA3_PATH . " -d -s " . escapeshellarg($dest_path) . " " . escapeshellarg($upload_path) . " " . escapeshellarg($patched_path), $output, $retval);
  521. if($retval != 0) {
  522. cleanup_upload_session($session_header["token"]);
  523. send_failure(13, "xdelta3 failed with exit code " . $retval);
  524. }
  525. rename($patched_path, $dest_path);
  526. }
  527. cleanup_upload_session($session_header["token"]);
  528. send_confirmation();
  529. }
  530. // Publish message
  531. if($message_info["type"] == 36) {
  532. $ppcl = get_ppcl();
  533. verify_collection_message($handle, $ppcl);
  534. $auth = read_authenticated_message($handle, $ppcl);
  535. $str_len = unpack("v", fread($handle, 2))[1];
  536. $pub_str = fread($handle, $str_len);
  537. try {
  538. $pub_entry = new CollectionPublication($pub_str, $ppcl->members);
  539. $signature = sodium_crypto_sign_open($auth["authentication"], $auth["member"]->signing_public_key);
  540. if($signature == null) {
  541. send_failure(5, "Could not verify member signature");
  542. }
  543. $expected_auth = hash("sha512", $pub_str, true);
  544. $expected_auth .= $ppcl->current_state_token;
  545. if($expected_auth != $signature) {
  546. send_failure(5, "Invalid authorisation token");
  547. }
  548. array_push($ppcl->publications, $pub_entry);
  549. file_put_contents(PUBLICATION_DIR . "/collection.ppcl", $ppcl->to_string());
  550. send_confirmation();
  551. }
  552. catch(Exception $e) {
  553. send_failure(14, $e->getMessage());
  554. }
  555. }
  556. // Unpublish message
  557. if($message_info["type"] == 37) {
  558. $ppcl = get_ppcl();
  559. verify_collection_message($handle, $ppcl);
  560. $auth = read_authenticated_message($handle, $ppcl);
  561. $name_len = unpack("C", fread($handle, 1))[1];
  562. $name = fread($handle, $name_len);
  563. $pub_entry = null;
  564. foreach($ppcl->publications as $pub) {
  565. if($pub->name == $name) {
  566. $pub_entry = $pub;
  567. break;
  568. }
  569. }
  570. if($pub_entry == null) {
  571. send_failure(16, "Publication \"" . $name . "\" not found");
  572. }
  573. $signature = sodium_crypto_sign_open($auth["authentication"], $auth["member"]->signing_public_key);
  574. if($signature == null) {
  575. send_failure(5, "Could not verify member signature");
  576. }
  577. $expected_auth = hash("sha512", $name, true);
  578. $expected_auth .= $ppcl->current_state_token;
  579. if($expected_auth != $signature) {
  580. send_failure(5, "Invalid authorisation token");
  581. }
  582. try {
  583. $i = array_search($pub_entry, $ppcl->publications);
  584. array_splice($ppcl->publications, $i, 1);
  585. file_put_contents(PUBLICATION_DIR . "/collection.ppcl", $ppcl->to_string());
  586. send_confirmation();
  587. }
  588. catch(Exception $e) {
  589. send_failure(14, $e->getMessage());
  590. }
  591. }
  592. // Rebuild index message
  593. if($message_info["type"] == 39) {
  594. $ppcl = get_ppcl();
  595. verify_collection_message($handle, $ppcl);
  596. $auth = read_authenticated_message($handle, $ppcl);
  597. $signature = sodium_crypto_sign_open($auth["authentication"], $auth["member"]->signing_public_key);
  598. if($signature == null) {
  599. send_failure(5, "Could not verify member signature");
  600. }
  601. $expected_auth = "PPIX\xFF" . $ppcl->current_state_token;
  602. if($expected_auth != $signature) {
  603. send_failure(5, "Invalid authorisation token");
  604. }
  605. try {
  606. include_once("ppix-gen.php");
  607. generate_ppix_from_ppcl();
  608. }
  609. catch(Exception $e) {
  610. send_failure(12, $e->getMessage());
  611. }
  612. send_confirmation();
  613. }
  614. // // Get identity message
  615. // if($message_info["type"] == 41) {
  616. // $ppcl = get_ppcl();
  617. // verify_collection_message($handle, $ppcl);
  618. // $member_name_size = unpack("C", fread($handle, 1))[1];
  619. // $member_name = fread($handle, $member_name_size);
  620. // $member = get_member($ppcl, $member_name);
  621. // $message = pack("v", strlen($ppcl->shared_signature_key));
  622. // $message .= $ppcl->shared_signature_key;
  623. // $message = pack("v", strlen($ppcl->current_state_token));
  624. // $message .= $ppcl->current_state_token;
  625. // $message .= pack("v", strlen($member->collection_secret));
  626. // $message .= $member->collection_secret;
  627. // send_message(161, $message);
  628. // }
  629. send_failure(1, "Messages of type " . $message_info["type"] . " are not supported by this server");
  630. ?>