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

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