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

tools.class.php 9.7KB

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