diff -uprN /home/files/coding/drupal/spam/filters/bayesian/bayesian.info sites/all/modules/spam/filters/bayesian/bayesian.info --- /home/files/coding/drupal/spam/filters/bayesian/bayesian.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/bayesian/bayesian.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,12 +0,0 @@ -; $Id: bayesian.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ -name = Bayesian filter -description = A bayesian filter. -package = Spam -dependencies[] = spam -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/bayesian/bayesian.install sites/all/modules/spam/filters/bayesian/bayesian.install --- /home/files/coding/drupal/spam/filters/bayesian/bayesian.install 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/bayesian/bayesian.install 1970-01-01 10:00:00.000000000 +1000 @@ -1,105 +0,0 @@ -. All rights reserved. - * - * Provides a generic Bayesian filter for use with other modules. - * Defines hooks for use with the Spam API. - */ - -/** - * Implementation of hook_schema(). - */ -function bayesian_schema() { - $schema['bayesian_tokens'] = array( - 'description' => t('TODO'), - 'fields' => array( - 'tid' => array( - 'description' => t('TODO'), - 'type' => 'serial', - 'unsigned' => 1, - 'not null' => TRUE, - ), - 'class' => array( - 'description' => t('TODO'), - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'token' => array( - 'description' => t('TODO'), - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'yes_count' => array( - 'description' => t('TODO'), - 'type' => 'int', - 'unsigned' => 1, - 'not null' => TRUE, - 'default' => 0, - ), - 'no_count' => array( - 'description' => t('TODO'), - 'type' => 'int', - 'unsigned' => 1, - 'not null' => TRUE, - 'default' => 0, - ), - 'probability' => array( - 'description' => t('TODO'), - 'type' => 'int', - 'unsigned' => 1, - 'not null' => TRUE, - 'default' => 0, - ), - 'last' => array( - 'description' => t('TODO'), - 'type' => 'int', - 'unsigned' => 1, - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'yes_count' => array('yes_count'), - 'no_count' => array('no_count'), - 'probability' => array('probability'), - 'last' => array('last'), - ), - 'unique keys' => array( - 'token' => array('class', 'token'), - ), - 'primary key' => array('tid'), - ); - - return $schema; -} - -/** - * Install bayesian module schema. - */ -function bayesian_install() { - // Create tables. - drupal_install_schema('bayesian'); -} - -/** - * Completely uninstall the spam module. - */ -function bayesian_uninstall() { - // Remove tables. - drupal_uninstall_schema('bayesian'); - - $tables = array('bayesian_tokens'); - foreach ($tables as $table) { - } - drupal_set_message(t('All bayesian module configuration data and tables have been deleted.')); -} - diff -uprN /home/files/coding/drupal/spam/filters/bayesian/bayesian.module sites/all/modules/spam/filters/bayesian/bayesian.module --- /home/files/coding/drupal/spam/filters/bayesian/bayesian.module 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/bayesian/bayesian.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,189 +0,0 @@ -. All rights reserved. - * - * Provides a generic Bayesian filter for use with other modules. - * Defines hooks for use with the Spam API. - */ - -/** - * Spam API Hook - */ -function bayesian_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - switch ($op) { - - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'bayesian', $type, $content, $fields, $extra)) return; - return bayesian_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'bayesian'; - break; - - case 'filter_info': - return array( - 'name' => t('Bayesian filter'), - 'module' => t('bayesian'), - 'description' => t('A bayesian spam filter.'), - 'help' => t('The bayesian filter can learn to tell the difference between valid content spam content.'), - ); - break; - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - ); - - case 'mark_as_spam': - case 'mark_as_not_spam': - if (!module_invoke('spam', 'filter_enabled', 'bayesian', $type, $content, $fields, $extra)) return; - spam_log(SPAM_DEBUG, 'bayesian_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); - $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); - $tokenizer = variable_get('bayesian_tokenizer', 'bayesian_tokenize'); - $tokens = $tokenizer($extra['content'], $type, $fields, $extra); - bayesian_tokens_update('spam', $tokens, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); - break; - } -} - -/** - * Determine whether or not the content is spam. - */ -function bayesian_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - $class = 'spam'; - $id = spam_invoke_module($type, 'content_id', $content, $extra); - $tokenizer = variable_get('bayesian_tokenizer', 'bayesian_tokenize'); - $tokens = $tokenizer($content, $type, $fields, $extra); - if (is_array($tokens)) { - foreach ($tokens as $token) { - $p = db_fetch_object(db_query("SELECT probability FROM {bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); - if (!$p-> 'help' => t('The bayesian filtability = variable_get('bayesian_default_probability', 40); - } - $t["$token,$p->probak; - - case 'filter_install': 50); - } - } - else { - // No tokens, return default score. - $action['total'] = variable_get('bayesian_default_probability', 40); - return $action; - } - - /* Sort token array so those tokens with the largest "drift" come first. - * Drift is this distance from a median of 50%. - */ - asort($t); - - /* Take the n most "interesting" tokens from the top of the token array. - * The larger a token's drift, the more interesting it is. - */ - $keys = array_keys($t); - $max = variable_get('bayesian_interesting_tokens', 15); - $total = 0; - for ($i = 0; $i < $max; $i++) { - if ($pair = array_pop($keys)) { - $p = explode(',', $pair); - $total = $total + $p[1]; - $action['bayesian'][$i] = array( - 'token' => $p[0], - 'probability' => $p[1], - ); - spam_log(SPAM_DEBUG, 'bayesian_spam_filter', t('interesting token [@count] (@token) probability(@probability)', array('@token' => $p[0], '@probability' => $p[1], '@count' => $i + 1)), $type, $id); - } - else { - // we've looked at all the tokens - break; - } - } - - $probability = round($total / $i, 1); - spam_log(SPAM_VERBOSE, 'bayesian_spam_filter', t('total(@total) count(@count) probability(@probability)', array('@probability' => $probability, '@total' => $total, '@count' => $i)), $type, $id); - - $action['total'] = $probability; - return $action; -} - -/** - * Update token probabilities in database. - */ -function bayesian_tokens_update($class, $tokens, $yes, $type = NULL, $id = 0) { - if (!is_array($tokens) || empty($tokens)) return; - foreach ($tokens as $token) { - $old = db_fetch_object(db_query("SELECT probability, yes_count, no_count FROM {bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); - if ($old->probability) { - $total = $old->yes_count + $old->no_count + 1; - $probability = spam_sanitize_score(($old->yes_count + ($yes ? 1 : 0)) / $total * 100); - spam_log(SPAM_DEBUG, 'bayesian_tokens_update', t('update token(@token) class(@class) yes(@yes) no(@no) prob(@prob): added @new', array('@token' => $token, '@class' => $class, '@yes' => $old->yes_count + ($yes ? 1 : 0), '@no' => $old->no_count + ($yes ? 0 : 1), '@prob' => $probability, '@new' => $yes ? 'yes' : 'no')), $type, $id); - if ($yes) { - db_query("UPDATE {bayesian_tokens} SET yes_count = yes_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); - } - else { - db_query("UPDATE {bayesian_tokens} SET no_count = no_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); - } - } - else { - $probability = ($yes ? 99 : 1); - spam_log(SPAM_DEBUG, 'bayesian_tokens_update', t('insert token(@token) class(@class) probability(@probability)', array('@token' => $token, '@class' => $class, '@probability' => $probability)), $type, $id); - db_query("INSERT INTO {bayesian_tokens} (class, token, yes_count, no_count, probability, last) VALUES('%s', '%s', %d, %d, %d, %d)", $class, $token, ($yes ? 1 : 0), ($yes ? 0 : 1), $probability, time()); - } - } -} - -/** - * Split content into an array of tokens. - */ -function bayesian_tokenize($content, $type, $fields, $extra = array(), $tag = NULL) { - static $tokens = array(); - - $id = spam_invoke_module($type, 'content_id', $content, $extra); - - if (is_object($content)) { - $content = (array)$content; - } - - if (!isset($tokens["$type-$id-$tag"])) { - $string = spam_get_text($content, $type, $fields, $extra); - - $URI = "(http://|https://|ftp://|mailto:)"; - // strip out unwanted html/url noise - $sanitized = preg_replace("'(www\.)|()|(href=)|(target=)|(src=)'i", '', $string); - $sanitized = preg_replace($URI, '', $sanitized); - - // Strip out values that should not be considered part of tokens, so - // things like '{viagra}' and 'vi.agra' are counted as hits towards - // 'viagra' - $sanitized = preg_replace("/[()\{\}\[\]#.,]/", '', $sanitized); - - // Force all tokens to lowercase, again to aggregate tokens. This both - // lowers the total token number of rows in the spam_tokens table and - // increases the strength of individual tokens by linking them to - // capitalized versions. - $sanitized = drupal_strtolower($sanitized); - - // divide sanitized string into tokens - $delimiters = " \t\n\r-_<>'\"`/|*%^&+=~:;?"; - $tok = strtok($sanitized, $delimiters); - while ($tok !== FALSE) { - // Only inspect the token if over minimum length. - if (drupal_strlen($tok) >= variable_get('bayesian_minimum_token_length', 3)) { - // If the token is longer than 255 characters, truncate it. - $toks[] = htmlspecialchars(drupal_substr("$tag$tok", 0, 254)); - } - $tok = strtok($delimiters); - } - - // allow external module ability to extract additional tokens - $hook = spam_invoke_api('tokenize', $string, $tag); - if ($hook['tokens']) { - $toks = array_merge($toks, $hook['tokens']); - } - $tokens["$type-$id-$tag"] = $toks; - } - - return $tokens["$type-$id-$tag"]; -} diff -uprN /home/files/coding/drupal/spam/filters/custom/custom.info sites/all/modules/spam/filters/custom/custom.info --- /home/files/coding/drupal/spam/filters/custom/custom.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/custom/custom.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,12 +0,0 @@ -; $Id: custom.info,v 1.1.2.2.2.1 2008/12/25 05:42:10 jeremy Exp $ -name = Custom filter -description = Allows the creation of custom spam filter rules. -package = Spam -dependencies[] = spam -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/custom/custom.install sites/all/modules/spam/filters/custom/custom.install --- /home/files/coding/drupal/spam/filters/custom/custom.install 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/custom/custom.install 1970-01-01 10:00:00.000000000 +1000 @@ -1,43 +0,0 @@ - array( - 'cid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), - 'filter' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => ''), - 'style' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), - 'status' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '2'), - 'scan' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), - 'action' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), - 'matches' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), - 'last' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), - 'weight' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), - ), - 'primary key' => array('cid'), - 'indexes' => array( - 'filter' => array('filter'), - 'last' => array('last'), - 'matches' => array('matches'), - 'weight' => array('weight'), - ), - ); - - return $schema; -} - -function custom_install() { - // Create my tables. - drupal_install_schema('custom'); -} - -function custom_uninstall() { - // Remove tables. - drupal_uninstall_schema('custom'); - drupal_set_message('The spam_custom table has been dropped.'); -} diff -uprN /home/files/coding/drupal/spam/filters/custom/custom.module sites/all/modules/spam/filters/custom/custom.module --- /home/files/coding/drupal/spam/filters/custom/custom.module 2009-08-06 00:25:13.000000000 +1000 +++ sites/all/modules/spam/filters/custom/custom.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,584 +0,0 @@ -. - * - * Allows manual definition of words and regular expressions to detect spam - * content. - */ - -define(SPAM_CUSTOM_STYLE_PLAIN, 0); -define(SPAM_CUSTOM_STYLE_REGEX, 1); - -define(SPAM_CUSTOM_STATUS_NOTSPAM, -2); -define(SPAM_CUSTOM_STATUS_PROBABLYNOT, -1); -define(SPAM_CUSTOM_STATUS_DISABLED, 0); -define(SPAM_CUSTOM_STATUS_PROBABLY, 1); -define(SPAM_CUSTOM_STATUS_SPAM, 2); - -define(SPAM_CUSTOM_SCAN_CONTENT, 0x1); -define(SPAM_CUSTOM_SCAN_REFERRER, 0x4); -define(SPAM_CUSTOM_SCAN_USERAGENT, 0x8); - -// TODO: support actions -//define(SPAM_CUSTOM_ACTION_DELETE, 0x1); -//define(SPAM_CUSTOM_ACTION_MAIL, 0x2); - -/** - * Spam API Hook - */ -function custom_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - switch ($op) { - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'custom', $type, $content, $fields, $extra)) return; - return custom_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'custom'; - - case 'filter_info': - return array( - 'name' => t('Custom filter'), - 'module' => t('custom'), - 'description' => t('Custom spam filters.'), - 'help' => t('The custom spam filter module allows you to manually define custom spam filter rules.'), - ); - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - 'gain' => 250, - 'weight' => -4, - ); - } -} - -/** - * Drupal _menu() hook. - */ -function custom_menu() { - $items = array(); - - $items['admin/settings/spam/filters/custom'] = array( - 'title' => 'Custom', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('custom_admin_settings'), - 'access arguments' => array('administer spam'), - 'description' => 'Configure the custom spam filter module.', - 'type' => MENU_LOCAL_TASK, - ); - - $items['admin/settings/spam/filters/custom/list'] = array( - 'title' => 'List', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('custom_admin_settings'), - 'access arguments' => array('administer spam'), - 'description' => 'Configure the custom spam filter module.', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['admin/settings/spam/filters/custom/create'] = array( - 'title' => 'Create', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('custom_admin_filter'), - 'access arguments' => array('administer spam'), - 'description' => 'Create a custom spam filter.', - 'type' => MENU_LOCAL_TASK, - ); - $items["admin/settings/spam/filters/custom/%/edit"] = array( - 'title' => 'Create', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('custom_admin_filter', 5), - 'access arguments' => array('administer spam'), - 'description' => 'Edit a custom spam filter.', - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -/** - * Adminsitrative interface for configuring custom spam filter rules. - */ -function custom_admin_settings() { - $form = array(); - - $form['options'] = array( - '#type' => 'fieldset', - '#title' => t('Options'), - '#prefix' => '
', - '#suffix' => '
' - ); - $options = array(); - foreach (module_invoke_all('spam_custom_operations') as $operation => $op) { - $options[$operation] = $op['label']; - } - $form['options']['operation'] = array( - '#type' => 'select', - '#options' => $options, - '#default_value' => 'scan' - ); - $form['options']['submit'] = array( - '#type' => 'submit', - '#value' => t('Execute') - ); - - $rows = array(); - $result = pager_query('SELECT * FROM {spam_custom} ORDER BY weight ASC'); - while ($custom = db_fetch_object($result)) { - $all[$custom->cid] = ''; - - // The filter text. - $form['filter'][$custom->cid] = array('#value' => $custom->filter); - - // What style of filter. - if ($custom->style == SPAM_CUSTOM_STYLE_PLAIN) { - $form['style'][$custom->cid] = array('#value' => t('Plain text')); - } - else if ($custom->style == SPAM_CUSTOM_STYLE_REGEX) { - $form['style'][$custom->cid] = array('#value' => t('Regular expression')); - } - - // What to scan. - $scan = array(); - if ($custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { - $scan[] = t('Content'); - } - if ($custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { - $scan[] = t('Referrer'); - } - if ($custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { - $scan[] = t('User agent'); - } - $form['scan'][$custom->cid] = array('#value' => implode(', ', $scan)); - - // What status to apply. - switch ($custom->status) { - case SPAM_CUSTOM_STATUS_NOTSPAM: - $status = t('Mark as not spam'); - break; - case SPAM_CUSTOM_STATUS_PROBABLYNOT: - $status = t('Mark as probably not spam'); - break; - case SPAM_CUSTOM_STATUS_DISABLED: - $status = t('Disabled'); - break; - case SPAM_CUSTOM_STATUS_PROBABLY: - $status = t('Mark as probably spam'); - break; - case SPAM_CUSTOM_STATUS_SPAM: - $status = t('Mark as spam'); - break; - default: - $status = t('Unknown'); - break; - } - $form['status'][$custom->cid] = array( - '#value' => $status - ); - - // How many times this filter has been matched. - $form['matches'][$custom->cid] = array('#value' => $custom->matches); - - // The last time this filter was matched. - $last = $custom->last ? t('@time ago', array('@time' => format_interval(time() - $custom->last))) : t('Never'); - $form['last'][$custom->cid] = array( - '#value' => $last - ); - - // Link to edit the filter. - $form['edit'][$custom->cid] = array( - '#value' => l(t('edit'), "admin/settings/spam/filters/custom/$custom->cid/edit"), - ); - - $rows[] = $row; - } - - $form['create2'] = array( - '#value' => '['. l(t('create custom filter'), 'admin/settings/spam/filters/custom/create') .']', - '#prefix' => '
', - '#suffix' => '
' - ); - - $form['custom'] = array( - '#type' => 'checkboxes', - '#options' => $all, - ); - $form['pager'] = array( - '#value' => theme('pager', NULL, 50, 0), - ); - - return $form; -} - -/** - * Drupal _theme() hook. - */ -function custom_theme() { - return array( - 'custom_admin_settings' => array( - 'file' => 'custom.module', - 'arguments' => array( - 'form' => NULL, - ), - ), - ); -} - -/** - * Format the custom filter admin page. - */ -function theme_custom_admin_settings($form) { - // TODO: use custom_upgrade() from custom_upgrade.inc in custom.install - //_custom_upgrade(); - $header = array(theme('table_select_header_cell'), t('Filter'), t('Style'), t('Scan'), t('Status'), t('Matches'), t('Last'), ''); - - $output = drupal_render($form['options']); - $rows = array(); - if (isset($form['filter']) && is_array($form['filter'])) { - foreach (element_children($form['filter']) as $key) { - $row = array(); - $row[] = drupal_render($form['custom'][$key]); - $row[] = drupal_render($form['filter'][$key]); - $row[] = drupal_render($form['style'][$key]); - $row[] = drupal_render($form['scan'][$key]); - $row[] = drupal_render($form['status'][$key]); - $row[] = drupal_render($form['matches'][$key]); - $row[] = drupal_render($form['last'][$key]); - $row[] = drupal_render($form['edit'][$key]); - $rows[] = $row; - } - - $output .= theme('table', $header, $rows); - if ($form['pager']['#value']) { - $output .= drupal_render($form['pager']); - } - } - else { - $output .= theme('table', $header, $rows); - $output .= ''. t('No custom filters created.') .''; - } - - $output .= drupal_render($form); - - return $output; -} - -/** - * Define callbacks for custom filter options. Unlike in main spam module, - * here a single ID is added to the end of the argument listing by the - * only function that calls this (custom_admin_settings_submit). - */ -function custom_spam_custom_operations() { - $operations = array( - 'disable' => array( - 'label' => t('Disable'), - 'callback' => 'custom_spam_filter_operations', - 'callback arguments' => array('disable'), - ), - 'delete' => array( - 'label' => t('Delete'), - 'callback' => 'custom_spam_filter_operations', - 'callback arguments' => array('delete'), - ), - ); - return $operations; -} - -/** - * Create or edit a custom spam filter. - */ -function custom_admin_filter($form_state, $cid = NULL) { - if ($cid) { - drupal_set_title('Edit'); - $custom = db_fetch_object(db_query('SELECT * FROM {spam_custom} WHERE cid = %d', $cid)); - if (!isset($custom->cid)) { - drupal_set_message(t('Failed to load custom filter.'), 'error'); - drupal_goto('admin/settings/spam/filters/custom'); - } - } - else { - drupal_set_title('Create'); - } - $form = array(); - $form['filter'] = array( - '#type' => 'textfield', - '#title' => t('Filter'), - '#description' => t('Enter a custom filter string. You can enter a word, a phrase, or a regular expression.'), - '#default_value' => $custom->cid ? $custom->filter : '', - '#required' => TRUE, - ); - $form['style'] = array( - '#type' => 'radios', - '#title' => t('Filter type'), - '#description' => t('For a custom filter to match exactly what you type, select plain text. If you would like to define a regular expression, your filter must be formatted as a Perl-compatible regular expression.'), - '#options' => array(SPAM_CUSTOM_STYLE_PLAIN => t('Plain text'), SPAM_CUSTOM_STYLE_REGEX => t('Regular expression')), - '#default_value' => $custom->cid ? $custom->style : SPAM_CUSTOM_STYLE_PLAIN, - '#required' => TRUE, - ); - $options = array(SPAM_CUSTOM_SCAN_CONTENT => ('Content'), SPAM_CUSTOM_SCAN_REFERRER => t('Referrer'), SPAM_CUSTOM_SCAN_USERAGENT => t('User agent')); - $scan = array(); - if ($custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { - $scan[] = SPAM_CUSTOM_SCAN_CONTENT; - } - if ($custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { - $scan[] = SPAM_CUSTOM_SCAN_REFERRER; - } - if ($custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { - $scan[] = SPAM_CUSTOM_SCAN_USERAGENT; - } - $form['scan'] = array( - '#type' => 'checkboxes', - '#title' => t('Scan'), - '#description' => t('Specify where you\'d like to apply your custom filter.'), - '#options' => $options, - '#required' => TRUE, - '#default_value' => !empty($scan) ? $scan : array(SPAM_CUSTOM_SCAN_CONTENT), - ); - $options = array(); - $form['status'] = array( - '#type' => 'radios', - '#title' => t('Status'), - '#description' => t('Select the status to apply when your custom filter matches site content. Filters are tested in the order they are displayed above, thus if content matches a filter that says to mark it as spam, and another to mark it as not spam, the first to match will be the actual status applied.'), - '#options' => array( - SPAM_CUSTOM_STATUS_DISABLED => t('Disabled'), - SPAM_CUSTOM_STATUS_SPAM => t('Mark as spam'), - SPAM_CUSTOM_STATUS_PROBABLY => t('Mark as probably spam'), - SPAM_CUSTOM_STATUS_PROBABLYNOT => t('Mark as probably not spam'), - SPAM_CUSTOM_STATUS_NOTSPAM => t('Mark as not spam')), - '#default_value' => $custom->cid ? $custom->status : SPAM_CUSTOM_STATUS_SPAM, - '#required' => TRUE, - ); - $form['weight'] = array( - '#type' => 'weight', - '#title' => t('Weight'), - '#description' => t('Give your custom filter a weight. "Lighter" filters with smaller weights will run before "heavier" filters with larger weights.'), - '#default_value' => $custom->weight, - ); - - $form['submit'] = array( - '#type' => 'submit', - '#value' => $custom->cid ? t('Update filter') : t('Create filter'), - ); - - if ($custom->cid) { - $form['cid'] = array( - '#type' => 'hidden', - '#value' => $custom->cid, - ); - } - - return $form; -} - -/** - * Be sure that the custom filter is valid. - */ -function custom_admin_filter_validate($form, &$form_state) { - if ($form_state['values']['style'] == SPAM_CUSTOM_STYLE_REGEX) { - if (preg_match($form_state['values']['filter'], 'test') === FALSE) { - form_set_error('filter', t('Failed to validate your filter\'s regular expression. It must be properly formatted as a Perl-compatible regular expression. Review the above error for details on the specific problem with your expression.')); - } - } - if (isset($form_state['values']['cid'])) { - // update - $cid = db_result(db_query("SELECT cid FROM {spam_custom} WHERE filter = '%s' AND cid <> %d", $form_state['values']['filter'], $form_state['values']['cid'])); - if ($cid) { - form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); - } - } - else { - // create - $cid = db_result(db_query("SELECT cid FROM {spam_custom} WHERE filter = '%s'", $form_state['values']['filter'])); - if ($cid) { - form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); - } - } -} - -/** - * Create/update custom filer. - */ -function custom_admin_filter_submit($form, &$form_state) { - $scan = 0; - if (is_array($form_state['values']['scan'])) { - foreach ($form_state['values']['scan'] as $s) { - $scan += $s; - } - } - if (isset($form_state['values']['cid'])) { - db_query("UPDATE {spam_custom} SET filter = '%s', style = %d, status = %d, scan = %d, weight = %d WHERE cid = %d", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight'], $form_state['values']['cid']); - drupal_set_message(t('Custom filter %filter updated.', array('%filter' => $form_state['values']['filter']))); - } - else { - db_query("INSERT INTO {spam_custom} (filter, style, status, scan, weight) VALUES ('%s', %d, %d, %d, %d)", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight']); - drupal_set_message(t('Custom filter %filter created.', array('%filter' => $form_state['values']['filter']))); - } - drupal_goto('admin/settings/spam/filters/custom'); -} - -/** - * Perform bulk operations on the filters. - */ -function custom_admin_settings_submit($form, &$form_state) { - if (is_array($form_state['values']['custom'])) { - foreach ($form_state['values']['custom'] as $cid => $selected) { - if ($selected) { - $process[] = $cid; - } - } - } - if (!empty($process)) { - foreach (module_invoke_all('spam_custom_operations') as $operation => $op) { - $options[$operation] = $op; - } - $operation = $form_state['values']['operation']; - if (isset($options[$operation])) { - $function = $options[$operation]['callback']; - $arguments = $options[$operation]['callback arguments']; - //TODO: Why is order different than spam.module, and why 1 at a time? - foreach ($process as $cid) { - call_user_func_array($function, array_merge($arguments, array($cid))); - } - } - } -} - -/** - * Perform custom operations. - * TODO: Confirmation would be nice. - */ -function custom_spam_filter_operations($op, $cid) { - $filter = db_fetch_object(db_query('SELECT cid, status, filter FROM {spam_custom} WHERE cid = %d', $cid)); - switch ($op) { - case 'delete': - if ($filter->cid) { - db_query('DELETE FROM {spam_custom} WHERE cid = %d', $cid); - drupal_set_message(t('Deleted custom filter %filter.', array('%filter' => $filter->filter))); - } - break; - case 'disable': - if ($filter->cid && $filter->status != SPAM_CUSTOM_STATUS_DISABLED) { - db_query('UPDATE {spam_custom} SET status = %d WHERE cid = %d', SPAM_CUSTOM_STATUS_DISABLED, $cid); - drupal_set_message(t('Disabled custom filter %filter.', array('%filter' => $filter->filter))); - } - break; - } -} - -/** - * Apply enabled custom filter rules against content. - */ -function custom_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - $probably = $probably_not = 0; - $id = spam_invoke_module($type, 'content_id', $content, $extra); - $result = db_query('SELECT cid, filter, style, status, scan, action FROM {spam_custom} WHERE status <> %d ORDER BY weight ASC', SPAM_CUSTOM_STATUS_DISABLED); - while ($custom = db_fetch_object($result)) { - $scan = ''; - if ($custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { - // scan content - if (is_object($content)) { - $content = (array)$content; - } - $scan .= spam_get_text($content, $type, $fields, $extra); - spam_log(SPAM_DEBUG, 'custom_spam_filter', t('scanning content with %filter.', array('%filter' => $custom->filter)), $type, $id); - } - if ($custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { - // scan referrer - // TODO: Determine if this is a live scan. If not, don't scan referrer. - $scan .= $_SERVER['HTTP_REFERER']; - spam_log(SPAM_DEBUG, 'custom_spam_filter', t('scanning referrer with %filter.', array('%filter' => $custom->filter)), $type, $id); - } - if ($custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { - // scan user agent - // TODO: Determine if this is a live scan. If not, don't scan user agent. - $scan .= $_SERVER['HTTP_USER_AGENT']; - spam_log(SPAM_DEBUG, 'custom_spam_filter', t('scanning user agent with %filter.', array('%filter' => $custom->filter)), $type, $id); - } - switch ($custom->style) { - case SPAM_CUSTOM_STYLE_PLAIN: - $match = preg_match_all("/$custom->filter/", $scan, $matches); - break; - case SPAM_CUSTOM_STYLE_REGEX: - $match = preg_match_all($custom->filter, $scan, $matches); - break; - } - if ($match) { - // Record that we've had one or more matches. - db_query('UPDATE {spam_custom} SET matches = matches + %d, last = %d WHERE cid = %d', $match, time(), $custom->cid); - spam_log(SPAM_VERBOSE, 'custom_spam_filter', t('matched with %filter.', array('%filter' => $custom->filter)), $type, $id); - - $action['custom'][] = array( - 'filter' => $custom->filter, - 'status' => $custom->status, - 'style' => $custom->style, - 'scan' => $custom->scan, - 'extra' => $custom->extra, - ); - - switch ($custom->status) { - - case SPAM_CUSTOM_STATUS_SPAM: - spam_log(SPAM_VERBOSE, 'custom_spam_filter', t('content is spam.'), $type, $id); - // no need to scan any more, we've found spam - $action['total'] = 99; - return $action; - - case SPAM_CUSTOM_STATUS_NOTSPAM: - spam_log(SPAM_VERBOSE, 'custom_spam_filter', t('content is not spam.'), $type, $id); - // no need to scan any more, we've found non-spam - $action['total'] = 1; - return $action; - - case SPAM_CUSTOM_STATUS_PROBABLYNOT: - spam_log(SPAM_DEBUG, 'custom_spam_filter', t('content is probably not spam.'), $type, $id); - // maintain internal counter that this is probably not spam - $probably_not += $match; - break; - - case SPAM_CUSTOM_STATUS_PROBABLY: - spam_log(SPAM_DEBUG, 'custom_spam_filter', t('content is probably spam.'), $type, $id); - // maintain internal counter that this is probably spam - $probably += $match; - break; - } - } - } - - if ($probably && $probably_not) { - if ($probably >= $probably_not) { - $probably -= $probably_not; - $probably_not = 0; - } - else { - $probably_not -= $probably; - $probably = 0; - } - } - if ($probably) { - spam_log(SPAM_VERBOSE, 'custom_spam_filter', t('matched adjusted total of !number probably spam rule(s).', array('!number' => $probably)), $type, $id); - if ($probably >= variable_get('spam_custom_probably', 3)) { - $action['total'] = 99; - } - else { - $action['total'] = variable_get('spam_custom_probably_value', variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)); - } - } - else if ($probably_not) { - spam_log(SPAM_VERBOSE, 'custom_spam_filter', t('matched adjusted total of !number probably-not spam rule(s).', array('!number' => $probably_not)), $type, $id); - if ($probably_not >= variable_get('spam_custom_probablynot', 3)) { - $action['total'] = 1; - } - else { - $action['total'] = variable_get('spam_custom_probablynot_value', 40); - } - } - else { - // No matched filters, so don't change the overall spam score. - $action['total'] = 0; - } - return $action; -} diff -uprN /home/files/coding/drupal/spam/filters/custom/custom-upgrade.inc sites/all/modules/spam/filters/custom/custom-upgrade.inc --- /home/files/coding/drupal/spam/filters/custom/custom-upgrade.inc 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/custom/custom-upgrade.inc 1970-01-01 10:00:00.000000000 +1000 @@ -1,72 +0,0 @@ -style) { - case SPAM_CUSTOM_PLAIN: - case SPAM_CUSTOM_URL: - $style = SPAM_CUSTOM_STYLE_PLAIN; - break; - case SPAM_CUSTOM_REGEX: - $style = SPAM_CUSTOM_STYLE_REGEX; - break; - } - switch ($old->effect) { - case SPAM_CUSTOM_DISABLED: - default: - $status = SPAM_CUSTOM_STATUS_DISABLED; - break; - case SPAM_CUSTOM_MAYBE_SPAM: - case SPAM_CUSTOM_USUALLY_SPAM: - $status = SPAM_CUSTOM_STATUS_PROBABLY; - break; - case SPAM_CUSTOM_USUALLY_NOTSPAM: - case SPAM_CUSTOM_MAYBE_NOTSPAM: - $status = SPAM_CUSTOM_STATUS_PROBABLYNOT; - break; - case SPAM_CUSTOM_NEVER_SPAM: - $status = SPAM_CUSTOM_STATUS_NOTSPAM; - break; - case SPAM_CUSTOM_ALWAYS_SPAM: - $status = SPAM_CUSTOM_STATUS_SPAM; - break; - } - if ($old->action & SPAM_CUSTOM_ACTION_HEADER || $old->action & SPAM_CUSTOM_ACTION_BODY) { - $scan = SPAM_CUSTOM_SCAN_CONTENT; - } - else { - $scan = SPAM_CUSTOM_SCAN_CONTENT; - $status = SPAM_CUSTOM_STATUS_DISABLED; - } - db_query("INSERT INTO {spam_custom} (filter, style, status, scan, matches, last) VALUES('%s', %d, %d, %d, %d, %d)", $old->filter, $style, $status, $scan, $old->matches, $old->last); - } - // Done with upgrade, drop old table. - $ret = array(); - db_drop_table($ret, 'old_spam_custom'); - } -} - diff -uprN /home/files/coding/drupal/spam/filters/duplicate/duplicate.info sites/all/modules/spam/filters/duplicate/duplicate.info --- /home/files/coding/drupal/spam/filters/duplicate/duplicate.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/duplicate/duplicate.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,12 +0,0 @@ -; $Id: duplicate.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ -name = Duplicate filter -description = A duplication detecting spam filter. -package = Spam -dependencies[] = spam -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/duplicate/duplicate.install sites/all/modules/spam/filters/duplicate/duplicate.install --- /home/files/coding/drupal/spam/filters/duplicate/duplicate.install 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/duplicate/duplicate.install 1970-01-01 10:00:00.000000000 +1000 @@ -1,45 +0,0 @@ - t('The base table for the Duplicate submodule'), - 'fields' => array( - 'iid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), - 'sid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), - 'content_hash' => array('type' => 'char', 'length' => '32', 'not null' => TRUE, 'default' => ''), - 'hostname' => array('type' => 'varchar', 'length' => '15', 'not null' => TRUE, 'default' => ''), - 'duplicate_hash' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), - 'duplicate_ip' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), - 'spam' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), - 'expired' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), - 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), - ), - 'primary key' => array('iid'), - 'indexes' => array( - 'content_hash' => array('content_hash'), - 'hostname' => array('hostname'), - 'sid' => array('sid'), - 'spam' => array('spam'), - 'timestamp' => array('timestamp'), - ), - ); - - return $schema; -} - -function duplicate_install() { - // Create my tables. - drupal_install_schema('duplicate'); -} - -function duplicate_uninstall() { - // Remove tables. - drupal_uninstall_schema('duplicate'); - drupal_set_message('The spam_duplicate table has been dropped.'); -} diff -uprN /home/files/coding/drupal/spam/filters/duplicate/duplicate.module sites/all/modules/spam/filters/duplicate/duplicate.module --- /home/files/coding/drupal/spam/filters/duplicate/duplicate.module 2009-06-18 05:32:47.000000000 +1000 +++ sites/all/modules/spam/filters/duplicate/duplicate.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,400 +0,0 @@ -. All rights reserved. - * - * Detects spam by looking for duplication of content, or posting IP. - */ - -define('DUPLICATE_BLACKLIST_SILENT', 0); -define('DUPLICATE_BLACKLIST_NOTIFY', 1); -define('DUPLICATE_BLACKLIST_BLOCK', 2); - -define('DUPLICATE_DEFAULT_THRESHOLD', 2); -define('DUPLICATE_DEFAULT_BLACKLIST', 3); - -define('DUPLICATE_NOT_SPAM', 0); -define('DUPLICATE_SPAM', 1); - -/** - * Drupal _menu() hook. - */ -function duplicate_menu() { - $items = array(); - - $items['admin/settings/spam/filters/duplicate'] = array( - 'title' => 'Duplicate', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('duplicate_admin_settings'), - 'access arguments' => array('administer spam'), - 'description' => 'Configure the spam duplicate filter.', - 'type' => MENU_LOCAL_TASK, - ); - $items['duplicate/denied/ip'] = array( - 'page callback' => 'duplicate_denied_ip', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - $items['duplicate/denied/post'] = array( - 'page callback' => 'duplicate_denied_post', - 'type' => MENU_CALLBACK, - 'access callback' => TRUE, - ); - - $items['admin/reports/spam/blocked_ip'] = array( - 'title' => 'Blocked IPs', - 'access arguments' => array('administer spam'), - 'page callback' => 'spam_logs_blocked_ip', - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -/** - * The arg() function may not be availble early in the bootstrap process, - * so we reimplement it here. - */ -function _duplicate_arg() { - static $arguments, $q; - - if (empty($arguments) || $q != $_GET['q']) { - $arguments = explode('/', $_GET['q']); - $q = $_GET['q']; - } - - if (isset($arguments[$index])) { - return $arguments[$index]; - } -} - - -/** - * If IP blacklisting and IP blocking are both enabled, perform a database - * query on each page load to see if the current visitor has been blacklisted. - */ -function duplicate_init() { - // Allow notification to blacklisted IP, if enabled. - if (_duplicate_arg(0) == 'duplicate' && _duplicate_arg(1) == 'denied' && _duplicate_arg(2) == 'ip') return; - - // Only perform database queries if functionality is enabled. - if ((variable_get('duplicate_blacklist_action', DUPLICATE_BLACKLIST_NOTIFY) == DUPLICATE_BLACKLIST_BLOCK) && (variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST) > -1)) { - // Blacklisting and IP blocking enabled. - $duplicate_ip = (int)db_query("SELECT COUNT(iid) FROM {spam_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), DUPLICATE_SPAM); - if ($duplicate_ip >= variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST)) { - if (user_access('bypass filters')) { - spam_log(SPAM_DEBUG, 'duplicate_init', t('Found !count spam for IP !ip, ignoring because user !user (uid !uid) is configured to bypass filters', array('!count' => $duplicate_ip, '!ip' => ip_address(), '!user' => $user->name, 'uid' => $user->uid)), $type, $id); - return; - } - drupal_goto("duplicate/denied/ip"); - } - } -} - -/** - * Spam API Hook - */ -function duplicate_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - - switch ($op) { - - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'duplicate', $type, $content, $fields, $extra)) return; - return duplicate_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'duplicate'; - - case 'insert': - if (!module_invoke('spam', 'filter_enabled', 'duplicate', $type, $content, $fields, $extra)) return; - if (is_array($extra) && $extra['sid'] && $extra['host'] && - !empty($content) && !empty($fields)) { - $hash = _duplicate_content_hash($content, $fields); - db_query("INSERT INTO {spam_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); - $action = _duplicate_action(); - if (is_array($action) && !empty($action)) { - if (isset($action['redirect'])) { - drupal_goto($action['redirect']); - } - } - } - break; - - case 'update': - if (!module_invoke('spam', 'filter_enabled', 'duplicate', $type, $content, $fields, $extra)) return; - if (is_array($extra) && $extra['sid'] && $extra['host'] && - !empty($content) && !empty($fields)) { - $hash = _duplicate_content_hash($content, $fields); - db_query("UPDATE {spam_duplicate} SET content_hash = '%s', hostname = '%s', timestamp = %d WHERE sid = %d", $hash, $extra['host'], time(), $extra['sid']); - if (!db_affected_rows()) { - db_query("INSERT INTO {spam_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); - } - $action = _duplicate_action(); - if (is_array($action) && !empty($action)) { - if (isset($action['redirect'])) { - drupal_goto($action['redirect']); - } - } - } - break; - - case 'delete': - if (is_array($extra) && $extra['sid'] && !empty($content) && !empty($fields)) { - db_query("DELETE FROM {spam_duplicate} WHERE sid = %d", $extra['sid']); - } - break; - - case 'filter_info': - return array( - 'name' => t('Duplicate filter'), - 'module' => t('duplicate'), - 'description' => t('A duplication spam filter.'), - 'help' => t('The duplicate filter detects spam by detecting content duplication.'), - ); - break; - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - 'weight' => -8, - ); - - case 'mark_as_spam': - if (!module_invoke('spam', 'filter_enabled', 'duplicate', $type, $content, $fields, $extra)) return; - db_query('UPDATE {spam_duplicate} SET spam = %d WHERE sid = %d', DUPLICATE_SPAM, $extra['sid']); - if (!db_affected_rows() && $extra['id'] && $extra['sid']) { - $content = spam_invoke_module($type, 'load', $extra['id']); - $fields = spam_invoke_module($type, 'filter_fields', $content); - $hash = _duplicate_content_hash($content, $fields); - $hostname = spam_invoke_module($type, 'hostname', $extra['id']); - db_query("INSERT INTO {spam_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); - } - $action = _duplicate_action(); - if (is_array($action) && isset($action['redirect'])) { - return $action['redirect']; - } - break; - - case 'mark_as_not_spam': - if (!module_invoke('spam', 'filter_enabled', 'duplicate', $type, $content, $fields, $extra)) return; - db_query('UPDATE {spam_duplicate} SET spam = %d WHERE sid = %d', DUPLICATE_NOT_SPAM, $extra['sid']); - if (!db_affected_rows() && $extra['id'] && $extra['sid']) { - // Updating content that we've not filtered before. Retrive all the - // data we need to add it to the spam_duplicate table. - $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); - $hash = _duplicate_content_hash($extra['content'], $fields); - $hostname = spam_invoke_module($type, 'hostname', $extra['id']); - db_query("INSERT INTO {spam_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); - } - break; - - } -} - -/** - * - */ -function duplicate_admin_settings() { - $form['content'] = array( - '#type' => 'fieldset', - '#title' => t('Content'), - '#collapsible' => TRUE, - ); - $limits = drupal_map_assoc(range(2, 15)); - $limits[-1] = t('unlimited'); - $form['content']['duplicate_threshold'] = array( - '#type' => 'select', - '#title' => t('Duplication threshold'), - '#default_value' => variable_get('duplicate_threshold', DUPLICATE_DEFAULT_THRESHOLD), - '#options' => $limits, - '#description' => t('Specify how many times the same identical content can be posted before it will be considered spam. When tuning this filter, note that users may accidentally submit the same content multiple times causing an otherwise acceptible posting to be duplicated.'), - ); - $form['content']['duplicate_post_message'] = array( - '#type' => 'textarea', - '#title' => t('Duplicate post message'), - '#default_value' => variable_get('duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), - '#description' => t('Message to show visitors when their content has been blocked because it was posted multiple times. The text "%IP" will be replaced by the visitors actual IP address.'), - ); - - $form['ip'] = array( - '#type' => 'fieldset', - '#title' => t('IP'), - '#collapsible' => TRUE, - ); - $limits = drupal_map_assoc(range(1, 15)); - $limits[-1] = t('unlimited'); - $form['ip']['duplicate_blacklist'] = array( - '#type' => 'select', - '#title' => t('IP blacklist threshold'), - '#default_value' => variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST), - '#options' => $limits, - '#description' => t('Specify how many times a given IP address is allowed to post possible spam content before the IP address is blacklisted and prevented from posting any additional content.'), - ); - $form['ip']['duplicate_blacklist_action'] = array( - '#type' => 'radios', - '#title' => t('IP blacklist action'), - '#options' => array(t('Silently prevent visitor from posting'), t('Notify blacklisted visitor when posting, prevent from posting'), t('Notify blacklisted visitor, prevent from visiting site')), - '#default_value' => variable_get('duplicate_blacklist_action', DUPLICATE_BLACKLIST_NOTIFY), - '#description' => t('Select an action from the above options. If notification is enabled, the user will be redirected to a custom page displaying the "Blacklisted IP message" defined below. If you only prevent users from posting, they will be able to view all site content. If you prevent a user from visiting your site, they will only ever see the "Blacklisted IP message".'), - ); - $form['ip']['duplicate_blacklist_message'] = array( - '#type' => 'textarea', - '#title' => t('Blacklisted IP message'), - '#default_value' => variable_get('duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), - '#description' => t('Message to show visitors when their IP has been blacklisted. The text "%IP" will be replaced by the visitors actual IP address.') - ); - - return system_settings_form($form); -} - -/** - * Save the configuration. - */ -function duplicate_admin_settings_submit($form, &$form_state) { -/* TODO The 'op' element in the form values is deprecated. - Each button can have #validate and #submit functions associated with it. - Thus, there should be one button that submits the form and which invokes - the normal form_id_validate and form_id_submit handlers. Any additional - buttons which need to invoke different validate or submit functionality - should have button-specific functions. */ - if ($form_state['values']['op'] == t('Reset to defaults')) { - variable_del('duplicate_threshold'); - variable_del('duplicate_post_message'); - variable_del('duplicate_blacklist'); - variable_del('duplicate_blacklist_action'); - variable_del('duplicate_blacklist_message'); - drupal_set_message('Configuration reset to defaults.'); - } - else { - variable_set('duplicate_threshold', $form_state['values']['duplicate_threshold']); - variable_set('duplicate_post_message', $form_state['values']['duplicate_post_message']); - variable_set('duplicate_blacklist', $form_state['values']['duplicate_blacklist']); - variable_set('duplicate_blacklist_action', $form_state['values']['duplicate_blacklist_action']); - variable_set('duplicate_blacklist_message', $form_state['values']['duplicate_blacklist_message']); - drupal_set_message('Configuration saved.'); - } -} - -/** - * Get and md5 hash of all content truncated together. - */ -function _duplicate_content_hash($content, $fields) { - if (is_object($content)) { - $content = (array)$content; - } - $hash = ''; - foreach ($fields['main'] as $field) { - $hash .= $content[$field]; - } - return md5($hash); -} - -/** - * Determine whether or not the content is spam. - */ -function duplicate_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - $score = 0; - $action = array(); - $hash = _duplicate_content_hash($content, $fields); - $id = spam_invoke_module($type, 'content_id', $content, $extra); - $duplicate_hash = db_result(db_query("SELECT COUNT(d.iid) FROM {spam_duplicate} d LEFT JOIN {spam_tracker} t ON d.sid = t.sid WHERE content_hash = '%s' AND content_id <> %d", $hash, $id)) + 1; - if ($duplicate_hash >= variable_get('duplicate_threshold', DUPLICATE_DEFAULT_THRESHOLD)) { - $sids = db_query("SELECT sid FROM {spam_duplicate} WHERE content_hash = '%s'", $hash); - if (!$filter_test) { - while ($sid = db_result($sids)) { - $unpublish = db_fetch_object(db_query('SELECT content_type, content_id, score FROM {spam_tracker} WHERE sid = %d', $sid)); - spam_mark_as_spam($unpublish->content_type, $unpublish->content_id, array('score' => 99)); - } - // Update counter tracking that we've blocked a duplicate posting of this - // content. (It will actually increment the counter on - // "duplicate_threshold" rows.) - db_query("UPDATE {spam_duplicate} SET duplicate_hash = duplicate_hash + 1 WHERE content_hash = '%s'", $hash); - } - $action['hash'] = array( - 'score' => 99, - 'description' => t('Content is identical to %count other existing posts.', array('%count' => variable_get('duplicate_threshold', DUPLICATE_DEFAULT_THRESHOLD))), - ); - $action['total'] = 99; - $action['redirect'] = 'duplicate/denied/post'; - _duplicate_action($action); - return $action; - } - - $duplicate_ip = db_result(db_query("SELECT COUNT(iid) FROM {spam_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), DUPLICATE_SPAM)); - if ($duplicate_ip >= variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST) && (variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST) > -1)) { - $action['ip'] = array( - 'score' => 99, - 'description' => t('Content was posted by the same IP address used to post %count other spam posts.', array('%count' => variable_get('duplicate_blacklist', DUPLICATE_DEFAULT_BLACKLIST))), - ); - $action['total'] = 99; - $action['redirect'] = 'duplicate/denied/ip'; - } - - return $action; -} - -function _duplicate_action($register = array()) { - static $action = array(); - - if (!empty($register)) { - $action = $register; - } - - return $action; -} - -/** - * - */ -function duplicate_denied_ip() { - $message = strtr(variable_get('duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); - spam_denied_page($message, t('Your IP address has been blocked by our spam filter.')); -} - -/** - * - */ -function duplicate_denied_post() { - $message = strtr(variable_get('duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); - spam_denied_page($message, t('You have attempted to post the same content multiple times.')); -} - -function spam_logs_blocked_ip() { - drupal_set_title(t('Spam Module Blocked IPs')); - - $header = array( - array('data' => t('IP Address'), 'field' => 'hostname'), - array('data' => t('Last Seen'), 'field' => 'timestamp', 'sort' => 'desc'), - array('data' => t('Counter'), 'field' => 'count'), - ); - - // This SQL is *nasty*, so if you think you can do better, please be my guest! - // This unfortunately has to be SQL because the pager module can't be told - // how many rows we've got (so we can't do our own processing in PHP and - // still get paging to work properly). - $sql = "SELECT * FROM (SELECT DISTINCT x.hostname, x.timestamp, COUNT(x.hostname) AS count FROM (SELECT timestamp, hostname FROM {spam_tracker} WHERE score > %d ORDER BY timestamp DESC) AS x GROUP BY x.hostname) AS y WHERE y.count >= %d"; - $arguments = array(variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD), variable_get('spam_blacklist_ip', DUPLICATE_DEFAULT_BLACKLIST)); - - $count_sql = preg_replace('/^SELECT \* /', 'SELECT count(hostname) ', $sql); - - $result = pager_query($sql . tablesort_sql($header), 50, 0, $count_sql, $arguments); - - while ($log = db_fetch_object($result)) { - $rows[] = array('data' => array( - $log->hostname, - format_date($log->timestamp, 'small'), - $log->count - ) - ); - } - - if (!$rows) { - $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); - } - - return theme('table', $header, $rows) . theme('pager', NULL, 50, 0); -} diff -uprN /home/files/coding/drupal/spam/filters/node_age/node_age.info sites/all/modules/spam/filters/node_age/node_age.info --- /home/files/coding/drupal/spam/filters/node_age/node_age.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/node_age/node_age.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,13 +0,0 @@ -; $Id: node_age.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ -name = Spam node age filter -description = A node-age comment filter plug-in for the spam module. -package = Spam -dependencies[] = spam -dependencies[] = comment -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/node_age/node_age.module sites/all/modules/spam/filters/node_age/node_age.module --- /home/files/coding/drupal/spam/filters/node_age/node_age.module 2009-08-07 03:55:31.000000000 +1000 +++ sites/all/modules/spam/filters/node_age/node_age.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,196 +0,0 @@ -. All rights reserved. - * - */ - -/** - * Drupal _menu() hook. - */ -function node_age_menu() { - $items = array(); - - $items['admin/settings/spam/filters/node-age'] = array( - 'title' => 'Node age', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('node_age_admin_settings'), - 'access arguments' => array('administer spam'), - 'description' => 'Configure the node age filter.', - 'type' => MENU_LOCAL_TASK, - ); - - return $items; -} - -function node_age_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - - switch ($op) { - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'node_age', $type, $content, $fields, $extra)) return; - return node_age_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'node_age'; - - case 'filter_info': - return array( - 'name' => t('Node age'), - 'module' => t('node_age'), - 'description' => t('A node-age comment spam filter.'), - 'help' => t('The node-age filter assigns a higher spam probability to comments made against older nodes.'), - ); - break; - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - 'gain' => 150, - 'weight' => -2, - ); - - } -} - -/** - * Determine if a comment is being posted against too old of a node. - */ -function node_age_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - if ($type == 'comment') { - $action = array(); - $id = spam_invoke_module($type, 'content_id', $content, $extra); - if (arg(0) == 'comment' && arg(1) == 'reply' && is_numeric(arg(2))) { - $nid = arg(2); - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('retrieved nid (@nid) from url', array('@nid' => $nid)), $type, $id); - } - else { - $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $id)); - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('retrieved nid (@nid) from database', array('@nid' => $nid)), $type, $id); - } - - if (!$nid) { - spam_log(SPAM_LOG, 'node_age_spam_filter', t('warning: nid not found for comment (@cid), skipping', array('@cid' => $id)), $type, $id); - $action['total'] = 0; - return $action; - } - - $node = spam_invoke_module('node', 'load', $nid); - if (is_object($node)) { - $timestamp_field = variable_get('node_age_filter_on', 'created'); - if ($node->$timestamp_field < (time() - variable_get('node_age_limit_long', 4838400))) { - $action['total'] = variable_get('node_age_weight_long', 99); - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('node (@nid) older than long limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); - } - else if ($node->$timestamp_field < (time() - variable_get('node_age_limit_short', 2419200))) { - $action['total'] = variable_get('node_age_weight_short', 85); - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('node (@nid) older than short limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); - } - else { - $action['total'] = 0; - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('node (@nid) is recent.', array('@nid' => $nid)), $type, $id); - } - } else { - spam_log(SPAM_LOG, 'node_age_spam_filter', t('warning: nid (@nid) does not map to node, skipping', array('@nid' => $nid)), $type, $id); - $action['total'] = 0; - } - } - else { - spam_log(SPAM_DEBUG, 'node_age_spam_filter', t('content type is not comment, skipping'), $type, $id); - $action['total'] = 0; - } - return $action; -} - -/** - * Module administrative configuration options. - */ -function node_age_admin_settings() { - $form = array(); - $form['short'] = array( - '#type' => 'fieldset', - '#title' => 'Old content', - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $limits = drupal_map_assoc(spam_range(604800, 14515200, 604800), 'format_interval'); - $weights = drupal_map_assoc(array(60, 65, 70, 75, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)); - $form['short']['node_age_limit_short'] = array( - '#type' => 'select', - '#title' => t('Old content is content not published within the past'), - '#options' => $limits, - '#required' => TRUE, - '#default_value' => variable_get('node_age_limit_short', 2419200), - ); - $form['short']['node_age_weight_short'] = array( - '#type' => 'select', - '#title' => t('Probability that comments posted to old content is spam'), - '#options' => $weights, - '#required' => TRUE, - '#description' => t('Probability that comments posted to old content are spam, as a percentage.'), - '#default_value' => variable_get('node_age_weight_short', 85), - ); - - $form['long'] = array( - '#type' => 'fieldset', - '#title' => 'Really old content', - '#collapsible' => TRUE, - '#collapsed' => TRUE, - ); - $form['long']['node_age_limit_long'] = array( - '#type' => 'select', - '#title' => t('Really old content is content not published within the past'), - '#options' => $limits, - '#required' => TRUE, - '#default_value' => variable_get('node_age_limit_long', 4838400), - ); - $form['long']['node_age_weight_long'] = array( - '#type' => 'select', - '#title' => t('Probability that comments posted to really old content is spam'), - '#options' => $weights, - '#required' => TRUE, - '#description' => t('Probability that comments posted to really old content are spam, as a percentage.'), - '#default_value' => variable_get('node_age_weight_long', 99), - ); - return system_settings_form($form); -} - -/** - * Validate the configuration. - */ -function node_age_admin_settings_validate($form, &$form_state) { - $limit_short = $form_state['values']['node_age_limit_short']; - $limit_long = $form_state['values']['node_age_limit_long']; - if ($limit_short >= $limit_long) { - form_set_error('node_age_limit_long', t('Really old content has to be older than old content.')); - } -} - -/** - * Save the configuration. - */ -function node_age_admin_settings_submit($form, &$form_state) { -/* TODO The 'op' element in the form values is deprecated. - Each button can have #validate and #submit functions associated with it. - Thus, there should be one button that submits the form and which invokes - the normal form_id_validate and form_id_submit handlers. Any additional - buttons which need to invoke different validate or submit functionality - should have button-specific functions. */ - if ($form_state['values']['op'] == t('Reset to defaults')) { - variable_del('node_age_limit_short'); - variable_del('node_age_weight_short'); - variable_del('node_age_limit_long'); - variable_del('node_age_weight_long'); - drupal_set_message('Configuration reset to defaults.'); - } - else { - variable_set('node_age_limit_short', $form_state['values']['node_age_limit_short']); - variable_set('node_age_weight_short', $form_state['values']['node_age_weight_short']); - variable_set('node_age_limit_long', $form_state['values']['node_age_limit_long']); - variable_set('node_age_weight_long', $form_state['values']['node_age_weight_long']); - drupal_set_message('Configuration saved.'); - } -} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.info sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.info --- /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.info 2009-12-02 19:13:25.000000000 +1100 @@ -0,0 +1,12 @@ +; $Id: bayesian.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam Bayesian filter +description = A bayesian filter. +package = Spam +dependencies[] = spam +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install --- /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,120 @@ +. All rights reserved. + * + * Provides a generic Bayesian filter for use with other modules. + * Defines hooks for use with the Spam API. + */ + +/** + * Implementation of hook_schema(). + */ +function spam_filter_bayesian_schema() { + $schema['spam_filter_bayesian_tokens'] = array( + 'description' => t('TODO'), + 'fields' => array( + 'tid' => array( + 'description' => t('TODO'), + 'type' => 'serial', + 'unsigned' => 1, + 'not null' => TRUE, + ), + 'class' => array( + 'description' => t('TODO'), + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'token' => array( + 'description' => t('TODO'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'yes_count' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'no_count' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'probability' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'last' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'yes_count' => array('yes_count'), + 'no_count' => array('no_count'), + 'probability' => array('probability'), + 'last' => array('last'), + ), + 'unique keys' => array( + 'token' => array('class', 'token'), + ), + 'primary key' => array('tid'), + ); + + return $schema; +} + +/** + * Install bayesian module schema. + */ +function spam_filter_bayesian_install() { + // Create tables. + drupal_install_schema('spam_filter_bayesian'); +} + +/** + * Completely uninstall the spam module. + */ +function spam_filter_bayesian_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_filter_bayesian'); + + $tables = array('spam_filter_bayesian_tokens'); + foreach ($tables as $table) { + } + drupal_set_message(t('All bayesian module configuration data and tables have been deleted.')); +} + +/** + * Fix variable namespacing. + */ +function spam_filter_bayesian_update_6101() { + $ret = array(); + $old_vars = array('bayesian_default_probability', 'bayesian_interesting_tokens', 'bayesian_minimum_token_length', 'bayesian_tokenizer'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_filter_'.$var, $var); + } + } + return $ret; + } + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install~ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install~ --- /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.install~ 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,120 @@ +. All rights reserved. + * + * Provides a generic Bayesian filter for use with other modules. + * Defines hooks for use with the Spam API. + */ + +/** + * Implementation of hook_schema(). + */ +function spam_bayesian_schema() { + $schema['spam_bayesian_tokens'] = array( + 'description' => t('TODO'), + 'fields' => array( + 'tid' => array( + 'description' => t('TODO'), + 'type' => 'serial', + 'unsigned' => 1, + 'not null' => TRUE, + ), + 'class' => array( + 'description' => t('TODO'), + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'token' => array( + 'description' => t('TODO'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'yes_count' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'no_count' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'probability' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + 'last' => array( + 'description' => t('TODO'), + 'type' => 'int', + 'unsigned' => 1, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'yes_count' => array('yes_count'), + 'no_count' => array('no_count'), + 'probability' => array('probability'), + 'last' => array('last'), + ), + 'unique keys' => array( + 'token' => array('class', 'token'), + ), + 'primary key' => array('tid'), + ); + + return $schema; +} + +/** + * Install bayesian module schema. + */ +function spam_bayesian_install() { + // Create tables. + drupal_install_schema('spam_bayesian'); +} + +/** + * Completely uninstall the spam module. + */ +function spam_bayesian_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_bayesian'); + + $tables = array('spam_bayesian_tokens'); + foreach ($tables as $table) { + } + drupal_set_message(t('All bayesian module configuration data and tables have been deleted.')); +} + +/** + * Fix variable namespacing. + */ +function spam_bayesian_update_6101() { + $ret = array(); + $old_vars = array('bayesian_default_probability', 'bayesian_interesting_tokens', 'bayesian_minimum_token_length', 'bayesian_tokenizer'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_'.$var, $var); + } + } + return $ret; + } + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module --- /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,189 @@ +. All rights reserved. + * + * Provides a generic Bayesian filter for use with other modules. + * Defines hooks for use with the Spam API. + */ + +/** + * Spam API Hook + */ +function spam_filter_bayesian_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_bayesian', $type, $content, $fields, $extra)) return; + return spam_filter_bayesian_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_bayesian'; + break; + + case 'filter_info': + return array( + 'name' => t('Bayesian filter'), + 'module' => t('spam_filter_bayesian'), + 'description' => t('A bayesian spam filter.'), + 'help' => t('The bayesian filter can learn to tell the difference between valid content spam content.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + ); + + case 'mark_as_spam': + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_bayesian', $type, $content, $fields, $extra)) return; + spam_log(SPAM_DEBUG, 'spam_filter_bayesian_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $tokenizer = variable_get('spam_filter_bayesian_tokenizer', 'spam_filter_bayesian_tokenize'); + $tokens = $tokenizer($extra['content'], $type, $fields, $extra); + spam_filter_bayesian_tokens_update('spam', $tokens, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); + break; + } +} + +/** + * Determine whether or not the content is spam. + */ +function spam_filter_bayesian_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $class = 'spam'; + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $tokenizer = variable_get('spam_filter_bayesian_tokenizer', 'spam_filter_bayesian_tokenize'); + $tokens = $tokenizer($content, $type, $fields, $extra); + if (is_array($tokens)) { + foreach ($tokens as $token) { + $p = db_fetch_object(db_query("SELECT probability FROM {spam_filter_bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); + if (!$p->probability) { + $p->probability = variable_get('spam_filter_bayesian_default_probability', 40); + } + $t["$token,$p->probability"] = abs($p->probability - 50); + } + } + else { + // No tokens, return default score. + $action['total'] = variable_get('spam_filter_bayesian_default_probability', 40); + return $action; + } + + /* Sort token array so those tokens with the largest "drift" come first. + * Drift is this distance from a median of 50%. + */ + asort($t); + + /* Take the n most "interesting" tokens from the top of the token array. + * The larger a token's drift, the more interesting it is. + */ + $keys = array_keys($t); + $max = variable_get('spam_filter_bayesian_interesting_tokens', 15); + $total = 0; + for ($i = 0; $i < $max; $i++) { + if ($pair = array_pop($keys)) { + $p = explode(',', $pair); + $total = $total + $p[1]; + $action['spam_filter_bayesian'][$i] = array( + 'token' => $p[0], + 'probability' => $p[1], + ); + spam_log(SPAM_DEBUG, 'spam_filter_bayesian_spam_filter', t('interesting token [@count] (@token) probability(@probability)', array('@token' => $p[0], '@probability' => $p[1], '@count' => $i + 1)), $type, $id); + } + else { + // we've looked at all the tokens + break; + } + } + + $probability = round($total / $i, 1); + spam_log(SPAM_VERBOSE, 'spam_filter_bayesian_spam_filter', t('total(@total) count(@count) probability(@probability)', array('@probability' => $probability, '@total' => $total, '@count' => $i)), $type, $id); + + $action['total'] = $probability; + return $action; +} + +/** + * Update token probabilities in database. + */ +function spam_filter_bayesian_tokens_update($class, $tokens, $yes, $type = NULL, $id = 0) { + if (!is_array($tokens) || empty($tokens)) return; + foreach ($tokens as $token) { + $old = db_fetch_object(db_query("SELECT probability, yes_count, no_count FROM {spam_filter_bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); + if ($old->probability) { + $total = $old->yes_count + $old->no_count + 1; + $probability = spam_sanitize_score(($old->yes_count + ($yes ? 1 : 0)) / $total * 100); + spam_log(SPAM_DEBUG, 'spam_filter_bayesian_tokens_update', t('update token(@token) class(@class) yes(@yes) no(@no) prob(@prob): added @new', array('@token' => $token, '@class' => $class, '@yes' => $old->yes_count + ($yes ? 1 : 0), '@no' => $old->no_count + ($yes ? 0 : 1), '@prob' => $probability, '@new' => $yes ? 'yes' : 'no')), $type, $id); + if ($yes) { + db_query("UPDATE {spam_filter_bayesian_tokens} SET yes_count = yes_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); + } + else { + db_query("UPDATE {spam_filter_bayesian_tokens} SET no_count = no_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); + } + } + else { + $probability = ($yes ? 99 : 1); + spam_log(SPAM_DEBUG, 'spam_filter_bayesian_tokens_update', t('insert token(@token) class(@class) probability(@probability)', array('@token' => $token, '@class' => $class, '@probability' => $probability)), $type, $id); + db_query("INSERT INTO {spam_filter_bayesian_tokens} (class, token, yes_count, no_count, probability, last) VALUES('%s', '%s', %d, %d, %d, %d)", $class, $token, ($yes ? 1 : 0), ($yes ? 0 : 1), $probability, time()); + } + } +} + +/** + * Split content into an array of tokens. + */ +function spam_filter_bayesian_tokenize($content, $type, $fields, $extra = array(), $tag = NULL) { + static $tokens = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($tokens["$type-$id-$tag"])) { + $string = spam_get_text($content, $type, $fields, $extra); + + $URI = "(http://|https://|ftp://|mailto:)"; + // strip out unwanted html/url noise + $sanitized = preg_replace("'(www\.)|()|(href=)|(target=)|(src=)'i", '', $string); + $sanitized = preg_replace($URI, '', $sanitized); + + // Strip out values that should not be considered part of tokens, so + // things like '{viagra}' and 'vi.agra' are counted as hits towards + // 'viagra' + $sanitized = preg_replace("/[()\{\}\[\]#.,]/", '', $sanitized); + + // Force all tokens to lowercase, again to aggregate tokens. This both + // lowers the total token number of rows in the spam_tokens table and + // increases the strength of individual tokens by linking them to + // capitalized versions. + $sanitized = drupal_strtolower($sanitized); + + // divide sanitized string into tokens + $delimiters = " \t\n\r-_<>'\"`/|*%^&+=~:;?"; + $tok = strtok($sanitized, $delimiters); + while ($tok !== FALSE) { + // Only inspect the token if over minimum length. + if (drupal_strlen($tok) >= variable_get('spam_filter_bayesian_minimum_token_length', 3)) { + // If the token is longer than 255 characters, truncate it. + $toks[] = htmlspecialchars(drupal_substr("$tag$tok", 0, 254)); + } + $tok = strtok($delimiters); + } + + // allow external module ability to extract additional tokens + $hook = spam_invoke_api('tokenize', $string, $tag); + if ($hook['tokens']) { + $toks = array_merge($toks, $hook['tokens']); + } + $tokens["$type-$id-$tag"] = $toks; + } + + return $tokens["$type-$id-$tag"]; +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module~ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_bayesian/spam_filter_bayesian.module~ 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,189 @@ +. All rights reserved. + * + * Provides a generic Bayesian filter for use with other modules. + * Defines hooks for use with the Spam API. + */ + +/** + * Spam API Hook + */ +function spam_bayesian_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_bayesian', $type, $content, $fields, $extra)) return; + return spam_bayesian_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_bayesian'; + break; + + case 'filter_info': + return array( + 'name' => t('Bayesian filter'), + 'module' => t('spam_bayesian'), + 'description' => t('A bayesian spam filter.'), + 'help' => t('The bayesian filter can learn to tell the difference between valid content spam content.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + ); + + case 'mark_as_spam': + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_bayesian', $type, $content, $fields, $extra)) return; + spam_log(SPAM_DEBUG, 'spam_bayesian_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $tokenizer = variable_get('spam_bayesian_tokenizer', 'spam_bayesian_tokenize'); + $tokens = $tokenizer($extra['content'], $type, $fields, $extra); + spam_bayesian_tokens_update('spam', $tokens, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); + break; + } +} + +/** + * Determine whether or not the content is spam. + */ +function spam_bayesian_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $class = 'spam'; + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $tokenizer = variable_get('spam_bayesian_tokenizer', 'spam_bayesian_tokenize'); + $tokens = $tokenizer($content, $type, $fields, $extra); + if (is_array($tokens)) { + foreach ($tokens as $token) { + $p = db_fetch_object(db_query("SELECT probability FROM {spam_bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); + if (!$p->probability) { + $p->probability = variable_get('spam_bayesian_default_probability', 40); + } + $t["$token,$p->probability"] = abs($p->probability - 50); + } + } + else { + // No tokens, return default score. + $action['total'] = variable_get('spam_bayesian_default_probability', 40); + return $action; + } + + /* Sort token array so those tokens with the largest "drift" come first. + * Drift is this distance from a median of 50%. + */ + asort($t); + + /* Take the n most "interesting" tokens from the top of the token array. + * The larger a token's drift, the more interesting it is. + */ + $keys = array_keys($t); + $max = variable_get('spam_bayesian_interesting_tokens', 15); + $total = 0; + for ($i = 0; $i < $max; $i++) { + if ($pair = array_pop($keys)) { + $p = explode(',', $pair); + $total = $total + $p[1]; + $action['spam_bayesian'][$i] = array( + 'token' => $p[0], + 'probability' => $p[1], + ); + spam_log(SPAM_DEBUG, 'spam_bayesian_spam_filter', t('interesting token [@count] (@token) probability(@probability)', array('@token' => $p[0], '@probability' => $p[1], '@count' => $i + 1)), $type, $id); + } + else { + // we've looked at all the tokens + break; + } + } + + $probability = round($total / $i, 1); + spam_log(SPAM_VERBOSE, 'spam_bayesian_spam_filter', t('total(@total) count(@count) probability(@probability)', array('@probability' => $probability, '@total' => $total, '@count' => $i)), $type, $id); + + $action['total'] = $probability; + return $action; +} + +/** + * Update token probabilities in database. + */ +function spam_bayesian_tokens_update($class, $tokens, $yes, $type = NULL, $id = 0) { + if (!is_array($tokens) || empty($tokens)) return; + foreach ($tokens as $token) { + $old = db_fetch_object(db_query("SELECT probability, yes_count, no_count FROM {spam_bayesian_tokens} WHERE class = '%s' AND token = '%s'", $class, $token)); + if ($old->probability) { + $total = $old->yes_count + $old->no_count + 1; + $probability = spam_sanitize_score(($old->yes_count + ($yes ? 1 : 0)) / $total * 100); + spam_log(SPAM_DEBUG, 'spam_bayesian_tokens_update', t('update token(@token) class(@class) yes(@yes) no(@no) prob(@prob): added @new', array('@token' => $token, '@class' => $class, '@yes' => $old->yes_count + ($yes ? 1 : 0), '@no' => $old->no_count + ($yes ? 0 : 1), '@prob' => $probability, '@new' => $yes ? 'yes' : 'no')), $type, $id); + if ($yes) { + db_query("UPDATE {spam_bayesian_tokens} SET yes_count = yes_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); + } + else { + db_query("UPDATE {spam_bayesian_tokens} SET no_count = no_count + 1, probability = %d, last = %d WHERE class = '%s' AND token = '%s'", $probability, time(), $class, $token); + } + } + else { + $probability = ($yes ? 99 : 1); + spam_log(SPAM_DEBUG, 'spam_bayesian_tokens_update', t('insert token(@token) class(@class) probability(@probability)', array('@token' => $token, '@class' => $class, '@probability' => $probability)), $type, $id); + db_query("INSERT INTO {spam_bayesian_tokens} (class, token, yes_count, no_count, probability, last) VALUES('%s', '%s', %d, %d, %d, %d)", $class, $token, ($yes ? 1 : 0), ($yes ? 0 : 1), $probability, time()); + } + } +} + +/** + * Split content into an array of tokens. + */ +function spam_bayesian_tokenize($content, $type, $fields, $extra = array(), $tag = NULL) { + static $tokens = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($tokens["$type-$id-$tag"])) { + $string = spam_get_text($content, $type, $fields, $extra); + + $URI = "(http://|https://|ftp://|mailto:)"; + // strip out unwanted html/url noise + $sanitized = preg_replace("'(www\.)|()|(href=)|(target=)|(src=)'i", '', $string); + $sanitized = preg_replace($URI, '', $sanitized); + + // Strip out values that should not be considered part of tokens, so + // things like '{viagra}' and 'vi.agra' are counted as hits towards + // 'viagra' + $sanitized = preg_replace("/[()\{\}\[\]#.,]/", '', $sanitized); + + // Force all tokens to lowercase, again to aggregate tokens. This both + // lowers the total token number of rows in the spam_tokens table and + // increases the strength of individual tokens by linking them to + // capitalized versions. + $sanitized = drupal_strtolower($sanitized); + + // divide sanitized string into tokens + $delimiters = " \t\n\r-_<>'\"`/|*%^&+=~:;?"; + $tok = strtok($sanitized, $delimiters); + while ($tok !== FALSE) { + // Only inspect the token if over minimum length. + if (drupal_strlen($tok) >= variable_get('spam_bayesian_minimum_token_length', 3)) { + // If the token is longer than 255 characters, truncate it. + $toks[] = htmlspecialchars(drupal_substr("$tag$tok", 0, 254)); + } + $tok = strtok($delimiters); + } + + // allow external module ability to extract additional tokens + $hook = spam_invoke_api('tokenize', $string, $tag); + if ($hook['tokens']) { + $toks = array_merge($toks, $hook['tokens']); + } + $tokens["$type-$id-$tag"] = $toks; + } + + return $tokens["$type-$id-$tag"]; +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.info sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.info --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.info 2009-12-02 19:13:25.000000000 +1100 @@ -0,0 +1,12 @@ +; $Id: custom.info,v 1.1.2.2.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam Custom filter +description = Allows the creation of custom spam filter rules. +package = Spam +dependencies[] = spam +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.install sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.install --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.install 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.install 2009-12-09 14:37:34.000000000 +1100 @@ -0,0 +1,60 @@ + array( + 'cid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), + 'filter' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => ''), + 'style' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'status' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '2'), + 'scan' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'action' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'matches' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + 'last' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + 'weight' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + ), + 'primary key' => array('cid'), + 'indexes' => array( + 'filter' => array('filter'), + 'last' => array('last'), + 'matches' => array('matches'), + 'weight' => array('weight'), + ), + ); + + return $schema; +} + +function spam_filter_custom_install() { + // Create my tables. + drupal_install_schema('spam_filter_custom'); +} + +function spam_filter_custom_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_filter_custom'); + drupal_set_message('The spam_filter_custom table has been dropped.'); +} + +/** + * Fix variable namespacing. + */ +function spam_filter_custom_update_6101() { + $ret = array(); + $old_vars = array('custom_probably', 'custom_probablynot', 'custom_probablynot_value', 'custom_probably_value'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_filter_'.$var, $var); + } + } + return $ret; + } + + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.install~ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.install~ --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.install~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.install~ 2009-12-09 14:37:34.000000000 +1100 @@ -0,0 +1,60 @@ + array( + 'cid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), + 'filter' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => ''), + 'style' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'status' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '2'), + 'scan' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'action' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + 'matches' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + 'last' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + 'weight' => array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'disp-width' => '3'), + ), + 'primary key' => array('cid'), + 'indexes' => array( + 'filter' => array('filter'), + 'last' => array('last'), + 'matches' => array('matches'), + 'weight' => array('weight'), + ), + ); + + return $schema; +} + +function spam_filter_custom_install() { + // Create my tables. + drupal_install_schema('spam_filter_custom'); +} + +function spam_filter_custom_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_filter_custom'); + drupal_set_message('The spam_filter_custom table has been dropped.'); +} + +/** + * Fix variable namespacing. + */ +function spam_filter_custom_update_6101() { + $ret = array(); + $old_vars = array('custom_probably', 'custom_probablynot', 'custom_probablynot_value', 'custom_probably_value'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_filter_'.$var, $var); + } + } + return $ret; + } + + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.module sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.module --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.module 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.module 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,584 @@ +. + * + * Allows manual definition of words and regular expressions to detect spam + * content. + */ + +define(SPAM_FILTER_CUSTOM_STYLE_PLAIN, 0); +define(SPAM_FILTER_CUSTOM_STYLE_REGEX, 1); + +define(SPAM_FILTER_CUSTOM_STATUS_NOTSPAM, -2); +define(SPAM_FILTER_CUSTOM_STATUS_PROBABLYNOT, -1); +define(SPAM_FILTER_CUSTOM_STATUS_DISABLED, 0); +define(SPAM_FILTER_CUSTOM_STATUS_PROBABLY, 1); +define(SPAM_FILTER_CUSTOM_STATUS_SPAM, 2); + +define(SPAM_FILTER_CUSTOM_SCAN_CONTENT, 0x1); +define(SPAM_FILTER_CUSTOM_SCAN_REFERRER, 0x4); +define(SPAM_FILTER_CUSTOM_SCAN_USERAGENT, 0x8); + +// TODO: support actions +//define(SPAM_FILTER_CUSTOM_ACTION_DELETE, 0x1); +//define(SPAM_FILTER_CUSTOM_ACTION_MAIL, 0x2); + +/** + * Spam API Hook + */ +function spam_filter_custom_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_custom', $type, $content, $fields, $extra)) return; + return spam_filter_custom_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_custom'; + + case 'filter_info': + return array( + 'name' => t('Custom filter'), + 'module' => t('spam_filter_custom'), + 'description' => t('Custom spam filters.'), + 'help' => t('The custom spam filter module allows you to manually define custom spam filter rules.'), + ); + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -4, + ); + } +} + +/** + * Drupal _menu() hook. + */ +function spam_filter_custom_menu() { + $items = array(); + + $items['admin/settings/spam/filters/custom'] = array( + 'title' => 'Custom', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_custom_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the custom spam filter module.', + 'type' => MENU_LOCAL_TASK, + ); + + $items['admin/settings/spam/filters/custom/list'] = array( + 'title' => 'List', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_custom_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the custom spam filter module.', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items['admin/settings/spam/filters/custom/create'] = array( + 'title' => 'Create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_custom_admin_filter'), + 'access arguments' => array('administer spam'), + 'description' => 'Create a custom spam filter.', + 'type' => MENU_LOCAL_TASK, + ); + $items["admin/settings/spam/filters/custom/%/edit"] = array( + 'title' => 'Create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_custom_admin_filter', 5), + 'access arguments' => array('administer spam'), + 'description' => 'Edit a custom spam filter.', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +/** + * Adminsitrative interface for configuring custom spam filter rules. + */ +function spam_filter_custom_admin_settings() { + $form = array(); + + $form['options'] = array( + '#type' => 'fieldset', + '#title' => t('Options'), + '#prefix' => '
', + '#suffix' => '
' + ); + $options = array(); + foreach (module_invoke_all('spam_filter_custom_operations') as $operation => $op) { + $options[$operation] = $op['label']; + } + $form['options']['operation'] = array( + '#type' => 'select', + '#options' => $options, + '#default_value' => 'scan' + ); + $form['options']['submit'] = array( + '#type' => 'submit', + '#value' => t('Execute') + ); + + $rows = array(); + $result = pager_query('SELECT * FROM {spam_filter_custom} ORDER BY weight ASC'); + while ($spam_filter_custom = db_fetch_object($result)) { + $all[$spam_filter_custom->cid] = ''; + + // The filter text. + $form['filter'][$spam_filter_custom->cid] = array('#value' => $spam_filter_custom->filter); + + // What style of filter. + if ($spam_filter_custom->style == SPAM_FILTER_CUSTOM_STYLE_PLAIN) { + $form['style'][$spam_filter_custom->cid] = array('#value' => t('Plain text')); + } + else if ($spam_filter_custom->style == SPAM_FILTER_CUSTOM_STYLE_REGEX) { + $form['style'][$spam_filter_custom->cid] = array('#value' => t('Regular expression')); + } + + // What to scan. + $scan = array(); + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_CONTENT) { + $scan[] = t('Content'); + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_REFERRER) { + $scan[] = t('Referrer'); + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_USERAGENT) { + $scan[] = t('User agent'); + } + $form['scan'][$spam_filter_custom->cid] = array('#value' => implode(', ', $scan)); + + // What status to apply. + switch ($spam_filter_custom->status) { + case SPAM_FILTER_CUSTOM_STATUS_NOTSPAM: + $status = t('Mark as not spam'); + break; + case SPAM_FILTER_CUSTOM_STATUS_PROBABLYNOT: + $status = t('Mark as probably not spam'); + break; + case SPAM_FILTER_CUSTOM_STATUS_DISABLED: + $status = t('Disabled'); + break; + case SPAM_FILTER_CUSTOM_STATUS_PROBABLY: + $status = t('Mark as probably spam'); + break; + case SPAM_FILTER_CUSTOM_STATUS_SPAM: + $status = t('Mark as spam'); + break; + default: + $status = t('Unknown'); + break; + } + $form['status'][$spam_filter_custom->cid] = array( + '#value' => $status + ); + + // How many times this filter has been matched. + $form['matches'][$spam_filter_custom->cid] = array('#value' => $spam_filter_custom->matches); + + // The last time this filter was matched. + $last = $spam_filter_custom->last ? t('@time ago', array('@time' => format_interval(time() - $spam_filter_custom->last))) : t('Never'); + $form['last'][$spam_filter_custom->cid] = array( + '#value' => $last + ); + + // Link to edit the filter. + $form['edit'][$spam_filter_custom->cid] = array( + '#value' => l(t('edit'), "admin/settings/spam/filters/custom/$spam_filter_custom->cid/edit"), + ); + + $rows[] = $row; + } + + $form['create2'] = array( + '#value' => '['. l(t('create custom filter'), 'admin/settings/spam/filters/custom/create') .']', + '#prefix' => '
', + '#suffix' => '
' + ); + + $form['spam_filter_custom'] = array( + '#type' => 'checkboxes', + '#options' => $all, + ); + $form['pager'] = array( + '#value' => theme('pager', NULL, 50, 0), + ); + + return $form; +} + +/** + * Drupal _theme() hook. + */ +function spam_filter_custom_theme() { + return array( + 'spam_filter_custom_admin_settings' => array( + 'file' => 'spam_filter_custom.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + ); +} + +/** + * Format the custom filter admin page. + */ +function theme_spam_filter_custom_admin_settings($form) { + // TODO: use spam_filter_custom_upgrade() from spam_filter_custom_upgrade.inc in spam_filter_custom.install + //_spam_filter_custom_upgrade(); + $header = array(theme('table_select_header_cell'), t('Filter'), t('Style'), t('Scan'), t('Status'), t('Matches'), t('Last'), ''); + + $output = drupal_render($form['options']); + $rows = array(); + if (isset($form['filter']) && is_array($form['filter'])) { + foreach (element_children($form['filter']) as $key) { + $row = array(); + $row[] = drupal_render($form['spam_filter_custom'][$key]); + $row[] = drupal_render($form['filter'][$key]); + $row[] = drupal_render($form['style'][$key]); + $row[] = drupal_render($form['scan'][$key]); + $row[] = drupal_render($form['status'][$key]); + $row[] = drupal_render($form['matches'][$key]); + $row[] = drupal_render($form['last'][$key]); + $row[] = drupal_render($form['edit'][$key]); + $rows[] = $row; + } + + $output .= theme('table', $header, $rows); + if ($form['pager']['#value']) { + $output .= drupal_render($form['pager']); + } + } + else { + $output .= theme('table', $header, $rows); + $output .= ''. t('No custom filters created.') .''; + } + + $output .= drupal_render($form); + + return $output; +} + +/** + * Define callbacks for custom filter options. Unlike in main spam module, + * here a single ID is added to the end of the argument listing by the + * only function that calls this (spam_filter_custom_admin_settings_submit). + */ +function spam_filter_custom_spam_filter_custom_operations() { + $operations = array( + 'disable' => array( + 'label' => t('Disable'), + 'callback' => 'spam_filter_custom_spam_filter_operations', + 'callback arguments' => array('disable'), + ), + 'delete' => array( + 'label' => t('Delete'), + 'callback' => 'spam_filter_custom_spam_filter_operations', + 'callback arguments' => array('delete'), + ), + ); + return $operations; +} + +/** + * Create or edit a custom spam filter. + */ +function spam_filter_custom_admin_filter($form_state, $cid = NULL) { + if ($cid) { + drupal_set_title('Edit'); + $spam_filter_custom = db_fetch_object(db_query('SELECT * FROM {spam_filter_custom} WHERE cid = %d', $cid)); + if (!isset($spam_filter_custom->cid)) { + drupal_set_message(t('Failed to load custom filter.'), 'error'); + drupal_goto('admin/settings/spam/filters/custom'); + } + } + else { + drupal_set_title('Create'); + } + $form = array(); + $form['filter'] = array( + '#type' => 'textfield', + '#title' => t('Filter'), + '#description' => t('Enter a custom filter string. You can enter a word, a phrase, or a regular expression.'), + '#default_value' => $spam_filter_custom->cid ? $spam_filter_custom->filter : '', + '#required' => TRUE, + ); + $form['style'] = array( + '#type' => 'radios', + '#title' => t('Filter type'), + '#description' => t('For a custom filter to match exactly what you type, select plain text. If you would like to define a regular expression, your filter must be formatted as a Perl-compatible regular expression.'), + '#options' => array(SPAM_FILTER_CUSTOM_STYLE_PLAIN => t('Plain text'), SPAM_FILTER_CUSTOM_STYLE_REGEX => t('Regular expression')), + '#default_value' => $spam_filter_custom->cid ? $spam_filter_custom->style : SPAM_FILTER_CUSTOM_STYLE_PLAIN, + '#required' => TRUE, + ); + $options = array(SPAM_FILTER_CUSTOM_SCAN_CONTENT => ('Content'), SPAM_FILTER_CUSTOM_SCAN_REFERRER => t('Referrer'), SPAM_FILTER_CUSTOM_SCAN_USERAGENT => t('User agent')); + $scan = array(); + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_CONTENT) { + $scan[] = SPAM_FILTER_CUSTOM_SCAN_CONTENT; + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_REFERRER) { + $scan[] = SPAM_FILTER_CUSTOM_SCAN_REFERRER; + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_USERAGENT) { + $scan[] = SPAM_FILTER_CUSTOM_SCAN_USERAGENT; + } + $form['scan'] = array( + '#type' => 'checkboxes', + '#title' => t('Scan'), + '#description' => t('Specify where you\'d like to apply your custom filter.'), + '#options' => $options, + '#required' => TRUE, + '#default_value' => !empty($scan) ? $scan : array(SPAM_FILTER_CUSTOM_SCAN_CONTENT), + ); + $options = array(); + $form['status'] = array( + '#type' => 'radios', + '#title' => t('Status'), + '#description' => t('Select the status to apply when your custom filter matches site content. Filters are tested in the order they are displayed above, thus if content matches a filter that says to mark it as spam, and another to mark it as not spam, the first to match will be the actual status applied.'), + '#options' => array( + SPAM_FILTER_CUSTOM_STATUS_DISABLED => t('Disabled'), + SPAM_FILTER_CUSTOM_STATUS_SPAM => t('Mark as spam'), + SPAM_FILTER_CUSTOM_STATUS_PROBABLY => t('Mark as probably spam'), + SPAM_FILTER_CUSTOM_STATUS_PROBABLYNOT => t('Mark as probably not spam'), + SPAM_FILTER_CUSTOM_STATUS_NOTSPAM => t('Mark as not spam')), + '#default_value' => $spam_filter_custom->cid ? $spam_filter_custom->status : SPAM_FILTER_CUSTOM_STATUS_SPAM, + '#required' => TRUE, + ); + $form['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#description' => t('Give your custom filter a weight. "Lighter" filters with smaller weights will run before "heavier" filters with larger weights.'), + '#default_value' => $spam_filter_custom->weight, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => $spam_filter_custom->cid ? t('Update filter') : t('Create filter'), + ); + + if ($spam_filter_custom->cid) { + $form['cid'] = array( + '#type' => 'hidden', + '#value' => $spam_filter_custom->cid, + ); + } + + return $form; +} + +/** + * Be sure that the custom filter is valid. + */ +function spam_filter_custom_admin_filter_validate($form, &$form_state) { + if ($form_state['values']['style'] == SPAM_FILTER_CUSTOM_STYLE_REGEX) { + if (preg_match($form_state['values']['filter'], 'test') === FALSE) { + form_set_error('filter', t('Failed to validate your filter\'s regular expression. It must be properly formatted as a Perl-compatible regular expression. Review the above error for details on the specific problem with your expression.')); + } + } + if (isset($form_state['values']['cid'])) { + // update + $cid = db_result(db_query("SELECT cid FROM {spam_filter_custom} WHERE filter = '%s' AND cid <> %d", $form_state['values']['filter'], $form_state['values']['cid'])); + if ($cid) { + form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); + } + } + else { + // create + $cid = db_result(db_query("SELECT cid FROM {spam_filter_custom} WHERE filter = '%s'", $form_state['values']['filter'])); + if ($cid) { + form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); + } + } +} + +/** + * Create/update custom filer. + */ +function spam_filter_custom_admin_filter_submit($form, &$form_state) { + $scan = 0; + if (is_array($form_state['values']['scan'])) { + foreach ($form_state['values']['scan'] as $s) { + $scan += $s; + } + } + if (isset($form_state['values']['cid'])) { + db_query("UPDATE {spam_filter_custom} SET filter = '%s', style = %d, status = %d, scan = %d, weight = %d WHERE cid = %d", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight'], $form_state['values']['cid']); + drupal_set_message(t('Custom filter %filter updated.', array('%filter' => $form_state['values']['filter']))); + } + else { + db_query("INSERT INTO {spam_filter_custom} (filter, style, status, scan, weight) VALUES ('%s', %d, %d, %d, %d)", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight']); + drupal_set_message(t('Custom filter %filter created.', array('%filter' => $form_state['values']['filter']))); + } + drupal_goto('admin/settings/spam/filters/custom'); +} + +/** + * Perform bulk operations on the filters. + */ +function spam_filter_custom_admin_settings_submit($form, &$form_state) { + if (is_array($form_state['values']['spam_filter_custom'])) { + foreach ($form_state['values']['spam_filter_custom'] as $cid => $selected) { + if ($selected) { + $process[] = $cid; + } + } + } + if (!empty($process)) { + foreach (module_invoke_all('spam_filter_custom_operations') as $operation => $op) { + $options[$operation] = $op; + } + $operation = $form_state['values']['operation']; + if (isset($options[$operation])) { + $function = $options[$operation]['callback']; + $arguments = $options[$operation]['callback arguments']; + //TODO: Why is order different than spam.module, and why 1 at a time? + foreach ($process as $cid) { + call_user_func_array($function, array_merge($arguments, array($cid))); + } + } + } +} + +/** + * Perform custom operations. + * TODO: Confirmation would be nice. + */ +function spam_filter_custom_spam_filter_operations($op, $cid) { + $filter = db_fetch_object(db_query('SELECT cid, status, filter FROM {spam_filter_custom} WHERE cid = %d', $cid)); + switch ($op) { + case 'delete': + if ($filter->cid) { + db_query('DELETE FROM {spam_filter_custom} WHERE cid = %d', $cid); + drupal_set_message(t('Deleted custom filter %filter.', array('%filter' => $filter->filter))); + } + break; + case 'disable': + if ($filter->cid && $filter->status != SPAM_FILTER_CUSTOM_STATUS_DISABLED) { + db_query('UPDATE {spam_filter_custom} SET status = %d WHERE cid = %d', SPAM_FILTER_CUSTOM_STATUS_DISABLED, $cid); + drupal_set_message(t('Disabled custom filter %filter.', array('%filter' => $filter->filter))); + } + break; + } +} + +/** + * Apply enabled custom filter rules against content. + */ +function spam_filter_custom_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $probably = $probably_not = 0; + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $result = db_query('SELECT cid, filter, style, status, scan, action FROM {spam_filter_custom} WHERE status <> %d ORDER BY weight ASC', SPAM_FILTER_CUSTOM_STATUS_DISABLED); + while ($spam_filter_custom = db_fetch_object($result)) { + $scan = ''; + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_CONTENT) { + // scan content + if (is_object($content)) { + $content = (array)$content; + } + $scan .= spam_get_text($content, $type, $fields, $extra); + spam_log(SPAM_DEBUG, 'spam_filter_custom_spam_filter', t('scanning content with %filter.', array('%filter' => $spam_filter_custom->filter)), $type, $id); + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_REFERRER) { + // scan referrer + // TODO: Determine if this is a live scan. If not, don't scan referrer. + $scan .= $_SERVER['HTTP_REFERER']; + spam_log(SPAM_DEBUG, 'spam_filter_custom_spam_filter', t('scanning referrer with %filter.', array('%filter' => $spam_filter_custom->filter)), $type, $id); + } + if ($spam_filter_custom->scan & SPAM_FILTER_CUSTOM_SCAN_USERAGENT) { + // scan user agent + // TODO: Determine if this is a live scan. If not, don't scan user agent. + $scan .= $_SERVER['HTTP_USER_AGENT']; + spam_log(SPAM_DEBUG, 'spam_filter_custom_spam_filter', t('scanning user agent with %filter.', array('%filter' => $spam_filter_custom->filter)), $type, $id); + } + switch ($spam_filter_custom->style) { + case SPAM_FILTER_CUSTOM_STYLE_PLAIN: + $match = preg_match_all("/$spam_filter_custom->filter/", $scan, $matches); + break; + case SPAM_FILTER_CUSTOM_STYLE_REGEX: + $match = preg_match_all($spam_filter_custom->filter, $scan, $matches); + break; + } + if ($match) { + // Record that we've had one or more matches. + db_query('UPDATE {spam_filter_custom} SET matches = matches + %d, last = %d WHERE cid = %d', $match, time(), $spam_filter_custom->cid); + spam_log(SPAM_VERBOSE, 'spam_filter_custom_spam_filter', t('matched with %filter.', array('%filter' => $spam_filter_custom->filter)), $type, $id); + + $action['spam_filter_custom'][] = array( + 'filter' => $spam_filter_custom->filter, + 'status' => $spam_filter_custom->status, + 'style' => $spam_filter_custom->style, + 'scan' => $spam_filter_custom->scan, + 'extra' => $spam_filter_custom->extra, + ); + + switch ($spam_filter_custom->status) { + + case SPAM_FILTER_CUSTOM_STATUS_SPAM: + spam_log(SPAM_VERBOSE, 'spam_filter_custom_spam_filter', t('content is spam.'), $type, $id); + // no need to scan any more, we've found spam + $action['total'] = 99; + return $action; + + case SPAM_FILTER_CUSTOM_STATUS_NOTSPAM: + spam_log(SPAM_VERBOSE, 'spam_filter_custom_spam_filter', t('content is not spam.'), $type, $id); + // no need to scan any more, we've found non-spam + $action['total'] = 1; + return $action; + + case SPAM_FILTER_CUSTOM_STATUS_PROBABLYNOT: + spam_log(SPAM_DEBUG, 'spam_filter_custom_spam_filter', t('content is probably not spam.'), $type, $id); + // maintain internal counter that this is probably not spam + $probably_not += $match; + break; + + case SPAM_FILTER_CUSTOM_STATUS_PROBABLY: + spam_log(SPAM_DEBUG, 'spam_filter_custom_spam_filter', t('content is probably spam.'), $type, $id); + // maintain internal counter that this is probably spam + $probably += $match; + break; + } + } + } + + if ($probably && $probably_not) { + if ($probably >= $probably_not) { + $probably -= $probably_not; + $probably_not = 0; + } + else { + $probably_not -= $probably; + $probably = 0; + } + } + if ($probably) { + spam_log(SPAM_VERBOSE, 'spam_filter_custom_spam_filter', t('matched adjusted total of !number probably spam rule(s).', array('!number' => $probably)), $type, $id); + if ($probably >= variable_get('spam_filter_custom_probably', 3)) { + $action['total'] = 99; + } + else { + $action['total'] = variable_get('spam_filter_custom_probably_value', variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)); + } + } + else if ($probably_not) { + spam_log(SPAM_VERBOSE, 'spam_filter_custom_spam_filter', t('matched adjusted total of !number probably-not spam rule(s).', array('!number' => $probably_not)), $type, $id); + if ($probably_not >= variable_get('spam_filter_custom_probablynot', 3)) { + $action['total'] = 1; + } + else { + $action['total'] = variable_get('spam_filter_custom_probablynot_value', 40); + } + } + else { + // No matched filters, so don't change the overall spam score. + $action['total'] = 0; + } + return $action; +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.module~ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom.module~ 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,584 @@ +. + * + * Allows manual definition of words and regular expressions to detect spam + * content. + */ + +define(SPAM_CUSTOM_STYLE_PLAIN, 0); +define(SPAM_CUSTOM_STYLE_REGEX, 1); + +define(SPAM_CUSTOM_STATUS_NOTSPAM, -2); +define(SPAM_CUSTOM_STATUS_PROBABLYNOT, -1); +define(SPAM_CUSTOM_STATUS_DISABLED, 0); +define(SPAM_CUSTOM_STATUS_PROBABLY, 1); +define(SPAM_CUSTOM_STATUS_SPAM, 2); + +define(SPAM_CUSTOM_SCAN_CONTENT, 0x1); +define(SPAM_CUSTOM_SCAN_REFERRER, 0x4); +define(SPAM_CUSTOM_SCAN_USERAGENT, 0x8); + +// TODO: support actions +//define(SPAM_CUSTOM_ACTION_DELETE, 0x1); +//define(SPAM_CUSTOM_ACTION_MAIL, 0x2); + +/** + * Spam API Hook + */ +function spam_custom_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_custom', $type, $content, $fields, $extra)) return; + return spam_custom_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_custom'; + + case 'filter_info': + return array( + 'name' => t('Custom filter'), + 'module' => t('spam_custom'), + 'description' => t('Custom spam filters.'), + 'help' => t('The custom spam filter module allows you to manually define custom spam filter rules.'), + ); + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -4, + ); + } +} + +/** + * Drupal _menu() hook. + */ +function spam_custom_menu() { + $items = array(); + + $items['admin/settings/spam/filters/custom'] = array( + 'title' => 'Custom', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_custom_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the custom spam filter module.', + 'type' => MENU_LOCAL_TASK, + ); + + $items['admin/settings/spam/filters/custom/list'] = array( + 'title' => 'List', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_custom_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the custom spam filter module.', + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + $items['admin/settings/spam/filters/custom/create'] = array( + 'title' => 'Create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_custom_admin_filter'), + 'access arguments' => array('administer spam'), + 'description' => 'Create a custom spam filter.', + 'type' => MENU_LOCAL_TASK, + ); + $items["admin/settings/spam/filters/custom/%/edit"] = array( + 'title' => 'Create', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_custom_admin_filter', 5), + 'access arguments' => array('administer spam'), + 'description' => 'Edit a custom spam filter.', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +/** + * Adminsitrative interface for configuring custom spam filter rules. + */ +function spam_custom_admin_settings() { + $form = array(); + + $form['options'] = array( + '#type' => 'fieldset', + '#title' => t('Options'), + '#prefix' => '
', + '#suffix' => '
' + ); + $options = array(); + foreach (module_invoke_all('spam_custom_operations') as $operation => $op) { + $options[$operation] = $op['label']; + } + $form['options']['operation'] = array( + '#type' => 'select', + '#options' => $options, + '#default_value' => 'scan' + ); + $form['options']['submit'] = array( + '#type' => 'submit', + '#value' => t('Execute') + ); + + $rows = array(); + $result = pager_query('SELECT * FROM {spam_custom} ORDER BY weight ASC'); + while ($spam_custom = db_fetch_object($result)) { + $all[$spam_custom->cid] = ''; + + // The filter text. + $form['filter'][$spam_custom->cid] = array('#value' => $spam_custom->filter); + + // What style of filter. + if ($spam_custom->style == SPAM_CUSTOM_STYLE_PLAIN) { + $form['style'][$spam_custom->cid] = array('#value' => t('Plain text')); + } + else if ($spam_custom->style == SPAM_CUSTOM_STYLE_REGEX) { + $form['style'][$spam_custom->cid] = array('#value' => t('Regular expression')); + } + + // What to scan. + $scan = array(); + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { + $scan[] = t('Content'); + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { + $scan[] = t('Referrer'); + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { + $scan[] = t('User agent'); + } + $form['scan'][$spam_custom->cid] = array('#value' => implode(', ', $scan)); + + // What status to apply. + switch ($spam_custom->status) { + case SPAM_CUSTOM_STATUS_NOTSPAM: + $status = t('Mark as not spam'); + break; + case SPAM_CUSTOM_STATUS_PROBABLYNOT: + $status = t('Mark as probably not spam'); + break; + case SPAM_CUSTOM_STATUS_DISABLED: + $status = t('Disabled'); + break; + case SPAM_CUSTOM_STATUS_PROBABLY: + $status = t('Mark as probably spam'); + break; + case SPAM_CUSTOM_STATUS_SPAM: + $status = t('Mark as spam'); + break; + default: + $status = t('Unknown'); + break; + } + $form['status'][$spam_custom->cid] = array( + '#value' => $status + ); + + // How many times this filter has been matched. + $form['matches'][$spam_custom->cid] = array('#value' => $spam_custom->matches); + + // The last time this filter was matched. + $last = $spam_custom->last ? t('@time ago', array('@time' => format_interval(time() - $spam_custom->last))) : t('Never'); + $form['last'][$spam_custom->cid] = array( + '#value' => $last + ); + + // Link to edit the filter. + $form['edit'][$spam_custom->cid] = array( + '#value' => l(t('edit'), "admin/settings/spam/filters/custom/$spam_custom->cid/edit"), + ); + + $rows[] = $row; + } + + $form['create2'] = array( + '#value' => '['. l(t('create custom filter'), 'admin/settings/spam/filters/custom/create') .']', + '#prefix' => '
', + '#suffix' => '
' + ); + + $form['spam_custom'] = array( + '#type' => 'checkboxes', + '#options' => $all, + ); + $form['pager'] = array( + '#value' => theme('pager', NULL, 50, 0), + ); + + return $form; +} + +/** + * Drupal _theme() hook. + */ +function spam_custom_theme() { + return array( + 'spam_custom_admin_settings' => array( + 'file' => 'spam_custom.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + ); +} + +/** + * Format the custom filter admin page. + */ +function theme_spam_custom_admin_settings($form) { + // TODO: use spam_custom_upgrade() from spam_custom_upgrade.inc in spam_custom.install + //_spam_custom_upgrade(); + $header = array(theme('table_select_header_cell'), t('Filter'), t('Style'), t('Scan'), t('Status'), t('Matches'), t('Last'), ''); + + $output = drupal_render($form['options']); + $rows = array(); + if (isset($form['filter']) && is_array($form['filter'])) { + foreach (element_children($form['filter']) as $key) { + $row = array(); + $row[] = drupal_render($form['spam_custom'][$key]); + $row[] = drupal_render($form['filter'][$key]); + $row[] = drupal_render($form['style'][$key]); + $row[] = drupal_render($form['scan'][$key]); + $row[] = drupal_render($form['status'][$key]); + $row[] = drupal_render($form['matches'][$key]); + $row[] = drupal_render($form['last'][$key]); + $row[] = drupal_render($form['edit'][$key]); + $rows[] = $row; + } + + $output .= theme('table', $header, $rows); + if ($form['pager']['#value']) { + $output .= drupal_render($form['pager']); + } + } + else { + $output .= theme('table', $header, $rows); + $output .= ''. t('No custom filters created.') .''; + } + + $output .= drupal_render($form); + + return $output; +} + +/** + * Define callbacks for custom filter options. Unlike in main spam module, + * here a single ID is added to the end of the argument listing by the + * only function that calls this (spam_custom_admin_settings_submit). + */ +function spam_custom_spam_custom_operations() { + $operations = array( + 'disable' => array( + 'label' => t('Disable'), + 'callback' => 'spam_custom_spam_filter_operations', + 'callback arguments' => array('disable'), + ), + 'delete' => array( + 'label' => t('Delete'), + 'callback' => 'spam_custom_spam_filter_operations', + 'callback arguments' => array('delete'), + ), + ); + return $operations; +} + +/** + * Create or edit a custom spam filter. + */ +function spam_custom_admin_filter($form_state, $cid = NULL) { + if ($cid) { + drupal_set_title('Edit'); + $spam_custom = db_fetch_object(db_query('SELECT * FROM {spam_custom} WHERE cid = %d', $cid)); + if (!isset($spam_custom->cid)) { + drupal_set_message(t('Failed to load custom filter.'), 'error'); + drupal_goto('admin/settings/spam/filters/custom'); + } + } + else { + drupal_set_title('Create'); + } + $form = array(); + $form['filter'] = array( + '#type' => 'textfield', + '#title' => t('Filter'), + '#description' => t('Enter a custom filter string. You can enter a word, a phrase, or a regular expression.'), + '#default_value' => $spam_custom->cid ? $spam_custom->filter : '', + '#required' => TRUE, + ); + $form['style'] = array( + '#type' => 'radios', + '#title' => t('Filter type'), + '#description' => t('For a custom filter to match exactly what you type, select plain text. If you would like to define a regular expression, your filter must be formatted as a Perl-compatible regular expression.'), + '#options' => array(SPAM_CUSTOM_STYLE_PLAIN => t('Plain text'), SPAM_CUSTOM_STYLE_REGEX => t('Regular expression')), + '#default_value' => $spam_custom->cid ? $spam_custom->style : SPAM_CUSTOM_STYLE_PLAIN, + '#required' => TRUE, + ); + $options = array(SPAM_CUSTOM_SCAN_CONTENT => ('Content'), SPAM_CUSTOM_SCAN_REFERRER => t('Referrer'), SPAM_CUSTOM_SCAN_USERAGENT => t('User agent')); + $scan = array(); + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { + $scan[] = SPAM_CUSTOM_SCAN_CONTENT; + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { + $scan[] = SPAM_CUSTOM_SCAN_REFERRER; + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { + $scan[] = SPAM_CUSTOM_SCAN_USERAGENT; + } + $form['scan'] = array( + '#type' => 'checkboxes', + '#title' => t('Scan'), + '#description' => t('Specify where you\'d like to apply your custom filter.'), + '#options' => $options, + '#required' => TRUE, + '#default_value' => !empty($scan) ? $scan : array(SPAM_CUSTOM_SCAN_CONTENT), + ); + $options = array(); + $form['status'] = array( + '#type' => 'radios', + '#title' => t('Status'), + '#description' => t('Select the status to apply when your custom filter matches site content. Filters are tested in the order they are displayed above, thus if content matches a filter that says to mark it as spam, and another to mark it as not spam, the first to match will be the actual status applied.'), + '#options' => array( + SPAM_CUSTOM_STATUS_DISABLED => t('Disabled'), + SPAM_CUSTOM_STATUS_SPAM => t('Mark as spam'), + SPAM_CUSTOM_STATUS_PROBABLY => t('Mark as probably spam'), + SPAM_CUSTOM_STATUS_PROBABLYNOT => t('Mark as probably not spam'), + SPAM_CUSTOM_STATUS_NOTSPAM => t('Mark as not spam')), + '#default_value' => $spam_custom->cid ? $spam_custom->status : SPAM_CUSTOM_STATUS_SPAM, + '#required' => TRUE, + ); + $form['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#description' => t('Give your custom filter a weight. "Lighter" filters with smaller weights will run before "heavier" filters with larger weights.'), + '#default_value' => $spam_custom->weight, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => $spam_custom->cid ? t('Update filter') : t('Create filter'), + ); + + if ($spam_custom->cid) { + $form['cid'] = array( + '#type' => 'hidden', + '#value' => $spam_custom->cid, + ); + } + + return $form; +} + +/** + * Be sure that the custom filter is valid. + */ +function spam_custom_admin_filter_validate($form, &$form_state) { + if ($form_state['values']['style'] == SPAM_CUSTOM_STYLE_REGEX) { + if (preg_match($form_state['values']['filter'], 'test') === FALSE) { + form_set_error('filter', t('Failed to validate your filter\'s regular expression. It must be properly formatted as a Perl-compatible regular expression. Review the above error for details on the specific problem with your expression.')); + } + } + if (isset($form_state['values']['cid'])) { + // update + $cid = db_result(db_query("SELECT cid FROM {spam_custom} WHERE filter = '%s' AND cid <> %d", $form_state['values']['filter'], $form_state['values']['cid'])); + if ($cid) { + form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); + } + } + else { + // create + $cid = db_result(db_query("SELECT cid FROM {spam_custom} WHERE filter = '%s'", $form_state['values']['filter'])); + if ($cid) { + form_set_error($cid, t('Custom filter %filter already exists', array('%filter' => $form_state['values']['filter']))); + } + } +} + +/** + * Create/update custom filer. + */ +function spam_custom_admin_filter_submit($form, &$form_state) { + $scan = 0; + if (is_array($form_state['values']['scan'])) { + foreach ($form_state['values']['scan'] as $s) { + $scan += $s; + } + } + if (isset($form_state['values']['cid'])) { + db_query("UPDATE {spam_custom} SET filter = '%s', style = %d, status = %d, scan = %d, weight = %d WHERE cid = %d", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight'], $form_state['values']['cid']); + drupal_set_message(t('Custom filter %filter updated.', array('%filter' => $form_state['values']['filter']))); + } + else { + db_query("INSERT INTO {spam_custom} (filter, style, status, scan, weight) VALUES ('%s', %d, %d, %d, %d)", $form_state['values']['filter'], $form_state['values']['style'], $form_state['values']['status'], $scan, $form_state['values']['weight']); + drupal_set_message(t('Custom filter %filter created.', array('%filter' => $form_state['values']['filter']))); + } + drupal_goto('admin/settings/spam/filters/custom'); +} + +/** + * Perform bulk operations on the filters. + */ +function spam_custom_admin_settings_submit($form, &$form_state) { + if (is_array($form_state['values']['spam_custom'])) { + foreach ($form_state['values']['spam_custom'] as $cid => $selected) { + if ($selected) { + $process[] = $cid; + } + } + } + if (!empty($process)) { + foreach (module_invoke_all('spam_custom_operations') as $operation => $op) { + $options[$operation] = $op; + } + $operation = $form_state['values']['operation']; + if (isset($options[$operation])) { + $function = $options[$operation]['callback']; + $arguments = $options[$operation]['callback arguments']; + //TODO: Why is order different than spam.module, and why 1 at a time? + foreach ($process as $cid) { + call_user_func_array($function, array_merge($arguments, array($cid))); + } + } + } +} + +/** + * Perform custom operations. + * TODO: Confirmation would be nice. + */ +function spam_custom_spam_filter_operations($op, $cid) { + $filter = db_fetch_object(db_query('SELECT cid, status, filter FROM {spam_custom} WHERE cid = %d', $cid)); + switch ($op) { + case 'delete': + if ($filter->cid) { + db_query('DELETE FROM {spam_custom} WHERE cid = %d', $cid); + drupal_set_message(t('Deleted custom filter %filter.', array('%filter' => $filter->filter))); + } + break; + case 'disable': + if ($filter->cid && $filter->status != SPAM_CUSTOM_STATUS_DISABLED) { + db_query('UPDATE {spam_custom} SET status = %d WHERE cid = %d', SPAM_CUSTOM_STATUS_DISABLED, $cid); + drupal_set_message(t('Disabled custom filter %filter.', array('%filter' => $filter->filter))); + } + break; + } +} + +/** + * Apply enabled custom filter rules against content. + */ +function spam_custom_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $probably = $probably_not = 0; + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $result = db_query('SELECT cid, filter, style, status, scan, action FROM {spam_custom} WHERE status <> %d ORDER BY weight ASC', SPAM_CUSTOM_STATUS_DISABLED); + while ($spam_custom = db_fetch_object($result)) { + $scan = ''; + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_CONTENT) { + // scan content + if (is_object($content)) { + $content = (array)$content; + } + $scan .= spam_get_text($content, $type, $fields, $extra); + spam_log(SPAM_DEBUG, 'spam_custom_spam_filter', t('scanning content with %filter.', array('%filter' => $spam_custom->filter)), $type, $id); + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_REFERRER) { + // scan referrer + // TODO: Determine if this is a live scan. If not, don't scan referrer. + $scan .= $_SERVER['HTTP_REFERER']; + spam_log(SPAM_DEBUG, 'spam_custom_spam_filter', t('scanning referrer with %filter.', array('%filter' => $spam_custom->filter)), $type, $id); + } + if ($spam_custom->scan & SPAM_CUSTOM_SCAN_USERAGENT) { + // scan user agent + // TODO: Determine if this is a live scan. If not, don't scan user agent. + $scan .= $_SERVER['HTTP_USER_AGENT']; + spam_log(SPAM_DEBUG, 'spam_custom_spam_filter', t('scanning user agent with %filter.', array('%filter' => $spam_custom->filter)), $type, $id); + } + switch ($spam_custom->style) { + case SPAM_CUSTOM_STYLE_PLAIN: + $match = preg_match_all("/$spam_custom->filter/", $scan, $matches); + break; + case SPAM_CUSTOM_STYLE_REGEX: + $match = preg_match_all($spam_custom->filter, $scan, $matches); + break; + } + if ($match) { + // Record that we've had one or more matches. + db_query('UPDATE {spam_custom} SET matches = matches + %d, last = %d WHERE cid = %d', $match, time(), $spam_custom->cid); + spam_log(SPAM_VERBOSE, 'spam_custom_spam_filter', t('matched with %filter.', array('%filter' => $spam_custom->filter)), $type, $id); + + $action['spam_custom'][] = array( + 'filter' => $spam_custom->filter, + 'status' => $spam_custom->status, + 'style' => $spam_custom->style, + 'scan' => $spam_custom->scan, + 'extra' => $spam_custom->extra, + ); + + switch ($spam_custom->status) { + + case SPAM_CUSTOM_STATUS_SPAM: + spam_log(SPAM_VERBOSE, 'spam_custom_spam_filter', t('content is spam.'), $type, $id); + // no need to scan any more, we've found spam + $action['total'] = 99; + return $action; + + case SPAM_CUSTOM_STATUS_NOTSPAM: + spam_log(SPAM_VERBOSE, 'spam_custom_spam_filter', t('content is not spam.'), $type, $id); + // no need to scan any more, we've found non-spam + $action['total'] = 1; + return $action; + + case SPAM_CUSTOM_STATUS_PROBABLYNOT: + spam_log(SPAM_DEBUG, 'spam_custom_spam_filter', t('content is probably not spam.'), $type, $id); + // maintain internal counter that this is probably not spam + $probably_not += $match; + break; + + case SPAM_CUSTOM_STATUS_PROBABLY: + spam_log(SPAM_DEBUG, 'spam_custom_spam_filter', t('content is probably spam.'), $type, $id); + // maintain internal counter that this is probably spam + $probably += $match; + break; + } + } + } + + if ($probably && $probably_not) { + if ($probably >= $probably_not) { + $probably -= $probably_not; + $probably_not = 0; + } + else { + $probably_not -= $probably; + $probably = 0; + } + } + if ($probably) { + spam_log(SPAM_VERBOSE, 'spam_custom_spam_filter', t('matched adjusted total of !number probably spam rule(s).', array('!number' => $probably)), $type, $id); + if ($probably >= variable_get('spam_custom_probably', 3)) { + $action['total'] = 99; + } + else { + $action['total'] = variable_get('spam_custom_probably_value', variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)); + } + } + else if ($probably_not) { + spam_log(SPAM_VERBOSE, 'spam_custom_spam_filter', t('matched adjusted total of !number probably-not spam rule(s).', array('!number' => $probably_not)), $type, $id); + if ($probably_not >= variable_get('spam_custom_probablynot', 3)) { + $action['total'] = 1; + } + else { + $action['total'] = variable_get('spam_custom_probablynot_value', 40); + } + } + else { + // No matched filters, so don't change the overall spam score. + $action['total'] = 0; + } + return $action; +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc 2009-12-09 14:39:37.000000000 +1100 @@ -0,0 +1,72 @@ +style) { + case SPAM_FILTER_CUSTOM_PLAIN: + case SPAM_FILTER_CUSTOM_URL: + $style = SPAM_FILTER_CUSTOM_STYLE_PLAIN; + break; + case SPAM_FILTER_CUSTOM_REGEX: + $style = SPAM_FILTER_CUSTOM_STYLE_REGEX; + break; + } + switch ($old->effect) { + case SPAM_FILTER_CUSTOM_DISABLED: + default: + $status = SPAM_FILTER_CUSTOM_STATUS_DISABLED; + break; + case SPAM_FILTER_CUSTOM_MAYBE_SPAM: + case SPAM_FILTER_CUSTOM_USUALLY_SPAM: + $status = SPAM_FILTER_CUSTOM_STATUS_PROBABLY; + break; + case SPAM_FILTER_CUSTOM_USUALLY_NOTSPAM: + case SPAM_FILTER_CUSTOM_MAYBE_NOTSPAM: + $status = SPAM_FILTER_CUSTOM_STATUS_PROBABLYNOT; + break; + case SPAM_FILTER_CUSTOM_NEVER_SPAM: + $status = SPAM_FILTER_CUSTOM_STATUS_NOTSPAM; + break; + case SPAM_FILTER_CUSTOM_ALWAYS_SPAM: + $status = SPAM_FILTER_CUSTOM_STATUS_SPAM; + break; + } + if ($old->action & SPAM_FILTER_CUSTOM_ACTION_HEADER || $old->action & SPAM_FILTER_CUSTOM_ACTION_BODY) { + $scan = SPAM_FILTER_CUSTOM_SCAN_CONTENT; + } + else { + $scan = SPAM_FILTER_CUSTOM_SCAN_CONTENT; + $status = SPAM_FILTER_CUSTOM_STATUS_DISABLED; + } + db_query("INSERT INTO {spam_filter_custom} (filter, style, status, scan, matches, last) VALUES('%s', %d, %d, %d, %d, %d)", $old->filter, $style, $status, $scan, $old->matches, $old->last); + } + // Done with upgrade, drop old table. + $ret = array(); + db_drop_table($ret, 'old_spam_custom'); + } +} + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc~ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc~ --- /home/files/coding/drupal/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_custom/spam_filter_custom-upgrade.inc~ 2009-12-09 14:39:37.000000000 +1100 @@ -0,0 +1,72 @@ +style) { + case SPAM_CUSTOM_PLAIN: + case SPAM_CUSTOM_URL: + $style = SPAM_CUSTOM_STYLE_PLAIN; + break; + case SPAM_CUSTOM_REGEX: + $style = SPAM_CUSTOM_STYLE_REGEX; + break; + } + switch ($old->effect) { + case SPAM_CUSTOM_DISABLED: + default: + $status = SPAM_CUSTOM_STATUS_DISABLED; + break; + case SPAM_CUSTOM_MAYBE_SPAM: + case SPAM_CUSTOM_USUALLY_SPAM: + $status = SPAM_CUSTOM_STATUS_PROBABLY; + break; + case SPAM_CUSTOM_USUALLY_NOTSPAM: + case SPAM_CUSTOM_MAYBE_NOTSPAM: + $status = SPAM_CUSTOM_STATUS_PROBABLYNOT; + break; + case SPAM_CUSTOM_NEVER_SPAM: + $status = SPAM_CUSTOM_STATUS_NOTSPAM; + break; + case SPAM_CUSTOM_ALWAYS_SPAM: + $status = SPAM_CUSTOM_STATUS_SPAM; + break; + } + if ($old->action & SPAM_CUSTOM_ACTION_HEADER || $old->action & SPAM_CUSTOM_ACTION_BODY) { + $scan = SPAM_CUSTOM_SCAN_CONTENT; + } + else { + $scan = SPAM_CUSTOM_SCAN_CONTENT; + $status = SPAM_CUSTOM_STATUS_DISABLED; + } + db_query("INSERT INTO {spam_custom} (filter, style, status, scan, matches, last) VALUES('%s', %d, %d, %d, %d, %d)", $old->filter, $style, $status, $scan, $old->matches, $old->last); + } + // Done with upgrade, drop old table. + $ret = array(); + db_drop_table($ret, 'old_spam_custom'); + } +} + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.info sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.info --- /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.info 2009-12-02 19:13:25.000000000 +1100 @@ -0,0 +1,12 @@ +; $Id: duplicate.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam Duplicate filter +description = A duplication detecting spam filter. +package = Spam +dependencies[] = spam +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install --- /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,60 @@ + t('The base table for the Duplicate submodule'), + 'fields' => array( + 'iid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), + 'sid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'content_hash' => array('type' => 'char', 'length' => '32', 'not null' => TRUE, 'default' => ''), + 'hostname' => array('type' => 'varchar', 'length' => '15', 'not null' => TRUE, 'default' => ''), + 'duplicate_hash' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'duplicate_ip' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'spam' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), + 'expired' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), + 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + ), + 'primary key' => array('iid'), + 'indexes' => array( + 'content_hash' => array('content_hash'), + 'hostname' => array('hostname'), + 'sid' => array('sid'), + 'spam' => array('spam'), + 'timestamp' => array('timestamp'), + ), + ); + + return $schema; +} + +function spam_filter_duplicate_install() { + // Create my tables. + drupal_install_schema('spam_filter_duplicate'); +} + +function spam_filter_duplicate_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_filter_duplicate'); + drupal_set_message('The spam_filter_duplicate table has been dropped.'); +} + +/** + * Fix variable namespacing. + */ +function spam_filter_duplicate_update_6101() { + $ret = array(); + $old_vars = array('duplicate_blacklist', 'duplicate_blacklist_action', 'duplicate_blacklist_message', 'duplicate_post_message', 'duplicate_threshold'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_filter_'.$var, $var); + } + } + return $ret; + } diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install~ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install~ --- /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.install~ 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,60 @@ + t('The base table for the Duplicate submodule'), + 'fields' => array( + 'iid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, 'disp-width' => '11'), + 'sid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'content_hash' => array('type' => 'char', 'length' => '32', 'not null' => TRUE, 'default' => ''), + 'hostname' => array('type' => 'varchar', 'length' => '15', 'not null' => TRUE, 'default' => ''), + 'duplicate_hash' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'duplicate_ip' => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'disp-width' => '11'), + 'spam' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), + 'expired' => array('type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, 'disp-width' => '4'), + 'timestamp' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => 0, 'disp-width' => '11'), + ), + 'primary key' => array('iid'), + 'indexes' => array( + 'content_hash' => array('content_hash'), + 'hostname' => array('hostname'), + 'sid' => array('sid'), + 'spam' => array('spam'), + 'timestamp' => array('timestamp'), + ), + ); + + return $schema; +} + +function spam_filter_duplicate_install() { + // Create my tables. + drupal_install_schema('spam_filter_duplicate'); +} + +function spam_filter_duplicate_uninstall() { + // Remove tables. + drupal_uninstall_schema('spam_filter_duplicate'); + drupal_set_message('The spam_filter_duplicate table has been dropped.'); +} + +/** + * Fix variable namespacing. + */ +function spam_filter_duplicate_update_6101() { + $ret = array(); + $old_vars = array('duplicate_blacklist', 'duplicate_blacklist_action', 'duplicate_blacklist_message', 'duplicate_post_message', 'duplicate_threshold'); + foreach ( $old_vars as $var ) { + $test = variable_get($var, null); + if ( !empty($test) ) { + $ret[] = update_sql('UPDATE {variables} SET name = %s WHERE name = %s', 'spam_'.$var, $var); + } + } + return $ret; + } diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module --- /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module 2009-12-09 15:46:20.000000000 +1100 @@ -0,0 +1,400 @@ +. All rights reserved. + * + * Detects spam by looking for duplication of content, or posting IP. + */ + +define('SPAM_FILTER_DUPLICATE_BLACKLIST_SILENT', 0); +define('SPAM_FILTER_DUPLICATE_BLACKLIST_NOTIFY', 1); +define('SPAM_FILTER_DUPLICATE_BLACKLIST_BLOCK', 2); + +define('SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD', 2); +define('SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST', 3); + +define('SPAM_FILTER_DUPLICATE_NOT_SPAM', 0); +define('SPAM_FILTER_DUPLICATE_SPAM', 1); + +/** + * Drupal _menu() hook. + */ +function spam_filter_duplicate_menu() { + $items = array(); + + $items['admin/settings/spam/filters/duplicate'] = array( + 'title' => 'Duplicate', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_duplicate_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the spam duplicate filter.', + 'type' => MENU_LOCAL_TASK, + ); + $items['duplicate/denied/ip'] = array( + 'page callback' => 'spam_filter_duplicate_denied_ip', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + $items['duplicate/denied/post'] = array( + 'page callback' => 'spam_filter_duplicate_denied_post', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + $items['admin/reports/spam/blocked_ip'] = array( + 'title' => 'Blocked IPs', + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_blocked_ip', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +/** + * The arg() function may not be availble early in the bootstrap process, + * so we reimplement it here. + */ +function _spam_filter_duplicate_arg() { + static $arguments, $q; + + if (empty($arguments) || $q != $_GET['q']) { + $arguments = explode('/', $_GET['q']); + $q = $_GET['q']; + } + + if (isset($arguments[$index])) { + return $arguments[$index]; + } +} + + +/** + * If IP blacklisting and IP blocking are both enabled, perform a database + * query on each page load to see if the current visitor has been blacklisted. + */ +function spam_filter_duplicate_init() { + // Allow notification to blacklisted IP, if enabled. + if (_spam_filter_duplicate_arg(0) == 'duplicate' && _spam_filter_duplicate_arg(1) == 'denied' && _spam_filter_duplicate_arg(2) == 'ip') return; + + // Only perform database queries if functionality is enabled. + if ((variable_get('spam_filter_duplicate_blacklist_action', SPAM_FILTER_DUPLICATE_BLACKLIST_NOTIFY) == SPAM_FILTER_DUPLICATE_BLACKLIST_BLOCK) && (variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) > -1)) { + // Blacklisting and IP blocking enabled. + $spam_filter_duplicate_ip = (int)db_query("SELECT COUNT(iid) FROM {spam_filter_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), SPAM_FILTER_DUPLICATE_SPAM); + if ($spam_filter_duplicate_ip >= variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST)) { + if (user_access('bypass filters')) { + spam_log(SPAM_DEBUG, 'spam_filter_duplicate_init', t('Found !count spam for IP !ip, ignoring because user !user (uid !uid) is configured to bypass filters', array('!count' => $spam_filter_duplicate_ip, '!ip' => ip_address(), '!user' => $user->name, 'uid' => $user->uid)), $type, $id); + return; + } + drupal_goto("duplicate/denied/ip"); + } + } +} + +/** + * Spam API Hook + */ +function spam_filter_duplicate_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + + switch ($op) { + + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + return spam_filter_duplicate_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_duplicate'; + + case 'insert': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + if (is_array($extra) && $extra['sid'] && $extra['host'] && + !empty($content) && !empty($fields)) { + $hash = _spam_filter_duplicate_content_hash($content, $fields); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); + $action = _spam_filter_duplicate_action(); + if (is_array($action) && !empty($action)) { + if (isset($action['redirect'])) { + drupal_goto($action['redirect']); + } + } + } + break; + + case 'update': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + if (is_array($extra) && $extra['sid'] && $extra['host'] && + !empty($content) && !empty($fields)) { + $hash = _spam_filter_duplicate_content_hash($content, $fields); + db_query("UPDATE {spam_filter_duplicate} SET content_hash = '%s', hostname = '%s', timestamp = %d WHERE sid = %d", $hash, $extra['host'], time(), $extra['sid']); + if (!db_affected_rows()) { + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); + } + $action = _spam_filter_duplicate_action(); + if (is_array($action) && !empty($action)) { + if (isset($action['redirect'])) { + drupal_goto($action['redirect']); + } + } + } + break; + + case 'delete': + if (is_array($extra) && $extra['sid'] && !empty($content) && !empty($fields)) { + db_query("DELETE FROM {spam_filter_duplicate} WHERE sid = %d", $extra['sid']); + } + break; + + case 'filter_info': + return array( + 'name' => t('Duplicate filter'), + 'module' => t('spam_filter_duplicate'), + 'description' => t('A duplication spam filter.'), + 'help' => t('The duplicate filter detects spam by detecting content duplication.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'weight' => -8, + ); + + case 'mark_as_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + db_query('UPDATE {spam_filter_duplicate} SET spam = %d WHERE sid = %d', SPAM_FILTER_DUPLICATE_SPAM, $extra['sid']); + if (!db_affected_rows() && $extra['id'] && $extra['sid']) { + $content = spam_invoke_module($type, 'load', $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $content); + $hash = _spam_filter_duplicate_content_hash($content, $fields); + $hostname = spam_invoke_module($type, 'hostname', $extra['id']); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); + } + $action = _spam_filter_duplicate_action(); + if (is_array($action) && isset($action['redirect'])) { + return $action['redirect']; + } + break; + + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + db_query('UPDATE {spam_filter_duplicate} SET spam = %d WHERE sid = %d', SPAM_FILTER_DUPLICATE_NOT_SPAM, $extra['sid']); + if (!db_affected_rows() && $extra['id'] && $extra['sid']) { + // Updating content that we've not filtered before. Retrive all the + // data we need to add it to the spam_filter_duplicate table. + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $hash = _spam_filter_duplicate_content_hash($extra['content'], $fields); + $hostname = spam_invoke_module($type, 'hostname', $extra['id']); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); + } + break; + + } +} + +/** + * + */ +function spam_filter_duplicate_admin_settings() { + $form['content'] = array( + '#type' => 'fieldset', + '#title' => t('Content'), + '#collapsible' => TRUE, + ); + $limits = drupal_map_assoc(range(2, 15)); + $limits[-1] = t('unlimited'); + $form['content']['spam_filter_duplicate_threshold'] = array( + '#type' => 'select', + '#title' => t('Duplication threshold'), + '#default_value' => variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD), + '#options' => $limits, + '#description' => t('Specify how many times the same identical content can be posted before it will be considered spam. When tuning this filter, note that users may accidentally submit the same content multiple times causing an otherwise acceptible posting to be duplicated.'), + ); + $form['content']['spam_filter_duplicate_post_message'] = array( + '#type' => 'textarea', + '#title' => t('Duplicate post message'), + '#default_value' => variable_get('spam_filter_duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), + '#description' => t('Message to show visitors when their content has been blocked because it was posted multiple times. The text "%IP" will be replaced by the visitors actual IP address.'), + ); + + $form['ip'] = array( + '#type' => 'fieldset', + '#title' => t('IP'), + '#collapsible' => TRUE, + ); + $limits = drupal_map_assoc(range(1, 15)); + $limits[-1] = t('unlimited'); + $form['ip']['spam_filter_duplicate_blacklist'] = array( + '#type' => 'select', + '#title' => t('IP blacklist threshold'), + '#default_value' => variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST), + '#options' => $limits, + '#description' => t('Specify how many times a given IP address is allowed to post possible spam content before the IP address is blacklisted and prevented from posting any additional content.'), + ); + $form['ip']['spam_filter_duplicate_blacklist_action'] = array( + '#type' => 'radios', + '#title' => t('IP blacklist action'), + '#options' => array(t('Silently prevent visitor from posting'), t('Notify blacklisted visitor when posting, prevent from posting'), t('Notify blacklisted visitor, prevent from visiting site')), + '#default_value' => variable_get('spam_filter_duplicate_blackZ>&!ô/ „XSƒ»(´7 0/´&6€R_%DóíATE_BLACKLIST_NOTIFY), + '#description' => t('Select an action from the above options. If notification is enabled, the user will be redirected to a custom page displaying the "Blacklisted IP message" defined below. If you only prevent users from posting, they will be able to view all site content. If you prevent a user from visiting your site, they will only ever see the "Blacklisted IP message".'), + ); + $form['ip']['spam_filter_duplicate_blacklist_message'] = array( + '#type' => 'textarea', + '#title' => t('Blacklisted IP message'), + '#default_value' => variable_get('spam_filter_duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), + '#description' => t('Message to show visitors when their IP has been blacklisted. The text "%IP" will be replaced by the visitors actual IP address.') + ); + + return system_settings_form($form); +} + +/** + * Save the configuration. + */ +function spam_filter_duplicate_admin_settings_submit($form, &$form_state) { +/* TODO The 'op' element in the form values is deprecated. + Each button can have #validate and #submit functions associated with it. + Thus, there should be one button that submits the form and which invokes + the normal form_id_validate and form_id_submit handlers. Any additional + buttons which need to invoke different validate or submit functionality + should have button-specific functions. */ + if ($form_state['values']['op'] == t('Reset to defaults')) { + variable_del('spam_filter_duplicate_threshold'); + variable_del('spam_filter_duplicate_post_message'); + variable_del('spam_filter_duplicate_blacklist'); + variable_del('spam_filter_duplicate_blacklist_action'); + variable_del('spam_filter_duplicate_blacklist_message'); + drupal_set_message('Configuration reset to defaults.'); + } + else { + variable_set('spam_filter_duplicate_threshold', $form_state['values']['spam_filter_duplicate_threshold']); + variable_set('spam_filter_duplicate_post_message', $form_state['values']['spam_filter_duplicate_post_message']); + variable_set('spam_filter_duplicate_blacklist', $form_state['values']['spam_filter_duplicate_blacklist']); + variable_set('spam_filter_duplicate_blacklist_action', $form_state['values']['spam_filter_duplicate_blacklist_action']); + variable_set('spam_filter_duplicate_blacklist_message', $form_state['values']['spam_filter_duplicate_blacklist_message']); + drupal_set_message('Configuration saved.'); + } +} + +/** + * Get and md5 hash of all content truncated together. + */ +function _spam_filter_duplicate_content_hash($content, $fields) { + if (is_object($content)) { + $content = (array)$content; + } + $hash = ''; + foreach ($fields['main'] as $field) { + $hash .= $content[$field]; + } + return md5($hash); +} + +/** + * Determine whether or not the content is spam. + */ +function spam_filter_duplicate_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $score = 0; + $action = array(); + $hash = _spam_filter_duplicate_content_hash($content, $fields); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam_filter_duplicate_hash = db_result(db_query("SELECT COUNT(d.iid) FROM {spam_filter_duplicate} d LEFT JOIN {spam_tracker} t ON d.sid = t.sid WHERE content_hash = '%s' AND content_id <> %d", $hash, $id)) + 1; + if ($spam_filter_duplicate_hash >= variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD)) { + $sids = db_query("SELECT sid FROM {spam_filter_duplicate} WHERE content_hash = '%s'", $hash); + if (!$filter_test) { + while ($sid = db_result($sids)) { + $unpublish = db_fetch_object(db_query('SELECT content_type, content_id, score FROM {spam_tracker} WHERE sid = %d', $sid)); + spam_mark_as_spam($unpublish->content_type, $unpublish->content_id, array('score' => 99)); + } + // Update counter tracking that we've blocked a duplicate posting of this + // content. (It will actually increment the counter on + // "duplicate_threshold" rows.) + db_query("UPDATE {spam_filter_duplicate} SET spam_filter_duplicate_hash = spam_filter_duplicate_hash + 1 WHERE content_hash = '%s'", $hash); + } + $action['hash'] = array( + 'score' => 99, + 'description' => t('Content is identical to %count other existing posts.', array('%count' => variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD))), + ); + $action['total'] = 99; + $action['redirect'] = 'duplicate/denied/post'; + _spam_filter_duplicate_action($action); + return $action; + } + + $spam_filter_duplicate_ip = db_result(db_query("SELECT COUNT(iid) FROM {spam_filter_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), SPAM_FILTER_DUPLICATE_SPAM)); + if ($spam_filter_duplicate_ip >= variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) && (variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) > -1)) { + $action['ip'] = array( + 'score' => 99, + 'description' => t('Content was posted by the same IP address used to post %count other spam posts.', array('%count' => variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST))), + ); + $action['total'] = 99; + $action['redirect'] = 'duplicate/denied/ip'; + } + + return $action; +} + +function _spam_filter_duplicate_action($register = array()) { + static $action = array(); + + if (!empty($register)) { + $action = $register; + } + + return $action; +} + +/** + * + */ +function spam_filter_duplicate_denied_ip() { + $message = strtr(variable_get('spam_filter_duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); + spam_denied_page($message, t('Your IP address has been blocked by our spam filter.')); +} + +/** + * + */ +function spam_filter_duplicate_denied_post() { + $message = strtr(variable_get('spam_filter_duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); + spam_denied_page($message, t('You have attempted to post the same content multiple times.')); +} + +function spam_logs_blocked_ip() { + drupal_set_title(t('Spam Module Blocked IPs')); + + $header = array( + array('data' => t('IP Address'), 'field' => 'hostname'), + array('data' => t('Last Seen'), 'field' => 'timestamp', 'sort' => 'desc'), + array('data' => t('Counter'), 'field' => 'count'), + ); + + // This SQL is *nasty*, so if you think you can do better, please be my guest! + // This unfortunately has to be SQL because the pager module can't be told + // how many rows we've got (so we can't do our own processing in PHP and + // still get paging to work properly). + $sql = "SELECT * FROM (SELECT DISTINCT x.hostname, x.timestamp, COUNT(x.hostname) AS count FROM (SELECT timestamp, hostname FROM {spam_tracker} WHERE score > %d ORDER BY timestamp DESC) AS x GROUP BY x.hostname) AS y WHERE y.count >= %d"; + $arguments = array(variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD), variable_get('spam_blacklist_ip', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST)); + + $count_sql = preg_replace('/^SELECT \* /', 'SELECT count(hostname) ', $sql); + + $result = pager_query($sql . tablesort_sql($header), 50, 0, $count_sql, $arguments); + + while ($log = db_fetch_object($result)) { + $rows[] = array('data' => array( + $log->hostname, + format_date($log->timestamp, 'small'), + $log->count + ) + ); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); + } + + return theme('table', $header, $rows) . theme('pager', NULL, 50, 0); +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module~ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_duplicate/spam_filter_duplicate.module~ 2009-12-09 15:46:20.000000000 +1100 @@ -0,0 +1,400 @@ +. All rights reserved. + * + * Detects spam by looking for duplication of content, or posting IP. + */ + +define('SPAM_FILTER_DUPLICATE_BLACKLIST_SILENT', 0); +define('SPAM_FILTER_DUPLICATE_BLACKLIST_NOTIFY', 1); +define('SPAM_FILTER_DUPLICATE_BLACKLIST_BLOCK', 2); + +define('SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD', 2); +define('SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST', 3); + +define('SPAM_FILTER_DUPLICATE_NOT_SPAM', 0); +define('SPAM_FILTER_DUPLICATE_SPAM', 1); + +/** + * Drupal _menu() hook. + */ +function spam_filter_duplicate_menu() { + $items = array(); + + $items['admin/settings/spam/filters/duplicate'] = array( + 'title' => 'Duplicate', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_duplicate_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the spam duplicate filter.', + 'type' => MENU_LOCAL_TASK, + ); + $items['duplicate/denied/ip'] = array( + 'page callback' => 'spam_filter_duplicate_denied_ip', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + $items['duplicate/denied/post'] = array( + 'page callback' => 'spam_filter_duplicate_denied_post', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + + $items['admin/reports/spam/blocked_ip'] = array( + 'title' => 'Blocked IPs', + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_blocked_ip', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +/** + * The arg() function may not be availble early in the bootstrap process, + * so we reimplement it here. + */ +function _spam_filter_duplicate_arg() { + static $arguments, $q; + + if (empty($arguments) || $q != $_GET['q']) { + $arguments = explode('/', $_GET['q']); + $q = $_GET['q']; + } + + if (isset($arguments[$index])) { + return $arguments[$index]; + } +} + + +/** + * If IP blacklisting and IP blocking are both enabled, perform a database + * query on each page load to see if the current visitor has been blacklisted. + */ +function spam_filter_duplicate_init() { + // Allow notification to blacklisted IP, if enabled. + if (_spam_filter_duplicate_arg(0) == 'duplicate' && _spam_filter_duplicate_arg(1) == 'denied' && _spam_filter_duplicate_arg(2) == 'ip') return; + + // Only perform database queries if functionality is enabled. + if ((variable_get('spam_filter_duplicate_blacklist_action', SPAM_FILTER_DUPLICATE_BLACKLIST_NOTIFY) == SPAM_FILTER_DUPLICATE_BLACKLIST_BLOCK) && (variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) > -1)) { + // Blacklisting and IP blocking enabled. + $spam_filter_duplicate_ip = (int)db_query("SELECT COUNT(iid) FROM {spam_filter_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), SPAM_FILTER_DUPLICATE_SPAM); + if ($spam_filter_duplicate_ip >= variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST)) { + if (user_access('bypass filters')) { + spam_log(SPAM_DEBUG, 'spam_filter_duplicate_init', t('Found !count spam for IP !ip, ignoring because user !user (uid !uid) is configured to bypass filters', array('!count' => $spam_filter_duplicate_ip, '!ip' => ip_address(), '!user' => $user->name, 'uid' => $user->uid)), $type, $id); + return; + } + drupal_goto("duplicate/denied/ip"); + } + } +} + +/** + * Spam API Hook + */ +function spam_filter_duplicate_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + + switch ($op) { + + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + return spam_filter_duplicate_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'duplicate'; + + case 'insert': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + if (is_array($extra) && $extra['sid'] && $extra['host'] && + !empty($content) && !empty($fields)) { + $hash = _spam_filter_duplicate_content_hash($content, $fields); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); + $action = _spam_filter_duplicate_action(); + if (is_array($action) && !empty($action)) { + if (isset($action['redirect'])) { + drupal_goto($action['redirect']); + } + } + } + break; + + case 'update': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + if (is_array($extra) && $extra['sid'] && $extra['host'] && + !empty($content) && !empty($fields)) { + $hash = _spam_filter_duplicate_content_hash($content, $fields); + db_query("UPDATE {spam_filter_duplicate} SET content_hash = '%s', hostname = '%s', timestamp = %d WHERE sid = %d", $hash, $extra['host'], time(), $extra['sid']); + if (!db_affected_rows()) { + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $extra['host'], time()); + } + $action = _spam_filter_duplicate_action(); + if (is_array($action) && !empty($action)) { + if (isset($action['redirect'])) { + drupal_goto($action['redirect']); + } + } + } + break; + + case 'delete': + if (is_array($extra) && $extra['sid'] && !empty($content) && !empty($fields)) { + db_query("DELETE FROM {spam_filter_duplicate} WHERE sid = %d", $extra['sid']); + } + break; + + case 'filter_info': + return array( + 'name' => t('Duplicate filter'), + 'module' => t('spam_filter_duplicate'), + 'description' => t('A duplication spam filter.'), + 'help' => t('The duplicate filter detects spam by detecting content duplication.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'weight' => -8, + ); + + case 'mark_as_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + db_query('UPDATE {spam_filter_duplicate} SET spam = %d WHERE sid = %d', SPAM_FILTER_DUPLICATE_SPAM, $extra['sid']); + if (!db_affected_rows() && $extra['id'] && $extra['sid']) { + $content = spam_invoke_module($type, 'load', $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $content); + $hash = _spam_filter_duplicate_content_hash($content, $fields); + $hostname = spam_invoke_module($type, 'hostname', $extra['id']); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); + } + $action = _spam_filter_duplicate_action(); + if (is_array($action) && isset($action['redirect'])) { + return $action['redirect']; + } + break; + + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_duplicate', $type, $content, $fields, $extra)) return; + db_query('UPDATE {spam_filter_duplicate} SET spam = %d WHERE sid = %d', SPAM_FILTER_DUPLICATE_NOT_SPAM, $extra['sid']); + if (!db_affected_rows() && $extra['id'] && $extra['sid']) { + // Updating content that we've not filtered before. Retrive all the + // data we need to add it to the spam_filter_duplicate table. + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $hash = _spam_filter_duplicate_content_hash($extra['content'], $fields); + $hostname = spam_invoke_module($type, 'hostname', $extra['id']); + db_query("INSERT INTO {spam_filter_duplicate} (sid, content_hash, hostname, timestamp) VALUES(%d, '%s', '%s', %d)", $extra['sid'], $hash, $hostname, time()); + } + break; + + } +} + +/** + * + */ +function spam_filter_duplicate_admin_settings() { + $form['content'] = array( + '#type' => 'fieldset', + '#title' => t('Content'), + '#collapsible' => TRUE, + ); + $limits = drupal_map_assoc(range(2, 15)); + $limits[-1] = t('unlimited'); + $form['content']['spam_filter_duplicate_threshold'] = array( + '#type' => 'select', + '#title' => t('Duplication threshold'), + '#default_value' => variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD), + '#options' => $limits, + '#description' => t('Specify how many times the same identical content can be posted before it will be considered spam. When tuning this filter, note that users may accidentally submit the same content multiple times causing an otherwise acceptible posting to be duplicated.'), + ); + $form['content']['spam_filter_duplicate_post_message'] = array( + '#type' => 'textarea', + '#title' => t('Duplicate post message'), + '#default_value' => variable_get('spam_filter_duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), + '#description' => t('Message to show visitors when their content has been blocked because it was posted multiple times. The text "%IP" will be replaced by the visitors actual IP address.'), + ); + + $form['ip'] = array( + '#type' => 'fieldset', + '#title' => t('IP'), + '#collapsible' => TRUE, + ); + $limits = drupal_map_assoc(range(1, 15)); + $limits[-1] = t('unlimited'); + $form['ip']['spam_filter_duplicate_blacklist'] = array( + '#type' => 'select', + '#title' => t('IP blacklist threshold'), + '#default_value' => variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST), + '#options' => $limits, + '#description' => t('Specify how many times a given IP address is allowed to post possible spam content before the IP address is blacklisted and prevented from posting any additional content.'), + ); + $form['ip']['spam_filter_duplicate_blacklist_action'] = array( + '#type' => 'radios', + '#title' => t('IP blacklist action'), + '#options' => array(t('Silently prevent visitor from posting'), t('Notify blacklisted visitor when posting, prevent from posting'), t('Notify blacklisted visitor, prevent from visiting site')), + '#default_value' => variable_get('spam_filter_duplicate_blacklist_action', SPAM_FILTER_DUPLICATE_BLACKLIST_NOTIFY), + '#description' => t('Select an action from the above options. If notification is enabled, the user will be redirected to a custom page displaying the "Blacklisted IP message" defined below. If you only prevent users from posting, they will be able to view all site content. If you prevent a user from visiting your site, they will only ever see the "Blacklisted IP message".'), + ); + $form['ip']['spam_filter_duplicate_blacklist_message'] = array( + '#type' => 'textarea', + '#title' => t('Blacklisted IP message'), + '#default_value' => variable_get('spam_filter_duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

', array('@site' => variable_get('site_name', 'Drupal')))), + '#description' => t('Message to show visitors when their IP has been blacklisted. The text "%IP" will be replaced by the visitors actual IP address.') + ); + + return system_settings_form($form); +} + +/** + * Save the configuration. + */ +function spam_filter_duplicate_admin_settings_submit($form, &$form_state) { +/* TODO The 'op' element in the form values is deprecated. + Each button can have #validate and #submit functions associated with it. + Thus, there should be one button that submits the form and which invokes + the normal form_id_validate and form_id_submit handlers. Any additional + buttons which need to invoke different validate or submit functionality + should have button-specific functions. */ + if ($form_state['values']['op'] == t('Reset to defaults')) { + variable_del('spam_filter_duplicate_threshold'); + variable_del('spam_filter_duplicate_post_message'); + variable_del('spam_filter_duplicate_blacklist'); + variable_del('spam_filter_duplicate_blacklist_action'); + variable_del('spam_filter_duplicate_blacklist_message'); + drupal_set_message('Configuration reset to defaults.'); + } + else { + variable_set('spam_filter_duplicate_threshold', $form_state['values']['spam_filter_duplicate_threshold']); + variable_set('spam_filter_duplicate_post_message', $form_state['values']['spam_filter_duplicate_post_message']); + variable_set('spam_filter_duplicate_blacklist', $form_state['values']['spam_filter_duplicate_blacklist']); + variable_set('spam_filter_duplicate_blacklist_action', $form_state['values']['spam_filter_duplicate_blacklist_action']); + variable_set('spam_filter_duplicate_blacklist_message', $form_state['values']['spam_filter_duplicate_blacklist_message']); + drupal_set_message('Configuration saved.'); + } +} + +/** + * Get and md5 hash of all content truncated together. + */ +function _spam_filter_duplicate_content_hash($content, $fields) { + if (is_object($content)) { + $content = (array)$content; + } + $hash = ''; + foreach ($fields['main'] as $field) { + $hash .= $content[$field]; + } + return md5($hash); +} + +/** + * Determine whether or not the content is spam. + */ +function spam_filter_duplicate_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $score = 0; + $action = array(); + $hash = _spam_filter_duplicate_content_hash($content, $fields); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam_filter_duplicate_hash = db_result(db_query("SELECT COUNT(d.iid) FROM {spam_filter_duplicate} d LEFT JOIN {spam_tracker} t ON d.sid = t.sid WHERE content_hash = '%s' AND content_id <> %d", $hash, $id)) + 1; + if ($spam_filter_duplicate_hash >= variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD)) { + $sids = db_query("SELECT sid FROM {spam_filter_duplicate} WHERE content_hash = '%s'", $hash); + if (!$filter_test) { + while ($sid = db_result($sids)) { + $unpublish = db_fetch_object(db_query('SELECT content_type, content_id, score FROM {spam_tracker} WHERE sid = %d', $sid)); + spam_mark_as_spam($unpublish->content_type, $unpublish->content_id, array('score' => 99)); + } + // Update counter tracking that we've blocked a duplicate posting of this + // content. (It will actually increment the counter on + // "duplicate_threshold" rows.) + db_query("UPDATE {spam_filter_duplicate} SET spam_filter_duplicate_hash = spam_filter_duplicate_hash + 1 WHERE content_hash = '%s'", $hash); + } + $action['hash'] = array( + 'score' => 99, + 'description' => t('Content is identical to %count other existing posts.', array('%count' => variable_get('spam_filter_duplicate_threshold', SPAM_FILTER_DUPLICATE_DEFAULT_THRESHOLD))), + ); + $action['total'] = 99; + $action['redirect'] = 'duplicate/denied/post'; + _spam_filter_duplicate_action($action); + return $action; + } + + $spam_filter_duplicate_ip = db_result(db_query("SELECT COUNT(iid) FROM {spam_filter_duplicate} WHERE hostname = '%s' AND spam = %d", ip_address(), SPAM_FILTER_DUPLICATE_SPAM)); + if ($spam_filter_duplicate_ip >= variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) && (variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST) > -1)) { + $action['ip'] = array( + 'score' => 99, + 'description' => t('Content was posted by the same IP address used to post %count other spam posts.', array('%count' => variable_get('spam_filter_duplicate_blacklist', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST))), + ); + $action['total'] = 99; + $action['redirect'] = 'duplicate/denied/ip'; + } + + return $action; +} + +function _spam_filter_duplicate_action($register = array()) { + static $action = array(); + + if (!empty($register)) { + $action = $register; + } + + return $action; +} + +/** + * + */ +function spam_filter_duplicate_denied_ip() { + $message = strtr(variable_get('spam_filter_duplicate_blacklist_message', t('

You are currently not allowed to post content to @site, as previous content posted by your IP address (%IP) has been flagged as potential spam.

If you have not posted spam to @site, please report this error along with your IP address to a site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); + spam_denied_page($message, t('Your IP address has been blocked by our spam filter.')); +} + +/** + * + */ +function spam_filter_duplicate_denied_post() { + $message = strtr(variable_get('spam_filter_duplicate_post_message', t('

You have attempted to post the same identical content multiple times, causing your posts to be flagged as potential spam. If this has happened in error, please report this error along with your IP address (%IP) to a @site site administrator. We apologize for any inconvenience.

')), array('@site' => variable_get('site_name', 'Drupal'), '%IP' => ip_address())); + spam_denied_page($message, t('You have attempted to post the same content multiple times.')); +} + +function spam_logs_blocked_ip() { + drupal_set_title(t('Spam Module Blocked IPs')); + + $header = array( + array('data' => t('IP Address'), 'field' => 'hostname'), + array('data' => t('Last Seen'), 'field' => 'timestamp', 'sort' => 'desc'), + array('data' => t('Counter'), 'field' => 'count'), + ); + + // This SQL is *nasty*, so if you think you can do better, please be my guest! + // This unfortunately has to be SQL because the pager module can't be told + // how many rows we've got (so we can't do our own processing in PHP and + // still get paging to work properly). + $sql = "SELECT * FROM (SELECT DISTINCT x.hostname, x.timestamp, COUNT(x.hostname) AS count FROM (SELECT timestamp, hostname FROM {spam_tracker} WHERE score > %d ORDER BY timestamp DESC) AS x GROUP BY x.hostname) AS y WHERE y.count >= %d"; + $arguments = array(variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD), variable_get('spam_blacklist_ip', SPAM_FILTER_DUPLICATE_DEFAULT_BLACKLIST)); + + $count_sql = preg_replace('/^SELECT \* /', 'SELECT count(hostname) ', $sql); + + $result = pager_query($sql . tablesort_sql($header), 50, 0, $count_sql, $arguments); + + while ($log = db_fetch_object($result)) { + $rows[] = array('data' => array( + $log->hostname, + format_date($log->timestamp, 'small'), + $log->count + ) + ); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); + } + + return theme('table', $header, $rows) . theme('pager', NULL, 50, 0); +} diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.info sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.info --- /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.info 2009-12-09 14:46:50.000000000 +1100 @@ -0,0 +1,13 @@ +; $Id: node_age.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam node age filter +description = A node-age comment filter plug-in for the spam module. +package = Spam +dependencies[] = spam +dependencies[] = comment +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.info~ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.info~ --- /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.info~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.info~ 2009-12-09 14:46:50.000000000 +1100 @@ -0,0 +1,13 @@ +; $Id: node_age.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam node age filter +description = A node-age comment filter plug-in for the spam module. +package = Spam +dependencies[] = spam +dependencies[] = comment +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.install sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.install --- /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.install 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.install 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,25 @@ +. All rights reserved. + * + */ + +/** + * Drupal _menu() hook. + */ +function spam_filter_node_age_menu() { + $items = array(); + + $items['admin/settings/spam/filters/node-age'] = array( + 'title' => 'Node age', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_filter_node_age_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the node age filter.', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +function spam_filter_node_age_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_node_age', $type, $content, $fields, $extra)) return; + return spam_filter_node_age_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_node_age'; + + case 'filter_info': + return array( + 'name' => t('Node age'), + 'module' => t('spam_filter_node_age'), + 'description' => t('A node-age comment spam filter.'), + 'help' => t('The node-age filter assigns a higher spam probability to comments made against older nodes.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 150, + 'weight' => -2, + ); + + } +} + +/** + * Determine if a comment is being posted against too old of a node. + */ +function spam_filter_node_age_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + if ($type == 'comment') { + $action = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + if (arg(0) == 'comment' && arg(1) == 'reply' && is_numeric(arg(2))) { + $nid = arg(2); + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('retrieved nid (@nid) from url', array('@nid' => $nid)), $type, $id); + } + else { + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $id)); + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('retrieved nid (@nid) from database', array('@nid' => $nid)), $type, $id); + } + + if (!$nid) { + spam_log(SPAM_LOG, 'spam_filter_node_age_spam_filter', t('warning: nid not found for comment (@cid), skipping', array('@cid' => $id)), $type, $id); + $action['total'] = 0; + return $action; + } + + $node = spam_invoke_module('node', 'load', $nid); + if (is_object($node)) { + $timestamp_field = variable_get('spam_filter_node_age_filter_on', 'created'); + if ($node->$timestamp_field < (time() - variable_get('spam_filter_node_age_limit_long', 4838400))) { + $action['total'] = variable_get('spam_filter_node_age_weight_long', 99); + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('node (@nid) older than long limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); + } + else if ($node->$timestamp_field < (time() - variable_get('spam_filter_node_age_limit_short', 2419200))) { + $action['total'] = variable_get('spam_filter_node_age_weight_short', 85); + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('node (@nid) older than short limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); + } + else { + $action['total'] = 0; + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('node (@nid) is recent.', array('@nid' => $nid)), $type, $id); + } + } else { + spam_log(SPAM_LOG, 'spam_filter_node_age_spam_filter', t('warning: nid (@nid) does not map to node, skipping', array('@nid' => $nid)), $type, $id); + $action['total'] = 0; + } + } + else { + spam_log(SPAM_DEBUG, 'spam_filter_node_age_spam_filter', t('content type is not comment, skipping'), $type, $id); + $action['total'] = 0; + } + return $action; +} + +/** + * Module administrative configuration options. + */ +function spam_filter_node_age_admin_settings() { + $form = array(); + $form['short'] = array( + '#type' => 'fieldset', + '#title' => 'Old content', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $limits = drupal_map_assoc(spam_range(604800, 14515200, 604800), 'format_interval'); + $weights = drupal_map_assoc(array(60, 65, 70, 75, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)); + $form['short']['spam_filter_node_age_limit_short'] = array( + '#type' => 'select', + '#title' => t('Old content is content not published within the past'), + '#options' => $limits, + '#required' => TRUE, + '#default_value' => variable_get('spam_filter_node_age_limit_short', 2419200), + ); + $form['short']['spam_filter_node_age_weight_short'] = array( + '#type' => 'select', + '#title' => t('Probability that comments posted to old content is spam'), + '#options' => $weights, + '#required' => TRUE, + '#description' => t('Probability that comments posted to old content are spam, as a percentage.'), + '#default_value' => variable_get('spam_filter_node_age_weight_short', 85), + ); + + $form['long'] = array( + '#type' => 'fieldset', + '#title' => 'Really old content', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $form['long']['spam_filter_node_age_limit_long'] = array( + '#type' => 'select', + '#title' => t('Really old content is content not published within the past'), + '#options' => $limits, + '#required' => TRUE, + '#default_value' => variable_get('spam_filter_node_age_limit_long', 4838400), + ); + $form['long']['spam_filter_node_age_weight_long'] = array( + '#type' => 'select', + '#title' => t('Probability that comments posted to really old content is spam'), + '#options' => $weights, + '#required' => TRUE, + '#description' => t('Probability that comments posted to really old content are spam, as a percentage.'), + '#default_value' => variable_get('spam_filter_node_age_weight_long', 99), + ); + return system_settings_form($form); +} + +/** + * Validate the configuration. + */ +function spam_filter_node_age_admin_settings_validate($form, &$form_state) { + $limit_short = $form_state['values']['spam_filter_node_age_limit_short']; + $limit_long = $form_state['values']['spam_filter_node_age_limit_long']; + if ($limit_short >= $limit_long) { + form_set_error('spam_filter_node_age_limit_long', t('Really old content has to be older than old content.')); + } +} + +/** + * Save the configuration. + */ +function spam_filter_node_age_admin_settings_submit($form, &$form_state) { +/* TODO The 'op' element in the form values is deprecated. + Each button can have #validate and #submit functions associated with it. + Thus, there should be one button that submits the form and which invokes + the normal form_id_validate and form_id_submit handlers. Any additional + buttons which need to invoke different validate or submit functionality + should have button-specific functions. */ + if ($form_state['values']['op'] == t('Reset to defaults')) { + variable_del('spam_filter_node_age_limit_short'); + variable_del('spam_filter_node_age_weight_short'); + variable_del('spam_filter_node_age_limit_long'); + variable_del('spam_filter_node_age_weight_long'); + drupal_set_message('Configuration reset to defaults.'); + } + else { + variable_set('spam_filter_node_age_limit_short', $form_state['values']['spam_filter_node_age_limit_short']); + variable_set('spam_filter_node_age_weight_short', $form_state['values']['spam_filter_node_age_weight_short']); + variable_set('spam_filter_node_age_limit_long', $form_state['values']['spam_filter_node_age_limit_long']); + variable_set('spam_filter_node_age_weight_long', $form_state['values']['spam_filter_node_age_weight_long']); + drupal_set_message('Configuration saved.'); + } +} \ No newline at end of file diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.module~ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_node_age/spam_filter_node_age.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_node_age/spam_filter_node_age.module~ 2009-12-09 14:46:52.000000000 +1100 @@ -0,0 +1,196 @@ +. All rights reserved. + * + */ + +/** + * Drupal _menu() hook. + */ +function spam_node_age_menu() { + $items = array(); + + $items['admin/settings/spam/filters/node-age'] = array( + 'title' => 'Node age', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_node_age_admin_settings'), + 'access arguments' => array('administer spam'), + 'description' => 'Configure the node age filter.', + 'type' => MENU_LOCAL_TASK, + ); + + return $items; +} + +function spam_node_age_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_node_age', $type, $content, $fields, $extra)) return; + return spam_node_age_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_node_age'; + + case 'filter_info': + return array( + 'name' => t('Node age'), + 'module' => t('spam_node_age'), + 'description' => t('A node-age comment spam filter.'), + 'help' => t('The node-age filter assigns a higher spam probability to comments made against older nodes.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 150, + 'weight' => -2, + ); + + } +} + +/** + * Determine if a comment is being posted against too old of a node. + */ +function spam_node_age_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + if ($type == 'comment') { + $action = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + if (arg(0) == 'comment' && arg(1) == 'reply' && is_numeric(arg(2))) { + $nid = arg(2); + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('retrieved nid (@nid) from url', array('@nid' => $nid)), $type, $id); + } + else { + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $id)); + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('retrieved nid (@nid) from database', array('@nid' => $nid)), $type, $id); + } + + if (!$nid) { + spam_log(SPAM_LOG, 'spam_node_age_spam_filter', t('warning: nid not found for comment (@cid), skipping', array('@cid' => $id)), $type, $id); + $action['total'] = 0; + return $action; + } + + $node = spam_invoke_module('node', 'load', $nid); + if (is_object($node)) { + $timestamp_field = variable_get('spam_node_age_filter_on', 'created'); + if ($node->$timestamp_field < (time() - variable_get('spam_node_age_limit_long', 4838400))) { + $action['total'] = variable_get('spam_node_age_weight_long', 99); + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('node (@nid) older than long limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); + } + else if ($node->$timestamp_field < (time() - variable_get('spam_node_age_limit_short', 2419200))) { + $action['total'] = variable_get('spam_node_age_weight_short', 85); + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('node (@nid) older than short limit, spam probability(@weight)', array('@nid' => $nid, '@weight' => $action['total'])), $type, $id); + } + else { + $action['total'] = 0; + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('node (@nid) is recent.', array('@nid' => $nid)), $type, $id); + } + } else { + spam_log(SPAM_LOG, 'spam_node_age_spam_filter', t('warning: nid (@nid) does not map to node, skipping', array('@nid' => $nid)), $type, $id); + $action['total'] = 0; + } + } + else { + spam_log(SPAM_DEBUG, 'spam_node_age_spam_filter', t('content type is not comment, skipping'), $type, $id); + $action['total'] = 0; + } + return $action; +} + +/** + * Module administrative configuration options. + */ +function spam_node_age_admin_settings() { + $form = array(); + $form['short'] = array( + '#type' => 'fieldset', + '#title' => 'Old content', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $limits = drupal_map_assoc(spam_range(604800, 14515200, 604800), 'format_interval'); + $weights = drupal_map_assoc(array(60, 65, 70, 75, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)); + $form['short']['spam_node_age_limit_short'] = array( + '#type' => 'select', + '#title' => t('Old content is content not published within the past'), + '#options' => $limits, + '#required' => TRUE, + '#default_value' => variable_get('spam_node_age_limit_short', 2419200), + ); + $form['short']['spam_node_age_weight_short'] = array( + '#type' => 'select', + '#title' => t('Probability that comments posted to old content is spam'), + '#options' => $weights, + '#required' => TRUE, + '#description' => t('Probability that comments posted to old content are spam, as a percentage.'), + '#default_value' => variable_get('spam_node_age_weight_short', 85), + ); + + $form['long'] = array( + '#type' => 'fieldset', + '#title' => 'Really old content', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $form['long']['spam_node_age_limit_long'] = array( + '#type' => 'select', + '#title' => t('Really old content is content not published within the past'), + '#options' => $limits, + '#required' => TRUE, + '#default_value' => variable_get('spam_node_age_limit_long', 4838400), + ); + $form['long']['spam_node_age_weight_long'] = array( + '#type' => 'select', + '#title' => t('Probability that comments posted to really old content is spam'), + '#options' => $weights, + '#required' => TRUE, + '#description' => t('Probability that comments posted to really old content are spam, as a percentage.'), + '#default_value' => variable_get('spam_node_age_weight_long', 99), + ); + return system_settings_form($form); +} + +/** + * Validate the configuration. + */ +function spam_node_age_admin_settings_validate($form, &$form_state) { + $limit_short = $form_state['values']['spam_node_age_limit_short']; + $limit_long = $form_state['values']['spam_node_age_limit_long']; + if ($limit_short >= $limit_long) { + form_set_error('spam_node_age_limit_long', t('Really old content has to be older than old content.')); + } +} + +/** + * Save the configuration. + */ +function spam_node_age_admin_settings_submit($form, &$form_state) { +/* TODO The 'op' element in the form values is deprecated. + Each button can have #validate and #submit functions associated with it. + Thus, there should be one button that submits the form and which invokes + the normal form_id_validate and form_id_submit handlers. Any additional + buttons which need to invoke different validate or submit functionality + should have button-specific functions. */ + if ($form_state['values']['op'] == t('Reset to defaults')) { + variable_del('spam_node_age_limit_short'); + variable_del('spam_node_age_weight_short'); + variable_del('spam_node_age_limit_long'); + variable_del('spam_node_age_weight_long'); + drupal_set_message('Configuration reset to defaults.'); + } + else { + variable_set('spam_node_age_limit_short', $form_state['values']['spam_node_age_limit_short']); + variable_set('spam_node_age_weight_short', $form_state['values']['spam_node_age_weight_short']); + variable_set('spam_node_age_limit_long', $form_state['values']['spam_node_age_limit_long']); + variable_set('spam_node_age_weight_long', $form_state['values']['spam_node_age_weight_long']); + drupal_set_message('Configuration saved.'); + } +} \ No newline at end of file diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.info sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.info --- /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.info 2009-09-01 10:28:00.000000000 +1000 @@ -0,0 +1,12 @@ +; $Id: surbl.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ +name = Spam Surbl filter +description = A Surbl filter plug-in for the spam module. +package = Spam +dependencies[] = spam +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.module sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.module --- /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.module 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.module 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,151 @@ +. + * + */ + +/** + * http://www.surbl.org/lists.html#multi bitmap + */ +define('SPAM_FILTER_SURBL_SC', 2); +define('SPAM_FILTER_SURBL_WS', 4); +define('SPAM_FILTER_SURBL_PH', 8); +define('SPAM_FILTER_SURBL_OB', 16); +define('SPAM_FILTER_SURBL_AB', 32); +define('SPAM_FILTER_SURBL_JP', 64); + +/** + * Spam hook_spamapi implementation. + */ +function spam_filter_surbl_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_surbl', $type, $content, $fields, $extra)) return; + return spam_filter_surbl_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_surbl'; + + case 'filter_info': + return array( + 'name' => t('Surbl filter'), + 'module' => t('spam_filter_surbl'), + 'description' => t('A spam url filter.'), + 'help' => t('Look up URLs in SURBL to determine if is spam.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -7, + ); + + } +} + +/** + * Extract URLs from content. + */ +function _spam_filter_surbl_url_extract($content, $type, $fields, $extra = array()) { + static $urls = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($urls["$type-$id"])) { + $string = ''; + foreach ($fields['main'] as $field) { + $string .= $content["$field"] .' '; + } + if (is_array($fields['other'])) { + foreach ($fields['other'] as $field) { + $string .= $content["$field"] .' '; + } + } + + // TODO: Improve this matching. We don't actually extract mailto: urls. + $URI = "(http://|https://|ftp://|mailto:)"; + // Find all urls in content. + preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zAiption = A Surbl filter plug-in :%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); + $u = array(); + foreach ($matches[2] as $url) { + $url = preg_replace("'$URI'", '', $url); + // get full domain (ie www.sample.com) + preg_match("/^()?([^\/\"\']+)/i", $url, $domain); + // get root domain (ie sample.com) + preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); + $u[md5($root[0])] = htmlspecialchars(drupal_strtolower($root[0])); + } + $urls["$type-$id"] = $u; + } + + return $urls["$type-$id"]; +} + +/** + * Search for known spam urls in content. + */ +function spam_filter_surbl_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $action = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam = FALSE; + + $urls = _spam_filter_surbl_url_extract($content, $type, $fields, $extra); + + if (is_array($urls) && !empty($urls)) { + foreach ($urls as $url) { + $lookup = "$url.multi.surbl.org"; + $ip = gethostbyname($lookup); + if ($ip != $lookup) { + // this domain was in a SURBL, process accordingly + preg_match("/[^\.\/]+$/", $ip, $code); + if ($code[0] & SPAM_FILTER_SURBL_SC) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('SpamCop message-body URI domains'))), $type, $id); + } + if ($code[0] & SPAM_FILTER_SURBL_WS) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('sa-blacklist domains'))), $type, $id); + } + if ($code[0] & SPAM_FILTER_SURBL_PH) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Phishing data source'))), $type, $id); + } + if ($code[0] & SPAM_FILTER_SURBL_OB) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Outblaze URI blacklist'))), $type, $id); + } + if ($code[0] & SPAM_FILTER_SURBL_AB) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('AbuseButler spamvertised sites'))), $type, $id); + } + if ($code[0] & SPAM_FILTER_SURBL_JP) { + spam_log(SPAM_IMPORTANT, 'spam_filter_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('jwSpamSpy + Prolocation data source'))), $type, $id); + } + $action['spam_filter_surbl'][] = array( + 'url' => $url, + 'probability' => 99, + ); + $spam = TRUE; + } + else { + spam_log(SPAM_DEBUG, 'spam_filter_surbl_spam_filter', t('not spam url(@url)', array('@url' => $url)), $type, $id); + } + } + } + + if ($spam) { + $action['total'] = 99; + } + else { + $action['total'] = 0; + } + + return $action; +} + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.module~ sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_surbl/spam_filter_surbl.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_surbl/spam_filter_surbl.module~ 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,151 @@ +. + * + */ + +/** + * http://www.surbl.org/lists.html#multi bitmap + */ +define('SPAM_SURBL_SC', 2); +define('SPAM_SURBL_WS', 4); +define('SPAM_SURBL_PH', 8); +define('SPAM_SURBL_OB', 16); +define('SPAM_SURBL_AB', 32); +define('SPAM_SURBL_JP', 64); + +/** + * Spam hook_spamapi implementation. + */ +function spam_surbl_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_surbl', $type, $content, $fields, $extra)) return; + return spam_surbl_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_surbl'; + + case 'filter_info': + return array( + 'name' => t('Surbl filter'), + 'module' => t('spam_surbl'), + 'description' => t('A spam url filter.'), + 'help' => t('Look up URLs in SURBL to determine if is spam.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -7, + ); + + } +} + +/** + * Extract URLs from content. + */ +function _spam_surbl_url_extract($content, $type, $fields, $extra = array()) { + static $urls = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($urls["$type-$id"])) { + $string = ''; + foreach ($fields['main'] as $field) { + $string .= $content["$field"] .' '; + } + if (is_array($fields['other'])) { + foreach ($fields['other'] as $field) { + $string .= $content["$field"] .' '; + } + } + + // TODO: Improve this matching. We don't actually extract mailto: urls. + $URI = "(http://|https://|ftp://|mailto:)"; + // Find all urls in content. + preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zA-Z0-9@:%_~#?&=.,/;-]*[a-zA-Z0-9@:%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); + $u = array(); + foreach ($matches[2] as $url) { + $url = preg_replace("'$URI'", '', $url); + // get full domain (ie www.sample.com) + preg_match("/^()?([^\/\"\']+)/i", $url, $domain); + // get root domain (ie sample.com) + preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); + $u[md5($root[0])] = htmlspecialchars(drupal_strtolower($root[0])); + } + $urls["$type-$id"] = $u; + } + + return $urls["$type-$id"]; +} + +/** + * Search for known spam urls in content. + */ +function spam_surbl_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $action = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam = FALSE; + + $urls = _spam_surbl_url_extract($content, $type, $fields, $extra); + + if (is_array($urls) && !empty($urls)) { + foreach ($urls as $url) { + $lookup = "$url.multi.surbl.org"; + $ip = gethostbyname($lookup); + if ($ip != $lookup) { + // this domain was in a SURBL, process accordingly + preg_match("/[^\.\/]+$/", $ip, $code); + if ($code[0] & SPAM_SURBL_SC) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('SpamCop message-body URI domains'))), $type, $id); + } + if ($code[0] & SPAM_SURBL_WS) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('sa-blacklist domains'))), $type, $id); + } + if ($code[0] & SPAM_SURBL_PH) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Phishing data source'))), $type, $id); + } + if ($code[0] & SPAM_SURBL_OB) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Outblaze URI blacklist'))), $type, $id); + } + if ($code[0] & SPAM_SURBL_AB) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('AbuseButler spamvertised sites'))), $type, $id); + } + if ($code[0] & SPAM_SURBL_JP) { + spam_log(SPAM_IMPORTANT, 'spam_surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('jwSpamSpy + Prolocation data source'))), $type, $id); + } + $action['spam_surbl'][] = array( + 'url' => $url, + 'probability' => 99, + ); + $spam = TRUE; + } + else { + spam_log(SPAM_DEBUG, 'spam_surbl_spam_filter', t('not spam url(@url)', array('@url' => $url)), $type, $id); + } + } + } + + if ($spam) { + $action['total'] = 99; + } + else { + $action['total'] = 0; + } + + return $action; +} + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.info sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.info --- /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.info 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.info 2009-12-09 14:06:14.000000000 +1100 @@ -0,0 +1,13 @@ +; $Id: url.info,v 1.1.2.1.2.1 2008/12/25 05:42:11 jeremy Exp $ +name = Spam URL filter +description = A URL filter plug-in for the spam module. +package = Spam +dependencies[] = spam +dependencies[] = spam_filter_bayesian +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.info~ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.info~ --- /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.info~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.info~ 2009-12-09 14:06:14.000000000 +1100 @@ -0,0 +1,13 @@ +; $Id: url.info,v 1.1.2.1.2.1 2008/12/25 05:42:11 jeremy Exp $ +name = Spam URL filter +description = A URL filter plug-in for the spam module. +package = Spam +dependencies[] = spam +dependencies[] = spam_bayesian +core = 6.x +; Information added by drupal.org packaging script on 2009-09-01 +version = "6.x-1.x-dev" +core = "6.x" +project = "spam" +datestamp = "1251764880" + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.install sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.install --- /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.install 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.install 2009-12-09 14:32:27.000000000 +1100 @@ -0,0 +1,26 @@ +. All rights reserved. + * + */ + +function spam_filter_url_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + // Don't both with this hook unless the filter is actually enabled. + + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_url', $type, $content, $fields, $extra)) return; + return spam_filter_url_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_url'; + + case 'filter_info': + return array( + 'name' => t('URL filter'), + 'module' => t('spam_filter_url'), + 'description' => t('A spam url filter.'), + 'help' => t('The url filter blocks posts containing spam-URLs, automatically learned with the bayesian filter module.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -6, + ); + + case 'mark_as_spam': + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_url', $type, $content, $fields, $extra)) return; + spam_log(SPAM_DEBUG, 'spam_filter_url_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $spam_filter_urls = _spam_filter_url_extract($extra['content'], $type, $fields, $extra); + spam_filter_url_update($spam_filter_urls, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); + break; + } +} + +function _spam_filter_url_extract($content, $type, $fields, $extra = array()) { + static $spam_filter_urls = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($spam_filter_urls["$type-$id"])) { + $string = ''; + foreach ($fields['main'] as $field) { + $string .= $content["$field"] .' '; + } + if (is_array($fields['other'])) { + foreach ($fields['other'] as $field) { + $string .= $content["$field"] .' '; + } + } + + // TODO: Improve this matching. We don't actually extract mailto: urls. + $URI = "(http://|https://|ftp://|mailto:)"; + // Find all urls in content. + preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zA-Z0-9@:%_~#?&=.,/;-]*[a-zA-Z0-9@:%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); + foreach ($matches[2] as $spam_filter_url) { + $spam_filter_url = preg_replace("'$URI'", '', $spam_filter_url); + // get full domain (ie www.sample.com) + preg_match("/^()?([^\/\"\']+)/i", $spam_filter_url, $domain); + // get root domain (ie sample.com) + preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); + $spam_filter_url = htmlspecialchars(drupal_strtolower($root[0])); + _spam_filter_url_count($spam_filter_url); + $u[] = $spam_filter_url; + } + $spam_filter_urls["$type-$id"] = $u; + } + + return $spam_filter_urls["$type-$id"]; +} + +/** + * Search for known spam urls in content. + */ +function spam_filter_url_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $action = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam = FALSE; + + $spam_filter_urls = _spam_filter_url_extract($content, $type, $fields, $extra); + + if (is_array($spam_filter_urls) && !empty($spam_filter_urls)) { + $count = _spam_filter_url_count(); + + $limit = variable_get('spam_filter_url_limit_total', 10); + if ($limit > -1 && $count['total'] > $limit) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('total urls(@total) > spam_filter_url_limit_total(@limit)', array('@total' => $count['total'], '@limit' => variable_get('spam_filter_url_limit_total', 10))), $type, $id); + $action['spam_filter_url'][] = array( + 'limit' => 'total', + 'total' => $count['total'], + ); + $action['total'] = 99; + return $action; + } + $limit = variable_get('spam_filter_url_limit_repeat', 5); + if ($limit > -1) { + // Sort urls from most repeated to least repeated. + asort($count); + + // skip count['total'] + array_pop($count); + + $max = array_pop($count); + if ($max > $limit) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('repeated urls(@total) > spam_filter_url_limit_repeat(@limit)', array('@total' => $max, '@limit' => variable_get('spam_filter_url_limit_repeat', 5))), $type, $id); + $action['spam_filter_url'][] = array( + 'limit' => 'repeat', + 'total' => $max, + ); + $action['total'] = 99; + } + } + + foreach ($spam_filter_urls as $spam_filter_url) { + $p = db_fetch_object(db_query("SELECT probability FROM {spam_filter_bayesian_tokens} WHERE class = 'spam_filter_url' AND token = '%s'", $spam_filter_url)); + $action['spam_filter_url'][] = array( + 'spam_filter_url' => $spam_filter_url, + 'probability' => $p->probability, + ); + if ($p->probability >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('found spam url(@url) probability(@probability)', array('@url' => $spam_filter_url, '@probability' => $p->probability)), $type, $id); + $spam = TRUE; + break; + } + spam_log(SPAM_DEBUG, 'spam_filter_url_spam_filter', t('not spam url(@url) probability(@probability)', array('@url' => $spam_filter_url, '@probability' => $p->probability)), $type, $id); + } + } + + if ($spam) { + $action['total'] = 99; + } + else { + $action['total'] = 0; + } + + return $action; +} + +/** + * Update url probabilities in database. + */ +function spam_filter_url_update($spam_filter_urls, $yes, $type, $id) { + module_invoke('spam_filter_bayesian', 'tokens_update', 'spam_filter_url', $spam_filter_urls, $yes, $type, $id); +} + +/** + * Keep track of the total number of URLs found in the current content. + * + * @param $spam_filter_url A URL to be added to a static array. + * @return Array of URLs showing how many times each URL is present, and + * the total number of arrays. + */ +function _spam_filter_url_count($spam_filter_url = NULL) { + // build up an array of all URLs seen in current content + static $spam_filter_urls = array(); + + if ($spam_filter_url != NULL) { + $spam_filter_urls["$spam_filter_url"]++; + $spam_filter_urls['total']++; + } + + return $spam_filter_urls; +} + diff -uprN /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.module~ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.module~ --- /home/files/coding/drupal/spam/filters/spam_filter_url/spam_filter_url.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/filters/spam_filter_url/spam_filter_url.module~ 2009-12-09 14:49:00.000000000 +1100 @@ -0,0 +1,182 @@ +. All rights reserved. + * + */ + +function spam_filter_url_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { + // Don't both with this hook unless the filter is actually enabled. + + switch ($op) { + case 'filter': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_url', $type, $content, $fields, $extra)) return; + return spam_filter_url_spam_filter($content, $type, $fields, $extra); + + case 'filter_module': + return 'spam_filter_url'; + + case 'filter_info': + return array( + 'name' => t('URL filter'), + 'module' => t('spam_filter_url'), + 'description' => t('A spam url filter.'), + 'help' => t('The url filter blocks posts containing spam-URLs, automatically learned with the bayesian filter module.'), + ); + break; + + case 'filter_install': + return array( + 'status' => SPAM_FILTER_ENABLED, + 'gain' => 250, + 'weight' => -6, + ); + + case 'mark_as_spam': + case 'mark_as_not_spam': + if (!module_invoke('spam', 'filter_enabled', 'spam_filter_url', $type, $content, $fields, $extra)) return; + spam_log(SPAM_DEBUG, 'spam_filter_url_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); + $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); + $spam_filter_urls = _spam_filter_url_extract($extra['content'], $type, $fields, $extra); + spam_filter_url_update($spam_filter_urls, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); + break; + } +} + +function _spam_filter_url_extract($content, $type, $fields, $extra = array()) { + static $spam_filter_urls = array(); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + + if (is_object($content)) { + $content = (array)$content; + } + + if (!isset($spam_filter_urls["$type-$id"])) { + $string = ''; + foreach ($fields['main'] as $field) { + $string .= $content["$field"] .' '; + } + if (is_array($fields['other'])) { + foreach ($fields['other'] as $field) { + $string .= $content["$field"] .' '; + } + } + + // TODO: Improve this matching. We don't actually extract mailto: urls. + $URI = "(http://|https://|ftp://|mailto:)"; + // Find all urls in content. + preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zA-Z0-9@:%_~#?&=.,/;-]*[a-zA-Z0-9@:%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); + foreach ($matches[2] as $spam_filter_url) { + $spam_filter_url = preg_replace("'$URI'", '', $spam_filter_url); + // get full domain (ie www.sample.com) + preg_match("/^()?([^\/\"\']+)/i", $spam_filter_url, $domain); + // get root domain (ie sample.com) + preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); + $spam_filter_url = htmlspecialchars(drupal_strtolower($root[0])); + _spam_filter_url_count($spam_filter_url); + $u[] = $spam_filter_url; + } + $spam_filter_urls["$type-$id"] = $u; + } + + return $spam_filter_urls["$type-$id"]; +} + +/** + * Search for known spam urls in content. + */ +function spam_filter_url_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { + $action = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + $spam = FALSE; + + $spam_filter_urls = _spam_filter_url_extract($content, $type, $fields, $extra); + + if (is_array($spam_filter_urls) && !empty($spam_filter_urls)) { + $count = _spam_filter_url_count(); + + $limit = variable_get('spam_filter_url_limit_total', 10); + if ($limit > -1 && $count['total'] > $limit) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('total urls(@total) > spam_filter_url_limit_total(@limit)', array('@total' => $count['total'], '@limit' => variable_get('spam_filter_url_limit_total', 10))), $type, $id); + $action['spam_filter_url'][] = array( + 'limit' => 'total', + 'total' => $count['total'], + ); + $action['total'] = 99; + return $action; + } + $limit = variable_get('spam_filter_url_limit_repeat', 5); + if ($limit > -1) { + // Sort urls from most repeated to least repeated. + asort($count); + + // skip count['total'] + array_pop($count); + + $max = array_pop($count); + if ($max > $limit) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('repeated urls(@total) > spam_filter_url_limit_repeat(@limit)', array('@total' => $max, '@limit' => variable_get('spam_filter_url_limit_repeat', 5))), $type, $id); + $action['spam_filter_url'][] = array( + 'limit' => 'repeat', + 'total' => $max, + ); + $action['total'] = 99; + } + } + + foreach ($spam_filter_urls as $spam_filter_url) { + $p = db_fetch_object(db_query("SELECT probability FROM {spam_bayesian_tokens} WHERE class = 'spam_filter_url' AND token = '%s'", $spam_filter_url)); + $action['spam_filter_url'][] = array( + 'spam_filter_url' => $spam_filter_url, + 'probability' => $p->probability, + ); + if ($p->probability >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + spam_log(SPAM_VERBOSE, 'spam_filter_url_spam_filter', t('found spam url(@url) probability(@probability)', array('@url' => $spam_filter_url, '@probability' => $p->probability)), $type, $id); + $spam = TRUE; + break; + } + spam_log(SPAM_DEBUG, 'spam_filter_url_spam_filter', t('not spam url(@url) probability(@probability)', array('@url' => $spam_filter_url, '@probability' => $p->probability)), $type, $id); + } + } + + if ($spam) { + $action['total'] = 99; + } + else { + $action['total'] = 0; + } + + return $action; +} + +/** + * Update url probabilities in database. + */ +function spam_filter_url_update($spam_filter_urls, $yes, $type, $id) { + module_invoke('spam_bayesian', 'tokens_update', 'spam_filter_url', $spam_filter_urls, $yes, $type, $id); +} + +/** + * Keep track of the total number of URLs found in the current content. + * + * @param $spam_filter_url A URL to be added to a static array. + * @return Array of URLs showing how many times each URL is present, and + * the total number of arrays. + */ +function _spam_filter_url_count($spam_filter_url = NULL) { + // build up an array of all URLs seen in current content + static $spam_filter_urls = array(); + + if ($spam_filter_url != NULL) { + $spam_filter_urls["$spam_filter_url"]++; + $spam_filter_urls['total']++; + } + + return $spam_filter_urls; +} + diff -uprN /home/files/coding/drupal/spam/filters/surbl/surbl.info sites/all/modules/spam/filters/surbl/surbl.info --- /home/files/coding/drupal/spam/filters/surbl/surbl.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/surbl/surbl.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,12 +0,0 @@ -; $Id: surbl.info,v 1.1.2.1.2.1 2008/12/25 05:42:10 jeremy Exp $ -name = Spam Surbl filter -description = A Surbl filter plug-in for the spam module. -package = Spam -dependencies[] = spam -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/surbl/surbl.module sites/all/modules/spam/filters/surbl/surbl.module --- /home/files/coding/drupal/spam/filters/surbl/surbl.module 2008-12-25 16:42:10.000000000 +1100 +++ sites/all/modules/spam/filters/surbl/surbl.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,151 +0,0 @@ -. - * - */ - -/** - * http://www.surbl.org/lists.html#multi bitmap - */ -define('SURBL_SC', 2); -define('SURBL_WS', 4); -define('SURBL_PH', 8); -define('SURBL_OB', 16); -define('SURBL_AB', 32); -define('SURBL_JP', 64); - -/** - * Spam hook_spamapi implementation. - */ -function surbl_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - switch ($op) { - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'surbl', $type, $content, $fields, $extra)) return; - return surbl_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'surbl'; - - case 'filter_info': - return array( - 'name' => t('Surbl filter'), - 'module' => t('surbl'), - 'description' => t('A spam url filter.'), - 'help' => t('Look up URLs in SURBL to determine if is spam.'), - ); - break; - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - 'gain' => 250, - 'weight' => -7, - ); - - } -} - -/** - * Extract URLs from content. - */ -function _surbl_url_extract($content, $type, $fields, $extra = array()) { - static $urls = array(); - $id = spam_invoke_module($type, 'content_id', $content, $extra); - - if (is_object($content)) { - $content = (array)$content; - } - - if (!isset($urls["$type-$id"])) { - $string = ''; - foreach ($fields['main'] as $field) { - $string .= $content["$field"] .' '; - } - if (is_array($fields['other'])) { - foreach ($fields['other'] as $field) { - $string .= $content["$field"] .' '; - } - } - - // TODO: Improve this matching. We don't actually extract mailto: urls. - $URI = "(http://|https://|ftp://|mailto:)"; - // Find all urls in content. - preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zA-Z0-9@:%_~#?&=.,/;-]*[a-zA-Z0-9@:%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); - $u = array(); - foreach ($matches[2] as $url) { - $url = preg_replace("'$URI'", '', $url); - // get full domain (ie www.sample.com) - preg_match("/^()?([^\/\"\']+)/i", $url, $domain); - // get root domain (ie sample.com) - preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); - $u[md5($root[0])] = htmlspecialchars(drupal_strtolower($root[0])); - } - $urls["$type-$id"] = $u; - } - - return $urls["$type-$id"]; -} - -/** - * Search for known spam urls in content. - */ -function surbl_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - $action = array(); - - $id = spam_invoke_module($type, 'content_id', $content, $extra); - $spam = FALSE; - - $urls = _surbl_url_extract($content, $type, $fields, $extra); - - if (is_array($urls) && !empty($urls)) { - foreach ($urls as $url) { - $lookup = "$url.multi.surbl.org"; - $ip = gethostbyname($lookup); - if ($ip != $lookup) { - // this domain was in a SURBL, process accordingly - preg_match("/[^\.\/]+$/", $ip, $code); - if ($code[0] & SURBL_SC) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('SpamCop message-body URI domains'))), $type, $id); - } - if ($code[0] & SURBL_WS) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('sa-blacklist domains'))), $type, $id); - } - if ($code[0] & SURBL_PH) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Phishing data source'))), $type, $id); - } - if ($code[0] & SURBL_OB) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('Outblaze URI blacklist'))), $type, $id); - } - if ($code[0] & SURBL_AB) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('AbuseButler spamvertised sites'))), $type, $id); - } - if ($code[0] & SURBL_JP) { - spam_log(SPAM_IMPORTANT, 'surbl_spam_filter', t('found spam url(@url) @surbl', array('@url' => $url, '@surbl' => t('jwSpamSpy + Prolocation data source'))), $type, $id); - } - $action['surbl'][] = array( - 'url' => $url, - 'probability' => 99, - ); - $spam = TRUE; - } - else { - spam_log(SPAM_DEBUG, 'surbl_spam_filter', t('not spam url(@url)', array('@url' => $url)), $type, $id); - } - } - } - - if ($spam) { - $action['total'] = 99; - } - else { - $action['total'] = 0; - } - - return $action; -} - diff -uprN /home/files/coding/drupal/spam/filters/url/url.info sites/all/modules/spam/filters/url/url.info --- /home/files/coding/drupal/spam/filters/url/url.info 2009-09-01 10:28:00.000000000 +1000 +++ sites/all/modules/spam/filters/url/url.info 1970-01-01 10:00:00.000000000 +1000 @@ -1,13 +0,0 @@ -; $Id: url.info,v 1.1.2.1.2.1 2008/12/25 05:42:11 jeremy Exp $ -name = Spam URL filter -description = A URL filter plug-in for the spam module. -package = Spam -dependencies[] = spam -dependencies[] = bayesian -core = 6.x -; Information added by drupal.org packaging script on 2009-09-01 -version = "6.x-1.x-dev" -core = "6.x" -project = "spam" -datestamp = "1251764880" - diff -uprN /home/files/coding/drupal/spam/filters/url/url.module sites/all/modules/spam/filters/url/url.module --- /home/files/coding/drupal/spam/filters/url/url.module 2008-12-25 16:42:11.000000000 +1100 +++ sites/all/modules/spam/filters/url/url.module 1970-01-01 10:00:00.000000000 +1000 @@ -1,182 +0,0 @@ -. All rights reserved. - * - */ - -function url_spamapi($op, $type = NULL, $content = array(), $fields = array(), $extra = NULL) { - // Don't both with this hook unless the filter is actually enabled. - - switch ($op) { - case 'filter': - if (!module_invoke('spam', 'filter_enabled', 'url', $type, $content, $fields, $extra)) return; - return url_spam_filter($content, $type, $fields, $extra); - - case 'filter_module': - return 'url'; - - case 'filter_info': - return array( - 'name' => t('URL filter'), - 'module' => t('url'), - 'description' => t('A spam url filter.'), - 'help' => t('The url filter blocks posts containing spam-URLs, automatically learned with the bayesian filter module.'), - ); - break; - - case 'filter_install': - return array( - 'status' => SPAM_FILTER_ENABLED, - 'gain' => 250, - 'weight' => -6, - ); - - case 'mark_as_spam': - case 'mark_as_not_spam': - if (!module_invoke('spam', 'filter_enabled', 'url', $type, $content, $fields, $extra)) return; - spam_log(SPAM_DEBUG, 'url_spamapi', t('@op', array('@op' => $op)), $type, $extra['id']); - $fields = spam_invoke_module($type, 'filter_fields', $extra['content']); - $urls = _url_extract($extra['content'], $type, $fields, $extra); - url_update($urls, ($op == 'mark_as_spam' ? TRUE : FALSE), $type, $extra['id']); - break; - } -} - -function _url_extract($content, $type, $fields, $extra = array()) { - static $urls = array(); - $id = spam_invoke_module($type, 'content_id', $content, $extra); - - if (is_object($content)) { - $content = (array)$content; - } - - if (!isset($urls["$type-$id"])) { - $string = ''; - foreach ($fields['main'] as $field) { - $string .= $content["$field"] .' '; - } - if (is_array($fields['other'])) { - foreach ($fields['other'] as $field) { - $string .= $content["$field"] .' '; - } - } - - // TODO: Improve this matching. We don't actually extract mailto: urls. - $URI = "(http://|https://|ftp://|mailto:)"; - // Find all urls in content. - preg_match_all("!(

|[ \n\r\t\(]*)($URI([a-zA-Z0-9@:%_~#?&=.,/;-]*[a-zA-Z0-9@:%_~#&=/;-]))([.,?]?)(?=(

|[ \n\r\t\)]*))!i", $string, $matches); - foreach ($matches[2] as $url) { - $url = preg_replace("'$URI'", '', $url); - // get full domain (ie www.sample.com) - preg_match("/^()?([^\/\"\']+)/i", $url, $domain); - // get root domain (ie sample.com) - preg_match("/[^\.\/]+\.[^\.\/]+$/", $domain[2], $root); - $url = htmlspecialchars(drupal_strtolower($root[0])); - _url_count($url); - $u[] = $url; - } - $urls["$type-$id"] = $u; - } - - return $urls["$type-$id"]; -} - -/** - * Search for known spam urls in content. - */ -function url_spam_filter($content, $type, $fields, $extra = array(), $filter_test = FALSE) { - $action = array(); - - $id = spam_invoke_module($type, 'content_id', $content, $extra); - $spam = FALSE; - - $urls = _url_extract($content, $type, $fields, $extra); - - if (is_array($urls) && !empty($urls)) { - $count = _url_count(); - - $limit = variable_get('url_limit_total', 10); - if ($limit > -1 && $count['total'] > $limit) { - spam_log(SPAM_VERBOSE, 'url_spam_filter', t('total urls(@total) > url_limit_total(@limit)', array('@total' => $count['total'], '@limit' => variable_get('url_limit_total', 10))), $type, $id); - $action['url'][] = array( - 'limit' => 'total', - 'total' => $count['total'], - ); - $action['total'] = 99; - return $action; - } - $limit = variable_get('url_limit_repeat', 5); - if ($limit > -1) { - // Sort urls from most repeated to least repeated. - asort($count); - - // skip count['total'] - array_pop($count); - - $max = array_pop($count); - if ($max > $limit) { - spam_log(SPAM_VERBOSE, 'url_spam_filter', t('repeated urls(@total) > url_limit_repeat(@limit)', array('@total' => $max, '@limit' => variable_get('url_limit_repeat', 5))), $type, $id); - $action['url'][] = array( - 'limit' => 'repeat', - 'total' => $max, - ); - $action['total'] = 99; - } - } - - foreach ($urls as $url) { - $p = db_fetch_object(db_query("SELECT probability FROM {bayesian_tokens} WHERE class = 'url' AND token = '%s'", $url)); - $action['url'][] = array( - 'url' => $url, - 'probability' => $p->probability, - ); - if ($p->probability >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { - spam_log(SPAM_VERBOSE, 'url_spam_filter', t('found spam url(@url) probability(@probability)', array('@url' => $url, '@probability' => $p->probability)), $type, $id); - $spam = TRUE; - break; - } - spam_log(SPAM_DEBUG, 'url_spam_filter', t('not spam url(@url) probability(@probability)', array('@url' => $url, '@probability' => $p->probability)), $type, $id); - } - } - - if ($spam) { - $action['total'] = 99; - } - else { - $action['total'] = 0; - } - - return $action; -} - -/** - * Update url probabilities in database. - */ -function url_update($urls, $yes, $type, $id) { - module_invoke('bayesian', 'tokens_update', 'url', $urls, $yes, $type, $id); -} - -/** - * Keep track of the total number of URLs found in the current content. - * - * @param $url A URL to be added to a static array. - * @return Array of URLs showing how many times each URL is present, and - * the total number of arrays. - */ -function _url_count($url = NULL) { - // build up an array of all URLs seen in current content - static $urls = array(); - - if ($url != NULL) { - $urls["$url"]++; - $urls['total']++; - } - - return $urls; -} - diff -uprN /home/files/coding/drupal/spam/modules/spam_comment.inc sites/all/modules/spam/modules/spam_comment.inc --- /home/files/coding/drupal/spam/modules/spam_comment.inc 2009-08-07 06:37:19.000000000 +1000 +++ sites/all/modules/spam/modules/spam_comment.inc 1970-01-01 10:00:00.000000000 +1000 @@ -1,387 +0,0 @@ -cid))) { - $comment->status = COMMENT_NOT_PUBLISHED; - } - break; - } -} - -/** - * Cache the comment id to be sure it's available when we need it. - */ -function _spam_comment_cid($id = NULL) { - static $cid = 0; - - if (isset($id) && is_numeric($id)) { - $cid = $id; - } - - return $cid; -} - -/** - * Spam module _spamapi() hook. - */ -function comment_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { - switch ($op) { - case 'content_module': - // Register with the spam api as a content type module. - return 'comment'; - - case 'content_id': - // Tell the spam module the id of a given comment. - if (is_object($arg1)) { - // The delete hook uses an object instead of an array. - $arg1 = (array)$arg1; - } - return _spam_comment_cid($arg1['cid']); - - case 'content_types': - // Register the "comment" content type with the spam module. - return array(array( - 'name' => t('comments'), - 'module' => t('comment'), - 'title' => t('Comments'), - 'description' => t('Check this box to filter comments for spam.'), - 'default_value' => 1, - )); - - case 'filter_content_type': - return (variable_get('spam_filter_comments', 1)); - - case 'filter_fields': - // Tell spam module which fields it should scan for spam. - $fields['main'] = array('subject', 'comment'); - if (is_object($arg1)) { - // The delete hook uses an object instead of an array. - $arg1 = (array)$arg1; - } - if (isset($arg1['author'])) { - $fields['other'][] = 'author'; - } - if (isset($arg1['name'])) { - $fields['other'][] = 'name'; - } - if (isset($arg1['mail'])) { - $fields['other'][] = 'mail'; - } - if (isset($arg1['homepage'])) { - $fields['other'][] = 'homepage'; - } - return $fields; - - case 'feedback_form': - $form = array(); - if (is_numeric($form['cid'])) { - $form['cid'] = array( - '#type' => 'textfield', - '#title' => t('Comment ID'), - '#value' => $arg1['cid'], - '#disabled' => TRUE, - ); - } - // fall through... - case 'error_form': - if (!is_array($form)) { - $form = array(); - } - $form['comment'] = array( - '#type' => 'fieldset', - '#title' => 'Comment', - ); - $form['comment']['title'] = array( - '#type' => 'textfield', - '#title' => t('Subject'), - '#value' => $arg1['subject'], - '#disabled' => TRUE, - ); - $form['comment']['body'] = array( - '#type' => 'textarea', - '#title' => t('Comment'), - '#value' => $arg1['comment'], - '#disabled' => TRUE, - ); - $form['comment']['author'] = array( - '#type' => 'markup', - '#prefix' => '
' . t('Author') . ':
', - '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), - ); - return $form; - - case 'load': - return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $arg1)); - - case 'title': - return db_result(db_query('SELECT subject FROM {comments} WHERE cid = %d', $arg1)); - - case 'edit_link': - return "comment/edit/$arg1"; - - case 'status': - $status = db_result(db_query('SELECT status FROM {comments} WHERE cid = %d', $arg1)); - if ($status == COMMENT_PUBLISHED) { - return SPAM_PUBLISHED; - } - else { - return SPAM_NOT_PUBLISHED; - } - - case 'hostname': - return db_result(db_query('SELECT hostname FROM {comments} WHERE cid = %d', $arg1)); - - case 'link': - if (is_object($arg1) && isset($arg1->cid)) { - return spam_links('comment', $arg1->cid, $arg1); - } - break; - - case 'redirect': - $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $arg1)); - return drupal_goto("/node/$nid", NULL, "comment-$arg1"); - - case 'overview_filter_join': - return 'INNER JOIN {comments} c ON t.content_id = c.cid'; - - case 'overview_filter_where': - switch ($arg1) { - case 'title': - return "c.subject LIKE '%%%s%%'"; - case 'status': - return 'c.status = %d'; - } - - case 'menu': - // Create page for listing all spam comments in comment admin section. - $items = array(); - $items['admin/content/comment/list/spam'] = array( - 'title' => t('Spam'), - 'page callback' => 'spam_comment_admin', - 'access arguments' => array('administer spam'), - //TODO: remove dependencies on comment.admin.inc? - 'file' => 'comment.admin.inc', - 'file path' => drupal_get_path('module', 'comment'), - 'type' => MENU_LOCAL_TASK, - ); - return $items; - - case 'publish': - // Only update comments that exist and need to be published. - $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d OR status = %d', $arg1, COMMENT_NOT_PUBLISHED, SPAM_COMMENT)); - if ($nid) { - db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_PUBLISHED, $arg1); - _comment_update_node_statistics($nid); - } - break; - - case 'unpublish': - // Only update comments that exist and need to be unpublished. - // When 'unpublishing' comments, we actually mark them as spam. - $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d', $arg1, COMMENT_PUBLISHED)); - if ($nid) { - db_query('UPDATE {comments} SET status = %d WHERE cid = %d', SPAM_COMMENT, $arg1); - _comment_update_node_statistics($nid); - } - break; - - case 'theme_forms': - // Add spam comments admin form to spam.module's hook_theme. - return array( - 'spam_comment_admin_overview' => array( - 'file' => 'modules/spam_comment.inc', - 'arguments' => array( - 'form' => NULL, - ), - ), - ); - } -} - -/** - * Form alter gets its own function so we can reference &$form without causing - * errors in PHP4 installations. (If we use spamapi, we have to set a default, - * which PHP4 doesn't support.) - */ -function comment_spamapi_form_alter(&$form, &$form_state, $form_id) { - // Scan comments before they are inserted into the database - if ($form_id == 'comment_form' && is_array($form)) { - $form['#validate'][] = 'comment_spam_scan'; - } - // Add spam options to comment form - else if ($form_id == 'comment_admin_overview' || $form_id == 'spam_comment_admin_overview') { - $form['#spam_submit_valuetype'] = 'comments'; - $form['#spam_submit_itemtype'] = 'comment'; - $form['#submit'] = array_merge(array('spam_admin_overview_submit'), $form['#submit']); - $parameters = $form['#parameters']; - if (is_array($parameters)) { - if (!in_array('new', $parameters)) { - $form['options']['operation']['#options']['markasnotspam'] = 'Mark the selected comments as not spam'; - } - if (!in_array('spam', $parameters)) { - $form['options']['operation']['#options']['markasspam'] = 'Mark the selected comments as spam'; - } - if (in_array('new', $parameters)) { - $form['options']['operation']['#options']['teachnotspam'] = 'Teach filters selected comments are not spam'; - } - } - if (is_array($form['#post']) && is_array($form['#post']['comments'])) { - foreach ($form['#post']['comments'] as $cid) { - $score = _spam_comment_score($cid); - if ($form['#post']['operation'] == 'markasspam') { - if (!spam_score_is_spam($score)) { - spam_mark_as_spam('comment', $cid); - } - } - else if ($form['#post']['operation'] == 'markasnotspam') { - if (spam_score_is_spam($score)) { - spam_mark_as_not_spam('comment', $cid); - } - } - else if ($form['#post']['operation'] == 'teachnotspam') { - spam_mark_as_not_spam('comment', $cid); - } - } - } - } -} - -/** - * Retreive spam score, caching in memory for repeated use. - */ -function _spam_comment_score($cid) { - static $scores = array(); - - if (!isset($scores[$cid])) { - $scores[$cid] = db_result(db_query("SELECT score FROM {spam_tracker} WHERE content_type = 'comment' AND content_id = %d", $cid)); - } - - return $scores[$cid]; -} - -/** - * Menu callback; present an administrative spam comment listing. - */ -function spam_comment_admin() { - $edit = $_POST; - - if ($edit['operation'] == 'delete' && $edit['comments']) { - return drupal_get_form('comment_multiple_delete_confirm'); - } - else { - return drupal_get_form('spam_comment_admin_overview', arg(4)); - } -} - -/** - * The 5.x version of comment_admin_overview doesn't provide hooks to easily - * create a spam overview page. This is nearly an exact copy of that function, - * with minor changes for loading spam and processing it. - */ -function spam_comment_admin_overview(&$form_state, $arg) { - // build an 'Update options' form - $form['options'] = array( - '#type' => 'fieldset', '#title' => t('Update options'), - '#prefix' => '
', '#suffix' => '
' - ); - $options = array( - 'markasnotspam' => t('Mark the selected comments as not spam'), - 'delete' => t('Delete the selected comments'), - ); - - $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'markasnotspam'); - $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update')); - - // load the comments that we want to display - $form['header'] = array('#type' => 'value', '#value' => array( - theme('table_select_header_cell'), - array('data' => t('Subject'), 'field' => 'subject'), - array('data' => t('Author'), 'field' => 'name'), - array('data' => t('Posted in'), 'field' => 'node_title'), - array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'), - array('data' => t('Operations')) - )); - $result = pager_query('SELECT c.subject, c.nid, c.cid, c.comment, c.timestamp, c.status, c.name, c.homepage, u.name AS registered_name, u.uid, n.title AS node_title FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid INNER JOIN {node} n ON n.nid = c.nid WHERE c.status = %d'. tablesort_sql($form['header']['#value']), 50, 0, NULL, SPAM_COMMENT); - - // build a table listing the appropriate comments - $destination = drupal_get_destination(); - while ($comment = db_fetch_object($result)) { - $comments[$comment->cid] = ''; - $comment->name = $comment->uid ? $comment->registered_name : $comment->name; - $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid))); - $form['username'][$comment->cid] = array('#value' => theme('username', $comment)); - $form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid)); - $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small')); - $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array('query' => $destination))); - } - $form['comments'] = array('#type' => 'checkboxes', '#options' => $comments); - $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); - - return $form; -} - -/** - * Duplicate function from comment module so we can set correct goto url. - */ -function spam_comment_admin_overview_validate($form, &$form_state) { - $form_state['values']['comments'] = array_diff($form_state['values']['comments'], array(0)); - if (count($form_state['values']['comments']) == 0) { - form_set_error('', t('Please select one or more comments to perform the update on.')); - drupal_goto('admin/content/comment/list/spam'); - } -} - -/** - * Re-use function from comment module. - */ -function spam_comment_admin_overview_submit($form, &$form_state) { - comment_admin_overview_submit($form, $form_state); -} - -/** - * Re-use function from comment module. - */ -function theme_spam_comment_admin_overview($form) { - return theme('comment_admin_overview', $form); -} - -/** - * Scan comment content before it is posted into the database. - */ -function comment_spam_scan($form, &$form_state) { - if ($form_state['clicked_button']['#value'] == t('Save')) { - $comment = $form['#post']; - $_SESSION['spam_form'] = $form; - spam_scan($comment, 'comment'); - } - else if (isset($_SESSION['spam_form'])) { - unset($_SESSION['spam_form']); - } -} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_comment.inc sites/all/modules/spam/modules/spam_content_comment.inc --- /home/files/coding/drupal/spam/modules/spam_content_comment.inc 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_comment.inc 2009-12-09 13:56:15.000000000 +1100 @@ -0,0 +1,387 @@ +cid))) { + $comment->status = COMMENT_NOT_PUBLISHED; + } + break; + } +} + +/** + * Cache the comment id to be sure it's available when we need it. + */ +function _spam_content_comment_cid($id = NULL) { + static $cid = 0; + + if (isset($id) && is_numeric($id)) { + $cid = $id; + } + + return $cid; +} + +/** + * Spam module _spamapi() hook. + */ +function comment_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { + switch ($op) { + case 'content_module': + // Register with the spam api as a content type module. + return 'comment'; + + case 'content_id': + // Tell the spam module the id of a given comment. + if (is_object($arg1)) { + // The delete hook uses an object instead of an array. + $arg1 = (array)$arg1; + } + return _spam_content_comment_cid($arg1['cid']); + + case 'content_types': + // Register the "comment" content type with the spam module. + return array(array( + 'name' => t('comments'), + 'module' => t('comment'), + 'title' => t('Comments'), + 'description' => t('Check this box to filter comments for spam.'), + 'default_value' => 1, + )); + + case 'filter_content_type': + return (variable_get('spam_filter_comments', 1)); + + case 'filter_fields': + // Tell spam module which fields it should scan for spam. + $fields['main'] = array('subject', 'comment'); + if (is_object($arg1)) { + // The delete hook uses an object instead of an array. + $arg1 = (array)$arg1; + } + if (isset($arg1['author'])) { + $fields['other'][] = 'author'; + } + if (isset($arg1['name'])) { + $fields['other'][] = 'name'; + } + if (isset($arg1['mail'])) { + $fields['other'][] = 'mail'; + } + if (isset($arg1['homepage'])) { + $fields['other'][] = 'homepage'; + } + return $fields; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['cid'])) { + $form['cid'] = array( + '#type' => 'textfield', + '#title' => t('Comment ID'), + '#value' => $arg1['cid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['comment'] = array( + '#type' => 'fieldset', + '#title' => 'Comment', + ); + $form['comment']['title'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#value' => $arg1['subject'], + '#disabled' => TRUE, + ); + $form['comment']['body'] = array( + '#type' => 'textarea', + '#title' => t('Comment'), + '#value' => $arg1['comment'], + '#disabled' => TRUE, + ); + $form['comment']['author'] = array( + '#type' => 'markup', + '#prefix' => '
' . t('Author') . ':
', + '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), + ); + return $form; + + case 'load': + return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $arg1)); + + case 'title': + return db_result(db_query('SELECT subject FROM {comments} WHERE cid = %d', $arg1)); + + case 'edit_link': + return "comment/edit/$arg1"; + + case 'status': + $status = db_result(db_query('SELECT status FROM {comments} WHERE cid = %d', $arg1)); + if ($status == COMMENT_PUBLISHED) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'hostname': + return db_result(db_query('SELECT hostname FROM {comments} WHERE cid = %d', $arg1)); + + case 'link': + if (is_object($arg1) && isset($arg1->cid)) { + return spam_links('comment', $arg1->cid, $arg1); + } + break; + + case 'redirect': + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $arg1)); + return drupal_goto("/node/$nid", NULL, "comment-$arg1"); + + case 'overview_filter_join': + return 'INNER JOIN {comments} c ON t.content_id = c.cid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "c.subject LIKE '%%%s%%'"; + case 'status': + return 'c.status = %d'; + } + + case 'menu': + // Create page for listing all spam comments in comment admin section. + $items = array(); + $items['admin/content/comment/list/spam'] = array( + 'title' => t('Spam'), + 'page callback' => 'spam_content_comment_admin', + 'access arguments' => array('administer spam'), + //TODO: remove dependencies on comment.admin.inc? + 'file' => 'comment.admin.inc', + 'file path' => drupal_get_path('module', 'comment'), + 'type' => MENU_LOCAL_TASK, + ); + return $items; + + case 'publish': + // Only update comments that exist and need to be published. + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d OR status = %d', $arg1, COMMENT_NOT_PUBLISHED, SPAM_COMMENT)); + if ($nid) { + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_PUBLISHED, $arg1); + _comment_update_node_statistics($nid); + } + break; + + case 'unpublish': + // Only update comments that exist and need to be unpublished. + // When 'unpublishing' comments, we actually mark them as spam. + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d', $arg1, COMMENT_PUBLISHED)); + if ($nid) { + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', SPAM_COMMENT, $arg1); + _comment_update_node_statistics($nid); + } + break; + + case 'theme_forms': + // Add spam comments admin form to spam.module's hook_theme. + return array( + 'spam_content_comment_admin_overview' => array( + 'file' => 'modules/spam_content_comment.inc', + 'arguments' => array( + 'form' => NULL, + ), + ), + ); + } +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function comment_spamapi_form_alter(&$form, &$form_state, $form_id) { + // Scan comments before they are inserted into the database + if ($form_id == 'comment_form' && is_array($form)) { + $form['#validate'][] = 'comment_spam_scan'; + } + // Add spam options to comment form + else if ($form_id == 'comment_admin_overview' || $form_id == 'spam_content_comment_admin_overview') { + $form['#spam_submit_valuetype'] = 'comments'; + $form['#spam_submit_itemtype'] = 'comment'; + $form['#submit'] = array_merge(array('spam_admin_overview_submit'), $form['#submit']); + $parameters = $form['#parameters']; + if (is_array($parameters)) { + if (!in_array('new', $parameters)) { + $form['options']['operation']['#options']['markasnotspam'] = 'Mark the selected comments as not spam'; + } + if (!in_array('spam', $parameters)) { + $form['options']['operation']['#options']['markasspam'] = 'Mark the selected comments as spam'; + } + if (in_array('new', $parameters)) { + $form['options']['operation']['#options']['teachnotspam'] = 'Teach filters selected comments are not spam'; + } + } + if (is_array($form['#post']) && is_array($form['#post']['comments'])) { + foreach ($form['#post']['comments'] as $cid) { + $score = _spam_content_comment_score($cid); + if ($form['#post']['operation'] == 'markasspam') { + if (!spam_score_is_spam($score)) { + spam_mark_as_spam('comment', $cid); + } + } + else if ($form['#post']['operation'] == 'markasnotspam') { + if (spam_score_is_spam($score)) { + spam_mark_as_not_spam('comment', $cid); + } + } + else if ($form['#post']['operation'] == 'teachnotspam') { + spam_mark_as_not_spam('comment', $cid); + } + } + } + } +} + +/** + * Retreive spam score, caching in memory for repeated use. + */ +function _spam_content_comment_score($cid) { + static $scores = array(); + + if (!isset($scores[$cid])) { + $scores[$cid] = db_result(db_query("SELECT score FROM {spam_tracker} WHERE content_type = 'comment' AND content_id = %d", $cid)); + } + + return $scores[$cid]; +} + +/** + * Menu callback; present an administrative spam comment listing. + */ +function spam_content_comment_admin() { + $edit = $_POST; + + if ($edit['operation'] == 'delete' && $edit['comments']) { + return drupal_get_form('comment_multiple_delete_confirm'); + } + else { + return drupal_get_form('spam_content_comment_admin_overview', arg(4)); + } +} + +/** + * The 5.x version of comment_admin_overview doesn't provide hooks to easily + * create a spam overview page. This is nearly an exact copy of that function, + * with minor changes for loading spam and processing it. + */ +function spam_content_comment_admin_overview(&$form_state, $arg) { + // build an 'Update options' form + $form['options'] = array( + '#type' => 'fieldset', '#title' => t('Update options'), + '#prefix' => '
', '#suffix' => '
' + ); + $options = array( + 'markasnotspam' => t('Mark the selected comments as not spam'), + 'delete' => t('Delete the selected comments'), + ); + + $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'markasnotspam'); + $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update')); + + // load the comments that we want to display + $form['header'] = array('#type' => 'value', '#value' => array( + theme('table_select_header_cell'), + array('data' => t('Subject'), 'field' => 'subject'), + array('data' => t('Author'), 'field' => 'name'), + array('data' => t('Posted in'), 'field' => 'node_title'), + array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'), + array('data' => t('Operations')) + )); + $result = pager_query('SELECT c.subject, c.nid, c.cid, c.comment, c.timestamp, c.status, c.name, c.homepage, u.name AS registered_name, u.uid, n.title AS node_title FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid INNER JOIN {node} n ON n.nid = c.nid WHERE c.status = %d'. tablesort_sql($form['header']['#value']), 50, 0, NULL, SPAM_COMMENT); + + // build a table listing the appropriate comments + $destination = drupal_get_destination(); + while ($comment = db_fetch_object($result)) { + $comments[$comment->cid] = ''; + $comment->name = $comment->uid ? $comment->registered_name : $comment->name; + $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid))); + $form['username'][$comment->cid] = array('#value' => theme('username', $comment)); + $form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid)); + $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small')); + $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array('query' => $destination))); + } + $form['comments'] = array('#type' => 'checkboxes', '#options' => $comments); + $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); + + return $form; +} + +/** + * Duplicate function from comment module so we can set correct goto url. + */ +function spam_content_comment_admin_overview_validate($form, &$form_state) { + $form_state['values']['comments'] = array_diff($form_state['values']['comments'], array(0)); + if (count($form_state['values']['comments']) == 0) { + form_set_error('', t('Please select one or more comments to perform the update on.')); + drupal_goto('admin/content/comment/list/spam'); + } +} + +/** + * Re-use function from comment module. + */ +function spam_content_comment_admin_overview_submit($form, &$form_state) { + comment_admin_overview_submit($form, $form_state); +} + +/** + * Re-use function from comment module. + */ +function theme_spam_content_comment_admin_overview($form) { + return theme('comment_admin_overview', $form); +} + +/** + * Scan comment content before it is posted into the database. + */ +function comment_spam_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save')) { + $comment = $form['#post']; + $_SESSION['spam_form'] = $form; + spam_scan($comment, 'comment'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_comment.inc~ sites/all/modules/spam/modules/spam_content_comment.inc~ --- /home/files/coding/drupal/spam/modules/spam_content_comment.inc~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_comment.inc~ 2009-12-09 13:56:14.000000000 +1100 @@ -0,0 +1,387 @@ +cid))) { + $comment->status = COMMENT_NOT_PUBLISHED; + } + break; + } +} + +/** + * Cache the comment id to be sure it's available when we need it. + */ +function _spam_content_comment_cid($id = NULL) { + static $cid = 0; + + if (isset($id) && is_numeric($id)) { + $cid = $id; + } + + return $cid; +} + +/** + * Spam module _spamapi() hook. + */ +function comment_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { + switch ($op) { + case 'content_module': + // Register with the spam api as a content type module. + return 'comment'; + + case 'content_id': + // Tell the spam module the id of a given comment. + if (is_object($arg1)) { + // The delete hook uses an object instead of an array. + $arg1 = (array)$arg1; + } + return _spam_content_comment_cid($arg1['cid']); + + case 'content_types': + // Register the "comment" content type with the spam module. + return array(array( + 'name' => t('comments'), + 'module' => t('comment'), + 'title' => t('Comments'), + 'description' => t('Check this box to filter comments for spam.'), + 'default_value' => 1, + )); + + case 'filter_content_type': + return (variable_get('spam_filter_comments', 1)); + + case 'filter_fields': + // Tell spam module which fields it should scan for spam. + $fields['main'] = array('subject', 'comment'); + if (is_object($arg1)) { + // The delete hook uses an object instead of an array. + $arg1 = (array)$arg1; + } + if (isset($arg1['author'])) { + $fields['other'][] = 'author'; + } + if (isset($arg1['name'])) { + $fields['other'][] = 'name'; + } + if (isset($arg1['mail'])) { + $fields['other'][] = 'mail'; + } + if (isset($arg1['homepage'])) { + $fields['other'][] = 'homepage'; + } + return $fields; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['cid'])) { + $form['cid'] = array( + '#type' => 'textfield', + '#title' => t('Comment ID'), + '#value' => $arg1['cid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['comment'] = array( + '#type' => 'fieldset', + '#title' => 'Comment', + ); + $form['comment']['title'] = array( + '#type' => 'textfield', + '#title' => t('Subject'), + '#value' => $arg1['subject'], + '#disabled' => TRUE, + ); + $form['comment']['body'] = array( + '#type' => 'textarea', + '#title' => t('Comment'), + '#value' => $arg1['comment'], + '#disabled' => TRUE, + ); + $form['comment']['author'] = array( + '#type' => 'markup', + '#prefix' => '
' . t('Author') . ':
', + '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), + ); + return $form; + + case 'load': + return db_fetch_object(db_query('SELECT * FROM {comments} WHERE cid = %d', $arg1)); + + case 'title': + return db_result(db_query('SELECT subject FROM {comments} WHERE cid = %d', $arg1)); + + case 'edit_link': + return "comment/edit/$arg1"; + + case 'status': + $status = db_result(db_query('SELECT status FROM {comments} WHERE cid = %d', $arg1)); + if ($status == COMMENT_PUBLISHED) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'hostname': + return db_result(db_query('SELECT hostname FROM {comments} WHERE cid = %d', $arg1)); + + case 'link': + if (is_object($arg1) && isset($arg1->cid)) { + return spam_links('comment', $arg1->cid, $arg1); + } + break; + + case 'redirect': + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', $arg1)); + return drupal_goto("/node/$nid", NULL, "comment-$arg1"); + + case 'overview_filter_join': + return 'INNER JOIN {comments} c ON t.content_id = c.cid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "c.subject LIKE '%%%s%%'"; + case 'status': + return 'c.status = %d'; + } + + case 'menu': + // Create page for listing all spam comments in comment admin section. + $items = array(); + $items['admin/content/comment/list/spam'] = array( + 'title' => t('Spam'), + 'page callback' => 'spam_content_comment_admin', + 'access arguments' => array('administer spam'), + //TODO: remove dependencies on comment.admin.inc? + 'file' => 'comment.admin.inc', + 'file path' => drupal_get_path('module', 'comment'), + 'type' => MENU_LOCAL_TASK, + ); + return $items; + + case 'publish': + // Only update comments that exist and need to be published. + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d OR status = %d', $arg1, COMMENT_NOT_PUBLISHED, SPAM_COMMENT)); + if ($nid) { + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_PUBLISHED, $arg1); + _comment_update_node_statistics($nid); + } + break; + + case 'unpublish': + // Only update comments that exist and need to be unpublished. + // When 'unpublishing' comments, we actually mark them as spam. + $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d AND status = %d', $arg1, COMMENT_PUBLISHED)); + if ($nid) { + db_query('UPDATE {comments} SET status = %d WHERE cid = %d', SPAM_COMMENT, $arg1); + _comment_update_node_statistics($nid); + } + break; + + case 'theme_forms': + // Add spam comments admin form to spam.module's hook_theme. + return array( + 'spam_content_comment_admin_overview' => array( + 'file' => 'modules/spam_content_comment.inc', + 'arguments' => array( + 'form' => NULL, + ), + ), + ); + } +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function comment_spamapi_form_alter(&$form, &$form_state, $form_id) { + // Scan comments before they are inserted into the database + if ($form_id == 'spam_content_comment_form' && is_array($form)) { + $form['#validate'][] = 'comment_spam_scan'; + } + // Add spam options to comment form + else if ($form_id == 'spam_content_comment_admin_overview' || $form_id == 'spam_content_comment_admin_overview') { + $form['#spam_submit_valuetype'] = 'comments'; + $form['#spam_submit_itemtype'] = 'comment'; + $form['#submit'] = array_merge(array('spam_admin_overview_submit'), $form['#submit']); + $parameters = $form['#parameters']; + if (is_array($parameters)) { + if (!in_array('new', $parameters)) { + $form['options']['operation']['#options']['markasnotspam'] = 'Mark the selected comments as not spam'; + } + if (!in_array('spam', $parameters)) { + $form['options']['operation']['#options']['markasspam'] = 'Mark the selected comments as spam'; + } + if (in_array('new', $parameters)) { + $form['options']['operation']['#options']['teachnotspam'] = 'Teach filters selected comments are not spam'; + } + } + if (is_array($form['#post']) && is_array($form['#post']['comments'])) { + foreach ($form['#post']['comments'] as $cid) { + $score = _spam_content_comment_score($cid); + if ($form['#post']['operation'] == 'markasspam') { + if (!spam_score_is_spam($score)) { + spam_mark_as_spam('comment', $cid); + } + } + else if ($form['#post']['operation'] == 'markasnotspam') { + if (spam_score_is_spam($score)) { + spam_mark_as_not_spam('comment', $cid); + } + } + else if ($form['#post']['operation'] == 'teachnotspam') { + spam_mark_as_not_spam('comment', $cid); + } + } + } + } +} + +/** + * Retreive spam score, caching in memory for repeated use. + */ +function _spam_content_comment_score($cid) { + static $scores = array(); + + if (!isset($scores[$cid])) { + $scores[$cid] = db_result(db_query("SELECT score FROM {spam_tracker} WHERE content_type = 'comment' AND content_id = %d", $cid)); + } + + return $scores[$cid]; +} + +/** + * Menu callback; present an administrative spam comment listing. + */ +function spam_content_comment_admin() { + $edit = $_POST; + + if ($edit['operation'] == 'delete' && $edit['comments']) { + return drupal_get_form('comment_multiple_delete_confirm'); + } + else { + return drupal_get_form('spam_content_comment_admin_overview', arg(4)); + } +} + +/** + * The 5.x version of comment_admin_overview doesn't provide hooks to easily + * create a spam overview page. This is nearly an exact copy of that function, + * with minor changes for loading spam and processing it. + */ +function spam_content_comment_admin_overview(&$form_state, $arg) { + // build an 'Update options' form + $form['options'] = array( + '#type' => 'fieldset', '#title' => t('Update options'), + '#prefix' => '
', '#suffix' => '
' + ); + $options = array( + 'markasnotspam' => t('Mark the selected comments as not spam'), + 'delete' => t('Delete the selected comments'), + ); + + $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'markasnotspam'); + $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update')); + + // load the comments that we want to display + $form['header'] = array('#type' => 'value', '#value' => array( + theme('table_select_header_cell'), + array('data' => t('Subject'), 'field' => 'subject'), + array('data' => t('Author'), 'field' => 'name'), + array('data' => t('Posted in'), 'field' => 'node_title'), + array('data' => t('Time'), 'field' => 'timestamp', 'sort' => 'desc'), + array('data' => t('Operations')) + )); + $result = pager_query('SELECT c.subject, c.nid, c.cid, c.comment, c.timestamp, c.status, c.name, c.homepage, u.name AS registered_name, u.uid, n.title AS node_title FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid INNER JOIN {node} n ON n.nid = c.nid WHERE c.status = %d'. tablesort_sql($form['header']['#value']), 50, 0, NULL, SPAM_COMMENT); + + // build a table listing the appropriate comments + $destination = drupal_get_destination(); + while ($comment = db_fetch_object($result)) { + $comments[$comment->cid] = ''; + $comment->name = $comment->uid ? $comment->registered_name : $comment->name; + $form['subject'][$comment->cid] = array('#value' => l($comment->subject, 'node/'. $comment->nid, array('attributes' => array('title' => truncate_utf8($comment->comment, 128)), 'fragment' => 'comment-'. $comment->cid))); + $form['username'][$comment->cid] = array('#value' => theme('username', $comment)); + $form['node_title'][$comment->cid] = array('#value' => l($comment->node_title, 'node/'. $comment->nid)); + $form['timestamp'][$comment->cid] = array('#value' => format_date($comment->timestamp, 'small')); + $form['operations'][$comment->cid] = array('#value' => l(t('edit'), 'comment/edit/'. $comment->cid, array('query' => $destination))); + } + $form['comments'] = array('#type' => 'checkboxes', '#options' => $comments); + $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); + + return $form; +} + +/** + * Duplicate function from comment module so we can set correct goto url. + */ +function spam_content_comment_admin_overview_validate($form, &$form_state) { + $form_state['values']['comments'] = array_diff($form_state['values']['comments'], array(0)); + if (count($form_state['values']['comments']) == 0) { + form_set_error('', t('Please select one or more comments to perform the update on.')); + drupal_goto('admin/content/comment/list/spam'); + } +} + +/** + * Re-use function from comment module. + */ +function spam_content_comment_admin_overview_submit($form, &$form_state) { + comment_admin_overview_submit($form, $form_state); +} + +/** + * Re-use function from comment module. + */ +function theme_spam_content_comment_admin_overview($form) { + return theme('comment_admin_overview', $form); +} + +/** + * Scan comment content before it is posted into the database. + */ +function comment_spam_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save')) { + $comment = $form['#post']; + $_SESSION['spam_form'] = $form; + spam_scan($comment, 'comment'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_node.inc sites/all/modules/spam/modules/spam_content_node.inc --- /home/files/coding/drupal/spam/modules/spam_content_node.inc 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_node.inc 2009-12-09 13:44:45.000000000 +1100 @@ -0,0 +1,200 @@ + $type->type, + 'module' => $type->module, + 'title' => $type->name, + 'description' => $type->description, + 'default_value' => 0, + ); + } + return $types; + + case 'filter_content_type': + if (is_array($arg1)) { + $arg1 = (object)$arg1; + } + return (variable_get("spam_filter_$arg1->type", 0)); + + case 'filter_fields': + // Tell spam filter which fields should be scanned for spam. + $fields['main'] = array('title', 'body'); + // TODO: other fields (CCK) + return $fields; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['nid'])) { + $form['nid'] = array( + '#type' => 'textfield', + '#title' => t('Node ID'), + '#value' => $arg1['nid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['node'] = array( + '#type' => 'fieldset', + '#title' => $type, + ); + $form['node']['title'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#value' => $arg1['title'], + '#disabled' => TRUE, + ); + $form['node']['body'] = array( + '#type' => 'textarea', + '#title' => t('Body'), + '#value' => $arg1['body'], + '#disabled' => TRUE, + ); + $form['node']['author'] = array( + '#type' => 'markup', + '#prefix' => '
' . t('Author') . ':
', + '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), + ); + // TODO: CCK fields + return $form; + + case 'load': + return node_load($arg1); + + case 'title': + return db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $arg1)); + + case 'status': + $status = db_result(db_query('SELECT status FROM {node} WHERE nid = %d', $arg1)); + if ($status == 1) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'edit_link': + return "node/$arg1/edit"; + + case 'link': + if (is_object($arg1) && isset($arg1->nid)) { + return spam_links('node', $arg1->nid, $arg1); + } + break; + + case 'redirect': + return drupal_goto("/node/$arg1"); + + case 'overview_filter_join': + return 'INNER JOIN {node} n ON t.content_id = n.nid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "n.title LIKE '%%%s%%'"; + case 'status': + return "n.status != %d"; + } + + case 'publish': + if (is_numeric($arg1)) { + $arg1 = node_load($arg1); + node_publish_action($arg1); + node_save($arg1); + } + break; + + case 'unpublish': + if (is_numeric($arg1)) { + $arg1 = node_load($arg1); + node_unpublish_action($arg1); + node_save($arg1); + } + break; + + } +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function node_spamapi_form_alter(&$form, &$form_state, $form_id) { + if (strpos($form_id, '_node_form')) { + $form['#validate'][] = 'node_spam_scan'; + } +} + +/** + * Scan node content before it is posted into the database. + */ +function node_spam_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save')) { + $node = $form['#post']; + $node['type'] = $form['type']['#value']; + $_SESSION['spam_form'] = $form; + spam_scan($node, 'node'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_node.inc~ sites/all/modules/spam/modules/spam_content_node.inc~ --- /home/files/coding/drupal/spam/modules/spam_content_node.inc~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_node.inc~ 2009-12-09 13:44:45.000000000 +1100 @@ -0,0 +1,201 @@ + $type->type, + 'module' => $type->module, + 'title' => $type->name, + 'description' => $type->description, + 'default_value' => 0, + ); + } + return $types; + + case 'filter_content_type': + if (is_array($arg1)) { + $arg1 = (object)$arg1; + } + return (variable_get("spam_filter_$arg1->type", 0)); + + case 'filter_fields': + // Tell spam filter which fields should be scanned for spam. + $fields['main'] = array('title', 'body'); + // TODO: other fields (CCK) + return $fields; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['nid'])) { + $form['nid'] = array( + '#type' => 'textfield', + '#title' => t('Node ID'), + '#value' => $arg1['nid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['node'] = array( + '#type' => 'fieldset', + '#title' => $type, + ); + $form['node']['title'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#value' => $arg1['title'], + '#disabled' => TRUE, + ); + $form['node']['body'] = array( + '#type' => 'textarea', + '#title' => t('Body'), + '#value' => $arg1['body'], + '#disabled' => TRUE, + ); + $form['node']['author'] = array( + '#type' => 'markup', + '#prefix' => '
' . t('Author') . ':
', + '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), + ); + // TODO: CCK fields + return $form; + + case 'load': + return node_load($arg1); + + case 'title': + return db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $arg1)); + + case 'status': + $status = db_result(db_query('SELECT status FROM {node} WHERE nid = %d', $arg1)); + if ($status == 1) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'edit_link': + return "node/$arg1/edit"; + + case 'link': + if (is_object($arg1) && isset($arg1->nid)) { + return spam_links('node', $arg1->nid, $arg1); + } + break; + + case 'redirect': + return drupal_goto("/node/$arg1"); + + case 'overview_filter_join': + return 'INNER JOIN {node} n ON t.content_id = n.nid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "n.title LIKE '%%%s%%'"; + case 'status': + return "n.status != %d"; + } + + case 'publish': + if (is_numeric($arg1)) { + $arg1 = node_load($arg1); + node_publish_action($arg1); + node_save($arg1); + } + break; + + case 'unpublish': + if (is_numeric($arg1)) { + $arg1 = node_load($arg1); + node_unpublish_action($arg1); + node_save($arg1); + } + break; + + } +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function spam_content_node_spamapi_form_alter(&$form, &$form_state, $form_id) { + if (strpos($form_id, '_node_form')) { + $form['#validate'][] = 'spam_content_node_scan'; + } +} + +/** + * Scan node content before it is posted into the database. + */ +function spam_content_node_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save')) { + $node = $form['#post']; + $node['type'] = $form['type']['#value']; + $_SESSION['spam_form'] = $form; + spam_scan($node, 'node'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_user.inc sites/all/modules/spam/modules/spam_content_user.inc --- /home/files/coding/drupal/spam/modules/spam_content_user.inc 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_user.inc 2009-12-09 13:59:12.000000000 +1100 @@ -0,0 +1,258 @@ +uid, $user) as $link) { + if ($link['href']) { + $links .= l($link['title'], $link['href']) .' '; + } + else { + $links .= $link['title'] .' '; + } + } + $items['spam_links'] = array( + 'value' => $links, + 'class' => 'spam', + ); + $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $user->uid)); + $status_text = t('User status: %status', array('%status' => $status ? t('not blocked') : t('blocked'))); + $items['spam_status'] = array( + 'value' => $status_text, + 'class' => 'spam', + ); + return array(t('Spam status') => $items); + } + break; + } +} + +/** + * Cache the user id to be sure it's available when we need it. + */ +function _spam_content_user_uid($id) { + static $uid = 0; + + if (isset($id) && is_numeric($id)) { + $uid = $id; + } + + return $uid; +} + +/** + * User module _spamapi() hook. + */ +function user_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { + switch ($op) { + case 'content_module': + // Register with the spam api as a content type module. + return 'user'; + + case 'content_id': + if (is_object($arg1)) { + $arg1 = (array)$arg1; + } + return _spam_content_user_uid($arg1['uid']); + + case 'content_types': + // Register the "user" content type with the spam module. + return array(array( + 'name' => t('users'), + 'module' => t('user'), + 'title' => t('Users'), + 'description' => t('Check this box to filter users for spam.'), + 'default_value' => 0, + )); + + case 'process_form': + // Hook to scan user before it is inserted into the database. + if ($arg1 == 'user_register' && is_array($arg2)) { + $user = $arg2['#post']; + if (is_array($user) && $user['op'] == t('Create new account')) { + $_SESSION['spam_form'] = $arg2; + spam_scan($user, 'user'); + } + else { + if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } + } + } + break; + + case 'filter_content_type': + return (variable_get('spam_filter_users', 0)); + + case 'filter_fields': + // Be sure we're always working with an array. + if (is_object($arg1)) { + $arg1 = (array)$arg1; + } + // Determine uid so we can cache fields in static. + if (isset($arg1['uid'])) { + $uid = $arg1['uid']; + } + else { + $uid = 0; + } + // Only figure out filter_fields once per user per page load. + static $fields = array(); + if (!isset($fields[$uid])) { + $fields[$uid]['main'] = array('name', 'mail'); + $fields[$uid]['other'] = array(); + if (isset($arg1['signature'])) { + $fields[$uid]['other'][] = 'signature'; + } + if (db_table_exists('profile_fields')) { + $result = db_query('SELECT name FROM {profile_fields}'); + while ($profile = db_fetch_object($result)) { + if (isset($arg1[$profile->name])) { + $fields[$uid]['other'][] = $profile->name; + } + } + } + } + return $fields[$uid]; + + case 'redirect': + if (is_numeric($arg1)) { + return drupal_goto("/user/$arg1"); + } + break; + + case 'load': + if (is_numeric($arg1)) { + return user_load(array('uid' => $arg1)); + } + break; + + case 'title': + return db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $arg1)); + + case 'edit_link': + return "user/$arg1/edit"; + + case 'status': + $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $arg1)); + if ($status == 1) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'overview_filter_join': + return 'INNER JOIN {users} u ON t.content_id = u.uid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "u.name LIKE '%%%s%%'"; + case 'status': + return "u.status != %d"; + } + + case 'feedback_filter_join_field': + return 'u.uid'; + + case 'publish': + // TODO: When un/publish user, should probably also un/publish content + if (is_numeric($arg1)) { + module_invoke('user', 'user_operations_unblock', array($arg1)); + } + break; + + case 'unpublish': + // TODO: When un/publish user, should probably also un/publish content + if (is_numeric($arg1)) { + module_invoke('user', 'user_operations_block', array($arg1)); + } + break; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['uid'])) { + $form['uid'] = array( + '#type' => 'textfield', + '#title' => t('User ID'), + '#value' => $arg1['uid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Username'), + '#value' => $arg1['name'], + '#disabled' => TRUE, + ); + $form['mail'] = array( + '#type' => 'textfield', + '#title' => t('Email address'), + '#value' => $arg1['mail'], + '#disabled' => TRUE, + ); + // TODO: Show profile fields. + return $form; + } +} + +/** + * Be sure we're scanning the latest profile data. + */ +function spam_content_user_profile_update($user) { + if (module_exists('profile')) { + profile_load_profile($user); + } + return $user; +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function user_spamapi_form_alter(&$form, &$form_state, $form_id) { + // Scan users before they are inserted into the database + if ($form_id == 'user_register' && is_array($form)) { + $form['#validate'][] = 'user_spam_scan'; + } +} + +/** + * Scan user before it is created in the database. + */ +function user_spam_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Create new account')) { + $account = $form['#post']; + $_SESSION['spam_form'] = $form; + spam_scan($account, 'user'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_content_user.inc~ sites/all/modules/spam/modules/spam_content_user.inc~ --- /home/files/coding/drupal/spam/modules/spam_content_user.inc~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/modules/spam_content_user.inc~ 2009-12-09 13:59:12.000000000 +1100 @@ -0,0 +1,258 @@ +uid, $user) as $link) { + if ($link['href']) { + $links .= l($link['title'], $link['href']) .' '; + } + else { + $links .= $link['title'] .' '; + } + } + $items['spam_links'] = array( + 'value' => $links, + 'class' => 'spam', + ); + $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $user->uid)); + $status_text = t('User status: %status', array('%status' => $status ? t('not blocked') : t('blocked'))); + $items['spam_status'] = array( + 'value' => $status_text, + 'class' => 'spam', + ); + return array(t('Spam status') => $items); + } + break; + } +} + +/** + * Cache the user id to be sure it's available when we need it. + */ +function _spam_content_user_uid($id) { + static $uid = 0; + + if (isset($id) && is_numeric($id)) { + $uid = $id; + } + + return $uid; +} + +/** + * User module _spamapi() hook. + */ +function user_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { + switch ($op) { + case 'content_module': + // Register with the spam api as a content type module. + return 'user'; + + case 'content_id': + if (is_object($arg1)) { + $arg1 = (array)$arg1; + } + return _spam_content_user_uid($arg1['uid']); + + case 'content_types': + // Register the "user" content type with the spam module. + return array(array( + 'name' => t('users'), + 'module' => t('user'), + 'title' => t('Users'), + 'description' => t('Check this box to filter users for spam.'), + 'default_value' => 0, + )); + + case 'process_form': + // Hook to scan user before it is inserted into the database. + if ($arg1 == 'user_register' && is_array($arg2)) { + $user = $arg2['#post']; + if (is_array($user) && $user['op'] == t('Create new account')) { + $_SESSION['spam_form'] = $arg2; + spam_scan($user, 'user'); + } + else { + if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } + } + } + break; + + case 'filter_content_type': + return (variable_get('spam_filter_users', 0)); + + case 'filter_fields': + // Be sure we're always working with an array. + if (is_object($arg1)) { + $arg1 = (array)$arg1; + } + // Determine uid so we can cache fields in static. + if (isset($arg1['uid'])) { + $uid = $arg1['uid']; + } + else { + $uid = 0; + } + // Only figure out filter_fields once per user per page load. + static $fields = array(); + if (!isset($fields[$uid])) { + $fields[$uid]['main'] = array('name', 'mail'); + $fields[$uid]['other'] = array(); + if (isset($arg1['signature'])) { + $fields[$uid]['other'][] = 'signature'; + } + if (db_table_exists('profile_fields')) { + $result = db_query('SELECT name FROM {profile_fields}'); + while ($profile = db_fetch_object($result)) { + if (isset($arg1[$profile->name])) { + $fields[$uid]['other'][] = $profile->name; + } + } + } + } + return $fields[$uid]; + + case 'redirect': + if (is_numeric($arg1)) { + return drupal_goto("/user/$arg1"); + } + break; + + case 'load': + if (is_numeric($arg1)) { + return user_load(array('uid' => $arg1)); + } + break; + + case 'title': + return db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $arg1)); + + case 'edit_link': + return "user/$arg1/edit"; + + case 'status': + $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $arg1)); + if ($status == 1) { + return SPAM_PUBLISHED; + } + else { + return SPAM_NOT_PUBLISHED; + } + + case 'overview_filter_join': + return 'INNER JOIN {users} u ON t.content_id = u.uid'; + + case 'overview_filter_where': + switch ($arg1) { + case 'title': + return "u.name LIKE '%%%s%%'"; + case 'status': + return "u.status != %d"; + } + + case 'feedback_filter_join_field': + return 'u.uid'; + + case 'publish': + // TODO: When un/publish user, should probably also un/publish content + if (is_numeric($arg1)) { + module_invoke('user', 'user_operations_unblock', array($arg1)); + } + break; + + case 'unpublish': + // TODO: When un/publish user, should probably also un/publish content + if (is_numeric($arg1)) { + module_invoke('user', 'user_operations_block', array($arg1)); + } + break; + + case 'feedback_form': + $form = array(); + if (is_numeric($form['uid'])) { + $form['uid'] = array( + '#type' => 'textfield', + '#title' => t('User ID'), + '#value' => $arg1['uid'], + '#disabled' => TRUE, + ); + } + // fall through... + case 'error_form': + if (!is_array($form)) { + $form = array(); + } + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Username'), + '#value' => $arg1['name'], + '#disabled' => TRUE, + ); + $form['mail'] = array( + '#type' => 'textfield', + '#title' => t('Email address'), + '#value' => $arg1['mail'], + '#disabled' => TRUE, + ); + // TODO: Show profile fields. + return $form; + } +} + +/** + * Be sure we're scanning the latest profile data. + */ +function spam_content_user_profile_update($user) { + if (module_exists('profile')) { + profile_load_profile($user); + } + return $user; +} + +/** + * Form alter gets its own function so we can reference &$form without causing + * errors in PHP4 installations. (If we use spamapi, we have to set a default, + * which PHP4 doesn't support.) + */ +function user_spamapi_form_alter(&$form, &$form_state, $form_id) { + // Scan users before they are inserted into the database + if ($form_id == 'user_register' && is_array($form)) { + $form['#validate'][] = 'user_spam_scan'; + } +} + +/** + * Scan user before it is created in the database. + */ +function user_spam_scan($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Create new account')) { + $account = $form['#post']; + $_SESSION['spam_form'] = $form; + spam_scan($account, 'user'); + } + else if (isset($_SESSION['spam_form'])) { + unset($_SESSION['spam_form']); + } +} diff -uprN /home/files/coding/drupal/spam/modules/spam_node.inc sites/all/modules/spam/modules/spam_node.inc --- /home/files/coding/drupal/spam/modules/spam_node.inc 2009-08-07 07:06:50.000000000 +1000 +++ sites/all/modules/spam/modules/spam_node.inc 1970-01-01 10:00:00.000000000 +1000 @@ -1,200 +0,0 @@ - $type->type, - 'module' => $type->module, - 'title' => $type->name, - 'description' => $type->description, - 'default_value' => 0, - ); - } - return $types; - - case 'filter_content_type': - if (is_array($arg1)) { - $arg1 = (object)$arg1; - } - return (variable_get("spam_filter_$arg1->type", 0)); - - case 'filter_fields': - // Tell spam filter which fields should be scanned for spam. - $fields['main'] = array('title', 'body'); - // TODO: other fields (CCK) - return $fields; - - case 'feedback_form': - $form = array(); - if (is_numeric($form['nid'])) { - $form['nid'] = array( - '#type' => 'textfield', - '#title' => t('Node ID'), - '#value' => $arg1['nid'], - '#disabled' => TRUE, - ); - } - // fall through... - case 'error_form': - if (!is_array($form)) { - $form = array(); - } - $form['node'] = array( - '#type' => 'fieldset', - '#title' => $type, - ); - $form['node']['title'] = array( - '#type' => 'textfield', - '#title' => t('Title'), - '#value' => $arg1['title'], - '#disabled' => TRUE, - ); - $form['node']['body'] = array( - '#type' => 'textarea', - '#title' => t('Body'), - '#value' => $arg1['body'], - '#disabled' => TRUE, - ); - $form['node']['author'] = array( - '#type' => 'markup', - '#prefix' => '
' . t('Author') . ':
', - '#value' => theme('username', user_load(array('uid' => $arg1['uid']))), - ); - // TODO: CCK fields - return $form; - - case 'load': - return node_load($arg1); - - case 'title': - return db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $arg1)); - - case 'status': - $status = db_result(db_query('SELECT status FROM {node} WHERE nid = %d', $arg1)); - if ($status == 1) { - return SPAM_PUBLISHED; - } - else { - return SPAM_NOT_PUBLISHED; - } - - case 'edit_link': - return "node/$arg1/edit"; - - case 'link': - if (is_object($arg1) && isset($arg1->nid)) { - return spam_links('node', $arg1->nid, $arg1); - } - break; - - case 'redirect': - return drupal_goto("/node/$arg1"); - - case 'overview_filter_join': - return 'INNER JOIN {node} n ON t.content_id = n.nid'; - - case 'overview_filter_where': - switch ($arg1) { - case 'title': - return "n.title LIKE '%%%s%%'"; - case 'status': - return "n.status != %d"; - } - - case 'publish': - if (is_numeric($arg1)) { - $arg1 = node_load($arg1); - node_publish_action($arg1); - node_save($arg1); - } - break; - - case 'unpublish': - if (is_numeric($arg1)) { - $arg1 = node_load($arg1); - node_unpublish_action($arg1); - node_save($arg1); - } - break; - - } -} - -/** - * Form alter gets its own function so we can reference &$form without causing - * errors in PHP4 installations. (If we use spamapi, we have to set a default, - * which PHP4 doesn't support.) - */ -function node_spamapi_form_alter(&$form, &$form_state, $form_id) { - if (strpos($form_id, '_node_form')) { - $form['#validate'][] = 'node_spam_scan'; - } -} - -/** - * Scan node content before it is posted into the database. - */ -function node_spam_scan($form, &$form_state) { - if ($form_state['clicked_button']['#value'] == t('Save')) { - $node = $form['#post']; - $node['type'] = $form['type']['#value']; - $_SESSION['spam_form'] = $form; - spam_scan($node, 'node'); - } - else if (isset($_SESSION['spam_form'])) { - unset($_SESSION['spam_form']); - } -} diff -uprN /home/files/coding/drupal/spam/modules/spam_user.inc sites/all/modules/spam/modules/spam_user.inc --- /home/files/coding/drupal/spam/modules/spam_user.inc 2009-08-07 06:44:26.000000000 +1000 +++ sites/all/modules/spam/modules/spam_user.inc 1970-01-01 10:00:00.000000000 +1000 @@ -1,258 +0,0 @@ -uid, $user) as $link) { - if ($link['href']) { - $links .= l($link['title'], $link['href']) .' '; - } - else { - $links .= $link['title'] .' '; - } - } - $items['spam_links'] = array( - 'value' => $links, - 'class' => 'spam', - ); - $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $user->uid)); - $status_text = t('User status: %status', array('%status' => $status ? t('not blocked') : t('blocked'))); - $items['spam_status'] = array( - 'value' => $status_text, - 'class' => 'spam', - ); - return array(t('Spam status') => $items); - } - break; - } -} - -/** - * Cache the user id to be sure it's available when we need it. - */ -function _spam_user_uid($id) { - static $uid = 0; - - if (isset($id) && is_numeric($id)) { - $uid = $id; - } - - return $uid; -} - -/** - * User module _spamapi() hook. - */ -function user_spamapi($op, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL) { - switch ($op) { - case 'content_module': - // Register with the spam api as a content type module. - return 'user'; - - case 'content_id': - if (is_object($arg1)) { - $arg1 = (array)$arg1; - } - return _spam_user_uid($arg1['uid']); - - case 'content_types': - // Register the "user" content type with the spam module. - return array(array( - 'name' => t('users'), - 'module' => t('user'), - 'title' => t('Users'), - 'description' => t('Check this box to filter users for spam.'), - 'default_value' => 0, - )); - - case 'process_form': - // Hook to scan user before it is inserted into the database. - if ($arg1 == 'user_register' && is_array($arg2)) { - $user = $arg2['#post']; - if (is_array($user) && $user['op'] == t('Create new account')) { - $_SESSION['spam_form'] = $arg2; - spam_scan($user, 'user'); - } - else { - if (isset($_SESSION['spam_form'])) { - unset($_SESSION['spam_form']); - } - } - } - break; - - case 'filter_content_type': - return (variable_get('spam_filter_users', 0)); - - case 'filter_fields': - // Be sure we're always working with an array. - if (is_object($arg1)) { - $arg1 = (array)$arg1; - } - // Determine uid so we can cache fields in static. - if (isset($arg1['uid'])) { - $uid = $arg1['uid']; - } - else { - $uid = 0; - } - // Only figure out filter_fields once per user per page load. - static $fields = array(); - if (!isset($fields[$uid])) { - $fields[$uid]['main'] = array('name', 'mail'); - $fields[$uid]['other'] = array(); - if (isset($arg1['signature'])) { - $fields[$uid]['other'][] = 'signature'; - } - if (db_table_exists('profile_fields')) { - $result = db_query('SELECT name FROM {profile_fields}'); - while ($profile = db_fetch_object($result)) { - if (isset($arg1[$profile->name])) { - $fields[$uid]['other'][] = $profile->name; - } - } - } - } - return $fields[$uid]; - - case 'redirect': - if (is_numeric($arg1)) { - return drupal_goto("/user/$arg1"); - } - break; - - case 'load': - if (is_numeric($arg1)) { - return user_load(array('uid' => $arg1)); - } - break; - - case 'title': - return db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $arg1)); - - case 'edit_link': - return "user/$arg1/edit"; - - case 'status': - $status = db_result(db_query('SELECT status FROM {users} WHERE uid = %d', $arg1)); - if ($status == 1) { - return SPAM_PUBLISHED; - } - else { - return SPAM_NOT_PUBLISHED; - } - - case 'overview_filter_join': - return 'INNER JOIN {users} u ON t.content_id = u.uid'; - - case 'overview_filter_where': - switch ($arg1) { - case 'title': - return "u.name LIKE '%%%s%%'"; - case 'status': - return "u.status != %d"; - } - - case 'feedback_filter_join_field': - return 'u.uid'; - - case 'publish': - // TODO: When un/publish user, should probably also un/publish content - if (is_numeric($arg1)) { - module_invoke('user', 'user_operations_unblock', array($arg1)); - } - break; - - case 'unpublish': - // TODO: When un/publish user, should probably also un/publish content - if (is_numeric($arg1)) { - module_invoke('user', 'user_operations_block', array($arg1)); - } - break; - - case 'feedback_form': - $form = array(); - if (is_numeric($form['uid'])) { - $form['uid'] = array( - '#type' => 'textfield', - '#title' => t('User ID'), - '#value' => $arg1['uid'], - '#disabled' => TRUE, - ); - } - // fall through... - case 'error_form': - if (!is_array($form)) { - $form = array(); - } - $form['name'] = array( - '#type' => 'textfield', - '#title' => t('Username'), - '#value' => $arg1['name'], - '#disabled' => TRUE, - ); - $form['mail'] = array( - '#type' => 'textfield', - '#title' => t('Email address'), - '#value' => $arg1['mail'], - '#disabled' => TRUE, - ); - // TODO: Show profile fields. - return $form; - } -} - -/** - * Be sure we're scanning the latest profile data. - */ -function spam_user_profile_update($user) { - if (module_exists('profile')) { - profile_load_profile($user); - } - return $user; -} - -/** - * Form alter gets its own function so we can reference &$form without causing - * errors in PHP4 installations. (If we use spamapi, we have to set a default, - * which PHP4 doesn't support.) - */ -function user_spamapi_form_alter(&$form, &$form_state, $form_id) { - // Scan users before they are inserted into the database - if ($form_id == 'user_register' && is_array($form)) { - $form['#validate'][] = 'user_spam_scan'; - } -} - -/** - * Scan user before it is created in the database. - */ -function user_spam_scan($form, &$form_state) { - if ($form_state['clicked_button']['#value'] == t('Create new account')) { - $account = $form['#post']; - $_SESSION['spam_form'] = $form; - spam_scan($account, 'user'); - } - else if (isset($_SESSION['spam_form'])) { - unset($_SESSION['spam_form']); - } -} diff -uprN /home/files/coding/drupal/spam/spam.module sites/all/modules/spam/spam.module --- /home/files/coding/drupal/spam/spam.module 2009-09-01 07:09:29.000000000 +1000 +++ sites/all/modules/spam/spam.module 2009-12-09 15:43:28.000000000 +1100 @@ -954,10 +954,10 @@ function spam_init_api() { if (!$initialized) { // We only need to module_load_include('initialized = TRUE', 'spam', ''); $path = drupal_get_path('module', 'spam') .'/modules'; - // These files must be names spam_*.inc, such as spam_node.inc. - $files = drupal_system_listing('spam_.*\.inc$', $path, 'name', 0); + // These files must be names spam_custom_*.inc, such as spam_node.inc. + $files = drupal_system_listing('spam_content_.*\.inc$', $path, 'name', 0); foreach ($files as $file) { - $module = substr_replace($file->name, '', 0, 5); + $module = substr_replace($file->name, '', 0, 13); if (module_exists($module)) { require_once './' . $file->filename; } @@ -971,7 +971,8 @@ function spam_init_api() { function spam_invoke_api() { $args = func_get_args(); array_unshift($args, 'spamapi'); - return call_user_func_array('module_invoke_all', $args); + $contentblah = call_user_func_array('module_invoke_all', $args); + return $contentblah; } /** @@ -980,6 +981,7 @@ function spam_invoke_api() { function spam_invoke_module() { $args = func_get_args(); $module = array_shift($args); + print_r($args,$module); array_unshift($args, $module, 'spamapi'); return call_user_func_array('module_invoke', $args); } diff -uprN /home/files/coding/drupal/spam/spam.module~ sites/all/modules/spam/spam.module~ --- /home/files/coding/drupal/spam/spam.module~ 1970-01-01 10:00:00.000000000 +1000 +++ sites/all/modules/spam/spam.module~ 2009-12-09 15:43:28.000000000 +1100 @@ -0,0 +1,2094 @@ +. All rights reserved. + */ + +define('SPAM_FILTER_ENABLED', 1); +define('SPAM_FILTER_DISABLED', 0); + +define('SPAM_ACTION_PREVENT_SILENT', 0); +define('SPAM_ACTION_PREVENT', 1); +define('SPAM_ACTION_HOLD', 2); +define('SPAM_ACTION_UNPUBLISH', 3); + +define('SPAM_DEFAULT_THRESHOLD', 86); + +define('SPAM_NOT_PUBLISHED', 0); +define('SPAM_PUBLISHED', 1); + +define('SPAM_LOG', 1); +define('SPAM_VERBOSE', 3); +define('SPAM_DEBUG', 5); + +spam_init_api(); + +/** + * API call for scanning content for spam. If spam is found, the appropriate + * action will be taken. + */ +function spam_scan($content, $type, $extra = array(), $filter_test = FALSE) { + if (user_access('bypass filters')) { + spam_log(SPAM_DEBUG, 'spam_scan', t('bypassing filters'), $type, $id); + return; + } + // bypass filters when admins publish content from spam feedback + if (isset($_SESSION['bypass_spam_filter'])) { + if (_spam_sign($content['form_token']) == $_SESSION['bypass_spam_filter']) { + unset($_SESSION['bypass_spam_filter']); + spam_log(SPAM_DEBUG, 'spam_scan', t('bypassing filters by request'), $type, $id); + return; + } + } + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_DEBUG, 'spam_scan', t('scanning content'), $type, $id); + spam_update_statistics(t('scan @type', array('@type' => $type))); + if (spam_content_is_spam($content, $type, $extra, $filter_test)) { + spam_log(SPAM_DEBUG, 'spam_scan', t('content is spam'), $type, $id); + spam_update_statistics(t('detected spam @type', array('@type' => $type))); + switch (variable_get('spam_visitor_action', SPAM_ACTION_PREVENT)) { + case SPAM_ACTION_PREVENT: + spam_log(SPAM_LOG, 'spam_scan', t('content is spam, action(prevent)'), $type, $id); + default: + $_SESSION['content'] = serialize((array)$content); + $_SESSION['type'] = $type; + spam_update_statistics(t('prevented spam @type', array('@type' => $type))); + drupal_goto('spam/denied'); + + case SPAM_ACTION_PREVENT_SILENT: + spam_log(SPAM_LOG, 'spam_scan', t('content is spam, action(prevent silently)'), $type, $id); + spam_update_statistics(t('silently prevented spam @type', array('@type' => $type))); + if ($id) { + // Content was already published, so we unpublish it. + spam_unpublish($type, $id, $extra); + } + // TODO: We redirect to avoid the content being posted, but we should + // be much smarter about where we redirect to. + drupal_goto(''); + break; + + case SPAM_ACTION_UNPUBLISH: + spam_log(SPAM_LOG, 'spam_scan', t('content is spam, action(unpublish)'), $type, $id); + spam_update_statistics(t('prevented spam @type', array('@type' => $type))); + if ($id) { + spam_unpublish($type, $id, $extra); + } + break; + + // TODO: SPAM_ACTION_HOLD + } + } +} + +/** + * API call to simply test if content is spam or not. No action is taken. + */ +function spam_content_is_spam($content, $type, $extra = array(), $filter_test = FALSE) { + if (user_access('bypass filters')) { + spam_log(SPAM_DEBUG, 'spam_content_is_spam', t('bypassing filters'), $type, $id); + return 0; + } + $score = spam_content_filter($content, $type, $extra, $filter_test); + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_DEBUG, 'spam_content_is_spam', t('checking if spam...'), $type, $id); + if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + if ($id) { + spam_mark_as_spam($type, $id, array('score' => $score)); + } + $spam = 1; + } + else { + $spam = 0; + } + spam_log(SPAM_DEBUG, 'spam_content_is_spam', t('score(@score) spam(@spam)', array('@score' => $score, '@spam' => $spam)), $type, $id); + return $spam; +} + +/** + * API call to determine the likeliness that a given piece of content is spam, + * returning a rating from 1% likelihood to 99% likelihood. It is unlikely + * that you want to call this function directly. + * + * @param $content + * An array holding the complete content. + * @param $type + * A string naming the type of content to be filtered. + */ +function spam_content_filter($content, $type, $extra, $filter_test = FALSE) { + if (user_access('bypass filters')) { + spam_log(SPAM_DEBUG, 'spam_content_filter', t('bypassing filters'), $type, $id); + return; + } + + if (!spam_filter_content_type($content, $type, $extra)) return; + + static $scores = array(); + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_DEBUG, 'spam_content_filter', t('invoking content filters'), $type, $id); + + // There's no need to scan the same content multiple times. + if (!$id || !isset($scores["$type-$id"])) { + // Determine which fields we need to run through the spam filter. + $fields = spam_invoke_module($type, 'filter_fields', $content, $extra); + if (!empty($fields) && is_array($fields['main'])) { + // TODO: Once content-type groups are implemented, this query will + // determine which group to filter the given piece of content with. It + // will default to a gid of 0 if undefined. + //$gid = (int)db_result(db_query("SELECT gid FROM {spam_filters_groups_data} WHERE content_type = '%s'", $type)); + $gid = $score = $total = 0; + $filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE gid = %d AND status = %d ORDER BY weight', $gid, SPAM_FILTER_ENABLED); + $counter = 0; + while ($filter = db_fetch_object($filters)) { + $counter++; + spam_log(SPAM_DEBUG, 'spam_content_filter', t('invoking @filter [@counter], gain = @gain', array('@filter' => $filter->name, '@counter' => $counter, '@gain' => $filter->gain)), $type, $id); + $actions[$filter->module] = spam_invoke_module($filter->module, 'filter', $type, $content, $fields, $extra, $filter_test); + spam_log(SPAM_VERBOSE, 'spam_content_filter', t('@filter: total(@total) redirect(@redirect) gain(@gain)', array('@filter' => $filter->name, '@total' => $actions[$filter->module]['total'], '@redirect' => $actions[$filter->module]['redirect'], '@gain' => $filter->gain)), $type, $id); + if ($actions[$filter->module]['total']) { + $score += ($actions[$filter->module]['total'] * $filter->gain); + $total += $filter->gain; + spam_log(SPAM_DEBUG, 'spam_content_filter', t('current score(@score) current total(@total) average(@average)', array('@score' => $score, '@total' => $total, '@average' => spam_sanitize_score($score / $total))), $type, $id); + if ($actions[$filter->module]['redirect']) { + $redirect = $actions[$filter->module]['redirect']; + break; + } + } + } + if (!$counter) { + spam_log(SPAM_VERBOSE, 'spam_content_filter', t('No filters enabled for group !gid, content not scanned.', array('!gid' => $gid)), $type, $id); + } + if ($total) { + $log_score = spam_sanitize_score($score / $total); + } + else { + $log_score = 1; + } + if ($id) { + $scores["$type-$id"] = $log_score; + } + } + else { + spam_log(SPAM_VERBOSE, 'spam_content_filter', t('No main filter field defined, skipping. Returned fields: !fields', array('!fields' => implode(', ', $fields))), $type, $id); + } + + if (isset($redirect)) { + if ($id) { + // A filter has us redirecting to an error screen, but this content + // has an id so we need to update its spam status in the database first. + if ($scores["$type-$id"] >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + spam_mark_as_spam($type, $id); + } + else { + spam_mark_as_not_spam($type, $id); + } + } + else { + spam_update_statistics(t('prevented spam @type', array('@type' => $type))); + } + spam_update_statistics(t('detected spam')); + spam_update_statistics(t('content_filter redirect')); + spam_log(SPAM_VERBOSE, 'spam_content_filter', t('Spam score [!score], redirecting to: !url', array('!score' => $log_score, '!url' => $redirect)), $type, $id); + drupal_goto($redirect); + } + } + else { + spam_log(SPAM_VERBOSE, 'spam_content_filter', t('Skipped content filters: id(!id) score(!score).', array('!id' => $id, '!score' => $scores["$type-$id"])), $type, $id); + } + if ($id) { + $score = $scores["$type-$id"]; + } + else if ($total) { + $score = spam_sanitize_score($score / $total); + } + else { + $score = 1; + } + spam_log(SPAM_LOG, 'spam_content_filter', t('final average(@score)', array('@score' => $score)), $type, $id); + + return $score; +} + +/** + * This function is called when new content is first posted to your website. + * + * @param $content + * An array holding the complete content. + * @param $type + * A string naming the type of content being inserted. + */ +function spam_content_insert($content, $type, $extra = array()) { + if (!spam_filter_content_type($content, $type, $extra)) return; + + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_VERBOSE, 'spam_content_insert', t('inserting'), $type, $id); + $score = 0; + + $error = FALSE; + if ($id) { + $score = spam_content_filter($content, $type, $extra); + db_query("INSERT INTO {spam_tracker} (content_type, content_id, score, hostname, timestamp) VALUES('%s', %d, %d, '%s', %d)", $type, $id, $score, ip_address(), time()); + $sid = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + if ($sid) { + watchdog('spam', 'Inserted %type with id %id into spam tracker table.', array('%type' => $type, '%id' => $id), WATCHDOG_NOTICE); + $extra['sid'] = $sid; + if (!isset($extra['host'])) { + // Content type modules can set this value, should REMOTE_ADDR not be + // the correct IP for their content type. + $extra['host'] = ip_address(); + } + $fields = spam_invoke_module($type, 'filter_fields', $content, $extra); + if (!empty($fields) && is_array($fields['main'])) { + $gid = 0; + $filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE gid = %d AND status = %d ORDER BY weight', $gid, SPAM_FILTER_ENABLED); + while ($filter = db_fetch_object($filters)) { + // Let filters act on insert action. + spam_invoke_module($filter->module, 'insert', $type, $content, $fields, $extra); + } + } + else { + watchdog('spam', 'Function spam_content_insert failed, no fields are defined for %type content type.', array('%type' => $type), WATCHDOG_ERROR); + $error = -3; + } + } + else { + watchdog('spam', 'Function spam_content_insert failed, unable to insert %type with id %id into spam_tracker table.', array('%type' => $type, '%id' => $id), WATCHDOG_ERROR); + $error = -2; + } + } + else { + watchdog('spam', 'Function spam_content_insert failed, unable to insert %type into spam_tracker table, no id found in the content array.', array('%type' => $type), WATCHDOG_ERROR); + $error = -1; + } + + // This content became spam during an insert, mark it as such. + if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + spam_mark_as_spam($type, $id, $extra); + $_SESSION['content'] = serialize((array)$content); + $_SESSION['type'] = $type; + spam_update_statistics(t('prevented spam @type', array('@type' => $type))); + spam_log(SPAM_DEBUG, 'spam_content_insert', t('redirecting to spam/denied'), $type, $id); + drupal_goto('spam/denied'); + } + + return $error; +} + +/** + * This function is called when content on your website is updated. + * + * @param $content + * An array holding the complete content. + * @param $type + * A string naming the type of content being updated. + */ +function spam_content_update($content, $type, $extra = array()) { + if (!spam_filter_content_type($content, $type, $extra)) return; + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_VERBOSE, 'spam_content_update', t('updating'), $type, $id); + + $error = FALSE; + $score = 0; + if ($id) { + $score = spam_content_filter($content, $type, $extra); + db_query("UPDATE {spam_tracker} SET score = %d, hostname = %d, timestamp = %d WHERE content_type = '%s' AND content_id = '%s'", $score, time(), $type, $id); + $sid = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + if ($sid) { + watchdog('spam', 'Updated %type with id %id in spam tracker table.', array('%type' => $type, '%id' => $id), WATCHDOG_NOTICE); + $extra['sid'] = $sid; + if (!isset($extra['host'])) { + // Content type modules can set this value, should REMOTE_ADDR not be + // the correct IP for their content type. + $extra['host'] = ip_address(); + } + $fields = spam_invoke_module($type, 'filter_fields', $content, $extra); + if (!empty($fields) && is_array($fields['main'])) { + $gid = 0; + $filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE gid = %d AND status = %d ORDER BY weight', $gid, SPAM_FILTER_ENABLED); + while ($filter = db_fetch_object($filters)) { + // Let filters act on insert action. + spam_invoke_module($filter->module, 'update', $type, $content, $fields, $extra); + } + } + else { + watchdog('spam', 'Function spam_content_update failed, no fields are defined for %type content type.', array('%type' => $type), WATCHDOG_ERROR); + $error = -3; + } + } + else { + watchdog('spam', 'Update to %type with id %id not filtered before, inserting.', array('%type' => $type, '%id' => $id), WATCHDOG_NOTICE); + // It seems that the content hasn't ever been scanned before, let's try + // inserting it. + $error = spam_content_insert($content, $type, $extra); + } + } + else { + watchdog('spam', 'Function spam_content_update failed, unable to update %type in spam_tracker table, no id found in the content array.', array('%type' => $type), WATCHDOG_ERROR); + $error = -1; + } + + // This content became spam during an update, mark it as such. + if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + spam_mark_as_spam($type, $id, $extra); + $_SESSION['content'] = serialize((array)$content); + $_SESSION['type'] = $type; + spam_update_statistics(t('prevented spam @type', array('@type' => $type))); + spam_log(SPAM_DEBUG, 'spam_content_update', t('redirecting to spam/denied'), $type, $id); + drupal_goto('spam/denied'); + } + + return $error; +} + +/** + * This function is called when content on your website is deleted. + * + * @param $content + * An array holding the complete content. + * @param $type + * A string naming the type of content being deleted. + */ +function spam_content_delete($content, $type, $extra = array()) { + $id = spam_invoke_module($type, 'content_id', $content, $extra); + spam_log(SPAM_VERBOSE, 'spam_content_delete', t('deleting'), $type, $id); + + $error = FALSE; + if ($id) { + $sid = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + if ($sid) { + $extra['sid'] = $sid; + if (!isset($extra['host'])) { + // Content type modules can set this value, should REMOTE_ADDR not be + // the correct IP for their content type. + $extra['host'] = ip_address(); + } + $fields = spam_invoke_module($type, 'filter_fields', $content, $extra); + if (!empty($fields) && is_array($fields['main'])) { + $gid = 0; + $filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE gid = %d AND status = %d ORDER BY weight', $gid, SPAM_FILTER_ENABLED); + while ($filter = db_fetch_object($filters)) { + // Let filters act on insert action. + spam_invoke_module($filter->module, 'delete', $type, $content, $fields, $extra); + } + } + else { + watchdog('spam', 'Function spam_content_delete failed, no fields are defined for %type content type.', array('%type' => $type), WATCHDOG_ERROR); + $error = -3; + } + db_query("DELETE FROM {spam_tracker} WHERE sid = %d", $sid); + watchdog('spam', 'Deleted %type content with id %id.', array('%type' => $type, '%id' => $id), WATCHDOG_NOTICE); + } + else { + watchdog('spam', 'Atempt to delete %type with id %id failed, does not exist in spam_tracker table.', array('%type' => $type, '%id' => $id), WATCHDOG_WARNING); + $error = -2; + } + } + else { + watchdog('spam', 'Function spam_content_delete failed, unable to delete %type from spam_tracker table, no id found in the content array.', array('%type' => $type), WATCHDOG_ERROR); + $error = -1; + } + + return $error; +} + +/** + * Increment internal counters. + */ +function spam_update_statistics($name, $op = '+', $inc = 1) { + if ($op != '+' && $op != '-') { + watchdog('spam', 'Invalid operator(@op), ignored.', array('@op' => $op)); + } + spam_log(LOG_DEBUG, 'spam_update_statistics', t('@name = @name @op @inc', array('@name' => $name, '@op' => $op, '@inc' => $inc))); + db_query("UPDATE {spam_statistics} SET count = count %s %d, timestamp = %d WHERE name = '%s'", $op, $inc, time(), $name); + if (!db_affected_rows()) { + if ($op == '-') { + $inc *= -1; + } + db_query("INSERT INTO {spam_statistics} (name, count, timestamp) VALUES('%s', %d, %d)", $name, $inc, time()); + } +} + +/*********************/ + +/** + * Drupal _cron hook. + */ +function spam_cron() { + // Delete expired logs. + if ($flush = variable_get('spam_log_delete', 259200)) { + db_query('DELETE FROM {spam_log} WHERE timestamp < %d', time() - $flush); + } +} + +/** + * Drupal _menu() hook. + */ +function spam_menu() { + $items = spam_invoke_api('menu'); + + $items['admin/content/spam'] = array( + 'title' => 'Spam', + 'page callback' => 'spam_admin_list', + 'access arguments' => array('administer spam'), + 'description' => 'Manage spam on your website.' + ); + $items['admin/content/spam/list'] = array( + 'title' => 'list', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'page callback' => 'spam_admin_list', + 'access arguments' => array('administer spam'), + ); + $items['admin/content/spam/feedback'] = array( + 'title' => 'feedback', + 'type' => MENU_LOCAL_TASK, + 'page callback' => 'spam_admin_list_feedback', + 'access arguments' => array('administer spam'), + 'weight' => 2, + ); + $items['admin/settings/spam'] = array( + 'title' => 'Spam', + 'page callback' => 'spam_admin_settings', + 'access arguments' => array('administer spam'), + 'description' => 'Configure the spam module.', + ); + $items['admin/settings/spam/general'] = array( + 'title' => 'General', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer spam'), + 'weight' => -4, + ); + $items['admin/settings/spam/filters'] = array( + 'title' => 'Filters', + 'page callback' => 'spam_admin_filter_overview', + 'type' => MENU_LOCAL_TASK, + 'access arguments' => array('administer spam'), + 'weight' => -2, + ); + $items['admin/settings/spam/filters/overview'] = array( + 'title' => 'Overview', + 'weight' => -2, + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer spam'), + ); +// TODO: Content groups. +/* + $items['admin/settings/spam/groups'] = array( + 'title' => 'Content groups', + 'weight' => 0, + 'page callback' => 'spam_admin_filter_groups', + 'access arguments' => array('administer spam'), + 'type' => MENU_LOCAL_TASK, + ); + */ + + $items['admin/reports/spam'] = array( + 'title' => 'Spam logs', + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_overview', + 'description' => 'Detect and manage spam posts.', + ); + $items['admin/reports/spam/logs'] = array( + 'title' => 'Logs', + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_overview', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/reports/spam/statistics'] = array( + 'title' => 'Statistics', + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_statistics', + 'type' => MENU_LOCAL_TASK, + 'weight' => -7, + ); + + $items['spam/denied'] = array( + 'page callback' => 'spam_denied_page', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + $items['spam/denied/error/%spam_hash/%'] = array( + 'title' => 'Report legitimate content', + 'load arguments' => array(4), + 'page callback' => 'spam_denied_in_error_page', + 'type' => MENU_CALLBACK, + 'access callback' => TRUE, + ); + $items['spam/%spam_mark/%/spam'] = array( + 'page callback' => 'spam_mark_as_spam', + 'page arguments' => array(1, 2, array('redirect' => TRUE)), + 'load arguments' => array(2, 3), + 'type' => MENU_CALLBACK, + 'access arguments' => array('administer spam'), + ); + $items['spam/%spam_mark/%/not_spam'] = array( + 'page callback' => 'spam_mark_as_not_spam', + 'page arguments' => array(1, 2, array('redirect' => TRUE)), + 'load arguments' => array(2, 3), + 'type' => MENU_CALLBACK, + 'access arguments' => array('administer spam'), + ); + $items["admin/reports/spam/%spam_number/detail"] = array( + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_entry', + 'page arguments' => array(3), + 'type' => MENU_LOCAL_CALLBACK, + ); + $items["admin/reports/spam/%spam_number/trace"] = array( + 'access arguments' => array('administer spam'), + 'page callback' => 'spam_logs_trace', + 'page arguments' => array(3), + 'type' => MENU_LOCAL_CALLBACK, + ); + $items["admin/content/spam/feedback/%spam_number"] = array( + 'title' => 'View feedback', + 'type' => MENU_CALLBACK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('spam_admin_feedback_form', 4), + 'access arguments' => array('administer spam'), + 'weight' => 2, + ); + + return $items; +} + +/** + * Wildcard loader function. + */ +function spam_hash_load($hash1, $hash2) { + return ($hash1 == md5($_SESSION['content']) && $hash2 = _spam_sign($_SESSION['content'])); +} + +/** + * Spam Mark wildcard loader function. + */ +function spam_mark_load($type, $id, $action) { + if (is_numeric($id) && ($action == 'spam' || $action == 'not_spam')) { + if (spam_invoke_module($type, 'content_module') == $type) { + if ($action == 'spam') { + spam_update_statistics(t('@type manually marked as spam', array('@type' => $type))); + } + else { + spam_update_statistics(t('@type manually marked as not spam', array('@type' => $type))); + } + return $type; + } + } + return $false; +} + +/** + * Spam module is_numeric test wildcard loader function. + */ +function spam_number_load($num) { + return is_numeric($num) ? $num : FALSE; +} + +/** + * Drupal _theme() hook. + */ +function spam_theme() { + $spam_theme_forms = array( + 'spam_admin_filters' => array( + 'file' => 'spam.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + 'spam_filter_form' => array( + 'file' => 'spam.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + 'spam_overview_filters' => array( + 'file' => 'spam.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + 'spam_admin_overview' => array( + 'file' => 'spam.module', + 'arguments' => array( + 'form' => NULL, + ), + ), + ); + return array_merge_recursive($spam_theme_forms, spam_invoke_api('theme_forms')); +} + +/** + * Drupal _perm hook. + */ +function spam_perm() { + return array('administer spam', 'bypass filters'); +} + +/** + * Online help. Drupal _help() hook. + */ +function spam_help($path, $arg) { + switch ($path) { + case 'admin/settings/spam': + return t('Enable and disable individual spam filters for each content type, controlling which order the content is passed through the filters.'); + break; + } +} + +/** + * Drupal form_alter() hook. + */ +function spam_form_alter(&$form, &$form_state, $form_id) { + foreach (module_list() as $module) { + // For PHP4, allows modules to update $form. + $function = $module .'_spamapi_form_alter'; + if (function_exists($function)) { + $function($form, $form_state, $form_id); + } + // Alternative method, for modules that don't need to update $form. + $function = $module .'_spamapi'; + if (function_exists($function)) { + $function('process_form', $form_id, $form); + } + } +} + +/** + * Drupal _link() hook. + */ +function spam_link($type, $content = 0, $main = 0) { + return spam_invoke_module($type, 'link', $content, $main); +} + +/****/ + +/** + * Spam filter overview page. Allows enabling/disabling, ordering, and tuning + * of individual filters, on a per-content-type-group basis. This allows you + * to enable different filters for different content types. + */ +function spam_admin_filter_overview() { + /** + * TODO: For phase one we will only have the default group. A later + * development phase will allow the creation/configuration of custom + * content-type groups. Content-types are defined through hooks, and + * include nodes (book, forum, etc), comments, users, profiles, etc... + */ + // Install any new filters that may have become available. + spam_init_filters(); + + $output = drupal_get_form('spam_admin_filters'); + return $output; +} + +/** + * Spam filter content-type groups page. Allows creation/deletion of + * content-type groups. Each content type can only be in one group. If a + * content-type is not specifically added to one of these groups, it is + * automatically part of the default group (gid=0). + */ +function spam_admin_filter_groups() { + $output = drupal_get_form('spam_admin_groups_form'); + return $output; +} + +/** + * Spam module settings page. + */ +function spam_admin_settings() { + $output = drupal_get_form('spam_admin_settings_form'); + return $output; +} + +function spam_admin_filters() { + $result = pager_query('SELECT fid, gid, name, status, weight, gain FROM {spam_filters} WHERE gid = %d ORDER BY weight ASC', 50, 0, NULL, 0); + + $counter = 0; + while ($filter = db_fetch_object($result)) { + $form['status']["status-$counter"] = array( + '#type' => 'checkbox', + '#default_value' => $filter->status, + ); + $form['name'][$counter] = array( + '#value' => $filter->name, + ); + $form['gain']["gain-$counter"] = array( + '#type' => 'select', + '#options' => drupal_map_assoc(spam_range(0, 250, 10)), + '#default_value' => $filter->gain, + ); + $form['weight']["weight-$counter"] = array( + '#type' => 'weight', + '#default_value' => $filter->weight, + ); + $form['fid']["fid-$counter"] = array( + '#type' => 'hidden', + '#value' => $filter->fid, + ); + $form['gid']["gid-$counter"] = array( + '#type' => 'hidden', + '#value' => $filter->gid, + ); + $counter++; + } + $form['pager'] = array( + '#value' => theme('pager', NULL, 50, 0) + ); + + $form['counter'] = array( + '#type' => 'hidden', + '#value' => $counter, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Update filters'), + ); + return $form; +} + +/** + * Perform the actual update. + */ +function spam_admin_filters_submit($form, &$form_state) { + for ($i = 0; $i < $form_state['values']['counter']; $i++) { + db_query('UPDATE {spam_filters} SET status = %d, gain = %d, weight = %d WHERE fid = %d AND gid = %d', $form_state['values']["status-$i"], $form_state['values']["gain-$i"], $form_state['values']["weight-$i"], $form_state['values']["fid-$i"], $form_state['values']["gid-$i"]); + } +} + +/** + * Display list of filters. + */ +function theme_spam_admin_filters($form) { + $header = array(t('Enabled'), t('Name'), t('Gain'), t('Weight')); + + if (isset($form['name']) && is_array($form['name'])) { + foreach (element_children($form['name']) as $key) { + $row = array(); + $row[] = drupal_render($form['status']["status-$key"]); + $row[] = drupal_render($form['name'][$key]); + $row[] = drupal_render($form['gain']["gain-$key"]); + $row[] = drupal_render($form['weight']["weight-$key"]); + $rows[] = $row; + } + } + else { + $rows[] = array(array('data' => t('There are currently no spam filters available.'), 'colspan' => 4)); + } + + $output .= theme('table', $header, $rows); + if ($form['pager']['#value']) { + $output .= drupal_render($form['pager']); + } + + $output .= drupal_render($form); + + return $output; +} + +/** + * Spam module groups form. + */ +function spam_admin_groups_form() { + $form['groups'] = array( + '#type' => 'fieldset', + '#value' => t('Groups'), + ); + return $form; +} + +/** + * Spam module settings form. + */ +function spam_admin_settings_form() { + $form['content'] = array( + '#type' => 'fieldset', + '#title' => t('Content to filter'), + '#collapsible' => TRUE, + ); + $modules = spam_invoke_api('content_module'); + foreach ($modules as $module) { + $content_types = spam_invoke_module($module, 'content_types'); + if (is_array($content_types)) { + foreach ($content_types as $content_type) { + $name = $content_type['name']; + $form['content']["spam_filter_$name"] = array( + '#type' => 'checkbox', + '#title' => t($content_type['title']), + '#description' => $content_type['description'], + '#default_value' => variable_get("spam_filter_$name", (int)$content_type['default_value']), + ); + } + } + } + + $form['actions'] = array( + '#type' => 'fieldset', + '#title' => t('Actions'), + '#collapsible' => TRUE, + ); + $form['actions']['spam_visitor_action'] = array( + '#type' => 'select', + '#title' => t('Posting action'), + '#options' => array(t('silently prevent spam content from being posted'), t('prevent spam content from being posted, notify visitor'), t('place spam into special review queue, notify visitor'), t('allow spam content to be posted, automatically unpublish and notify visitor')), + '#default_value' => variable_get('spam_visitor_action', SPAM_ACTION_PREVENT), + ); + $form['actions']['spam_filtered_message'] = array( + '#type' => 'textarea', + '#title' => t('Spam filter message'), + '#default_value' => variable_get('spam_filtered_message', t('

Your posting on @site from %IP has been automatically flagged by our spam filters as being inappropriate for this website.

At @site we work very hard behind the scenes to keep our web pages free of spam. Unfortunately, sometimes we accidentally block legitimate content. If you are attempting to post legitimate content to this website, you can help us to improve our spam filters by emailing the following information to a site administrator:

%LINK

', array('@site' => variable_get('site_name', 'Drupal')))), + '#description' => t('Message to show visitors when the spam filters block them from posting content. The text "%IP" will be replaced by the visitors actual IP address.') + ); + + // TODO: These options are for debugging the spam module. They should be + // disabled before the module is released. + $form['advanced'] = array( + '#type' => 'fieldset', + '#title' => t('Advanced configuration'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $options = drupal_map_assoc(spam_range(10, 40, 10)) + drupal_map_assoc(spam_range(45, 70, 5)) + drupal_map_assoc(spam_range(72, 88, 2)) + drupal_map_assoc(spam_range(90, 99)); + $form['advanced']['spam_threshold'] = array( + '#type' => 'select', + '#title' => t('Spam threshold'), + '#options' => $options, + '#default_value' => variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD), + '#description' => t('Each of filtered content will be assigned a single number from 1 to 99. This number signifies the percent of likelihood that the filtered content is spam. Any piece of content whose spam value is equal to or greater than this threshold will be considered spam. Any piece of content whose spam value is less than this threshold will be considered not spam.'), + ); + $form['advanced']['spam_log_level'] = array( + '#type' => 'select', + '#title' => t('Log level'), + '#options' => array(0 => t('Disabled'), SPAM_LOG => t('Important'), SPAM_VERBOSE => t('Verbose'), SPAM_DEBUG => t('Debug')), + '#default_value' => variable_get('spam_log_level', SPAM_LOG), + // TODO: Add informative description. + '#description' => t('Logging level.'), + ); + $period = drupal_map_assoc(array(0, 3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), 'format_interval'); + $period[0] = t('never'); + $form['advanced']['spam_log_delete'] = array( + '#type' => 'select', + '#title' => t('Discard spam logs older than'), + '#default_value' => variable_get('spam_log_delete', 259200), + '#options' => $period, + '#description' => t('Older spam log entries will be automatically discarded. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status'))) + ); + + return system_settings_form($form); +} + +/** + * Determine if we should be filtering a given content type. + */ +function spam_filter_content_type($content, $type, $extra) { + $filter = spam_invoke_module($type, 'filter_content_type', $content, $extra); + if (!$filter) { + spam_log(SPAM_DEBUG, 'spam_filter_content_type', t('not configured to scan this content type'), $type, $id); + } + return $filter; +} + +/** + * Determine if a given filter is enabled. + */ +function spam_filter_enabled($filter, $type, $content, $fields, $extra) { + return db_result(db_query("SELECT status FROM {spam_filters} WHERE module = '%s'", $filter)); +} + +/** + * Check if any new spam filters are available for installation. + */ +function spam_init_filters() { + static $initialized = FALSE; + + if (!$initialized) { + $modules = spam_invoke_api('filter_module'); + dprint_r($modules); + foreach ($modules as $module) { + $filter = spam_invoke_module($module, 'filter_info'); + $fid = db_result(db_query_range("SELECT fid FROM {spam_filters} WHERE name = '%s' AND module = '%s'", $filter['name'], $filter['module'], 0, 1)); + if (!$fid) { + spam_install_filter($filter); + } + } + } +} + +/** + * Install the named spam filter, making it available for detecting spam + * content. It will be configured per any defaults defined by the filter. + * + * @param $filter array + * array - must contain 'name' and 'module' elements + */ +function spam_install_filter($filter) { + // Typically we install a filter that's never been installed before. But + // it's also possible to use this function to restore a filter to its default + // settings. + db_query("DELETE FROM {spam_filters} WHERE name = '%s' AND module = '%s'", $filter['name'], $filter['module']); + $default['name'] = $filter['name']; + $default['module'] = $filter['module']; + $default['status'] = SPAM_FILTER_ENABLED; + $default['weight'] = 0; + $default['gain'] = 100; + // Allow module to override defaults. The module can also set other defaults + // when this hook is called. + $defaults = spam_invoke_module($filter['module'], 'filter_install', NULL, array(), array(), $default); + foreach ($defaults as $key => $value) { + $default[$key] = $value; + } + db_query("INSERT INTO {spam_filters} (name, module, status, weight, gain) VALUES('%s', '%s', %d, %d, %d)", $default['name'], $default['module'], $default['status'], $default['weight'], $default['gain']); +} + +/** + * As the spam module isn't a core Drupal module, many important modules won't + * utilize its API. We define the appropriate hooks for these modules in the + * modules/ subdirectory. For example, we define the spam api hooks for the + * node module in modules/spam_node.inc. + */ +function spam_init_api() { + static $initialized = FALSE; + + if (!$initialized) { + // We only need to module_load_include('initialized = TRUE', 'spam', ''); + $path = drupal_get_path('module', 'spam') .'/modules'; + // These files must be names spam_custom_*.inc, such as spam_node.inc. + $files = drupal_system_listing('spam_content_.*\.inc$', $path, 'name', 0); + foreach ($files as $file) { + $module = substr_replace($file->name, '', 0, 13); + if (module_exists($module)) { + require_once './' . $file->filename; + } + } + } +} + +/** + * Invoke spam API functions defined by other modules. + */ +function spam_invoke_api() { + $args = func_get_args(); + array_unshift($args, 'spamapi'); + $contentblah = call_user_func_array('module_invoke_all', $args); + return $contentblah; +} + +/** + * Invoke spam API functions in a specific module. + */ +function spam_invoke_module() { + $args = func_get_args(); + $module = array_shift($args); + print_r($args,$module); + array_unshift($args, $module, 'spamapi'); + return call_user_func_array('module_invoke', $args); +} + +/** + * Manage spam content. + */ +function spam_admin_list() { + $output = drupal_get_form('spam_filter_form'); + $output .= drupal_get_form('spam_admin_overview'); + return $output; +} + +/** + * Spam feedback overview. + */ +function spam_admin_list_feedback() { + $header = array( + array('data' => t('Date'), 'field' => 'timestamp', 'sort' => 'desc'), + array('data' => t('Type'), 'field' => 'content_type'), + array('data' => t('From'), 'field' => 'hostname'), + array('data' => t('Preview')), + array('data' => t('Options'))); + $sql = 'SELECT * FROM {spam_filters_errors}'; + $sql .= tablesort_sql($header); + $result = pager_query($sql, 25); + + $rows = array(); + while ($feedback = db_fetch_object($result)) { + $row = array(); + $row[] = array('data' => format_date($feedback->timestamp, 'small')); + $row[] = array('data' => $feedback->content_type); + $row[] = array('data' => $feedback->hostname); + $row[] = array('data' => _spam_truncate($feedback->feedback, 32)); + $row[] = l(t('view'), "admin/content/spam/feedback/$feedback->bid"); + $rows[] = $row; + } + + $output = theme('table', $header, $rows); + $output .= theme('pager', NULL, 25, 0); + return $output; +} + +/** + * Spam feedback details. + */ +function spam_admin_feedback_form($form_state, $bid) { + $form = array(); + + $feedback = db_fetch_object(db_query('SELECT * FROM {spam_filters_errors} WHERE bid = %d', $bid)); + + $form = spam_invoke_module($feedback->content_type, 'feedback_form', unserialize($feedback->content)); + if (!is_array($form)) { + $form = array(); + } + + $form['date'] = array( + '#type' => 'markup', + '#prefix' => '
' . t('Posted') . ':
', + '#value' => format_date($feedback->timestamp), + ); + + $form['feedback'] = array( + '#type' => 'textarea', + '#title' => t('Feedback'), + '#value' => $feedback->feedback, + '#disabled' => TRUE, + ); + + $trid = db_result(db_query_range("SELECT trid FROM {spam_log} WHERE content_type = '%s' AND content_id = %d", $feedback->content_type, $feedback->content_id, 0, 1)); + if (!empty($trid)) { + $form['logs'] = array( + '#type' => 'markup', + '#prefix' => '
', + '#suffix' => '
', + '#value' => l(t('Spam logs'), "admin/reports/spam/$trid/trace"), + ); + } + + $form['publish'] = array( + '#type' => 'submit', + '#value' => t('Publish content'), + ); + $form['delete'] = array( + '#type' => 'submit', + '#value' => t('Delete feedback'), + '#submit' => array('spam_admin_feedback_delete_submit'), + ); + $form['cancel'] = array( + '#value' => l(t('Cancel'), 'admin/content/spam/feedback'), + ); + + $form['content'] = array( + '#type' => 'hidden', + '#value' => $feedback->content, + ); + $form['spam_form'] = array( + '#type' => 'hidden', + '#value' => $feedback->form, + ); + $form['bid'] = array( + '#type' => 'hidden', + '#value' => $feedback->bid, + ); + $form['type'] = array( + '#type' => 'hidden', + '#value' => $feedback->content_type, + ); + $form['id'] = array( + '#type' => 'hidden', + '#value' => $feedback->content_id, + ); + + return $form; +} + +/** + * Process spam feedback. + */ +function spam_admin_feedback_form_submit($form, &$form_state) { + $content = unserialize($form_state['values']['content']); + // mark the content as not spam + $extra['content'] = $content; + spam_mark_as_not_spam($form_state['values']['type'], $form_state['values']['id'], $extra); + // publish the content + // TODO: don't execute form if content is already published + // return will contain a url to the new content + $form = unserialize($form_state['values']['spam_form']); + $_SESSION['bypass_spam_filter'] = _spam_sign($form['#post']['form_token']); + $form_state = array(); + $return = drupal_process_form($content['form_id'], $form, $form_state); + db_query('DELETE FROM {spam_filters_errors} WHERE bid = %d', $form_state['values']['bid']); + drupal_set_message(t('Content published.')); + drupal_goto('admin/content/spam/feedback'); +} + +function spam_admin_feedback_delete_submit($form, &$form_state) { + // TODO: Confirm the delete. + db_query('DELETE FROM {spam_filters_errors} WHERE bid = %d', $form_state['values']['bid']); + drupal_set_message(t('Feedback deleted.')); + drupal_goto('admin/content/spam/feedback'); +} + +function _spam_truncate($text, $length = 64) { + if (drupal_strlen($text) > $length) { + $text = drupal_substr($text, 0, $length) .'...'; + } + return $text; +} + +/** + * Return form for spam administration overview filters. + */ +function spam_filter_form() { + $session = &$_SESSION['spam_overview_filter']; + $session = is_array($session) ? $session : array(); + $filters = spam_overview_filters(); + + $i = 0; + $form['filters'] = array('#type' => 'fieldset', + '#title' => t('Show only spam where'), + '#theme' => 'spam_overview_filters', + ); + foreach ($session as $filter) { + list($type, $value) = $filter; + if ($filters[$type]['options']) { + $value = $filters[$type]['options'][$value]; + } + if ($type == 'feedback') { + if ($value) { + $value = t('not provided'); + } + else { + $value = t('provided'); + } + } + $string = ($i++ ? 'and where %a is %b' : '%a is %b'); + $form['filters']['current'][] = array('#value' => t($string, array('%a' => $filters[$type]['title'] , '%b' => $value))); + unset($filters[$type]); + } + + // See if module type is set, if not we can't yet perform certain filters. + if (isset($filters['module'])) { + unset($filters['title']); + unset($filters['status']); + unset($filters['feedback']); + } + + foreach ($filters as $key => $filter) { + $names[$key] = $filter['title']; + if ($filter['options']) { + $form['filters']['status'][$key] = array( + '#type' => 'select', + '#options' => $filter['options'] + ); + } + else if ($key == 'feedback') { + $form['filters']['status'][$key] = array( + '#type' => 'select', + '#options' => array(t('provided'), t('not provided')), + ); + } + else { + $form['filters']['status'][$key] = array( + '#type' => 'textfield', + '#size' => 15, + ); + } + } + + $form['filters']['filter'] = array('#type' => 'radios', '#options' => $names, '#default_value' => 'status'); + $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter'))); + if (count($session)) { + $form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo')); + $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset')); + } + + return $form; +} + +/** + * Theme spam administration filter form. + */ +function theme_spam_filter_form($form) { + $output .= '
'; + $output .= drupal_render($form['filters']); + $output .= '
'; + $output .= drupal_render($form); + return $output; +} + +/** + * Process result from ad administration filter form. + */ +function spam_filter_form_submit($form, &$form_state) { + $filters = spam_overview_filters(); + switch ($form_state['values']['op']) { + case t('Filter'): + case t('Refine'): + if (isset($form_state['values']['filter'])) { + $filter = $form_state['values']['filter']; + + if (isset($filters[$filter]['options'])) { + // Flatten the options array to accommodate hierarchical/nested options. + $flat_options = form_options_flatten($filters[$filter]['options']); + if (isset($flat_options[$form_state['values'][$filter]])) { + $_SESSION['spam_overview_filter'][] = array($filter, $form_state['values'][$filter]); + } + } + else { + $_SESSION['spam_overview_filter'][] = array($filter, $form_state['values'][$filter]); + } + } + break; + case t('Undo'): + array_pop($_SESSION['spam_overview_filter']); + break; + case t('Reset'): + $_SESSION['spam_overview_filter'] = array(); + break; + } +} + +/** + * List spam administration filters that can be applied. + */ +function spam_overview_filters() { + $filters['module'] = array( + 'title' => t('Type'), + 'options' => module_invoke_all('spamapi', 'content_module', array()), + ); + $filters['hostname'] = array( + 'title' => t('IP'), + ); + $filters['feedback'] = array( + 'title' => t('Feedback'), + ); + $filters['title'] = array( + 'title' => t('Title'), + ); + $filters['status'] = array( + 'title' => t('Status'), + 'options' => array(t('published'), t('not published')), + ); + + return $filters; +} + +/** + * Theme spam administration filter selector. + */ +function theme_spam_overview_filters($form) { + $output .= ''; + + if ($help) { + $output .= '

'. t('To filter on the Title or the Status, you must first filter on the Type.') .'

'; + } + + return $output; +} + +/** + * A filterable list of spam. + */ +function spam_admin_overview() { + $filter = spam_build_filter_query(); + $result = pager_query('SELECT t.* FROM {spam_tracker} t '. $filter['join'] .' '. $filter['where'] .' ORDER BY t.timestamp DESC', 50, 0, NULL, $filter['args']); + + $form['options'] = array('#type' => 'fieldset', + '#title' => t('Update options'), + '#prefix' => '
', + '#suffix' => '
', + ); + // Set desired callbacks as a subset of spam_spam_operations(). + $options = array('markasnotspam'=>array(), 'publish'=>array(), 'unpublish'=>array()); + $alloptions = module_invoke_all('spam_operations'); + foreach ($options as $operation => $array) { + $options[$operation] = $alloptions[$operation]['label']; + } + $form['options']['operation'] = array('#type' => 'select', '#options' => $options, '#default_value' => 'approve'); + $form['options']['submit'] = array('#type' => 'submit', '#value' => t('Update')); + + $destination = drupal_get_destination(); + while ($spam = db_fetch_object($result)) { + $all["$spam->content_type-$spam->content_id"] = ''; + $form['content_type']["$spam->content_type-$spam->content_id"] = array('#value' => $spam->content_type); + $title = spam_invoke_module($spam->content_type, 'title', $spam->content_id); + $link = spam_invoke_module($spam->content_type, 'edit_link', $spam->content_id); + $form['title']["$spam->content_type-$spam->content_id"] = array('#value' => l($title, $link)); + $form['score']["$spam->content_type-$spam->content_id"] = array('#value' => $spam->score); + $status = spam_invoke_module($spam->content_type, 'status', $spam->content_id); + $form['status']["$spam->content_type-$spam->content_id"] = array('#value' => $status == SPAM_PUBLISHED ? t('published') : t('not published')); + $form['hostname']["$spam->content_type-$spam->content_id"] = array('#value' => $spam->hostname); + $feedback = db_result(db_query("SELECT bid FROM {spam_filters_errors} WHERE content_type = '%s' AND content_id = %d", $spam->content_type, $spam->content_id)); + if ($feedback) { + $form['feedback']["$spam->content_type-$spam->content_id"] = array('#value' => l('view', "admin/content/spam/feedback/$feedback")); + } + else { + $form['feedback']["$spam->content_type-$spam->content_id"] = array('#value' => 'n/a'); + } + //$form['operations']["$spam->content_type-$spam->content_id"] = array('#value' => l(t('edit'), 'node/'. $ad->aid .'/edit', array('query' => $destination))); + } + $form['spam'] = array('#type' => 'checkboxes', '#options' => $all); + $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); + return $form; +} + +/** + * Required to select something. + */ +function spam_admin_overview_validate($form, &$form_state) { + $spam = array_filter($form_state['values']['spam']); + if (count($spam) == 0) { + form_set_error('', t('You have note selected any spam content.')); + } +} + +/** + * Submit the spam administration update form. Reusable for content + * submodules. This should probably be a TODO for going into the API docs, + * and replacing some custom submits. Submodules will need to define + * #spam_submit_valuetype and #spam_submit_itemtype in their forms. Should + * also specify their wanted callbacks as a subset of spam_spam_operations() + * (see spam_admin_overview for example). + */ +function spam_admin_overview_submit($form, &$form_state) { + $operations = module_invoke_all('spam_operations'); + if ($operations[$form_state['values']['operation']]) { + $operation = $operations[$form_state['values']['operation']]; + if (!($valuetype = $form['#spam_submit_valuetype'])) $valuetype="spam"; + // Filter out unchecked nodes + //$spam = array_filter($form_state['values'][$valuetype]); + if ($form['#spam_submit_itemtype']) { + foreach ($form_state['values'][$valuetype] as $item => $content) + $spam["{$form['#spam_submit_itemtype']}-$item"] = "{$form['#spam_submit_itemtype']}-$item"; + } + else { + $spam = $form_state['values'][$valuetype]; + } + if ($function = $operation['callback']) { + // Add in callback arguments if present. + if (isset($operation['callback arguments'])) { + $args = array_merge(array($spam), $operation['callback arguments']); + } + else { + $args = array($spam); + } + call_user_func_array($function, $args); + + cache_clear_all(); + drupal_set_message(t('The update has been performed.')); + } + } +} + +/** + * Theme spam administration overview. + */ +function theme_spam_admin_overview($form) { + // Overview table: + $header = array(theme('table_select_header_cell'), t('Type'), t('Title'), t('Score'), t('Status'), t('IP'), t('Feedback')); + + $output .= drupal_render($form['options']); + if (isset($form['title']) && is_array($form['title'])) { + foreach (element_children($form['title']) as $key) { + $row = array(); + $row[] = drupal_render($form['spam'][$key]); + $row[] = drupal_render($form['content_type'][$key]); + $row[] = drupal_render($form['title'][$key]); + $row[] = drupal_render($form['score'][$key]); + $row[] = drupal_render($form['status'][$key]); + $row[] = drupal_render($form['hostname'][$key]); + $row[] = drupal_render($form['feedback'][$key]); + $rows[] = $row; + } + + } + else { + $rows[] = array(array('data' => t('No spam content found.'), 'colspan' => '6')); + } + + $output .= theme('table', $header, $rows); + if ($form['pager']['#value']) { + $output .= drupal_render($form['pager']); + } + + $output .= drupal_render($form); + + return $output; +} + +/** + * Implementation of hook_spam_operations(). Probably doesn't need to be an + * API hook, because spam functions won't differ much between submodules (ex. + * mark and unmark) and are already abstracted from the content type. Forms + * should only implement what they want out of this. TODO: refactor out of + * content submodules, take out of API calls. See spam_admin_overview_submit. + */ +function spam_spam_operations() { + $operations = array( + 'markasspam' => array( + 'label' => t('Mark as not spam'), + 'callback' => 'spam_operations_callback', + 'callback arguments' => array('mark_as_spam'), + ), + 'markasnotspam' => array( + 'label' => t('Mark as not spam'), + 'callback' => 'spam_operations_callback', + 'callback arguments' => array('mark_as_not_spam'), + ), + 'trainasnotspam' => array( + 'label' => t('Train as not spam'), + 'callback' => 'spam_operations_callback', + 'callback arguments' => array('train_as_not_spam'), + ), + 'publish' => array( + 'label' => t('Publish'), + 'callback' => 'spam_operations_callback', + 'callback arguments' => array('publish'), + ), + 'unpublish' => array( + 'label' => t('Unpublish'), + 'callback' => 'spam_operations_callback', + 'callback arguments' => array('unpublish'), + ), + ); + return $operations; +} + +/** + * Callback function for admin mass editing spam. Mark as spam. + */ +function spam_operations_callback($spam, $op) { + foreach ($spam as $content) { + $pieces = explode('-', $content); + if (drupal_strlen($pieces[0]) && is_numeric($pieces[1]) && drupal_strlen($op)) { + $func = "spam_{$op}"; + if (function_exists($func)) { + spam_log(SPAM_VERBOSE, 'spam_operations_callback', t('bulk update @op', array('@op' => $op)), $pieces[0], $pieces[1]); + $func($pieces[0], $pieces[1]); + } + else { + spam_log(SPAM_LOG, 'spam_operations_callback', t('no such function (@func), failed op(@op)', array('@func' => $func, '@op' => $op)), $pieces[0], $pieces[1]); + } + } + else { + spam_log(SPAM_LOG, 'spam_operations_callback', t('unexpected error: content_type(@type) content_id(@id) op(@op)', array('@type' => $pieces[0], '@id' => $pieces[1], '@op' => $op))); + } + } +} + +/** + * Build query for spam administration filters based on session. + */ +function spam_build_filter_query() { + $filters = spam_overview_filters(); + + // Build query + $where = $args = array(); + $join = ''; + $where[] = 't.score >= '. variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD); + foreach ($_SESSION['spam_overview_filter'] as $index => $filter) { + list($key, $value) = $filter; + switch ($key) { + case 'module': + $modules = spam_invoke_api('content_module', array()); + $where[] = "t.content_type = '%s'"; + $args[] = $type = $modules[$value]; + break; + case 'hostname': + $where[] = "t.hostname LIKE '%%%s%%'"; + $args[] = $value; + break; + case 'title': + $join = spam_invoke_module($type, 'overview_filter_join', 'title'); + $where[] = spam_invoke_module($type, 'overview_filter_where', 'title'); + $args[] = $value; + break; + case 'status': + $join = spam_invoke_module($type, 'overview_filter_join', 'status'); + $where[] = spam_invoke_module($type, 'overview_filter_where', 'status'); + $args[] = $value; + break; + case 'feedback': + if ($value) { + $join = "INNER JOIN {spam_filters_errors} e ON e.content_id != t.content_id"; + } + else { + $join = "INNER JOIN {spam_filters_errors} e ON e.content_id = t.content_id"; + } + break; + } + } + $where = count($where) ? 'WHERE '. implode(' AND ', $where) : ''; + + return array('where' => $where, 'join' => $join, 'args' => $args); +} + +/** + * Be sure the spam score is within the allowable range of 1 and 99. + */ +function spam_sanitize_score($score) { + if ((int)$score < 1) { + return 1; + } + else if ((int)$score > 99) { + return 99; + } + return round($score, 0); +} + +/** + * Helper function, returns a randomized token used to build a unique path for + * reporting mis-filtered content. Intended as a spam deterrent. + */ +function _spam_sign($text) { + if (!variable_get('spam_sign_start', '')) { + variable_set('spam_sign_start', rand(0, 22)); + variable_set('spam_sign_end', rand(45, 115)); + } + if (!variable_get('spam_sign_key', '')) { + variable_set('spam_sign_key', md5(microtime())); + } + // TODO: Be sure signing is changing... + return md5(drupal_substr($text, variable_get('spam_sign_start', 0), variable_get('spam_sign_end', 11)) . variable_get('spam_sign_key', '')); +} + +/** + * Helper function, generate a link for reporting mis-filtered content. + */ +function _spam_error_link($text) { + if (($text) && is_array(unserialize($text))) { + return l(t('Report spam filter error.'), "spam/denied/error/" . md5($text) .'/'. _spam_sign($text)); + } + else { + return t('Unable to generate link. Please be sure you have cookies enabled and try your posting again.'); + } +} + +/** + * Generate an error message informing the user that their posting has been + * blocked by the spam filter. Provide a dynamic link for reporting if their + * posting was blocked in error. + * + * TODO: We don't need to use the maintenance_page, we could create our own + * using the current theme. + */ +function spam_denied_page($message = NULL, $title = NULL) { + drupal_set_header('HTTP/1.1 403 Forbidden'); + if (!$message) { + $message = strtr(variable_get('spam_filtered_message', t('

Your posting on @site from %IP has been automatically flagged by our spam filters as being inappropriate for this website.

At @site we work very hard to keep our web pages free of spam. Unfortunately, sometimes we accidentally block legitimate content. If you are attempting to post legitimate content to this website, you can help us to improve our spam filters and ensure that your post appears on our website by clicking this link:

%LINK
', array('@site' => variable_get('site_name', 'Drupal'), ))), array('%IP' => ip_address(), '%LINK' => _spam_error_link($_SESSION['content']))); + } + if (!$title) { + $title = t('Your posting was blocked by our spam filter.'); + } + drupal_set_title($title); + print theme('maintenance_page', filter_xss_admin($message)); +} + +/** + * Allow the user to report when their content was inapropriately marked as + * spam. + */ +function spam_denied_in_error_page() { + if ($_SESSION['content']) { + $content = unserialize($_SESSION['content']); + if (is_array($content)) { + $hash = md5($_SESSION['content']); + $exists = db_result(db_query("SELECT bid FROM {spam_filters_errors} WHERE content_hash = '%s'", $hash)); + if ($exists) { + $output = t('You have already reported this content as not spam. Please be patient; a site administrator will review it soon.'); + } + else { + return drupal_get_form('spam_error_page'); + } + } + } + return $output; +} + +/** + * Require user reporting non-spam to submit feedback. + */ +// TODO: add captcha +function spam_error_page() { + $content = unserialize($_SESSION['content']); + $type = $_SESSION['type']; + $form = $_SESSION['spam_form']; + + $form = spam_invoke_module($type, 'error_form', $content); + if (!is_array($form)) { + $form = array(); + } + + $form['feedback'] = array( + '#type' => 'textarea', + '#title' => t('Feedback'), + '#required' => TRUE, + '#description' => t('Please offer some feedback to the site administrator, explaining how your content is relevant to this website.'), + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Send'), + ); + return $form; +} + +/** + * Store reported legitimate content in database. + */ +function spam_error_page_submit($form, &$form_state) { + global $user; + $content = unserialize($_SESSION['content']); + $type = $_SESSION['type']; + $id = spam_invoke_module($type, 'content_id', $content); + $hash = md5($_SESSION['content']); + if (is_array($_SESSION['spam_form'])) { + $spam_form = serialize($_SESSION['spam_form']); + } + else { + $spam_form = $_SESSION['spam_form']; + } + db_query("INSERT INTO {spam_filters_errors} (uid, content_type, content_id, content_hash, content, form, hostname, feedback, timestamp) VALUES(%d, '%s', %d, '%s', '%s', '%s', '%s', '%s', %d)", $user->uid, $type, $id, $hash, $_SESSION['content'], $spam_form, ip_address() , $form_state['values']['feedback'], time()); + $_SESSION['content'] = $_SESSION['type'] = $_SESSION['spam_form'] = ''; + drupal_set_message(t('Your feedback will be reviewed by a site administrator.')); + drupal_goto(''); +} + +/** + * Add the appropriate links to all content that is actively being filtered. + */ +function spam_links($type, $id, $content) { + $links = array(); + if (spam_invoke_module($type, 'filter_content_type', $content)) { + if (user_access('administer spam')) { + $score = (int)db_result(db_query("SELECT score FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + + if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + $links['spam'] = array('title' => t('spam (@score)', array('@score' => $score))); + $links['mark-as-not-spam'] = array('href' => "spam/$type/$id/not_spam", 'title' => t('mark as not spam')); + } + else { + $links['spam'] = array('title' => t('not spam (@score)', array('@score' => $score))); + $links['mark-as-spam'] = array('href' => "spam/$type/$id/spam", 'title' => t('mark as spam')); + } + } + } + return $links; +} + +/** + * Invoke appropriate actions for marking content as spam. + * TODO: Integrate with the Actions module, making actions fully configurable. + */ +function spam_mark_as_spam($type, $id, $extra = array()) { + // TODO: Fix this loop + static $loop = array(); + if (isset($loop[$id])) { + spam_log(SPAM_DEBUG, 'spam_mark_as_spam', t('FIX ME: looping'), $type, $id); + return; + } + $loop[$id] = TRUE; + + spam_update_statistics(t('@type marked as spam', array('@type' => $type))); + $extra['sid'] = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + if (!$extra['score']) { + $extra['score'] = 99; + } + spam_log(SPAM_VERBOSE, 'spam_mark_as_spam', t('marked as spam, score(@score)', array('@score' => $extra['score'])), $type, $id); + if ($extra['sid']) { + db_query('UPDATE {spam_tracker} SET score = %d WHERE sid = %d', $extra['score'], $extra['sid']); + $extra['content'] = spam_invoke_module($type, 'load', $id); + } + else { + $hostname = spam_invoke_module($type, 'hostname', $id); + db_query("INSERT INTO {spam_tracker} (content_type, content_id, score, hostname, timestamp) VALUES('%s', %d, %d, '%s', %d)", $type, $id, $extra['score'], $hostname, time()); + $extra['sid'] = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + $extra['content'] = spam_invoke_module($type, 'load', $id); + } + $extra['id'] = $id; + spam_invoke_api('mark_as_spam', $type, array(), array(), $extra); + + if ($id) { + // For now, we're hard coding the actions... + spam_unpublish($type, $id); + } + + if ($extra['redirect']) { + spam_invoke_module($type, 'redirect', $id); + } +} + +/** + * Invoke appropriate actions for marking content as not spam. + * TODO: Integrate with the Actions module, making actions fully configurable. + */ +function spam_mark_as_not_spam($type, $id, $extra = array()) { + // TODO: Fix this loop + static $loop = array(); + if (isset($loop[$id])) { + spam_log(SPAM_DEBUG, 'spam_mark_as_not_spam', t('FIX ME: looping'), $type, $id); + return; + } + $loop[$id] = TRUE; + + spam_update_statistics(t('@type marked as not spam', array('@type' => $type))); + $extra['sid'] = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + if (!$extra['score']) { + $extra['score'] = 1; + } + spam_log(SPAM_VERBOSE, 'spam_mark_as_not_spam', t('marked as not spam, score(@score)', array('@score' => $extra['score'])), $type, $id); + if ($extra['sid']) { + db_query('UPDATE {spam_tracker} SET score = %d WHERE sid = %d', $extra['score'], $extra['sid']); + } + else if ($id) { + $hostname = spam_invoke_module($type, 'hostname', $id); + db_query("INSERT INTO {spam_tracker} (content_type, content_id, score, hostname, timestamp) VALUES('%s', %d, %d, '%s', %d)", $type, $id, $extra['score'], $hostname, time()); + $extra['sid'] = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = %d", $type, $id)); + } + if (!isset($extra['content'])) { + $extra['content'] = spam_invoke_module($type, 'load', $id); + } + + $extra['id'] = $id; + spam_invoke_api('mark_as_not_spam', $type, array(), array(), $extra); + + if ($id) { + // For now, we're hard coding the actions... + spam_publish($type, $id); + } + + if ($extra['redirect']) { + spam_invoke_module($type, 'redirect', $id); + } +} + +/** + * Extract text from content array. + */ +function spam_get_text($content, $type, $fields, $extra = array(), $full = TRUE) { + if (is_object($content)) { + $content = (array)$content; + } + + $text = ''; + + foreach ($fields['main'] as $field) { + $text .= $content[$field] .' '; + } + if ($full && is_array($fields['other'])) { + foreach ($fields['other'] as $field) { + $text .= $content[$field] .' '; + } + } + return $text; +} + +/** + * Write to the spam_log database table. + */ +function spam_log($level, $function, $message, $type = NULL, $id = NULL) { + global $user; + + $trid = _spam_log_trace($message, $type, $id); + + if (variable_get('spam_log_level', SPAM_LOG) >= $level) { + db_query("INSERT INTO {spam_log} (level, trid, content_type, content_id, uid, function, message, hostname, timestamp) VALUES(%d, %d, '%s', %d, %d, '%s', '%s', '%s', %d)", $level, $trid, $type, $id, $user->uid, $function, $message, ip_address(), time()); + } +} + +/** + * Maintain a "trace id", allowing easy tracing of all spam actions for each + * page load. Only active if logging is set to verbose or higher. + */ +function _spam_log_trace($message, $type, $id) { + global $user; + static $trid = NULL; + + if (!$trid && (variable_get('spam_log_level', SPAM_DEBUG) >= SPAM_VERBOSE)) { + $key = md5(microtime() . $message); + db_query("INSERT INTO {spam_log} (level, content_type, content_id, uid, function, message, hostname, timestamp) VALUES(%d, '%s', %d, %d, '%s', '%s', '%s', %d)", SPAM_VERBOSE, $type, $id, $user->uid, '_spam_log_trace', $key, ip_address(), time()); + $trid = db_result(db_query("SELECT lid FROM {spam_log} WHERE message = '%s'", $key)); + if ($trid) { + db_query("UPDATE {spam_log} SET trid = %d, message = '%s' WHERE lid = %d", $trid, t('--'), $trid); + } + else { + $trid = 1; + spam_log(SPAM_LOG, '_spam_log_trace', t('Failed to obtain a valid trid.')); + } + } + return $trid; +} + +/** + * Display statistics overview. + */ +function spam_logs_statistics() { + drupal_set_title("Spam statistics"); + + $statistics = array(); + + $stats = array( + array( + 'title' => 'scanned @module', + 'query' => 'scan %s', + ), + array( + 'title' => 'prevented @module spam', + 'query' => 'prevented spam %s', + ), + array( + 'title' => 'marked @module as spam', + 'query' => '%s marked as spam', + ), + array( + 'title' => 'manually marked @module as spam', + 'query' => '%s manually marked as spam', + ), + array( + 'title' => 'marked @module as not spam', + 'query' => '%s marked as not spam', + ), + array( + 'title' => 'manually marked @module as not spam', + 'query' => '%s manually marked as not spam', + ), + ); + + $header = array('', t('Action'), t('Count'), t('Last')); + $displayed = array(); + $modules = spam_invoke_api('content_module'); + foreach ($modules as $module) { + foreach ($stats as $stat) { + $query = str_replace('@name', $stat['query'], "SELECT * FROM {spam_statistics} WHERE name = '@name'"); + if ($result = db_fetch_object(db_query($query, $module))) { + $row = array(); + if (!isset($displayed[$module])) { + $displayed[$module] = TRUE; + $row[] = array('data' => "$module", 'colspan' => 4); + $rows[] = $row; + $row = array(); + } + $row[] = ''; + $row[] = array('data' => t($stat['title'], array('@module' => $module))); + $row[] = array('data' => number_format($result->count)); + $row[] = array('data' => t('@time ago', array('@time' => format_interval(time() - $result->timestamp)))); + $rows[] = $row; + } + } + } + + $output = theme('table', $header, $rows); + return $output; +} + +/** + * Display an overview of the latest spam_log entries. + */ +function spam_logs_overview($type = NULL, $id = NULL) { + drupal_set_title(t('Spam module logs')); + + $header = array( + array('data' => t('type'), 'field' => 'content_type'), + array('data' => t('id'), 'field' => 'content_id'), + array('data' => t('date'), 'field' => 'lid', 'sort' => 'desc'), + array('data' => t('message'), 'field' => 'message'), + array('data' => t('user'), 'field' => 'uid'), + array('data' => t('operations')), + ); + + if ($id) { + $sql = "SELECT * FROM {spam_log} WHERE content_type = '%s' AND content_id = %d"; + $arguments = array($type, $id); + } + else if ($type) { + $sql = "SELECT * FROM {spam_log} WHERE content_type = '%s'"; + $arguments = array($type); + } + else { + $sql = "SELECT * FROM {spam_log}"; + $arguments = array(); + } + + $result = pager_query($sql . tablesort_sql($header), 50, 0, NULL, $arguments); + + while ($log = db_fetch_object($result)) { + $options = ''; + if ($log->trid > 1) { + $options = l(t('trace'), "admin/reports/spam/$log->trid/trace") .' | '; + } + $options .= l(t('detail'), "admin/reports/spam/$log->lid/detail"); + $rows[] = array('data' => array( + t($log->content_type), + $log->content_id, + format_date($log->timestamp, 'small'), + truncate_utf8($log->message, 64) . (drupal_strlen($log->message) > 64 ? '...' : ''), + theme('username', user_load(array('uid' => $log->uid))), + $options, + ) + ); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); + } + + return theme('table', $header, $rows) . theme('pager', NULL, 50, 0); +} + +/** + * Displays complete information about a single log entry. + */ +function spam_logs_entry($id = NULL) { + if (!$id) { + return NULL; + } + + $breadcrumb[] = l(t('Home'), NULL); + $breadcrumb[] = l(t('Administer'), 'admin'); + $breadcrumb[] = l(t('Logs'), 'admin/reports'); + $breadcrumb[] = l(t('Spam'), 'admin/reports/spam'); + $breadcrumb[] = l(t('Spam module log entry'), 'admin/reports/spam/detail'); + drupal_set_breadcrumb($breadcrumb); + + $message = db_fetch_object(db_query('SELECT * FROM {spam_log} WHERE lid = %d', $id)); + + if ($message->content_type) { + $table[] = array( array('data' => t('Content type'), 'header' => TRUE), array('data' => l(t($message->content_type), "admin/reports/spam/$message->content_type")) ); + } + else { + $table[] = array( array('data' => t('Content type'), 'header' => TRUE), array('data' => t('unknown')) ); + } + if ($message->content_id) { + $table[] = array( array('data' => t('!type ID', array('!type' => drupal_ucfirst($message->content_type))), 'header' => TRUE), array('data' => l(t($message->content_id), "admin/reports/spam/$message->content_type/$message->content_id")) ); + } + $table[] = array( array('data' => t('Date'), 'header' => TRUE), array('data' => format_date($message->timestamp, 'large')) ); + $table[] = array( array('data' => t('User'), 'header' => TRUE), array('data' => theme('username', user_load(array('uid' => $message->uid)))) ); + $table[] = array( array('data' => t('Spam module function'), 'header' => TRUE), array('data' => $message->function) ); + $table[] = array( array('data' => t('Message'), 'header' => TRUE), array('data' => $message->message) ); + $table[] = array( array('data' => t('Hostname'), 'header' => TRUE), array('data' => $message->hostname) ); + $table[] = array( array('data' => t('Options'), 'header' => TRUE), array('data' => l(t('trace'), "admin/reports/spam/$message->trid/trace")) ); + return theme('table', NULL, $table); +} + +/** + * Trace all logs generated by the same page load. + */ +function spam_logs_trace($trid = NULL) { + if (!$trid) return; + + drupal_set_title(t('Spam module logs trace')); + + $breadcrumb[] = l(t('Home'), NULL); + $breadcrumb[] = l(t('Administer'), 'admin'); + $breadcrumb[] = l(t('Logs'), 'admin/reports'); + $breadcrumb[] = l(t('Spam'), 'admin/reports/spam'); + $breadcrumb[] = l(t('Spam module log trace'), 'admin/reports/spam/trace'); + drupal_set_breadcrumb($breadcrumb); + + $header = array( + array('data' => t('type'), 'field' => 'content_type'), + array('data' => t('id'), 'field' => 'content_id'), + array('data' => t('date'), 'field' => 'lid', 'sort' => 'asc'), + array('data' => t('function'), 'field' => 'function'), + array('data' => t('message'), 'field' => 'message'), + array('data' => t('user'), 'field' => 'uid'), + array('data' => t('operations')), + ); + + $sql = "SELECT * FROM {spam_log} WHERE trid = %d"; + $arguments = array($trid); + + $result = pager_query($sql . tablesort_sql($header), 50, 0, NULL, $arguments); + + while ($log = db_fetch_object($result)) { + $options = l(t('detail'), "admin/reports/spam/$log->lid/detail"); + $rows[] = array('data' => array( + t($log->content_type), + $log->content_id, + format_date($log->timestamp, 'small'), + truncate_utf8($log->function, 20) . (drupal_strlen($log->function) > 20 ? '...' : ''), + truncate_utf8($log->message, 64) . (drupal_strlen($log->message) > 64 ? '...' : ''), + theme('username', user_load(array('uid' => $log->uid))), + $options, + ) + ); + } + + if (!$rows) { + $rows[] = array(array('data' => t('No log messages available.'), 'colspan' => 6)); + } + + return theme('table', $header, $rows) . theme('pager', NULL, 50, 0); +} + +function spam_score_is_spam($score) { + if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) { + return (TRUE); + } + else { + return FALSE; + } +} + +/** + * Support PHP4 which has no 'step' parameter in its range() function. + */ +function spam_range($low, $high, $step = 1) { + if (version_compare(phpversion(), '5') < 0) { + // Emultate range with a step paramater for PHP4 users. + $rng = array(); + for ($i = $low; $i <= $high; $i+=$step) { + $rng[] = $i; + } + return $rng; + } + else { + return range($low, $high, $step); + } +} + +/** + * Invoke unpublish action for given content type. + * TODO: Integrate with the Actions module. + */ +function spam_unpublish($type, $id, $extra = array()) { + spam_log(SPAM_VERBOSE, 'spam_unpublish', t('unpublished'), $type, $id); + spam_invoke_module($type, 'unpublish', $id, $extra); + cache_clear_all(); + spam_update_statistics(t('unpublish @type', array('@type' => $type))); +} + +/** + * Invoke unpublish action for given content type. + * TODO: Integrate with the Actions module. + */ +function spam_publish($type, $id, $extra = array()) { + spam_log(SPAM_VERBOSE, 'spam_publish', t('published'), $type, $id); + spam_invoke_module($type, 'publish', $id, $extra); + cache_clear_all(); + spam_update_statistics(t('publish @type', array('@type' => $type))); +}