chrisrth
chrisrth

Reputation: 1212

MIME::Parser - Can't save binary attachment

I am using Perl to read messages, look for and save attachments. Attachments will always be binary pdf documents and there will never be more than one attachment. I need to read the subject, check for and save an attachment (if exists) an copy message to a folder for temporary storage.

The reading, printing, copying functions all work. I've tried a lot of different scenarios with MIME::Parser (I have MIME::Tools installed) but either get a blank file or file with 1 or 2 characters. I'd also like to know how to determine / set the file extension rather than just blindly rename to .pdf.

#!/usr/bin/perl


use Net::IMAP::Simple::SSL;
use Email::Simple;
use MIME::Parser;

print "Content-type: text/html\n\n";

$server = new Net::IMAP::Simple::SSL('xxx');
$server->login('xxx','xxx');

my $folder='inbox';

my ($unseen, $recent, $total) = $server->status($folder);
my $newm = $server->select('INBOX');

my $tmp=($total-9); #limit for testing

my $outputdir = "./temp";
my $parser = new MIME::Parser;
$parser->output_dir($outputdir);


for (my $i = $tmp; $i <= $total; $i++) {

        if ($server->seen($i)) {
        print "Message #$i has been seen before...<br />";
        } else {

        my $es=Email::Simple->new(join '', @{$server->top($i)});
        print $es->header('Subject')." on ";
        print $es->header('Date')."<br />";
        print "You've just seen message #$i<br />" if $server->see($i)."<br />";
        $msg = $server->get($i);
        $parser->parse_data($msg);
        $server->copy($i,'dump');

        }

    }

 $server->quit();

 exit;

Error

parse_data: wrong argument ref type: Net::IMAP::Simple::_message at mailextract.pl line x

Upvotes: 0

Views: 2577

Answers (2)

chrisrth
chrisrth

Reputation: 1212

#!/usr/bin/perl

use Net::IMAP::Simple::SSL;
use MIME::Parser;

print "Content-type: text/html\n\n";

$server = new Net::IMAP::Simple::SSL('xxx');
$server->login('xxx','xxx');


my $newm=0;
   $newm = $server->select('INBOX');

if ($newm==0) {
  $server->quit();
  print "No New Messages.";
  exit;
  }

my $outputdir = "./temp";
my $parser = new MIME::Parser;
$parser->output_dir($outputdir);


for (my $i = 1; $i <= $newm; $i++) {

  my $entity = $parser->parse($server->getfh($i));
  my $from = $entity->head->get('From');
  my $subject = $entity->head->get('Subject');
  my $timestamp = $entity->head->get('Date');

  print "#$i $from / $subject / $timestamp<br />";

  for my $part ($entity->parts()) {
    print " / ".$part->mime_type;
    if ( $part->mime_type eq 'application/octet-stream' || $part->mime_type eq 'application/pdf' ) {
      my $filename = $part->bodyhandle->path;
      print " / $filename";
      }
    print "<br />";
    }
  $server->copy($i,'dump');
  $server->delete($i);
  }
$server->quit();

Upvotes: 0

Oesor
Oesor

Reputation: 6642

Don't know why you're using two different parsers...

my $entity = $parser->parse_data($message);
my $from = $entity->head->get('From');
my $subject = $entity->head->get('Subject');
my $timestamp = $entity->head->get('Date');

for my $part ($entity->parts()) {
  if ( $part->mime_type eq 'application/pdf' ) { ### Few different types in use, see what your
                                                 ###  messages get sent with
    my $filename = $part->bodyhandle->path;
    ...
    ### Do whatever
  }
}

Edit: And your error is happening because you're not feeding through the correct thing to be parsed, a Net::IMAP::Simple::_message instead of:

parse_data DATA

Instance method. Parse a MIME message that's already in core. This internally creates an "in memory" filehandle on a Perl scalar value

using PerlIO

You may supply the DATA in any of a number of ways...

    A scalar which holds the message. A reference to this scalar will be used internally.

    A ref to a scalar which holds the message. This reference will be used internally.

    DEPRECATED

    A ref to an array of scalars. The array is internally concatenated into a temporary string, and a reference to the new

string is used internally.

    It is much more efficient to pass in a scalar reference, so please consider refactoring your code to use that interface instead.

If you absolutely MUST pass an array, you may be better off using IO::ScalarArray in the calling code to generate a filehandle, and passing that filehandle to parse()

Try $parser->parse($server->getfh($i));

Upvotes: 1

Related Questions