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.

tools.class.php 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <?
  2. class Tools {
  3. /**
  4. * Returns true if given IP is banned.
  5. *
  6. * @param string $IP
  7. */
  8. public static function site_ban_ip($IP) {
  9. global $Debug;
  10. $A = substr($IP, 0, strcspn($IP, '.'));
  11. $IPNum = Tools::ip_to_unsigned($IP);
  12. $IPBans = G::$Cache->get_value('ip_bans_'.$A);
  13. if (!is_array($IPBans)) {
  14. $SQL = sprintf("
  15. SELECT ID, FromIP, ToIP
  16. FROM ip_bans
  17. WHERE FromIP BETWEEN %d << 24 AND (%d << 24) - 1", $A, $A + 1);
  18. $QueryID = G::$DB->get_query_id();
  19. G::$DB->query($SQL);
  20. $IPBans = G::$DB->to_array(0, MYSQLI_NUM);
  21. G::$DB->set_query_id($QueryID);
  22. G::$Cache->cache_value('ip_bans_'.$A, $IPBans, 0);
  23. }
  24. $Debug->log_var($IPBans, 'IP bans for class '.$A);
  25. foreach ($IPBans as $Index => $IPBan) {
  26. list ($ID, $FromIP, $ToIP) = $IPBan;
  27. if ($IPNum >= $FromIP && $IPNum <= $ToIP) {
  28. return true;
  29. }
  30. }
  31. return false;
  32. }
  33. /**
  34. * Returns the unsigned form of an IP address.
  35. *
  36. * @param string $IP The IP address x.x.x.x
  37. * @return string the long it represents.
  38. */
  39. public static function ip_to_unsigned($IP) {
  40. $IPnum = sprintf('%u', ip2long($IP));
  41. if (!$IPnum) {
  42. // Try to encode as IPv6 (stolen from stackoverflow)
  43. // Note that this is *wrong* and because of PHP's wankery stops being accurate after the most significant 16 digits or so
  44. // But since this is only used for geolocation and IPv6 blocks are allocated in huge numbers, it's still fine
  45. $IPnum = '';
  46. foreach (unpack('C*', inet_pton($IP)) as $byte) {
  47. $IPnum .= str_pad(decbin($byte), 8, "0", STR_PAD_LEFT);
  48. }
  49. $IPnum = base_convert(ltrim($IPnum, '0'), 2, 10);
  50. }
  51. return $IPnum;
  52. }
  53. /**
  54. * Geolocate an IP address using the database
  55. *
  56. * @param $IP the ip to fetch the country for
  57. * @return the country of origin
  58. */
  59. public static function geoip($IP) {
  60. static $IPs = array();
  61. if (isset($IPs[$IP])) {
  62. return $IPs[$IP];
  63. }
  64. if (is_number($IP)) {
  65. $Long = $IP;
  66. } else {
  67. $Long = Tools::ip_to_unsigned($IP);
  68. }
  69. if (!$Long || $Long == 2130706433) { // No need to check cc for 127.0.0.1
  70. return false;
  71. }
  72. $QueryID = G::$DB->get_query_id();
  73. G::$DB->query("
  74. SELECT EndIP, Code
  75. FROM geoip_country
  76. WHERE $Long >= StartIP
  77. ORDER BY StartIP DESC
  78. LIMIT 1");
  79. if ((!list($EndIP, $Country) = G::$DB->next_record()) || $EndIP < $Long) {
  80. $Country = '?';
  81. }
  82. G::$DB->set_query_id($QueryID);
  83. $IPs[$IP] = $Country;
  84. return $Country;
  85. }
  86. /**
  87. * Gets the hostname for an IP address
  88. *
  89. * @param $IP the IP to get the hostname for
  90. * @return hostname fetched
  91. */
  92. public static function get_host_by_ip($IP) {
  93. $testar = explode('.', $IP);
  94. if (count($testar) != 4) {
  95. return $IP;
  96. }
  97. for ($i = 0; $i < 4; ++$i) {
  98. if (!is_numeric($testar[$i])) {
  99. return $IP;
  100. }
  101. }
  102. $host = `host -W 1 $IP`;
  103. return ($host ? end(explode(' ', $host)) : $IP);
  104. }
  105. /**
  106. * Gets an hostname using AJAX
  107. *
  108. * @param $IP the IP to fetch
  109. * @return a span with JavaScript code
  110. */
  111. public static function get_host_by_ajax($IP) {
  112. static $ID = 0;
  113. ++$ID;
  114. return '<span id="host_'.$ID.'">Resolving host...<script type="text/javascript">ajax.get(\'tools.php?action=get_host&ip='.$IP.'\',function(host) {$(\'#host_'.$ID.'\').raw().innerHTML=host;});</script></span>';
  115. }
  116. /**
  117. * Looks up the full host of an IP address, by system call.
  118. * Used as the server-side counterpart to get_host_by_ajax.
  119. *
  120. * @param string $IP The IP address to look up.
  121. * @return string the host.
  122. */
  123. public static function lookup_ip($IP) {
  124. //TODO: use the G::$Cache
  125. $Output = explode(' ',shell_exec('host -W 1 '.escapeshellarg($IP)));
  126. if (count($Output) == 1 && empty($Output[0])) {
  127. //No output at all implies the command failed
  128. return '';
  129. }
  130. if (count($Output) != 5) {
  131. return false;
  132. } else {
  133. return trim($Output[4]);
  134. }
  135. }
  136. /**
  137. * Format an IP address with links to IP history.
  138. *
  139. * @param string IP
  140. * @return string The HTML
  141. */
  142. public static function display_ip($IP) {
  143. $Line = display_str($IP).' ('.Tools::get_country_code_by_ajax($IP).') ';
  144. $Line .= '<a href="user.php?action=search&amp;ip_history=on&amp;ip='.display_str($IP).'&amp;matchtype=strict" title="Search" class="brackets tooltip">S</a>';
  145. return $Line;
  146. }
  147. public static function get_country_code_by_ajax($IP) {
  148. static $ID = 0;
  149. ++$ID;
  150. return '<span id="cc_'.$ID.'">Resolving CC...<script type="text/javascript">ajax.get(\'tools.php?action=get_cc&ip='.$IP.'\', function(cc) {$(\'#cc_'.$ID.'\').raw().innerHTML = cc;});</script></span>';
  151. }
  152. /**
  153. * Disable an array of users.
  154. *
  155. * @param array $UserIDs (You can also send it one ID as an int, because fuck types)
  156. * @param BanReason 0 - Unknown, 1 - Manual, 2 - Ratio, 3 - Inactive, 4 - Unused.
  157. */
  158. public static function disable_users($UserIDs, $AdminComment, $BanReason = 1) {
  159. $QueryID = G::$DB->get_query_id();
  160. if (!is_array($UserIDs)) {
  161. $UserIDs = array($UserIDs);
  162. }
  163. G::$DB->query("
  164. UPDATE users_info AS i
  165. JOIN users_main AS m ON m.ID = i.UserID
  166. SET m.Enabled = '2',
  167. m.can_leech = '0',
  168. i.AdminComment = CONCAT('".sqltime()." - ".($AdminComment ? $AdminComment : 'Disabled by system')."\n\n', i.AdminComment),
  169. i.BanDate = '".sqltime()."',
  170. i.BanReason = '$BanReason',
  171. i.RatioWatchDownload = ".($BanReason == 2 ? 'm.Downloaded' : "'0'")."
  172. WHERE m.ID IN(".implode(',', $UserIDs).') ');
  173. G::$Cache->decrement('stats_user_count', G::$DB->affected_rows());
  174. foreach ($UserIDs as $UserID) {
  175. G::$Cache->delete_value("enabled_$UserID");
  176. G::$Cache->delete_value("user_info_$UserID");
  177. G::$Cache->delete_value("user_info_heavy_$UserID");
  178. G::$Cache->delete_value("user_stats_$UserID");
  179. G::$DB->query("
  180. SELECT SessionID
  181. FROM users_sessions
  182. WHERE UserID = '$UserID'
  183. AND Active = 1");
  184. while (list($SessionID) = G::$DB->next_record()) {
  185. G::$Cache->delete_value("session_$UserID"."_$SessionID");
  186. }
  187. G::$Cache->delete_value("users_sessions_$UserID");
  188. G::$DB->query("
  189. DELETE FROM users_sessions
  190. WHERE UserID = '$UserID'");
  191. }
  192. // Remove the users from the tracker.
  193. G::$DB->query('
  194. SELECT torrent_pass
  195. FROM users_main
  196. WHERE ID in ('.implode(', ', $UserIDs).')');
  197. $PassKeys = G::$DB->collect('torrent_pass');
  198. $Concat = '';
  199. foreach ($PassKeys as $PassKey) {
  200. if (strlen($Concat) > 3950) { // Ocelot's read buffer is 4 KiB and anything exceeding it is truncated
  201. Tracker::update_tracker('remove_users', array('passkeys' => $Concat));
  202. $Concat = $PassKey;
  203. } else {
  204. $Concat .= $PassKey;
  205. }
  206. }
  207. Tracker::update_tracker('remove_users', array('passkeys' => $Concat));
  208. G::$DB->set_query_id($QueryID);
  209. }
  210. /**
  211. * Warn a user.
  212. *
  213. * @param int $UserID
  214. * @param int $Duration length of warning in seconds
  215. * @param string $reason
  216. */
  217. public static function warn_user($UserID, $Duration, $Reason) {
  218. global $Time;
  219. $QueryID = G::$DB->get_query_id();
  220. G::$DB->query("
  221. SELECT Warned
  222. FROM users_info
  223. WHERE UserID = $UserID
  224. AND Warned != '0000-00-00 00:00:00'");
  225. if (G::$DB->has_results()) {
  226. //User was already warned, appending new warning to old.
  227. list($OldDate) = G::$DB->next_record();
  228. $NewExpDate = date('Y-m-d H:i:s', strtotime($OldDate) + $Duration);
  229. Misc::send_pm($UserID, 0,
  230. 'You have received multiple warnings.',
  231. "When you received your latest warning (set to expire on ".date('Y-m-d', (time() + $Duration)).'), you already had a different warning (set to expire on '.date('Y-m-d', strtotime($OldDate)).").\n\n Due to this collision, your warning status will now expire at $NewExpDate.");
  232. $AdminComment = date('Y-m-d')." - Warning (Clash) extended to expire at $NewExpDate by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n";
  233. G::$DB->query('
  234. UPDATE users_info
  235. SET
  236. Warned = \''.db_string($NewExpDate).'\',
  237. WarnedTimes = WarnedTimes + 1,
  238. AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment)
  239. WHERE UserID = \''.db_string($UserID).'\'');
  240. } else {
  241. //Not changing, user was not already warned
  242. $WarnTime = time_plus($Duration);
  243. G::$Cache->begin_transaction("user_info_$UserID");
  244. G::$Cache->update_row(false, array('Warned' => $WarnTime));
  245. G::$Cache->commit_transaction(0);
  246. $AdminComment = date('Y-m-d')." - Warned until $WarnTime by " . G::$LoggedUser['Username'] . "\nReason: $Reason\n\n";
  247. G::$DB->query('
  248. UPDATE users_info
  249. SET
  250. Warned = \''.db_string($WarnTime).'\',
  251. WarnedTimes = WarnedTimes + 1,
  252. AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment)
  253. WHERE UserID = \''.db_string($UserID).'\'');
  254. }
  255. G::$DB->set_query_id($QueryID);
  256. }
  257. /**
  258. * Update the notes of a user
  259. * @param unknown $UserID ID of user
  260. * @param unknown $AdminComment Comment to update with
  261. */
  262. public static function update_user_notes($UserID, $AdminComment) {
  263. $QueryID = G::$DB->get_query_id();
  264. G::$DB->query('
  265. UPDATE users_info
  266. SET AdminComment = CONCAT(\''.db_string($AdminComment).'\', AdminComment)
  267. WHERE UserID = \''.db_string($UserID).'\'');
  268. G::$DB->set_query_id($QueryID);
  269. }
  270. /**
  271. * Check if an IP address is part of a given CIDR range.
  272. * @param string $CheckIP the IP address to be looked up
  273. * @param string $Subnet the CIDR subnet to be checked against
  274. */
  275. public static function check_cidr_range($CheckIP, $Subnet) {
  276. $IP = ip2long($CheckIP);
  277. $CIDR = split('/', $Subnet);
  278. $SubnetIP = ip2long($CIDR[0]);
  279. $SubnetMaskBits = 32 - $CIDR[1];
  280. return (($IP>>$SubnetMaskBits) == ($SubnetIP>>$SubnetMaskBits));
  281. }
  282. }
  283. ?>