Как я могу загрузить вложения почты IMAP через SSL и сохранить их локально, используя Perl? - PullRequest
7 голосов
/ 16 марта 2010

Мне нужны предложения о том, как я могу загрузить вложения из моих писем IMAP, которые имеют вложения и текущую дату в строке темы, т.е. формат YYYYMMDD и сохранить вложения по локальному пути.

Я прошел модуль Perl Mail :: IMAPClient и могу подключиться к почтовому серверу IMAP, но мне нужна помощь по другим задачам. Еще одна вещь, на которую следует обратить внимание: мой IMAP-сервер требует аутентификации SSL.

Также вложениями могут быть файлы gz, tar или tar.gz.

Ответы [ 4 ]

5 голосов
/ 16 марта 2010

Ниже приведена простая программа, которая делает то, что вы хотите.

#! /usr/bin/perl

use warnings;
use strict;

Минимальная версия для Email::MIME предназначена для введения walk_parts.

use Email::MIME 1.901;
use IO::Socket::SSL;
use Mail::IMAPClient;
use POSIX qw/ strftime /;
use Term::ReadKey;

Вы не хотите жестко кодировать пароль в своей программе?

sub read_password {
  local $| = 1;
  print "Enter password: ";

  ReadMode "noecho";
  my $password = <STDIN>;
  ReadMode "restore";

  die "$0: unexpected end of input"
    unless defined $password;

  print "\n";
  chomp $password; 
  $password;
}

Подключение с использованием SSL. Мы должны быть в состоянии сделать это с помощью простого параметра Ssl для конструктора, но некоторые поставщики решили разбить его в своих пакетах.

my $pw = read_password;
my $imap = Mail::IMAPClient->new(
 #Debug    => 1,
  User     => "you\@domain.com",
  Password => $pw,
  Uid      => 1,
  Peek     => 1,  # don't set \Seen flag
  Socket   => IO::Socket::SSL->new(
                Proto    => 'tcp',
                PeerAddr => 'imap.domain.com',
                PeerPort => 993,
              ),
);

die "$0: connect: $@" if defined $@;

Если вам нужна папка, отличная от папки «Входящие», измените ее.

$imap->select("INBOX")
  or die "$0: select INBOX: ", $imap->LastError, "\n";

Используя поиск по IMAP, мы ищем все сообщения, темы которых содержат сегодняшнюю дату в формате ГГГГММДД. Дата может быть где угодно в теме, поэтому, например, тема «foo bar baz 20100316» будет соответствовать сегодня.

my $today = strftime "%Y%m%d", localtime $^T;
my @messages = $imap->search(SUBJECT => $today);
die "$0: search: $@" if defined $@;

Для каждого такого сообщения записывать его вложения в файлы в текущем каталоге. Мы пишем самый внешний слой вложений и не копаем вложенные вложения. Часть с параметром имени в его типе содержимого (как в image/jpeg; name="foo.jpg") считается вложением, и мы игнорируем все остальные части. Имя сохраненного вложения - это следующие компоненты, разделенные -: сегодняшняя дата, идентификатор сообщения IMAP, индекс позиции в сообщении и его имя на основе единицы.

