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

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