CM_PERL(7)

πŸ’– CM_PERL(7) πŸ’–

okay so, welcome to cm_perl(7)</p> β€” this here is the manual for the claws-mail-perl-filter situation, aka the Perl plugin for Claws Mail. grab a snack, we're going in πŸ“


πŸŽ€ NAME πŸŽ€

cm_perl β€” a Perl Plugin for Claws Mail ✨


πŸ¦‹ DESCRIPTION πŸ¦‹

This plugin gives you an extended filtering engine for the email client Claws Mail. The whole point? It lets you use full Perl power in your email filters. like, actual Perl. in your filters. iconic behaviour, honestly πŸ‘‘


⭐ QUICK START ⭐

To get started, bestie, you can use the matcherrc2perlfilter.pl script that lives in the tools-directory to translate your old filtering rules into Perl. Just execute the script and follow the instructions πŸ“

heads up though β€” with recent versions of Claws Mail, this script might not work due to upstream syntax changes. This will get updated in the future. Send me an email if you have problems getting started πŸ’Œ

That said? You might wanna consider reading the rest of this manual and rewriting your rules yourself if you're choosing to use the plugin, because the Perl code that script produces is not exactly pretty πŸ™ˆ

Don't speak Perl? No problem, bestie β€” perldoc perlintro should give you enough information to do fancy stuff πŸ’…


🌸 USAGE 🌸

The Perl plugin expects a Perl script file called perl_filter in Claws Mail' config directory (usually $HOME/.claws-mail β€” try claws-mail --config-dir if you're unsure). If that file doesn't exist on plugin start, an empty one is created for you, no drama 🫢

This file, which doesn't need to start with a sha-bang (!#/bin/perl), holds the Perl instructions for your email filters. And to encourage some good manners, the code is executed in a use strict; environment 😌✨

Both Claws Mail' filtering conditions and actions are mapped to Perl functions with corresponding names, wherever this is possible. cute, right?


πŸ‘‘ FUNCTION LISTING πŸ‘‘

For a detailed function description, see section "FUNCTION DESCRIPTIONS", below πŸ‘‡

Standard Filtering Conditions

  • all, marked, unread, deleted, new, replied,
  • forwarded, locked, ignore_thread, colorlabel,
  • match, matchcase, regexp, regexpcase, test,
  • size_greater, size_smaller, size_equal,
  • score_greater, score_lower, score_equal, age_greater,
  • age_lower, partial, tagged

Standard Filtering Actions

  • mark, unmark, dele, mark_as_unread, mark_as_read,
  • lock, unlock, move, copy, color, execute,
  • hide, set_score, change_score, stop, forward,
  • forward_as_attachment, redirect, set_tag, unset_tag,
  • clear_tags

Fun stuff 🌈

  • header, body, filepath, extract_addresses,
  • move_to_trash, abort, addr_in_addressbook,
  • from_in_addressbook, get_attribute_value, SA_is_spam,
  • exit, manual, make_sure_folder_exists,
  • filter_log, filter_log_verbosity,
  • make_sure_tag_exists

πŸ”₯ FUNCTION DESCRIPTIONS πŸ”₯

In general, after the filtering invoked by the Perl script, the mail is passed on to Claws' internal filtering engine β€” unless a final rule was hit. Final rules stop not only the Perl filtering script at the point of their occurrence, but also prevent processing that email by Claws' internal filtering engine. (this might sound confusing, but you're already familiar with that concept from standard filters: after an email was e.g. moved, the following rules don't apply anymore. same energy πŸ’«)

Also, be careful with the way you quote. In particular, remember that the @-sign has a special meaning in Perl, and gets interpolated inside double quotes. See "Quote and Quote-like Operators" in perlop to learn more about quoting and interpolation πŸŽ€

