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

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