View source
<?php
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);
function spam_scan($content, $type, $extra = array(), $filter_test = FALSE) {
$id = spam_invoke_module($type, 'content_id', $content, $extra);
spam_log(SPAM_DEBUG, 'spam_scan', t('scanning content'), $type, $id);
$spam = spam_content_is_spam($content, $type, $extra, $filter_test);
if (spam_bypass_filters() || user_access('bypass filters')) {
spam_log(SPAM_DEBUG, 'spam_scan', t('bypassing spam actions'), $type, $id);
return $spam['score'];
}
_spam_update_statistics(t('scan @type', array(
'@type' => $type,
)));
if ($spam['is_spam']) {
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_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,
)));
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;
case SPAM_ACTION_HOLD:
$_SESSION['spam_content'] = serialize((array) $content);
$_SESSION['spam_type'] = $type;
spam_log(SPAM_LOG, 'spam_scan', t('content is spam, holding'), $type, $id);
_spam_update_statistics(t('held spam @type', array(
'@type' => $type,
)));
if ($id) {
spam_hold($type, $id, $content);
}
break;
case SPAM_ACTION_PREVENT:
spam_log(SPAM_LOG, 'spam_scan', t('content is spam, action(prevent)'), $type, $id);
default:
$_SESSION['spam_content'] = serialize((array) $content);
$_SESSION['spam_type'] = $type;
_spam_update_statistics(t('prevented spam @type', array(
'@type' => $type,
)));
drupal_goto('spam/denied');
break;
}
}
}
function spam_content_is_spam($content, $type, $extra = array(), $filter_test = FALSE) {
static $scores = array();
$id = spam_invoke_module($type, 'content_id', $content, $extra);
if (!isset($scores["{$type}-{$id}"])) {
$score = spam_content_filter($content, $type, $extra, $filter_test);
spam_log(SPAM_DEBUG, 'spam_content_is_spam', t('checking if spam...'), $type, $id);
if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) {
$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);
$scores["{$type}-{$id}"] = array(
'score' => $score,
'is_spam' => $spam,
);
}
return $scores["{$type}-{$id}"];
}
function spam_content_filter($content, $type, $extra, $filter_test = FALSE) {
$id = spam_invoke_module($type, 'content_id', $content, $extra);
if (!spam_filter_content_type($content, $type, $extra)) {
return;
}
static $scores = array();
if (isset($scores["{$type}-{$id}"])) {
return $scores["{$type}-{$id}"];
}
spam_log(SPAM_DEBUG, 'spam_content_filter', t('invoking content filters'), $type, $id);
if (!$id || !isset($scores["{$type}-{$id}"])) {
$fields = spam_invoke_module($type, 'filter_fields', $content, $extra);
if (!empty($fields) && is_array($fields['main'])) {
$score = $total = 0;
$filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE status = %d ORDER BY weight', 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);
$actions_total = empty($actions[$filter->module]['total']) ? 0 : $actions[$filter->module]['total'];
spam_log(SPAM_VERBOSE, 'spam_content_filter', t('@filter: total(@total) redirect(@redirect) gain(@gain)', array(
'@filter' => $filter->name,
'@total' => $actions_total,
'@redirect' => isset($actions[$filter->module]['redirect']) ? $actions[$filter->module]['redirect'] : '',
'@gain' => $filter->gain,
)), $type, $id);
if ($actions_total) {
$score += $actions_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 (isset($actions[$filter->module]['redirect'])) {
if (!isset($extra['redirect']) || $extra['redirect']) {
$redirect = $actions[$filter->module]['redirect'];
break;
}
}
}
}
if (!$counter) {
spam_log(SPAM_VERBOSE, 'spam_content_filter', t('No filters enabled, content not scanned.'), $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) {
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);
if (spam_bypass_filters() || user_access('bypass filters')) {
spam_log(SPAM_DEBUG, 'spam_content_filter', t('bypassing filter redirect'), $type, $id);
return;
}
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;
}
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);
if (spam_bypass_filters() || user_access('bypass filters')) {
spam_log(SPAM_DEBUG, 'spam_content_insert', t('bypassing filters'), $type, $id);
return;
}
spam_log(SPAM_VERBOSE, 'spam_content_insert', t('inserting'), $type, $id);
$score = 0;
$error = FALSE;
if ($id) {
$spam = spam_content_is_spam($content, $type, $extra);
$score = $spam['score'];
$sid = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = '%s'", $type, $id));
if (!$sid) {
db_query("INSERT INTO {spam_tracker} (content_type, content_id, score, hostname, timestamp) VALUES('%s', '%s', %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 = '%s'", $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'])) {
$extra['host'] = ip_address();
}
$fields = spam_invoke_module($type, 'filter_fields', $content, $extra);
if (!empty($fields) && is_array($fields['main'])) {
$filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE status = %d ORDER BY weight', SPAM_FILTER_ENABLED);
while ($filter = db_fetch_object($filters)) {
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;
}
if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) {
spam_mark_as_spam($type, $id, $extra);
$_SESSION['spam_content'] = serialize((array) $content);
$_SESSION['spam_type'] = $type;
if (!in_array(variable_get('spam_visitor_action', SPAM_ACTION_PREVENT), array(
SPAM_ACTION_HOLD,
))) {
_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;
}
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);
if (spam_bypass_filters() || user_access('bypass filters')) {
spam_log(SPAM_DEBUG, 'spam_content_update', t('bypassing filters'), $type, $id);
return;
}
spam_log(SPAM_VERBOSE, 'spam_content_update', t('updating'), $type, $id);
$error = FALSE;
$score = 0;
if ($id) {
$spam = spam_content_is_spam($content, $type, $extra);
$score = $spam['score'];
db_query("UPDATE {spam_tracker} SET score = %d, hostname = '%s', timestamp = %d WHERE content_type = '%s' AND content_id = '%s'", $score, ip_address(), time(), $type, $id);
$sid = db_result(db_query("SELECT sid FROM {spam_tracker} WHERE content_type = '%s' AND content_id = '%s'", $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'])) {
$extra['host'] = ip_address();
}
$fields = spam_invoke_module($type, 'filter_fields', $content, $extra);
if (!empty($fields) && is_array($fields['main'])) {
$filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE status = %d ORDER BY weight', SPAM_FILTER_ENABLED);
while ($filter = db_fetch_object($filters)) {
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);
$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;
}
if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) {
spam_mark_as_spam($type, $id, $extra);
$_SESSION['spam_content'] = serialize((array) $content);
$_SESSION['spam_type'] = $type;
if (!in_array(variable_get('spam_visitor_action', SPAM_ACTION_PREVENT), array(
SPAM_ACTION_HOLD,
))) {
_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;
}
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 = '%s'", $type, $id));
if ($sid) {
$extra['sid'] = $sid;
if (!isset($extra['host'])) {
$extra['host'] = ip_address();
}
$fields = spam_invoke_module($type, 'filter_fields', $content, $extra);
if (!empty($fields) && is_array($fields['main'])) {
$filters = db_query('SELECT name, module, gain FROM {spam_filters} WHERE status = %d ORDER BY weight', SPAM_FILTER_ENABLED);
while ($filter = db_fetch_object($filters)) {
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', 'Removed %type (id %id) from spam_tracker table.', array(
'%type' => $type,
'%id' => $id,
), WATCHDOG_NOTICE);
}
else {
watchdog('spam', 'Attempt to remove %type (id %id) from spam_tracker failed: does not exist.', array(
'%type' => $type,
'%id' => $id,
), WATCHDOG_NOTICE);
$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;
}
function _spam_update_statistics($name, $op = '+', $inc = 1) {
spam_log(LOG_DEBUG, 'spam_update_statistics', t('@name = @name @op @inc', array(
'@name' => $name,
'@op' => $op,
'@inc' => $inc,
)));
if ($op != '+' && $op != '-') {
watchdog('spam', 'Invalid operator(@op), ignored.', array(
'@op' => $op,
));
return;
}
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());
}
}
function spam_cron() {
if ($flush = variable_get('spam_log_delete', 259200)) {
db_query('DELETE FROM {spam_log} WHERE timestamp < %d', time() - $flush);
}
}
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',
),
);
$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_callback',
'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_callback',
'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_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_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;
}
function spam_hash_load($hash1, $hash2) {
return $hash1 == md5($_SESSION['spam_content']) && ($hash2 = _spam_sign($_SESSION['spam_content']));
}
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;
}
function spam_number_load($num) {
return is_numeric($num) ? $num : FALSE;
}
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'));
}
function spam_perm() {
return array(
'administer spam',
'bypass filters',
);
}
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;
}
}
function spam_form_alter(&$form, &$form_state, $form_id) {
foreach (module_list() as $module) {
$function = $module . '_spamapi_form_alter';
if (function_exists($function)) {
$function($form, $form_state, $form_id);
}
$function = $module . '_spamapi';
if (function_exists($function)) {
$function('process_form', $form_id, $form);
}
}
}
function spam_link($type, $content = 0, $main = 0) {
return spam_invoke_module($type, 'link', $content, $main);
}
function spam_admin_filter_overview() {
spam_init_filters();
$output = drupal_get_form('spam_admin_filters');
return $output;
}
function spam_admin_settings() {
$output = drupal_get_form('spam_admin_settings_form');
return $output;
}
function spam_admin_filters() {
$result = pager_query('SELECT fid, name, status, weight, gain FROM {spam_filters} 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,
);
$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;
}
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', $form_state['values']["status-{$i}"], $form_state['values']["gain-{$i}"], $form_state['values']["weight-{$i}"], $form_state['values']["fid-{$i}"]);
}
}
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;
}
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' => $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(
SPAM_ACTION_PREVENT_SILENT => t('silently prevent spam content from being posted'),
SPAM_ACTION_PREVENT => t('prevent spam content from being posted, notify visitor'),
SPAM_ACTION_UNPUBLISH => t('allow spam content to be posted, automatically unpublish and notify visitor'),
SPAM_ACTION_HOLD => t('allow spam content to be posted, do not unpublish and do not 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('<p>Your posting on @site from %IP has been automatically flagged by our spam filters as being inappropriate for this website.</p><p>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:</p><p>%LINK</p>', 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.'),
);
$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),
'#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 <a href="@cron">cron maintenance task</a>.)', array(
'@cron' => url('admin/reports/status'),
)),
);
return system_settings_form($form);
}
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);
}
return $filter;
}
function spam_filter_enabled($filter, $type, $content, $fields, $extra) {
return db_result(db_query("SELECT status FROM {spam_filters} WHERE module = '%s'", $filter));
}
function spam_init_filters() {
static $initialized = FALSE;
if (!$initialized) {
$modules = spam_invoke_api('filter_module');
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);
}
}
}
}
function spam_install_filter($filter) {
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;
$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']);
}
spam_load_inc_files();
function spam_load_inc_files() {
$path = drupal_get_path('module', 'spam') . '/content';
$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;
}
}
}
function spam_invoke_api() {
$args = func_get_args();
array_unshift($args, 'spamapi');
return call_user_func_array('module_invoke_all', $args);
}
function spam_invoke_module() {
$args = func_get_args();
$module = array_shift($args);
array_unshift($args, $module, 'spamapi');
return call_user_func_array('module_invoke', $args);
}
function spam_admin_list() {
$output = drupal_get_form('spam_filter_form');
$output .= drupal_get_form('spam_admin_overview');
return $output;
}
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;
}
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' => '<div><strong>' . t('Posted') . ':</strong></div>',
'#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 = '%s'", $feedback->content_type, $feedback->content_id, 0, 1));
if (!empty($trid)) {
$form['logs'] = array(
'#type' => 'markup',
'#prefix' => '<div>',
'#suffix' => '</div>',
'#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;
}
function spam_admin_feedback_form_submit($form, &$form_state) {
$content = unserialize($form_state['values']['content']);
$extra['content'] = $content;
$extra['type'] = $form_state['values']['type'];
$extra['feedback_form'] = TRUE;
spam_mark_as_not_spam($form_state['values']['type'], $form_state['values']['id'], $extra);
$values = $form_state['values'];
if (!spam_invoke_module($form_state['values']['type'], 'feedback_approved', $form_state['values']['id'], $extra)) {
if (variable_get('spam_visitor_action', SPAM_ACTION_PREVENT)) {
$form = unserialize($form_state['values']['spam_form']);
$_SESSION['spam_bypass_spam_filter'] = TRUE;
$form_state = array();
$return = drupal_process_form($content['form_id'], $form, $form_state);
}
}
db_query('DELETE FROM {spam_filters_errors} WHERE bid = %d', $values['bid']);
drupal_set_message(t('Content published.'));
drupal_goto('admin/content/spam/feedback');
}
function spam_admin_feedback_delete_submit($form, &$form_state) {
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;
}
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++ ? '<em>and</em> where <strong>%a</strong> is <strong>%b</strong>' : '<strong>%a</strong> is <strong>%b</strong>';
$form['filters']['current'][] = array(
'#value' => t($string, array(
'%a' => $filters[$type]['title'],
'%b' => $value,
)),
);
unset($filters[$type]);
}
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;
}
function theme_spam_filter_form($form) {
$output .= '<div id="spam-admin-filter">';
$output .= drupal_render($form['filters']);
$output .= '</div>';
$output .= drupal_render($form);
return $output;
}
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'])) {
$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;
}
}
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;
}
function theme_spam_overview_filters($form) {
$output .= '<ul class="clear-block">';
if (sizeof($form['current'])) {
foreach (element_children($form['current']) as $key) {
$output .= '<li>' . drupal_render($form['current'][$key]) . '</li>';
}
}
$help = FALSE;
$output .= '<li><dl class="multiselect">' . (sizeof($form['current']) ? '<dt><em>' . t('and') . '</em> ' . t('where') . '</dt>' : '') . '<dd class="a">';
foreach (element_children($form['filter']) as $key) {
if ($key == 'module') {
$help = TRUE;
}
$output .= drupal_render($form['filter'][$key]);
}
$output .= '</dd>';
$output .= '<dt>' . t('is') . '</dt><dd class="b">';
foreach (element_children($form['status']) as $key) {
$output .= drupal_render($form['status'][$key]);
}
$output .= '</dd>';
$output .= '</dl>';
$output .= '<div class="container-inline" id="spam-admin-buttons">' . drupal_render($form['buttons']) . '</div>';
$output .= '</li></ul>';
if ($help) {
$output .= '<p><em>' . t('To filter on the Title or the Status, you must first filter on the Type.') . '</em></p>';
}
return $output;
}
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' => '<div class="container-inline">',
'#suffix' => '</div>',
);
$options = array(
'markasnotspam' => array(),
'publish' => array(),
'unpublish' => array(),
);
$operations = module_invoke_all('spam_operations');
foreach ($options as $option => $array) {
$options[$option] = $operations[$option]['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);
if ($link) {
$form['title']["{$spam->content_type}-{$spam->content_id}"] = array(
'#value' => l($title, $link),
);
}
else {
$form['title']["{$spam->content_type}-{$spam->content_id}"] = array(
'#value' => check_plain($title),
);
}
$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 = '%s'", $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' => '<em>n/a</em>',
);
}
}
$form['spam'] = array(
'#type' => 'checkboxes',
'#options' => $all,
);
$form['pager'] = array(
'#value' => theme('pager', NULL, 50, 0),
);
return $form;
}
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 not selected any spam content.'));
}
}
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";
}
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']) {
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.'));
}
}
}
function theme_spam_admin_overview($form) {
$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' => '7',
),
);
}
$output .= theme('table', $header, $rows);
if ($form['pager']['#value']) {
$output .= drupal_render($form['pager']);
}
$output .= drupal_render($form);
return $output;
}
function spam_spam_operations() {
$operations = array(
'markasspam' => array(
'label' => t('Mark as 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;
}
function spam_operations_callback($spam, $op) {
foreach ($spam as $content) {
$pieces = explode('-', $content);
if (drupal_strlen($pieces[0]) && drupal_strlen($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,
)));
}
}
}
function spam_build_filter_query() {
$filters = spam_overview_filters();
$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,
);
}
function spam_sanitize_score($score) {
if ((int) $score < 1) {
return 1;
}
else {
if ((int) $score > 99) {
return 99;
}
}
return round($score, 0);
}
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()));
}
return md5(drupal_substr($text, variable_get('spam_sign_start', 0), variable_get('spam_sign_end', 11)) . variable_get('spam_sign_key', ''));
}
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.');
}
}
function spam_denied_page($message = NULL, $title = NULL) {
global $theme;
$no_maintenance = !empty($theme);
if (!$no_maintenance) {
drupal_maintenance_theme();
}
drupal_set_header('HTTP/1.1 403 Forbidden');
if (!$message) {
$message = strtr(variable_get('spam_filtered_message', t('<p>Your posting on @site from %IP has been automatically flagged by our spam filters as being inappropriate for this website.</p>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:</p><blockquote>%LINK</blockquote>', array(
'@site' => variable_get('site_name', 'Drupal'),
))), array(
'%IP' => ip_address(),
'%LINK' => _spam_error_link($_SESSION['spam_content']),
));
}
if (!$title) {
$title = t('Your posting was blocked by our spam filter');
}
drupal_set_title($title);
if (!$no_maintenance) {
echo theme('maintenance_page', filter_xss_admin($message));
}
else {
echo '<html><head><title>', check_plain($title), '</title></head><body><h1>', check_plain($title), '</h1><p>', filter_xss_admin($message), '</p></body></html>';
}
}
function spam_denied_in_error_page() {
if ($_SESSION['spam_content']) {
$content = unserialize($_SESSION['spam_content']);
if (is_array($content)) {
$hash = md5($_SESSION['spam_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;
}
function spam_error_page() {
$content = unserialize($_SESSION['spam_content']);
$type = $_SESSION['spam_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;
}
function spam_error_page_submit($form, &$form_state) {
spam_feedback_insert($form_state['values']['feedback']);
drupal_set_message(t('Your feedback will be reviewed by a site administrator.'));
$form_state['redirect'] = '';
}
function spam_feedback_insert($feedback) {
global $user, $language;
$content = unserialize($_SESSION['spam_content']);
$type = $_SESSION['spam_type'];
$id = spam_invoke_module($type, 'content_id', $content);
$hash = md5($_SESSION['spam_content']);
if (is_array($_SESSION['spam_form'])) {
$spam_form = serialize($_SESSION['spam_form']);
}
else {
$spam_form = $_SESSION['spam_form'];
}
if (!db_result(db_query("SELECT COUNT(*) FROM {spam_filters_errors} WHERE content_hash = '%s'", $hash))) {
db_query("INSERT INTO {spam_filters_errors} (uid, language, content_type, content_id, content_hash, content, form, hostname, feedback, timestamp) VALUES(%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d)", $user->uid, $language->language, $type, $id, $hash, $_SESSION['spam_content'], $spam_form, ip_address(), $feedback, time());
}
$_SESSION['spam_content'] = $_SESSION['spam_type'] = $_SESSION['spam_form'] = '';
}
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 = '%s'", $type, $id));
if ($score >= variable_get('spam_threshold', SPAM_DEFAULT_THRESHOLD)) {
$links['spam'] = array(
'title' => t('spam (@score)', array(
'@score' => $score,
)),
);
$token = drupal_get_token("not spam {$type} {$id}");
$links['mark-as-not-spam'] = array(
'href' => "spam/{$type}/{$id}/not_spam",
'title' => t('mark as not spam'),
'query' => array(
'token' => $token,
),
);
}
else {
$token = drupal_get_token("spam {$type} {$id}");
$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'),
'query' => array(
'token' => $token,
),
);
}
}
}
return $links;
}
function spam_mark_as_spam_callback($type, $id, $extra = array()) {
if (drupal_valid_token($_GET['token'], "spam {$type} {$id}")) {
spam_mark_as_spam($type, $id, $extra);
}
else {
return drupal_access_denied();
}
}
function spam_mark_as_not_spam_callback($type, $id, $extra = array()) {
if (drupal_valid_token($_GET['token'], "not spam {$type} {$id}")) {
spam_mark_as_not_spam($type, $id, $extra);
}
else {
return drupal_access_denied();
}
}
function spam_mark_as_spam($type, $id, $extra = array()) {
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 = '%s'", $type, $id));
if (empty($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']);
}
else {
$hostname = spam_invoke_module($type, 'hostname', $id);
db_query("INSERT INTO {spam_tracker} (content_type, content_id, score, hostname, timestamp) VALUES('%s', '%s', %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 = '%s'", $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) {
if (!in_array(variable_get('spam_visitor_action', SPAM_ACTION_PREVENT), array(
SPAM_ACTION_HOLD,
))) {
spam_unpublish($type, $id);
}
}
if (!empty($extra['redirect'])) {
spam_invoke_module($type, 'redirect', $id);
}
}
function spam_mark_as_not_spam($type, $id, $extra = array()) {
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 = '%s'", $type, $id));
if (!isset($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', '%s', %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 = '%s'", $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) {
$_SESSION['spam_bypass_spam_filter'] = TRUE;
spam_publish($type, $id);
}
if (!empty($extra['redirect'])) {
spam_invoke_module($type, 'redirect', $id);
}
}
function spam_get_text($content, $type, $fields, $extra = array(), $full = TRUE) {
if (is_object($content)) {
$content = (array) $content;
}
$text = '';
if (isset($fields['main']) && is_array($fields['main'])) {
foreach ($fields['main'] as $field) {
$text .= $content[$field] . ' ';
}
}
if ($full && isset($fields['other']) && is_array($fields['other'])) {
foreach ($fields['other'] as $field) {
$text .= $content[$field] . ' ';
}
}
return $text;
}
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', '%s', %d, '%s', '%s', '%s', %d)", $level, $trid, $type, $id, $user->uid, $function, $message, ip_address(), time());
}
}
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', '%s', %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;
}
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' => "<b>{$module}</b>",
'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;
}
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 = '%s'";
$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);
}
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);
}
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' => 7,
),
);
}
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;
}
}
function spam_range($low, $high, $step = 1) {
if (version_compare(phpversion(), '5') < 0) {
$rng = array();
for ($i = $low; $i <= $high; $i += $step) {
$rng[] = $i;
}
return $rng;
}
else {
return range($low, $high, $step);
}
}
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,
)));
}
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,
)));
}
function spam_hold($type, $id, $extra = array()) {
spam_log(SPAM_VERBOSE, 'spam_hold', t('held'), $type, $id);
spam_invoke_module($type, 'hold', $id, $extra);
}
function spam_action_info() {
$spam_actions = array();
$spam_types = array(
'node',
'comment',
'user',
);
foreach ($spam_types as $type) {
if (module_exists($type)) {
$spam_actions['spam_mark_' . $type . '_as_spam_action'] = array(
'description' => t('Mark ' . $type . ' as spam'),
'type' => $type,
'configurable' => FALSE,
'hooks' => array(
'any' => TRUE,
),
);
$spam_actions['spam_mark_' . $type . '_as_not_spam_action'] = array(
'description' => t('Mark ' . $type . ' as not spam'),
'type' => $type,
'configurable' => FALSE,
'hooks' => array(
'any' => TRUE,
),
);
}
}
return $spam_actions;
}
function spam_bypass_filters() {
if (isset($_SESSION['spam_bypass_spam_filter']) && $_SESSION['spam_bypass_spam_filter']) {
unset($_SESSION['spam_bypass_spam_filter']);
return TRUE;
}
return FALSE;
}