私は同様の問題を抱えています (gmail ではありません)。私にとって最大の問題は、再現可能なテスト ケースを作成することでした。そして、ついにそれを作成することができました(以下を参照)。
Seen
フラグに関しては、次のようになります。
- メッセージが新規/未表示の場合、IMAP fetch for
\Seen
flag は空を返します (つまり、電子メール メッセージに関連して存在しません)。
- メールボックス (INBOX) で IMAP 選択を行うと、そのフォルダー内の新しい (フラグ
UNSEEN
を持たない) メールの ID (または UID) のリストを含む「フラグ」が取得されます。\Seen
- 私のテストケースでは、メッセージのヘッダーを取得すると
BODY.PEEK
、\Seen
メッセージは設定されません。でそれらを取得するとBODY
、\Seen
設定されます
- 私のテストケースでは、フェッチも
(RFC822)
設定されていません\Seen
(Gmailのケースとは異なります)
pprint.pprint(inspect.getmembers(mail))
テストケースでは、 (あなたの代わりに)やろうとしていますが、設定されているdumpobj(uid, mail)
ことを確認した後でのみです\Seen
。私が得た出力はmail_object_inspect.txtに投稿されています. さらにmail.as_string()
印刷:
'差出人: jesse@example.com\n宛先: user@example.com\n件名: これはテスト メッセージです!\n\nこんにちは。私は破綻した投資銀行であるベアー スターンズ\nのディレクターのエグゼクティブ アシスタントです。6,000,000 米ドルに\nアクセスできます。...\n'
さらに悪いことに、コードのどこにも「フィールド」について言及imaplib
されていません (以下のファイル名は、大文字と小文字を区別しない「フィールド」がどこにも含まれていない場合に出力されます)。
$ grep -L -i field /usr/lib/python{2.7,3.2}/imaplib.py
/usr/lib/python2.7/imaplib.py
/usr/lib/python3.2/imaplib.py
...だから、その情報はあなたのダンプに保存されていなかったと思います。
ここでは、テスト ケースの再構築について少し説明します。最も難しいのは、システムに大量のものをインストールすることなく、任意のユーザーと電子メールですばやく実行できる小さな IMAP サーバーを見つけることでした。最後に、 trivial-server.plを見つけました。これは、Perl のNet::IMAP::Serverのサンプル ファイルです。Ubuntu 11.04 でテスト済み。
テスト ケースは、要約して投稿しようとする 2 つのファイル (多くのコメントを含む) と共に、この gistに貼り付けられます。
- trivial-serverB.pl - Perl (v5.10.1)
Net::IMAP::Server
サーバー (telnet クライアント セッションで、ファイルの最後に端末出力が貼り付けられます)
- testimap.py - Python 2.7/3.2
imaplib
クライアント (ファイルの最後にターミナル出力ペーストがあり、それ自体がサーバーで動作しています)
trivial-serverB.pl
最初に、次のものがあることを確認してNet::IMAP::Server
ください。注意してください。多くの依存関係があるため、以下のコマンドのインストールには時間がかかる場合があります。
sudo perl -MCPAN -e 'install Net::IMAP::Server'
次に、取得trivial-serverB.pl
したディレクトリに、SSL 証明書を含むサブディレクトリを作成します。
mkdir certs
openssl req \
-x509 -nodes -days 365 \
-subj '/C=US/ST=Oregon/L=Portland/CN=localhost' \
-newkey rsa:1024 -keyout certs/server-key.pem -out certs/server-cert.pem
最後に、管理プロパティを使用してサーバーを実行します。
sudo perl trivial-serverB.pl
trivial-serverB.pl
には、クライアントが SSL なしで接続できるようにするハックがあることに注意してください。ここにあるtrivial-serverB.pl
:
#!/usr/bin/perl
use v5.10.1;
use feature qw(say);
use Net::IMAP::Server;
package Demo::IMAP::Hack;
$INC{'Demo/IMAP/Hack.pm'} = 1;
sub capabilityb {
my $self = shift;
print STDERR "Capabilitin'\n";
my $base = $self->server->capability;
my @words = split " ", $base;
@words = grep {$_ ne "STARTTLS"} @words
if $self->is_encrypted;
unless ($self->auth) {
my $auth = $self->auth || $self->server->auth_class->new;
my @auth = $auth->sasl_provides;
# hack:
#unless ($self->is_encrypted) {
# # Lack of encrpytion makes us turn off all plaintext auth
# push @words, "LOGINDISABLED";
# @auth = grep {$_ ne "PLAIN"} @auth;
#}
push @words, map {"AUTH=$_"} @auth;
}
return join(" ", @words);
}
package Demo::IMAP::Auth;
$INC{'Demo/IMAP/Auth.pm'} = 1;
use base 'Net::IMAP::Server::DefaultAuth';
sub auth_plain {
my ( $self, $user, $pass ) = @_;
# XXX DO AUTH CHECK
$self->user($user);
return 1;
}
package Demo::IMAP::Model;
$INC{'Demo/IMAP/Model.pm'} = 1;
use base 'Net::IMAP::Server::DefaultModel';
sub init {
my $self = shift;
$self->root( Demo::IMAP::Mailbox->new() );
$self->root->add_child( name => "INBOX" );
}
###########################################
package Demo::IMAP::Mailbox;
use base qw/Net::IMAP::Server::Mailbox/;
use Data::Dumper;
my $data = <<'EOF';
From: jesse@example.com
To: user@example.com
Subject: This is a test message!
Hello. I am executive assistant to the director of
Bear Stearns, a failed investment Bank. I have
access to USD6,000,000. ...
EOF
my $msg = Net::IMAP::Server::Message->new($data);
sub load_data {
my $self = shift;
$self->add_message($msg);
}
my %ports = ( port => 143, ssl_port => 993 );
$ports{$_} *= 10 for grep {$> > 0} keys %ports;
$myserv = Net::IMAP::Server->new(
auth_class => "Demo::IMAP::Auth",
model_class => "Demo::IMAP::Model",
user => 'nobody',
log_level => 3, # at least 3 to output 'CONNECT TCP Peer: ...' message; 4 to output IMAP commands too
%ports,
);
# apparently, this overload MUST be after the new?! here:
{
no strict 'refs';
*Net::IMAP::Server::Connection::capability = \&Demo::IMAP::Hack::capabilityb;
}
# https://stackoverflow.com/questions/27206371/printing-addresses-of-perl-object-methods
say " -", $myserv->can('validate'), " -", $myserv->can('capability'), " -", \&Net::IMAP::Server::Connection::capability, " -", \&Demo::IMAP::Hack::capabilityb;
$myserv->run();
testimap.py
上記のサーバーをある端末で実行すると、別の端末で次のことができます。
python testimap.py
このコードは、上記のサーバーが提示する 1 つの (そして唯一の) メッセージからフィールドとコンテンツを読み取るだけで、最終的に\Seen
フィールドを復元 (削除) します。
import sys
if sys.version_info[0] < 3: # python 2.7
def uttc(x):
return x
else: # python 3+
def uttc(x):
return x.decode("utf-8")
import imaplib
import email
import pprint,inspect
imap_user = 'nobody'
imap_password = 'whatever'
imap_server = 'localhost'
conn = imaplib.IMAP4(imap_server)
conn.debug = 3
try:
(retcode, capabilities) = conn.login(imap_user, imap_password)
except:
print(sys.exc_info()[1])
sys.exit(1)
# not conn.select(readonly=1), else we cannot modify the \Seen flag later
conn.select() # Select inbox or default namespace
(retcode, messages) = conn.search(None, '(UNSEEN)')
if retcode == 'OK':
for num in uttc(messages[0]).split(' '):
if not(num):
print("No messages available: num is `{0}`!".format(num))
break
print('Processing message: {0}'.format(num))
typ, data = conn.fetch(num,'(FLAGS)')
isSeen = ( "Seen" in uttc(data[0]) )
print('Got flags: {2}: {0} .. {1}'.format(typ,data, # NEW: OK .. ['1 (FLAGS ())']
"Seen" if isSeen else "NEW"))
print('Peeking headers, message: {0} '.format(num))
typ, data = conn.fetch(num,'(BODY.PEEK[HEADER])')
pprint.pprint(data)
typ, data = conn.fetch(num,'(FLAGS)')
isSeen = ( "Seen" in uttc(data[0]) )
print('Got flags: {2}: {0} .. {1}'.format(typ,data, # NEW: OK .. ['1 (FLAGS ())']
"Seen" if isSeen else "NEW"))
print('Get RFC822 body, message: {0} '.format(num))
typ, data = conn.fetch(num,'(RFC822)')
mail = email.message_from_string(uttc(data[0][1]))
#pprint.pprint(inspect.getmembers(mail))
typ, data = conn.fetch(num,'(FLAGS)')
isSeen = ( "Seen" in uttc(data[0]) )
print('Got flags: {2}: {0} .. {1}'.format(typ,data, # NEW: OK .. ['1 (FLAGS ())']
"Seen" if isSeen else "NEW"))
print('Get headers, message: {0} '.format(num))
typ, data = conn.fetch(num,'(BODY[HEADER])') # note, FLAGS (\\Seen) is now in data, even if not explicitly requested!
pprint.pprint(data)
print('Get RFC822 body, message: {0} '.format(num))
typ, data = conn.fetch(num,'(RFC822)')
mail = email.message_from_string(uttc(data[0][1]))
pprint.pprint(inspect.getmembers(mail)) # this is in mail_object_inspect.txt
pprint.pprint(mail.as_string())
typ, data = conn.fetch(num,'(FLAGS)')
isSeen = ( "Seen" in uttc(data[0]) )
print('Got flags: {2}: {0} .. {1}'.format(typ,data, # Seen: OK .. ['1 (FLAGS (\\Seen))']
"Seen" if isSeen else "NEW"))
conn.select() # select again, to see flags server side
# * OK [UNSEEN 0] # no more unseen messages (if there was only one msg in folder)
print('Restoring flag to unseen/new, message: {0} '.format(num))
ret, data = conn.store(num,'-FLAGS','\\Seen')
if ret == 'OK':
print("Set back to unseen; Got OK: {0}{1}{2}".format(data,'\n',30*'-'))
print(mail)
typ, data = conn.fetch(num,'(FLAGS)')
isSeen = ( "Seen" in uttc(data[0]) )
print('Got flags: {2}: {0} .. {1}'.format(typ,data, # NEW: OK .. [b'1 (FLAGS ())']
"Seen" if isSeen else "NEW"))
conn.close()
参考文献