X-Git-Url: https://vcs.fsf.org/?p=squirrelmail.git;a=blobdiff_plain;f=plugins%2Ffilters%2Ffilters.php;h=77876908aee5248e94a2cc13608bad5e22e1baf5;hp=8261c27cfd4e88eeaef927c31adf635b7b4cef73;hb=74320ac6edefb7bc8384dc7c9ca55b9503023dba;hpb=cf2aa192e8426667f537f75259a832a3d3f0632a diff --git a/plugins/filters/filters.php b/plugins/filters/filters.php index 8261c27c..77876908 100644 --- a/plugins/filters/filters.php +++ b/plugins/filters/filters.php @@ -1,32 +1,94 @@ _("Message Filters"), + 'url' => SM_PATH . 'plugins/filters/options.php', + 'desc' => _("Filtering enables messages with different criteria to be automatically filtered into different folders for easier organization."), + 'js' => false + ); + + if ($AllowSpamFilters) { + $optpage_blocks[] = array( + 'name' => _("SPAM Filters"), + 'url' => SM_PATH . 'plugins/filters/spamoptions.php', + 'desc' => _("SPAM filters allow you to select from various DNS based blacklists to detect junk email in your INBOX and move it to another folder (like Trash)."), + 'js' => false + ); + } +} + +/* Receive the status of the folder and do something with it */ +function filters_folder_status($statusarr) { + + global $filter_inbox_count; + if (empty($filter_inbox_count)) $filter_inbox_count=0; + + if ($statusarr['MAILBOX'] == 'INBOX') + { + if (!empty($statusarr['MESSAGES'])) $filter_inbox_count=$statusarr['MESSAGES']; + } +} + +/** + * Saves the DNS Cache to disk * @access private */ function filters_SaveCache () { @@ -45,7 +107,7 @@ function filters_SaveCache () { $fp = fopen($data_dir . '/dnscache', 'r'); flock($fp,LOCK_EX); } - $fp1=fopen($data_dir . '/dnscache', 'w+'); + $fp1 = fopen($data_dir . '/dnscache', 'w+'); foreach ($SpamFilters_DNScache as $Key=> $Value) { $tstr = $Key . ',' . $Value['L'] . ',' . $Value['T'] . "\n"; @@ -57,7 +119,7 @@ function filters_SaveCache () { } /** - * FIXME: Undocumented function + * Loads the DNS Cache from disk * @access private */ function filters_LoadCache () { @@ -67,20 +129,21 @@ function filters_LoadCache () { $SpamFilters_DNScache = array(); if ($fp = fopen ($data_dir . '/dnscache', 'r')) { flock($fp,LOCK_SH); - while ($data=fgetcsv($fp,1024)) { + while ($data = fgetcsv($fp,1024)) { if ($data[2] > time()) { $SpamFilters_DNScache[$data[0]]['L'] = $data[1]; $SpamFilters_DNScache[$data[0]]['T'] = $data[2]; } } - flock($fp,LOCK_UN); } } } /** - * FIXME: Undocumented function + * Uses the BulkQuery executable to query all the RBLs at once + * @param array $filters Array of SPAM Filters + * @param array $IPs Array of IP Addresses * @access private */ function filters_bulkquery($filters, $IPs) { @@ -121,21 +184,51 @@ function filters_bulkquery($filters, $IPs) { } /** - * FIXME: Undocumented function + * Starts the filtering process + * @param array $hook_args (since 1.5.2) do hook arguments. Is used to check + * hook name, array key = 0. * @access private */ -function start_filters() { +function start_filters($hook_args) { global $imapServerAddress, $imapPort, $imap_stream, $imapConnection, - $UseSeparateImapConnection, $AllowSpamFilters; + $UseSeparateImapConnection, $AllowSpamFilters, $filter_inbox_count, + $username; + + /** + * check hook that calls filtering. If filters are called by right_main_after_header, + * do filtering only when we are in INBOX folder. + */ + if ($hook_args[0]=='right_main_after_header' && + (sqgetGlobalVar('mailbox',$mailbox,SQ_FORM) && $mailbox!='INBOX')) { + return; + } + + $filters = load_filters(); + + // No point running spam filters if there aren't any to run // + if ($AllowSpamFilters) { + $spamfilters = load_spam_filters(); + + $AllowSpamFilters = false; + foreach($spamfilters as $value) { + if ($value['enabled'] == SMPREF_ON) { + $AllowSpamFilters = true; + break; + } + } + } + + // No user filters, and no spam filters, no need to continue // + if (!$AllowSpamFilters && empty($filters)) { + return; + } - sqgetGlobalVar('username', $username, SQ_SESSION); - sqgetGlobalVar('key', $key, SQ_COOKIE); // Detect if we have already connected to IMAP or not. // Also check if we are forced to use a separate IMAP connection if ((!isset($imap_stream) && !isset($imapConnection)) || $UseSeparateImapConnection ) { - $stream = sqimap_login($username, $key, $imapServerAddress, + $stream = sqimap_login($username, false, $imapServerAddress, $imapPort, 10); $previously_connected = false; } else if (isset($imapConnection)) { @@ -145,9 +238,17 @@ function start_filters() { $previously_connected = true; $stream = $imap_stream; } - $aStatus = sqimap_status_messages ($stream, 'INBOX', array('MESSAGES')); - if ($aStatus['MESSAGES']) { + if (!isset($filter_inbox_count)) { + $aStatus = sqimap_status_messages ($stream, 'INBOX', array('MESSAGES')); + if (!empty($aStatus['MESSAGES'])) { + $filter_inbox_count=$aStatus['MESSAGES']; + } else { + $filter_inbox_count=0; + } + } + + if ($filter_inbox_count > 0) { sqimap_mailbox_select($stream, 'INBOX'); // Filter spam from inbox before we sort them into folders if ($AllowSpamFilters) { @@ -164,7 +265,8 @@ function start_filters() { } /** - * FIXME: Undocumented function + * Does the loop through each filter + * @param stream imap_stream the stream to read from * @access private */ function user_filters($imap_stream) { @@ -186,6 +288,12 @@ function user_filters($imap_stream) { $filters[$i]['what'], $filters[$i]['folder'], $filters_user_scan, $expunge); $expunge = filter_search_and_delete($imap_stream, 'CC', $filters[$i]['what'], $filters[$i]['folder'], $filters_user_scan, $expunge); + } else if ($filters[$i]['where'] == 'Header and Body') { + $expunge = filter_search_and_delete($imap_stream, 'TEXT', + $filters[$i]['what'], $filters[$i]['folder'], $filters_user_scan, $expunge); + } else if ($filters[$i]['where'] == 'Message Body') { + $expunge = filter_search_and_delete($imap_stream, 'BODY', + $filters[$i]['what'], $filters[$i]['folder'], $filters_user_scan, $expunge); } else { /* * If it's a normal TO, CC, SUBJECT, or FROM, then handle it @@ -203,18 +311,31 @@ function user_filters($imap_stream) { } /** - * FIXME: Undocumented function + * Creates and runs the IMAP command to filter messages + * @param string $imap_stream TODO: Document this parameter + * @param string $where Which part of the message to search (TO, CC, SUBJECT, etc...) + * @param string $what String to search for + * @param string $where_to Folder it will move to + * @param string $user_scan Whether to search all or just unseen + * @param string $should_expunge * @access private */ function filter_search_and_delete($imap_stream, $where, $what, $where_to, $user_scan, $should_expunge) { global $languages, $squirrelmail_language, $allow_charset_search, $imap_server_type; + //TODO: make use of new mailbox cache. See mailbox_display.phpinfo + + if (strtolower($where_to) == 'inbox') { + return array(); + } + if ($user_scan == 'new') { $category = 'UNSEEN'; } else { $category = 'ALL'; } + $category .= ' UNDELETED'; if ($allow_charset_search && isset($languages[$squirrelmail_language]['CHARSET']) && @@ -227,6 +348,7 @@ function filter_search_and_delete($imap_stream, $where, $what, $where_to, $user_ } if ($where == 'Header') { $what = explode(':', $what); + $where = strtoupper($where); $where = trim($where . ' ' . $what[0]); $what = addslashes(trim($what[1])); } @@ -234,25 +356,51 @@ function filter_search_and_delete($imap_stream, $where, $what, $where_to, $user_ // see comments in squirrelmail sqimap_search function if ($imap_server_type == 'macosx' || $imap_server_type == 'hmailserver') { $search_str .= ' ' . $where . ' ' . $what; + /* read data back from IMAP */ + $read = sqimap_run_command($imap_stream, $search_str, true, $response, $message, TRUE); } else { - $search_str .= ' ' . $where . ' {' . strlen($what) . "}\r\n" - . $what . "\r\n"; + $search_str .= ' ' . $where . ' {' . strlen($what) . "}"; + $sid = sqimap_session_id(true); + fputs ($imap_stream, $sid . ' ' . $search_str . "\r\n"); + $read2 = sqimap_fgets($imap_stream); + # server should respond with Ready for argument, then we will send search text + #echo "RR2 $read2
"; + fputs ($imap_stream, "$what\r\n"); + #echo "SS $what
"; + $read2 = sqimap_fgets($imap_stream); + #echo "RR2 $read2
"; + $read[]=$read2; + $read3 = sqimap_fgets($imap_stream); + #echo "RR3 $read3
"; + list($rtag,$response,$message)=explode(' ',$read3,3); +## $read2 = sqimap_retrieve_imap_response($imap_stream, $sid, true, +## $response, $message, $search_str, false, true, false); + #echo "RR2 $read2 / RESPONSE $response
"; } - /* read data back from IMAP */ - $read = sqimap_run_command($imap_stream, $search_str, true, $response, $message, TRUE); if (isset($read[0])) { $ids = array(); - for ($i=0,$iCnt=count($read);$i<$iCnt;++$i) { + for ($i = 0, $iCnt = count($read); $i < $iCnt; ++$i) { if (preg_match("/^\* SEARCH (.+)$/", $read[$i], $regs)) { - $ids = preg_split("/ /", trim($regs[1])); - break; + $ids += explode(' ', trim($regs[1])); } } if ($response == 'OK' && count($ids)) { if (sqimap_mailbox_exists($imap_stream, $where_to)) { $should_expunge = true; - sqimap_msgs_list_move ($imap_stream, $ids, $where_to); + sqimap_msgs_list_move ($imap_stream, $ids, $where_to, false); + } + } elseif ($response != 'OK') { + $query = $search_str . "\r\n".$what ."\r\n"; + if ($response == 'NO') { + if (strpos($message,'BADCHARSET') !== false || + strpos($message,'character') !== false) { + sqm_trigger_imap_error('SQM_IMAP_BADCHARSET',$query, $response, $message); + } else { + sqm_trigger_imap_error('SQM_IMAP_ERROR',$query, $response, $message); + } + } else { + sqm_trigger_imap_error('SQM_IMAP_ERROR',$query, $response, $message); } } } @@ -260,7 +408,8 @@ function filter_search_and_delete($imap_stream, $where, $what, $where_to, $user_ } /** - * FIXME: Undocumented function + * Loops through all the Received Headers to find IP Addresses + * @param stream imap_stream the stream to read from * @access private */ function spam_filters($imap_stream) { @@ -281,7 +430,7 @@ function spam_filters($imap_stream) { $run = false; - foreach ($filters as $Key => $Value) { + foreach ($filters as $Value) { if ($Value['enabled']) { $run = true; break; @@ -300,10 +449,10 @@ function spam_filters($imap_stream) { $search_array = array(); $read = sqimap_run_command($imap_stream, 'SEARCH UNSEEN', true, $response, $message, TRUE); if (isset($read[0])) { - for ($i=0,$iCnt=count($read);$i<$iCnt;++$i) { + for ($i = 0, $iCnt = count($read); $i < $iCnt; ++$i) { if (preg_match("/^\* SEARCH (.+)$/", $read[$i], $regs)) { - $search_array = preg_split("/ /", trim($regs[1])); - break; + $search_array = explode(' ', trim($regs[1])); + break; } } } @@ -344,14 +493,15 @@ function spam_filters($imap_stream) { $aSpamIds[] = $MsgNum; $isspam = true; } + if ($bulkquery) { array_shift($aMatch); - $IP = explode('.',$aMatch); + $IP = explode('.', $aMatch); foreach ($filters as $key => $value) { if ($filters[$key]['enabled'] && $filters[$key]['dns']) { if (strlen($SpamFilters_DNScache[$IP.'.'.$filters[$key]['dns']]) == 0) { - $IPs[$IP] = true; - break; + $IPs[$IP] = true; + break; } } } @@ -368,7 +518,7 @@ function spam_filters($imap_stream) { } // Lookie! It's spam! Yum! if (count($aSpamIds) && sqimap_mailbox_exists($imap_stream, $filters_spam_folder)) { - sqimap_msgs_list_move ($imap_stream, $aSpamIds, $filters_spam_folder); + sqimap_msgs_list_move($imap_stream, $aSpamIds, $filters_spam_folder); sqimap_mailbox_expunge($imap_stream, 'INBOX'); } @@ -384,9 +534,14 @@ function spam_filters($imap_stream) { } /** - * FIXME: Undocumented function * Does the loop through each enabled filter for the specified IP address. * IP format: $a.$b.$c.$d + * @param int $a First subset of IP + * @param int $b Second subset of IP + * @param int $c Third subset of IP + * @param int $d Forth subset of IP + * @param array $filters The Spam Filters + * @return boolean Whether the IP is Spam * @access private */ function filters_spam_check_site($a, $b, $c, $d, &$filters) { @@ -394,8 +549,14 @@ function filters_spam_check_site($a, $b, $c, $d, &$filters) { foreach ($filters as $key => $value) { if ($filters[$key]['enabled']) { if ($filters[$key]['dns']) { + + /** + * RFC allows . on end of hostname to force domain lookup to + * not use search domain from resolv.conf, i.e. to ensure + * search domain isn't used if no hostname is found + */ $filter_revip = $d . '.' . $c . '.' . $b . '.' . $a . '.' . - $filters[$key]['dns']; + $filters[$key]['dns'] . '.'; if(!isset($SpamFilters_DNScache[$filter_revip]['L'])) $SpamFilters_DNScache[$filter_revip]['L'] = ''; @@ -409,8 +570,12 @@ function filters_spam_check_site($a, $b, $c, $d, &$filters) { $SpamFilters_DNScache[$filter_revip]['T'] = time() + $SpamFilters_CacheTTL; } - if ($SpamFilters_DNScache[$filter_revip]['L'] == - $filters[$key]['result']) { + + /** + * gethostbyname returns ip if resolved, or returns original + * host supplied to function if there is no resolution + */ + if ($SpamFilters_DNScache[$filter_revip]['L'] != $filter_revip) { return 1; } } @@ -420,24 +585,26 @@ function filters_spam_check_site($a, $b, $c, $d, &$filters) { } /** - * FIXME: Undocumented function + * Loads the filters from the user preferences + * @return array All the user filters * @access private */ function load_filters() { global $data_dir, $username; $filters = array(); - for ($i=0; $fltr = getPref($data_dir, $username, 'filter' . $i); $i++) { + for ($i = 0; $fltr = getPref($data_dir, $username, 'filter' . $i); $i++) { $ary = explode(',', $fltr); $filters[$i]['where'] = $ary[0]; - $filters[$i]['what'] = $ary[1]; + $filters[$i]['what'] = str_replace('###COMMA###', ',', $ary[1]); $filters[$i]['folder'] = $ary[2]; } return $filters; } /** - * FIXME: Undocumented function + * Loads the Spam Filters and checks the preferences for the enabled status + * @return array All the spam filters * @access private */ function load_spam_filters() { @@ -493,14 +660,6 @@ function load_spam_filters() { _("COMMERCIAL - RBL+ Dial-up entries."); } - $filters['ORDB']['prefname'] = 'filters_spam_ordb'; - $filters['ORDB']['name'] = 'Open Relay Database List'; - $filters['ORDB']['link'] = 'http://www.ordb.org/'; - $filters['ORDB']['dns'] = 'relays.ordb.org'; - $filters['ORDB']['result'] = '127.0.0.2'; - $filters['ORDB']['comment'] = - _("FREE - ORDB was born when ORBS went off the air. It seems to have fewer false positives than ORBS did though."); - $filters['FiveTen Direct']['prefname'] = 'filters_spam_fiveten_src'; $filters['FiveTen Direct']['name'] = 'Five-Ten-sg.com Direct SPAM Sources'; $filters['FiveTen Direct']['link'] = 'http://www.five-ten-sg.com/blackhole.php'; @@ -569,7 +728,7 @@ function load_spam_filters() { $filters['SPAMhaus']['name'] = 'SPAMhaus Lists'; $filters['SPAMhaus']['link'] = 'http://www.spamhaus.org'; $filters['SPAMhaus']['dns'] = 'sbl.spamhaus.org'; - $filters['SPAMhaus']['result'] = '127.0.0.6'; + $filters['SPAMhaus']['result'] = '127.0.0.2'; $filters['SPAMhaus']['comment'] = _("FREE - SPAMhaus - A list of well-known SPAM sources."); @@ -685,47 +844,22 @@ function load_spam_filters() { $filters['NJABL DUL']['comment'] = _("FREE, for now - Not Just Another Blacklist - Dial-up IPs."); - $filters['Conf DSBL.ORG Relay']['prefname'] = 'filters_spam_dsbl_conf_ss'; - $filters['Conf DSBL.ORG Relay']['name'] = 'DSBL.org Confirmed Relay List'; - $filters['Conf DSBL.ORG Relay']['link'] = 'http://www.dsbl.org/'; - $filters['Conf DSBL.ORG Relay']['dns'] = 'list.dsbl.org'; - $filters['Conf DSBL.ORG Relay']['result'] = '127.0.0.2'; - $filters['Conf DSBL.ORG Relay']['comment'] = - _("FREE - Distributed Sender Boycott List - Confirmed Relays"); - - $filters['Conf DSBL.ORG Multi-Stage']['prefname'] = 'filters_spam_dsbl_conf_ms'; - $filters['Conf DSBL.ORG Multi-Stage']['name'] = 'DSBL.org Confirmed Multi-Stage Relay List'; - $filters['Conf DSBL.ORG Multi-Stage']['link'] = 'http://www.dsbl.org/'; - $filters['Conf DSBL.ORG Multi-Stage']['dns'] = 'multihop.dsbl.org'; - $filters['Conf DSBL.ORG Multi-Stage']['result'] = '127.0.0.2'; - $filters['Conf DSBL.ORG Multi-Stage']['comment'] = - _("FREE - Distributed Sender Boycott List - Confirmed Multi-stage Relays"); - - $filters['UN-Conf DSBL.ORG']['prefname'] = 'filters_spam_dsbl_unc'; - $filters['UN-Conf DSBL.ORG']['name'] = 'DSBL.org UN-Confirmed Relay List'; - $filters['UN-Conf DSBL.ORG']['link'] = 'http://www.dsbl.org/'; - $filters['UN-Conf DSBL.ORG']['dns'] = 'unconfirmed.dsbl.org'; - $filters['UN-Conf DSBL.ORG']['result'] = '127.0.0.2'; - $filters['UN-Conf DSBL.ORG']['comment'] = - _("FREE - Distributed Sender Boycott List - UN-Confirmed Relays"); - foreach ($filters as $Key => $Value) { - $filters[$Key]['enabled'] = getPref($data_dir, $username, - $filters[$Key]['prefname']); + $filters[$Key]['enabled'] = (bool)getPref($data_dir, $username, $filters[$Key]['prefname']); } return $filters; } /** - * FIXME: Undocumented function + * Removes a User filter + * @param int $id ID of the filter to remove * @access private */ function remove_filter ($id) { global $data_dir, $username; - while ($nextFilter = getPref($data_dir, $username, 'filter' . - ($id + 1))) { + while ($nextFilter = getPref($data_dir, $username, 'filter' . ($id + 1))) { setPref($data_dir, $username, 'filter' . $id, $nextFilter); $id ++; } @@ -734,7 +868,9 @@ function remove_filter ($id) { } /** - * FIXME: Undocumented function + * Swaps two filters + * @param int $id1 ID of first filter to swap + * @param int $id2 ID of second filter to swap * @access private */ function filter_swap($id1, $id2) { @@ -750,20 +886,20 @@ function filter_swap($id1, $id2) { } /** - * This update the filter rules when renaming or deleting folders + * This updates the filter rules when renaming or deleting folders * @param array $args * @access private */ function update_for_folder ($args) { $old_folder = $args[0]; - $new_folder = $args[2]; - $action = $args[1]; + $new_folder = $args[2]; + $action = $args[1]; global $data_dir, $username; $filters = array(); $filters = load_filters(); $filter_count = count($filters); $p = 0; - for ($i=0;$i<$filter_count;$i++) { + for ($i = 0; $i < $filter_count; $i++) { if (!empty($filters)) { if ($old_folder == $filters[$i]['folder']) { if ($action == 'rename') { @@ -793,4 +929,3 @@ function do_error($string) { echo $string; echo "

\n"; } -?> \ No newline at end of file