Reading Attachments

davorg on 2004-11-02T14:38:22

I love it when Perl makes my life easier.I read my email in mutt on a remote server. This is fine, but can be a bit of a problem when I want to access a MIME attachment. Then I need to save the attachment to the filesystem and use scp to copy it to my local system.

But after watching Casey's talk on filtering email on the Perl Whirl, I spent 20 minutes knocking up this program which saves attachments to a directory in my web server's document tree. Now I can just go to a (password protected) page on my web site and read any attachment.

Email::MIME is a great module.

#!/usr/bin/perl
                                                                                
use strict;
use warnings;
                                                                                
use Email::MIME;
use constant DUMP => '/path/to/some/directory';
my $email = Email::MIME->new(join '', <>);
                                                                                
save_parts($email);
                                                                                
sub save_parts {
  my ($mime) = @_;
                                                                                
  return unless $mime->content_type;
                                                                                
  if ($mime->content_type !~ /^(text|multipart|message)/) {
    my $file = DUMP . $mime->filename(1);
    open FILE, '>', $file or die $!;
    print FILE $mime->body;
    close FILE;
    chmod 0644, $file;
  }
                                                                                
  my @parts = $mime->parts;
  if (@parts > 1) {
    save_parts($_) for @parts;
  }
}


Separated by a common language

VSarkiss on 2004-11-02T15:39:11

I spent 20 minutes knocking up this program
I don't think any American writer would have used that phrase in quite that fashion.

I remember reading in a Sherlock Holmes story, when Holmes wakes up Dr. Watson by knocking on his door early in the morning, he apologizes by saying:

Very sorry to knock you up, Watson, but it's the common lot this morning. Someone knocked up Mrs. Hudson, she prevailed upon me, and I upon you.
(I may not have the quote 100%, but it's very close.)

Re:Separated by a common language

Dom2 on 2004-11-02T16:13:25

Hmmm, it's not uncommon in British English either, as a term for being pregnant. But using it in a phrase to mean "quickly built" is also not uncommon and it's normally fairly easy to distinguish by context!

-Dom

Similar to what I have

Aristotle on 2004-11-02T20:46:26

Nine is quite a bit more elaborate though. Notable differences:

sysopen my $fh, $fn, O_WRONLY | O_EXCL | O_CREAT
    or die "Couldn't open $fn for writing: $!\n";

so I don't get stuff inadvertantly overwritten. Also,

for( $msg->parts ) {
    my $fn = $_->filename;
    next if not defined $fn;
    # ...
}

so I don't have to look at MIME types — that way only seemed to lead to madness. (What're you passing a parameter to ->filename() for btw?)

Other reasons for its length include a folder processing mode, ability to read files passed to it on the command line, and more. I have it nearly PODded up and everything, too — should probably put it online somewhere. Not that it's a huge amount of work, but I like to make small but shiny tools. :-)

I have noticed that Perl gets all flustered if you try to throw excessively large folders at it, though. :-( It works very fine from my .procmail though. Email::MIME is quite a fine module indeed, along with all the other nuggets the Perl Email Project turned out.

Re:Similar to what I have

Aristotle on 2004-11-02T21:24:51

In fact my code was closer to completion than I thought. Go take a look if you will. Thanks for inspiring me to finish this up. :-)

Re:Similar to what I have

davorg on 2004-11-02T21:32:16

What're you passing a parameter to ->filename() for btw?

The docs say 'Normally it will return the filename from the headers, but if filename is passed a true parameter, it will generate an appropriate "stable" filename if one is not found in the MIME headers.' So it's a safety feature. It invents a filename if one isn't already given.

Re:Similar to what I have

Aristotle on 2004-11-02T22:26:54

D'oh, I managed to miss that.

Same thing, but different

brian_d_foy on 2004-11-02T21:33:57

I wrote about this in The Perl Journal for one of the recent issues. I do the same thing, although each email address gets their own directory, and I only run it from within PINE when I want to detach something.

Path Attacks?

pjf on 2004-11-03T13:23:02

Davorg knocked up a script that contained:

    my $file = DUMP . $mime->filename(1);
    open FILE, '>', $file or die $!;

What happens when I send you an attachment with a filename of ../../../../../home/davorg/.ssh/authorized_keys, or perhaps more innocently .htaccess?

This is an excellent use for File::Basename, and Aristotle's previous sysopen() involving O_EXCL|O_CREAT.

No prizes for guessing which course I've been recently reviewing.

Re:Path Attacks?

davorg on 2004-11-03T13:40:00

I don't suppose you'd believe that this was a simplified copy of the program would you :)

It's also been pointed out that an attachment called .htaccess would have interesting results.