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 32KB

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