foreach my $id (@messages) {
  die "$0: funky ID ($id)" unless $id =~ /\A\d+\z/;

  my $str = $imap->message_string($id)
    or die "$0: message_string: $@";

  my $n = 1;
  Email::MIME->new($str)->walk_parts(sub {
    my($part) = @_;
    return unless ($part->content_type =~ /\bname=([^"]+)/ 
                or $part->content_type =~ /\bname="([^"]+)"/); # " grr...

    my $name = "./$today-$id-" . $n++ . "-$1";
    print "$0: writing $name...\n";
    open my $fh, ">", $name
      or die "$0: open $name: $!";
    print $fh $part->content_type =~ m!^text/!
                ? $part->body_str
                : $part->body
      or die "$0: print $name: $!";
    close $fh
      or warn "$0: close $name: $!";
  });
}
3 голосов
/ 16 марта 2010

Если вы хотите использовать Mail :: IMAPClient , вы можете указать использовать SSL .

Кроме того, Net :: IMAP :: Simple :: SSL также может помочь вам в этом. Интерфейс такой же, как у Net :: IMAP :: Simple .

Получив сообщение, Анализ электронных писем с вложениями показывает, как извлечь вложения. Я не пробовал, но я догадываюсь, что использование Email :: MIME :: walk_parts может использоваться для значительного упрощения сценария, показанного в этой статье PerlMonks.

1 голос
/ 08 июня 2018

Я предпочитаю подход Mail :: IMAPClient , описанный Грегом, но очень важно binmode () дескриптор файла вывода, а именно, чтобы Windows не принимала 0x0A байтов в качестве перевода строки и заменив их на CRLF и, таким образом, сделав недействительными двоичные файлы. Извините, что замаскировал это как ответ, комментарии были бы уместны, но у меня пока нет репутации.

1 голос
/ 26 января 2016

Я немного изменил свой подход к загрузке вложений из @Greg, поскольку было показано, что скачивать вложения из SAP XML ненадежно. Они не соответствуют стандарту Content-Type: application/pdf; name=XXXXX, поэтому это доставило мне много проблем. Пример:

Content-ID: <payload-xxxxxxxxxxxxx@sap.com>
Content-Disposition: attachment;
    filename="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xml"
Content-Type: application/xml
Content-Descripton: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.xml

Остальная часть программы остается почти такой же. Разница в том, что теперь я использую MIME::Parser для извлечения всего сообщения, и я выбрасываю все, что связано с телом и изображением. Я также удалил Peek => 1, так как я хотел пометить сообщения как прочитанные после их загрузки (и переходить только по непрочитанным сообщениям). Log::Logger помог создать централизованный журнал:

--- Фрагмент 1 --- Libs

#! /usr/bin/perl
use warnings;
use strict;
use Mail::IMAPClient; #IMAP connection
use Log::Logger; #Logging facility
use MIME::Parser; #Mime "slicer"
use DateTime; #Date
use File::Copy; #File manipulation
use File::Path qw( mkpath );

--- Фрагмент 2 --- Инициализация журнала

$log_script = new Log::Logger;
$log_script->open_append("/var/log/downloader.log");
my $dt = DateTime->now;
$dt->set_time_zone('America/Sao_Paulo');
$hour = (join ' ', $dt->ymd, $dt->hms);

--- Фрагмент 3 --- Загрузчик почты

$imap->select($remote_dir) or ($log_script->log("$hour: Account $account, Dir $remote_dir. Check if this folder exists") and next);
# Select unseen messages only
my @mails = ($imap->unseen);
foreach my $id (@mails) {
  my $subject = $imap->subject($id);
  my $str = $imap->message_string($id) or ($log_script->log("$hour: Account $account, Email \<$subject\> with problems. Crawling through next email") and next);
  my $parser = MIME::Parser->new();
  $parser->output_dir( $temp_dir );
  $parser->parse_data( $str );
  opendir(DIR, $temp_dir);
  foreach $file (readdir(DIR)) {
    next unless (-f "$temp_dir/$file");
    if ("$file" =~ /^msg/i){ # ignores body
      $body .= "$file ";
      unlink "$temp_dir/$file";
    } elsif (("$file" =~ /jpg$/i) # ignores signature images
          or ("$file" =~ /gif$/i)
          or ("$file" =~ /png$/i)) {
      $body .= "$file ";
      unlink "$temp_dir/$file";
    } else { # move attachments to destination dir
      $log_script->log("$hour: Account: $account, File $file, Email \<$subject\>, saved $local_dir");
      move "$temp_dir/$file", "$local_dir";
    };
 };
  $log_script->log("$hour: Files from email \<$subject\> ignored as they are body related stuff: $body") if $body;
...