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

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