如何通过SSL下载IMAP邮件附件并使用Perl将其保存在本地?

ioekq8ef  于 2022-11-15  发布在  Perl
关注(0)|答案(4)|浏览(169)

我需要建议如何从我的IMAP邮件下载附件,其中有附件和当前日期的主题行,即YYYYMMDD,并将附件保存到本地路径。
我通过了Perl模块Mail::IMAPClient,能够连接到IMAP邮件服务器,但在其他任务上需要帮助。还有一件事要注意的是,我的IMAP服务器需要SSL认证。
附件也可以是gz、tar或tar.gz文件。

58wvjzkj

58wvjzkj1#

下面是一个简单的程序,它可以做你想做的事情。

#! /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搜索,我们将查找主题中包含YYYYMMDD格式的今天日期的所有邮件。日期可以在主题中的任何位置,因此,例如,主题“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邮件ID、其在邮件中的位置的索引(从1开始)以及其名称。

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: $!";
  });
}
vlf7wbxs

vlf7wbxs2#

如果您想继续使用Mail::IMAPClient,可以告诉它使用SSL。
或者,Net::IMAP::Simple::SSL也可以帮助您实现这一点。其接口与Net::IMAP::Simple提供的接口相同。
一旦你有了消息,Parsing emails with attachments将展示如何提取附件。我还没有尝试过,但我的直觉是,使用Email::MIME::walk_parts可以大大简化PerlMonks文章中显示的脚本。

mbyulnm0

mbyulnm03#

我已经改变了一点我从@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 ---库

#! /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;
5w9g7ksd

5w9g7ksd4#

我更喜欢Greg提出的Mail::IMAPClient方法,但它对**binmode()**输出文件句柄是必不可少的,也就是说,防止Windows将0x0A字节假定为换行符,并将其替换为CRLF,从而使二进制文件无效。很抱歉,我将此伪装成一个答案,注解是适当的,但我现在没有任何声誉。

相关问题