diff --git a/classes/config.template b/classes/config.template
index 71080bb0..e5623846 100644
--- a/classes/config.template
+++ b/classes/config.template
@@ -104,6 +104,11 @@ define('LOG_ENTRIES_PER_PAGE', 50);
// Cache catalogues
define('THREAD_CATALOGUE', 500); // Limit to THREAD_CATALOGUE posts per cache key.
+// Email delivery method and information
+define('EMAIL_DELIVERY_TYPE', ''); // should be either 'mailgun' to use mailgun services or 'local' to use a local SMTP server or relay
+define('MAILGUN_API_KEY', '');
+define('MAILGUN_API_URL', '');
+
// IRC settings
define('BOT_NICK', '');
define('BOT_SERVER', ''); // IRC server address. Used for onsite chat tool.
diff --git a/classes/irc.class.php b/classes/irc.class.php
index d6e1af41..6debcea8 100644
--- a/classes/irc.class.php
+++ b/classes/irc.class.php
@@ -180,46 +180,6 @@ protected function listen() {
}
}
- if (preg_match("/:([^!]+)![^\s]* QUIT.* /", $this->Data, $Nick)) {
- if (isset($this->Identified[$Nick[1]])) {
- unset($this->Identified[$Nick[1]]);
- }
- if (isset($this->DisabledUsers[$Nick[1]])) {
- if ($this->DisabledUsers[$Nick[1]]['UserID'] != 0) {
- G::$DB->query("
- DELETE FROM disable_list
- WHERE Nick = '$Nick[1]'");
- G::$Cache->decrement_value('num_disablees');
- }
- unset($this->DisabledUsers[$Nick[1]]);
- }
- }
-
- if (preg_match("/:([^!]+)![^\s]* PART ".BOT_DISABLED_CHAN.'/', $this->Data, $Nick)) {
- if (isset($this->DisabledUsers[$Nick[1]])) {
- if ($this->DisabledUsers[$Nick[1]]['UserID'] != 0) {
- G::$DB->query("
- DELETE FROM disable_list
- WHERE Nick = '$Nick[1]'");
- G::$Cache->decrement_value('num_disablees');
- }
- unset($this->DisabledUsers[$Nick[1]]);
- }
- }
-
- if (preg_match("/:([^!]+)![^\s]* KICK ".BOT_DISABLED_CHAN.'.* /', $this->Data, $Nick)) {
- $Nick = explode(' ', $Nick[0]);
- if (isset($this->DisabledUsers[$Nick[3]])) {
- if ($this->DisabledUsers[$Nick[3]]['UserID'] != 0) {
- G::$DB->query("
- DELETE FROM disable_list
- WHERE Nick = '$Nick[3]'");
- G::$Cache->decrement_value('num_disablees');
- }
- unset($this->DisabledUsers[$Nick[3]]);
- }
- }
-
if (preg_match('/End of message of the day./', $this->Data)) {
$this->connect_events();
}
diff --git a/classes/misc.class.php b/classes/misc.class.php
index fb104ef4..fa2f4eb4 100644
--- a/classes/misc.class.php
+++ b/classes/misc.class.php
@@ -1,24 +1,67 @@
class Misc {
- /**
- * Send an email.
- *
- * @param string $To the email address to send it to.
- * @param string $Subject
- * @param string $Body
- * @param string $From The user part of the user@NONSSL_SITE_URL email address.
- * @param string $ContentType text/plain or text/html
- */
- public static function send_email($To, $Subject, $Body, $From = 'noreply', $ContentType = 'text/plain') {
- $Headers = 'MIME-Version: 1.0'."\r\n";
- $Headers .= 'Content-type: '.$ContentType.'; charset=iso-8859-1'."\r\n";
- $Headers .= 'From: '.SITE_NAME.' <'.$From.'@'.NONSSL_SITE_URL.'>'."\r\n";
- $Headers .= 'Reply-To: '.$From.'@'.NONSSL_SITE_URL."\r\n";
- $Headers .= 'X-Mailer: Project Gazelle'."\r\n";
- $Headers .= 'Message-Id: <'.Users::make_secret().'@'.NONSSL_SITE_URL.">\r\n";
- $Headers .= 'X-Priority: 3'."\r\n";
- mail($To, $Subject, $Body, $Headers, "-f $From@".NONSSL_SITE_URL);
- }
+ /**
+ * Send an email.
+ *
+ * We can do this one of two ways - either using MailGun or with PHP's mail function.
+ * Checks for EMAIL_DELIVERY_TYPE and then proceeds as directed to send e-mail.
+ *
+ * @param string $To the email address to send it to.
+ * @param string $Subject
+ * @param string $Body
+ * @param string $From The user part of the user@NONSSL_SITE_URL email address.
+ * @param string $ContentType text/plain or text/html
+ */
+
+ public static function send_email($To, $Subject, $Body, $From, $ContentType) {
+
+ switch (EMAIL_DELIVERY_TYPE) {
+ case 'local':
+ // remove the next line if you want to send HTML email from some places...
+ $ContentType='text/plain';
+ $Headers = 'MIME-Version: 1.0'."\r\n";
+ $Headers .= 'Content-type: '.$ContentType.'; charset=iso-8859-1'."\r\n";
+ $Headers .= 'From: '.SITE_NAME.' <'.$From.'@'.NONSSL_SITE_URL.'>'."\r\n";
+ $Headers .= 'Reply-To: '.$From.'@'.NONSSL_SITE_URL."\r\n";
+ $Headers .= 'X-Mailer: Project Gazelle'."\r\n";
+ $Headers .= 'Message-Id: <'.Users::make_secret().'@'.NONSSL_SITE_URL.">\r\n";
+ $Headers .= 'X-Priority: 3'."\r\n";
+ mail($To, $Subject, $Body, $Headers, "-f $From@".NONSSL_SITE_URL);
+ break;
+
+ case 'mailgun':
+ // set up our message first
+ $From .= '@'.NONSSL_SITE_URL;
+ $OutgoingEmail = array(
+ 'from' => $From,
+ 'to' => $To,
+ 'h:Reply-To' => $From,
+ 'subject' => $Subject,
+ 'text' => $Body);
+ // now let's POST it to mailgun
+ $Curl = curl_init();
+ curl_setopt($Curl, CURLOPT_URL, MAILGUN_API_URL);
+ curl_setopt($Curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ curl_setopt($Curl, CURLOPT_USERPWD, 'api:'.MAILGUN_API_KEY);
+ curl_setopt($Curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($Curl, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($Curl, CURLOPT_POST, true);
+ curl_setopt($Curl, CURLOPT_POSTFIELDS, $OutgoingEmail);
+
+ $RequestResult = curl_exec($Curl);
+ $RequestStatusCode = curl_getinfo($Curl, CURLINFO_HTTP_CODE);
+ curl_close($Curl);
+ // alert on failed emails
+ if ($RequestStatusCode != 200) {
+ send_irc('PRIVMSG '.STATUS_CHAN." !dev email failed to $To with error message $RequestResult");
+ }
+ break;
+
+ default:
+ die('You have either not configured an email delivery method in config.php or your value is incorrect.');
+ break;
+ }
+ }
/**
diff --git a/classes/sphinxql.class.php b/classes/sphinxql.class.php
index 51f9b888..a4cb7038 100644
--- a/classes/sphinxql.class.php
+++ b/classes/sphinxql.class.php
@@ -110,12 +110,13 @@ public function error($Msg, $Halt = false) {
/**
* Escape special characters before sending them to the Sphinx server.
* Two escapes needed because the first one is eaten up by the mysql driver.
+ * Lowercase ASCII characters because some Sphinx operators are all caps words.
*
* @param string $String string to escape
* @return escaped string
*/
public static function sph_escape_string($String) {
- return strtr($String, array(
+ return strtr(strtolower($String), array(
'('=>'\\\\(',
')'=>'\\\\)',
'|'=>'\\\\|',
diff --git a/classes/sphinxqlquery.class.php b/classes/sphinxqlquery.class.php
index b6858193..b03f8e91 100644
--- a/classes/sphinxqlquery.class.php
+++ b/classes/sphinxqlquery.class.php
@@ -30,7 +30,7 @@ public function __construct($Server = SPHINXQL_HOST, $Port = SPHINXQL_PORT, $Soc
* Specify what data the Sphinx query is supposed to return
*
* @param string $Fields Attributes and expressions
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function select($Fields) {
$this->Select = $Fields;
@@ -41,7 +41,7 @@ public function select($Fields) {
* Specify the indexes to use in the search
*
* @param string $Indexes comma separated list of indexes
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function from($Indexes) {
$this->Indexes = $Indexes;
@@ -54,7 +54,7 @@ public function from($Indexes) {
* @param string $Attribute attribute which the filter will apply to
* @param mixed $Values scalar or array of numerical values. Array uses boolean OR in query condition
* @param bool $Exclude whether to exclude or include matching documents. Default mode is to include matches
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function where($Attribute, $Values, $Exclude = false) {
if (empty($Attribute) || !isset($Values)) {
@@ -95,7 +95,7 @@ public function where($Attribute, $Values, $Exclude = false) {
* @param string $Attribute attribute which the filter will apply to
* @param array $Value upper limit for matches
* @param bool $Inclusive whether to use <= or <
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function where_lt($Attribute, $Value, $Inclusive = false) {
if (empty($Attribute) || !isset($Value) || !is_number($Value)) {
@@ -112,7 +112,7 @@ public function where_lt($Attribute, $Value, $Inclusive = false) {
* @param string $Attribute attribute which the filter will apply to
* @param array $Value lower limit for matches
* @param bool $Inclusive whether to use >= or >
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function where_gt($Attribute, $Value, $Inclusive = false) {
if (empty($Attribute) || !isset($Value) || !is_number($Value)) {
@@ -128,7 +128,7 @@ public function where_gt($Attribute, $Value, $Inclusive = false) {
*
* @param string $Attribute attribute which the filter will apply to
* @param array $Values pair of numerical values that defines the filter range
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function where_between($Attribute, $Values) {
if (empty($Attribute) || empty($Values) || count($Values) != 2 || !is_number($Values[0]) || !is_number($Values[1])) {
@@ -145,7 +145,7 @@ public function where_between($Attribute, $Values) {
*
* @param string $Expr query expression
* @param string $Field field to match $Expr against. Default is *, which means all available fields
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function where_match($Expr, $Field = '*', $Escape = true) {
if (empty($Expr)) {
@@ -168,7 +168,7 @@ public function where_match($Expr, $Field = '*', $Escape = true) {
* @param string $Attribute attribute to use for sorting.
* Passing an empty attribute value will clear the current sort settings
* @param string $Mode sort method to apply to the selected attribute
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function order_by($Attribute = false, $Mode = false) {
if (empty($Attribute)) {
@@ -184,7 +184,7 @@ public function order_by($Attribute = false, $Mode = false) {
*
* @param string $Attribute group matches with the same $Attribute value.
* Passing an empty attribute value will clear the current group settings
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function group_by($Attribute = false) {
if (empty($Attribute)) {
@@ -201,7 +201,7 @@ public function group_by($Attribute = false) {
* @param string $Attribute attribute to use for sorting.
* Passing an empty attribute will clear the current group sort settings
* @param string $Mode sort method to apply to the selected attribute
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function order_group_by($Attribute = false, $Mode = false) {
if (empty($Attribute)) {
@@ -218,7 +218,7 @@ public function order_group_by($Attribute = false, $Mode = false) {
* @param int $Offset number of matches to discard
* @param int $Limit number of matches to return
* @param int $MaxMatches number of results to store in the Sphinx server's memory. Must be >= ($Offset+$Limit)
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) {
$this->Limits = "$Offset, $Limit";
@@ -231,7 +231,7 @@ public function limit($Offset, $Limit, $MaxMatches = SPHINX_MAX_MATCHES) {
*
* @param string $Name setting name
* @param mixed $Value value
- * @return current Sphinxql query object
+ * @return current SphinxqlQuery object
*/
public function set($Name, $Value) {
$this->Options[$Name] = $Value;
@@ -265,6 +265,7 @@ private function build_query() {
}
if (!empty($this->Filters)) {
$this->QueryString .= "\nWHERE ".implode("\n\tAND ", $this->Filters);
+ unset($this->Filters['expr']);
}
if (!empty($this->GroupBy)) {
$this->QueryString .= "\nGROUP BY $this->GroupBy";
@@ -288,7 +289,7 @@ private function build_query() {
* Construct and send the query. Register the query in the global Sphinxql object
*
* @param bool GetMeta whether to fetch meta data for the executed query. Default is yes
- * @return Sphinxql result object
+ * @return SphinxqlResult object
*/
public function query($GetMeta = true) {
$QueryStartTime = microtime(true);
@@ -310,7 +311,7 @@ public function query($GetMeta = true) {
*
* @param string Query query expression
* @param bool GetMeta whether to fetch meta data for the executed query. Default is yes
- * @return Sphinxql result object
+ * @return SphinxqlResult object
*/
public function raw_query($Query, $GetMeta = true) {
$this->QueryString = $Query;
@@ -321,7 +322,7 @@ public function raw_query($Query, $GetMeta = true) {
* Run a pre-processed query. Only used internally
*
* @param bool GetMeta whether to fetch meta data for the executed query
- * @return Sphinxql result object
+ * @return SphinxqlResult object
*/
private function send_query($GetMeta) {
if (!$this->QueryString) {
@@ -368,6 +369,16 @@ private function get_meta() {
return $this->raw_query("SHOW META", false)->to_pair(0, 1);
}
+ /**
+ * Copy attribute filters from another SphinxqlQuery object
+ *
+ * @param SphinxqlQuery $SphQLSource object to copy the filters from
+ * @return current SphinxqlQuery object
+ */
+ public function copy_attributes_from($SphQLSource) {
+ $this->Filters = $SphQLSource->Filters;
+ }
+
/**
* Store error messages
*/
diff --git a/classes/tags.class.php b/classes/tags.class.php
index 8ab798c1..efa8b89b 100644
--- a/classes/tags.class.php
+++ b/classes/tags.class.php
@@ -197,30 +197,34 @@ public static function get_aliases() {
public static function remove_aliases($Tags) {
$TagAliases = self::get_aliases();
- $End = count($Tags['include']);
- for ($i = 0; $i < $End; $i++) {
- foreach ($TagAliases as $TagAlias) {
- if ($Tags['include'][$i] === $TagAlias['BadTag']) {
- $Tags['include'][$i] = $TagAlias['AliasTag'];
- break;
+ if (isset($Tags['include'])) {
+ $End = count($Tags['include']);
+ for ($i = 0; $i < $End; $i++) {
+ foreach ($TagAliases as $TagAlias) {
+ if ($Tags['include'][$i] === $TagAlias['BadTag']) {
+ $Tags['include'][$i] = $TagAlias['AliasTag'];
+ break;
+ }
}
}
+ // Only keep unique entries after unifying tag standard
+ $Tags['include'] = array_unique($Tags['include']);
}
- $End = count($Tags['exclude']);
- for ($i = 0; $i < $End; $i++) {
- foreach ($TagAliases as $TagAlias) {
- if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) {
- $Tags['exclude'][$i] = '!'.$TagAlias['AliasTag'];
- break;
+ if (isset($Tags['exclude'])) {
+ $End = count($Tags['exclude']);
+ for ($i = 0; $i < $End; $i++) {
+ foreach ($TagAliases as $TagAlias) {
+ if (substr($Tags['exclude'][$i], 1) === $TagAlias['BadTag']) {
+ $Tags['exclude'][$i] = '!'.$TagAlias['AliasTag'];
+ break;
+ }
}
}
+ // Only keep unique entries after unifying tag standard
+ $Tags['exclude'] = array_unique($Tags['exclude']);
}
- // Only keep unique entries after unifying tag standard
- $Tags['include'] = array_unique($Tags['include']);
- $Tags['exclude'] = array_unique($Tags['exclude']);
-
return $Tags;
}
diff --git a/classes/tools.class.php b/classes/tools.class.php
index 435dd3a1..8505ba76 100644
--- a/classes/tools.class.php
+++ b/classes/tools.class.php
@@ -21,7 +21,6 @@ public static function site_ban_ip($IP) {
G::$DB->set_query_id($QueryID);
G::$Cache->cache_value('ip_bans_'.$A, $IPBans, 0);
}
- $Debug->log_var($IPBans, 'IP bans for class '.$A);
foreach ($IPBans as $Index => $IPBan) {
list ($ID, $FromIP, $ToIP) = $IPBan;
if ($IPNum >= $FromIP && $IPNum <= $ToIP) {
@@ -104,9 +103,21 @@ public static function get_host_by_ip($IP) {
* @return a span with JavaScript code
*/
public static function get_host_by_ajax($IP) {
- static $ID = 0;
- ++$ID;
- return 'Resolving host...';
+ static $IPs = array();
+ $Class = strtr($IP, '.', '-');
+ $HTML = 'Resolving host...';
+ if (!isset($IPs[$IP])) {
+ $HTML .= '';
+ }
+ $HTML .= '';
+ $IPs[$IP] = 1;
+ return $HTML;
}
@@ -146,12 +157,23 @@ public static function display_ip($IP) {
}
public static function get_country_code_by_ajax($IP) {
- static $ID = 0;
- ++$ID;
- return 'Resolving CC...';
+ static $IPs = array();
+ $Class = strtr($IP, '.', '-');
+ $HTML = 'Resolving CC...';
+ if (!isset($IPs[$IP])) {
+ $HTML .= '';
+ }
+ $HTML .= '';
+ $IPs[$IP] = 1;
+ return $HTML;
}
-
/**
* Disable an array of users.
*
diff --git a/classes/top10view.class.php b/classes/top10view.class.php
index c05f36b0..681e7bce 100644
--- a/classes/top10view.class.php
+++ b/classes/top10view.class.php
@@ -11,6 +11,7 @@ public static function render_linkbox($Selected) {
=self::get_selected_link("Tags", $Selected == "tags")?>
=self::get_selected_link("Favorites", $Selected == "votes")?>
=self::get_selected_link("Donors", $Selected == "donors")?>
+
}
diff --git a/classes/torrentsearch.class.php b/classes/torrentsearch.class.php
new file mode 100644
index 00000000..d5dec07e
--- /dev/null
+++ b/classes/torrentsearch.class.php
@@ -0,0 +1,769 @@
+
+class TorrentSearch {
+ const TAGS_ANY = 0;
+ const TAGS_ALL = 1;
+ const SPH_BOOL_AND = ' ';
+ const SPH_BOOL_OR = ' | ';
+
+ /**
+ * Map of sort mode => attribute name for ungrouped torrent page
+ */
+ public static $SortOrders = array(
+ 'year' => 'year',
+ 'time' => 'id',
+ 'size' => 'size',
+ 'seeders' => 'seeders',
+ 'leechers' => 'leechers',
+ 'snatched' => 'snatched',
+ 'random' => 1);
+
+ /**
+ * Map of sort mode => attribute name for grouped torrent page
+ */
+ private static $SortOrdersGrouped = array(
+ 'year' => 'year',
+ 'time' => 'id',
+ 'size' => 'maxsize',
+ 'seeders' => 'sumseeders',
+ 'leechers' => 'sumleechers',
+ 'snatched' => 'sumsnatched',
+ 'random' => 1);
+
+ /**
+ * Map of sort mode => aggregate expression required for some grouped sort orders
+ */
+ private static $AggregateExp = array(
+ 'size' => 'MAX(size) AS maxsize',
+ 'seeders' => 'SUM(seeders) AS sumseeders',
+ 'leechers' => 'SUM(leechers) AS sumleechers',
+ 'snatched' => 'SUM(snatched) AS sumsnatched');
+
+ /**
+ * Map of attribute name => global variable name with list of values that can be used for filtering
+ */
+ private static $Attributes = array(
+ 'filter_cat' => false,
+ 'releasetype' => 'ReleaseTypes',
+ 'freetorrent' => false,
+ 'hascue' => false,
+ 'haslog' => false,
+ 'scene' => false,
+ 'vanityhouse' => false,
+ 'year' => false);
+
+ /**
+ * List of fields that can be used for fulltext searches
+ */
+ private static $Fields = array(
+ 'artistname' => 1,
+ 'cataloguenumber' => 1,
+ 'description' => 1,
+ 'encoding' => 1,
+ 'filelist' => 1,
+ 'format' => 1,
+ 'groupname' => 1,
+ 'media' => 1,
+ 'recordlabel' => 1,
+ 'remastercataloguenumber' => 1,
+ 'remasterrecordlabel' => 1,
+ 'remastertitle' => 1,
+ 'remasteryear' => 1,
+ 'searchstr' => 1,
+ 'taglist' => 1);
+
+ /**
+ * List of torrent-specific fields that can be used for filtering
+ */
+ private static $TorrentFields = array(
+ 'description' => 1,
+ 'encoding' => 1,
+ 'filelist' => 1,
+ 'format' => 1,
+ 'media' => 1,
+ 'remastercataloguenumber' => 1,
+ 'remasterrecordlabel' => 1,
+ 'remastertitle' => 1,
+ 'remasteryear' => 1);
+
+ /**
+ * Some form field names don't match the ones in the index
+ */
+ private static $FormsToFields = array(
+ 'searchstr' => '(groupname,artistname,yearfulltext)');
+
+ /**
+ * Specify the operator type to use for fields. Empty key sets the default
+ */
+ private static $FieldOperators = array(
+ '' => self::SPH_BOOL_AND,
+ 'encoding' => self::SPH_BOOL_OR,
+ 'format' => self::SPH_BOOL_OR,
+ 'media' => self::SPH_BOOL_OR);
+
+ /**
+ * Specify the separator character to use for fields. Empty key sets the default
+ */
+ private static $FieldSeparators = array(
+ '' => ' ',
+ 'encoding' => '|',
+ 'format' => '|',
+ 'media' => '|',
+ 'taglist' => ',');
+
+ /**
+ * Primary SphinxqlQuery object used to get group IDs or torrent IDs for ungrouped searches
+ */
+ private $SphQL;
+
+ /**
+ * Second SphinxqlQuery object used to get torrent IDs if torrent-specific fulltext filters are used
+ */
+ private $SphQLTor;
+
+ /**
+ * Ordered result array or false if query resulted in an error
+ */
+ private $SphResults;
+
+ /**
+ * Requested page
+ */
+ private $Page;
+
+ /**
+ * Number of results per page
+ */
+ private $PageSize;
+
+ /**
+ * Number of results
+ */
+ private $NumResults = 0;
+
+ /**
+ * Array with info from all matching torrent groups
+ */
+ private $Groups = array();
+
+ /**
+ * True if the NOT operator can be used. Sphinx needs at least one positive search condition
+ */
+ private $EnableNegation = false;
+
+ /**
+ * Whether any filters were used
+ */
+ private $Filtered = false;
+
+ /**
+ * Whether the random sort order is selected
+ */
+ private $Random = false;
+
+ /**
+ * Storage for fulltext search terms
+ * ['Field name' => [
+ * 'include' => [],
+ * 'exclude' => [],
+ * 'operator' => self::SPH_BOOL_AND | self::SPH_BOOL_OR
+ * ]], ...
+ */
+ private $Terms = array();
+
+ /**
+ * Unprocessed search terms for retrieval
+ */
+ private $RawTerms = array();
+
+ /**
+ * Storage for used torrent-specific attribute filters
+ * ['Field name' => 'Search expression', ...]
+ */
+ private $UsedTorrentAttrs = array();
+
+ /**
+ * Storage for used torrent-specific fulltext fields
+ * ['Field name' => 'Search expression', ...]
+ */
+ private $UsedTorrentFields = array();
+
+ /**
+ * Initialize and configure a TorrentSearch object
+ *
+ * @param bool $GroupResults whether results should be grouped by group id
+ * @param string $OrderBy attribute to use for sorting the results
+ * @param string $OrderWay Whether to use ascending or descending order
+ * @param int $Page Page number to display
+ * @param int $PageSize Number of results per page
+ */
+ public function __construct($GroupResults, $OrderBy, $OrderWay, $Page, $PageSize) {
+ if ($GroupResults && !isset(self::$SortOrdersGrouped[$OrderBy])
+ || !$GroupResults && !isset(self::$SortOrders[$OrderBy])
+ || !in_array($OrderWay, array('asc', 'desc'))
+ ) {
+ global $Debug;
+ $ErrMsg = "TorrentSearch constructor arguments:\n" . print_r(func_get_args(), true);
+ $Debug->analysis('Bad arguments in TorrentSearch constructor', $ErrMsg, 3600*24);
+ error('-1');
+ }
+ if (!is_number($Page) || $Page < 1) {
+ $Page = 1;
+ }
+ if (check_perms('site_search_many')) {
+ $this->Page = $Page;
+ } else {
+ $this->Page = min($Page, SPHINX_MAX_MATCHES / $PageSize);
+ }
+ $ResultLimit = $PageSize;
+ $this->PageSize = $PageSize;
+ $this->GroupResults = $GroupResults;
+ $this->SphQL = new SphinxqlQuery();
+ if ($OrderBy === 'random') {
+ $this->SphQL->select('id, groupid')
+ ->order_by('RAND()', '');
+ $this->Random = true;
+ $this->Page = 1;
+ if ($GroupResults) {
+ // Get more results because ORDER BY RAND() can't be used in GROUP BY queries
+ $ResultLimit *= 5;
+ }
+ } elseif ($GroupResults) {
+ $Select = 'groupid';
+ if (isset(self::$AggregateExp[$OrderBy])) {
+ $Select .= ', ' . self::$AggregateExp[$OrderBy];
+ }
+ $this->SphQL->select($Select)
+ ->group_by('groupid')
+ ->order_group_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay)
+ ->order_by(self::$SortOrdersGrouped[$OrderBy], $OrderWay);
+ } else {
+ $this->SphQL->select('id, groupid')
+ ->order_by(self::$SortOrders[$OrderBy], $OrderWay);
+ }
+ $Offset = ($this->Page - 1) * $ResultLimit;
+ $MaxMatches = $Offset + $ResultLimit;
+ $this->SphQL->from('torrents, delta')
+ ->limit($Offset, $ResultLimit, $MaxMatches);
+ }
+
+ /**
+ * Process search terms and run the main query
+ *
+ * @param array $Terms Array containing all search terms (e.g. $_GET)
+ * @return array List of matching group IDs with torrent ID as key for ungrouped results
+ */
+ public function query($Terms = array()) {
+ $this->process_search_terms($Terms);
+ $this->build_query();
+ $this->run_query();
+ $this->process_results();
+ return $this->SphResults;
+ }
+
+ /**
+ * Internal function that runs the queries needed to get the desired results
+ */
+ private function run_query() {
+ $SphQLResult = $this->SphQL->query();
+ if ($SphQLResult->Errno > 0) {
+ $this->SphResults = false;
+ return;
+ }
+ if ($this->Random && $this->GroupResults) {
+ $TotalCount = $SphQLResult->get_meta('total_found');
+ $this->SphResults = $SphQLResult->collect('groupid');
+ $GroupIDs = array_keys($this->SphResults);
+ $GroupCount = count($GroupIDs);
+ while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < $this->PageSize) {
+ // Make sure we get $PageSize results, or all of them if there are less than $PageSize hits
+ $this->SphQL->where('groupid', $GroupIDs, true);
+ $SphQLResult = $this->SphQL->query();
+ if (!$SphQLResult->has_results()) {
+ break;
+ }
+ $this->SphResults += $SphQLResult->collect('groupid');
+ $GroupIDs = array_keys($this->SphResults);
+ $GroupCount = count($GroupIDs);
+ }
+ if ($GroupCount > $this->PageSize) {
+ $this->SphResults = array_slice($this->SphResults, 0, $this->PageSize, true);
+ }
+ $this->NumResults = count($this->SphResults);
+ } else {
+ $this->NumResults = (int)$SphQLResult->get_meta('total_found');
+ if ($this->GroupResults) {
+ $this->SphResults = $SphQLResult->collect('groupid');
+ } else {
+ $this->SphResults = $SphQLResult->to_pair('id', 'groupid');
+ }
+ }
+ }
+
+ /**
+ * Process search terms and store the parts in appropriate arrays until we know if
+ * the NOT operator can be used
+ */
+ private function build_query() {
+ foreach ($this->Terms as $Field => $Words) {
+ $SearchString = '';
+ if (isset(self::$FormsToFields[$Field])) {
+ $Field = self::$FormsToFields[$Field];
+ }
+ $QueryParts = array('include' => array(), 'exclude' => array());
+ if (!$this->EnableNegation && !empty($Words['exclude'])) {
+ $Words['include'] = $Words['exclude'];
+ unset($Words['exclude']);
+ }
+ if (!empty($Words['include'])) {
+ foreach ($Words['include'] as $Word) {
+ $QueryParts['include'][] = Sphinxql::sph_escape_string($Word);
+ }
+ }
+ if (!empty($Words['exclude'])) {
+ foreach ($Words['exclude'] as $Word) {
+ $QueryParts['exclude'][] = '!' . Sphinxql::sph_escape_string(substr($Word, 1));
+ }
+ }
+ if (!empty($QueryParts)) {
+ if (isset($Words['operator'])) {
+ // Is the operator already specified?
+ $Operator = $Words['operator'];
+ } elseif(isset(self::$FieldOperators[$Field])) {
+ // Does this field have a non-standard operator?
+ $Operator = self::$FieldOperators[$Field];
+ } else {
+ // Go for the default operator
+ $Operator = self::$FieldOperators[''];
+ }
+ if (!empty($QueryParts['include'])) {
+ $SearchString .= '( ' . implode($Operator, $QueryParts['include']) . ' ) ';
+ }
+ if (!empty($QueryParts['exclude'])) {
+ $SearchString .= implode(' ', $QueryParts['exclude']);
+ }
+ $this->SphQL->where_match($SearchString, $Field, false);
+ if (isset(self::$TorrentFields[$Field])) {
+ $this->UsedTorrentFields[$Field] = $SearchString;
+ }
+ $this->Filtered = true;
+ }
+ }
+ }
+
+ /**
+ * Look at each search term and figure out what to do with it
+ *
+ * @param array $Terms Array with search terms from query()
+ */
+ private function process_search_terms($Terms) {
+ foreach ($Terms as $Key => $Term) {
+ if (isset(self::$Fields[$Key])) {
+ $this->process_field($Key, $Term);
+ } elseif (isset(self::$Attributes[$Key])) {
+ $this->process_attribute($Key, $Term);
+ }
+ $this->RawTerms[$Key] = $Term;
+ }
+ $this->post_process_fields();
+ }
+
+ /**
+ * Process attribute filters and store them in case we need to post-process grouped results
+ *
+ * @param string $Attribute Name of the attribute to filter against
+ * @param mixed $Value The filter's condition for a match
+ */
+ private function process_attribute($Attribute, $Value) {
+ if ($Value === '') {
+ return;
+ }
+ switch ($Attribute) {
+ case 'year':
+ if (!$this->search_year($Value)) {
+ return;
+ }
+ break;
+
+ case 'haslog':
+ if ($Value == 0) {
+ $this->SphQL->where('haslog', 0);
+ } elseif ($Value == 100) {
+ $this->SphQL->where('logscore', 100);
+ } elseif ($Value < 0) {
+ $this->SphQL->where_lt('logscore', 100);
+ $this->SphQL->where('haslog', 1);
+ } else {
+ $this->SphQL->where('haslog', 1);
+ }
+ $this->UsedTorrentAttrs['haslog'] = $Value;
+ break;
+
+ case 'freetorrent':
+ if ($Value == 3) {
+ $this->SphQL->where('freetorrent', 0, true);
+ $this->UsedTorrentAttrs['freetorrent'] = 3;
+ } elseif ($Value >= 0 && $Value < 3) {
+ $this->SphQL->where('freetorrent', $Value);
+ $this->UsedTorrentAttrs[$Attribute] = $Value;
+ } else {
+ return;
+ }
+ break;
+
+ case 'filter_cat':
+ if (!is_array($Value)) {
+ $Value = array_fill_keys(explode('|', $Value), 1);
+ }
+ $CategoryFilter = array();
+ foreach (array_keys($Value) as $Category) {
+ if (is_number($Category)) {
+ $CategoryFilter[] = $Category;
+ } else {
+ global $Categories;
+ $ValidValues = array_map('strtolower', $Categories);
+ if (($CategoryID = array_search(strtolower($Category), $ValidValues)) !== false) {
+ $CategoryFilter[] = $CategoryID + 1;
+ }
+ }
+ }
+ if (empty($CategoryFilter)) {
+ $CategoryFilter = 0;
+ }
+ $this->SphQL->where('categoryid', $CategoryFilter);
+ break;
+
+ default:
+ if (!is_number($Value) && self::$Attributes[$Attribute] !== false) {
+ // Check if the submitted value can be converted to a valid one
+ $ValidValuesVarname = self::$Attributes[$Attribute];
+ global $$ValidValuesVarname;
+ $ValidValues = array_map('strtolower', $$ValidValuesVarname);
+ if (($Value = array_search(strtolower($Value), $ValidValues)) === false) {
+ // Force the query to return 0 results if value is still invalid
+ $Value = max(array_keys($ValidValues)) + 1;
+ }
+ }
+ $this->SphQL->where($Attribute, $Value);
+ $this->UsedTorrentAttrs[$Attribute] = $Value;
+ break;
+ }
+ $this->Filtered = true;
+ }
+
+ /**
+ * Look at a fulltext search term and figure out if it needs special treatment
+ *
+ * @param string $Field Name of the search field
+ * @param string $Term Search expression for the field
+ */
+ private function process_field($Field, $Term) {
+ $Term = trim($Term);
+ if ($Term === '') {
+ return;
+ }
+ if ($Field === 'searchstr') {
+ $this->search_basic($Term);
+ } elseif ($Field === 'filelist') {
+ $this->search_filelist($Term);
+ } elseif ($Field === 'taglist') {
+ $this->search_taglist($Term);
+ } else {
+ $this->add_field($Field, $Term);
+ }
+ }
+
+ /**
+ * Some fields may require post-processing
+ */
+ private function post_process_fields() {
+ if (isset($this->Terms['taglist'])) {
+ // Replace bad tags with tag aliases
+ $this->Terms['taglist'] = Tags::remove_aliases($this->Terms['taglist']);
+ if (isset($this->RawTerms['tags_type']) && (int)$this->RawTerms['tags_type'] === self::TAGS_ANY) {
+ $this->Terms['taglist']['operator'] = self::SPH_BOOL_OR;
+ }
+ // Update the RawTerms array so get_terms() can return the corrected search terms
+ if (isset($this->Terms['taglist']['include'])) {
+ $AllTags = $this->Terms['taglist']['include'];
+ } else {
+ $AllTags = array();
+ }
+ if (isset($this->Terms['taglist']['exclude'])) {
+ $AllTags = array_merge($AllTags, $this->Terms['taglist']['exclude']);
+ }
+ $this->RawTerms['taglist'] = str_replace('_', '.', implode(', ', $AllTags));
+ }
+ }
+
+ /**
+ * Handle magic keywords in the basic torrent search
+ *
+ * @param string $Term Given search expression
+ */
+ private function search_basic($Term) {
+ global $Bitrates, $Formats, $Media;
+ $SearchBitrates = array_map('strtolower', $Bitrates);
+ array_push($SearchBitrates, 'v0', 'v1', 'v2', '24bit');
+ $SearchFormats = array_map('strtolower', $Formats);
+ $SearchMedia = array_map('strtolower', $Media);
+
+ foreach (explode(' ', $Term) as $Word) {
+ if (in_array($Word, $SearchBitrates)) {
+ $this->add_word('encoding', $Word);
+ } elseif (in_array($Word, $SearchFormats)) {
+ $this->add_word('format', $Word);
+ } elseif (in_array($Word, $SearchMedia)) {
+ $this->add_word('media', $Word);
+ } elseif ($Word === '100%') {
+ $this->process_attribute('haslog', 100);
+ } elseif ($Word === '!100%') {
+ $this->process_attribute('haslog', -1);
+ } else {
+ $this->add_word('searchstr', $Word);
+ }
+ }
+ }
+
+ /**
+ * Use phrase boundary for file searches to make sure we don't count
+ * partial hits from multiple files
+ *
+ * @param string $Term Given search expression
+ */
+ private function search_filelist($Term) {
+ $SearchString = '"' . Sphinxql::sph_escape_string($Term) . '"~20';
+ $this->SphQL->where_match($SearchString, 'filelist', false);
+ $this->UsedTorrentFields['filelist'] = $SearchString;
+ $this->EnableNegation = true;
+ $this->Filtered = true;
+ }
+
+ /**
+ * Prepare tag searches before sending them to the normal treatment
+ *
+ * @param string $Term Given search expression
+ */
+ private function search_taglist($Term) {
+ $Term = strtr($Term, '.', '_');
+ $this->add_field('taglist', $Term);
+ }
+
+ /**
+ * The year filter accepts a range. Figure out how to handle the filter value
+ *
+ * @param string $Term Filter condition. Can be an integer or a range with the format X-Y
+ * @return bool True if parameters are valid
+ */
+ private function search_year($Term) {
+ $Years = explode('-', $Term);
+ if (count($Years) === 1 && is_number($Years[0])) {
+ // Exact year
+ $this->SphQL->where('year', $Years[0]);
+ } elseif (count($Years) === 2) {
+ if (empty($Years[0]) && is_number($Years[1])) {
+ // Range: 0 - 2005
+ $this->SphQL->where_lt('year', $Years[1], true);
+ } elseif (empty($Years[1]) && is_number($Years[0])) {
+ // Range: 2005 - 2^32-1
+ $this->SphQL->where_gt('year', $Years[0], true);
+ } elseif (is_number($Years[0]) && is_number($Years[1])) {
+ // Range: 2005 - 2009
+ $this->SphQL->where_between('year', array(min($Years), max($Years)));
+ } else {
+ // Invalid input
+ return false;
+ }
+ } else {
+ // Invalid input
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add a field filter that doesn't need special treatment
+ *
+ * @param string $Field Name of the search field
+ * @param string $Term Search expression for the field
+ */
+ private function add_field($Field, $Term) {
+ if (isset(self::$FieldSeparators[$Field])) {
+ $Separator = self::$FieldSeparators[$Field];
+ } else {
+ $Separator = self::$FieldSeparators[''];
+ }
+ $Words = explode($Separator, $Term);
+ foreach ($Words as $Word) {
+ $this->add_word($Field, $Word);
+ }
+ }
+
+ /**
+ * Add a keyword to the array of search terms
+ *
+ * @param string $Field Name of the search field
+ * @param string $Word Keyword
+ */
+ private function add_word($Field, $Word) {
+ $Word = trim($Word);
+ // Skip isolated hyphens to enable "Artist - Title" searches
+ if ($Word === '' || $Word === '-') {
+ return;
+ }
+ if ($Word[0] === '!' && strlen($Word) >= 2 && strpos($Word, '!', 1) === false) {
+ $this->Terms[$Field]['exclude'][] = $Word;
+ } else {
+ $this->Terms[$Field]['include'][] = $Word;
+ $this->EnableNegation = true;
+ }
+ }
+
+ /**
+ * @return array Torrent group information for the matches from Torrents::get_groups
+ */
+ public function get_groups() {
+ return $this->Groups;
+ }
+
+ /**
+ * @param string $Type Field or attribute name
+ * @return string Unprocessed search terms
+ */
+ public function get_terms($Type) {
+ return isset($this->RawTerms[$Type]) ? $this->RawTerms[$Type] : '';
+ }
+
+ /**
+ * @return int Result count
+ */
+ public function record_count() {
+ return $this->NumResults;
+ }
+
+ /**
+ * @return bool Whether any filters were used
+ */
+ public function has_filters() {
+ return $this->Filtered;
+ }
+
+ /**
+ * @return bool Whether any torrent-specific fulltext filters were used
+ */
+ public function need_torrent_ft() {
+ return $this->GroupResults && $this->NumResults > 0 && !empty($this->UsedTorrentFields);
+ }
+
+ /**
+ * Get torrent group info and remove any torrents that don't match
+ */
+ private function process_results() {
+ if (count($this->SphResults) == 0) {
+ return;
+ }
+ $this->Groups = Torrents::get_groups($this->SphResults);
+ if ($this->need_torrent_ft()) {
+ // Query Sphinx for torrent IDs if torrent-specific fulltext filters were used
+ $this->filter_torrents_sph();
+ } elseif ($this->GroupResults) {
+ // Otherwise, let PHP discard unmatching torrents
+ $this->filter_torrents_internal();
+ }
+ // Ungrouped searches don't need any additional filtering
+ }
+
+ /**
+ * Build and run a query that gets torrent IDs from Sphinx when fulltext filters
+ * were used to get primary results and they are grouped
+ */
+ private function filter_torrents_sph() {
+ $AllTorrents = array();
+ foreach ($this->Groups as $GroupID => $Group) {
+ if (!empty($Group['Torrents'])) {
+ $AllTorrents += array_fill_keys(array_keys($Group['Torrents']), $GroupID);
+ }
+ }
+ $TorrentCount = count($AllTorrents);
+ $this->SphQLTor = new SphinxqlQuery();
+ $this->SphQLTor->select('id')->from('torrents, delta');
+ foreach ($this->UsedTorrentFields as $Field => $Term) {
+ $this->SphQLTor->where_match($Term, $Field, false);
+ }
+ $this->SphQLTor->copy_attributes_from($this->SphQL);
+ $this->SphQLTor->where('id', array_keys($AllTorrents))->limit(0, $TorrentCount, $TorrentCount);
+ $SphQLResultTor = $this->SphQLTor->query();
+ $MatchingTorrentIDs = $SphQLResultTor->to_pair('id', 'id');
+ foreach ($AllTorrents as $TorrentID => $GroupID) {
+ if (!isset($MatchingTorrentIDs[$TorrentID])) {
+ unset($this->Groups[$GroupID]['Torrents'][$TorrentID]);
+ }
+ }
+ }
+
+ /**
+ * Non-Sphinx method of collecting IDs of torrents that match any
+ * torrent-specific attribute filters that were used in the search query
+ */
+ private function filter_torrents_internal() {
+ foreach ($this->Groups as $GroupID => $Group) {
+ if (empty($Group['Torrents'])) {
+ continue;
+ }
+ foreach ($Group['Torrents'] as $TorrentID => $Torrent) {
+ if (!$this->filter_torrent_internal($Torrent)) {
+ unset($this->Groups[$GroupID]['Torrents'][$TorrentID]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Post-processing to determine if a torrent is a real hit or if it was
+ * returned because another torrent in the group matched. Only used if
+ * there are no torrent-specific fulltext conditions
+ *
+ * @param array $Torrent Torrent array, probably from Torrents::get_groups()
+ * @return bool True if it's a real hit
+ */
+ private function filter_torrent_internal($Torrent) {
+ if (isset($this->UsedTorrentAttrs['freetorrent'])) {
+ $FilterValue = $this->UsedTorrentAttrs['freetorrent'];
+ if ($FilterValue == '3' && $Torrent['FreeTorrent'] == '0') {
+ // Either FL or NL is ok
+ return false;
+ } elseif ($FilterValue != '3' && $FilterValue != (int)$Torrent['FreeTorrent']) {
+ return false;
+ }
+ }
+ if (isset($this->UsedTorrentAttrs['hascue'])) {
+ if ($this->UsedTorrentAttrs['hascue'] != (int)$Torrent['HasCue']) {
+ return false;
+ }
+ }
+ if (isset($this->UsedTorrentAttrs['haslog'])) {
+ $FilterValue = $this->UsedTorrentAttrs['haslog'];
+ if ($FilterValue == '0') {
+ // No logs
+ $Pass = empty($Torrent['HasLog']);
+ } elseif ($FilterValue == '100') {
+ // 100% logs
+ $Pass = $Torrent['LogScore'] == '100';
+ } elseif ($FilterValue < 0) {
+ // Unscored or <100% logs
+ $Pass = !empty($Torrent['HasLog']) && $Torrent['LogScore'] != '100';
+ } else {
+ // Any log score
+ $Pass = $Torrent['HasLog'] == '1';
+ }
+ if (!$Pass) {
+ return false;
+ }
+ }
+ if (isset($this->UsedTorrentAttrs['scene'])) {
+ if ($this->UsedTorrentAttrs['scene'] != (int)$Torrent['Scene']) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/classes/tracker.class.php b/classes/tracker.class.php
index e6c940b8..cb7f4383 100644
--- a/classes/tracker.class.php
+++ b/classes/tracker.class.php
@@ -130,6 +130,8 @@ private static function send_request($Get, $MaxAttempts = 1, &$Err = false) {
if ($Sleep) {
sleep($Sleep);
}
+ $Sleep = 6;
+
// Send request
$File = fsockopen(TRACKER_HOST, TRACKER_PORT, $ErrorNum, $ErrorString);
if ($File) {
@@ -140,7 +142,6 @@ private static function send_request($Get, $MaxAttempts = 1, &$Err = false) {
}
} else {
$Err = "Failed to fsockopen() - $ErrorNum - $ErrorString";
- $Sleep = 6;
continue;
}
diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt
index da27b69d..b02b6c5d 100644
--- a/docs/CHANGES.txt
+++ b/docs/CHANGES.txt
@@ -1,5 +1,76 @@
CHANGE LOG
+2014-11-06 by porkpie
+ Create a TorrentSearch class that does most of the torrent search job.
+ Add PHP post-processing of matches if a search query doesn't need
+ any torrent-specific fulltext conditions.
+ Comments
+
+2014-11-03 by downinthings
+Fix comments on User API
+
+2014-10-30 by tobbez
+Add stats API method
+
+2014-10-27 by porkpie
+Clear news cache immediately after updating the database
+
+2014-10-20 by Narcolepsy
+Consolidated commit list since 2014-04-20:
+
+Fix some LTR issues (bug thread 189685)
+Move loading comments before View::show_header. This should fix subscriptions and quote notifications appearing when a user is already on the page of that last subscription
+Replace some hardcoded IRC channels with configuration parameters
+Fix HTML in
for non-{Music, Audiobooks, Comedy} requests (bug thread 189855)
+Fix: link directly to the torrent on bad files/folders better.php pages (thread 190009)
+Update internal cache when a cache key is changed
+This should make quote notifications disappear when viewing threads (#183300)
+Cast user id to int on user profile pages to get rid of trailing period
+Remove WhatMan's spellcheck tool (seriously it was crap)
+Fix a few cache key typos:
+ Blog => blog
+ requests_$RequestID => request_$RequestID
+ users_stats_$UserID => user_stats_$UserID
+Fix some sorting issues related to Torrents::get_groups not returning the groups in the same order they were passed in; DO NOT iterate over Torrents::get_groups results...
+Fix image in bookmarks JSON API
+Fix comments appearing on the wrong page (bug thread 190203)
+Passwords aren't limited to 40 characters
+Fix better.php/single and rename some variables so they make some sense
+ Bug thread #190289
+Don't continue to process forum post deletes if the post or thread doesn't exist
+Trusting rdns is bad
+Fix two cache issues in the schedule
+ - Permissions of users that are demoted to User/Member for low ratio or upload were not updated/cleared, which meant that e.g. demoted PU users could still see the PU forum
+ - The cache was cleared before the promotion/demotion queries. This could cause a race condition if the cache key were filled again (through a Users::user_info call)
+Fix some cache issues (bug thread 190802)
+Speed up BBCode
+BBCode profiling
+Fix a bunch of bookmarks stuff. HTML/CSS consistency, jQuerified functions
+Bye sizzle
+show rank 4 instead of 5 when 5 points in pms
+More accurate donor rank expiration times
+Apparently users_donor_ranks can contain NULL values.
+ Change these to 0 after fetching to fix cache
+Fix a grammar error and use SITE_NAME constant in the post-invite confirmation PM that gets sent to the inviter
+Remove trailing whitespace in public Gazelle, improve grammar, add some missing spaces in 80char's CSS file
+Remove extraneous whitespace from various files
+Coding standards cleanup in script_start.php
+ Remove some WCD-specific smileys from public Gazelle
+
+ Remove these:
+ * ackbar-what.png
+ * jesusfish-what.png
+ * louisdefunes-what.png
+Add Tools::check_cidr_range to check if an IP is inside a given range in CIDR format
+Torrent size is a 64-bit Sphinx attribute, so let's make use of it
+ Should partially fix bug #185554
+Fix SQLI in PushServer::push_pushbullet
+Minor coding standards fixes in classes/pushserver.class.php
+Fix User Upload Count API
+fix lack of artist name in concert titles
+add mailgun as alternative to self-hosting email, config changes in template to match
+user api additional stats, patch from user downinthings
+
2014-04-20 by SevenNationArmy
Replace old IRC applet with Mibbit
diff --git a/gazelle.sql b/gazelle.sql
index 1ad91ba3..16ff96a4 100644
--- a/gazelle.sql
+++ b/gazelle.sql
@@ -1128,7 +1128,7 @@ CREATE TABLE `torrents` (
`Seeders` int(6) NOT NULL DEFAULT '0',
`last_action` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`FreeTorrent` enum('0','1','2') NOT NULL DEFAULT '0',
- `FreeLeechType` enum('0','1','2','3') NOT NULL DEFAULT '0',
+ `FreeLeechType` enum('0','1','2','3','4','5','6','7') NOT NULL DEFAULT '0',
`Time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`Description` text,
`Snatched` int(10) unsigned NOT NULL DEFAULT '0',
diff --git a/ocelot-1.0.tar.gz b/ocelot-1.0.tar.gz
new file mode 100644
index 00000000..324c72d0
Binary files /dev/null and b/ocelot-1.0.tar.gz differ
diff --git a/sections/ajax/announcements.php b/sections/ajax/announcements.php
index eff908b3..586b1097 100644
--- a/sections/ajax/announcements.php
+++ b/sections/ajax/announcements.php
@@ -77,7 +77,7 @@
}
}
-json_die("success", array(
+json_print("success", array(
'announcements' => $JsonAnnouncements,
'blogPosts' => $JsonBlog
));
diff --git a/sections/ajax/artist.php b/sections/ajax/artist.php
index 77bb5087..e882abb8 100644
--- a/sections/ajax/artist.php
+++ b/sections/ajax/artist.php
@@ -342,7 +342,7 @@ function compare($X, $Y) {
$Cache->cache_value($Key, $Data, 3600);
-json_die("success", array(
+json_print("success", array(
'id' => (int)$ArtistID,
'name' => $Name,
'notificationsEnabled' => $notificationsEnabled,
diff --git a/sections/ajax/browse.php b/sections/ajax/browse.php
index d8e7df7f..38d963f6 100644
--- a/sections/ajax/browse.php
+++ b/sections/ajax/browse.php
@@ -1,494 +1,44 @@
include(SERVER_ROOT.'/sections/torrents/functions.php');
-/** Start default parameters and validation **/
-// Setting default search options
-if (!empty($_GET['setdefault'])) {
- $UnsetList = array('page', 'setdefault');
- $UnsetRegexp = '/(&|^)('.implode('|', $UnsetList).')=.*?(&|$)/i';
-
- $DB->query("
- SELECT SiteOptions
- FROM users_info
- WHERE UserID = '".db_string($LoggedUser['ID'])."'");
- list($SiteOptions) = $DB->next_record(MYSQLI_NUM, false);
- if (!empty($SiteOptions)) {
- $SiteOptions = unserialize($SiteOptions);
- } else {
- $SiteOptions = array();
- }
- $SiteOptions['DefaultSearch'] = preg_replace($UnsetRegexp, '', $_SERVER['QUERY_STRING']);
- $DB->query("
- UPDATE users_info
- SET SiteOptions = '".db_string(serialize($SiteOptions))."'
- WHERE UserID = '".db_string($LoggedUser['ID'])."'");
- $Cache->begin_transaction("user_info_heavy_$UserID");
- $Cache->update_row(false, array('DefaultSearch' => $SiteOptions['DefaultSearch']));
- $Cache->commit_transaction(0);
-
-// Clearing default search options
-} elseif (!empty($_GET['cleardefault'])) {
- $DB->query("
- SELECT SiteOptions
- FROM users_info
- WHERE UserID = '".db_string($LoggedUser['ID'])."'");
- list($SiteOptions) = $DB->next_record(MYSQLI_NUM, false);
- $SiteOptions = unserialize($SiteOptions);
- $SiteOptions['DefaultSearch'] = '';
- $DB->query("
- UPDATE users_info
- SET SiteOptions = '".db_string(serialize($SiteOptions))."'
- WHERE UserID = '".db_string($LoggedUser['ID'])."'");
- $Cache->begin_transaction("user_info_heavy_$UserID");
- $Cache->update_row(false, array('DefaultSearch' => ''));
- $Cache->commit_transaction(0);
-
-// Use default search options
-} elseif (empty($_SERVER['QUERY_STRING']) || (count($_GET) === 1 && isset($_GET['page']))) {
- if (!empty($LoggedUser['DefaultSearch'])) {
- if (!empty($_GET['page'])) {
- $Page = $_GET['page'];
- parse_str($LoggedUser['DefaultSearch'], $_GET);
- $_GET['page'] = $Page;
- } else {
- parse_str($LoggedUser['DefaultSearch'], $_GET);
- }
- }
-}
-// Terms were not submitted via the search form
-if (!isset($_GET['searchsubmit'])) {
- $_GET['group_results'] = !$LoggedUser['DisableGrouping2'];
-}
-
-if (isset($_GET['group_results']) && $_GET['group_results']) {
- $_GET['group_results'] = 1;
- $GroupResults = true;
- $SortOrders = array(
- // 'url attr' => [global order, order within group]
- 'year' => array('year', 'year'),
- 'time' => array('id', 'id'),
- 'size' => array('maxsize', 'size'),
- 'seeders' => array('sumseeders', 'seeders'),
- 'leechers' => array('sumleechers', 'leechers'),
- 'snatched' => array('sumsnatched', 'snatched'),
- 'random' => false);
-
- $AggregateExp = array(
- 'maxsize' => 'MAX(size) AS maxsize',
- 'sumseeders' => 'SUM(seeders) AS sumseeders',
- 'sumleechers' => 'SUM(leechers) AS sumleechers',
- 'sumsnatched' => 'SUM(snatched) AS sumsnatched');
-} else {
- $GroupResults = false;
- $SortOrders = array(
- 'year' => 'year',
- 'time' => 'id',
- 'size' => 'size',
- 'seeders' => 'seeders',
- 'leechers' => 'leechers',
- 'snatched' => 'snatched',
- 'random' => false);
-}
-
-if (empty($_GET['order_by']) || !isset($SortOrders[$_GET['order_by']])) {
- $_GET['order_by'] = 'time';
- $OrderBy = 'time'; // For header links
-} else {
- $OrderBy = $_GET['order_by'];
-}
-
if (!empty($_GET['order_way']) && $_GET['order_way'] == 'asc') {
$OrderWay = 'asc';
} else {
- $_GET['order_way'] = 'desc';
$OrderWay = 'desc';
}
-/** End default parameters and validation **/
-
-/** Start preparation of property arrays **/
-array_pop($Bitrates); // remove 'other'
-$SearchBitrates = array_merge($Bitrates, array('v0', 'v1', 'v2', '24bit'));
-
-foreach ($SearchBitrates as $ID => $Val) {
- $SearchBitrates[$ID] = strtolower($Val);
-}
-foreach ($Formats as $ID => $Val) {
- $SearchFormats[$ID] = strtolower($Val);
-}
-/** End preparation of property arrays **/
-
-/** Start query preparation **/
-$SphQL = new SphinxqlQuery();
-$SphQLTor = new SphinxqlQuery();
-
-if ($OrderBy == 'random') {
- $SphQL->select('id, groupid, categoryid')
- ->order_by('RAND()', '');
- $Random = true;
-} elseif ($GroupResults) {
- $OrderProperties = $SortOrders[$OrderBy];
- $SphQL->select('groupid, categoryid' . (isset($AggregateExp[$OrderProperties[0]]) ? ', '.$AggregateExp[$OrderProperties[0]] : ''))
- ->group_by('groupid')
- ->order_by($OrderProperties[0], $OrderWay)
- ->order_group_by($OrderProperties[1], $OrderWay);
-
+if (empty($_GET['order_by']) || !isset(TorrentSearch::$SortOrders[$_GET['order_by']])) {
+ $OrderBy = 'time';
} else {
- $SphQL->select('id, groupid, categoryid')
- ->order_by($SortOrders[$OrderBy], $OrderWay);
-}
-$SphQL->from('torrents, delta');
-$SphQLTor->select('id, groupid')->from('torrents, delta');
-/** End query preparation **/
-
-/** Start building search query **/
-$Filtered = false;
-$EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator
-
-// File list searches make use of the proximity operator to ensure that all keywords match the same file
-if (!empty($_GET['filelist'])) {
- $SearchString = trim($_GET['filelist']);
- if ($SearchString !== '') {
- $SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
- $SphQL->where_match($SearchString, 'filelist', false);
- $SphQLTor->where_match($SearchString, 'filelist', false);
- $EnableNegation = true;
- }
+ $OrderBy = $_GET['order_by'];
}
-// Collect all entered search terms to find out whether to enable the NOT operator
-$SearchWords = array();
-foreach (array('artistname', 'groupname', 'recordlabel', 'cataloguenumber',
- 'taglist', 'remastertitle', 'remasteryear', 'remasterrecordlabel',
- 'remastercataloguenumber', 'encoding', 'format', 'media', 'description') as $Search) {
- if (!empty($_GET[$Search])) {
- $SearchString = trim($_GET[$Search]);
- if ($SearchString !== '') {
- $SearchWords[$Search] = array('include' => array(), 'exclude' => array());
- if ($Search == 'taglist') {
- $SearchString = strtr($SearchString, '.', '_');
- $Words = explode(',', $SearchString);
- } else {
- $Words = explode(' ', $SearchString);
- }
- foreach ($Words as $Word) {
- $Word = trim($Word);
- // Skip isolated hyphens to enable "Artist - Title" searches
- if ($Word === '-') {
- continue;
- }
- if ($Word[0] === '!' && strlen($Word) >= 2) {
- if (strpos($Word, '!', 1) === false) {
- $SearchWords[$Search]['exclude'][] = $Word;
- } else {
- $SearchWords[$Search]['include'][] = $Word;
- $EnableNegation = true;
- }
- } elseif ($Word !== '') {
- $SearchWords[$Search]['include'][] = $Word;
- $EnableNegation = true;
- }
- }
- }
- }
+$GroupResults = !isset($_GET['group_results']) || $_GET['group_results'] != '0';
+$Page = !empty($_GET['page']) ? (int)$_GET['page'] : 1;
+$Search = new TorrentSearch($GroupResults, $OrderBy, $OrderWay, $Page, TORRENTS_PER_PAGE);
+$Results = $Search->query($_GET);
+$Groups = $Search->get_groups();
+$NumResults = $Search->record_count();
+
+if ($Results === false) {
+ json_die('error', 'Search returned an error. Make sure all parameters are valid and of the expected types.');
}
-
-//Simple search
-if (!empty($_GET['searchstr'])) {
- $SearchString = trim($_GET['searchstr']);
- $Words = explode(' ', strtolower($SearchString));
- if (!empty($Words)) {
- $FilterBitrates = $FilterFormats = array();
- $BasicSearch = array('include' => array(), 'exclude' => array());
- foreach ($Words as $Word) {
- $Word = trim($Word);
- // Skip isolated hyphens to enable "Artist - Title" searches
- if ($Word === '-') {
- continue;
- }
- if ($Word[0] === '!' && strlen($Word) >= 2) {
- if ($Word === '!100%') {
- $_GET['haslog'] = '-1';
- } elseif (strpos($Word, '!', 1) === false) {
- $BasicSearch['exclude'][] = $Word;
- } else {
- $BasicSearch['include'][] = $Word;
- $EnableNegation = true;
- }
- } elseif (in_array($Word, $SearchBitrates)) {
- $FilterBitrates[] = $Word;
- $EnableNegation = true;
- } elseif (in_array($Word, $SearchFormats)) {
- $FilterFormats[] = $Word;
- $EnableNegation = true;
- } elseif ($Word === '100%') {
- $_GET['haslog'] = '100';
- } elseif ($Word !== '') {
- $BasicSearch['include'][] = $Word;
- $EnableNegation = true;
- }
- }
- if (!$EnableNegation && !empty($BasicSearch['exclude'])) {
- $BasicSearch['include'] = array_merge($BasicSearch['include'], $BasicSearch['exclude']);
- unset($BasicSearch['exclude']);
- }
- $QueryParts = array();
- foreach ($BasicSearch['include'] as $Word) {
- $QueryParts[] = Sphinxql::sph_escape_string($Word);
- }
- if (!empty($BasicSearch['exclude'])) {
- foreach ($BasicSearch['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
- }
- }
- if (!empty($FilterBitrates)) {
- $SearchString = implode(' ', $FilterBitrates);
- $SphQL->where_match($SearchString, 'encoding', false);
- $SphQLTor->where_match($SearchString, 'encoding', false);
- }
- if (!empty($FilterFormats)) {
- $SearchString = implode(' ', $FilterFormats);
- $SphQL->where_match($SearchString, 'format', false);
- $SphQLTor->where_match($SearchString, 'format', false);
- }
- if (!empty($QueryParts)) {
- $SearchString = implode(' ', $QueryParts);
- $SphQL->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
- $SphQLTor->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
- }
- }
-}
-
-// Tag list
-if (!empty($SearchWords['taglist'])) {
- $_GET['tags_type'] = (!isset($_GET['tags_type']) || $_GET['tags_type'] == 1) ? '1' : '0';
- $TagType = (int)$_GET['tags_type'];
-
- //Get tags
- $Tags = $SearchWords['taglist'];
-
- $TagFilter = Tags::tag_filter_sph($Tags, $EnableNegation, $TagType);
- if (!empty($TagFilter['predicate'])) {
- $SphQL->where_match($TagFilter['predicate'], 'taglist', false);
- $SphQLTor->where_match($TagFilter['predicate'], 'taglist', false);
- }
- unset($SearchWords['taglist']);
-}
-elseif (!isset($_GET['tags_type'])) {
- $_GET['tags_type'] = '1';
-}
-
-foreach ($SearchWords as $Search => $Words) {
- $QueryParts = array();
- if (!$EnableNegation && !empty($Words['exclude'])) {
- $Words['include'] = array_merge($Words['include'], $Words['exclude']);
- unset($Words['exclude']);
- }
- foreach ($Words['include'] as $Word) {
- $QueryParts[] = Sphinxql::sph_escape_string($Word);
- }
- if (!empty($Words['exclude'])) {
- foreach ($Words['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
- }
- }
- if (!empty($QueryParts)) {
- $SearchString = implode(' ', $QueryParts);
- $SphQL->where_match($SearchString, $Search, false);
- $SphQLTor->where_match($SearchString, $Search, false);
- }
-}
-
-if (!empty($_GET['year'])) {
- $Years = explode('-', $_GET['year']);
- if (is_number($Years[0]) || (empty($Years[0]) && !empty($Years[1]) && is_number($Years[1]))) {
- if (count($Years) === 1) {
- $SphQL->where('year', (int)$Years[0]);
- $SphQLTor->where('year', (int)$Years[0]);
- } else {
- if (empty($Years[1]) || !is_number($Years[1])) {
- $Years[1] = PHP_INT_MAX;
- } elseif ($Years[0] > $Years[1]) {
- $Years = array_reverse($Years);
- }
- $SphQL->where_between('year', array((int)$Years[0], (int)$Years[1]));
- $SphQLTor->where_between('year', array((int)$Years[0], (int)$Years[1]));
- }
- }
-}
-
-if (isset($_GET['haslog']) && $_GET['haslog'] !== '') {
- if ($_GET['haslog'] === '100') {
- $SphQL->where('logscore', 100);
- $SphQLTor->where('logscore', 100);
- } elseif ($_GET['haslog'] < 0) {
- // Exclude torrents with log score equal to 100
- $SphQL->where('logscore', 100, true);
- $SphQL->where('haslog', 1);
- $SphQLTor->where('logscore', 100, true);
- $SphQLTor->where('haslog', 1);
- } elseif ($_GET['haslog'] == 0) {
- $SphQL->where('haslog', 0);
- $SphQLTor->where('haslog', 0);
- } else {
- $SphQL->where('haslog', 1);
- $SphQLTor->where('haslog', 1);
- }
-}
-
-foreach (array('hascue', 'scene', 'vanityhouse', 'releasetype') as $Search) {
- if (isset($_GET[$Search]) && $_GET[$Search] !== '') {
- $SphQL->where($Search, $_GET[$Search]);
- // Release type is group specific
- if ($Search != 'releasetype') {
- $SphQLTor->where($Search, $_GET[$Search]);
- }
- }
-}
-
-if (isset($_GET['freetorrent']) && $_GET['freetorrent'] !== '') {
- switch ($_GET['freetorrent']) {
- case 0: // Only normal freeleech
- $SphQL->where('freetorrent', 0);
- $SphQLTor->where('freetorrent', 0);
- break;
- case 1: // Only free leech
- $SphQL->where('freetorrent', 1);
- $SphQLTor->where('freetorrent', 1);
- break;
- case 2: // Only neutral leech
- $SphQL->where('freetorrent', 2);
- $SphQLTor->where('freetorrent', 2);
- break;
- case 3: // Free or neutral leech
- $SphQL->where('freetorrent', 0, true);
- $SphQLTor->where('freetorrent', 0, true);
- break;
- }
-}
-
-if (!empty($_GET['filter_cat'])) {
- $SphQL->where('categoryid', array_keys($_GET['filter_cat']));
-}
-/** End building search query **/
-
-/** Run search query and collect results **/
-if (isset($Random) && $GroupResults) {
- // ORDER BY RAND() can't be used together with GROUP BY, so we need some special tactics
- $Page = 1;
- $SphQL->limit(0, 5 * TORRENTS_PER_PAGE, 5 * TORRENTS_PER_PAGE);
- $SphQLResult = $SphQL->query();
- $TotalCount = $SphQLResult->get_meta('total_found');
- $Results = $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- $GroupCount = count($GroupIDs);
- while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < TORRENTS_PER_PAGE) {
- // Make sure we get TORRENTS_PER_PAGE results, or all of them if there are less than TORRENTS_PER_PAGE hits
- $SphQL->where('groupid', $GroupIDs, true);
- $SphQLResult = $SphQL->query();
- if (!$SphQLResult->has_results()) {
- break;
- }
- $Results += $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- $GroupCount = count($GroupIDs);
- }
- if ($GroupCount > TORRENTS_PER_PAGE) {
- $Results = array_slice($Results, 0, TORRENTS_PER_PAGE, true);
- }
- $GroupIDs = array_keys($Results);
- $NumResults = count($Results);
-} else {
- if (!empty($_GET['page']) && is_number($_GET['page']) && $_GET['page'] > 0) {
- if (check_perms('site_search_many')) {
- $Page = $_GET['page'];
- } else {
- $Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $_GET['page']);
- }
- $Offset = ($Page - 1) * TORRENTS_PER_PAGE;
- $SphQL->limit($Offset, TORRENTS_PER_PAGE, $Offset + TORRENTS_PER_PAGE);
- } else {
- $Page = 1;
- $SphQL->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE);
- }
- $SphQLResult = $SphQL->query();
- $NumResults = $SphQLResult->get_meta('total_found');
- if ($GroupResults) {
- $Results = $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- } else {
- $Results = $SphQLResult->to_array('id');
- $GroupIDs = $SphQLResult->collect('groupid');
- }
-}
-
-if (!check_perms('site_search_many') && $NumResults > SPHINX_MAX_MATCHES) {
- $NumResults = SPHINX_MAX_MATCHES;
-}
-
-if ($NumResults) {
- $Groups = Torrents::get_groups($GroupIDs);
-
- if (!empty($Groups) && $GroupResults) {
- $TorrentIDs = array();
- foreach ($Groups as $Group) {
- if (!empty($Group['Torrents'])) {
- $TorrentIDs = array_merge($TorrentIDs, array_keys($Group['Torrents']));
- }
- }
- $TorrentCount = count($TorrentIDs);
- if ($TorrentCount > 0) {
- // Get a list of all torrent ids that match the search query
- $SphQLTor->where('id', $TorrentIDs)->limit(0, $TorrentCount, $TorrentCount);
- $SphQLResultTor = $SphQLTor->query();
- $TorrentIDs = $SphQLResultTor->to_pair('id', 'id'); // Because isset() is faster than in_array()
- }
- }
-}
-/** End run search query and collect results **/
-
if ($NumResults == 0) {
-
-$DB->query("
- SELECT
- tags.Name,
- ((COUNT(tags.Name) - 2) * (SUM(tt.PositiveVotes) - SUM(tt.NegativeVotes))) / (tags.Uses * 0.8) AS Score
- FROM xbt_snatched AS s
- INNER JOIN torrents AS t ON t.ID = s.fid
- INNER JOIN torrents_group AS g ON t.GroupID = g.ID
- INNER JOIN torrents_tags AS tt ON tt.GroupID = g.ID
- INNER JOIN tags ON tags.ID = tt.TagID
- WHERE s.uid = '$LoggedUser[ID]'
- AND tt.TagID != '13679'
- AND tt.TagID != '4820'
- AND tt.TagID != '2838'
- AND g.CategoryID = '1'
- AND tags.Uses > '10'
- GROUP BY tt.TagID
- ORDER BY Score DESC
- LIMIT 8");
- $JsonYouMightLike = array();
- while (list($Tag) = $DB->next_record()) {
- $JsonYouMightLike[] = $Tag;
- }
-
-
- json_die("success", array(
+ json_die('success', array(
'results' => array(),
- 'youMightLike' => $JsonYouMightLike
+ 'youMightLike' => array() // This slow and broken feature has been removed
));
}
$Bookmarks = Bookmarks::all_bookmarks('torrent');
$JsonGroups = array();
-foreach ($Results as $Result) {
- $GroupID = $Result['groupid'];
+foreach ($Results as $Key => $GroupID) {
$GroupInfo = $Groups[$GroupID];
if (empty($GroupInfo['Torrents'])) {
continue;
}
- $CategoryID = $Result['categoryid'];
+ $CategoryID = $GroupInfo['CategoryID'];
$GroupYear = $GroupInfo['Year'];
$ExtendedArtists = $GroupInfo['ExtendedArtists'];
$GroupCatalogueNumber = $GroupInfo['CatalogueNumber'];
@@ -500,15 +50,14 @@
$GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0;
foreach ($Torrents as $T) {
$GroupTime = max($GroupTime, strtotime($T['Time']));
- if ($T['Size'] > $MaxSize) {
- $MaxSize = $T['Size'];
- }
+ $MaxSize = max($MaxSize, $T['Size']);
$TotalLeechers += $T['Leechers'];
$TotalSeeders += $T['Seeders'];
$TotalSnatched += $T['Snatched'];
}
} else {
- $Torrents = array($Result['id'] => $GroupInfo['Torrents'][$Result['id']]);
+ $TorrentID = $Key;
+ $Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]);
}
$TagList = explode(' ', str_replace('_', '.', $GroupInfo['TagList']));
@@ -516,22 +65,12 @@
if (!empty($ExtendedArtists[1]) || !empty($ExtendedArtists[4]) || !empty($ExtendedArtists[5]) || !empty($ExtendedArtists[6])) {
unset($ExtendedArtists[2]);
unset($ExtendedArtists[3]);
- $DisplayName = Artists::display_artists($ExtendedArtists, false, false, true);
+ $DisplayName = Artists::display_artists($ExtendedArtists, false, false, false);
foreach ($ExtendedArtists[1] as $Artist) {
$JsonArtists[] = array(
- 'id' => (int)$Artist['id'],
- 'name' => $Artist['name'],
- 'aliasid' => (int)$Artist['id']
- );
- }
- } elseif (!empty($Artists)) {
- $DisplayName = Artists::display_artists(array(1 => $Artists), false, false, true);
- foreach ($Artists as $Artist) {
- $JsonArtists[] = array(
- 'id' => (int)$Artist['id'],
- 'name' => $Artist['name'],
- 'aliasid' => (int)$Artist['id']
- );
+ 'id' => (int)$Artist['id'],
+ 'name' => $Artist['name'],
+ 'aliasid' => (int)$Artist['aliasid']);
}
} else {
$DisplayName = '';
@@ -551,11 +90,6 @@
foreach ($Torrents as $TorrentID => $Data) {
// All of the individual torrents in the group
- // If they're using the advanced search and have chosen enabled grouping, we just skip the torrents that don't check out
- if (!isset($TorrentIDs[$TorrentID])) {
- continue;
- }
-
if ($Data['Remastered'] && !$Data['RemasterYear']) {
$FirstUnknown = !isset($FirstUnknown);
}
@@ -682,11 +216,7 @@
);
}
}
-
-echo json_encode(
- array(
- 'status' => 'success',
- 'response' => array(
- 'currentPage' => intval($Page),
- 'pages' => ceil($NumResults / TORRENTS_PER_PAGE),
- 'results' => $JsonGroups)));
+json_print('success', array(
+ 'currentPage' => intval($Page),
+ 'pages' => ceil($NumResults / TORRENTS_PER_PAGE),
+ 'results' => $JsonGroups));
diff --git a/sections/ajax/info.php b/sections/ajax/info.php
index 6af1c11d..103d6877 100644
--- a/sections/ajax/info.php
+++ b/sections/ajax/info.php
@@ -93,7 +93,7 @@
// Subscriptions
$NewSubscriptions = Subscriptions::has_new_subscriptions();
-json_die("success", array(
+json_print("success", array(
'username' => $LoggedUser['Username'],
'id' => (int)$LoggedUser['ID'],
'authkey' => $LoggedUser['AuthKey'],
diff --git a/sections/ajax/news_ajax.php b/sections/ajax/news_ajax.php
index 1a480e66..6e8e7bcb 100644
--- a/sections/ajax/news_ajax.php
+++ b/sections/ajax/news_ajax.php
@@ -37,4 +37,4 @@
);
}
-json_die('success', json_encode($NewsResponse));
+json_print('success', json_encode($NewsResponse));
diff --git a/sections/ajax/notifications.php b/sections/ajax/notifications.php
index be5e1ee8..65bbb980 100644
--- a/sections/ajax/notifications.php
+++ b/sections/ajax/notifications.php
@@ -99,7 +99,7 @@
}
}
-json_die("success", array(
+json_print("success", array(
'currentPages' => intval($Page),
'pages' => ceil($TorrentCount / NOTIFICATIONS_PER_PAGE),
'numNew' => $NumNew,
diff --git a/sections/ajax/request.php b/sections/ajax/request.php
index 077f5a86..a844f2ca 100644
--- a/sections/ajax/request.php
+++ b/sections/ajax/request.php
@@ -112,7 +112,7 @@
foreach ($Request['Tags'] as $Tag) {
$JsonTags[] = $Tag;
}
-json_die('success', array(
+json_print('success', array(
'requestId' => (int)$RequestID,
'requestorId' => (int)$Request['UserID'],
'requestorName' => $Requestor['Username'],
diff --git a/sections/ajax/requests.php b/sections/ajax/requests.php
index c52e51e0..1add4ec1 100644
--- a/sections/ajax/requests.php
+++ b/sections/ajax/requests.php
@@ -321,7 +321,7 @@
}
if ($NumResults == 0) {
- json_die("success", array(
+ json_print("success", array(
'currentPage' => 1,
'pages' => 1,
'results' => array()
@@ -381,7 +381,7 @@
);
}
- json_die("success", array(
+ json_print("success", array(
'currentPage' => intval($Page),
'pages' => ceil($NumResults / REQUESTS_PER_PAGE),
'results' => $JsonResults
diff --git a/sections/ajax/stats.php b/sections/ajax/stats.php
index 13d3c71e..2ab24ae5 100644
--- a/sections/ajax/stats.php
+++ b/sections/ajax/stats.php
@@ -1,7 +1,133 @@
-if (in_array($_GET['stat'], array('inbox', 'uploads', 'bookmarks', 'notifications', 'subscriptions', 'comments', 'friends'))) {
- $Cache->begin_transaction('stats_links');
- $Cache->update_row(false, array($_GET['stat'] => '+1'));
- $Cache->commit_transaction(0);
+// Begin user stats
+if (($UserCount = $Cache->get_value('stats_user_count')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM users_main
+ WHERE Enabled = '1'");
+ list($UserCount) = $DB->next_record();
+ $Cache->cache_value('stats_user_count', $UserCount, 0); //inf cache
}
+
+if (($UserStats = $Cache->get_value('stats_users')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM users_main
+ WHERE Enabled = '1'
+ AND LastAccess > '".time_minus(3600 * 24)."'");
+ list($UserStats['Day']) = $DB->next_record();
+
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM users_main
+ WHERE Enabled = '1'
+ AND LastAccess > '".time_minus(3600 * 24 * 7)."'");
+ list($UserStats['Week']) = $DB->next_record();
+
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM users_main
+ WHERE Enabled = '1'
+ AND LastAccess > '".time_minus(3600 * 24 * 30)."'");
+ list($UserStats['Month']) = $DB->next_record();
+
+ $Cache->cache_value('stats_users', $UserStats, 0);
+}
+
+// Begin torrent stats
+if (($TorrentCount = $Cache->get_value('stats_torrent_count')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM torrents");
+ list($TorrentCount) = $DB->next_record();
+ $Cache->cache_value('stats_torrent_count', $TorrentCount, 604800); // staggered 1 week cache
+}
+
+if (($AlbumCount = $Cache->get_value('stats_album_count')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM torrents_group
+ WHERE CategoryID = '1'");
+ list($AlbumCount) = $DB->next_record();
+ $Cache->cache_value('stats_album_count', $AlbumCount, 604830); // staggered 1 week cache
+}
+
+if (($ArtistCount = $Cache->get_value('stats_artist_count')) === false) {
+ $DB->query("
+ SELECT COUNT(ArtistID)
+ FROM artists_group");
+ list($ArtistCount) = $DB->next_record();
+ $Cache->cache_value('stats_artist_count', $ArtistCount, 604860); // staggered 1 week cache
+}
+
+if (($PerfectCount = $Cache->get_value('stats_perfect_count')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM torrents
+ WHERE ((LogScore = 100 AND Format = 'FLAC')
+ OR (Media = 'Vinyl' AND Format = 'FLAC')
+ OR (Media = 'WEB' AND Format = 'FLAC')
+ OR (Media = 'DVD' AND Format = 'FLAC')
+ OR (Media = 'Soundboard' AND Format = 'FLAC')
+ )");
+ list($PerfectCount) = $DB->next_record();
+ $Cache->cache_value('stats_perfect_count', $PerfectCount, 604890); // staggered 1 week cache
+}
+
+// Begin request stats
+if (($RequestStats = $Cache->get_value('stats_requests')) === false) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM requests");
+ list($RequestCount) = $DB->next_record();
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM requests
+ WHERE FillerID > 0");
+ list($FilledCount) = $DB->next_record();
+ $Cache->cache_value('stats_requests', array($RequestCount, $FilledCount), 11280);
+} else {
+ list($RequestCount, $FilledCount) = $RequestStats;
+}
+
+
+// Begin swarm stats
+if (($PeerStats = $Cache->get_value('stats_peers')) === false) {
+ //Cache lock!
+ if ($Cache->get_query_lock('peer_stats')) {
+ $DB->query("
+ SELECT IF(remaining=0,'Seeding','Leeching') AS Type, COUNT(uid)
+ FROM xbt_files_users
+ WHERE active = 1
+ GROUP BY Type");
+ $PeerCount = $DB->to_array(0, MYSQLI_NUM, false);
+ $LeecherCount = isset($PeerCount['Leeching']) ? $PeerCount['Leeching'][1] : 0;
+ $SeederCount = isset($PeerCount['Seeding']) ? $PeerCount['Seeding'][1] : 0;
+ $Cache->cache_value('stats_peers', array($LeecherCount, $SeederCount), 1209600); // 2 week cache
+ $Cache->clear_query_lock('peer_stats');
+ } else {
+ $LeecherCount = $SeederCount = 0;
+ }
+} else {
+ list($LeecherCount, $SeederCount) = $PeerStats;
+}
+
+json_print("success", array(
+ 'maxUsers' => USER_LIMIT,
+ 'enabledUsers' => (int) $UserCount,
+ 'usersActiveThisDay' => (int) $UserStats['Day'],
+ 'usersActiveThisWeek' => (int) $UserStats['Week'],
+ 'usersActiveThisMonth' => (int) $UserStats['Month'],
+
+ 'torrentCount' => (int) $TorrentCount,
+ 'releaseCount' => (int) $AlbumCount,
+ 'artistCount' => (int) $ArtistCount,
+ 'perfectFlacCount' => (int) $PerfectCount,
+
+ 'requestCount' => (int) $RequestCount,
+ 'filledRequestCount' => (int) $FilledCount,
+
+ 'seederCount' => (int) $SeederCount,
+ 'leecherCount' => (int) $LeecherCount
+));
?>
diff --git a/sections/ajax/subscriptions.php b/sections/ajax/subscriptions.php
index 98aea2f4..c8daeeb5 100644
--- a/sections/ajax/subscriptions.php
+++ b/sections/ajax/subscriptions.php
@@ -86,7 +86,7 @@
$JsonPosts[] = $JsonPost;
}
-json_die('success', array(
+json_print('success', array(
'threads' => $JsonPosts
));
?>
diff --git a/sections/ajax/tcomments.php b/sections/ajax/tcomments.php
index d167cf5b..9dc513f7 100644
--- a/sections/ajax/tcomments.php
+++ b/sections/ajax/tcomments.php
@@ -31,7 +31,7 @@
);
}
-json_die("success", array(
+json_print("success", array(
'page' => (int)$Page,
'pages' => ceil($NumComments / TORRENT_COMMENTS_PER_PAGE),
'comments' => $JsonComments
diff --git a/sections/ajax/torrent.php b/sections/ajax/torrent.php
index dedafeea..f6dd87cb 100644
--- a/sections/ajax/torrent.php
+++ b/sections/ajax/torrent.php
@@ -128,4 +128,4 @@
'username' => $Userinfo['Username']
);
-json_die("success", array('group' => $JsonTorrentDetails, 'torrent' => array_pop($JsonTorrentList)));
+json_print("success", array('group' => $JsonTorrentDetails, 'torrent' => array_pop($JsonTorrentList)));
diff --git a/sections/ajax/torrentgroup.php b/sections/ajax/torrentgroup.php
index e95658cd..bbf9974e 100644
--- a/sections/ajax/torrentgroup.php
+++ b/sections/ajax/torrentgroup.php
@@ -118,4 +118,4 @@
);
}
-json_die("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));
+json_print("success", array('group' => $JsonTorrentDetails, 'torrents' => $JsonTorrentList));
diff --git a/sections/ajax/user.php b/sections/ajax/user.php
index 702a6d6f..1ef89abb 100644
--- a/sections/ajax/user.php
+++ b/sections/ajax/user.php
@@ -101,8 +101,10 @@ function check_paranoia_here($Setting) {
WHERE UserID = '$UserID'");
list($Uploads) = $DB->next_record();
} else {
- $RequestsVoted = 0;
- $TotalSpent = 0;
+ $RequestsFilled = null;
+ $TotalBounty = null;
+ $RequestsVoted = null;
+ $TotalSpent = null;
}
if (check_paranoia_here('uploads+')) {
$DB->query("
@@ -121,7 +123,7 @@ function check_paranoia_here($Setting) {
WHERE UserID = $UserID");
list($ArtistsAdded) = $DB->next_record();
} else {
- $ArtistsAdded = 0;
+ $ArtistsAdded = null;
}
// Do the ranks.
@@ -189,6 +191,33 @@ function check_paranoia_here($Setting) {
list($NumComments) = $DB->next_record();
}
+if (check_paranoia_here('torrentcomments+')) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM comments
+ WHERE Page = 'artist'
+ AND AuthorID = '$UserID'");
+ list($NumArtistComments) = $DB->next_record();
+}
+
+if (check_paranoia_here('torrentcomments+')) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM comments
+ WHERE Page = 'collages'
+ AND AuthorID = '$UserID'");
+ list($NumCollageComments) = $DB->next_record();
+}
+
+if (check_paranoia_here('torrentcomments+')) {
+ $DB->query("
+ SELECT COUNT(ID)
+ FROM comments
+ WHERE Page = 'requests'
+ AND AuthorID = '$UserID'");
+ list($NumRequestComments) = $DB->next_record();
+}
+
if (check_paranoia_here('collages+')) {
$DB->query("
SELECT COUNT(ID)
@@ -304,7 +333,7 @@ function check_paranoia_here($Setting) {
header('Content-Type: text/plain; charset=utf-8');
-json_die("success", array(
+json_print("success", array(
'username' => $Username,
'avatar' => $Avatar,
'isFriend' => $Friend,
@@ -338,18 +367,24 @@ function check_paranoia_here($Setting) {
),
'community' => array(
'posts' => (int)$ForumPosts,
- 'torrentComments' => (int)$NumComments,
+ 'torrentComments' => (($NumComments == null) ? null : (int)$NumComments),
+ 'artistComments' => (($NumArtistComments == null) ? null : (int)$NumArtistComments),
+ 'collageComments' => (($NumCollageComments == null) ? null : (int)$NumCollageComments),
+ 'requestComments' => (($NumRequestComments == null) ? null : (int)$NumRequestComments),
'collagesStarted' => (($NumCollages == null) ? null : (int)$NumCollages),
'collagesContrib' => (($NumCollageContribs == null) ? null : (int)$NumCollageContribs),
'requestsFilled' => (($RequestsFilled == null) ? null : (int)$RequestsFilled),
+ 'bountyEarned' => (($TotalBounty == null) ? null : (int)$TotalBounty),
'requestsVoted' => (($RequestsVoted == null) ? null : (int)$RequestsVoted),
+ 'bountySpent' => (($TotalSpent == null) ? null : (int)$TotalSpent),
'perfectFlacs' => (($PerfectFLACs == null) ? null : (int)$PerfectFLACs),
'uploaded' => (($Uploads == null) ? null : (int)$Uploads),
'groups' => (($UniqueGroups == null) ? null : (int)$UniqueGroups),
'seeding' => (($Seeding == null) ? null : (int)$Seeding),
'leeching' => (($Leeching == null) ? null : (int)$Leeching),
'snatched' => (($Snatched == null) ? null : (int)$Snatched),
- 'invited' => (($Invited == null) ? null : (int)$Invited)
+ 'invited' => (($Invited == null) ? null : (int)$Invited),
+ 'artistsAdded' => (($ArtistsAdded == null) ? null : (int)$ArtistsAdded)
)
));
?>
diff --git a/sections/ajax/user_recents.php b/sections/ajax/user_recents.php
index c81ed1e8..fc22b979 100644
--- a/sections/ajax/user_recents.php
+++ b/sections/ajax/user_recents.php
@@ -62,7 +62,7 @@
$Results['uploads'] = "hidden";
}
-json_die("success", $Results);
+json_print("success", $Results);
function check_paranoia_here($Setting) {
global $Paranoia, $Class, $UserID, $Preview;
diff --git a/sections/ajax/usersearch.php b/sections/ajax/usersearch.php
index 98e6cc7f..249f992f 100644
--- a/sections/ajax/usersearch.php
+++ b/sections/ajax/usersearch.php
@@ -51,7 +51,7 @@
);
}
-json_die("success", array(
+json_print("success", array(
'currentPage' => (int)$Page,
'pages' => ceil($NumResults / USERS_PER_PAGE),
'results' => $JsonUsers
diff --git a/sections/ajax/wiki.php b/sections/ajax/wiki.php
index 62153945..9c3b1d5e 100644
--- a/sections/ajax/wiki.php
+++ b/sections/ajax/wiki.php
@@ -23,7 +23,7 @@
Text::$TOC = true;
$TextBody = Text::full_format($Body, false);
-json_die("success", array(
+json_print("success", array(
'title' => $Title,
'bbBody' => $Body,
'body' => $TextBody,
diff --git a/sections/artist/concerts.php b/sections/artist/concerts.php
index 99cb3929..8bd75c7e 100644
--- a/sections/artist/concerts.php
+++ b/sections/artist/concerts.php
@@ -16,7 +16,7 @@
make_concert_link($Event);
}
} else { // Single event
- make_concert_link($ArtistEvents['events']['event']);
+ make_concert_link($ArtistEvents['events']['event'], $Name);
}
echo '';
}
@@ -34,7 +34,7 @@
-function make_concert_link($Event) {
+function make_concert_link($Event, $Name) {
// The event doesn't have a start date (this should never happen)
if ($Event['startDate'] == '') {
return;
@@ -46,7 +46,7 @@ function make_concert_link($Event) {
?>
@@ -80,7 +80,7 @@ function get_concert_post_template($Artist, $Event) {
if (strpos ($Url, '://') === false) {
$Url = 'http://' . $Url;
}
- $Website = '[b]Web site:[/b] ' . $Url;
+ $Website = '[b]Website:[/b] ' . $Url;
}
if (isset($Event['artists']['artist']) && (count($Event['artists']['artist']) === 1 && strtolower($Event['artists']['artist'][1]) == strtolower($Artist))) {
$i = 0;
diff --git a/sections/artist/download.php b/sections/artist/download.php
index 51702bde..7ec57041 100644
--- a/sections/artist/download.php
+++ b/sections/artist/download.php
@@ -81,8 +81,14 @@
if (!$DB->has_results()) {
error(404);
}
-$Releases = $DB->to_array('GroupID', MYSQLI_ASSOC, false);
-$GroupIDs = array_keys($Releases);
+$ArtistRoles = array();
+while (list($GroupID, $Importance) = $DB->next_record(MYSQLI_NUM, false)) {
+ // Get the highest importances to place the .torrents in the most relevant folders
+ if (!isset($ArtistRoles[$GroupID]) || $Importance < $ArtistRoles[$GroupID]) {
+ $ArtistRoles[$GroupID] = $Importance;
+ }
+}
+$GroupIDs = array_keys($ArtistRoles);
$SQL = 'SELECT CASE ';
@@ -163,12 +169,40 @@
$Collector->skip_file($Download);
continue;
}
- if ($Releases[$GroupID]['Importance'] == 1) {
- $ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']];
- } elseif ($Releases[$GroupID]['Importance'] == 2) {
- $ReleaseTypeName = 'Guest Appearance';
- } elseif ($Releases[$GroupID]['Importance'] == 3) {
- $ReleaseTypeName = 'Remixed By';
+ switch ($ArtistRoles[$GroupID]) {
+ /**
+ * 1 => Main artist
+ * 2 => Guest artist
+ * 3 => Remixer
+ * 4 => Composer
+ * 5 => Conductor
+ * 6 => DJ / Compiler
+ * 7 => Producer
+ */
+ case '1':
+ $ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']];
+ break;
+ case '2':
+ $ReleaseTypeName = 'Guest Appearance';
+ break;
+ case '3':
+ $ReleaseTypeName = 'Remixed By';
+ break;
+ case '4':
+ $ReleaseTypeName = 'Composition';
+ break;
+ case '5':
+ $ReleaseTypeName = 'Conducted By';
+ break;
+ case '6':
+ $ReleaseTypeName = 'DJ Mix';
+ break;
+ case '7':
+ $ReleaseTypeName = 'Produced By';
+ break;
+ default:
+ $ReleaseTypeName = 'Other';
+ break;
}
$Collector->add_file($TorrentFile, $Download, $ReleaseTypeName);
unset($Download);
diff --git a/sections/login/index.php b/sections/login/index.php
index 72825d33..630324d5 100644
--- a/sections/login/index.php
+++ b/sections/login/index.php
@@ -30,21 +30,22 @@
// Recover password
if (!empty($_REQUEST['key'])) {
// User has entered a new password, use step 2
+
$DB->query("
SELECT
m.ID,
m.Email,
m.ipcc,
i.ResetExpires
- FROM users_main AS m
+ FROM users_main as m
INNER JOIN users_info AS i ON i.UserID = m.ID
WHERE i.ResetKey = '".db_string($_REQUEST['key'])."'
AND i.ResetKey != ''
AND m.Enabled = '1'");
list($UserID, $Email, $Country, $Expires) = $DB->next_record();
-
if ($UserID && strtotime($Expires) > time()) {
- // If the user has requested a password change, and his key has not expired
+
+ // If the user has requested a password change, and his key has not expired
$Validate->SetFields('password', '1', 'regex', 'You entered an invalid password. A strong password is 8 characters or longer, contains at least 1 lowercase and uppercase letter, contains at least a number or symbol', array('regex' => '/(?=^.{8,}$)(?=.*[^a-zA-Z])(?=.*[A-Z])(?=.*[a-z]).*$/'));
$Validate->SetFields('verifypassword', '1', 'compare', 'Your passwords did not match.', array('comparefield' => 'password'));
diff --git a/sections/requests/take_fill.php b/sections/requests/take_fill.php
index 3a19a103..c2f229e6 100644
--- a/sections/requests/take_fill.php
+++ b/sections/requests/take_fill.php
@@ -219,5 +219,8 @@
$SphQL = new SphinxqlQuery();
$SphQL->raw_query("UPDATE requests, requests_delta SET torrentid = $TorrentID, fillerid = $FillerID WHERE id = $RequestID", false);
+
+
+
header("Location: requests.php?action=view&id=$RequestID");
?>
diff --git a/sections/schedule/index.php b/sections/schedule/index.php
index 480fa2c1..69cc14dc 100644
--- a/sections/schedule/index.php
+++ b/sections/schedule/index.php
@@ -825,7 +825,7 @@ function next_hour() {
GROUP BY um.ID");
while (list($Username, $Email) = $DB->next_record()) {
$Body = "Hi $Username,\n\nIt has been almost 4 months since you used your account at ".site_url().". This is an automated email to inform you that your account will be disabled in 10 days if you do not sign in.";
- Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body);
+ Misc::send_email($Email, 'Your '.SITE_NAME.' account is about to be disabled', $Body, 'noreply');
}
$DB->query("
@@ -878,22 +878,29 @@ function next_hour() {
//------------- Demote users --------------------------------------------//
sleep(10);
+ // Demote to Member when the ratio falls below 0.95 or they have less than 25 GB upload
+ $DemoteClasses = [POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM];
$Query = $DB->query('
SELECT ID
FROM users_main
- WHERE PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
- AND Uploaded / Downloaded < 0.95
- OR PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
- AND Uploaded < 25 * 1024 * 1024 * 1024');
+ WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ')
+ AND (
+ Uploaded / Downloaded < 0.95
+ OR Uploaded < 25 * 1024 * 1024 * 1024
+ )');
echo "demoted 1\n";
$DB->query('
- UPDATE users_main
- SET PermissionID = '.MEMBER.'
- WHERE PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
- AND Uploaded / Downloaded < 0.95
- OR PermissionID IN('.POWER.', '.ELITE.', '.TORRENT_MASTER.')
- AND Uploaded < 25 * 1024 * 1024 * 1024');
+ UPDATE users_info AS ui
+ JOIN users_main AS um ON um.ID = ui.UserID
+ SET
+ um.PermissionID = ' . MEMBER . ",
+ ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(MEMBER) . " by System\n\n', ui.AdminComment)
+ WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ')
+ AND (
+ um.Uploaded / um.Downloaded < 0.95
+ OR um.Uploaded < 25 * 1024 * 1024 * 1024
+ )');
$DB->set_query_id($Query);
while (list($UserID) = $DB->next_record()) {
/*$Cache->begin_transaction("user_info_$UserID");
@@ -905,17 +912,22 @@ function next_hour() {
}
echo "demoted 2\n";
+ // Demote to User when the ratio drops below 0.65
+ $DemoteClasses = [MEMBER, POWER, ELITE, TORRENT_MASTER, POWER_TM, ELITE_TM];
$Query = $DB->query('
SELECT ID
FROM users_main
- WHERE PermissionID IN('.MEMBER.', '.POWER.', '.ELITE.', '.TORRENT_MASTER.')
+ WHERE PermissionID IN(' . implode(', ', $DemoteClasses) . ')
AND Uploaded / Downloaded < 0.65');
echo "demoted 3\n";
$DB->query('
- UPDATE users_main
- SET PermissionID = '.USER.'
- WHERE PermissionID IN('.MEMBER.', '.POWER.', '.ELITE.', '.TORRENT_MASTER.')
- AND Uploaded / Downloaded < 0.65');
+ UPDATE users_info AS ui
+ JOIN users_main AS um ON um.ID = ui.UserID
+ SET
+ um.PermissionID = ' . USER . ",
+ ui.AdminComment = CONCAT('" . sqltime() . ' - Class changed to ' . Users::make_class_string(USER) . " by System\n\n', ui.AdminComment)
+ WHERE um.PermissionID IN (" . implode(', ', $DemoteClasses) . ')
+ AND um.Uploaded / um.Downloaded < 0.65');
$DB->set_query_id($Query);
while (list($UserID) = $DB->next_record()) {
/*$Cache->begin_transaction("user_info_$UserID");
diff --git a/sections/tools/development/update_geoip.php b/sections/tools/development/update_geoip.php
index 6ddfaaa2..3cadf214 100644
--- a/sections/tools/development/update_geoip.php
+++ b/sections/tools/development/update_geoip.php
@@ -13,7 +13,7 @@
shell_exec('unzip GeoLiteCity-latest.zip');
shell_exec('rm GeoLiteCity-latest.zip');
-if (($Locations = file("GeoLiteCity_".date('Ym')."05/GeoLiteCity-Location.csv", FILE_IGNORE_NEW_LINES)) === false) {
+if (($Locations = file("GeoLiteCity_".date('Ym')."02/GeoLiteCity-Location.csv", FILE_IGNORE_NEW_LINES)) === false) {
error('Download or extraction of maxmind database failed');
}
array_shift($Locations);
@@ -32,7 +32,7 @@
echo 'There are '.count($CountryIDs).' CountryIDs';
echo '
';
-if (($Blocks = file("GeoLiteCity_".date('Ym')."05/GeoLiteCity-Blocks.csv", FILE_IGNORE_NEW_LINES)) === false) {
+if (($Blocks = file("GeoLiteCity_".date('Ym')."02/GeoLiteCity-Blocks.csv", FILE_IGNORE_NEW_LINES)) === false) {
echo 'Error';
}
array_shift($Blocks);
diff --git a/sections/tools/index.php b/sections/tools/index.php
index 4fa8fa83..e633154c 100644
--- a/sections/tools/index.php
+++ b/sections/tools/index.php
@@ -168,15 +168,14 @@
$DB->query("
INSERT INTO news (UserID, Title, Body, Time)
VALUES ('$LoggedUser[ID]', '".db_string($_POST['title'])."', '".db_string($_POST['body'])."', '".sqltime()."')");
+ $Cache->delete_value('news_latest_id');
+ $Cache->delete_value('news_latest_title');
+ $Cache->delete_value('news');
NotificationsManager::send_push(NotificationsManager::get_push_enabled_users(), $_POST['title'], $_POST['body'], site_url() . 'index.php', NotificationsManager::NEWS);
- $Cache->delete_value('news_latest_id');
- $Cache->delete_value('news_latest_title');
- $Cache->delete_value('news');
-
header('Location: index.php');
break;
diff --git a/sections/top10/index.php b/sections/top10/index.php
index a2f13aad..7175f588 100644
--- a/sections/top10/index.php
+++ b/sections/top10/index.php
@@ -35,6 +35,7 @@
case 'lastfm':
include(SERVER_ROOT.'/sections/top10/lastfm.php');
break;
+
default:
error(404);
break;
diff --git a/sections/torrents/browse.php b/sections/torrents/browse.php
index 5268bc7a..4dd1dc73 100644
--- a/sections/torrents/browse.php
+++ b/sections/torrents/browse.php
@@ -1,23 +1,4 @@
-/************************************************************************
-*-------------------- Browse page ---------------------------------------
-* Welcome to one of the most complicated pages in all of gazelle - the
-* browse page.
-*
-* This is the page that is displayed when someone visits torrents.php
-*
-* It offers normal and advanced search, as well as enabled/disabled
-* grouping.
-*
-* Don't blink.
-* Blink and you're dead.
-* Don't turn your back.
-* Don't look away.
-* And don't blink.
-* Good Luck.
-*
-*************************************************************************/
-
include(SERVER_ROOT.'/sections/torrents/functions.php');
// The "order by x" links on columns headers
@@ -35,7 +16,6 @@ function header_link($SortKey, $DefaultWay = 'desc') {
return "torrents.php?order_way=$NewWay&order_by=$SortKey&".Format::get_url(array('order_way', 'order_by'));
}
-/** Start default parameters and validation **/
if (!empty($_GET['searchstr']) || !empty($_GET['groupname'])) {
if (!empty($_GET['searchstr'])) {
$InfoHash = $_GET['searchstr'];
@@ -113,422 +93,29 @@ function header_link($SortKey, $DefaultWay = 'desc') {
}
}
// Terms were not submitted via the search form
-if (!isset($_GET['searchsubmit'])) {
- $_GET['group_results'] = !$LoggedUser['DisableGrouping2'];
-}
-
-if (isset($_GET['group_results']) && $_GET['group_results']) {
- $_GET['group_results'] = 1;
- $GroupResults = true;
- $SortOrders = array(
- // 'url attr' => [global order, order within group]
- 'year' => array('year', 'year'),
- 'time' => array('id', 'id'),
- 'size' => array('maxsize', 'size'),
- 'seeders' => array('sumseeders', 'seeders'),
- 'leechers' => array('sumleechers', 'leechers'),
- 'snatched' => array('sumsnatched', 'snatched'),
- 'random' => false);
-
- $AggregateExp = array(
- 'maxsize' => 'MAX(size) AS maxsize',
- 'sumseeders' => 'SUM(seeders) AS sumseeders',
- 'sumleechers' => 'SUM(leechers) AS sumleechers',
- 'sumsnatched' => 'SUM(snatched) AS sumsnatched');
+if (isset($_GET['searchsubmit'])) {
+ $GroupResults = !empty($_GET['group_results']);
} else {
- $GroupResults = false;
- $SortOrders = array(
- 'year' => 'year',
- 'time' => 'id',
- 'size' => 'size',
- 'seeders' => 'seeders',
- 'leechers' => 'leechers',
- 'snatched' => 'snatched',
- 'random' => false);
-}
-
-if (empty($_GET['order_by']) || !isset($SortOrders[$_GET['order_by']])) {
- $_GET['order_by'] = 'time';
- $OrderBy = 'time'; // For header links
-} else {
- $OrderBy = $_GET['order_by'];
+ $GroupResults = !$LoggedUser['DisableGrouping2'];
}
if (!empty($_GET['order_way']) && $_GET['order_way'] == 'asc') {
$OrderWay = 'asc';
} else {
- $_GET['order_way'] = 'desc';
$OrderWay = 'desc';
}
-/** End default parameters and validation **/
-
-/** Start preparation of property arrays **/
-array_pop($Bitrates); // remove 'other'
-$SearchBitrates = array_merge($Bitrates, array('v0', 'v1', 'v2', '24bit'));
-
-foreach ($SearchBitrates as $ID => $Val) {
- $SearchBitrates[$ID] = strtolower($Val);
-}
-foreach ($Formats as $ID => $Val) {
- $SearchFormats[$ID] = strtolower($Val);
-}
-/** End preparation of property arrays **/
-
-/** Start query preparation **/
-$SphQL = new SphinxqlQuery();
-$SphQLTor = new SphinxqlQuery();
-
-if ($OrderBy == 'random') {
- $SphQL->select('id, groupid, categoryid')
- ->order_by('RAND()', '');
- $Random = true;
-} elseif ($GroupResults) {
- $OrderProperties = $SortOrders[$OrderBy];
- $SphQL->select('groupid, categoryid' . (isset($AggregateExp[$OrderProperties[0]]) ? ', '.$AggregateExp[$OrderProperties[0]] : ''))
- ->group_by('groupid')
- ->order_by($OrderProperties[0], $OrderWay)
- ->order_group_by($OrderProperties[1], $OrderWay);
-
+if (empty($_GET['order_by']) || !isset(TorrentSearch::$SortOrders[$_GET['order_by']])) {
+ $OrderBy = 'time'; // For header links
} else {
- $SphQL->select('id, groupid, categoryid')
- ->order_by($SortOrders[$OrderBy], $OrderWay);
-}
-$SphQL->from('torrents, delta');
-$SphQLTor->select('id')->from('torrents, delta');
-/** End query preparation **/
-
-/** Start building search query **/
-$Filtered = false;
-$EnableNegation = false; // Sphinx needs at least one positive search condition to support the NOT operator
-
-// File list searches make use of the proximity operator to ensure that all keywords match the same file
-if (!empty($_GET['filelist'])) {
- $SearchString = trim($_GET['filelist']);
- if ($SearchString !== '') {
- $SearchString = '"'.Sphinxql::sph_escape_string($_GET['filelist']).'"~20';
- $SphQL->where_match($SearchString, 'filelist', false);
- $SphQLTor->where_match($SearchString, 'filelist', false);
- $EnableNegation = true;
- $Filtered = true;
- }
+ $OrderBy = $_GET['order_by'];
}
-// Collect all entered search terms to find out whether to enable the NOT operator
-$SearchWords = array();
-foreach (array('artistname', 'groupname', 'recordlabel', 'cataloguenumber',
- 'taglist', 'remastertitle', 'remasteryear', 'remasterrecordlabel',
- 'remastercataloguenumber', 'encoding', 'format', 'media', 'description') as $Search) {
- if (!empty($_GET[$Search])) {
- $SearchString = trim($_GET[$Search]);
- if ($SearchString !== '') {
- $SearchWords[$Search] = array('include' => array(), 'exclude' => array());
- if ($Search == 'taglist') {
- $SearchString = strtr($SearchString, '.', '_');
- $Words = explode(',', $SearchString);
- } else {
- $Words = explode(' ', $SearchString);
- }
- foreach ($Words as $Word) {
- $Word = trim($Word);
- // Skip isolated hyphens to enable "Artist - Title" searches
- if ($Word === '-') {
- continue;
- }
- if ($Word[0] === '!' && strlen($Word) >= 2) {
- if (strpos($Word, '!', 1) === false) {
- $SearchWords[$Search]['exclude'][] = $Word;
- } else {
- $SearchWords[$Search]['include'][] = $Word;
- $EnableNegation = true;
- }
- } elseif ($Word !== '') {
- $SearchWords[$Search]['include'][] = $Word;
- $EnableNegation = true;
- }
- }
- }
- }
-}
-
-//Simple search
-if (!empty($_GET['searchstr'])) {
- $SearchString = trim($_GET['searchstr']);
- $Words = explode(' ', strtolower($SearchString));
- if (!empty($Words)) {
- $FilterBitrates = $FilterFormats = array();
- $BasicSearch = array('include' => array(), 'exclude' => array());
- foreach ($Words as $Word) {
- $Word = trim($Word);
- // Skip isolated hyphens to enable "Artist - Title" searches
- if ($Word === '-') {
- continue;
- }
- if ($Word[0] === '!' && strlen($Word) >= 2) {
- if ($Word === '!100%') {
- $_GET['haslog'] = '-1';
- } elseif (strpos($Word, '!', 1) === false) {
- $BasicSearch['exclude'][] = $Word;
- } else {
- $BasicSearch['include'][] = $Word;
- $EnableNegation = true;
- }
- } elseif (in_array($Word, $SearchBitrates)) {
- $FilterBitrates[] = $Word;
- $EnableNegation = true;
- } elseif (in_array($Word, $SearchFormats)) {
- $FilterFormats[] = $Word;
- $EnableNegation = true;
- } elseif ($Word === '100%') {
- $_GET['haslog'] = '100';
- } elseif ($Word !== '') {
- $BasicSearch['include'][] = $Word;
- $EnableNegation = true;
- }
- }
- if (!$EnableNegation && !empty($BasicSearch['exclude'])) {
- $BasicSearch['include'] = array_merge($BasicSearch['include'], $BasicSearch['exclude']);
- unset($BasicSearch['exclude']);
- }
- $QueryParts = array();
- foreach ($BasicSearch['include'] as $Word) {
- $QueryParts[] = Sphinxql::sph_escape_string($Word);
- }
- if (!empty($BasicSearch['exclude'])) {
- foreach ($BasicSearch['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
- }
- }
- if (!empty($FilterBitrates)) {
- $SearchString = implode(' ', $FilterBitrates);
- $SphQL->where_match($SearchString, 'encoding', false);
- $SphQLTor->where_match($SearchString, 'encoding', false);
- $Filtered = true;
- }
- if (!empty($FilterFormats)) {
- $SearchString = implode(' ', $FilterFormats);
- $SphQL->where_match($SearchString, 'format', false);
- $SphQLTor->where_match($SearchString, 'format', false);
- $Filtered = true;
- }
- if (!empty($QueryParts)) {
- $SearchString = implode(' ', $QueryParts);
- $SphQL->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
- $SphQLTor->where_match($SearchString, '(groupname,artistname,yearfulltext)', false);
- $Filtered = true;
- }
- }
-}
-
-// Tag list
-if (!empty($SearchWords['taglist'])) {
- $_GET['tags_type'] = (!isset($_GET['tags_type']) || $_GET['tags_type'] == 1) ? '1' : '0';
- $TagType = (int)$_GET['tags_type'];
-
- //Get tags
- $Tags = $SearchWords['taglist'];
-
- $TagFilter = Tags::tag_filter_sph($Tags, $EnableNegation, $TagType);
- $TagListString = $TagFilter['input'];
-
- if (!empty($TagFilter['predicate'])) {
- $SphQL->where_match($TagFilter['predicate'], 'taglist', false);
- $SphQLTor->where_match($TagFilter['predicate'], 'taglist', false);
- $Filtered = true;
- }
- unset($SearchWords['taglist']);
-
-}
-elseif (!isset($_GET['tags_type'])) {
- $_GET['tags_type'] = '1';
-}
-
-foreach ($SearchWords as $Search => $Words) {
- $QueryParts = array();
- if (!$EnableNegation && !empty($Words['exclude'])) {
- $Words['include'] = array_merge($Words['include'], $Words['exclude']);
- unset($Words['exclude']);
- }
- foreach ($Words['include'] as $Word) {
- $QueryParts[] = Sphinxql::sph_escape_string($Word);
- }
- if (!empty($Words['exclude'])) {
- foreach ($Words['exclude'] as $Word) {
- $QueryParts[] = '!'.Sphinxql::sph_escape_string(substr($Word, 1));
- }
- }
- if (!empty($QueryParts)) {
- $SearchString = implode(' ', $QueryParts);
- $SphQL->where_match($SearchString, $Search, false);
- $SphQLTor->where_match($SearchString, $Search, false);
- $Filtered = true;
- }
-}
-
-if (!empty($_GET['year'])) {
- $Years = explode('-', $_GET['year']);
- if (is_number($Years[0]) || (empty($Years[0]) && !empty($Years[1]) && is_number($Years[1]))) {
- if (count($Years) === 1) {
- $SphQL->where('year', $Years[0]);
- $SphQLTor->where('year', $Years[0]);
- } else {
- if (empty($Years[0])) {
- $SphQL->where_lt('year', $Years[1], true);
- $SphQLTor->where_lt('year', $Years[1], true);
- } elseif (empty($Years[1]) || !is_number($Years[1])) {
- $SphQL->where_gt('year', $Years[0], true);
- $SphQLTor->where_gt('year', $Years[0], true);
- } else {
- if ($Years[0] > $Years[1]) {
- $Years = array_reverse($Years);
- }
- $SphQL->where_between('year', array($Years[0], $Years[1]));
- $SphQLTor->where_between('year', array($Years[0], $Years[1]));
- }
- }
- $Filtered = true;
- }
-}
-
-if (isset($_GET['haslog']) && $_GET['haslog'] !== '') {
- if ($_GET['haslog'] === '100') {
- $SphQL->where('logscore', 100);
- $SphQLTor->where('logscore', 100);
- } elseif ($_GET['haslog'] < 0) {
- // Look for torrents with log score < 100
- $SphQL->where_lt('logscore', 100);
- $SphQL->where('haslog', 1);
- $SphQLTor->where_lt('logscore', 100);
- $SphQLTor->where('haslog', 1);
- } elseif ($_GET['haslog'] == 0) {
- $SphQL->where('haslog', 0);
- $SphQLTor->where('haslog', 0);
- } else {
- $SphQL->where('haslog', 1);
- $SphQLTor->where('haslog', 1);
- }
- $Filtered = true;
-}
-
-foreach (array('hascue', 'scene', 'vanityhouse', 'releasetype') as $Search) {
- if (isset($_GET[$Search]) && $_GET[$Search] !== '') {
- $SphQL->where($Search, $_GET[$Search]);
- // Release type is group specific
- if ($Search != 'releasetype') {
- $SphQLTor->where($Search, $_GET[$Search]);
- }
- $Filtered = true;
- }
-}
-
-if (isset($_GET['freetorrent']) && $_GET['freetorrent'] !== '') {
- switch ($_GET['freetorrent']) {
- case 0: // Only normal freeleech
- $SphQL->where('freetorrent', 0);
- $SphQLTor->where('freetorrent', 0);
- $Filtered = true;
- break;
- case 1: // Only free leech
- $SphQL->where('freetorrent', 1);
- $SphQLTor->where('freetorrent', 1);
- $Filtered = true;
- break;
- case 2: // Only neutral leech
- $SphQL->where('freetorrent', 2);
- $SphQLTor->where('freetorrent', 2);
- $Filtered = true;
- break;
- case 3: // Free or neutral leech
- $SphQL->where('freetorrent', 0, true);
- $SphQLTor->where('freetorrent', 0, true);
- $Filtered = true;
- break;
- default:
- unset($_GET['freetorrent']);
- break;
- }
-}
-
-if (!empty($_GET['filter_cat'])) {
- $SphQL->where('categoryid', array_keys($_GET['filter_cat']));
- $Filtered = true;
-}
-/** End building search query **/
-
-/** Run search query and collect results **/
-if (isset($Random) && $GroupResults) {
- // ORDER BY RAND() can't be used together with GROUP BY, so we need some special tactics
- $Page = 1;
- $SphQL->limit(0, 5 * TORRENTS_PER_PAGE, 5 * TORRENTS_PER_PAGE);
- $SphQLResult = $SphQL->query();
- $TotalCount = $SphQLResult->get_meta('total_found');
- $Results = $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- $GroupCount = count($GroupIDs);
- while ($SphQLResult->get_meta('total') < $TotalCount && $GroupCount < TORRENTS_PER_PAGE) {
- // Make sure we get TORRENTS_PER_PAGE results, or all of them if there are less than TORRENTS_PER_PAGE hits
- $SphQL->where('groupid', $GroupIDs, true);
- $SphQLResult = $SphQL->query();
- if (!$SphQLResult->has_results()) {
- break;
- }
- $Results += $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- $GroupCount = count($GroupIDs);
- }
- if ($GroupCount > TORRENTS_PER_PAGE) {
- $Results = array_slice($Results, 0, TORRENTS_PER_PAGE, true);
- }
- $GroupIDs = array_keys($Results);
- $NumResults = count($Results);
-} else {
- if (!empty($_GET['page']) && is_number($_GET['page']) && $_GET['page'] > 0) {
- if (check_perms('site_search_many')) {
- $Page = $_GET['page'];
- } else {
- $Page = min(SPHINX_MAX_MATCHES / TORRENTS_PER_PAGE, $_GET['page']);
- }
- $Offset = ($Page - 1) * TORRENTS_PER_PAGE;
- $SphQL->limit($Offset, TORRENTS_PER_PAGE, $Offset + TORRENTS_PER_PAGE);
- } else {
- $Page = 1;
- $SphQL->limit(0, TORRENTS_PER_PAGE, TORRENTS_PER_PAGE);
- }
- $SphQLResult = $SphQL->query();
- $NumResults = $SphQLResult->get_meta('total_found');
- if ($GroupResults) {
- $Results = $SphQLResult->to_array('groupid');
- $GroupIDs = array_keys($Results);
- } else {
- $Results = $SphQLResult->to_array('id');
- $GroupIDs = $SphQLResult->collect('groupid');
- }
-}
-
-if (!check_perms('site_search_many') && $NumResults > SPHINX_MAX_MATCHES) {
- $NumResults = SPHINX_MAX_MATCHES;
-}
-
-if ($NumResults) {
- $Groups = Torrents::get_groups($GroupIDs);
-
- if (!empty($Groups) && $GroupResults) {
- $TorrentIDs = array();
- foreach ($Groups as $Group) {
- if (!empty($Group['Torrents'])) {
- $TorrentIDs = array_merge($TorrentIDs, array_keys($Group['Torrents']));
- }
- }
- $TorrentCount = count($TorrentIDs);
- if ($TorrentCount > 0) {
- // Get a list of all torrent ids that match the search query
- $SphQLTor->where('id', $TorrentIDs)->limit(0, $TorrentCount, $TorrentCount);
- $SphQLResultTor = $SphQLTor->query();
- $TorrentIDs = $SphQLResultTor->to_pair('id', 'id'); // Because isset() is faster than in_array()
- }
- }
-}
-/** End run search query and collect results **/
+$Page = !empty($_GET['page']) ? (int) $_GET['page'] : 1;
+$Search = new TorrentSearch($GroupResults, $OrderBy, $OrderWay, $Page, TORRENTS_PER_PAGE);
+$Results = $Search->query($_GET);
+$Groups = $Search->get_groups();
+$NumResults = $Search->record_count();
$HideFilter = isset($LoggedUser['ShowTorFilter']) && $LoggedUser['ShowTorFilter'] == 0;
// This is kinda ugly, but the enormous if paragraph was really hard to read
@@ -715,7 +302,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
Tags (comma-separated): |
- />
+ />
/>
/>
|
@@ -743,7 +330,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
- />
+ />
|
@@ -818,7 +405,7 @@ function header_link($SortKey, $DefaultWay = 'desc') {
- if ($Filtered) { ?>
+ if ($Search->has_filters()) { ?>
}
@@ -913,13 +500,12 @@ function header_link($SortKey, $DefaultWay = 'desc') {
// Start printing torrent list
-foreach ($Results as $Result) {
- $GroupID = $Result['groupid'];
+foreach ($Results as $Key => $GroupID) {
$GroupInfo = $Groups[$GroupID];
if (empty($GroupInfo['Torrents'])) {
continue;
}
- $CategoryID = $Result['categoryid'];
+ $CategoryID = $GroupInfo['CategoryID'];
$GroupYear = $GroupInfo['Year'];
$ExtendedArtists = $GroupInfo['ExtendedArtists'];
$GroupCatalogueNumber = $GroupInfo['CatalogueNumber'];
@@ -930,16 +516,15 @@ function header_link($SortKey, $DefaultWay = 'desc') {
$Torrents = $GroupInfo['Torrents'];
$GroupTime = $MaxSize = $TotalLeechers = $TotalSeeders = $TotalSnatched = 0;
foreach ($Torrents as $T) {
- if (isset($TorrentIDs[$T['ID']])) {
- $GroupTime = max($GroupTime, strtotime($T['Time']));
- $MaxSize = max($MaxSize, $T['Size']);
- $TotalLeechers += $T['Leechers'];
- $TotalSeeders += $T['Seeders'];
- $TotalSnatched += $T['Snatched'];
- }
+ $GroupTime = max($GroupTime, strtotime($T['Time']));
+ $MaxSize = max($MaxSize, $T['Size']);
+ $TotalLeechers += $T['Leechers'];
+ $TotalSeeders += $T['Seeders'];
+ $TotalSnatched += $T['Snatched'];
}
} else {
- $Torrents = array($Result['id'] => $GroupInfo['Torrents'][$Result['id']]);
+ $TorrentID = $Key;
+ $Torrents = array($TorrentID => $GroupInfo['Torrents'][$TorrentID]);
}
$TorrentTags = new Tags($GroupInfo['TagList']);
@@ -1021,11 +606,6 @@ function header_link($SortKey, $DefaultWay = 'desc') {
foreach ($Torrents as $TorrentID => $Data) {
// All of the individual torrents in the group
- // If they're using the advanced search and have chosen enabled grouping, we just skip the torrents that don't check out
- if (!isset($TorrentIDs[$TorrentID])) {
- continue;
- }
-
//Get report info for each torrent, use the cache if available, if not, add to it.
$Reported = false;
$Reports = Torrents::get_reports($TorrentID);
diff --git a/sections/torrents/download.php b/sections/torrents/download.php
index 77923338..225537f2 100644
--- a/sections/torrents/download.php
+++ b/sections/torrents/download.php
@@ -37,7 +37,7 @@
/* uTorrent Remote and various scripts redownload .torrent files periodically.
To prevent this retardation from blowing bandwidth etc., let's block it
if the .torrent file has been downloaded four times before */
-$ScriptUAs = array('BTWebClient*', 'Python-urllib*', 'python-requests*');
+$ScriptUAs = array('BTWebClient*', 'Python-urllib*', 'python-requests*', 'uTorrent*');
if (Misc::in_array_partial($_SERVER['HTTP_USER_AGENT'], $ScriptUAs)) {
$DB->query("
SELECT 1
diff --git a/sections/user/takemoderate.php b/sections/user/takemoderate.php
index 1078c7ab..425298b7 100644
--- a/sections/user/takemoderate.php
+++ b/sections/user/takemoderate.php
@@ -693,7 +693,7 @@
Once you are connected to our server you will need to join our disabled users channel.
Type: /join '.BOT_DISABLED_CHAN.'
-Please visit us soon so we can help you resolve this matter.');
+Please visit us soon so we can help you resolve this matter.', 'noreply');
}
if ($MergeStatsFrom && check_perms('users_edit_ratio')) {
diff --git a/sections/userhistory/ip_history.php b/sections/userhistory/ip_history.php
index b2688330..20d53d58 100644
--- a/sections/userhistory/ip_history.php
+++ b/sections/userhistory/ip_history.php
@@ -16,35 +16,24 @@
if (!is_number($UserID)) {
error(404);
}
-
-$DB->query("
- SELECT
- um.Username,
- p.Level AS Class
- FROM users_main AS um
- LEFT JOIN permissions AS p ON p.ID = um.PermissionID
- WHERE um.ID = $UserID");
-list($Username, $Class) = $DB->next_record();
-
-if (!check_perms('users_view_ips', $Class)) {
+$UserInfo = Users::user_info($UserID);
+if (!check_perms('users_view_ips', $UserInfo['Class'])) {
error(403);
}
-$UsersOnly = $_GET['usersonly'];
+$UsersOnly = !empty($_GET['usersonly']);
-if (isset($_POST['ip'])) {
- $SearchIP = db_string(str_replace("*", "%", trim($_POST['ip'])));
- $SearchIPQuery = " AND h1.IP LIKE '$SearchIP' ";
+if (!empty($_GET['ip']) && trim($_GET['ip']) != '') {
+ $SearchIP = db_string(str_replace("*", "%", trim($_GET['ip'])));
+ $SearchIPQuery = "AND IP LIKE '$SearchIP'";
+} else {
+ $SearchIPQuery = "";
}
-View::show_header("IP address history for $Username");
+View::show_header("IP address history for $UserInfo[Username]");
?>