BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

upload_handle.php 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. <?php
  2. #declare(strict_types=1);
  3. /**
  4. * Take upload
  5. *
  6. * This pages handles the backend of the torrent upload function.
  7. * It checks the data, and if it all validates, it builds the torrent file,
  8. * then writes the data to the database and the torrent to the disk.
  9. */
  10. $ENV = ENV::go();
  11. $Feed = new Feed;
  12. $Validate = new Validate;
  13. require_once "$ENV->SERVER_ROOT/classes/feed.class.php";
  14. require_once "$ENV->SERVER_ROOT/classes/validate.class.php";
  15. require_once "$ENV->SERVER_ROOT/sections/torrents/functions.php";
  16. enforce_login();
  17. authorize();
  18. /**
  19. * Set $Properties array
  20. *
  21. * This is used if the form doesn't validate,
  22. * and when the time comes to enter it into the database.
  23. *
  24. * todo: Do something about this mess
  25. */
  26. $Properties = [];
  27. $Type = $Categories[(int) $_POST['type']];
  28. $TypeID = $_POST['type'] + 1;
  29. $Properties['CategoryID'] = $TypeID;
  30. $Properties['CategoryName'] = $Type;
  31. $Properties['Title'] = $_POST['title']; # Title1
  32. $Properties['Title2'] = $_POST['title_rj']; # Title2
  33. $Properties['TitleJP'] = $_POST['title_jp']; # Title3
  34. $Properties['CatalogueNumber'] = isset($_POST['catalogue']) ? $_POST['catalogue'] : '';
  35. $Properties['Year'] = $_POST['year'];
  36. $Properties['Studio'] = isset($_POST['studio']) ? $_POST['studio'] : '';
  37. $Properties['Series'] = isset($_POST['series']) ? $_POST['series'] : '';
  38. $Properties['Container'] = isset($_POST['container']) ? $_POST['container'] : '';
  39. $Properties['Media'] = $_POST['media'];
  40. $Properties['Codec'] = isset($_POST['codec']) ? $_POST['codec'] : '';
  41. if (!($_POST['resolution'] ?? false)) {
  42. $_POST['resolution'] = $_POST['ressel'] ?? '';
  43. }
  44. $Properties['Resolution'] = $_POST['resolution'] ?? '';
  45. $Properties['Version'] = $_POST['version'] ?? '';
  46. $Properties['Censored'] = (isset($_POST['censored'])) ? '1' : '0';
  47. $Properties['Anonymous'] = (isset($_POST['anonymous'])) ? '1' : '0';
  48. $Properties['Archive'] = (isset($_POST['archive']) && $_POST['archive'] !== '---') ? $_POST['archive'] : '';
  49. if (isset($_POST['library_image'])) {
  50. $Properties['LibraryImage'] = $_POST['library_image'];
  51. }
  52. if (isset($_POST['tags'])) {
  53. $Properties['TagList'] = implode(',', array_unique(explode(',', str_replace(' ', '', $_POST['tags']))));
  54. }
  55. if (isset($_POST['image'])) {
  56. $Properties['Image'] = $_POST['image'];
  57. }
  58. if (isset($_POST['release'])) {
  59. $Properties['ReleaseGroup'] = $_POST['release'];
  60. }
  61. $Properties['GroupDescription'] = trim($_POST['album_desc']);
  62. $Properties['TorrentDescription'] = $_POST['release_desc'];
  63. $Properties['Screenshots'] = isset($_POST['screenshots']) ? $_POST['screenshots'] : '';
  64. $Properties['Mirrors'] = isset($_POST['mirrors']) ? $_POST['mirrors'] : '';
  65. if ($_POST['album_desc']) {
  66. $Properties['GroupDescription'] = trim($_POST['album_desc']);
  67. } elseif ($_POST['desc']) {
  68. $Properties['GroupDescription'] = trim($_POST['desc']);
  69. }
  70. if (isset($_POST['groupid'])) {
  71. $Properties['GroupID'] = $_POST['groupid'];
  72. }
  73. if (isset($Properties['GroupID'])) {
  74. $Properties['Artists'] = Artists::get_artist($Properties['GroupID']);
  75. }
  76. if (empty($_POST['artists'])) {
  77. $Err = "You didn't enter any creators";
  78. } else {
  79. $Artists = $_POST['artists'];
  80. }
  81. if (!empty($_POST['requestid'])) {
  82. $RequestID = $_POST['requestid'];
  83. $Properties['RequestID'] = $RequestID;
  84. }
  85. /**
  86. * Validate data in upload form
  87. */
  88. # Submit button
  89. $Validate->SetFields(
  90. 'type',
  91. '1',
  92. 'inarray',
  93. 'Please select a valid type.',
  94. array('inarray' => array_keys($Categories))
  95. );
  96. # torrents_group.category_id
  97. $Validate->SetFields(
  98. 'type',
  99. '1',
  100. 'inarray',
  101. 'Please select a valid type.',
  102. array('inarray' => array_keys($Categories))
  103. );
  104. if (!$_POST['groupid']) {
  105. # torrents_group.identifier
  106. $Validate->SetFields(
  107. 'catalogue',
  108. '0',
  109. 'string',
  110. 'Accession Number must be between 0 and 50 characters.',
  111. array('maxlength' => 50, 'minlength' => 0)
  112. );
  113. # torrents.Version
  114. $Validate->SetFields(
  115. 'version',
  116. '0',
  117. 'string',
  118. 'Version must be between 0 and 10 characters.',
  119. array('maxlength' => 10, 'minlength' => 0)
  120. );
  121. # torrents_group.title
  122. $Validate->SetFields(
  123. 'title',
  124. '1',
  125. 'string',
  126. 'Torrent Title must be between 10 and 255 characters.',
  127. array('maxlength' => 255, 'minlength' => 10)
  128. );
  129. # torrents_group.subject
  130. $Validate->SetFields(
  131. 'title_rj',
  132. '0',
  133. 'string',
  134. 'Organism must be between 0 and 255 characters.',
  135. array('maxlength' => 255, 'minlength' => 0)
  136. );
  137. # torrents_group.object
  138. $Validate->SetFields(
  139. 'title_jp',
  140. '0',
  141. 'string',
  142. 'Strain/Variety must be between 0 and 255 characters.',
  143. array('maxlength' => 255, 'minlength' => 0)
  144. );
  145. # torrents_group.workgroup
  146. $Validate->SetFields(
  147. 'studio',
  148. '1',
  149. 'string',
  150. 'Department/Lab must be between 0 and 100 characters.',
  151. array('maxlength' => 100, 'minlength' => 0)
  152. );
  153. # torrents_group.location
  154. $Validate->SetFields(
  155. 'series',
  156. '0',
  157. 'string',
  158. 'Location must be between 0 and 100 characters.',
  159. array('maxlength' => 100, 'minlength' => 0)
  160. );
  161. /* todo: Fix the year validation
  162. # torrents_group.year
  163. $Validate->SetFields(
  164. 'year',
  165. '1',
  166. 'number',
  167. 'The year of the original release must be entered.',
  168. array('maxlength' => 4, 'minlength' => 4)
  169. );
  170. */
  171. # torrents.Media
  172. $Validate->SetFields(
  173. 'media',
  174. '1',
  175. 'inarray',
  176. 'Please select a valid platform.',
  177. array('inarray' => $ENV->flatten($ENV->META->Platforms))
  178. );
  179. /*
  180. # torrents.Container
  181. $Validate->SetFields(
  182. 'container',
  183. '1',
  184. 'inarray',
  185. 'Please select a valid format.',
  186. array('inarray' => array_merge($Containers, $ContainersGames))
  187. );
  188. */
  189. # torrents.Resolution
  190. $Validate->SetFields(
  191. 'resolution',
  192. '1',
  193. 'string',
  194. 'Scope must be between 4 and 20 characters.',
  195. array('maxlength' => 20, 'minlength' => 4)
  196. );
  197. # torrents_group.tag_list
  198. $Validate->SetFields(
  199. 'tags',
  200. '1',
  201. 'string',
  202. 'You must enter at least five tags. Maximum length is 500 characters.',
  203. array('maxlength' => 500, 'minlength' => 10)
  204. );
  205. # torrents_group.picture
  206. $Validate->SetFields(
  207. 'image',
  208. '0',
  209. 'link',
  210. 'The image URL you entered was invalid.',
  211. array('maxlength' => 255, 'minlength' => 10) # x.yz/a.bc
  212. );
  213. }
  214. # torrents_group.description
  215. $Validate->SetFields(
  216. 'album_desc',
  217. '1',
  218. 'string',
  219. 'The description must be between 100 and 65535 characters.',
  220. array('maxlength' => 65535, 'minlength' => 100)
  221. );
  222. /* todo: Fix the Group ID validation
  223. # torrents_group.id
  224. $Validate->SetFields(
  225. 'groupid',
  226. '0',
  227. 'number',
  228. 'Group ID was not numeric.'
  229. );
  230. */
  231. $Err = $Validate->ValidateForm($_POST); // Validate the form
  232. # todo: Move all this validation code to the Validate class
  233. if (count(explode(',', $Properties['TagList'])) < 5) {
  234. $Err = 'You must enter at least 5 tags.';
  235. }
  236. if (!(isset($_POST['title']) || isset($_POST['title_rj']) || isset($_POST['title_jp']))) {
  237. $Err = 'You must enter at least one title.';
  238. }
  239. $File = $_FILES['file_input']; // This is our torrent file
  240. $TorrentName = $File['tmp_name'];
  241. if (!is_uploaded_file($TorrentName) || !filesize($TorrentName)) {
  242. $Err = 'No torrent file uploaded, or file is empty.';
  243. } elseif (substr(strtolower($File['name']), strlen($File['name']) - strlen('.torrent')) !== '.torrent') {
  244. $Err = "You seem to have put something other than a torrent file into the upload field. (".$File['name'].").";
  245. }
  246. // Multiple artists!
  247. $LogName = '';
  248. if (empty($Properties['GroupID']) && empty($ArtistForm)) {
  249. $ArtistNames = [];
  250. $ArtistForm = [];
  251. for ($i = 0; $i < count($Artists); $i++) {
  252. if (trim($Artists[$i]) !== '') {
  253. if (!in_array($Artists[$i], $ArtistNames)) {
  254. $ArtistForm[$i] = array('name' => Artists::normalise_artist_name($Artists[$i]));
  255. array_push($ArtistNames, $ArtistForm[$i]['name']);
  256. }
  257. }
  258. }
  259. $LogName .= Artists::display_artists($ArtistForm, false, true, false);
  260. } elseif (empty($ArtistForm)) {
  261. $DB->query("
  262. SELECT ta.ArtistID, ag.Name
  263. FROM torrents_artists AS ta
  264. JOIN artists_group AS ag ON ta.ArtistID = ag.ArtistID
  265. WHERE ta.GroupID = ?
  266. ORDER BY ag.Name ASC", $Properties['GroupID']);
  267. $ArtistForm = [];
  268. while (list($ArtistID, $ArtistName) = $DB->next_record(MYSQLI_BOTH, false)) {
  269. array_push($ArtistForm, array('id' => $ArtistID, 'name' => display_str($ArtistName)));
  270. array_push($ArtistsUnescaped, array('name' => $ArtistName));
  271. }
  272. $LogName .= Artists::display_artists($ArtistsUnescaped, false, true, false);
  273. }
  274. if ($Err) { // Show the upload form, with the data the user entered
  275. $UploadForm = $Type;
  276. require_once SERVER_ROOT.'/sections/upload/upload.php' ;
  277. error(400, $NoHTML = true);
  278. }
  279. ImageTools::blacklisted($Properties['Image']);
  280. /**
  281. * Make variables ready for database input
  282. *
  283. * Prepared SQL statements do this for us,
  284. * so there is nothing to do here anymore.
  285. */
  286. $T = $Properties;
  287. /**
  288. * Generate torrent file
  289. */
  290. $Tor = new BencodeTorrent($TorrentName, true);
  291. $PublicTorrent = $Tor->make_private(); // The torrent is now private
  292. $UnsourcedTorrent = $Tor->make_sourced(); // The torrent now has the source field set
  293. $InfoHash = pack('H*', $Tor->info_hash());
  294. if (isset($Tor->Dec['encrypted_files'])) {
  295. $Err = 'This torrent contains an encrypted file list which is not supported here.';
  296. }
  297. // File list and size
  298. list($TotalSize, $FileList) = $Tor->file_list();
  299. $NumFiles = count($FileList);
  300. $TmpFileList = [];
  301. $TooLongPaths = [];
  302. $DirName = (isset($Tor->Dec['info']['files']) ? Format::make_utf8($Tor->get_name()) : '');
  303. check_name($DirName); // Check the folder name against the blacklist
  304. foreach ($FileList as $File) {
  305. list($Size, $Name) = $File;
  306. // Check file name and extension against blacklist/whitelist
  307. check_file($Type, $Name);
  308. // Make sure the filename is not too long
  309. if (mb_strlen($Name, 'UTF-8') + mb_strlen($DirName, 'UTF-8') + 1 > 255) { # MAX_FILENAME_LENGTH
  310. $TooLongPaths[] = "$DirName/$Name";
  311. }
  312. // Add file info to array
  313. $TmpFileList[] = Torrents::filelist_format_file($File);
  314. }
  315. if (count($TooLongPaths) > 0) {
  316. $Names = implode(' <br />', $TooLongPaths);
  317. $Err = "The torrent contained one or more files with too long a name:<br /> $Names";
  318. }
  319. $FilePath = $DirName;
  320. $FileString = implode("\n", $TmpFileList);
  321. $Debug->set_flag('upload: torrent decoded');
  322. if (!empty($Err)) { // Show the upload form, with the data the user entered
  323. $UploadForm = $Type;
  324. include(SERVER_ROOT.'/sections/upload/upload.php');
  325. error();
  326. }
  327. //******************************************************************************//
  328. //--------------- Autofill format and archive ----------------------------------//
  329. if ($T['Container'] === 'Autofill') {
  330. # torrents.Container
  331. $T['Container'] = $Validate->ParseExtensions(
  332. # $FileList
  333. $Tor->file_list(),
  334. # $Category
  335. $T['CategoryName'],
  336. # $FileTypes
  337. $T['FileTypes'],
  338. );
  339. }
  340. if ($T['Archive'] === 'Autofill') {
  341. # torrents.Archive
  342. $T['Archive'] = $Validate->ParseExtensions(
  343. # $FileList
  344. $Tor->file_list(),
  345. # $Category
  346. array_keys($ENV->META->Formats->Archives),
  347. # $FileTypes
  348. array_merge($ENV->META->Formats->Archives),
  349. );
  350. }
  351. //******************************************************************************//
  352. //--------------- Start database stuff -----------------------------------------//
  353. $Body = $T['GroupDescription'];
  354. // Trickery
  355. if (!preg_match('/^'.IMAGE_REGEX.'$/i', $T['Image'])) {
  356. $T['Image'] = '';
  357. }
  358. // Does it belong in a group?
  359. if ($T['GroupID']) {
  360. $DB->prepare_query("
  361. SELECT
  362. `id`,
  363. `picture`,
  364. `description`,
  365. `revision_id`,
  366. `title`,
  367. `year`,
  368. `tag_list`
  369. FROM
  370. `torrents_group`
  371. WHERE
  372. `id` = $T[GroupID]
  373. ");
  374. $DB->exec_prepared_query();
  375. if ($DB->has_results()) {
  376. // Don't escape tg.title. It's written directly to the log table
  377. list($GroupID, $WikiImage, $WikiBody, $RevisionID, $T['Title'], $T['Year'], $T['TagList']) = $DB->next_record(MYSQLI_NUM, array(4));
  378. $T['TagList'] = str_replace(array(' ', '.', '_'), array(', ', '.', '.'), $T['TagList']);
  379. if (!$T['Image'] && $WikiImage) {
  380. $T['Image'] = $WikiImage;
  381. }
  382. if (strlen($WikiBody) > strlen($Body)) {
  383. $Body = $WikiBody;
  384. if (!$T['Image'] || $T['Image'] === $WikiImage) {
  385. $NoRevision = true;
  386. }
  387. }
  388. $T['Artist'] = Artists::display_artists(Artists::get_artist($GroupID), false, false);
  389. }
  390. }
  391. if (!isset($GroupID) || !$GroupID) {
  392. foreach ($ArtistForm as $Num => $Artist) {
  393. // The album hasn't been uploaded. Try to get the artist IDs
  394. $DB->query("
  395. SELECT
  396. ArtistID,
  397. Name
  398. FROM artists_group
  399. WHERE Name = ?", $Artist['name']);
  400. if ($DB->has_results()) {
  401. while (list($ArtistID, $Name) = $DB->next_record(MYSQLI_NUM, false)) {
  402. if (!strcasecmp($Artist['name'], $Name)) {
  403. $ArtistForm[$Num] = ['id' => $ArtistID, 'name' => $Name];
  404. break;
  405. }
  406. }
  407. }
  408. }
  409. }
  410. // Needs to be here as it isn't set for add format until now
  411. $LogName .= $T['Title'];
  412. // For notifications. Take note now whether it's a new group
  413. $IsNewGroup = !isset($GroupID) || !$GroupID;
  414. //----- Start inserts
  415. if ((!isset($GroupID) || !$GroupID)) {
  416. // Array to store which artists we have added already, to prevent adding an artist twice
  417. $ArtistsAdded = [];
  418. foreach ($ArtistForm as $Num => $Artist) {
  419. if (!isset($Artist['id']) || !$Artist['id']) {
  420. if (isset($ArtistsAdded[strtolower($Artist['name'])])) {
  421. $ArtistForm[$Num] = $ArtistsAdded[strtolower($Artist['name'])];
  422. } else {
  423. // Create artist
  424. $DB->query("
  425. INSERT INTO artists_group (Name)
  426. VALUES ( ? )", $Artist['name']);
  427. $ArtistID = $DB->inserted_id();
  428. $Cache->increment('stats_artist_count');
  429. $ArtistForm[$Num] = array('id' => $ArtistID, 'name' => $Artist['name']);
  430. $ArtistsAdded[strtolower($Artist['name'])] = $ArtistForm[$Num];
  431. }
  432. }
  433. }
  434. unset($ArtistsAdded);
  435. }
  436. if (!isset($GroupID) || !$GroupID) {
  437. // Create torrent group
  438. $DB->prepare_query(
  439. "
  440. INSERT INTO torrents_group
  441. (`category_id`, `title`, `subject`, `object`, `year`,
  442. `location`, `workgroup`, `identifier`, `timestamp`,
  443. `description`, `picture`)
  444. VALUES
  445. ( ?, ?, ?, ?, ?,
  446. ?, ?, ?, NOW(),
  447. ?, ? )",
  448. $TypeID,
  449. $T['Title'],
  450. $T['Title2'],
  451. $T['TitleJP'],
  452. $T['Year'],
  453. $T['Series'],
  454. $T['Studio'],
  455. $T['CatalogueNumber'],
  456. $Body,
  457. $T['Image']
  458. );
  459. $DB->exec_prepared_query();
  460. $GroupID = $DB->inserted_id();
  461. foreach ($ArtistForm as $Num => $Artist) {
  462. $DB->query("
  463. INSERT IGNORE INTO torrents_artists (GroupID, ArtistID, UserID)
  464. VALUES ( ?, ?, ? )", $GroupID, $Artist['id'], $LoggedUser['ID']);
  465. $Cache->increment('stats_album_count');
  466. $Cache->delete_value('artist_groups_'.$Artist['id']);
  467. }
  468. $Cache->increment('stats_group_count');
  469. // Add screenshots
  470. // todo: Clear DB_MYSQL::exec_prepared_query() errors
  471. $Screenshots = explode("\n", $T['Screenshots']);
  472. $Screenshots = array_map('trim', $Screenshots);
  473. $Screenshots = array_filter($Screenshots, function ($s) {
  474. return preg_match('/^'.$ENV->DOI_REGEX.'$/i', $s);
  475. });
  476. $Screenshots = array_unique($Screenshots);
  477. $Screenshots = array_slice($Screenshots, 0, 10);
  478. # Add optional web seeds similar to screenshots
  479. # Support an arbitrary and limited number of sources
  480. $Mirrors = explode("\n", $T['Mirrors']);
  481. $Mirrors = array_map('trim', $Mirrors);
  482. $Mirrors = array_filter($Mirrors, function ($s) {
  483. return preg_match('/^'.URL_REGEX.'$/i', $s);
  484. });
  485. $Mirrors = array_unique($Mirrors);
  486. $Mirrors = array_slice($Mirrors, 0, 2);
  487. # Downgrade TLS on resource URIs
  488. # Required for BEP 19 compatibility
  489. $Mirrors = str_ireplace('tps://', 'tp://', $Mirrors);
  490. # Perform the DB inserts here
  491. # Screenshots (Publications)
  492. if (!empty($Screenshots)) {
  493. $Screenshot = '';
  494. $DB->prepare_query("
  495. INSERT INTO torrents_doi
  496. (TorrentID, UserID, Time, URI)
  497. VALUES (?, ?, NOW(), ?)", $GroupID, $LoggedUser['ID'], $Screenshot);
  498. foreach ($Screenshots as $Screenshot) {
  499. $DB->exec_prepared_query();
  500. }
  501. }
  502. # Mirrors
  503. if (!empty($Mirrors)) {
  504. $Mirror = '';
  505. $DB->prepare_query("
  506. INSERT INTO torrents_mirrors
  507. (GroupID, UserID, Time, URI)
  508. VALUES (?, ?, NOW(), ?)", $GroupID, $LoggedUser['ID'], $Mirror);
  509. foreach ($Mirrors as $Mirror) {
  510. $DB->exec_prepared_query();
  511. }
  512. }
  513. # Main if/else
  514. } else {
  515. $DB->query("
  516. UPDATE torrents_group
  517. SET Time = NOW()
  518. WHERE ID = ?", $GroupID);
  519. $Cache->delete_value("torrent_group_$GroupID");
  520. $Cache->delete_value("torrents_details_$GroupID");
  521. $Cache->delete_value("detail_files_$GroupID");
  522. }
  523. // Description
  524. if (!isset($NoRevision) || !$NoRevision) {
  525. $DB->query("
  526. INSERT INTO wiki_torrents
  527. (PageID, Body, UserID, Summary, Time, Image)
  528. VALUES
  529. ( ?, ?, ?, 'Uploaded new torrent', NOW(), ? )", $GroupID, $T['GroupDescription'], $LoggedUser['ID'], $T['Image']);
  530. $RevisionID = $DB->inserted_id();
  531. // Revision ID
  532. $DB->prepare_query("
  533. UPDATE
  534. `torrents_group`
  535. SET
  536. `revision_id` = '$RevisionID'
  537. WHERE
  538. `id` = '$GroupID'
  539. ");
  540. $DB->exec_prepared_query();
  541. }
  542. // Tags
  543. $Tags = explode(',', $T['TagList']);
  544. if (!$T['GroupID']) {
  545. foreach ($Tags as $Tag) {
  546. $Tag = Misc::sanitize_tag($Tag);
  547. if (!empty($Tag)) {
  548. $Tag = Misc::get_alias_tag($Tag);
  549. $DB->query("
  550. INSERT INTO tags
  551. (Name, UserID)
  552. VALUES
  553. ( ?, ? )
  554. ON DUPLICATE KEY UPDATE
  555. Uses = Uses + 1;", $Tag, $LoggedUser['ID']);
  556. $TagID = $DB->inserted_id();
  557. $DB->query("
  558. INSERT INTO torrents_tags
  559. (TagID, GroupID, UserID)
  560. VALUES
  561. ( ?, ?, ? )
  562. ON DUPLICATE KEY UPDATE TagID=TagID", $TagID, $GroupID, $LoggedUser['ID']);
  563. }
  564. }
  565. }
  566. // Use this section to control freeleeches
  567. $T['FreeTorrent'] = '0';
  568. $T['FreeLeechType'] = '0';
  569. $DB->query("
  570. SELECT Name, First, Second
  571. FROM misc
  572. WHERE Second = 'freeleech'");
  573. if ($DB->has_results()) {
  574. $FreeLeechTags = $DB->to_array('Name');
  575. foreach ($FreeLeechTags as $Tag => $Exp) {
  576. if ($Tag === 'global' || in_array($Tag, $Tags)) {
  577. $T['FreeTorrent'] = '1';
  578. $T['FreeLeechType'] = '3';
  579. break;
  580. }
  581. }
  582. }
  583. // Torrents over a size in bytes are neutral leech
  584. // Download doesn't count, upload does
  585. if (($TotalSize > 10737418240)) { # 10 GiB
  586. $T['FreeTorrent'] = '2';
  587. $T['FreeLeechType'] = '2';
  588. }
  589. // Torrent
  590. $DB->query(
  591. "
  592. INSERT INTO torrents
  593. (GroupID, UserID, Media, Container, Codec, Resolution,
  594. Version, Censored,
  595. Anonymous, Archive, info_hash, FileCount, FileList, FilePath, Size, Time,
  596. Description, FreeTorrent, FreeLeechType)
  597. VALUES
  598. ( ?, ?, ?, ?, ?, ?,
  599. ?, ?,
  600. ?, ?, ?, ?, ?, ?, ?, NOW(),
  601. ?, ?, ? )",
  602. $GroupID,
  603. $LoggedUser['ID'],
  604. $T['Media'],
  605. $T['Container'],
  606. $T['Codec'],
  607. $T['Resolution'],
  608. $T['Version'],
  609. $T['Censored'],
  610. $T['Anonymous'],
  611. $T['Archive'],
  612. $InfoHash,
  613. $NumFiles,
  614. $FileString,
  615. $FilePath,
  616. $TotalSize,
  617. $T['TorrentDescription'],
  618. $T['FreeTorrent'],
  619. $T['FreeLeechType']
  620. );
  621. $TorrentID = $DB->inserted_id();
  622. $Cache->increment('stats_torrent_count');
  623. $Tor->Dec['comment'] = 'https://'.SITE_DOMAIN.'/torrents.php?torrentid='.$TorrentID;
  624. Tracker::update_tracker('add_torrent', [
  625. 'id' => $TorrentID,
  626. 'info_hash' => rawurlencode($InfoHash),
  627. 'freetorrent' => $T['FreeTorrent']
  628. ]);
  629. $Debug->set_flag('upload: ocelot updated');
  630. // Prevent deletion of this torrent until the rest of the upload process is done
  631. // (expire the key after 10 minutes to prevent locking it for too long in case there's a fatal error below)
  632. $Cache->cache_value("torrent_{$TorrentID}_lock", true, 600);
  633. // Give BP if necessary
  634. // todo: Repurpose this
  635. if (($Type === "Movies" || $Type === "Anime") && ($T['Container'] === 'ISO' || $T['Container'] === 'M2TS' || $T['Container'] === 'VOB IFO')) {
  636. $BPAmt = (int) 2*($TotalSize / (1024*1024*1024))*1000;
  637. $DB->query("
  638. UPDATE users_main
  639. SET BonusPoints = BonusPoints + ?
  640. WHERE ID = ?", $BPAmt, $LoggedUser['ID']);
  641. $DB->query("
  642. UPDATE users_info
  643. SET AdminComment = CONCAT(NOW(), ' - Received $BPAmt ".BONUS_POINTS." for uploading a torrent $TorrentID\n\n', AdminComment)
  644. WHERE UserID = ?", $LoggedUser['ID']);
  645. $Cache->delete_value('user_info_heavy_'.$LoggedUser['ID']);
  646. $Cache->delete_value('user_stats_'.$LoggedUser['ID']);
  647. }
  648. // Add to shop freeleeches if necessary
  649. if ($T['FreeLeechType'] === 3) {
  650. // Figure out which duration to use
  651. $Expiry = 0;
  652. foreach ($FreeLeechTags as $Tag => $Exp) {
  653. if ($Tag === 'global' || in_array($Tag, $Tags)) {
  654. if (((int) $FreeLeechTags[$Tag]['First']) > $Expiry) {
  655. $Expiry = (int) $FreeLeechTags[$Tag]['First'];
  656. }
  657. }
  658. }
  659. if ($Expiry > 0) {
  660. $DB->query("
  661. INSERT INTO shop_freeleeches
  662. (TorrentID, ExpiryTime)
  663. VALUES
  664. (" . $TorrentID . ", FROM_UNIXTIME(" . $Expiry . "))
  665. ON DUPLICATE KEY UPDATE
  666. ExpiryTime = FROM_UNIXTIME(UNIX_TIMESTAMP(ExpiryTime) + ($Expiry - FROM_UNIXTIME(NOW())))");
  667. } else {
  668. Torrents::freeleech_torrents($TorrentID, 0, 0);
  669. }
  670. }
  671. //******************************************************************************//
  672. //--------------- Write torrent file -------------------------------------------//
  673. $FileName = "$ENV->TORRENT_STORE/$TorrentID.torrent";
  674. file_put_contents($FileName, $Tor->encode());
  675. chmod($FileName, 0400);
  676. Misc::write_log("Torrent $TorrentID ($LogName) (".number_format($TotalSize / (1024 * 1024), 2).' MB) was uploaded by ' . $LoggedUser['Username']);
  677. Torrents::write_group_log($GroupID, $TorrentID, $LoggedUser['ID'], 'uploaded ('.number_format($TotalSize / (1024 * 1024), 2).' MB)', 0);
  678. Torrents::update_hash($GroupID);
  679. $Debug->set_flag('upload: sphinx updated');
  680. //******************************************************************************//
  681. //---------------------- Recent Uploads ----------------------------------------//
  682. if (trim($T['Image']) !== '') {
  683. $RecentUploads = $Cache->get_value("recent_uploads_$UserID");
  684. if (is_array($RecentUploads)) {
  685. do {
  686. foreach ($RecentUploads as $Item) {
  687. if ($Item['ID'] === $GroupID) {
  688. break 2;
  689. }
  690. }
  691. // Only reached if no matching GroupIDs in the cache already.
  692. if (count($RecentUploads) === 5) {
  693. array_pop($RecentUploads);
  694. }
  695. array_unshift($RecentUploads, array(
  696. 'ID' => $GroupID,
  697. 'Name' => trim($T['Title']),
  698. 'Artist' => Artists::display_artists($ArtistForm, false, true),
  699. 'WikiImage' => trim($T['Image'])));
  700. $Cache->cache_value("recent_uploads_$UserID", $RecentUploads, 0);
  701. } while (0);
  702. }
  703. }
  704. /**
  705. * Post-processing
  706. *
  707. * Because tracker updates and notifications can be slow, we're redirecting the user to the destination page
  708. * and flushing the buffers to make it seem like the PHP process is working in the background.
  709. */
  710. if ($PublicTorrent) {
  711. View::show_header('Warning'); ?>
  712. <h1>Warning</h1>
  713. <p>
  714. <strong>Your torrent has been uploaded but you must re-download your torrent file from
  715. <a
  716. href="torrents.php?id=<?=$GroupID?>&torrentid=<?=$TorrentID?>">here</a>
  717. because the site modified it to make it private.</strong>
  718. </p>
  719. <?php
  720. View::show_footer();
  721. } elseif ($UnsourcedTorrent) {
  722. View::show_header('Warning'); ?>
  723. <h1>Warning</h1>
  724. <p>
  725. <strong>Your torrent has been uploaded but you must re-download your torrent file from
  726. <a
  727. href="torrents.php?id=<?=$GroupID?>&torrentid=<?=$TorrentID?>">here</a>
  728. because the site modified it to add a source flag.</strong>
  729. </p>
  730. <?php
  731. View::show_footer();
  732. } elseif ($RequestID) {
  733. header("Location: requests.php?action=takefill&requestid=$RequestID&torrentid=$TorrentID&auth=".$LoggedUser['AuthKey']);
  734. } else {
  735. header("Location: torrents.php?id=$GroupID&torrentid=$TorrentID");
  736. }
  737. if (function_exists('fastcgi_finish_request')) {
  738. fastcgi_finish_request();
  739. } else {
  740. ignore_user_abort(true);
  741. ob_flush();
  742. flush();
  743. ob_start(); // So we don't keep sending data to the client
  744. }
  745. /**
  746. * IRC announce and feeds
  747. */
  748. $Announce = '';
  749. # Category and title
  750. $Announce .= '['.$T['CategoryName'].'] ';
  751. $Announce .= substr(trim(empty($T['Title']) ? (empty($T['Title2']) ? $T['TitleJP'] : $T['Title2']) : $T['Title']), 0, 100);
  752. $Announce .= ' ';
  753. # Exploded for sanity
  754. $Announce .= '[ '
  755. . Torrents::torrent_info(
  756. $Data = $T,
  757. $ShowMedia = true,
  758. $ShowEdition = false,
  759. $HTMLy = false
  760. )
  761. . ' ]';
  762. # Twitter stuff
  763. # Grab $Announce here
  764. /*
  765. $Tweet = $Announce;
  766. $TweetTags =
  767. implode(
  768. ' ',
  769. # Add hashtags
  770. preg_replace(
  771. '/^\s* /', # remove space in "* /"
  772. '#',
  773. # Trim raw explosion
  774. array_filter(
  775. explode(
  776. ',',
  777. # todo: Get validated output
  778. $T['TagList']
  779. ),
  780. 'trim'
  781. )
  782. )
  783. );
  784. $Tweet .= $TweetTags;
  785. */
  786. # Tags and link
  787. $Announce .= ' - '.trim($T['TagList']);
  788. $Announce .= ' - '.site_url()."torrents.php?id=$GroupID&torrentid=$TorrentID";
  789. $Announce .= ' - '.site_url()."torrents.php?action=download&id=$TorrentID";
  790. # Unused
  791. #$Title = '['.$T['CategoryName'].'] '.$Announce;
  792. #$Announce .= Artists::display_artists($ArtistForm, false);
  793. // ENT_QUOTES is needed to decode single quotes/apostrophes
  794. send_irc(ANNOUNCE_CHAN, html_entity_decode($Announce, ENT_QUOTES));
  795. $Debug->set_flag('upload: announced on irc');
  796. /**
  797. * Manage motifications
  798. */
  799. // For RSS
  800. $Item = $Feed->item(
  801. $Announce,
  802. Text::strip_bbcode($Body),
  803. 'torrents.php?action=download&amp;authkey=[[AUTHKEY]]&amp;torrent_pass=[[PASSKEY]]&amp;id='.$TorrentID,
  804. $Properties['Anonymous'] ? 'Anonymous' : $LoggedUser['Username'],
  805. 'torrents.php?id='.$GroupID,
  806. trim($T['TagList'])
  807. );
  808. // Notifications
  809. $SQL = "
  810. SELECT
  811. unf.`ID`,
  812. unf.`UserID`,
  813. `torrent_pass`
  814. FROM
  815. `users_notify_filters` AS unf
  816. JOIN `users_main` AS um
  817. ON
  818. um.`ID` = unf.`UserID`
  819. WHERE
  820. um.`Enabled` = '1'
  821. ";
  822. # Creators
  823. if (empty($ArtistsUnescaped)) {
  824. $ArtistsUnescaped = $ArtistForm;
  825. }
  826. if (!empty($ArtistsUnescaped)) {
  827. $ArtistNameList = [];
  828. $GuestArtistNameList = [];
  829. foreach ($ArtistsUnescaped as $Importance => $Artist) {
  830. $ArtistNameList[] = "Artists LIKE '%|".db_string(str_replace('\\', '\\\\', $Artist['name']), true)."|%'";
  831. }
  832. $SQL .= " AND (";
  833. if (count($ArtistNameList) > 0) {
  834. $SQL .= implode(' OR ', $ArtistNameList);
  835. $SQL .= " OR ";
  836. }
  837. $SQL .= "Artists = '') AND (";
  838. } else {
  839. $SQL .= "AND (Artists = '') AND (";
  840. }
  841. # Tags
  842. reset($Tags);
  843. $TagSQL = [];
  844. $NotTagSQL = [];
  845. foreach ($Tags as $Tag) {
  846. $TagSQL[] = " Tags LIKE '%|".db_string(trim($Tag))."|%' ";
  847. $NotTagSQL[] = " NotTags LIKE '%|".db_string(trim($Tag))."|%' ";
  848. }
  849. $TagSQL[] = "Tags = ''";
  850. $SQL .= implode(' OR ', $TagSQL);
  851. $SQL .= ") AND !(".implode(' OR ', $NotTagSQL).')';
  852. $SQL .= " AND (Categories LIKE '%|".db_string(trim($Type))."|%' OR Categories = '') ";
  853. /*
  854. Notify based on the following:
  855. 1. The torrent must match the formatbitrate filter on the notification
  856. 2. If they set NewGroupsOnly to 1, it must also be the first torrent in the group to match the formatbitrate filter on the notification
  857. */
  858. if ($T['Format']) {
  859. $SQL .= " AND (Formats LIKE '%|".db_string(trim($T['Format']))."|%' OR Formats = '') ";
  860. } else {
  861. $SQL .= " AND (Formats = '') ";
  862. }
  863. if ($T['Media']) {
  864. $SQL .= " AND (Media LIKE '%|".db_string(trim($T['Media']))."|%' OR Media = '') ";
  865. } else {
  866. $SQL .= " AND (Media = '') ";
  867. }
  868. // Either they aren't using NewGroupsOnly
  869. $SQL .= "AND ((NewGroupsOnly = '0' ";
  870. // Or this is the first torrent in the group to match the formatbitrate filter
  871. $SQL .= ") OR ( NewGroupsOnly = '1' ";
  872. $SQL .= '))';
  873. if ($T['Year']) {
  874. $SQL .= " AND (('".db_string(trim($T['Year']))."' BETWEEN FromYear AND ToYear)
  875. OR (FromYear = 0 AND ToYear = 0)) ";
  876. } else {
  877. $SQL .= " AND (FromYear = 0 AND ToYear = 0) ";
  878. }
  879. $SQL .= " AND UserID != '".$LoggedUser['ID']."' ";
  880. $DB->query("
  881. SELECT
  882. `Paranoia`
  883. FROM
  884. `users_main`
  885. WHERE
  886. `ID` = $LoggedUser[ID]
  887. ");
  888. list($Paranoia) = $DB->next_record();
  889. $Paranoia = unserialize($Paranoia);
  890. if (!is_array($Paranoia)) {
  891. $Paranoia = [];
  892. }
  893. if (!in_array('notifications', $Paranoia)) {
  894. $SQL .= " AND (Users LIKE '%|".$LoggedUser['ID']."|%' OR Users = '') ";
  895. }
  896. $SQL .= " AND UserID != '".$LoggedUser['ID']."' ";
  897. $DB->query($SQL);
  898. $Debug->set_flag('upload: notification query finished');
  899. if ($DB->has_results()) {
  900. $UserArray = $DB->to_array('UserID');
  901. $FilterArray = $DB->to_array('ID');
  902. $InsertSQL = '
  903. INSERT IGNORE INTO `users_notify_torrents` (`UserID`, `GroupID`, `TorrentID`, `FilterID`)
  904. VALUES ';
  905. $Rows = [];
  906. foreach ($UserArray as $User) {
  907. list($FilterID, $UserID, $Passkey) = $User;
  908. $Rows[] = "('$UserID', '$GroupID', '$TorrentID', '$FilterID')";
  909. $Feed->populate("torrents_notify_$Passkey", $Item);
  910. $Cache->delete_value("notifications_new_$UserID");
  911. }
  912. $InsertSQL .= implode(',', $Rows);
  913. $DB->query($InsertSQL);
  914. $Debug->set_flag('upload: notification inserts finished');
  915. foreach ($FilterArray as $Filter) {
  916. list($FilterID, $UserID, $Passkey) = $Filter;
  917. $Feed->populate("torrents_notify_{$FilterID}_$Passkey", $Item);
  918. }
  919. }
  920. // RSS for bookmarks
  921. $DB->query("
  922. SELECT
  923. u.`ID`,
  924. u.`torrent_pass`
  925. FROM
  926. `users_main` AS u
  927. JOIN `bookmarks_torrents` AS b
  928. ON
  929. b.`UserID` = u.`ID`
  930. WHERE
  931. b.`GroupID` = '$GroupID'
  932. ");
  933. while (list($UserID, $Passkey) = $DB->next_record()) {
  934. $Feed->populate("torrents_bookmarks_t_$Passkey", $Item);
  935. }
  936. $Feed->populate('torrents_all', $Item);
  937. $Feed->populate('torrents_'.strtolower($Type), $Item);
  938. $Debug->set_flag('upload: notifications handled');
  939. # Clear cache
  940. $Cache->delete_value("torrents_details_$GroupID");
  941. $Cache->delete_value("contest_scores");
  942. # Allow deletion of this torrent now
  943. $Cache->delete_value("torrent_{$TorrentID}_lock");