Standard Filtering Conditions

  • all β€” Returns a true value. Available for completeness only. (it's giving "I'm just here to be here" πŸ’€)
  • marked β€” Returns a true value if the marked flag is set, false otherwise.
  • unread β€” Returns a true value if the unread flag is set, false otherwise.
  • deleted β€” Returns a true value if the deleted flag is set, false otherwise.
  • new β€” Returns a true value if the new flag is set, false otherwise.
  • replied β€” Returns a true value if the replied flag is set, false otherwise.
  • forwarded β€” Returns a true value if the forwarded flag is set, false otherwise.
  • locked β€” Returns a true value if the locked flag is set, false otherwise.
  • ignore_thread β€” Returns a true value if the "Ignore Thread" flag is set, false otherwise.
  • colorlabel COLOR, colorlabel β€” Returns a true value if message has the color COLOR. COLOR can be either a numeric value between 0 and 7 (with colors corresponding to the internal filtering engine), or the english color name as it is introduced in the filtering dialog (that is, one of: none, orange, red, pink, sky blue, blue, green or brown, while upper and lower case letters make no difference). If COLOR is omitted, 0 (none) is assumed.
  • size_greater SIZE β€” Returns a true value if message size is greater than SIZE, false otherwise.
  • size_smaller SIZE β€” Returns a true value if message size is smaller than SIZE, false otherwise.
  • size_equal SIZE β€” Returns a true value if message size is equal to SIZE, false otherwise.
  • score_greater SCORE β€” Returns a true value if message score is greater than SCORE, false otherwise.
  • score_lower SCORE β€” Returns a true value if message score is lower than SCORE, false otherwise.
  • score_equal SCORE β€” Returns a true value if message score is equal to SCORE, false otherwise.
  • age_greater AGE β€” Returns a true value if message age is greater than AGE, false otherwise.
  • age_lower AGE β€” Returns a true value if message age is lower than AGE, false otherwise.
  • partial β€” Returns a true value if message has only partially been downloaded, false otherwise.
  • tagged β€” Returns a true value if the messages has one or more tags.
  • test β€” Corresponds the 'test' internal filtering rule. In particular, it accepts the same symbols, namely:
    • %% β€” %
    • %s β€” Subject
    • %f β€” From
    • %t β€” To
    • %c β€” Cc
    • %d β€” Date
    • %i β€” Message-ID
    • %n β€” Newsgroups
    • %r β€” References
    • %F β€” Filename β€” should not be modified
  • match WHERE WHAT, matchcase WHERE WHAT, regexp WHERE WHAT, regexpcase WHERE WHAT β€” The matching functions have a special syntax πŸ‘€

The first argument is either any of to_or_cc, body_part, headers_part, headers_cont, message, to, from, subject, cc, newsgroups, inreplyto, references, or tag (those strings may or may not be quoted), and the pattern matching works on that area. If it is any other string (which must then be quoted), this string is taken to be the name of a header field.

The second argument is the string to look for. For match, matchcase, regexp and regexpcase we have case sensitive normal matching, case insensitive normal matching, case sensitive regular expression matching and case insensitive regular expression pattern matching, respectively.

The functions return true if the pattern was found, false otherwise.

Just as with the built-in filtering engine, the message body is searched and provided as is β€” no character-set analysis is done. Likewise, no HTML-tags are stripped. It should be possible to use external modules or programs for these tasks though. If you're doing that, drop me a message with your experiences πŸ’Œ

With Perl having its strengths in pattern matching, using Perl's builtin operators are usually a better option than using these functions. fr fr.

Standard Filtering Actions

The actions return a true value upon success, and 'undef' when an error occurred. Final message rules are indicated. (see above for a sketch of what a final rule is 🌟)

  • mark β€” Mark the message.
  • unmark β€” Unmark the message.
  • dele β€” Delete the message. Note the name change of Claws Mail' "delete" to "dele". This is because "delete" is one of Perl's builtin commands which cannot be redefined (if it can, tell me how πŸ‘€).
This is a final rule.
  • mark_as_read β€” Mark the message as read
  • mark_as_unread β€” Mark the message as unread
  • lock β€” Lock the message
  • unlock β€” Remove the message lock
  • move DESTINATION β€” Move the message to folder DESTINATION. The folder notation is the same that Claws Mail uses. You can copy & paste from the move dialog of the normal filtering, until you get a feeling for the notation.
This is a final rule.
  • copy DESTINATION β€” Copy the message to folder DESTINATION. The folder notation is the same that Claws Mail uses. You can copy & paste from the move dialog of the normal filtering, until you get a feeling for the notation.
  • execute COMMAND β€” This is the same as the test rule from section "Standard Filtering Conditions" except that it always returns a true value.
  • hide β€” Hide the message
  • set_score SCORE β€” Set message score to SCORE
  • change_score SCORE β€” Change message score by SCORE
  • stop β€” Stop Perl script at this point. Note that this is not a final rule, meaning that the email gets passed on to the internal filtering engine. See "abort" below if you don't want that.
  • forward ACCOUNT, EMAIL β€” Forward the message to email address EMAIL, using the account ID ACCOUNT as sender account. So far, you have to create a rule in the normal filtering engine to find out that number.
  • forward_as_attachment, ACCOUNT EMAIL β€” Forward the message to email address EMAIL in an attachment, using the account ID ACCOUNT as sender account. So far, you have to create a rule in the normal filtering engine to find out that number.
  • redirect ACCOUNT, EMAIL β€” Redirect the message to EMAIL, using the account ID ACCOUNT as sender account. So far, you have to create a rule in the normal filtering engine to find out that number.
  • set_tag TAG β€” Apply tag TAG. TAG must exist.
  • unset_tag TAG β€” Unset tag TAG.
  • clear_tags β€” Clear all tags.

Fun stuff 🌈

Functions ✨

  • header ARG, header β€” If ARG is not given, returns a list of all header field names of the mail.

If ARG is given, returns 'undef' if the header field ARG does not exist in the email. Otherwise, it returns:

  • in scalar context β€” The value of the header field ARG.
  • in list context β€” A list of all available header field values. This is useful if a header field occurs more than once in an email (eg the Received header).

...part 1 ending here bestie, more coming πŸ‘€πŸ’…

Okay so one tiny special-case before we keep going β€” the header field "References" is built different πŸ’…. In a scalar context it hands you back the first reference. In a list context it hands you back a list of all the references. Both, depending on the vibe you ask in. ✨

body

Returns the email body. As a scalar. That's it, that's the function. 🫢

filepath

Returns the file and path of the email that's being filtered right now (it lines up with the %F argument in the test rule).

extract_addresses

Pulls email addresses out of a string and gives you back a list of everything it found. Right now it hunts for addresses with the regex '[-.+\w]+\@[-.+\w]+'. Heads up bestie, this will not find every valid email address out there. If you've got a better regexp, feel free to send it over. 😭

move_to_trash

Moves the email message to the default trash folder.

This is a final rule. Once it fires, that's the end of the line for this mail. πŸ‘‘

abort

Stops the Perl script right at this point.

Also a final rule β€” unlike stop, there's no coming back from this one. πŸ’€

addr_in_addressbook EMAIL, ADDRESSBOOK

addr_in_addressbook EMAIL

Returns a true value if the email address EMAIL lives in the addressbook named ADDRESSBOOK. If you don't pass ADDRESSBOOK, it returns true if that email address is in any addressbook. Living everywhere and nowhere, icon behaviour. 🌈

from_in_addressbook ADDRESSBOOK

from_in_addressbook

Checks whether the email address sitting in the From-header is in addressbook ADDRESSBOOK (or any addressbook, if you left it out). It's implemented as:

my ($from) = extract_addresses(header("from"));
return 0 unless $from;
return addr_in_addressbook($from,@_);

so the exact same restrictions as extract_addresses apply here too. No cap. πŸŽ€

get_attribute_value EMAIL, ATTRIBUTE, ADDRESSBOOK

get_attribute_value EMAIL, ATTRIBUTE

Digs through addressbook ADDRESSBOOK (or all the addressbooks, if you didn't name one) looking for a contact with the email address EMAIL. If it finds one, it checks whether that contact has a user attribute called ATTRIBUTE. Then it returns the value of that attribute β€” or an empty string if the attribute wasn't found. And as usual, 'undef' gets returned if an error happened. 😭

SA_is_spam

This one is an alias to

not test 'spamc -c < %F > /dev/null'

exit

Has been redefined to be an alias to 'stop'. So do not use Perl's own 'exit' command, because that would straight up exit Claws Mail. Not the drama we're looking for. πŸ™ˆ

manual

Returns a true value if the filter script was invoked manually β€” like, via the Tools menu. Main character energy only. πŸ’…

make_sure_folder_exists IDENTIFIER

Returns a true value if the folder with id IDENTIFIER (e.g. #mh/Mail/foo/bar) exists or could be created.

make_sure_tag_exists TAG

Returns a true value if the tag TAG exists or could be created.

filter_log SECTION, TEXT

filter_log TEXT

Writes TEXT to the filter logfile. SECTION can be any of

  • "LOG_MANUAL"
  • "LOG_MATCH"
  • "LOG_ACTION"

If SECTION is omitted, "LOG_MANUAL" is assumed.

filter_log_verbosity VERBOSITY

filter_log_verbosity

Changes the filter log verbosity for the current mail. VERBOSITY must be any of

  1. Be silent
  2. Log MANUAL type
  3. Log Action type
  4. Log MATCH type

For what those numbers actually mean, go read the LOGGING section. If VERBOSITY is omitted, the filter logfile verbosity is not changed at all.

This function returns the filter_log_verbosity number from before the change (if any change happened). Time travel, lowkey. ⭐

Variables

$permanent

This scalar keeps its value between filtered mail messages. When the plugin starts up, it's initialized to the empty string. 🌸


✨ LOGGING ✨

To keep track of everything that's been done to your mails while filtering, this plugin supports logging. Three verbosity levels are recognized:

  1. logging disabled
  2. log only manual messages β€” that is, messages introduced by the "filter_log" command in filter scripts
  3. log manual messages and filter actions
  4. log manual messages, filter actions and filter matches

The messages get logged in Claws Mail's log window. The default log level is 2. Log level 3 is not recommended, because the matcher functions log a message whenever they succeed β€” so if you've got negative checks, you'll end up with confusing entries and you'll be sitting there like ??? 😭 If you want to keep track of matching, do it manually with "filter_log", or do it by temporarily enabling matcher logging with "filter_log_verbosity".

The first time you unload this plugin (or shut down Claws Mail), a section called [PerlPlugin] will be created in Claws Mail's configuration file clawsrc, containing one variable:

* filter_log_verbosity

If you want to change the default behaviour, you can edit this line.

Make sure Claws Mail is not running while you do this. πŸ”₯

It will be possible to access these settings via the GUI as soon as the author finds time to write a corresponding GTK plugin, or somebody else is interested in contributing one. (If that's you, you're the moment. πŸ‘‘)


🌈 EXAMPLE 🌈

This section lists a small example of a Perl script file. I'm sure you get the idea..

#-8<----------------------------------------------------
# -*- perl -*-

# local functions

# Learn ham messages, and move them to specified folder. This is
# useful for making sure a bayes filter sees ham as well.
sub learn_and_move {
    execute('put command to learn ham here');
    move(@_);
}

# Two-stage spam filter. Every email that scores higher than 15
# on SpamAssassin gets moved into the default trash folder.
# All mails lower than that, but higher than SpamAssassin's
# 'required_hits' go into  #mh/mail/Spam.
sub spamcheck {
    my $surely_spam = 15;
    my $filepath = filepath;
    my $spamc = `spamc -c < $filepath`;
    my ($value,$threshold) = ($spamc =~ m|([-.,0-9]+)/([-.,0-9]+)|);
    if($value >= $surely_spam) {
       mark_as_read;
       move_to_trash;
    }
    if($value >= $threshold) {mark_as_read; move '#mh/mail/Spam';}
}

# Perl script execution starts here.

# Some specific sorting
learn_and_move '#mh/mail/MailLists/Claws Mail/user'
  if matchcase('sender','claws-mail-users-admin@lists.sourceforge.net');
learn_and_move '#mh/mail/MailLists/Sylpheed'
  if matchcase('list-id','sylpheed.good-day.net');

# Implement incoming folders using addressbook
# attributes. Target folders for specific email addresses are
# stored directly in the addressbook. This way, if an email
# address changes, we only have to update the addressbook, not
# the filter rules! Besides that, we can greatly unclutter the
# filter script.

# get the email address in the from header
my $fromheader = header "from";
my ($from) = extract_addresses $fromheader;

# check if this email address has an associated attribute
# called "incoming_folder". If if has, the value of this
# attribute is supposed to be the target folder.
my $value = get_attribute_value $from, "incoming_folder";
learn_and_move($value) if $value;

# An example of a whitelist: If the from-address is in my
# "office" addressbook, move the mail to folder #mh/mail/office
learn_and_move '#mh/mail/office' if from_in_addressbook("office");

# If the from-address is in any other addressbook, move the
# mail to folder #mh/mail/inbox/known
learn_and_move '#mh/mail/inbox/known' if from_in_addressbook;

# Feed the remaining mails through SpamAssassin.
spamcheck;

# mails that make it to the end of the script are passed on to
# the internal filtering engine. If the internal rules don't say
# otherwise, the mails end up in the default inbox.
#-8<----------------------------------------------------

πŸ’€ BUGS πŸ’€

  • Do not use this plugin together with other filtering plugins β€” especially the Spamassassin and ClamAV plugins. They're all registered on the same hook, and the order in which the plugins execute is not guaranteed. Chaos behaviour, and not the cute kind. πŸ™ˆ
  • The filter script is not (yet) updated automatically when a folder gets renamed. Same deal for folder names inside addressbook user attributes. 😭
  • This plugin has only been tested with POP3 accounts. If you've got experience with IMAP or newsgroup accounts, drop the author a message. 🫢
  • Warning during compile time:
*** Warning: Linking the shared library perl_plugin.la against the
*** static library
/usr/lib/perl5/5.8.3/i586-linux-thread-multi/auto/DynaLoader/DynaLoader.a
is not portable!

Ideas to solve this one are welcome :-)

Please report comments, suggestions and bugreports to the address given in the AUTHOR section of this document.


πŸ“œ LICENSE and (no) WARRANTY πŸ“œ

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 this program. If not, see http://www.gnu.org/licenses/.


🌟 SEE ALSO 🌟

claws-mail(1), perl(1)

πŸ’– AUTHOR πŸ’–

Holger Berndt <berndth@gmx.de>


And that's the whole page, bestie β€” you ate it. πŸŽ€ This one's from version 4.4.0-1build1, dated 2026-08-21, and it's the CM_PERL(7) manual page itself. Go forth and filter, you iconic little Unix gremlin. πŸ’…βœ¨πŸ¦‹

πŸ“¦ where this page comes from