2

Every day we receive an e-mail from e.g. foo@example.tld with an attachment, the filename is e.g. report.xlsx

How can I save the file with the received date? e.g. 20180131_report.xlsx and how can I filter on the subject or the sender?

My ~/.procmailrc:

:0
*^content-Type:
{
    :fw
    | ripmime --overwrite --no-nameless -i - -d /dir/to/save/attachment
}
6

If your Procmail or the receiving MTA is configured to put in a From_ line before the message proper, this pseudo-header generally already contains the date. You'll need to parse it, which is a drag, so unless this is a system where you really need to optimize for perfomance (hundreds of matches per second on this condition?) the absolutely easiest solution is to call date +%Y%m%d.

To match on either of two unrelated headers, just put them both in a regex with |:

:0
* ^Content-type:
* ^From:(.*\<)?foo@example\.tld|^Subject: Your daily report
| ripmime --overwrite --no-nameless -i - -d /dir/to/save/attachment/$(date +%Y%m%d)_report.xslx

(Bug here; see update below.)

The fw flags don't make sense in this context so I took them out (and actually I'm not sure the Content-type: condition makes a lot of sense either; most messages will have it anyway, these days).

If you have more complex conditions you want to combine, you can use a fundamental principle from logic called de Morgan's laws. There is no direct syntax in Procmail to say "this condition or that condition", but you can refactor this to "not ((not this condition) and (not that condition))."

:0
* ! this condition
* ! that condition
{ } # nothing happens here
:0E # else
{ LOG="at least one of them matched
" }

Or simply use scoring;

:0
* 1^0 this condition
* 1^0 that conditon
{ LOG="at least one of them matched
" }

Update:

It looks like ripmime doesn't actually support (extracting or) naming an individual attachment. The easiest solution is perhaps a cron job which renames the latest arrival a bit before midnight (or if you know when it arrives, a bit after the latest time you expect it):

 55 23 * * * cd /dir/to/save/attachment && mv report.xslx "$(date +%%Y%%m%%d)"_report.xslx

Notice how (peculiarly) you need to double any percent signs in a cron command! You would obviously revert the Procmail recipe above to simply have ripmime save to /dir/to/save/attachment

Alternatively, I would rename the attachment right after it arrives, perhaps while also tightening the conditions considerably. The following includes a fair amount of guesswork as to how exactly the message which delivers the attachment is encoded -- it could choose between a number of different content types, MIME structures, MIME header conventions, etc, so it probably doesn't work without some tweaking.

:0
* ^From:(.*\<)?foo@example\.tld
* ^Subject: Your daily report
* HB ?? ^Content-type: application/(octet-stream|vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet|vnd\.ms-here-be-dragons-xslx); filename="?report.xslx
| ( cd dir/to/save/attachment; \
    ripmime --overwrite --no-nameless -i - -d . && \
    mv report.xslx $(date +%Y%m%d)"_report.xslx )

The Content-type: header might not contain the filename; it could (and these days should) be specified in Content-Disposition: but many senders put it in both places for backwards compatibility. The filename should properly be RFC2231-encoded which means a number of optional fields could be populated where I have conveniently assumed they will be empty, like they were when ASCII filenames were the only game in town.

Notice also how I require the sender and the subject to match now. The HB ?? says (imprecisely) to look for a match either in the main message headers, or somewhere in the body. Properly speaking, the match should be in the headers of a MIME body part in the latter case, but Procmail has no easy way to specify this.

  • The babel of Excel content types: stackoverflow.com/questions/2937465/… – tripleee Feb 3 '18 at 11:16
  • One other option for renaming could be to write the file to a randomly-named directory and then rename whatever file is in that directory to report.xlsx. It may also be possible to use the --postfix option to ripmime, but that doesn't cater for the attachment having a name different from report.xlsx. – mjturner Feb 4 '18 at 11:07
  • Mm yeah, ripmime without --overwrite will take adequate precautions to not replace an existing file; but then you need additional logic to figure out what it did. I assume the OP used --overwrite consciously to replace any previous file. – tripleee Feb 4 '18 at 11:28
3

Answering each of your questions:

  1. You can call out to external commands from within a recipe, so you can do something like this to define variable TODAY:

    TODAY=`date +%Y%m%d`
    

    You can then refer to it in your recipes:

    ripmime --overwrite --no-nameless -i - -d /dir/to/save/attachment/$TODAY_report_xlsx
    

    If you need to determine the filename dynamically, adjust the recipe accordingly.

  2. To filter on a subject and a sender, you can do something like:

    :0
    * ^content-Type:
    * ^From:.* foo@example.tld
    * ^Subject:.*Report
    {
    ....
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.