b2evolution PHP Cross Reference Blogging Systems

Source: /inc/antispam/model/_antispam.funcs.php - 777 lines - 23956 bytes - Summary - Text - Print

Description: This file implements Antispam handling functions. This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}. See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements Antispam handling functions.
   4   *
   5   * This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}.
   9   * Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   * Parts of this file are copyright (c)2004 by Vegar BERG GULDAL - {@link http://funky-m.com/}.
  11   * Parts of this file are copyright (c)2005 by The University of North Carolina at Charlotte as
  12   * contributed by Jason Edgecombe {@link http://tst.uncc.edu/}.
  13   *
  14   * @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
  15   *
  16   * {@internal Open Source relicensing agreement:
  17   * Daniel HAHLER grants Francois PLANQUE the right to license
  18   * Daniel HAHLER's contributions to this file and the b2evolution project
  19   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  20   *
  21   * Vegar BERG GULDAL grants Francois PLANQUE the right to license
  22   * Vegar BERG GULDAL's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   *
  25   * The University of North Carolina at Charlotte grants Francois PLANQUE the right to license
  26   * Jason EDGECOMBE's contributions to this file and the b2evolution project
  27   * under the GNU General Public License (http://www.opensource.org/licenses/gpl-license.php)
  28   * and the Mozilla Public License (http://www.opensource.org/licenses/mozilla1.1.php).
  29   *  }}
  30   *
  31   * @package evocore
  32   *
  33   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  34   * @author blueyed: Daniel HAHLER.
  35   * @author fplanque: Francois PLANQUE.
  36   * @author vegarg: Vegar BERG GULDAL.
  37   *
  38   * @version $Id: _antispam.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  39   */
  40  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  41  
  42  
  43  /**
  44   * antispam_create(-)
  45   *
  46   * Insert a new abuse string into DB
  47   */
  48  function antispam_create( $abuse_string, $aspm_source = 'local' )
  49  {
  50      global $DB;
  51  
  52      // Cut the crap if the string is empty:
  53      $abuse_string = trim( $abuse_string );
  54      if( empty( $abuse_string ) )
  55      {
  56          return false;
  57      }
  58  
  59      // Check if the string already is in the blacklist:
  60      if( antispam_check($abuse_string) )
  61      {
  62          return false;
  63      }
  64  
  65      // Insert new string into DB:
  66      $sql = "INSERT INTO T_antispam( aspm_string, aspm_source )
  67                      VALUES( '".$DB->escape($abuse_string)."', '$aspm_source' )";
  68      $DB->query( $sql );
  69  
  70      return true;
  71  }
  72  
  73  
  74  /**
  75   * antispam_update_source(-)
  76   *
  77   * Note: We search by string because we sometimes don't know the ID
  78   * (e-g when download already in list/cache)
  79   */
  80  function antispam_update_source( $aspm_string, $aspm_source )
  81  {
  82      global $DB;
  83  
  84      $sql = "UPDATE T_antispam
  85                      SET aspm_source = '$aspm_source'
  86                      WHERE aspm_string = '".$DB->escape($aspm_string)."'";
  87      $DB->query( $sql );
  88  }
  89  
  90  /*
  91   * antispam_delete(-)
  92   *
  93   * Remove an entry from the ban list
  94   */
  95  function antispam_delete( $string_ID )
  96  {
  97      global $DB;
  98  
  99      $sql = "DELETE FROM T_antispam
 100                      WHERE aspm_ID = $string_ID";
 101      $DB->query( $sql );
 102  }
 103  
 104  
 105  /**
 106   * Check if a string contains abusive substrings
 107   *
 108   * Note: Letting the database do the LIKE %% match is a little faster than doing in it PHP,
 109   * not to mention the incredibly long overhead of preloading the list into PHP
 110   *
 111   * @todo dh> IMHO this method is too generic used! It gets used for:
 112   *           - comment author name
 113   *           - comment/message author email
 114   *           - comment content
 115   *           - message (email) content
 116   *           - validate_url
 117   *           ..and validates all this against the antispam blacklist!
 118   *           We should rather differentiate here more and make it pluggable!
 119   *
 120   * @return string blacklisted keyword found or false if no spam detected
 121   */
 122  function antispam_check( $haystack )
 123  {
 124      global $DB, $Debuglog, $Timer;
 125  
 126      // TODO: 'SELECT COUNT(*) FROM T_antispam WHERE aspm_string LIKE "%'.$url.'%" ?
 127  
 128      $Timer->resume( 'antispam_url' ); // resuming to get the total number..
 129      $block = $DB->get_var(
 130          'SELECT aspm_string
 131             FROM  T_antispam
 132            WHERE '.$DB->quote($haystack).' LIKE CONCAT("%",aspm_string,"%")
 133            LIMIT 0, 1', 0, 0, 'Check URL against antispam blacklist' );
 134      if( $block )
 135      {
 136              $Debuglog->add( 'Spam block: '.$block );
 137              return $block;    // SPAM detected!
 138      }
 139      $Timer->pause( 'antispam_url' );
 140  
 141      return false;    // no problem.
 142  }
 143  
 144  
 145  // -------------------- XML-RPC callers ---------------------------
 146  
 147  /**
 148   * Pings b2evolution.net to report abuse from a particular domain.
 149   *
 150   * @param string The keyword to report as abuse.
 151   * @return boolean True on success, false on failure.
 152   */
 153  function antispam_report_abuse( $abuse_string )
 154  {
 155      global $debug, $antispamsrv_host, $antispamsrv_port, $antispamsrv_uri, $antispam_test_for_real;
 156      global $baseurl, $Messages, $Settings;
 157  
 158      if( ! $Settings->get('antispam_report_to_central') )
 159      {
 160          $Messages->add( 'Reporting is disabled.', 'error' );  // NO TRANS necessary
 161          return;
 162      }
 163  
 164      if( preg_match( '#^http://localhost[/:]#', $baseurl) && ( $antispamsrv_host != 'localhost' ) && empty( $antispam_test_for_real )  )
 165      { // Local install can only report to local test server
 166          $Messages->add( T_('Reporting abuse to b2evolution aborted (Running on localhost).'), 'error' );
 167          return(false);
 168      }
 169  
 170      // Construct XML-RPC client:
 171      load_funcs('xmlrpc/model/_xmlrpc.funcs.php');
 172      $client = new xmlrpc_client( $antispamsrv_uri, $antispamsrv_host, $antispamsrv_port);
 173      $client->debug = $debug;
 174  
 175      // Construct XML-RPC message:
 176      $message = new xmlrpcmsg(
 177                                  'b2evo.reportabuse',                        // Function to be called
 178                                  array(
 179                                      new xmlrpcval(0,'int'),                   // Reserved
 180                                      new xmlrpcval('annonymous','string'),     // Reserved
 181                                      new xmlrpcval('nopassrequired','string'), // Reserved
 182                                      new xmlrpcval($abuse_string,'string'),    // The abusive string to report
 183                                      new xmlrpcval($baseurl,'string'),         // The base URL of this b2evo
 184                                  )
 185                              );
 186      $result = $client->send($message);
 187      if( $ret = xmlrpc_logresult( $result, $Messages, false ) )
 188      { // Remote operation successful:
 189          antispam_update_source( $abuse_string, 'reported' );
 190  
 191          $Messages->add( sprintf( T_('Reported abuse to %s.'), $antispamsrv_host ), 'success' );
 192      }
 193      else
 194      {
 195          $Messages->add( T_('Failed to report abuse to b2evolution.net.'), 'error' );
 196      }
 197  
 198      return($ret);
 199  }
 200  
 201  
 202  /**
 203   * Request abuse list from central blacklist.
 204   *
 205   * @return boolean true = success, false = error
 206   */
 207  function antispam_poll_abuse()
 208  {
 209      global $Messages, $Settings, $baseurl, $debug, $antispamsrv_host, $antispamsrv_port, $antispamsrv_uri;
 210  
 211      // Construct XML-RPC client:
 212      load_funcs('xmlrpc/model/_xmlrpc.funcs.php');
 213      $client = new xmlrpc_client( $antispamsrv_uri, $antispamsrv_host, $antispamsrv_port);
 214      $client->debug = $debug;
 215  
 216      // Get datetime from last update, because we only want newer stuff...
 217      $last_update = $Settings->get( 'antispam_last_update' );
 218      // Encode it in the XML-RPC format
 219      $Messages->add( T_('Latest update timestamp').': '.$last_update, 'note' );
 220      $startat = mysql2date( 'Ymd\TH:i:s', $last_update );
 221      //$startat = iso8601_encode( mktime(substr($m,11,2),substr($m,14,2),substr($m,17,2),substr($m,5,2),substr($m,8,2),substr($m,0,4)) );
 222  
 223      // Construct XML-RPC message:
 224      $message = new xmlrpcmsg(
 225                                  'b2evo.pollabuse',                            // Function to be called
 226                                  array(
 227                                      new xmlrpcval(0,'int'),                     // Reserved
 228                                      new xmlrpcval('annonymous','string'),       // Reserved
 229                                      new xmlrpcval('nopassrequired','string'),   // Reserved
 230                                      new xmlrpcval($startat,'dateTime.iso8601'), // Datetime to start at
 231                                      new xmlrpcval(0,'int')                      // Reserved
 232                                  )
 233                              );
 234  
 235      $Messages->add( sprintf( T_('Requesting abuse list from %s...'), $antispamsrv_host ), 'note' );
 236  
 237      $result = $client->send($message);
 238  
 239      if( $ret = xmlrpc_logresult( $result, $Messages, false ) )
 240      { // Response is not an error, let's process it:
 241          $response = $result->value();
 242          if( $response->kindOf() == 'struct' )
 243          { // Decode struct:
 244              $response = xmlrpc_decode_recurse($response);
 245              if( !isset( $response['strings'] ) || !isset( $response['lasttimestamp'] ) )
 246              {
 247                  $Messages->add( T_('Incomplete reponse.'), 'error' );
 248                  $ret = false;
 249              }
 250              else
 251              { // Start registering strings:
 252                  $value = $response['strings'];
 253                  if( count($value) == 0 )
 254                  {
 255                      $Messages->add( T_('No new blacklisted strings are available.'), 'note' );
 256                  }
 257                  else
 258                  { // We got an array of strings:
 259                      $Messages->add( T_('Adding strings to local blacklist:'), 'note' );
 260                      foreach($value as $banned_string)
 261                      {
 262                          if( antispam_create( $banned_string, 'central' ) )
 263                          { // Creation successed
 264                              $Messages->add( T_('Adding:').' &laquo;'.$banned_string.'&raquo;: '
 265                                  .T_('OK.'), 'note' );
 266                          }
 267                          else
 268                          { // Was already handled
 269                              $Messages->add( T_('Adding:').' &laquo;'.$banned_string.'&raquo;: '
 270                                  .T_('Not necessary! (Already handled)'), 'note' );
 271                              antispam_update_source( $banned_string, 'central' );
 272                          }
 273                      }
 274                      // Store latest timestamp:
 275                      $endedat = date('Y-m-d H:i:s', iso8601_decode($response['lasttimestamp']) );
 276                      $Messages->add( T_('New latest update timestamp').': '.$endedat, 'note' );
 277  
 278                      $Settings->set( 'antispam_last_update', $endedat );
 279                      $Settings->dbupdate();
 280                  }
 281                  $Messages->add( T_('Done.'), 'success' );
 282              }
 283          }
 284          else
 285          {
 286              $Messages->add( T_('Invalid reponse.'), 'error' );
 287              $ret = false;
 288          }
 289      }
 290  
 291      return($ret);
 292  }
 293  
 294  
 295  /**
 296   * Get the base domain that could be blacklisted from an URL.
 297   *
 298   * We want to concentrate on the main domain and we want to prefix it with either . or // in order not
 299   * to blacklist too large.
 300   *
 301   * {@internal This function gets tested in _misc.funcs.simpletest.php}}
 302   *
 303   * @param string URL or domain
 304   * @return string|false the pattern to match this domain in the blacklist; false if we could not extract the base domain
 305   */
 306  function get_ban_domain( $url )
 307  {
 308      // echo '<p>'.$url;
 309  
 310      // Remove http:// part + everything after the last path element ( '/' alone is ignored on purpose )
 311      $domain = preg_replace( '~^ ([a-z]+://)? ([^/#]+) (/ ([^/]*/)+ )? .* ~xi', '\\2\\3', $url );
 312  
 313      // echo '<br>'.$domain;
 314  
 315      if( preg_match( '~^[0-9.]+$~', $domain ) )
 316      {    // All numeric = IP address, don't try to cut it any further
 317          return '//'.$domain;
 318      }
 319  
 320      // Remove any www*. prefix:
 321      $base_domain = preg_replace( '~^(www \w* \. )~xi', '', $domain );
 322  
 323      if( empty($base_domain) )
 324      {
 325          return false;
 326      }
 327  
 328      if( evo_strlen( $base_domain ) < evo_strlen( $domain ) )
 329      {    // The guy is spamming with subdomains (or www):
 330          return '.'.$base_domain;
 331      }
 332  
 333      // The guy is spamming with the base domain:
 334      return '//'.$base_domain;
 335  }
 336  
 337  
 338  /**
 339   * Get the blog restricted condition
 340   *
 341   * Creates an sql command part, which is a condition, that restrict to show comments from those blogs,
 342   * where current user has no edit permission for comments.
 343   * It is used by the antispam.ctrl, when current_User wants to delete the affected comments.
 344   * 
 345   * asimo> It was changed so it doesn't restrict to blogs now, but it restricts to comment statuses.
 346   * When we will have per blog permanently delete comments permission then this function must be changed.
 347   *
 348   * @param array with key => value pairs, where the keys are the comment statuses and values are the boolean values to delete comments with the given statuses or not
 349   * @return string sql WHERE condition part, corresponding the user permissions
 350   */
 351  function blog_restrict( $delstatuses )
 352  {
 353      global $current_User;
 354  
 355      if( empty( $delstatuses ) )
 356      { // none of the statuses should be deleted
 357          return ' AND false';
 358      }
 359  
 360      // asimo> Currently only global blogs editall permission gives rights to permanently delete comments
 361      // Probably this function must be changed when the advanced collection perms will be finished
 362      if( !$current_User->check_perm( 'blogs', 'editall', false ) )
 363      { // User has permission to permanently delete comments on this blog
 364          return ' AND false';
 365      }
 366  
 367      $restriction = '( comment_status = "%s" )';
 368      $or = '';
 369      $condition = '';
 370      foreach( $delstatuses as $status )
 371      {
 372          $condition = $condition.$or.sprintf( $restriction, $status/*, $blog_ids */);
 373          $or = ' OR ';
 374      }
 375  
 376      return ' AND ( '.$condition.' )';
 377  }
 378  
 379  
 380  /**
 381   * Show affected comments
 382   *
 383   * @param array affected Comment list, all comments in this list must have the same status
 384   * @param string Comment visibility status in this list
 385   * @param string ban keyword
 386   * @param integer The number of corresponding comments on which current user has no permission
 387   */
 388  function echo_affected_comments( $affected_comments, $status, $keyword, $noperms_count )
 389  {
 390      global $current_User;
 391  
 392      $num_comments = count( $affected_comments );
 393      if( $num_comments == 0 )
 394      {
 395          if( $noperms_count == 0 )
 396          { // There isn't any affected comment witch corresponding status
 397              printf( '<p>'.T_('No %s comments match the keyword [%s].').'</p>', '<strong>'.$status.'</strong>', htmlspecialchars($keyword) );
 398          }
 399          else
 400          { // There are affected comment witch corresponding status, but current user has no permission
 401              printf( '<p>'.T_('There are %d matching %s comments, but you have no permission to edit them.').'</p>', $noperms_count, '<strong>'.$status.'</strong>' );
 402          }
 403          return;
 404      }
 405  
 406      echo '<p>';
 407      if( $current_User->check_perm( 'blogs', 'editall', false ) )
 408      { // current User has rights to permanently delete comments
 409          $checkbox_status = 'checked="checked"';
 410      }
 411      else
 412      { // current User doesn't have rights to permanently delete comments, so disable delete checkbox
 413          $checkbox_status = 'disabled="disabled"';
 414      }
 415      echo '<input type="checkbox" name="del'.$status.'" id="del'.$status.'_cb" value="1" '.$checkbox_status.'/>';
 416      echo '<label for="del'.$status.'_cb"> ';
 417      echo sprintf ( T_('Delete the following %s %s comments:'), $num_comments == 500 ? '500+' : $num_comments, '<strong>'.$status.'</strong>' );
 418      echo '</label>';
 419      echo '</p>';
 420  
 421      echo '<table class="grouped" cellspacing="0">';
 422      echo '<thead><tr>';
 423      echo '<th class="firstcol">'.T_('Date').'</th>';
 424      echo '<th class="center">'.T_('Auth. IP').'</th>';
 425      echo '<th>'.T_('Author').'</th>';
 426      echo '<th>'.T_('Auth. URL').'</th>';
 427      echo '<th>'.T_('Content starts with...').'</th>';
 428      echo '<th class="shrinkwrap">'.T_('Action').'</th>';
 429      echo '</tr></thead>';
 430      $count = 0;
 431      foreach( $affected_comments as $Comment )
 432      {
 433          echo '<tr class="'.(($count%2 == 1) ? 'odd' : 'even').'">';
 434          echo '<td class="firstcol timestamp">'.mysql2localedatetime_spans( $Comment->get( 'date' ) ).'</td>';
 435          echo '<td class="center">'.$Comment->get( 'author_IP' ).'</td>';
 436          echo '<td>'.$Comment->get_author_name().'</td>';
 437          echo '<td>';
 438          disp_url( $Comment->get_author_url(), 50 );
 439          echo '</td>';
 440          echo '<td>'.strmaxlen(strip_tags( $Comment->get_content( 'raw_text' ) ), 71).'</td>';
 441          // no permission check, because affected_comments contains current user editable comments
 442          echo '<td class="shrinkwrap">'.action_icon( T_('Edit...'), 'edit', '?ctrl=comments&amp;action=edit&amp;comment_ID='.$Comment->ID ).'</td>';
 443          echo '</tr>';
 444          $count++;
 445      }
 446      echo "</tbody></table>";
 447  }
 448  
 449  
 450  /**
 451   * Get IP range from DB
 452   *
 453   * @param integer IP start of range
 454   * @param integer IP end of range
 455   * @param integer ID of existing IP range
 456   * @return object Row of the table T_antispam__iprange (NULL - if IP range doesn't exist in DB yet)
 457  */
 458  function get_ip_range( $ip_start, $ip_end, $aipr_ID = 0 )
 459  {
 460      global $DB;
 461  
 462      $SQL = new SQL();
 463      $SQL->SELECT( '*' );
 464      $SQL->FROM( 'T_antispam__iprange' );
 465      $SQL->WHERE( ' (
 466          ( '.$DB->quote( $ip_start ).' >= aipr_IPv4start AND '.$DB->quote( $ip_start ).' <= aipr_IPv4end ) OR
 467          ( '.$DB->quote( $ip_end ).' >= aipr_IPv4start AND '.$DB->quote( $ip_end ).' <= aipr_IPv4end ) OR
 468          ( '.$DB->quote( $ip_start ).' <= aipr_IPv4start AND '.$DB->quote( $ip_end ).' >= aipr_IPv4end )
 469      )' );
 470      if( !empty( $aipr_ID ) )
 471      {
 472          $SQL->WHERE_and( 'aipr_ID != '.$aipr_ID );
 473      }
 474  
 475      return $DB->get_row( $SQL->get() );
 476  }
 477  
 478  
 479  /**
 480   * Block request by IP address
 481   *
 482   * @param string IP address
 483   */
 484  function antispam_block_ip( $IP_address = '' )
 485  {
 486      global $DB;
 487  
 488      if( empty( $IP_address ) && array_key_exists( 'REMOTE_ADDR', $_SERVER ) )
 489      {
 490          $IP_address = $_SERVER['REMOTE_ADDR'];
 491      }
 492  
 493      $IP_address = ip2int( $IP_address );
 494  
 495      $SQL = new SQL();
 496      $SQL->SELECT( 'aipr_ID' );
 497      $SQL->FROM( 'T_antispam__iprange' );
 498      $SQL->WHERE( 'aipr_IPv4start <= '.$DB->quote( $IP_address ) );
 499      $SQL->WHERE_and( 'aipr_IPv4end >= '.$DB->quote( $IP_address ) );
 500      $SQL->WHERE_and( 'aipr_status = \'blocked\'' );
 501      $ip_range_ID = $DB->get_var( $SQL->get() );
 502  
 503      if( !is_null( $ip_range_ID ) )
 504      { // We should block the request from this IP address
 505          $DB->query( 'UPDATE T_antispam__iprange
 506              SET aipr_block_count = aipr_block_count + 1
 507              WHERE aipr_ID = '.$DB->quote( $ip_range_ID ) );
 508  
 509          debug_die( 'This request has been blocked.' );
 510      }
 511  }
 512  
 513  
 514  /**
 515   * Move user to suspect group by IP address
 516   *
 517   * @param string IP address
 518   */
 519  function antispam_suspect_user( $IP_address = '' )
 520  {
 521      global $DB, $Settings, $current_User;
 522  
 523      $suspicious_group_ID = $Settings->get('antispam_suspicious_group');
 524  
 525      if( empty( $suspicious_group_ID ) )
 526      { // We don't need to move users to suspicious group
 527          return;
 528      }
 529  
 530      if( !is_logged_in() )
 531      { // User must be logged in for this action
 532          return;
 533      }
 534  
 535      if( $current_User->grp_ID == $suspicious_group_ID )
 536      { // Current User already is in suspicious group
 537          return;
 538      }
 539  
 540      $antispam_trust_groups = $Settings->get('antispam_trust_groups');
 541      if( !empty( $antispam_trust_groups ) )
 542      {
 543          $antispam_trust_groups = explode( ',', $antispam_trust_groups );
 544          if( in_array( $current_User->grp_ID, $antispam_trust_groups ) )
 545          { // Current User has group which cannot be moved to suspicious users
 546              return;
 547          }
 548      }
 549  
 550      if( empty( $IP_address ) && array_key_exists( 'REMOTE_ADDR', $_SERVER ) )
 551      {
 552          $IP_address = $_SERVER['REMOTE_ADDR'];
 553      }
 554  
 555      $IP_address = ip2int( $IP_address );
 556  
 557      $SQL = new SQL();
 558      $SQL->SELECT( 'aipr_ID' );
 559      $SQL->FROM( 'T_antispam__iprange' );
 560      $SQL->WHERE( 'aipr_IPv4start <= '.$DB->quote( $IP_address ) );
 561      $SQL->WHERE_and( 'aipr_IPv4end >= '.$DB->quote( $IP_address ) );
 562      $SQL->WHERE_and( 'aipr_status = \'suspect\'' );
 563      $ip_range_ID = $DB->get_row( $SQL->get() );
 564  
 565      if( !is_null( $ip_range_ID ) )
 566      { // Move current user to suspicious group because current IP address is suspected
 567          $GroupCache = & get_GroupCache();
 568          if( $suspicious_Group = & $GroupCache->get_by_ID( $suspicious_group_ID, false, false ) )
 569          { // Group exists in DB and we can change user's group
 570              $current_User->set_Group( $suspicious_Group );
 571              $current_User->dbupdate();
 572          }
 573      }
 574  }
 575  
 576  
 577  /**
 578   * Get status titles of ip range
 579   *
 580   * @param boolean TRUE - to include false statuses, which don't exist in DB
 581   * @return array Status titles
 582   */
 583  function aipr_status_titles( $include_false_statuses = true )
 584  {
 585      $status_titles = array();
 586      if( $include_false_statuses )
 587      {    // Include Unknown status
 588          $status_titles[''] = T_('Unknown ');
 589      }
 590      $status_titles['trusted'] = T_('Trusted');
 591      $status_titles['suspect'] = T_('Suspect');
 592      $status_titles['blocked'] = T_('Blocked');
 593  
 594      return $status_titles;
 595  }
 596  
 597  
 598  /**
 599   * Get status colors of ip range
 600   *
 601   * @return array Color values
 602   */
 603  function aipr_status_colors()
 604  {
 605      return array(
 606              ''        => '999999',
 607              'trusted' => '00CC00',
 608              'suspect' => 'FFAA00',
 609              'blocked' => 'FF0000',
 610          );
 611  }
 612  
 613  
 614  /**
 615   * Get array of status icons for email address
 616   *
 617   * @return array Status icons
 618   */
 619  function aipr_status_icons()
 620  {
 621      return array(
 622              ''        => get_icon( 'bullet_white', 'imgtag', array( 'title' => aipr_status_title( '' ) ) ),
 623              'trusted' => get_icon( 'bullet_green', 'imgtag', array( 'title' => aipr_status_title( 'trusted' ) ) ),
 624              'suspect' => get_icon( 'bullet_orange', 'imgtag', array( 'title' => aipr_status_title( 'suspect' ) ) ),
 625              'blocked' => get_icon( 'bullet_red', 'imgtag', array( 'title' => aipr_status_title( 'blocked' ) ) )
 626          );
 627  }
 628  
 629  
 630  /**
 631   * Get status title of ip range by status value
 632   *
 633   * @param string Status value
 634   * @return string Status title
 635   */
 636  function aipr_status_title( $status )
 637  {
 638      $aipr_statuses = aipr_status_titles();
 639  
 640      return isset( $aipr_statuses[ $status ] ) ? $aipr_statuses[ $status ] : $status;
 641  }
 642  
 643  
 644  /**
 645   * Get status color of ip range by status value
 646   *
 647   * @param string Status value
 648   * @return string Color value
 649   */
 650  function aipr_status_color( $status )
 651  {
 652      if( $status == 'NULL' )
 653      {
 654          $status = '';
 655      }
 656  
 657      $aipr_status_colors = aipr_status_colors();
 658  
 659      return isset( $aipr_status_colors[ $status ] ) ? '#'.$aipr_status_colors[ $status ] : 'none';
 660  }
 661  
 662  
 663  /**
 664   * Get status icon of ip range by status value
 665   *
 666   * @param string Status value
 667   * @return string Icon
 668   */
 669  function aipr_status_icon( $status )
 670  {
 671      $aipr_status_icons = aipr_status_icons();
 672  
 673      return isset( $aipr_status_icons[ $status ] ) ? $aipr_status_icons[ $status ] : '';
 674  }
 675  
 676  
 677  /**
 678   * Get blogs with comments numbers
 679   *
 680   * @param string Comment status
 681   * @return array Blogs
 682   */
 683  function antispam_bankruptcy_blogs( $comment_status = NULL )
 684  {
 685      global $DB, $Settings;
 686  
 687      $SQL = new SQL( 'Get blogs list with number of comments' );
 688      $SQL->SELECT( 'blog_ID, blog_name, COUNT( comment_ID ) AS comments_count' );
 689      $SQL->FROM( 'T_comments' );
 690      $SQL->FROM_add( 'INNER JOIN T_items__item ON comment_post_ID = post_ID' );
 691      $SQL->FROM_add( 'INNER JOIN T_categories ON post_main_cat_ID = cat_ID' );
 692      $SQL->FROM_add( 'INNER JOIN T_blogs ON cat_blog_ID = blog_ID' );
 693      if( !empty( $comment_status ) )
 694      { // Limit by comment status
 695          $SQL->WHERE( 'comment_status = '.$DB->quote( $comment_status ) );
 696      }
 697      $SQL->GROUP_BY( 'blog_ID' );
 698      $SQL->ORDER_BY( 'blog_'.$Settings->get('blogs_order_by').' '.$Settings->get('blogs_order_dir') );
 699  
 700      return $DB->get_results( $SQL->get() );
 701  }
 702  
 703  
 704  /**
 705   * Delete ALL comments from selected blogs
 706   *
 707   * @param string Comment status
 708   * @param array Blog IDs
 709   */
 710  function antispam_bankruptcy_delete( $blog_IDs = array(), $comment_status = NULL )
 711  {
 712      global $DB;
 713  
 714      if( empty( $blog_IDs ) )
 715      { // No blogs selected
 716          echo T_('Please select at least one blog.');
 717          return;
 718      }
 719  
 720      echo T_('The comments are deleting...');
 721      evo_flush();
 722  
 723      $DB->begin();
 724  
 725      $items_IDs_SQL = new SQL( 'Get all posts IDs of selected blogs' );
 726      $items_IDs_SQL->SELECT( 'postcat_post_ID' );
 727      $items_IDs_SQL->FROM( 'T_postcats' );
 728      $items_IDs_SQL->FROM_add( 'INNER JOIN T_categories ON postcat_cat_ID = cat_ID' );
 729      $items_IDs_SQL->WHERE( 'cat_blog_ID IN ( '.$DB->quote( $blog_IDs ).' )' );
 730      $items_IDs = $DB->get_col( $items_IDs_SQL->get() );
 731  
 732      $comments_IDs_SQL = new SQL( 'Get all comments IDs of selected blogs' );
 733      $comments_IDs_SQL->SELECT( 'comment_ID' );
 734      $comments_IDs_SQL->FROM( 'T_comments' );
 735      $comments_IDs_SQL->WHERE( 'comment_post_ID IN ( '.$DB->quote( $items_IDs ).' )' );
 736      if( !empty( $comment_status ) )
 737      { // Limit by comment status
 738          $comments_IDs_SQL->WHERE_and( 'comment_status = '.$DB->quote( $comment_status ) );
 739      }
 740  
 741      $affected_rows = 1;
 742      while( $affected_rows > 0 )
 743      {
 744          $affected_rows = 0;
 745  
 746          // Delete the cascades
 747          $affected_rows += $DB->query( 'DELETE FROM T_links
 748              WHERE link_cmt_ID IN ( '.$comments_IDs_SQL->get().' )
 749              LIMIT 10000' );
 750          $affected_rows += $DB->query( 'DELETE FROM T_comments__prerendering
 751              WHERE cmpr_cmt_ID IN ( '.$comments_IDs_SQL->get().' )
 752              LIMIT 10000' );
 753          $affected_rows += $DB->query( 'DELETE FROM T_comments__votes
 754              WHERE cmvt_cmt_ID IN ( '.$comments_IDs_SQL->get().' )
 755              LIMIT 10000' );
 756  
 757          // Delete the comments
 758          $sql_comments_where = '';
 759          if( !empty( $comment_status ) )
 760          { // Limit by comment status
 761              $sql_comments_where = ' AND comment_status = '.$DB->quote( $comment_status );
 762          }
 763          $affected_rows += $DB->query( 'DELETE FROM T_comments 
 764              WHERE comment_post_ID IN ( '.$DB->quote( $items_IDs ).' )'.
 765              $sql_comments_where.'
 766              LIMIT 10000' );
 767  
 768          echo ' .';
 769          evo_flush();
 770      }
 771  
 772      echo 'OK';
 773  
 774      $DB->commit();
 775  }
 776  
 777  ?>

title

Description

title

Description

title

Description

title

title

Body