diff --git a/plugins/markasjunk/README.md b/plugins/markasjunk/README.md new file mode 100644 index 000000000..a3fa181ba --- /dev/null +++ b/plugins/markasjunk/README.md @@ -0,0 +1,133 @@ +Roundcube Webmail MarkAsJunk Plugin +=================================== +This plugin adds "mark as spam" or "mark as not spam" button to the message +menu. + +When not in the Junk mailbox: + Messages are moved into the Junk mailbox and marked as read + +When in the Junk mailbox: + The buttons are changed to "mark as not spam" or "this message is not spam" + and the message is moved to the Inbox + + +License +------- + +This plugin is released under the [GNU General Public License Version 3+][gpl]. + +Even if skins might contain some programming work, they are not considered +as a linked part of the plugin and therefore skins DO NOT fall under the +provisions of the GPL license. See the README file located in the core skins +folder for details on the skin license. + + +Configuration +------------- + +The default config file is plugins/markasjunk/config.inc.php.dist +Rename this to plugins/markasjunk/config.inc.php + +All config parameters are optional. + + +The Learning Driver +------------------- + +The learning driver allows you to perform additional processing on each message +marked as spam/ham. A driver must contain a class named markasjunk_{driver +file name}. The class must contain 3 functions: + +**spam:** This function should take 2 arguments: an array of UIDs of message(s) +being marked as spam, the name of the mailbox containing those messages + +**ham:** This function should take 2 arguments: an array of UIDs of message(s) +being marked as ham, the name of the mailbox containing those messages + +**init:** Optional, this function should take 0 arguments. eg: allows drivers +to add JS to the page to control which of the spam/ham options are displayed. +The `jsevents` driver is available to show how to use the JS events. + +Several drivers are provided by default they are: + +**cmd_learn:** This driver calls an external command (for example salearn) to +process the message + +**dir_learn:** This driver places a copy of the message in a predefined folder, +for example to allow for processing later + +**email_learn:** This driver emails the message either as an attachment or +directly to a set address. This driver requires Roundcube 1.4 or above. + +**sa_blacklist:** This driver adds the sender address of a spam message to the +users blacklist (or whitelist of ham messages) Requires SAUserPrefs plugin + +**amavis_blacklist:** This driver adds the sender address of a spam message to +the users blacklist (or whitelist of ham messages) Requires Amacube plugin. +Driver by Der-Jan + +**sa_detach:** If the message is a Spamassassin spam report with the original +email attached then this is detached and saved in the Inbox, the spam report is +deleted + +**edit_headers:** Edit the message headers. Headers are edited using +preg_replace. + +**WARNING:** Be sure to match the entire header line, including the name of the +header, and include the ^ and $ and test carefully before use on real messages. +This driver alters the message source + + +Running multiple drivers +------------------------ + +**WARNING:** This is very dangerous please always test carefully. Run multiple +drivers at your own risk! It may be safer to create one driver that does +everything you want. + +It is possible to run multiple drivers when marking a message as spam/ham. For +example running sa_blacklist followed by cmd_learn or edit_headers and +cmd_learn. An [example multi-driver][multidriver] is available. This is a +starting point only, it requires modification for individual cases. + + +Spam learning commands +---------------------- + +Spamassassin: + +```sa-learn --spam --username=%u %f``` or +```sa-learn --spam --prefs-file=/var/mail/%d/%l/.spamassassin/user_prefs %f``` + + +Ham learning commands +--------------------- + +Spamassassin: + +```sa-learn --ham --username=%u %f``` or +```sa-learn --ham --prefs-file=/var/mail/%d/%l/.spamassassin/user_prefs %f``` + + +edit_headers example config +--------------------------- + +**WARNING:** These are simple examples of how to configure the driver options, +use at your own risk + +```php +$config['markasjunk_spam_patterns'] = array( + 'patterns' => array('/^(Subject:\s*)(.*)$/m'), + 'replacements' => array('$1[SPAM] $2') +); +``` + +```php +$config['markasjunk_ham_patterns'] = array( + 'patterns' => array('/^(Subject:\s*)\[SPAM\](.*)$/m'), + 'replacements' => array('$1$2') +); +``` + +[gpl]: https://www.gnu.org/licenses/gpl.html +[multidriver]: https://gist.github.com/johndoh/8173505 diff --git a/plugins/markasjunk/composer.json b/plugins/markasjunk/composer.json index 7fa6b7d61..e52ee6726 100644 --- a/plugins/markasjunk/composer.json +++ b/plugins/markasjunk/composer.json @@ -1,14 +1,19 @@ { "name": "roundcube/markasjunk", "type": "roundcube-plugin", - "description": "Adds a new button to the mailbox toolbar to mark the selected messages as Junk and move them to the configured Junk folder.", + "description": "Adds buttons to mark messages as Junk/Non-Junk with moving to the Junk folder and Spam/Ham learning.", "license": "GPLv3+", - "version": "1.2", + "version": "2.0", "authors": [ { "name": "Thomas Bruederli", "email": "roundcube@gmail.com", "role": "Lead" + }, + { + "name": "Philip Weir", + "email": "roundcube@tehinterweb.co.uk", + "role": "Developer" } ], "repositories": [ diff --git a/plugins/markasjunk/config.inc.php.dist b/plugins/markasjunk/config.inc.php.dist new file mode 100644 index 000000000..45603562b --- /dev/null +++ b/plugins/markasjunk/config.inc.php.dist @@ -0,0 +1,167 @@ + 'mail1_config.inc.php', +// 'mail2.domain.tld' => 'mail2_config.inc.php', +// ); +$config['markasjunk_host_config'] = null; + +// cmd_learn Driver options +// ------------------------ +// The command used to learn that a message is spam +// The command can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %i is replaced with the email address from the user's default identity +// %s is replaced with the email address the message is from +// %f is replaced with the path to the message file +// %h:
is replaced with the content of that header from the message (lower case) eg: %h:x-dspam-signature +// If you do not want to run the command set this to null +$config['markasjunk_spam_cmd'] = null; + +// The command used to learn that a message is ham +// The command can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %i is replaced with the email address from the user's default identity +// %s is replaced with the email address the message is from +// %f is replaced with the path to the message file +// %h:
is replaced with the content of that header from the message (lower case) eg: %h:x-dspam-signature +// If you do not want to run the command set this to null +$config['markasjunk_ham_cmd'] = null; + +// dir_learn Driver options +// ------------------------ +// The full path of the directory used to store spam (must be writable by webserver) +$config['markasjunk_spam_dir'] = null; + +// The full path of the directory used to store ham (must be writable by webserver) +$config['markasjunk_ham_dir'] = null; + +// The filename prefix +// The filename can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %t is replaced with the type of message (spam/ham) +$config['markasjunk_filename'] = null; + +// email_learn Driver options +// -------------------------- +// The email address that spam messages will be sent to +// The address can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %i is replaced with the email address from the user's default identity +// If you do not want to send an email set this to null +$config['markasjunk_email_spam'] = null; + +// The email address that ham messages will be sent to +// The address can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %i is replaced with the email address from the user's default identity +// If you do not want to send an email set this to null +$config['markasjunk_email_ham'] = null; + +// Should the spam/ham message be sent as an attachment +$config['markasjunk_email_attach'] = true; + +// The email subject (when sending as attachment) +// The subject can contain the following macros that will be expanded as follows: +// %u is replaced with the username (from the session info) +// %l is replaced with the local part of the username (if the username is an email address) +// %d is replaced with the domain part of the username (if the username is an email address or default mail domain if not) +// %t is replaced with the type of message (spam/ham) +$config['markasjunk_email_subject'] = 'learn this message as %t'; + +// sa_blacklist Driver options +// --------------------------- +// Path to SAUserPrefs config file +$config['markasjunk_sauserprefs_config'] = '../sauserprefs/config.inc.php'; + +// amavis_blacklist Driver options +// --------------------------- +// Path to amacube config file +$config['markasjunk_amacube_config'] = '../amacube/config.inc.php'; + +// edit_headers Driver options +// --------------------------- +// Patterns to match and replace headers for spam messages +// Replacement method uses preg_replace - http://www.php.net/manual/function.preg-replace.php +// WARNING: Be sure to match the entire header line, including the name of the header, also use ^ and $ and the 'm' flag +// see the README for an example +// TEST CAREFULLY BEFORE USE ON REAL MESSAGES +$config['markasjunk_spam_patterns'] = array( + 'patterns' => array(), + 'replacements' => array() +); + +// Patterns to match and replace headers for spam messages +// Replacement method uses preg_replace - http://www.php.net/manual/function.preg-replace.php +// WARNING: Be sure to match the entire header line, including the name of the header, also use ^ and $ and the 'm' flag +// see the README for an example +// TEST CAREFULLY BEFORE USE ON REAL MESSAGES +$config['markasjunk_ham_patterns'] = array( + 'patterns' => array(), + 'replacements' => array() +); diff --git a/plugins/markasjunk/drivers/amavis_blacklist.php b/plugins/markasjunk/drivers/amavis_blacklist.php new file mode 100644 index 000000000..36e203c62 --- /dev/null +++ b/plugins/markasjunk/drivers/amavis_blacklist.php @@ -0,0 +1,148 @@ +_do_list($uids, true); + } + + public function ham($uids, $src_mbox, $dst_mbox) + { + $this->_do_list($uids, false); + } + + private function _do_list($uids, $spam) + { + $rcube = rcube::get_instance(); + $this->user_email = $rcube->user->data['username']; + + if (is_file($rcube->config->get('markasjunk_amacube_config')) && !$rcube->config->load_from_file($rcube->config->get('markasjunk_amacube_config'))) { + rcube::raise_error(array('code' => 527, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Failed to load config from " . $rcube->config->get('markasjunk_amacube_config') + ), true, false); + + return false; + } + + $db = rcube_db::factory($rcube->config->get('amacube_db_dsn'), '', true); + $db->set_debug((bool) $rcube->config->get('sql_debug')); + $db->db_connect('w'); + + $debug = $rcube->config->get('markasjunk_debug'); + + // check DB connections and exit on failure + if ($err_str = $db->is_error()) { + rcube::raise_error(array( + 'code' => 603, + 'type' => 'db', + 'message' => $err_str + ), false, true); + } + + $sql_result = $db->query("SELECT `id` FROM `users` WHERE `email` = ?", $this->user_email); + if ($sql_result && ($res_array = $db->fetch_assoc($sql_result))) { + $rid = $res_array['id']; + } + else { + if ($debug) { + rcube::write_log('markasjunk', $this->user_email . ' not found in users table'); + } + + return false; + } + + foreach ($uids as $uid) { + $message = new rcube_message($uid); + $email = $message->sender['mailto']; + $sql_result = $db->query("SELECT `id` FROM `mailaddr` WHERE `email` = ? ORDER BY `priority` DESC", $email); + + if ($sql_result && ($res_array = $db->fetch_assoc($sql_result))) { + $sid = $res_array['id']; + } + else { + if ($debug) { + rcube::write_log('markasjunk', $email . ' not found in mailaddr table - add it'); + } + + $sql_result = $db->query("INSERT INTO `mailaddr` ( `priority`, `email` ) VALUES ( 20, ? )", $email); + if ($sql_result) { + $sid = $db->insert_id(); + } + else { + if ($debug) { + rcube::write_log('markasjunk', 'Cannot add ' . $email . ' to mailaddr table: ' . $db->is_error($sql_result)); + } + + return false; + } + } + + $wb = ''; + $sql_result = $db->query("SELECT `wb` FROM `wblist` WHERE `sid` = ? AND `rid` =?", $sid, $rid); + if ($sql_result && ($res_array = $db->fetch_assoc($sql_result))) { + $wb = $res_array['wb']; + } + + if (!$wb || (!$spam && preg_match('/^([BbNnFf])[\s]*\z/', $wb)) || ($spam && preg_match('/^([WwYyTt])[\s]*\z/', $wb))) { + $newwb = 'w'; + + if ($spam) { + $newwb = 'b'; + } + + if ($wb) { + $sql_result = $db->query('UPDATE `wblist` SET `wb` = ? WHERE `sid` = ? AND `rid` = ?', + $newwb, $sid, $rid); + } + else { + $sql_result = $db->query('INSERT INTO `wblist` (`sid`, `rid`, `wb`) VALUES (?,?,?)', + $sid, $rid, $newwb); + } + + if (!$sql_result) { + if ($debug) { + rcube::write_log('markasjunk', 'Cannot update wblist for user ' . $this->user_email . ' with ' . $email); + } + + return false; + } + } + } + } +} diff --git a/plugins/markasjunk/drivers/cmd_learn.php b/plugins/markasjunk/drivers/cmd_learn.php new file mode 100644 index 000000000..60430beeb --- /dev/null +++ b/plugins/markasjunk/drivers/cmd_learn.php @@ -0,0 +1,114 @@ + + * + * Copyright (C) 2009-2018 Philip Weir + * + * This driver is part of the MarkASJunk plugin for Roundcube. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Roundcube. If not, see https://www.gnu.org/licenses/. + */ +class markasjunk_cmd_learn +{ + public function spam($uids, $src_mbox, $dst_mbox) + { + $this->_do_salearn($uids, true, $src_mbox); + } + + public function ham($uids, $src_mbox, $dst_mbox) + { + $this->_do_salearn($uids, false, $src_mbox); + } + + private function _do_salearn($uids, $spam, $src_mbox) + { + $rcube = rcube::get_instance(); + $temp_dir = realpath($rcube->config->get('temp_dir')); + $command = $rcube->config->get($spam ? 'markasjunk_spam_cmd' : 'markasjunk_ham_cmd'); + $debug = $rcube->config->get('markasjunk_debug'); + + if (!$command) { + return; + } + + // backwards compatibility %xds removed in markasjunk v1.12 + $command = str_replace('%xds', '%h:x-dspam-signature', $command); + $command = str_replace('%u', $_SESSION['username'], $command); + $command = str_replace('%l', $rcube->user->get_username('local'), $command); + $command = str_replace('%d', $rcube->user->get_username('domain'), $command); + if (strpos($command, '%i') !== false) { + $identity_arr = $rcube->user->get_identity(); + $command = str_replace('%i', $identity_arr['email'], $command); + } + + foreach ($uids as $uid) { + // reset command for next message + $tmp_command = $command; + + if (strpos($tmp_command, '%s') !== false) { + $message = new rcube_message($uid); + $tmp_command = str_replace('%s', escapeshellarg($message->sender['mailto']), $tmp_command); + } + + if (strpos($command, '%h') !== false) { + $storage = $rcube->get_storage(); + $storage->check_connection(); + $storage->conn->select($src_mbox); + + preg_match_all('/%h:([\w-_]+)/', $tmp_command, $header_names, PREG_SET_ORDER); + foreach ($header_names as $header) { + $val = null; + if ($msg = $storage->conn->fetchHeader($src_mbox, $uid, true, false, array($header[1]))) { + $val = $msg->{$header[1]} ?: $msg->others[$header[1]]; + } + + if (!empty($val)) { + $tmp_command = str_replace($header[0], escapeshellarg($val), $tmp_command); + } + else { + if ($debug) { + rcube::write_log('markasjunk', 'header ' . $header[1] . ' not found in message ' . $src_mbox . '/' . $uid); + } + + continue 2; + } + } + } + + if (strpos($command, '%f') !== false) { + $tmpfname = tempnam($temp_dir, 'rcmSALearn'); + file_put_contents($tmpfname, $rcube->storage->get_raw_body($uid)); + $tmp_command = str_replace('%f', escapeshellarg($tmpfname), $tmp_command); + } + + $output = shell_exec($tmp_command); + + if ($debug) { + rcube::write_log('markasjunk', $tmp_command); + rcube::write_log('markasjunk', $output); + } + + if (strpos($command, '%f') !== false) { + unlink($tmpfname); + } + } + } +} diff --git a/plugins/markasjunk/drivers/dir_learn.php b/plugins/markasjunk/drivers/dir_learn.php new file mode 100644 index 000000000..0fa295460 --- /dev/null +++ b/plugins/markasjunk/drivers/dir_learn.php @@ -0,0 +1,64 @@ +_do_messagemove($uids, true); + } + + public function ham($uids, $src_mbox, $dst_mbox) + { + $this->_do_messagemove($uids, false); + } + + private function _do_messagemove($uids, $spam) + { + $rcube = rcube::get_instance(); + $dest_dir = unslashify($rcube->config->get($spam ? 'markasjunk_spam_dir' : 'markasjunk_ham_dir')); + + if (!$dest_dir) { + return; + } + + $debug = $rcube->config->get('markasjunk_debug'); + $filename = $rcube->config->get('markasjunk_filename'); + $filename = str_replace('%u', $_SESSION['username'], $filename); + $filename = str_replace('%t', ($spam) ? 'spam' : 'ham', $filename); + $filename = str_replace('%l', $rcube->user->get_username('local'), $filename); + $filename = str_replace('%d', $rcube->user->get_username('domain'), $filename); + + foreach ($uids as $uid) { + $tmpfname = tempnam($dest_dir, $filename); + file_put_contents($tmpfname, $rcube->storage->get_raw_body($uid)); + + if ($debug) { + rcube::write_log('markasjunk', $tmpfname); + } + } + } +} diff --git a/plugins/markasjunk/drivers/edit_headers.php b/plugins/markasjunk/drivers/edit_headers.php new file mode 100644 index 000000000..5b4c6ca12 --- /dev/null +++ b/plugins/markasjunk/drivers/edit_headers.php @@ -0,0 +1,68 @@ +_edit_headers($uids, true, $dst_mbox); + } + + public function ham(&$uids, $src_mbox, $dst_mbox) + { + $this->_edit_headers($uids, false, $dst_mbox); + } + + private function _edit_headers(&$uids, $spam, $dst_mbox) + { + $rcube = rcube::get_instance(); + $args = $rcube->config->get($spam ? 'markasjunk_spam_patterns' : 'markasjunk_ham_patterns'); + + if (count($args['patterns']) == 0) { + return; + } + + $new_uids = array(); + foreach ($uids as $uid) { + $raw_message = $rcube->storage->get_raw_body($uid); + $raw_headers = $rcube->storage->get_raw_headers($uid); + + $updated_headers = preg_replace($args['patterns'], $args['replacements'], $raw_headers); + $raw_message = str_replace($raw_headers, $updated_headers, $raw_message); + + $saved = $rcube->storage->save_message($dst_mbox, $raw_message); + + if ($saved !== false) { + $rcube->output->command('rcmail_markasjunk_move', null, $uid); + array_push($new_uids, $saved); + } + } + + if (count($new_uids) > 0) { + $uids = $new_uids; + } + } +} diff --git a/plugins/markasjunk/drivers/email_learn.php b/plugins/markasjunk/drivers/email_learn.php new file mode 100644 index 000000000..98df0eef0 --- /dev/null +++ b/plugins/markasjunk/drivers/email_learn.php @@ -0,0 +1,173 @@ +_do_emaillearn($uids, true); + } + + public function ham($uids, $src_mbox, $dst_mbox) + { + $this->_do_emaillearn($uids, false); + } + + private function _do_emaillearn($uids, $spam) + { + $this->rcube = rcube::get_instance(); + $identity_arr = $this->rcube->user->get_identity(); + $from = $identity_arr['email']; + $from_string = format_email_recipient($identity_arr['email'], $identity_arr['name']); + $attach = $this->rcube->config->get('markasjunk_email_attach', false); + $temp_dir = unslashify($this->rcube->config->get('temp_dir')); + + $mailto = $this->rcube->config->get($spam ? 'markasjunk_email_spam' : 'markasjunk_email_ham'); + $mailto = $this->_parse_vars($mailto, $spam, $from); + + // no address to send to, exit + if (!$mailto) { + return; + } + + $subject = $this->rcube->config->get('markasjunk_email_subject'); + $subject = $this->_parse_vars($subject, $spam, $from); + + foreach ($uids as $uid) { + $MESSAGE = new rcube_message($uid); + $message_file = null; + + // set message charset as default + if (!empty($MESSAGE->headers->charset)) { + $this->rcube->storage->set_charset($MESSAGE->headers->charset); + } + + $OUTPUT = $this->rcube->output; + $SENDMAIL = new rcmail_sendmail(null, array( + 'sendmail' => true, + 'from' => $from, + 'mailto' => $mailto, + 'dsn_enabled' => false, + 'charset' => 'UTF-8', + 'error_handler' => function() use ($OUTPUT) { + call_user_func_array(array($OUTPUT, 'show_message'), func_get_args()); + $OUTPUT->send(); + } + )); + + if ($attach) { + $headers = array( + 'Date' => $this->rcube->user_date(), + 'From' => $from_string, + 'To' => $mailto, + 'Subject' => $subject, + 'User-Agent' => $this->rcube->config->get('useragent'), + 'Message-ID' => $this->rcube->gen_message_id($from), + 'X-Sender' => $from + ); + + $message_text = ($spam ? 'Spam' : 'Ham') . ' report from ' . $this->rcube->config->get('product_name'); + + // create attachment + $orig_subject = $MESSAGE->get_header('subject'); + $disp_name = (!empty($orig_subject) ? $orig_subject : 'message_rfc822') . '.eml'; + $message_file = tempnam($temp_dir, 'rcm'); + $attachment = array(); + + if ($fp = fopen($message_file, 'w')) { + $this->rcube->storage->get_raw_body($uid, $fp); + fclose($fp); + + $attachment = array( + 'name' => $disp_name, + 'mimetype' => 'message/rfc822', + 'path' => $message_file, + 'size' => filesize($message_file), + 'charset' => $MESSAGE->headers->charset + ); + } + + // create message + $MAIL_MIME = $SENDMAIL->create_message($headers, $message_text, false, array($attachment)); + + if (count($attachment) > 0) { // sanity check incase creating the attachment failed + $folding = (int) $this->rcube->config->get('mime_param_folding'); + + $MAIL_MIME->addAttachment($attachment['path'], + $attachment['mimetype'], $attachment['name'], true, + '8bit', 'attachment', $attachment['charset'], '', '', + $folding ? 'quoted-printable' : null, + $folding == 2 ? 'quoted-printable' : null, + '', RCUBE_CHARSET + ); + } + } + else { + $headers = array( + 'Resent-From' => $from_string, + 'Resent-To' => $mailto, + 'Resent-Date' => $this->rcube->user_date(), + 'Resent-Message-ID' => $this->rcube->gen_message_id($from) + ); + + // create the bounce message + $MAIL_MIME = new rcmail_resend_mail(array( + 'bounce_message' => $MESSAGE, + 'bounce_headers' => $headers, + )); + } + + $SENDMAIL->deliver_message($MAIL_MIME); + $message_file = $message_file ?: $MAIL_MIME->mailbody_file; + + // clean up + if ($message_file) { + unlink($message_file); + } + + if ($this->rcube->config->get('markasjunk_debug')) { + rcube::write_log('', $uid . ($spam ? ' SPAM ' : ' HAM ') . $mailto . ' (' . $subject . ')'); + + if ($smtp_error['vars']) { + rcube::write_log('', $smtp_error['vars']); + } + } + } + } + + private function _parse_vars($data, $spam, $from) + { + $data = str_replace('%u', $_SESSION['username'], $data); + $data = str_replace('%t', $spam ? 'spam' : 'ham', $data); + $data = str_replace('%l', $this->rcube->user->get_username('local'), $data); + $data = str_replace('%d', $this->rcube->user->get_username('domain'), $data); + $data = str_replace('%i', $from, $data); + + return $data; + } +} diff --git a/plugins/markasjunk/drivers/jsevent.php b/plugins/markasjunk/drivers/jsevent.php new file mode 100644 index 000000000..1b0540c4a --- /dev/null +++ b/plugins/markasjunk/drivers/jsevent.php @@ -0,0 +1,81 @@ +addition_spam_folders); + $js_suspicious_folders = json_encode($this->suspicious_folders); + + $script = << -1) { + props.disp.spam = false; + props.disp.ham = true; + } + else if ($.inArray(rcmail.env.mailbox, suspicious_folders) > -1) { + props.disp.spam = true; + props.disp.ham = true; + + // from here it is also possible to alter the buttons themselves... + props.objs.spamobj.find('a > span').text('As possibly spam'); + } + else { + props.objs.spamobj.find('a > span').text(rcmail.get_label('markasjunk.markasjunk')); + } + + return props; +}); +EOL; + + $rcmail = rcmail::get_instance(); + $rcmail->output->add_script($script, 'docready'); + } + + public function spam(&$uids, $mbox) + { + // Treat message as spam... + } + + public function ham(&$uids, $mbox) + { + // Treat message as ham... + } +} diff --git a/plugins/markasjunk/drivers/sa_blacklist.php b/plugins/markasjunk/drivers/sa_blacklist.php new file mode 100644 index 000000000..dbd4a6d99 --- /dev/null +++ b/plugins/markasjunk/drivers/sa_blacklist.php @@ -0,0 +1,145 @@ +_do_list($uids, true); + } + + public function ham($uids, $src_mbox, $dst_mbox) + { + $this->_do_list($uids, false); + } + + private function _do_list($uids, $spam) + { + $rcube = rcube::get_instance(); + $this->sa_user = $rcube->config->get('sauserprefs_userid', "%u"); + $this->sa_table = $rcube->config->get('sauserprefs_sql_table_name'); + $this->sa_username_field = $rcube->config->get('sauserprefs_sql_username_field'); + $this->sa_preference_field = $rcube->config->get('sauserprefs_sql_preference_field'); + $this->sa_value_field = $rcube->config->get('sauserprefs_sql_value_field'); + + $identity_arr = $rcube->user->get_identity(); + $identity = $identity_arr['email']; + + $this->sa_user = str_replace('%u', $_SESSION['username'], $this->sa_user); + $this->sa_user = str_replace('%l', $rcube->user->get_username('local'), $this->sa_user); + $this->sa_user = str_replace('%d', $rcube->user->get_username('domain'), $this->sa_user); + $this->sa_user = str_replace('%i', $identity, $this->sa_user); + + if (is_file($rcube->config->get('markasjunk_sauserprefs_config')) && !$rcube->config->load_from_file($rcube->config->get('markasjunk_sauserprefs_config'))) { + rcube::raise_error(array('code' => 527, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Failed to load config from " . $rcube->config->get('markasjunk_sauserprefs_config') + ), true, false); + + return false; + } + + $db = rcube_db::factory($rcube->config->get('sauserprefs_db_dsnw'), $rcube->config->get('sauserprefs_db_dsnr'), $rcube->config->get('sauserprefs_db_persistent')); + $db->set_debug((bool) $rcube->config->get('sql_debug')); + $db->db_connect('w'); + + // check DB connections and exit on failure + if ($err_str = $db->is_error()) { + rcube::raise_error(array( + 'code' => 603, + 'type' => 'db', + 'message' => $err_str + ), false, true); + } + + foreach ($uids as $uid) { + $message = new rcube_message($uid); + $email = $message->sender['mailto']; + + if ($spam) { + // delete any whitelisting for this address + $db->query( + "DELETE FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?", + $this->sa_user, + 'whitelist_from', + $email); + + // check address is not already blacklisted + $sql_result = $db->query( + "SELECT `value` FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?", + $this->sa_user, + 'blacklist_from', + $email); + + if (!$db->fetch_array($sql_result)) { + $db->query( + "INSERT INTO `{$this->sa_table}` (`{$this->sa_username_field}`, `{$this->sa_preference_field}`, `{$this->sa_value_field}`) VALUES (?, ?, ?)", + $this->sa_user, + 'blacklist_from', + $email); + + if ($rcube->config->get('markasjunk_debug')) { + rcube::write_log('markasjunk', $this->sa_user . ' blacklist ' . $email); + } + } + } + else { + // delete any blacklisting for this address + $db->query( + "DELETE FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?", + $this->sa_user, + 'blacklist_from', + $email); + + // check address is not already whitelisted + $sql_result = $db->query( + "SELECT `value` FROM `{$this->sa_table}` WHERE `{$this->sa_username_field}` = ? AND `{$this->sa_preference_field}` = ? AND `{$this->sa_value_field}` = ?", + $this->sa_user, + 'whitelist_from', + $email); + + if (!$db->fetch_array($sql_result)) { + $db->query( + "INSERT INTO `{$this->sa_table}` (`{$this->sa_username_field}`, `{$this->sa_preference_field}`, `{$this->sa_value_field}`) VALUES (?, ?, ?)", + $this->sa_user, + 'whitelist_from', + $email); + + if ($rcube->config->get('markasjunk_debug')) { + rcube::write_log('markasjunk', $this->sa_user . ' whitelist ' . $email); + } + } + } + } + } +} diff --git a/plugins/markasjunk/drivers/sa_detach.php b/plugins/markasjunk/drivers/sa_detach.php new file mode 100644 index 000000000..3d4fdb769 --- /dev/null +++ b/plugins/markasjunk/drivers/sa_detach.php @@ -0,0 +1,62 @@ +storage; + $new_uids = array(); + + foreach ($uids as $uid) { + $saved = false; + $message = new rcube_message($uid); + + if (count($message->attachments) > 0) { + foreach ($message->attachments as $part) { + if ($part->ctype_primary == 'message' && $part->ctype_secondary == 'rfc822' && $part->ctype_parameters['x-spam-type'] == 'original') { + $orig_message_raw = $message->get_part_body($part->mime_id); + + if ($saved = $storage->save_message($dst_mbox, $orig_message_raw)) { + $rcube->output->command('rcmail_markasjunk_move', null, $uid); + array_push($new_uids, $saved); + } + } + } + } + } + + if (count($new_uids) > 0) { + $uids = $new_uids; + } + } +} diff --git a/plugins/markasjunk/localization/en_US.inc b/plugins/markasjunk/localization/en_US.inc index aaa3c91ac..9f9e5fd3f 100644 --- a/plugins/markasjunk/localization/en_US.inc +++ b/plugins/markasjunk/localization/en_US.inc @@ -5,7 +5,7 @@ | plugins/markasjunk/localization/.inc | | | | Localization file of the Roundcube Webmail Mark-As-Junk plugin | - | Copyright (C) 2012-2013, The Roundcube Dev Team | + | Copyright (C) 2012-2018, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -17,8 +17,14 @@ */ $labels = array(); -$labels['buttontext'] = 'Junk'; -$labels['buttontitle'] = 'Mark as Junk'; -$labels['reportedasjunk'] = 'Successfully reported as Junk'; +$labels['buttonjunk'] = 'This message is junk'; +$labels['asjunk'] = 'As junk'; +$labels['markasjunk'] = 'Mark as junk'; +$labels['buttonnotjunk'] = 'This message is not junk'; +$labels['asnotjunk'] = 'As not junk'; +$labels['markasnotjunk'] = 'Mark as not junk'; +$labels['notjunk'] = 'Not junk'; -?> \ No newline at end of file +$messages = array(); +$messages['reportedasjunk'] = 'Successfully reported as junk'; +$messages['reportedasnotjunk'] = 'Successfully reported as not junk'; diff --git a/plugins/markasjunk/markasjunk.js b/plugins/markasjunk/markasjunk.js index 7540c893d..964cae8bb 100644 --- a/plugins/markasjunk/markasjunk.js +++ b/plugins/markasjunk/markasjunk.js @@ -4,7 +4,8 @@ * @licstart The following is the entire license notice for the * JavaScript code in this file. * - * Copyright (c) 2013, The Roundcube Dev Team + * Copyright (c) 2013-2018 The Roundcube Dev Team + * Copyright (C) 2009-2018 Philip Weir * * The JavaScript code in this page is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License @@ -15,29 +16,117 @@ * for the JavaScript code in this file. */ -function rcmail_markasjunk(prop) -{ - if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length)) - return; - - var uids = rcmail.env.uid ? rcmail.env.uid : rcmail.message_list.get_selection().join(','), - lock = rcmail.set_busy(true, 'loading'); +rcube_webmail.prototype.markasjunk_mark = function(is_spam) { + var uids = this.env.uid ? [this.env.uid] : this.message_list.get_selection(); + if (!uids) + return; - rcmail.http_post('plugin.markasjunk', '_uid='+uids+'&_mbox='+urlencode(rcmail.env.mailbox), lock); + var lock = this.set_busy(true, 'loading'); + this.http_post('plugin.markasjunk.' + (is_spam ? 'junk' : 'not_junk'), this.selection_post_data({_uid: uids}), lock); } -// callback for app-onload event -if (window.rcmail) { - rcmail.addEventListener('init', function(evt) { - - // register command (directly enable in message view mode) - rcmail.register_command('plugin.markasjunk', rcmail_markasjunk, rcmail.env.uid); - - // add event-listener to message list - if (rcmail.message_list) - rcmail.message_list.addEventListener('select', function(list){ - rcmail.enable_command('plugin.markasjunk', list.get_selection().length > 0); - }); - }) +rcube_webmail.prototype.rcmail_markasjunk_move = function(mbox, uids) { + var prev_uid = this.env.uid, a_uids = $.isArray(uids) ? uids : uids.split(","); + + if (this.message_list && a_uids.length == 1 && !this.message_list.in_selection([a_uids[0]])) + this.env.uid = a_uids[0]; + + if (mbox) + this.move_messages(mbox); + else if (this.env.markasjunk_permanently_remove == true) + this.permanently_remove_messages(); + else + this.delete_messages(); + + this.env.uid = prev_uid; +} + +rcube_webmail.prototype.markasjunk_toggle_button = function() { + var spamobj = $('a.junk'), + hamobj = $('a.notjunk'), + disp = {spam: true, ham: true}; + + if (this.env.markasjunk_spam_only) { + disp.ham = false; + } + else if (!this.is_multifolder_listing() && this.env.markasjunk_spam_mailbox) { + if (this.env.mailbox != this.env.markasjunk_spam_mailbox) + disp.ham = false; + else + disp.spam = false; + } + + // if only 1 button is visible make sure its the last one (for styling) + // allow for multiple instances of the buttons, eg toolbar and contextmenu + $.each(spamobj, function(i) { + var cur_spamobj = spamobj.eq(i), + cur_hamobj = hamobj.eq(i), + cur_index = spamobj.eq(i).index(); + + if (cur_spamobj.parent('li').length > 0) { + cur_spamobj = cur_spamobj.parent(); + cur_hamobj = cur_hamobj.parent(); + } + + var evt_rtn = rcmail.triggerEvent('markasjunk-update', {objs: {spamobj: cur_spamobj, hamobj: cur_hamobj}, disp: disp}); + if (evt_rtn && evt_rtn.abort) + return; + + disp = evt_rtn ? evt_rtn.disp : disp; + + disp.spam ? cur_spamobj.show() : cur_spamobj.hide(); + disp.ham ? cur_hamobj.show() : cur_hamobj.hide(); + + if (disp.spam && !disp.ham) { + if (cur_index < cur_hamobj.index()) { + cur_spamobj.insertAfter(cur_hamobj); + } + } + else if (cur_index > cur_hamobj.index()) { + cur_hamobj.insertAfter(cur_spamobj); + } + }); +} + +rcube_webmail.prototype.markasjunk_is_spam_mbox = function() { + return !this.is_multifolder_listing() && this.env.mailbox == this.env.markasjunk_spam_mailbox; } +if (window.rcmail) { + rcmail.addEventListener('init', function() { + // register command (directly enable in message view mode) + rcmail.register_command('plugin.markasjunk.junk', function() { rcmail.markasjunk_mark(true); }, !rcmail.markasjunk_is_spam_mbox() && rcmail.env.uid); + rcmail.register_command('plugin.markasjunk.not_junk', function() { rcmail.markasjunk_mark(false); }, rcmail.env.uid); + + if (rcmail.message_list) { + rcmail.message_list.addEventListener('select', function(list) { + rcmail.enable_command('plugin.markasjunk.junk', !rcmail.markasjunk_is_spam_mbox() && list.get_selection(false).length > 0); + rcmail.enable_command('plugin.markasjunk.not_junk', list.get_selection(false).length > 0); + }); + } + + // make sure the correct icon is displayed even when there is no listupdate event + rcmail.markasjunk_toggle_button(); + }); + + rcmail.addEventListener('listupdate', function() { rcmail.markasjunk_toggle_button(); }); + + rcmail.addEventListener('beforemoveto', function(mbox) { + if (mbox && typeof mbox === 'object') + mbox = mbox.id; + + var is_spam = null; + + // check if destination mbox equals junk box (and we're not already in the junk box) + if (rcmail.env.markasjunk_move_spam && mbox && mbox == rcmail.env.markasjunk_spam_mailbox && mbox != rcmail.env.mailbox) + is_spam = true; + // or if destination mbox equals ham box and we are in the junk box + else if (rcmail.env.markasjunk_move_ham && mbox && mbox == rcmail.env.markasjunk_ham_mailbox && rcmail.env.mailbox == rcmail.env.markasjunk_spam_mailbox) + is_spam = false; + + if (is_spam !== null) { + rcmail.markasjunk_mark(is_spam); + return false; + } + }); +} diff --git a/plugins/markasjunk/markasjunk.php b/plugins/markasjunk/markasjunk.php index bcf39f587..bad4933e2 100644 --- a/plugins/markasjunk/markasjunk.php +++ b/plugins/markasjunk/markasjunk.php @@ -1,73 +1,321 @@ 'Junk', + 'NONJUNK' => 'NonJunk' + ); + + + public function init() { - $rcmail = rcmail::get_instance(); + $this->register_action('plugin.markasjunk.junk', array($this, 'mark_message')); + $this->register_action('plugin.markasjunk.not_junk', array($this, 'mark_message')); - $this->register_action('plugin.markasjunk', array($this, 'request_action')); - $this->add_hook('storage_init', array($this, 'storage_init')); + $this->rcube = rcube::get_instance(); + $this->load_config(); + $this->_load_host_config(); - if ($rcmail->action == '' || $rcmail->action == 'show') { - $this->add_texts('localization', true); + // Host exceptions + $hosts = $this->rcube->config->get('markasjunk_allowed_hosts'); + if (!empty($hosts) && !in_array($_SESSION['storage_host'], (array) $hosts)) { + return; + } + + $this->ham_mbox = $this->rcube->config->get('markasjunk_ham_mbox', 'INBOX'); + $this->spam_mbox = $this->rcube->config->get('markasjunk_spam_mbox', $this->rcube->config->get('junk_mbox')); + $toolbar = $this->rcube->config->get('markasjunk_toolbar', true); + $this->_init_flags(); + + if ($this->rcube->action == '' || $this->rcube->action == 'show') { $this->include_script('markasjunk.js'); + $this->add_texts('localization', true); $this->include_stylesheet($this->local_skin_path() . '/markasjunk.css'); - $this->add_button(array( - 'type' => 'link', - 'label' => 'buttontext', - 'command' => 'plugin.markasjunk', - 'title' => 'buttontitle', - 'domain' => 'markasjunk', - 'class' => 'button buttonPas junk disabled', - 'classact' => 'button junk', - 'innerclass' => 'inner', - ),'toolbar'); + if ($toolbar) { + // add the buttons to the main toolbar + $this->add_button(array( + 'command' => 'plugin.markasjunk.junk', + 'type' => 'link', + 'class' => 'button buttonPas junk disabled', + 'classact' => 'button junk', + 'classsel' => 'button junk pressed', + 'title' => 'markasjunk.buttonjunk', + 'innerclass' => 'inner', + 'label' => 'junk' + ), 'toolbar'); + + $this->add_button(array( + 'command' => 'plugin.markasjunk.not_junk', + 'type' => 'link', + 'class' => 'button buttonPas notjunk disabled', + 'classact' => 'button notjunk', + 'classsel' => 'button notjunk pressed', + 'title' => 'markasjunk.buttonnotjunk', + 'innerclass' => 'inner', + 'label' => 'markasjunk.notjunk' + ), 'toolbar'); + } + else { + // add the buttons to the mark message menu + $this->add_button(array( + 'command' => 'plugin.markasjunk.junk', + 'type' => 'link-menuitem', + 'label' => 'markasjunk.asjunk', + 'id' => 'markasjunk', + 'class' => 'icon junk', + 'classact' => 'icon junk active', + 'innerclass' => 'icon junk' + ), 'markmenu'); + + $this->add_button(array( + 'command' => 'plugin.markasjunk.not_junk', + 'type' => 'link-menuitem', + 'label' => 'markasjunk.asnotjunk', + 'id' => 'markasnotjunk', + 'class' => 'icon notjunk', + 'classact' => 'icon notjunk active', + 'innerclass' => 'icon notjunk' + ), 'markmenu'); + } + + // add markasjunk folder settings to the env for JS + $this->rcube->output->set_env('markasjunk_ham_mailbox', $this->ham_mbox); + $this->rcube->output->set_env('markasjunk_spam_mailbox', $this->spam_mbox); + $this->rcube->output->set_env('markasjunk_move_spam', $this->rcube->config->get('markasjunk_move_spam', false)); + $this->rcube->output->set_env('markasjunk_move_ham', $this->rcube->config->get('markasjunk_move_ham', false)); + $this->rcube->output->set_env('markasjunk_permanently_remove', $this->rcube->config->get('markasjunk_permanently_remove', false)); + $this->rcube->output->set_env('markasjunk_spam_only', $this->rcube->config->get('markasjunk_spam_only', false)); + + // check for init method from driver + $this->_call_driver('init'); + } + } + + public function mark_message() + { + $this->add_texts('localization'); + + $is_spam = $this->rcube->action == 'plugin.markasjunk.junk' ? true : false; + $messageset = rcmail::get_uids(null, null, $multifolder, rcube_utils::INPUT_POST); + $mbox_name = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); + $dest_mbox = $is_spam ? $this->spam_mbox : $this->ham_mbox; + $result = $is_spam ? $this->_spam($messageset, $dest_mbox) : $this->_ham($messageset, $dest_mbox); + + if ($result) { + if ($dest_mbox && ($mbox_name !== $dest_mbox || $multifolder)) { + $this->rcube->output->command('rcmail_markasjunk_move', $dest_mbox, $this->_messageset_to_uids($messageset, $multifolder)); + } + else { + $this->rcube->output->command('command', 'list', $mbox_name); + } + + $this->rcube->output->command('display_message', $is_spam ? $this->gettext('reportedasjunk') : $this->gettext('reportedasnotjunk'), 'confirmation'); } + + $this->rcube->output->send(); + } + + public function set_flags($p) + { + $p['message_flags'] = array_merge((array) $p['message_flags'], $this->flags); + + return $p; } - function storage_init($args) + private function _spam(&$messageset, $dest_mbox = null) { - $flags = array( - 'JUNK' => 'Junk', - 'NONJUNK' => 'NonJunk', - ); + $storage = $this->rcube->get_storage(); + $result = true; - // register message flags - $args['message_flags'] = array_merge((array)$args['message_flags'], $flags); + foreach ($messageset as $source_mbox => &$uids) { + $storage->set_folder($source_mbox); - return $args; + $result = $this->_call_driver('spam', $uids, $source_mbox, $dest_mbox); + + // abort function of the driver says so + if (!$result) { + break; + } + + if ($this->rcube->config->get('markasjunk_read_spam', false)) { + $storage->set_flag($uids, 'SEEN', $source_mbox); + } + + if (array_key_exists('JUNK', $this->flags)) { + $storage->set_flag($uids, 'JUNK', $source_mbox); + } + + if (array_key_exists('NONJUNK', $this->flags)) { + $storage->unset_flag($uids, 'NONJUNK', $source_mbox); + } + } + + return $result; } - function request_action() + private function _ham(&$messageset, $dest_mbox = null) { - $this->add_texts('localization'); + $storage = $this->rcube->get_storage(); + $result = true; + + foreach ($messageset as $source_mbox => &$uids) { + $storage->set_folder($source_mbox); + + $result = $this->_call_driver('ham', $uids, $source_mbox, $dest_mbox); - $rcmail = rcmail::get_instance(); - $storage = $rcmail->get_storage(); + // abort function of the driver says so + if (!$result) { + break; + } - foreach (rcmail::get_uids(null, null, $multifolder, rcube_utils::INPUT_POST) as $mbox => $uids) { - $storage->unset_flag($uids, 'NONJUNK', $mbox); - $storage->set_flag($uids, 'JUNK', $mbox); + if ($this->rcube->config->get('markasjunk_unread_ham', false)) { + $storage->unset_flag($uids, 'SEEN', $source_mbox); + } + + if (array_key_exists('JUNK', $this->flags)) { + $storage->unset_flag($uids, 'JUNK', $source_mbox); + } + + if (array_key_exists('NONJUNK', $this->flags)) { + $storage->set_flag($uids, 'NONJUNK', $source_mbox); + } + } + + return $result; + } + + private function _call_driver($action, &$uids = null, $source_mbox = null, $dest_mbox = null) + { + $driver_name = $this->rcube->config->get('markasjunk_learning_driver'); + + if (empty($driver_name)) { + return true; + } + + $driver = $this->home . "/drivers/$driver_name.php"; + $class = "markasjunk_$driver_name"; + + if (!is_readable($driver)) { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => "markasjunk plugin: Unable to open driver file $driver" + ), true, false); + } + + include_once $driver; + + if (!class_exists($class, false) || !method_exists($class, 'spam') || !method_exists($class, 'ham')) { + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => "markasjunk plugin: Broken driver: $driver" + ), true, false); + } + + // call the relevant function from the driver + $object = new $class(); + if ($action == 'spam') { + $object->spam($uids, $source_mbox, $dest_mbox); + } + elseif ($action == 'ham') { + $object->ham($uids, $source_mbox, $dest_mbox); + } + elseif ($action == 'init' && method_exists($object, 'init')) { // method_exists check here for backwards compatibility + $object->init(); + } + + return $object->is_error ? false : true; + } + + private function _messageset_to_uids($messageset, $multifolder) + { + $a_uids = array(); + + foreach ($messageset as $mbox => $uids) { + foreach ($uids as $uid) { + $a_uids[] = $multifolder ? $uid . '-' . $mbox : $uid; + } } - if (($junk_mbox = $rcmail->config->get('junk_mbox'))) { - $rcmail->output->command('move_messages', $junk_mbox); + return $a_uids; + } + + private function _load_host_config() + { + $configs = $this->rcube->config->get('markasjunk_host_config'); + if (empty($configs) || !array_key_exists($_SESSION['storage_host'], (array) $configs)) { + return; } - $rcmail->output->command('display_message', $this->gettext('reportedasjunk'), 'confirmation'); - $rcmail->output->send(); + $file = $configs[$_SESSION['storage_host']]; + $this->load_config($file); + } + + private function _init_flags() + { + $spam_flag = $this->rcube->config->get('markasjunk_spam_flag'); + $ham_flag = $this->rcube->config->get('markasjunk_ham_flag'); + + if ($spam_flag === false) { + unset($this->flags['JUNK']); + } + elseif (!empty($spam_flag)) { + $this->flags['JUNK'] = $spam_flag; + } + + if ($ham_flag === false) { + unset($this->flags['NONJUNK']); + } + elseif (!empty($ham_flag)) { + $this->flags['NONJUNK'] = $ham_flag; + } + + if (count($this->flags) > 0) { + // register the ham/spam flags with the core + $this->add_hook('storage_init', array($this, 'set_flags')); + } } } diff --git a/plugins/markasjunk/skins/classic/images/mail_toolbar.png b/plugins/markasjunk/skins/classic/images/mail_toolbar.png new file mode 100644 index 000000000..aad0e9ebc Binary files /dev/null and b/plugins/markasjunk/skins/classic/images/mail_toolbar.png differ diff --git a/plugins/markasjunk/skins/classic/images/messageactions.png b/plugins/markasjunk/skins/classic/images/messageactions.png new file mode 100644 index 000000000..11a7f7f51 Binary files /dev/null and b/plugins/markasjunk/skins/classic/images/messageactions.png differ diff --git a/plugins/markasjunk/skins/classic/junk_act.png b/plugins/markasjunk/skins/classic/junk_act.png deleted file mode 100644 index b5a84f604..000000000 Binary files a/plugins/markasjunk/skins/classic/junk_act.png and /dev/null differ diff --git a/plugins/markasjunk/skins/classic/junk_pas.png b/plugins/markasjunk/skins/classic/junk_pas.png deleted file mode 100644 index b88a561a4..000000000 Binary files a/plugins/markasjunk/skins/classic/junk_pas.png and /dev/null differ diff --git a/plugins/markasjunk/skins/classic/markasjunk.css b/plugins/markasjunk/skins/classic/markasjunk.css index 5b1d47b46..fd1703fdd 100644 --- a/plugins/markasjunk/skins/classic/markasjunk.css +++ b/plugins/markasjunk/skins/classic/markasjunk.css @@ -1,6 +1,43 @@ +#messagetoolbar a.junk, +#messagetoolbar a.notjunk +{ + text-indent: -5000px; + background-image: url(images/mail_toolbar.png); +} + +#messagetoolbar a.junk +{ + background-position: -32px 0; +} + +#messagetoolbar a.junk.pressed +{ + background-position: -32px -32px; +} + +#messagetoolbar a.notjunk +{ + background-position: 0 0; +} + +#messagetoolbar a.notjunk.pressed +{ + background-position: 0 -32px; +} -#messagetoolbar a.button.junk { - text-indent: -5000px; - background: url(junk_act.png) 0 0 no-repeat; +ul.toolbarmenu li a.junk, +ul.toolbarmenu li a.notjunk +{ + background-image: url(images/messageactions.png) !important; + background-repeat: no-repeat; } +ul.toolbarmenu li a.junk +{ + background-position: 6px -17px !important; +} + +ul.toolbarmenu li a.notjunk +{ + background-position: 6px 1px !important; +} diff --git a/plugins/markasjunk/skins/larry/.gitignore b/plugins/markasjunk/skins/larry/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/markasjunk/skins/larry/images/mail_toolbar.png b/plugins/markasjunk/skins/larry/images/mail_toolbar.png new file mode 100644 index 000000000..573e91ade Binary files /dev/null and b/plugins/markasjunk/skins/larry/images/mail_toolbar.png differ diff --git a/plugins/markasjunk/skins/larry/images/messageactions.png b/plugins/markasjunk/skins/larry/images/messageactions.png new file mode 100644 index 000000000..8884cf79b Binary files /dev/null and b/plugins/markasjunk/skins/larry/images/messageactions.png differ diff --git a/plugins/markasjunk/skins/larry/markasjunk.css b/plugins/markasjunk/skins/larry/markasjunk.css new file mode 100644 index 000000000..5fa45249d --- /dev/null +++ b/plugins/markasjunk/skins/larry/markasjunk.css @@ -0,0 +1,31 @@ +#messagetoolbar a.junk, +#messagetoolbar a.notjunk +{ + background-image: url(images/mail_toolbar.png); +} + +#messagetoolbar a.junk +{ + background-position: center -14px; +} + +#messagetoolbar a.notjunk +{ + background-position: center -61px; +} + +ul.toolbarmenu li a.junk span.icon, +ul.toolbarmenu li a.notjunk span.icon +{ + background-image: url(images/messageactions.png) !important; +} + +ul.toolbarmenu li a.junk span.icon +{ + background-position: 1px -17px !important; +} + +ul.toolbarmenu li a.notjunk span.icon +{ + background-position: 1px 4px !important; +} \ No newline at end of file diff --git a/skins/elastic/styles/widgets/toolbar.less b/skins/elastic/styles/widgets/toolbar.less index 36b9c3d3a..93042c788 100644 --- a/skins/elastic/styles/widgets/toolbar.less +++ b/skins/elastic/styles/widgets/toolbar.less @@ -108,6 +108,9 @@ &.junk:before { content: @fa-var-fire; } + &.notjunk:before { + content: @fa-var-inbox; + } &.enigma:before, &.encrypt:before { content: @fa-var-lock; @@ -615,6 +618,12 @@ a.encrypt.sign:before { content: @fa-var-lock; // TODO } + a.junk:before { + content: @fa-var-fire; + } + a.notjunk:before { + content: @fa-var-inbox; + } } .toolbarmenu.listing li {