Oppaitime's version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

advancedsearch.php 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. <?
  2. if (!empty($_GET['search'])) {
  3. if (preg_match('/^'.IP_REGEX.'$/', $_GET['search'])) {
  4. $_GET['ip'] = $_GET['search'];
  5. } elseif (preg_match('/^'.EMAIL_REGEX.'$/i', $_GET['search'])) {
  6. $_GET['email'] = $_GET['search'];
  7. } elseif (preg_match(USERNAME_REGEX,$_GET['search'])) {
  8. $DB->query("
  9. SELECT ID
  10. FROM users_main
  11. WHERE Username = '".db_string($_GET['search'])."'");
  12. if (list($ID) = $DB->next_record()) {
  13. header("Location: user.php?id=$ID");
  14. die();
  15. }
  16. $_GET['username'] = $_GET['search'];
  17. } else {
  18. $_GET['comment'] = $_GET['search'];
  19. }
  20. }
  21. define('USERS_PER_PAGE', 30);
  22. function wrap($String, $ForceMatch = '', $IPSearch = false) {
  23. if (!$ForceMatch) {
  24. global $Match;
  25. } else {
  26. $Match = $ForceMatch;
  27. }
  28. if ($Match == ' REGEXP ') {
  29. if (strpos($String, '\'') !== false || preg_match('/^.*\\\\$/i', $String)) {
  30. error('Regex contains illegal characters.');
  31. }
  32. } else {
  33. $String = db_string($String);
  34. }
  35. if ($Match == ' LIKE ') {
  36. // Fuzzy search
  37. // Stick in wildcards at beginning and end of string unless string starts or ends with |
  38. if (($String[0] != '|') && !$IPSearch) {
  39. $String = "%$String";
  40. } elseif ($String[0] == '|') {
  41. $String = substr($String, 1, strlen($String));
  42. }
  43. if (substr($String, -1, 1) != '|') {
  44. $String = "$String%";
  45. } else {
  46. $String = substr($String, 0, -1);
  47. }
  48. }
  49. $String = "'$String'";
  50. return $String;
  51. }
  52. function date_compare($Field, $Operand, $Date1, $Date2 = '') {
  53. $Date1 = db_string($Date1);
  54. $Date2 = db_string($Date2);
  55. $Return = array();
  56. switch ($Operand) {
  57. case 'on':
  58. $Return [] = " $Field >= '$Date1 00:00:00' ";
  59. $Return [] = " $Field <= '$Date1 23:59:59' ";
  60. break;
  61. case 'before':
  62. $Return [] = " $Field < '$Date1 00:00:00' ";
  63. break;
  64. case 'after':
  65. $Return [] = " $Field > '$Date1 23:59:59' ";
  66. break;
  67. case 'between':
  68. $Return [] = " $Field >= '$Date1 00:00:00' ";
  69. $Return [] = " $Field <= '$Date2 00:00:00' ";
  70. break;
  71. }
  72. return $Return;
  73. }
  74. function num_compare($Field, $Operand, $Num1, $Num2 = '') {
  75. if ($Num1 != 0) {
  76. $Num1 = db_string($Num1);
  77. }
  78. if ($Num2 != 0) {
  79. $Num2 = db_string($Num2);
  80. }
  81. $Return = array();
  82. switch ($Operand) {
  83. case 'equal':
  84. $Return [] = " $Field = '$Num1' ";
  85. break;
  86. case 'above':
  87. $Return [] = " $Field > '$Num1' ";
  88. break;
  89. case 'below':
  90. $Return [] = " $Field < '$Num1' ";
  91. break;
  92. case 'between':
  93. $Return [] = " $Field > '$Num1' ";
  94. $Return [] = " $Field < '$Num2' ";
  95. break;
  96. default:
  97. print_r($Return);
  98. die();
  99. }
  100. return $Return;
  101. }
  102. // Arrays, regexes, and all that fun stuff we can use for validation, form generation, etc
  103. $DateChoices = array('inarray'=>array('on', 'before', 'after', 'between'));
  104. $SingleDateChoices = array('inarray'=>array('on', 'before', 'after'));
  105. $NumberChoices = array('inarray'=>array('equal', 'above', 'below', 'between', 'buffer'));
  106. $YesNo = array('inarray'=>array('any', 'yes', 'no'));
  107. $OrderVals = array('inarray'=>array('Username', 'Ratio', 'IP', 'Email', 'Joined', 'Last Seen', 'Uploaded', 'Downloaded', 'Invites', 'Snatches'));
  108. $WayVals = array('inarray'=>array('Ascending', 'Descending'));
  109. if (count($_GET)) {
  110. $DateRegex = array('regex' => '/\d{4}-\d{2}-\d{2}/');
  111. $ClassIDs = array();
  112. $SecClassIDs = array();
  113. foreach ($Classes as $ClassID => $Value) {
  114. if ($Value['Secondary']) {
  115. $SecClassIDs[] = $ClassID;
  116. } else {
  117. $ClassIDs[] = $ClassID;
  118. }
  119. }
  120. $Val->SetFields('comment', '0', 'string', 'Comment is too long.', array('maxlength' => 512));
  121. $Val->SetFields('disabled_invites', '0', 'inarray', 'Invalid disabled_invites field', $YesNo);
  122. $Val->SetFields('joined', '0', 'inarray', 'Invalid joined field', $DateChoices);
  123. $Val->SetFields('join1', '0', 'regex', 'Invalid join1 field', $DateRegex);
  124. $Val->SetFields('join2', '0', 'regex', 'Invalid join2 field', $DateRegex);
  125. $Val->SetFields('lastactive', '0', 'inarray', 'Invalid lastactive field', $DateChoices);
  126. $Val->SetFields('lastactive1', '0', 'regex', 'Invalid lastactive1 field', $DateRegex);
  127. $Val->SetFields('lastactive2', '0', 'regex', 'Invalid lastactive2 field', $DateRegex);
  128. $Val->SetFields('ratio', '0', 'inarray', 'Invalid ratio field', $NumberChoices);
  129. $Val->SetFields('uploaded', '0', 'inarray', 'Invalid uploaded field', $NumberChoices);
  130. $Val->SetFields('downloaded', '0', 'inarray', 'Invalid downloaded field', $NumberChoices);
  131. //$Val->SetFields('snatched', '0', 'inarray', 'Invalid snatched field', $NumberChoices);
  132. $Val->SetFields('matchtype', '0', 'inarray', 'Invalid matchtype field', array('inarray' => array('strict', 'fuzzy', 'regex')));
  133. $Val->SetFields('lockedaccount', '0', 'inarray', 'Invalid locked account field', array('inarray' => array('any', 'locked', 'unlocked')));
  134. $Val->SetFields('enabled', '0', 'inarray', 'Invalid enabled field', array('inarray' => array('', 0, 1, 2)));
  135. $Val->SetFields('class', '0', 'inarray', 'Invalid class', array('inarray' => $ClassIDs));
  136. $Val->SetFields('secclass', '0', 'inarray', 'Invalid class', array('inarray' => $SecClassIDs));
  137. $Val->SetFields('donor', '0', 'inarray', 'Invalid donor field', $YesNo);
  138. $Val->SetFields('warned', '0', 'inarray', 'Invalid warned field', $YesNo);
  139. $Val->SetFields('disabled_uploads', '0', 'inarray', 'Invalid disabled_uploads field', $YesNo);
  140. $Val->SetFields('order', '0', 'inarray', 'Invalid ordering', $OrderVals);
  141. $Val->SetFields('way', '0', 'inarray', 'Invalid way', $WayVals);
  142. $Val->SetFields('passkey', '0', 'string', 'Invalid passkey', array('maxlength' => 32));
  143. $Val->SetFields('avatar', '0', 'string', 'Avatar URL too long', array('maxlength' => 512));
  144. $Val->SetFields('stylesheet', '0', 'inarray', 'Invalid stylesheet', array_unique(array_keys($Stylesheets)));
  145. $Val->SetFields('cc', '0', 'inarray', 'Invalid Country Code', array('maxlength' => 2));
  146. $Err = $Val->ValidateForm($_GET);
  147. if (!$Err) {
  148. // Passed validation. Let's rock.
  149. $RunQuery = false; // if we should run the search
  150. if (isset($_GET['matchtype']) && $_GET['matchtype'] == 'strict') {
  151. $Match = ' = ';
  152. } elseif (isset($_GET['matchtype']) && $_GET['matchtype'] == 'regex') {
  153. $Match = ' REGEXP ';
  154. } else {
  155. $Match = ' LIKE ';
  156. }
  157. $OrderTable = array(
  158. 'Username' => 'um1.Username',
  159. 'Joined' => 'ui1.JoinDate',
  160. 'Email' => 'um1.Email',
  161. 'IP' => 'um1.IP',
  162. 'Last Seen' => 'um1.LastAccess',
  163. 'Uploaded' => 'um1.Uploaded',
  164. 'Downloaded' => 'um1.Downloaded',
  165. 'Ratio' => '(um1.Uploaded / um1.Downloaded)',
  166. 'Invites' => 'um1.Invites',
  167. 'Snatches' => 'Snatches');
  168. $WayTable = array('Ascending'=>'ASC', 'Descending'=>'DESC');
  169. $Where = array();
  170. $Having = array();
  171. $Join = array();
  172. $Group = array();
  173. $Distinct = '';
  174. $Order = '';
  175. $SQL = '
  176. SQL_CALC_FOUND_ROWS
  177. um1.ID,
  178. um1.Username,
  179. um1.Uploaded,
  180. um1.Downloaded,';
  181. if ($_GET['snatched'] == 'off') {
  182. $SQL .= "'X' AS Snatches,";
  183. } else {
  184. $SQL .= "
  185. (
  186. SELECT COUNT(xs.uid)
  187. FROM xbt_snatched AS xs
  188. WHERE xs.uid = um1.ID
  189. ) AS Snatches,";
  190. }
  191. if ($_GET['invitees'] == 'off') {
  192. $SQL .= "'X' AS Invitees,";
  193. } else {
  194. $SQL .= "
  195. (
  196. SELECT COUNT(ui2.UserID)
  197. FROM users_info AS ui2
  198. WHERE um1.ID = ui2.Inviter
  199. ) AS Invitees,";
  200. }
  201. $SQL .= '
  202. um1.PermissionID,
  203. um1.Email,
  204. um1.Enabled,
  205. um1.IP,
  206. um1.Invites,
  207. ui1.DisableInvites,
  208. ui1.Warned,
  209. ui1.Donor,
  210. ui1.JoinDate,
  211. um1.LastAccess
  212. FROM users_main AS um1
  213. JOIN users_info AS ui1 ON ui1.UserID = um1.ID ';
  214. if (!empty($_GET['username'])) {
  215. $Where[] = 'um1.Username'.$Match.wrap($_GET['username']);
  216. }
  217. if (!empty($_GET['email'])) {
  218. if (isset($_GET['email_history'])) {
  219. $Distinct = 'DISTINCT ';
  220. }
  221. $Join['the'] = ' JOIN users_emails_decrypted AS he ON he.ID = um1.ID ';
  222. $Where[] = ' he.Email '.$Match.wrap($_GET['email']);
  223. }
  224. if (!empty($_GET['email_cnt']) && is_number($_GET['email_cnt'])) {
  225. $Query = "
  226. SELECT UserID
  227. FROM users_history_emails
  228. GROUP BY UserID
  229. HAVING COUNT(DISTINCT Email) ";
  230. if ($_GET['emails_opt'] === 'equal') {
  231. $operator = '=';
  232. }
  233. if ($_GET['emails_opt'] === 'above') {
  234. $operator = '>';
  235. }
  236. if ($_GET['emails_opt'] === 'below') {
  237. $operator = '<';
  238. }
  239. $Query .= $operator.' '.$_GET['email_cnt'];
  240. $DB->query($Query);
  241. $Users = implode(',', $DB->collect('UserID'));
  242. if (!empty($Users)) {
  243. $Where[] = "um1.ID IN ($Users)";
  244. }
  245. }
  246. if (!empty($_GET['ip'])) {
  247. if (isset($_GET['ip_history'])) {
  248. $Distinct = 'DISTINCT ';
  249. }
  250. $Join['tip'] = ' JOIN users_ips_decrypted AS tip ON tip.ID = um1.ID ';
  251. $Where[] = ' tip.IP '.$Match.wrap($_GET['ip'], '', true);
  252. }
  253. if ($_GET['lockedaccount'] != '' && $_GET['lockedaccount'] != 'any') {
  254. $Join['la'] = '';
  255. if ($_GET['lockedaccount'] == 'unlocked') {
  256. $Join['la'] .= ' LEFT';
  257. $Where[] = ' la.UserID IS NULL';
  258. }
  259. $Join['la'] .= ' JOIN locked_accounts AS la ON la.UserID = um1.ID ';
  260. }
  261. if (!empty($_GET['cc'])) {
  262. if ($_GET['cc_op'] == 'equal') {
  263. $Where[] = "um1.ipcc = '".db_string($_GET['cc'])."'";
  264. } else {
  265. $Where[] = "um1.ipcc != '".db_string($_GET['cc'])."'";
  266. }
  267. }
  268. if (!empty($_GET['tracker_ip'])) {
  269. $Distinct = 'DISTINCT ';
  270. $Join['xfu'] = ' JOIN xbt_files_users AS xfu ON um1.ID = xfu.uid ';
  271. $Where[] = ' xfu.ip '.$Match.wrap($_GET['tracker_ip'], '', true);
  272. }
  273. // if (!empty($_GET['tracker_ip'])) {
  274. // $Distinct = 'DISTINCT ';
  275. // $Join['xs'] = ' JOIN xbt_snatched AS xs ON um1.ID = xs.uid ';
  276. // $Where[] = ' xs.IP '.$Match.wrap($_GET['ip']);
  277. // }
  278. if (!empty($_GET['comment'])) {
  279. $Where[] = 'ui1.AdminComment'.$Match.wrap($_GET['comment']);
  280. }
  281. if (strlen($_GET['invites1'])) {
  282. $Invites1 = round($_GET['invites1']);
  283. $Invites2 = round($_GET['invites2']);
  284. $Where[] = implode(' AND ', num_compare('Invites', $_GET['invites'], $Invites1, $Invites2));
  285. }
  286. if (strlen($_GET['invitees1']) && $_GET['invitees'] != 'off') {
  287. $Invitees1 = round($_GET['invitees1']);
  288. $Invitees2 = round($_GET['invitees2']);
  289. $Having[] = implode(' AND ', num_compare('Invitees', $_GET['invitees'], $Invitees1, $Invitees2));
  290. }
  291. if ($_GET['disabled_invites'] == 'yes') {
  292. $Where[] = 'ui1.DisableInvites = \'1\'';
  293. } elseif ($_GET['disabled_invites'] == 'no') {
  294. $Where[] = 'ui1.DisableInvites = \'0\'';
  295. }
  296. if ($_GET['disabled_uploads'] == 'yes') {
  297. $Where[] = 'ui1.DisableUpload = \'1\'';
  298. } elseif ($_GET['disabled_uploads'] == 'no') {
  299. $Where[] = 'ui1.DisableUpload = \'0\'';
  300. }
  301. if ($_GET['join1']) {
  302. $Where[] = implode(' AND ', date_compare('ui1.JoinDate', $_GET['joined'], $_GET['join1'], $_GET['join2']));
  303. }
  304. if ($_GET['lastactive1']) {
  305. $Where[] = implode(' AND ', date_compare('um1.LastAccess', $_GET['lastactive'], $_GET['lastactive1'], $_GET['lastactive2']));
  306. }
  307. if ($_GET['ratio1']) {
  308. $Decimals = strlen(array_pop(explode('.', $_GET['ratio1'])));
  309. if (!$Decimals) {
  310. $Decimals = 0;
  311. }
  312. $Where[] = implode(' AND ', num_compare("ROUND(Uploaded/Downloaded,$Decimals)", $_GET['ratio'], $_GET['ratio1'], $_GET['ratio2']));
  313. }
  314. if (strlen($_GET['uploaded1'])) {
  315. $Upload1 = round($_GET['uploaded1']);
  316. $Upload2 = round($_GET['uploaded2']);
  317. if ($_GET['uploaded'] != 'buffer') {
  318. $Where[] = implode(' AND ', num_compare('ROUND(Uploaded / 1024 / 1024 / 1024)', $_GET['uploaded'], $Upload1, $Upload2));
  319. } else {
  320. $Where[] = implode(' AND ', num_compare('ROUND((Uploaded / 1024 / 1024 / 1024) - (Downloaded / 1024 / 1024 / 1023))', 'between', $Upload1 * 0.9, $Upload1 * 1.1));
  321. }
  322. }
  323. if (strlen($_GET['downloaded1'])) {
  324. $Download1 = round($_GET['downloaded1']);
  325. $Download2 = round($_GET['downloaded2']);
  326. $Where[] = implode(' AND ', num_compare('ROUND(Downloaded / 1024 / 1024 / 1024)', $_GET['downloaded'], $Download1, $Download2));
  327. }
  328. if (strlen($_GET['snatched1'])) {
  329. $Snatched1 = round($_GET['snatched1']);
  330. $Snatched2 = round($_GET['snatched2']);
  331. $Having[] = implode(' AND ', num_compare('Snatches', $_GET['snatched'], $Snatched1, $Snatched2));
  332. }
  333. if ($_GET['enabled'] != '') {
  334. $Where[] = 'um1.Enabled = '.wrap($_GET['enabled'], '=');
  335. }
  336. if ($_GET['class'] != '') {
  337. $Where[] = 'um1.PermissionID = '.wrap($_GET['class'], '=');
  338. }
  339. if ($_GET['secclass'] != '') {
  340. $Join['ul'] = ' JOIN users_levels AS ul ON um1.ID = ul.UserID ';
  341. $Where[] = 'ul.PermissionID = '.wrap($_GET['secclass'], '=');
  342. }
  343. if ($_GET['donor'] == 'yes') {
  344. $Where[] = 'ui1.Donor = \'1\'';
  345. } elseif ($_GET['donor'] == 'no') {
  346. $Where[] = 'ui1.Donor = \'0\'';
  347. }
  348. if ($_GET['warned'] == 'yes') {
  349. $Where[] = 'ui1.Warned IS NOT NULL';
  350. } elseif ($_GET['warned'] == 'no') {
  351. $Where[] = 'ui1.Warned IS NULL';
  352. }
  353. if ($_GET['disabled_ip']) {
  354. $Distinct = 'DISTINCT ';
  355. if ($_GET['ip_history']) {
  356. if (!isset($Join['tip'])) {
  357. $Join['tip'] = ' JOIN users_ips_decrypted AS tip ON tip.ID = um1.ID ';
  358. }
  359. $Join['tip2'] = ' JOIN users_ips_decrypted2 AS tip2 ON tip2.IP = tip.IP ';
  360. $Join['um2'] = ' JOIN users_main AS um2 ON um2.ID = tip2.ID AND um2.Enabled = \'2\' ';
  361. } else {
  362. $Join['um2'] = ' JOIN users_main AS um2 ON um2.IP = um1.IP AND um2.Enabled = \'2\' ';
  363. }
  364. }
  365. if (!empty($_GET['passkey'])) {
  366. $Where[] = 'um1.torrent_pass'.$Match.wrap($_GET['passkey']);
  367. }
  368. if (!empty($_GET['avatar'])) {
  369. $Where[] = 'ui1.Avatar'.$Match.wrap($_GET['avatar']);
  370. }
  371. if ($_GET['stylesheet'] != '') {
  372. $Where[] = 'ui1.StyleID = '.wrap($_GET['stylesheet'], '=');
  373. }
  374. if ($OrderTable[$_GET['order']] && $WayTable[$_GET['way']]) {
  375. $Order = ' ORDER BY '.$OrderTable[$_GET['order']].' '.$WayTable[$_GET['way']].' ';
  376. }
  377. //---------- Finish generating the search string
  378. $SQL = 'SELECT '.$Distinct.$SQL;
  379. $SQL .= implode(' ', $Join);
  380. if (count($Where)) {
  381. $SQL .= ' WHERE '.implode(' AND ', $Where);
  382. }
  383. if (count($Group)) {
  384. $SQL .= " GROUP BY " . implode(' ,', $Group);
  385. }
  386. if (count($Having)) {
  387. $SQL .= ' HAVING '.implode(' AND ', $Having);
  388. }
  389. $SQL .= $Order;
  390. if (count($Where) > 0 || count($Join) > 0 || count($Having) > 0) {
  391. $RunQuery = true;
  392. }
  393. list($Page, $Limit) = Format::page_limit(USERS_PER_PAGE);
  394. $SQL .= " LIMIT $Limit";
  395. } else {
  396. error($Err);
  397. }
  398. }
  399. View::show_header('User search');
  400. ?>
  401. <div class="thin">
  402. <form class="search_form" name="users" action="user.php" method="get">
  403. <input type="hidden" name="action" value="search" />
  404. <table class="layout">
  405. <tr>
  406. <td class="label nobr">Username:</td>
  407. <td width="24%">
  408. <input type="text" name="username" size="20" value="<?=display_str($_GET['username'])?>" />
  409. </td>
  410. <td class="label nobr">Joined:</td>
  411. <td width="24%">
  412. <select name="joined">
  413. <option value="on"<? if ($_GET['joined'] === 'on') { echo ' selected="selected"'; } ?>>On</option>
  414. <option value="before"<? if ($_GET['joined'] === 'before') { echo ' selected="selected"'; } ?>>Before</option>
  415. <option value="after"<? if ($_GET['joined'] === 'after') { echo ' selected="selected"'; } ?>>After</option>
  416. <option value="between"<? if ($_GET['joined'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  417. </select>
  418. <input type="text" name="join1" size="10" value="<?=display_str($_GET['join1'])?>" placeholder="YYYY-MM-DD" />
  419. <input type="text" name="join2" size="10" value="<?=display_str($_GET['join2'])?>" placeholder="YYYY-MM-DD" />
  420. </td>
  421. <td class="label nobr">Enabled:</td>
  422. <td>
  423. <select name="enabled">
  424. <option value=""<? if ($_GET['enabled'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  425. <option value="0"<? if ($_GET['enabled'] === '0') { echo ' selected="selected"'; } ?>>Unconfirmed</option>
  426. <option value="1"<? if ($_GET['enabled'] === '1') { echo ' selected="selected"'; } ?>>Enabled</option>
  427. <option value="2"<? if ($_GET['enabled'] === '2') { echo ' selected="selected"'; } ?>>Disabled</option>
  428. </select>
  429. </td>
  430. </tr>
  431. <tr>
  432. <td class="label nobr">Email address:</td>
  433. <td>
  434. <input type="text" name="email" size="20" value="<?=display_str($_GET['email'])?>" />
  435. </td>
  436. <td class="label nobr">Last active:</td>
  437. <td width="30%">
  438. <select name="lastactive">
  439. <option value="on"<? if ($_GET['lastactive'] === 'on') { echo ' selected="selected"'; } ?>>On</option>
  440. <option value="before"<? if ($_GET['lastactive'] === 'before') { echo ' selected="selected"'; } ?>>Before</option>
  441. <option value="after"<? if ($_GET['lastactive'] === 'after') { echo ' selected="selected"'; } ?>>After</option>
  442. <option value="between"<? if ($_GET['lastactive'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  443. </select>
  444. <input type="text" name="lastactive1" size="10" value="<?=display_str($_GET['lastactive1'])?>" placeholder="YYYY-MM-DD" />
  445. <input type="text" name="lastactive2" size="10" value="<?=display_str($_GET['lastactive2'])?>" placeholder="YYYY-MM-DD" />
  446. </td>
  447. <td class="label nobr">Primary class:</td>
  448. <td>
  449. <select name="class">
  450. <option value=""<? if ($_GET['class'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  451. <? foreach ($ClassLevels as $Class) {
  452. if ($Class['Secondary']) {
  453. continue;
  454. }
  455. ?>
  456. <option value="<?=$Class['ID'] ?>"<? if ($_GET['class'] === $Class['ID']) { echo ' selected="selected"'; } ?>><?=Format::cut_string($Class['Name'], 10, 1, 1).' ('.$Class['Level'].')'?></option>
  457. <? } ?>
  458. </select>
  459. </td>
  460. </tr>
  461. <tr>
  462. <td class="label tooltip nobr" title="To fuzzy search (default) for a block of addresses (e.g. 55.66.77.*), enter &quot;55.66.77.&quot; without the quotes">IP address:</td>
  463. <td>
  464. <input type="text" name="ip" size="20" value="<?=display_str($_GET['ip'])?>" />
  465. </td>
  466. <td class="label nobr">Locked Account:</td>
  467. <td>
  468. <select name="lockedaccount">
  469. <option value="any"<? if ($_GET['lockedaccount'] == 'any') { echo ' selected="selected"'; } ?>>Any</option>
  470. <option value="locked"<? if ($_GET['lockedaccount'] == 'locked') { echo ' selected="selected"'; } ?>>Locked</option>
  471. <option value="unlocked"<? if ($_GET['lockedaccount'] == 'unlocked') { echo ' selected="selected"'; } ?>>Unlocked</option>
  472. </select>
  473. </td>
  474. <td class="label nobr">Secondary class:</td>
  475. <td>
  476. <select name="secclass">
  477. <option value=""<? if ($_GET['secclass'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  478. <? $Secondaries = array();
  479. // Neither level nor ID is particularly useful when searching secondary classes, so let's do some
  480. // kung-fu to sort them alphabetically.
  481. $fnc = function($Class1, $Class2) { return strcmp($Class1['Name'], $Class2['Name']); };
  482. foreach ($ClassLevels as $Class) {
  483. if (!$Class['Secondary']) {
  484. continue;
  485. }
  486. $Secondaries[] = $Class;
  487. }
  488. usort($Secondaries, $fnc);
  489. foreach ($Secondaries as $Class) {
  490. ?>
  491. <option value="<?=$Class['ID'] ?>"<? if ($_GET['secclass'] === $Class['ID']) { echo ' selected="selected"'; } ?>><?=Format::cut_string($Class['Name'], 20, 1, 1)?></option>
  492. <? } ?>
  493. </select>
  494. </td>
  495. </tr>
  496. <tr>
  497. <td class="label nobr">Extra:</td>
  498. <td>
  499. <ul class="options_list nobullet">
  500. <li>
  501. <input type="checkbox" name="ip_history" id="ip_history"<? if ($_GET['ip_history']) { echo ' checked="checked"'; } ?> />
  502. <label for="ip_history">IP history</label>
  503. </li>
  504. <li>
  505. <input type="checkbox" name="email_history" id="email_history"<? if ($_GET['email_history']) { echo ' checked="checked"'; } ?> />
  506. <label for="email_history">Email history</label>
  507. </li>
  508. </ul>
  509. </td>
  510. <td class="label nobr">Ratio:</td>
  511. <td width="30%">
  512. <select name="ratio">
  513. <option value="equal"<? if ($_GET['ratio'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  514. <option value="above"<? if ($_GET['ratio'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  515. <option value="below"<? if ($_GET['ratio'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  516. <option value="between"<? if ($_GET['ratio'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  517. </select>
  518. <input type="text" name="ratio1" size="6" value="<?=display_str($_GET['ratio1'])?>" />
  519. <input type="text" name="ratio2" size="6" value="<?=display_str($_GET['ratio2'])?>" />
  520. </td>
  521. <td class="label nobr">Donor:</td>
  522. <td>
  523. <select name="donor">
  524. <option value=""<? if ($_GET['donor'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  525. <option value="yes"<? if ($_GET['donor'] === 'yes') { echo ' selected="selected"'; } ?>>Yes</option>
  526. <option value="no"<? if ($_GET['donor'] === 'no') { echo ' selected="selected"'; } ?>>No</option>
  527. </select>
  528. </td>
  529. </tr>
  530. <tr>
  531. <? if (check_perms('users_mod')) { ?>
  532. <td class="label nobr">Staff notes:</td>
  533. <td>
  534. <input type="text" name="comment" size="20" value="<?=display_str($_GET['comment'])?>" />
  535. </td>
  536. <? } else { ?>
  537. <td class="label nobr"></td>
  538. <td>
  539. </td>
  540. <? } ?>
  541. <td class="label tooltip nobr" title="Units are in gibibytes (the base 2 sibling of gigabytes)">Uploaded:</td>
  542. <td width="30%">
  543. <select name="uploaded">
  544. <option value="equal"<? if ($_GET['uploaded'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  545. <option value="above"<? if ($_GET['uploaded'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  546. <option value="below"<? if ($_GET['uploaded'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  547. <option value="between"<? if ($_GET['uploaded'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  548. <option value="buffer"<? if ($_GET['uploaded'] === 'buffer') { echo ' selected="selected"'; } ?>>Buffer</option>
  549. </select>
  550. <input type="text" name="uploaded1" size="6" value="<?=display_str($_GET['uploaded1'])?>" />
  551. <input type="text" name="uploaded2" size="6" value="<?=display_str($_GET['uploaded2'])?>" />
  552. </td>
  553. <td class="label nobr">Warned:</td>
  554. <td>
  555. <select name="warned">
  556. <option value=""<? if ($_GET['warned'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  557. <option value="yes"<? if ($_GET['warned'] === 'yes') { echo ' selected="selected"'; } ?>>Yes</option>
  558. <option value="no"<? if ($_GET['warned'] === 'no') { echo ' selected="selected"'; } ?>>No</option>
  559. </select>
  560. </td>
  561. </tr>
  562. <tr>
  563. <td class="label nobr"># of invites:</td>
  564. <td>
  565. <select name="invites">
  566. <option value="equal"<? if ($_GET['invites'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  567. <option value="above"<? if ($_GET['invites'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  568. <option value="below"<? if ($_GET['invites'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  569. <option value="between"<? if ($_GET['invites'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  570. </select>
  571. <input type="text" name="invites1" size="6" value="<?=display_str($_GET['invites1'])?>" />
  572. <input type="text" name="invites2" size="6" value="<?=display_str($_GET['invites2'])?>" />
  573. </td>
  574. <td class="label tooltip nobr" title="Units are in gibibytes (the base 2 sibling of gigabytes)">Downloaded:</td>
  575. <td width="30%">
  576. <select name="downloaded">
  577. <option value="equal"<? if ($_GET['downloaded'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  578. <option value="above"<? if ($_GET['downloaded'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  579. <option value="below"<? if ($_GET['downloaded'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  580. <option value="between"<? if ($_GET['downloaded'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  581. </select>
  582. <input type="text" name="downloaded1" size="6" value="<?=display_str($_GET['downloaded1'])?>" />
  583. <input type="text" name="downloaded2" size="6" value="<?=display_str($_GET['downloaded2'])?>" />
  584. </td>
  585. <td class="label tooltip nobr" title="Only display users that have a disabled account linked by IP address">
  586. <label for="disabled_ip">Disabled accounts<br />linked by IP:</label>
  587. </td>
  588. <td>
  589. <input type="checkbox" name="disabled_ip" id="disabled_ip"<? if ($_GET['disabled_ip']) { echo ' checked="checked"'; } ?> />
  590. </td>
  591. </tr>
  592. <tr>
  593. <td class="label nobr">Disabled invites:</td>
  594. <td>
  595. <select name="disabled_invites">
  596. <option value=""<? if ($_GET['disabled_invites'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  597. <option value="yes"<? if ($_GET['disabled_invites'] === 'yes') { echo ' selected="selected"'; } ?>>Yes</option>
  598. <option value="no"<? if ($_GET['disabled_invites'] === 'no') { echo ' selected="selected"'; } ?>>No</option>
  599. </select>
  600. </td>
  601. <td class="label nobr">Snatched:</td>
  602. <td width="30%">
  603. <select name="snatched">
  604. <option value="equal"<? if (isset($_GET['snatched']) && $_GET['snatched'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  605. <option value="above"<? if (isset($_GET['snatched']) && $_GET['snatched'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  606. <option value="below"<? if (isset($_GET['snatched']) && $_GET['snatched'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  607. <option value="between"<? if (isset($_GET['snatched']) && $_GET['snatched'] === 'between') { echo ' selected="selected"'; } ?>>Between</option>
  608. <option value="off"<? if (!isset($_GET['snatched']) || $_GET['snatched'] === 'off') { echo ' selected="selected"'; } ?>>Off</option>
  609. </select>
  610. <input type="text" name="snatched1" size="6" value="<?=display_str($_GET['snatched1'])?>" />
  611. <input type="text" name="snatched2" size="6" value="<?=display_str($_GET['snatched2'])?>" />
  612. </td>
  613. <td class="label nobr">Disabled uploads:</td>
  614. <td>
  615. <select name="disabled_uploads">
  616. <option value=""<? if (isset($_GET['disabled_uploads']) && $_GET['disabled_uploads'] === '') { echo ' selected="selected"'; } ?>>Any</option>
  617. <option value="yes"<? if (isset($_GET['disabled_uploads']) && $_GET['disabled_uploads'] === 'yes') { echo ' selected="selected"'; } ?>>Yes</option>
  618. <option value="no"<? if (isset($_GET['disabled_uploads']) && $_GET['disabled_uploads'] === 'no') { echo ' selected="selected"'; } ?>>No</option>
  619. </select>
  620. </td>
  621. </tr>
  622. <tr>
  623. <td width="30%" class="label nobr"># of invitees:</td>
  624. <td>
  625. <select name="invitees">
  626. <option value="equal" <?=isset($_GET['invitees']) && $_GET['invitees'] == 'equal' ? 'selected' : ''?>>Equal</option>
  627. <option value="above" <?=isset($_GET['invitees']) && $_GET['invitees'] == 'above' ? 'selected' : ''?>>Above</option>
  628. <option value="below" <?=isset($_GET['invitees']) && $_GET['invitees'] == 'below' ? 'selected' : ''?>>Below</option>
  629. <option value="between" <?=isset($_GET['invitees']) && $_GET['invitees'] == 'between' ? 'selected' : ''?>>Between</option>
  630. <option value="off" <?=!isset($_GET['invitees']) || $_GET['invitees'] == 'off' ? 'selected' : ''?>>Off</option>
  631. </select>
  632. <input type="text" name="invitees1" size="6" value="<?=display_str($_GET['invitees1'])?>" />
  633. <input type="text" name="invitees2" size="6" value="<?=display_str($_GET['invitees2'])?>" />
  634. </td>
  635. <td class="label nobr">Passkey:</td>
  636. <td>
  637. <input type="text" name="passkey" size="20" value="<?=display_str($_GET['passkey'])?>" />
  638. </td>
  639. <td class="label nobr">Tracker IP:</td>
  640. <td>
  641. <input type="text" name="tracker_ip" size="20" value="<?=display_str($_GET['tracker_ip'])?>" />
  642. </td>
  643. </tr>
  644. <tr>
  645. <td class="label tooltip nobr" title="Supports partial URL matching, e.g. entering &quot;&#124;https://whatimg.com&quot; will search for avatars hosted on https://whatimg.com">Avatar URL:</td>
  646. <td>
  647. <input type="text" name="avatar" size="20" value="<?=display_str($_GET['avatar'])?>" />
  648. </td>
  649. <td class="label nobr">Stylesheet:</td>
  650. <td>
  651. <select name="stylesheet" id="stylesheet">
  652. <option value="">Any</option>
  653. <? foreach ($Stylesheets as $Style) { ?>
  654. <option value="<?=$Style['ID']?>"<?Format::selected('stylesheet',$Style['ID'])?>><?=$Style['ProperName']?></option>
  655. <? } ?>
  656. </select>
  657. </td>
  658. <td class="label tooltip nobr" title="Two-letter codes as defined in ISO 3166-1 alpha-2">Country code:</td>
  659. <td width="30%">
  660. <select name="cc_op">
  661. <option value="equal"<? if ($_GET['cc_op'] === 'equal') { echo ' selected="selected"'; } ?>>Equals</option>
  662. <option value="not_equal"<? if ($_GET['cc_op'] === 'not_equal') { echo ' selected="selected"'; } ?>>Not equal</option>
  663. </select>
  664. <input type="text" name="cc" size="2" value="<?=display_str($_GET['cc'])?>" />
  665. </td>
  666. </tr>
  667. <tr>
  668. <td class="label nobr">Search type:</td>
  669. <td>
  670. <ul class="options_list nobullet">
  671. <li>
  672. <input type="radio" name="matchtype" id="strict_match_type" value="strict"<? if ($_GET['matchtype'] == 'strict' || !$_GET['matchtype']) { echo ' checked="checked"'; } ?> />
  673. <label class="tooltip" title="A &quot;strict&quot; search uses no wildcards in search fields, and it is analogous to &#96;grep -E &quot;&circ;SEARCHTERM&#36;&quot;&#96;" for="strict_match_type">Strict</label>
  674. </li>
  675. <li>
  676. <input type="radio" name="matchtype" id="fuzzy_match_type" value="fuzzy"<? if ($_GET['matchtype'] == 'fuzzy' || !$_GET['matchtype']) { echo ' checked="checked"'; } ?> />
  677. <label class="tooltip" title="A &quot;fuzzy&quot; search automatically prepends and appends wildcards to search strings, except for IP address searches, unless the search string begins or ends with a &quot;&#124;&quot; (pipe). It is analogous to a vanilla grep search (except for the pipe stuff)." for="fuzzy_match_type">Fuzzy</label>
  678. </li>
  679. <li>
  680. <input type="radio" name="matchtype" id="regex_match_type" value="regex"<? if ($_GET['matchtype'] == 'regex') { echo ' checked="checked"'; } ?> />
  681. <label class="tooltip" title="A &quot;regex&quot; search uses MySQL's regular expression syntax." for="regex_match_type">Regex</label>
  682. </li>
  683. </ul>
  684. </td>
  685. <td class="label nobr">Order:</td>
  686. <td class="nobr">
  687. <select name="order">
  688. <?
  689. foreach (array_shift($OrderVals) as $Cur) { ?>
  690. <option value="<?=$Cur?>"<? if (isset($_GET['order']) && $_GET['order'] == $Cur || (!isset($_GET['order']) && $Cur == 'Joined')) { echo ' selected="selected"'; } ?>><?=$Cur?></option>
  691. <? } ?>
  692. </select>
  693. <select name="way">
  694. <? foreach (array_shift($WayVals) as $Cur) { ?>
  695. <option value="<?=$Cur?>"<? if (isset($_GET['way']) && $_GET['way'] == $Cur || (!isset($_GET['way']) && $Cur == 'Descending')) { echo ' selected="selected"'; } ?>><?=$Cur?></option>
  696. <? } ?>
  697. </select>
  698. </td>
  699. <td class="label nobr"># of emails:</td>
  700. <td>
  701. <select name="emails_opt">
  702. <option value="equal"<? if ($_GET['emails_opt'] === 'equal') { echo ' selected="selected"'; } ?>>Equal</option>
  703. <option value="above"<? if ($_GET['emails_opt'] === 'above') { echo ' selected="selected"'; } ?>>Above</option>
  704. <option value="below"<? if ($_GET['emails_opt'] === 'below') { echo ' selected="selected"'; } ?>>Below</option>
  705. </select>
  706. <input type="text" name="email_cnt" size="6" value="<?=display_str($_GET['email_cnt'])?>" />
  707. </td>
  708. </tr>
  709. <tr>
  710. <td colspan="6" class="center">
  711. <input type="submit" value="Search users" />
  712. </td>
  713. </tr>
  714. </table>
  715. </form>
  716. </div>
  717. <?
  718. if ($RunQuery) {
  719. if (!empty($_GET['ip'])) {
  720. if (isset($_GET['ip_history'])) {
  721. $DB->query("SELECT UserID, IP FROM users_history_ips");
  722. } else {
  723. $DB->query("SELECT ID, IP FROM users_main");
  724. }
  725. while(list($ID, $EncIP) = $DB->next_record()) {
  726. $IPs[] = $ID.", '".DBCrypt::decrypt($EncIP)."'";
  727. }
  728. $DB->query("CREATE TEMPORARY TABLE users_ips_decrypted (ID INT(10) UNSIGNED NOT NULL, IP VARCHAR(45) NOT NULL, PRIMARY KEY (ID,IP)) ENGINE=MEMORY");
  729. $DB->query("INSERT IGNORE INTO users_ips_decrypted (ID, IP) VALUES(".implode("),(", $IPs).")");
  730. if ($_GET['disabled_ip'] && $_GET['ip_history']) {
  731. $DB->query("CREATE TEMPORARY TABLE users_ips_decrypted2 SELECT * FROM users_ips_decrypted");
  732. }
  733. }
  734. if (!empty($_GET['email'])) {
  735. if (isset($_GET['email_history'])) {
  736. $DB->query("SELECT UserID, Email FROM users_history_emails");
  737. } else {
  738. $DB->query("SELECT ID, Email FROM users_main");
  739. }
  740. while(list($ID, $EncEmail) = $DB->next_record()) {
  741. $Emails[] = $ID.", '".DBCrypt::decrypt($EncEmail)."'";
  742. }
  743. $DB->query("CREATE TEMPORARY TABLE users_emails_decrypted (ID INT(10) UNSIGNED NOT NULL, Email VARCHAR(255) NOT NULL, PRIMARY KEY (ID,Email)) ENGINE=MEMORY");
  744. $DB->query("INSERT IGNORE INTO users_emails_decrypted (ID, Email) VALUES(".implode("),(", $Emails).")");
  745. }
  746. $Results = $DB->query($SQL);
  747. $DB->query('SELECT FOUND_ROWS()');
  748. list($NumResults) = $DB->next_record();
  749. if (!empty($_GET['ip'])) {
  750. $DB->query("DROP TABLE users_ips_decrypted");
  751. }
  752. if (!empty($_GET['email'])) {
  753. $DB->query("DROP TABLE users_emails_decrypted");
  754. }
  755. $DB->set_query_id($Results);
  756. } else {
  757. $DB->query('SET @nothing = 0');
  758. $NumResults = 0;
  759. }
  760. ?>
  761. <div class="linkbox">
  762. <?
  763. $Pages = Format::get_pages($Page, $NumResults, USERS_PER_PAGE, 11);
  764. echo $Pages;
  765. ?>
  766. </div>
  767. <div class="box pad center">
  768. <h2><?=number_format($NumResults)?> results</h2>
  769. <table width="100%">
  770. <tr class="colhead">
  771. <td>Username</td>
  772. <td>Ratio</td>
  773. <td>IP address</td>
  774. <td>Email</td>
  775. <td>Joined</td>
  776. <td>Last seen</td>
  777. <td>Upload</td>
  778. <td>Download</td>
  779. <td>Downloads</td>
  780. <td>Snatched</td>
  781. <td>Invites</td>
  782. <? if (isset($_GET['invitees']) && $_GET['invitees'] != 'off') { ?>
  783. <td>Invitees</td>
  784. <? } ?>
  785. </tr>
  786. <?
  787. while (list($UserID, $Username, $Uploaded, $Downloaded, $Snatched, $Invitees, $Class, $Email, $Enabled, $IP, $Invites, $DisableInvites, $Warned, $Donor, $JoinDate, $LastAccess) = $DB->next_record()) {
  788. $IP = apc_exists('DBKEY') ? DBCrypt::decrypt($IP) : '[Encrypted]';
  789. $Email = apc_exists('DBKEY') ? DBCrypt::decrypt($Email) : '[Encrypted]';
  790. ?>
  791. <tr>
  792. <td><?=Users::format_username($UserID, true, true, true, true)?></td>
  793. <td><?=Format::get_ratio_html($Uploaded, $Downloaded)?></td>
  794. <td style="word-break: break-all;"><?=display_str($IP)?> (<?=Tools::get_country_code_by_ajax($IP)?>)</td>
  795. <td><?=display_str($Email)?></td>
  796. <td><?=time_diff($JoinDate)?></td>
  797. <td><?=time_diff($LastAccess)?></td>
  798. <td><?=Format::get_size($Uploaded)?></td>
  799. <td><?=Format::get_size($Downloaded)?></td>
  800. <? $DB->query("
  801. SELECT COUNT(ud.UserID)
  802. FROM users_downloads AS ud
  803. JOIN torrents AS t ON t.ID = ud.TorrentID
  804. WHERE ud.UserID = $UserID");
  805. list($Downloads) = $DB->next_record();
  806. $DB->set_query_id($Results);
  807. ?>
  808. <td><?=number_format((int)$Downloads)?></td>
  809. <td><?=(is_numeric($Snatched) ? number_format($Snatched) : display_str($Snatched))?></td>
  810. <td><? if ($DisableInvites) { echo 'X'; } else { echo number_format($Invites); } ?></td>
  811. <? if (isset($_GET['invitees']) && $_GET['invitees'] != 'off') { ?>
  812. <td><?=number_format($Invitees)?></td>
  813. <? } ?>
  814. </tr>
  815. <?
  816. }
  817. ?>
  818. </table>
  819. </div>
  820. <div class="linkbox">
  821. <?=$Pages?>
  822. </div>
  823. <?
  824. View::show_footer();
  825. ?>