[prev in list] [next in list] [prev in thread] [next in thread]
List: squirrelmail-plugins
Subject: Re: [SM-PLUGINS] Blacklist option on the Message-List - solved
From: "Gowranga" <gowranga () serc ! iisc ! ernet ! in>
Date: 2007-02-21 16:38:47
Message-ID: 3161.10.16.10.72.1172075207.squirrel () mail ! serc ! iisc ! ernet ! in
[Download RAW message or body]
Hello,
Spam_buttons together with the use of SpamAssassin+SQL (sasql) plugin can
now have blacklist/whitelist options working on the Message-List as well
as message view (read_body). Users are allowed to check a group of
messages whose addresses need to be blacklisted/not-blacklisted, and get
reports along the lines of messages marked as SPAM.
Following are the requirements/assumptions made while using this feature:
1. The files in spam_buttons plugin have already been subjected to patch
obtained from http://www.qv90.de/sqm
2. Patches are applied to both spam_buttons and sasql plugins. The file
"aux_read_body.php" resides in SM_PATH/plugins/spam_buttons path.
3. A spam folder (mail/Spam) is already existing, else needs to be
created.
4. Attempts to block email addresses with usernames
(as seen in username@domain) having umlaut and special characters
such as
`, ' (, [, #, $, ? , etc., will result in blocking the entire domain.
5. Certain spam messages carry forged user email addresses. While using
blacklist option on the Message_List, one needs to be careful in not
causing ones own address to be blacklisted. In case of an unforeseen
mishap, such entries need to be deleted using the
"Not Blacklist"/remove option.
6. SM is of version 1.4.x.
I hope the SQM community finds it useful.
Thanks.
-gowranga
["aux_read_body.php" (application/octet-stream)]
<?php
include_once(SM_PATH.'plugins/sasql/sasql_conf.php');
include_once(SM_PATH.'plugins/sasql/sasql_db.php');
include_once(SM_PATH.'include/validate.php');
include_once(SM_PATH.'functions/page_header.php');
include_once(SM_PATH.'include/load_prefs.php');
// From sasql_conf.php
global $SqlDSN;
global $SqlTable;
global $allowed_wl_params;
global $wl_params;
/* SquirrelMail required files. */
require_once(SM_PATH . 'functions/global.php');
require_once(SM_PATH . 'functions/imap.php');
require_once(SM_PATH . 'functions/mime.php');
require_once(SM_PATH . 'functions/url_parser.php');
/* get the globals we may need */
sqgetGlobalVar('key', $key, SQ_COOKIE);
sqgetGlobalVar('username', $username, SQ_SESSION);
sqgetGlobalVar('msgs', $msgs, SQ_SESSION);
sqgetGlobalVar('lastTargetMailbox', $lastTargetMailbox, SQ_SESSION);
sqgetGlobalVar('server_sort_array', $server_sort_array, SQ_SESSION);
if (!sqgetGlobalVar('messages', $messages, SQ_SESSION) ) {
$messages = array();
}
/** GET VARS */
if ( sqgetGlobalVar('view_hdr', $temp, SQ_GET) ) {
$view_hdr = (int) $temp;
}
/** POST VARS */
sqgetGlobalVar('move_id', $move_id, SQ_POST);
/** GET/POST VARS */
sqgetGlobalVar('passed_ent_id', $passed_ent_id);
sqgetGlobalVar('mailbox', $mailbox);
if ( sqgetGlobalVar('passed_id', $temp) ) {
$passed_id = (int) $temp;
}
if ( sqgetGlobalVar('sort', $temp) ) {
$sort = (int) $temp;
}
if ( sqgetGlobalVar('startMessage', $temp) ) {
$startMessage = (int) $temp;
}
/* end of get globals */
global $uid_support, $sqimap_capabilities;
/**
* $message contains all information about the message
* including header and body
*/
function formatEnvheader($mailbox, $passed_id, $passed_ent_id, $message) {
$header = $message->rfc822_header;
$env = array();
$from_name = $header->getAddr_s('from');
if (!$from_name) {
$from_name = $header->getAddr_s('sender');
if (!$from_name) {
$from_name = _("Unknown sender");
}
}
$env[_("From")] = decodeHeader($from_name);
foreach ($env as $key => $val) {
preg_match("/([#|\]($?[%{^&}\)\*`'~.\-\+\w]+@[A-Za-z0-9\.]+[A-Za-z0-9\.-]+\.[A-Za-z0-9]+)/", \
$val, $from_matches); $chkfrom = $from_matches[1]; //bbf`ss@er.gh.com
/** echo $from_matches[1] . "<br />\n"; **/
preg_match("/([.\-\+\w]+@[A-Za-z0-9\.]+[A-Za-z0-9\.-]+\.[A-Za-z0-9]+)/", $val, \
$fr_matches); //$fr_matches[1] is ss@er.gh.com
/** echo $fr_matches[1] . "<br />\n";
if ($from_matches[1] != $fr_matches[1])
echo ' Caught' . "<br />\n"; **/
if ($chkfrom != $fr_matches[1]) {
// Black Listing entire domain in case the name has utf8 or spl chars
$domPart = preg_split("/@/", $chkfrom);
$chkfrom = "*@" . $domPart[1];
}
}
return $chkfrom;
}
?>
["functions-spam_buttons-squirrelmail-1.4.9a.diff" (application/octet-stream)]
--- plugins/spam_buttons/bac_functions.php 2007-02-21 19:10:38.357560312 +0530
+++ plugins/spam_buttons/functions.php 2007-02-21 19:10:50.353736616 +0530
@@ -57,26 +57,30 @@
if (strtoupper($method) == 'POST')
{
sqgetGlobalVar('isSpam', $isSpam, SQ_POST);
+ sqgetGlobalVar('isbl', $isbl, SQ_POST);
sqgetGlobalVar('notSpam', $notSpam, SQ_POST);
+ sqgetGlobalVar('notbl', $notbl, SQ_POST);
}
else
{
sqgetGlobalVar('isSpam', $isSpam, SQ_GET);
+ sqgetGlobalVar('isbl', $isbl, SQ_GET);
sqgetGlobalVar('notSpam', $notSpam, SQ_GET);
+ sqgetGlobalVar('notbl', $notbl, SQ_GET);
}
// build message ID array if user came from one of the
// links on the message view page
//
- if ($isSpam == 'yslnk' || $notSpam == 'yslnk')
+if ($isSpam == 'yslnk' || $isbl == 'yslnk'|| $notSpam == 'yslnk' || $notbl == \
'yslnk') $msg = array($passed_id);
// if no messages were selected or spam buttons were not clicked on
// this request, just return and let SM handle the error, if any
//
- if (empty($msg) || (empty($isSpam) && empty($notSpam)))
+ if (empty($msg) || (empty($isSpam) && empty($isbl) && empty($notSpam) && \
empty($notbl))) return;
@@ -147,6 +151,88 @@
}
+ // -----------------------------------------------------------------
+ //
+ // BLACKLIST
+ //
+
+
+ // mark as blacklist!
+ //
+ if (!empty($isbl))
+ {
+
+ $note = _("Successfully reported as blacklist");
+//TODO? include something that identifies the message(s) that were reported(have to \
add it up in the loop above)? might take too much screen real estate....? +
+ if (!empty($spam_folder)) {
+ $imap_stream = sqimap_login($username, $key, $imapServerAddress,
+ $imapPort, 0);
+ $mbx_response = sqimap_mailbox_select($imap_stream, $mailbox, false, \
false, true); +
+ if (sqimap_mailbox_exists($imap_stream, $spam_folder)) {
+ include_once(SM_PATH . 'plugins/spam_buttons/aux_read_body.php');
+
+ foreach($msg as $i => $v) {
+
+$uidvalidity = $mbx_response['UIDVALIDITY'];
+
+if (!isset($messages[$uidvalidity])) {
+ $messages[$uidvalidity] = array();
+}
+if (!isset($messages[$uidvalidity][$v]) || !$uid_support) {
+ $message = sqimap_get_message($imap_stream, $v, $mailbox);
+ $FirstTimeSee = !$message->is_seen;
+ $message->is_seen = true;
+ $messages[$uidvalidity][$v] = $message;
+} else {
+// $message = sqimap_get_message($imap_stream, $v, $mailbox);
+ $message = $messages[$uidvalidity][$v];
+ $FirstTimeSee = !$message->is_seen;
+}
+
+if (isset($passed_ent_id) && $passed_ent_id) {
+ $message = $message->getEntity($passed_ent_id);
+ if ($message->type0 != 'message' && $message->type1 != 'rfc822') {
+ $message = $message->parent;
+ }
+
+ $read = sqimap_run_command ($imap_stream, "FETCH $v \
BODY[$passed_ent_id.HEADER]", true, $response, $msg, $uid_support); + \
$rfc822_header = new Rfc822Header(); + $rfc822_header->parseHeader($read);
+ $message->rfc822_header = $rfc822_header;
+} else {
+ $passed_ent_id = 0;
+}
+$header = $message->header;
+
+$rets=formatEnvheader($mailbox, $v, $passed_ent_id, $message, $color, \
$FirstTimeSee); +
+$preference='blacklist_from' ;
+
+ $DBHandle = sasql_DBConnect($SqlDSN);
+ sasql_AddPref ($DBHandle, $SqlTable, $username, $preference, $rets);
+ sasql_DBDisconnect($DBHandle);
+
+
+$spam_folder = 'mail/Spam'; // repeat, altho' defined in config.php
+
+ sqimap_msgs_list_copy($imap_stream, $v, $spam_folder);
+ sqimap_toggle_flag($imap_stream, $v, '\\Deleted', true, true);
+ }
+ } else {
+ $note = sprintf(_("Error: Spam folder %s does not exist."), \
$spam_folder); + }
+ sqimap_logout($imap_stream);
+ }
+
+
+ else
+//TODO: we could put a warning here for the sysadmin...
+ $note = '';
+
+ }
+
// -----------------------------------------------------------------
//
@@ -208,6 +294,87 @@
}
+ // -----------------------------------------------------------------
+ //
+ // WHITELIST
+ //
+
+
+ // mark as not_blacklist!
+ //
+ else if (!empty($notbl))
+ {
+
+ $note = _("Successfully reported as non-blacklist");
+//TODO? include something that identifies the message(s) that were reported(have to \
add it up in the loop above)? might take too much screen real estate....? +
+ if (!empty($spam_folder)) {
+ $imap_stream = sqimap_login($username, $key, $imapServerAddress,
+ $imapPort, 0);
+$mbx_response = sqimap_mailbox_select($imap_stream, $mailbox, false, false, true);
+
+ if (sqimap_mailbox_exists($imap_stream, $spam_folder)) {
+ include_once(SM_PATH . 'plugins/spam_buttons/aux_read_body.php');
+
+ foreach($msg as $i => $v) {
+
+$uidvalidity = $mbx_response['UIDVALIDITY'];
+
+if (!isset($messages[$uidvalidity])) {
+ $messages[$uidvalidity] = array();
+}
+if (!isset($messages[$uidvalidity][$v]) || !$uid_support) {
+ $message = sqimap_get_message($imap_stream, $v, $mailbox);
+ $FirstTimeSee = !$message->is_seen;
+ $message->is_seen = true;
+ $messages[$uidvalidity][$v] = $message;
+} else {
+// $message = sqimap_get_message($imap_stream, $v, $mailbox);
+ $message = $messages[$uidvalidity][$v];
+ $FirstTimeSee = !$message->is_seen;
+}
+
+if (isset($passed_ent_id) && $passed_ent_id) {
+ $message = $message->getEntity($passed_ent_id);
+ if ($message->type0 != 'message' && $message->type1 != 'rfc822') {
+ $message = $message->parent;
+ }
+
+ $read = sqimap_run_command ($imap_stream, "FETCH $v BODY[$passed_ent_id.HEADER]", \
true, $response, $msg, $uid_support); + $rfc822_header = new Rfc822Header();
+ $rfc822_header->parseHeader($read);
+ $message->rfc822_header = $rfc822_header;
+} else {
+ $passed_ent_id = 0;
+}
+$header = $message->header;
+
+$rets=formatEnvheader($mailbox, $v, $passed_ent_id, $message, $color, \
$FirstTimeSee); +$preference='blacklist_from' ; //Remove from blacklist
+
+ $DBHandle = sasql_DBConnect($SqlDSN);
+ sasql_DelPref_value ($DBHandle, $SqlTable, $username, $preference, $rets);
+ sasql_DBDisconnect($DBHandle);
+
+$inbox_folder='INBOX';
+
+if (sqimap_msgs_list_copy($imap_stream, $v, $inbox_folder)) {
+ sqimap_toggle_flag($imap_stream, $v, '\\Deleted', true, \
true); + }
+ }
+ } else {
+ $note = sprintf(_("Error: Folder %s does not exist."), \
$inbox_folder); + }
+ sqimap_logout($imap_stream);
+ }
+
+
+ else
+//TODO: we could put a warning here for the sysadmin...
+ $note = '';
+
+ }
+
bindtextdomain('squirrelmail', SM_PATH . 'locale');
textdomain('squirrelmail');
@@ -613,9 +780,12 @@
{
global $show_not_spam_button, $spam_button_text, $not_spam_button_text,
+ $show_not_bl_button, $bl_button_text, $not_bl_button_text, \
$show_is_bl_button, $show_is_spam_button, $spam_folder;
include_once(SM_PATH . 'plugins/spam_buttons/config.php');
+
+
//TODO: create tooltip with more verbose explanation
$ret = '';
sqgetGlobalVar('mailbox', $mailbox);
@@ -624,10 +794,18 @@
if (empty($spam_folder) || $mailbox != $spam_folder)
$ret .= '<input type="submit" name="isSpam" value="' . $spam_button_text . '" \
/>';
+ if ($show_is_bl_button)
+ if (empty($spam_folder) || $mailbox != $spam_folder)
+ $ret .= '<input type="submit" name="isbl" value="' . $bl_button_text . '" /> ';
+
if ($show_not_spam_button)
if (empty($spam_folder) || $mailbox == $spam_folder)
$ret .= '<input type="submit" name="notSpam" value="' . $not_spam_button_text \
. '" />';
-
+
+ if ($show_not_bl_button)
+ if (empty($spam_folder) || $mailbox == $spam_folder)
+ $ret .= '<input type="submit" name="notbl" value="' . $not_bl_button_text . '" \
/> '; +
return $ret;
}
@@ -643,6 +821,7 @@
global $show_spam_link_on_read_body, $show_not_spam_button,
$spam_button_text, $not_spam_button_text, $show_is_spam_button,
+ $show_not_bl_button, $bl_button_text, $not_bl_button_text, \
$show_is_bl_button, $spam_folder;
include_once(SM_PATH . 'plugins/spam_buttons/config.php');
@@ -656,19 +835,21 @@
sqgetGlobalVar('view_as_html', $view_as_html, SQ_FORM);
//TODO: create tooltip with more verbose explanation
+
if ($show_is_spam_button)
if (empty($spam_folder) || $mailbox != $spam_folder)
echo ' | <a href="' . SM_PATH . 'src/read_body.php?isSpam=yslnk&mailbox='
. urlencode($mailbox) . '&passed_id=' . $passed_id . '&view_as_html='
. $view_as_html . '&startMessage=' . $startMessage . '">'
. $spam_button_text . '</a>';
-
+
if ($show_not_spam_button)
if (empty($spam_folder) || $mailbox == $spam_folder)
echo ' | <a href="' . SM_PATH . 'src/read_body.php?notSpam=yslnk&mailbox='
. urlencode($mailbox) . '&passed_id=' . $passed_id . '&view_as_html='
. $view_as_html . '&startMessage=' . $startMessage . '">'
. $not_spam_button_text . '</a>';
+
}
["config-spam_buttons-squirrelmail-1.4.9a.diff" (application/octet-stream)]
--- plugins/spam_buttons/config.php.sample 2007-02-20 13:20:40.994441192 +0530
+++ plugins/spam_buttons/config.php 2007-02-20 15:04:23.563466912 +0530
@@ -7,10 +7,15 @@
$spam_report_email_method, $is_spam_subject_prefix, \
$is_not_spam_subject_prefix,
$show_is_spam_button, $spam_report_smtpServerAddress, $spam_report_smtpPort,
$spam_report_useSendmail, $spam_report_smtp_auth_mech, \
$spam_report_use_smtp_tls, +
+ $show_bl_buttons_on_message_list, $show_not_bl_button, $bl_button_text, \
$not_bl_button_text, $show_is_bl_button, $spam_folder, $sb_debug;
bindtextdomain('spam_buttons', SM_PATH . 'locale');
textdomain('spam_buttons');
+include_once(SM_PATH . 'plugins/sasql/sasql_hooks.php');
+global $chkfrom;
+
// -------------------------------------------------------------------
@@ -19,11 +24,14 @@
//
+
// You may change the button text, but if you do, there is a good
// chance that those buttons will ONLY be displayed in one language
//
$spam_button_text = _("Spam");
$not_spam_button_text = _("Not Spam");
+$bl_button_text = _("Blacklist");
+$not_bl_button_text = _("Not Blacklist");
@@ -32,7 +40,9 @@
// 0 = don't display button, 1 = display button
//
$show_not_spam_button = 1;
+$show_not_bl_button = 1;
$show_is_spam_button = 1;
+$show_is_bl_button = 1;
@@ -47,7 +57,7 @@
// $spam_folder, then non-spam button will be available. If you then
// hit the no-spam button, the message will be moved to INBOX folder.
//
-$spam_folder = 'Spam';
+$spam_folder = 'mail/Spam';
@@ -55,6 +65,7 @@
//
// 0 = no, 1 = yes
//
+$show_bl_buttons_on_message_list = 1;
$show_spam_buttons_on_message_list = 1;
@@ -163,8 +174,9 @@
//
//
//
-$is_spam_shell_command = '';
-$is_not_spam_shell_command = '';
+
+$is_spam_shell_command = 'sudo -u root /usr/bin/sa-learn --spam \
--username=###USERNAME###'; +$is_not_spam_shell_command = 'sudo -u root \
/usr/bin/sa-learn --ham --username=###USERNAME###';
@@ -222,7 +234,6 @@
$spam_report_use_smtp_tls = '';
-
bindtextdomain('squirrelmail', SM_PATH . 'locale');
textdomain('squirrelmail');
?>
["sasql_wblist-sasql-squirrelmail-1.4.9a.diff" (application/octet-stream)]
--- plugins/sasql/bac_sasql_wblist.php 2007-02-20 18:22:47.305823008 +0530
+++ plugins/sasql/sasql_wblist.php 2007-02-20 18:23:00.461822992 +0530
@@ -32,6 +32,7 @@
sqgetglobalVar('passed_id', $passed_id, SQ_FORM); // Message id
sqgetGlobalVar('startMessage', $startMessage, SQ_FORM); // Paginator index.
sqgetGlobalVar('ent_id', $ent_id, SQ_FORM); // Entity id (jump back to attachment)
+sqgetGlobalVar('msg', $msg, SQ_FORM);
sqgetGlobalVar('preference', $preference, SQ_FORM);
if ($action) {
@@ -55,21 +56,113 @@
if ($error < 0) {
$DBHandle = sasql_DBConnect($SqlDSN);
- if ($action == 'new') {
+
+// if in 1.4.x we need to print message
+ // to user after report, do that here
+ //
+ if (!check_sm_version(1, 5, 0) && sqgetGlobalVar('sb_note', $sb_note, SQ_SESSION))
+ {
+
+ echo html_tag('div', '<b>' . $sb_note .'</b>', 'center') . "<br />\n";
+ sqsession_unregister('sb_note');
+
+ }
+
+ if ($action == 'new' && $preference == 'blacklist_from') {
sasql_AddPref ($DBHandle, $SqlTable, $username, $preference, $address);
- } elseif ($action == 'delete') {
+
+ $spam_folder = 'mail/Spam';
+
+ if (!($spam_folder)) {
+// $note = sprintf(_("Error: Spam folder %s does not exist."), $spam_folder);
+ $fh = fopen($spam_folder, 'w') or die("can't open file");
+ fclose($fh);
+ }
+
+ $redir_uri = SM_PATH . 'plugins/sasql/move_to.php?';
+ $redir_uri .= "mailbox=$mailbox";
+ $redir_uri .= "&sort=$sort";
+ $redir_uri .= "&startMessage=$startMessage;";
+ $redir_uri .= "&move_id=$passed_id";
+ $redir_uri .= "&targetMailbox=$spam_folder&location=../../src/right_main.php";
+ if ($ent_id) { $redir_uri .= "&ent_id=$ent_id"; }
+
+ $note = _("Successfully reported as blacklist");
+
+// SM 1.5: just display note and let go
+ //
+ if (check_sm_version(1, 5, 0))
+ {
+ echo html_tag('div', '<b>' . $note .'</b>', 'center') . "<br />\n";
+ return;
+ }
+
+
+ // For SM 1.4.x, redirect to message list so SM
+ // doesn't try to do something else funny (1.4.x
+ // assumes it has to delete messages... yikes)
+ //
+ else
+ {
+ // put note in session so we can display it when back in
+ // message list, without putting $note in the location, since
+ // it is hard to remove from there, even on subsequent requests
+ //
+ sqsession_register($note, 'sb_note');
+
+ header("Location: $redir_uri");
+ exit; }
+ } elseif ($action == 'delete' && $preference == 'blacklist_from') {
sasql_DelPref_value ($DBHandle, $SqlTable, $username, $preference, $address);
+
+ $redir_uri = SM_PATH . 'plugins/sasql/move_to.php?';
+ $redir_uri .= "mailbox=$mailbox";
+ $redir_uri .= "&sort=$sort";
+ $redir_uri .= "&startMessage=$startMessage;";
+ $redir_uri .= "&move_id=$passed_id";
+ $redir_uri .= "&targetMailbox=INBOX&location=../../src/right_main.php";
+ if ($ent_id) { $redir_uri .= "&ent_id=$ent_id"; }
+$note = _("Successfully reported as non-blacklist");
+
+// SM 1.5: just display note and let go
+ //
+ if (check_sm_version(1, 5, 0))
+ {
+ echo html_tag('div', '<b>' . $note .'</b>', 'center') . "<br />\n";
+ return;
+ }
+
+
+ // For SM 1.4.x, redirect to message list so SM
+ // doesn't try to do something else funny (1.4.x
+ // assumes it has to delete messages... yikes)
+ //
+ else
+ {
+ // put note in session so we can display it when back in
+ // message list, without putting $note in the location, since
+ // it is hard to remove from there, even on subsequent requests
+ //
+ sqsession_register($note, 'sb_note');
+ header("Location: $redir_uri");
+ exit;
}
+ } elseif ($action == 'new' && $preference == 'whitelist_from') {
+ sasql_AddPref ($DBHandle, $SqlTable, $username, $preference, $address);
+ } elseif ($action == 'delete' && $preference == 'whitelist_from') {
+sasql_DelPref_value ($DBHandle, $SqlTable, $username, $preference, $address);
+ }
sasql_DBDisconnect($DBHandle);
}
}
-$redir_uri = SM_PATH . 'src/read_body.php?';
-$redir_uri .= "&mailbox=$mailbox";
-$redir_uri .= "&passed_id=$passed_id";
-$redir_uri .= "&startMessage=$startMessage;";
-if ($ent_id) { $redir_uri .= "&ent_id=$ent_id"; }
-header("Location: $redir_uri");
+ $redir_uri = SM_PATH . 'src/read_body.php?';
+ $redir_uri .= "&mailbox=$mailbox";
+ $redir_uri .= "&passed_id=$passed_id";
+ $redir_uri .= "&startMessage=$startMessage;";
+ if ($ent_id) { $redir_uri .= "&ent_id=$ent_id"; }
+
+ header("Location: $redir_uri");
?>
["sasql_hooks-sasql-squirrelmail-1.4.9a.diff" (application/octet-stream)]
--- plugins/sasql/bac_sasql_hooks.php 2007-02-20 18:22:00.128994976 +0530
+++ plugins/sasql/sasql_hooks.php 2007-02-20 18:27:58.979441416 +0530
@@ -138,6 +138,9 @@
// SM stuff
global $username, $message, $sent_folder;
+ // address stuff used in spam_buttons/functions.php
+ global $chkfrom, $sort;
+
// sasql stuff
global $SqlDSN, $SqlTable;
global $use_quick_link;
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
--
squirrelmail-plugins mailing list
Posting Guidelines: http://www.squirrelmail.org/wiki/MailingListPostingGuidelines
List Address: squirrelmail-plugins@lists.sourceforge.net
List Archives: http://news.gmane.org/thread.php?group=gmane.mail.squirrelmail.plugins
List Archives: http://sourceforge.net/mailarchive/forum.php?forum_id=3931
List Info: https://lists.sourceforge.net/lists/listinfo/squirrelmail-plugins
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic