/includes/lib/Submission.php

https://github.com/Bumblebee-Project/acpi-www · PHP · 200 lines · 136 code · 20 blank · 44 comment · 16 complexity · 80ef060cb35d7e78b1d6358e2cdd589a MD5 · raw file

  1. <?php
  2. /**
  3. * @license http://www.gnu.org/licenses/gpl.html GPLv3
  4. * @author Lekensteyn <lekensteyn@gmail.com
  5. */
  6. // XXX: refactor? perhaps the new uploaded file needs to be an object too
  7. class Submission {
  8. private $submission_id;
  9. /**
  10. * Pushes some uploads to a queue folder
  11. * @param Upload $acpidump An acpidump.txt upload
  12. * @param Upload $lspci An lspci.txt upload
  13. * @param Upload $dmidecode An dmidecode.txt upload
  14. * @throws SubmissionException when the upload cannot be saved
  15. * @throws DatabaseException when a query fails to execute
  16. */
  17. public static function saveUploads(Upload $acpidump, Upload $lspci,
  18. Upload $dmidecode) {
  19. $db = Database::getInstance();
  20. $acpidump_hash = $acpidump->sha1sum();
  21. $dmidecode_hash = $dmidecode->sha1sum();
  22. $lspci_hash = $lspci->sha1sum();
  23. if (self::getSubmissionIdFromHashes($acpidump_hash, $lspci_hash,
  24. $dmidecode_hash) > 0) {
  25. throw new SubmissionException(
  26. 'The data for your machine has been submitted before.'
  27. );
  28. }
  29. $stmt_ins_submission = $db->prepare(
  30. 'INSERT INTO submission SET acpidump_hash=UNHEX(?),
  31. dmidecode_hash=UNHEX(?), lspci_hash=UNHEX(?)'
  32. );
  33. $stmt_ins_submission->bind_param('sss',
  34. $acpidump_hash,
  35. $dmidecode_hash,
  36. $lspci_hash
  37. );
  38. if (!$stmt_ins_submission->execute()) {
  39. throw new DatabaseException(
  40. 'The submission could not be saved.'
  41. );
  42. }
  43. // integer cast in case the database behaves weird (safeguard)
  44. $submission_id = $stmt_ins_submission->insert_id;
  45. $stmt_ins_submission->close();
  46. $submission = new Submission($submission_id);
  47. try {
  48. self::saveUploadedFiles($submission->getDir(),
  49. $acpidump, $lspci, $dmidecode);
  50. } catch (Exception $ex) {
  51. $submission->remove();
  52. // cleaned up, now pass the error
  53. throw $ex;
  54. }
  55. return $submission;
  56. }
  57. private static function saveUploadedFiles($submission_dir,
  58. Upload $acpidump, Upload $lspci, Upload $dmidecode) {
  59. if (!mkdir($submission_dir)) {
  60. throw new SubmissionException(
  61. 'The submission could not be saved'
  62. );
  63. }
  64. $acpidump->saveTo("$submission_dir/acpidump.txt");
  65. $lspci->saveTo("$submission_dir/lspci.txt");
  66. $dmidecode->saveTo("$submission_dir/dmidecode.txt");
  67. }
  68. /**
  69. * Gets the submission id for upload hashes if any
  70. * @param string $acpidump_hash The hash of acpidump.txt
  71. * @param string $lspci_hash The hash of lspci.txt
  72. * @param string $dmidecode_hash The hash of dmidecode.txt
  73. * @return int A number higher than zero if the submission ID was found,
  74. * zero otherwise
  75. * @throws DatabaseException when the submission_id lookup query fails
  76. */
  77. public static function getSubmissionIdFromHashes($acpidump_hash,
  78. $lspci_hash, $dmidecode_hash) {
  79. $db = Database::getInstance();
  80. $stmt_hash_exists = $db->prepare(
  81. 'SELECT submission_id FROM submission WHERE
  82. acpidump_hash=UNHEX(?) &&
  83. dmidecode_hash=UNHEX(?) &&
  84. lspci_hash=UNHEX(?)
  85. LIMIT 1'
  86. );
  87. $stmt_hash_exists->bind_param('sss',
  88. $acpidump_hash,
  89. $dmidecode_hash,
  90. $lspci_hash
  91. );
  92. if (!$stmt_hash_exists->execute()) {
  93. throw new DatabaseException(
  94. 'It did not became clear whether the files have been submitted'
  95. . 'before.'
  96. );
  97. }
  98. $stmt_hash_exists->bind_result($submission_id);
  99. if (!$stmt_hash_exists->fetch()) {
  100. $submission_id = 0;
  101. }
  102. $stmt_hash_exists->close();
  103. return $submission_id;
  104. }
  105. public function __construct($submission_id) {
  106. $this->submission_id = (int)$submission_id;
  107. if ($this->submission_id < 1) {
  108. throw SubmissionException('Invalid submission ID.');
  109. }
  110. }
  111. /**
  112. * Determines the path to the submission directory
  113. * @return string The path on the filesystem to the submission directory
  114. */
  115. public function getDir() {
  116. return SUBMISSIONS_DIR . '/' . $this->submission_id;
  117. }
  118. /**
  119. * Removes the submission entry in the database and related uploaded files
  120. */
  121. public function remove() {
  122. $this->removeDatabaseEntry();
  123. $this->removeFiles();
  124. }
  125. private function removeDatabaseEntry() {
  126. $db = Database::getInstance();
  127. // $submission_id is a number, this is enforced in the constructor
  128. $db->query('DELETE FROM submission WHERE submission_id=' .
  129. $this->submission_id);
  130. }
  131. private function removeFiles() {
  132. $submission_dir = $this->getDir();
  133. foreach (array('acpidump.txt', 'lspci.txt', 'dmidecode.txt') as $file) {
  134. $path = "$submission_dir/$file";
  135. is_file($path) && unlink($path);
  136. }
  137. if (is_dir($submission_dir) && $dh = opendir($submission_dir)) {
  138. $isEmpty = true;
  139. while (($entry=readdir($dh)) !== false) {
  140. if ($entry != '.' && $entry != '..') {
  141. $isEmpty = true;
  142. break;
  143. }
  144. }
  145. closedir($dh);
  146. $isEmpty && rmdir($submission_dir);
  147. }
  148. }
  149. /**
  150. * Get the submission ID
  151. * @returns int The ID of the submission
  152. */
  153. public function getSubmissionId() {
  154. return $this->submission_id;
  155. }
  156. /**
  157. * Generates a key for claiming a submission
  158. * @return string A key of size 40 which can be used for claiming an upload
  159. * @throws DatabaseException when the key could not be retrieved
  160. */
  161. public function submissionClaimKey() {
  162. $db = Database::getInstance();
  163. // generate a 160-bit random string. @Mathematicans, any errors here?
  164. $claim_key = str_shuffle(sha1(time() . '-' . mt_rand()));
  165. $stmt_gen_key = $db->prepare('INSERT INTO submission_claim SET
  166. claim_key=UNHEX(?), submission_id=?');
  167. $stmt_gen_key->bind_param('sd', $claim_key, $this->submission_id);
  168. if (!$stmt_gen_key->execute()) {
  169. // The chance is higher that the issue is caused by a dead database
  170. // than a collision
  171. throw new DatabaseException(
  172. 'A key for claiming this submission could not be generated.'
  173. );
  174. }
  175. $stmt_gen_key->close();
  176. return $claim_key;
  177. }
  178. }
  179. class SubmissionException extends Exception {}
  180. ?>