pprf.php 23 KB

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