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);
spam_init_api();
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;
}
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) {
spam_unpublish($type, $id, $extra);
}
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;
}
}
}
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;
}
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);
if (!$id || !$scores["{$type}-{$id}"]) {
$fields = spam_invoke_module($type, 'filter_fields', $content, $extra);
if (!empty($fields) && is_array($fields['main'])) {
$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 ($id) {
if ($total) {
$scores["{$type}-{$id}"] = spam_sanitize_score($score / $total);
}
else {
$scores["{$type}-{$id}"] = 1;
}
}
}
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'));
drupal_goto($redirect);
}
}
if ($id) {
$score = $scores["{$type}-{$id}"];
}
else {
if ($total) {
$score = spam_sanitize_score($score / $total);
}
else {
$score = 1;
}
}
spam_log(SPAM_VERBOSE, '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);
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, $_SERVER['REMOTE_ADDR'], 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', t('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'] = $_SERVER['REMOTE_ADDR'];
}
$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)) {
spam_invoke_module($filter->module, 'insert', $type, $content, $fields, $extra);
}
}
else {
watchdog('spam', t('Function spam_content_insert failed, no fields are defined for %type content type.', array(
'%type' => $type,
)), WATCHDOG_ERROR);
$error = -3;
}
}
else {
watchdog('spam', t('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', t('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['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;
}
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', t('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'] = $_SERVER['REMOTE_ADDR'];
}
$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)) {
spam_invoke_module($filter->module, 'update', $type, $content, $fields, $extra);
}
}
else {
watchdog('spam', t('Function spam_content_update failed, no fields are defined for %type content type.', array(
'%type' => $type,
)), WATCHDOG_ERROR);
$error = -3;
}
}
else {
watchdog('spam', t('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', t('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['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;
}
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'])) {
$extra['host'] = $_SERVER['REMOTE_ADDR'];
}
$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)) {
spam_invoke_module($filter->module, 'delete', $type, $content, $fields, $extra);
}
}
else {
watchdog('spam', t('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', t('Deleted %type content with id %id.', array(
'%type' => $type,
'%id' => $id,
)), WATCHDOG_NOTICE);
}
else {
watchdog('spam', t('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', t('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) {
if ($op != '+' && $op != '-') {
watchdog('spam', t('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());
}
}
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($may_cache) {
$items = spam_invoke_api('menu', $may_cache);
if ($may_cache) {
$items[] = array(
'path' => 'admin/content/spam',
'title' => t('Spam'),
'callback' => 'spam_admin_list',
'access' => user_access('administer spam'),
'description' => t('Manage spam on your website.'),
);
$items[] = array(
'path' => 'admin/content/spam/list',
'title' => t('list'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'callback' => 'spam_admin_list',
'access' => user_access('administer spam'),
);
$items[] = array(
'path' => 'admin/content/spam/feedback',
'title' => t('feedback'),
'type' => MENU_LOCAL_TASK,
'callback' => 'spam_admin_list_feedback',
'access' => user_access('administer spam'),
'weight' => 2,
);
$items[] = array(
'path' => 'admin/settings/spam',
'title' => t('Spam'),
'callback' => 'spam_admin_settings',
'access' => user_access('administer spam'),
'description' => t('Configure the spam module.'),
);
$items[] = array(
'path' => 'admin/settings/spam/general',
'title' => t('General'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'access' => user_access('administer spam'),
'weight' => -4,
);
$items[] = array(
'path' => 'admin/settings/spam/filters',
'title' => t('Filters'),
'callback' => 'spam_admin_filter_overview',
'type' => MENU_LOCAL_TASK,
'access' => user_access('administer spam'),
'weight' => -2,
);
$items[] = array(
'path' => 'admin/settings/spam/filters/overview',
'title' => t('Overview'),
'weight' => -2,
'type' => MENU_DEFAULT_LOCAL_TASK,
'access' => user_access('administer spam'),
);
$items[] = array(
'path' => 'admin/logs/spam',
'title' => t('Spam logs'),
'access' => user_access('administer spam'),
'callback' => 'spam_logs_overview',
'description' => t('Detect and manage spam posts.'),
);
$items[] = array(
'path' => 'admin/logs/spam/logs',
'title' => t('Logs'),
'access' => user_access('administer spam'),
'callback' => 'spam_logs_overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[] = array(
'path' => 'admin/logs/spam/statistics',
'title' => t('Statistics'),
'access' => user_access('administer spam'),
'callback' => 'spam_logs_statistics',
'type' => MENU_LOCAL_TASK,
'weight' => -7,
);
$items[] = array(
'path' => 'spam/denied',
'callback' => 'spam_denied_page',
'type' => MENU_CALLBACK,
'access' => TRUE,
);
}
else {
if (arg(0) == 'spam') {
if (arg(1) == 'denied' && arg(2) == 'error') {
$hash1 = md5($_SESSION['content']);
$hash2 = _spam_sign($_SESSION['content']);
if (arg(3) == $hash1 && arg(4) == $hash2) {
$items[] = array(
'path' => "spam/denied/error/{$hash1}/{$hash2}",
'title' => t('Report legitimate content'),
'callback' => 'spam_denied_in_error_page',
'type' => MENU_CALLBACK,
'access' => TRUE,
);
}
}
if (is_numeric(arg(2)) && (arg(3) == 'spam' || arg(3) == 'notspam')) {
$type = arg(1);
if (spam_invoke_module($type, 'content_module') == $type) {
$id = arg(2);
$action = arg(3);
if ($action == 'spam') {
$callback = 'spam_mark_as_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,
)));
$callback = 'spam_mark_as_not_spam';
}
$items[] = array(
'path' => "spam/{$type}/{$id}/{$action}",
'callback' => $callback,
'callback arguments' => array(
$type,
$id,
array(
'redirect' => TRUE,
),
),
'type' => MENU_CALLBACK,
'access' => TRUE,
);
}
}
}
$sid = arg(3);
if (is_numeric($sid)) {
$items[] = array(
'path' => "admin/logs/spam/{$sid}/detail",
'access' => user_access('administer spam'),
'callback' => 'spam_logs_entry',
'callback arguments' => array(
$sid,
),
'type' => MENU_LOCAL_CALLBACK,
);
$items[] = array(
'path' => "admin/logs/spam/{$sid}/trace",
'access' => user_access('administer spam'),
'callback' => 'spam_logs_trace',
'callback arguments' => array(
$sid,
),
'type' => MENU_LOCAL_CALLBACK,
);
}
$bid = arg(4);
if (is_numeric($bid)) {
$items[] = array(
'path' => "admin/content/spam/feedback/{$bid}",
'title' => t('View feedback'),
'type' => MENU_CALLBACK,
'callback' => 'drupal_get_form',
'callback arguments' => array(
'spam_admin_feedback_form',
$bid,
),
'access' => user_access('administer spam'),
'weight' => 2,
);
}
}
return $items;
}
function spam_perm() {
return array(
'administer spam',
'bypass filters',
);
}
function spam_help($path) {
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_id, &$form) {
foreach (module_list() as $module) {
$function = $module . '_spamapi_form_alter';
if (function_exists($function)) {
$function($form_id, $form);
}
$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_filter_groups() {
$output = drupal_get_form('spam_admin_groups_form');
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, 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;
}
function spam_admin_filters_submit($form_id, $form_values) {
for ($i = 0; $i < $form_values['counter']; $i++) {
db_query('UPDATE {spam_filters} SET status = %d, gain = %d, weight = %d WHERE fid = %d AND gid = %d', $form_values["status-{$i}"], $form_values["gain-{$i}"], $form_values["weight-{$i}"], $form_values["fid-{$i}"], $form_values["gid-{$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_groups_form() {
$form['groups'] = array(
'#type' => 'fieldset',
'#value' => t('Groups'),
);
return $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'][$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('<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_admin_settings_form_submit($form_id, $form_values) {
$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'];
if ($form_values['op'] == t('Reset to defaults')) {
variable_del("spam_filter_{$name}");
}
else {
variable_set("spam_filter_{$name}", $form_values[$name]);
if ($form_values[$name]) {
$filter_types[] = $name;
}
}
}
}
}
variable_set('filter_types', implode(',', $filter_types));
if ($form_values['op'] == t('Reset to defaults')) {
variable_del('spam_visitor_action');
variable_del('spam_filtered_message');
variable_del('spam_threshold');
variable_del('spam_log_level');
variable_del('spam_log_delete');
}
else {
variable_set('spam_visitor_action', $form_values['spam_visitor_action']);
variable_set('spam_filtered_message', $form_values['spam_filtered_message']);
variable_set('spam_threshold', $form_values['spam_threshold']);
variable_set('spam_log_level', $form_values['spam_log_level']);
variable_set('spam_log_delete', $form_values['spam_log_delete']);
}
}
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;
}
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("SELECT fid FROM {spam_filters} WHERE name = '%s' AND module = '%s' LIMIT 1", $filter['name'], $filter['module']));
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']);
}
function spam_init_api() {
static $initialized = FALSE;
if (!$initialized) {
$initialized = TRUE;
$path = drupal_get_path('module', 'spam') . '/modules';
$files = drupal_system_listing('spam_.*\\.inc$', $path, 'name', 0);
foreach ($files as $file) {
$module = substr_replace($file->name, '', 0, 5);
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($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['feedback'] = array(
'#type' => 'textarea',
'#title' => t('Feedback'),
'#value' => $feedback->feedback,
'#disabled' => TRUE,
);
$form['publish'] = array(
'#type' => 'submit',
'#value' => t('Publish content'),
);
$form['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete feedback'),
);
$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_id, $form_values) {
if ($form_values['op'] == t('Publish content')) {
$content = unserialize($form_values['content']);
$extra['content'] = $content;
spam_mark_as_not_spam($form_values['type'], $form_values['id'], $extra);
$form = unserialize($form_values['spam_form']);
$_SESSION['bypass_spam_filter'] = _spam_sign($form['#post']['form_token']);
$return = drupal_process_form($content['form_id'], unserialize($form_values['spam_form']));
db_query('DELETE FROM {spam_filters_errors} WHERE bid = %d', $form_values['bid']);
drupal_set_message(t('Content published.'));
drupal_goto('admin/content/spam/feedback');
}
if ($form_values['op'] == t('Delete feedback')) {
db_query('DELETE FROM {spam_filters_errors} WHERE bid = %d', $form_values['bid']);
drupal_set_message(t('Feedback deleted.'));
drupal_goto('admin/content/spam/feedback');
}
}
function _spam_truncate($text, $length = 64) {
if (strlen($text) > $length) {
$text = 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_id, $form_values) {
$filters = spam_overview_filters();
switch ($form_values['op']) {
case t('Filter'):
case t('Refine'):
if (isset($form_values['filter'])) {
$filter = $form_values['filter'];
if (isset($filters[$filter]['options'])) {
$flat_options = form_options_flatten($filters[$filter]['options']);
if (isset($flat_options[$form_values[$filter]])) {
$_SESSION['spam_overview_filter'][] = array(
$filter,
$form_values[$filter],
);
}
}
else {
$_SESSION['spam_overview_filter'][] = array(
$filter,
$form_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();
foreach (module_invoke_all('spam_operations') as $operation => $array) {
$options[$operation] = $array['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' => '<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_id, $form_values) {
$spam = array_filter($form_values['spam']);
if (count($spam) == 0) {
form_set_error('', t('You have note selected any spam content.'));
}
}
function spam_admin_overview_submit($form_id, $form_values) {
$operations = module_invoke_all('spam_operations');
$operation = $operations[$form_values['operation']];
$spam = array_filter($form_values['spam']);
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' => '6',
),
);
}
$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(
'notspam' => array(
'label' => t('Mark as not spam'),
'callback' => 'spam_operations_callback',
'callback arguments' => array(
'mark_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 (strlen($pieces[0]) && is_numeric($pieces[1]) && 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(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) {
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' => $_SERVER['REMOTE_ADDR'],
'%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));
}
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 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['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('Submit'),
);
return $form;
}
function spam_error_page_submit($form_id, $form_values) {
global $user;
$content = unserialize($_SESSION['content']);
$id = spam_invoke_module($type, 'content_id', $content);
$hash = md5($_SESSION['content']);
$type = $_SESSION['type'];
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, $_SERVER['REMOTE_ADDR'], $form_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('');
}
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}/notspam",
'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;
}
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 = %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) {
spam_unpublish($type, $id);
}
if ($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 = %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) {
spam_publish($type, $id);
}
if ($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 = '';
foreach ($fields['main'] as $field) {
$text .= $content[$field] . ' ';
}
if ($full && 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', %d, %d, '%s', '%s', '%s', %d)", $level, $trid, $type, $id, $user->uid, $function, $message, $_SERVER['REMOTE_ADDR'], 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', %d, %d, '%s', '%s', '%s', %d)", SPAM_VERBOSE, $type, $id, $user->uid, '_spam_log_trace', $key, $_SERVER['REMOTE_ADDR'], 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 = %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/logs/spam/{$log->trid}/trace") . ' | ';
}
$options .= l(t('detail'), "admin/logs/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) . (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[] = array(
'path' => '',
'title' => t('Home'),
);
$breadcrumb[] = array(
'path' => 'admin',
'title' => t('Administer'),
);
$breadcrumb[] = array(
'path' => 'admin/logs',
'title' => t('Logs'),
);
$breadcrumb[] = array(
'path' => 'admin/logs/spam',
'title' => t('Spam'),
);
$breadcrumb[] = array(
'path' => 'admin/logs/spam/detail',
'title' => t('Spam module log entry'),
);
menu_set_location($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/logs/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' => ucfirst($message->content_type),
)),
'header' => TRUE,
),
array(
'data' => l(t($message->content_id), "admin/logs/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/logs/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[] = array(
'path' => '',
'title' => t('Home'),
);
$breadcrumb[] = array(
'path' => 'admin',
'title' => t('Administer'),
);
$breadcrumb[] = array(
'path' => 'admin/logs',
'title' => t('Logs'),
);
$breadcrumb[] = array(
'path' => 'admin/logs/spam',
'title' => t('Spam'),
);
$breadcrumb[] = array(
'path' => 'admin/logs/spam/trace',
'title' => t('Spam module log trace'),
);
menu_set_location($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/logs/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) . (strlen($log->function) > 20 ? '...' : ''),
truncate_utf8($log->message, 64) . (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;
}
}
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,
)));
}