将消息从一个IMAP服务器迁移到另一个服务器的脚本
我们办公室使用两个IMAP邮件服务器,一个是接收邮件的服务器,里面存放着最近的邮件,另一个是存档服务器。我们主要使用Outlook 2010,目前的做法是定期把发出的邮件从接收服务器拖到存档服务器。
今天有人让我考虑写一个脚本,定期(可能用crontab)抓取所有发出的邮件并把它们移动到存档服务器。
我查了一些关于SSL或telnet的例子,想要访问服务器并进行一些操作。不过,我不知道最好的方法是什么,或者在IMAP环境下如何跨服务器移动文件。
有什么好的办法可以做到这一点吗?我比较喜欢用Python,因为我对它比较熟悉,但如果其他语言已经有现成的解决方案,我也可以接受。
更新:
好吧,这里有一些代码。目前这段代码可以正常复制邮件,但会在存档服务器上重复已有的邮件。
import imaplib
import sys
#copy from
f_server = 'some.secret.ip.address'
f_username = 'j@example.com'
f_password = 'password'
f_box_name = 'Sent Messages'
#copy to
t_server = 'archive.server.i.p'
t_username = 'username'
t_password = 'password'
t_box_name = 'test'
To = imaplib.IMAP4(t_server)
To.login(t_username, t_password)
print 'Logged into mail server'
From = imaplib.IMAP4(f_server)
From.login(f_username, f_password)
print 'Logged into archive'
From.select(f_box_name) #open box which will have its contents copied
print 'Fetching messages...'
typ, data = From.search(None, 'ALL') #get all messages in the box
msgs = data[0].split()
sys.stdout.write(" ".join(['Copying', str(len(msgs)), 'messages']))
for num in msgs: #iterate over each messages id number
typ, data = From.fetch(num, '(RFC822)')
sys.stdout.write('.')
To.append(t_box_name, None, None, data[0][1]) #add a copy of the message to the archive box specified above
sys.stdout.write('\n')
try:
From.close()
From.logout()
try:
To.close()
To.logout()
一些资料来源:
Doug Hellman's Blog: imaplib - IMAP4客户端库
Tyler Lesmann's Blog: 用Python和imaplib复制IMAP邮箱
我还需要做的事情:
- 在实时服务器上删除/清除邮件
- 不复制重复的邮件(其实可以通过复制后删除原件来解决,但...)
- 处理错误
更新 2:
有没有人有什么想法,如何在复制时不产生重复邮件?(暂时不考虑删除原件的选项)我考虑过搜索文本,但意识到嵌套回复可能会影响这个方法。
3 个回答
可能对提问者来说已经太晚了,但希望对之后跟进的人有帮助。
这看起来是一个比较通用的需求。你可能不需要自己写代码。
你可以考虑使用一个邮件传输代理(MTA),它可以把所有邮件的副本发送到一个归档,同时也把邮件发送到你的IMAP服务器。如果你觉得自己设置这个很麻烦,可以考虑使用一个第三方服务,他们会帮你管理归档,并把邮件转发到你现有的邮件服务器。
如果你真的想通过IMAP来复制邮件,我建议你看看 offlineimap。
如果你真的想自己动手,跟踪你已经看过的邮件的方法是使用邮件的Message-ID头部。
我不太清楚你处理的消息量有多大,但你可以从每条消息中提取出 Message-ID
,然后用这个来判断它是否是重复的。每次准备移动消息时,可以生成一个目标服务器上已有的ID列表,或者在消息归档时把它们添加到一个简单的数据库里。
如果查找的成本太高,你可以通过添加一个额外的消息属性,比如 Date
,来缩小范围,然后在不再需要旧列表时把它们删除。
这是我最后使用的代码。我并不认为它是完美的,因为它用的是msg_num而不是id,这样有点风险。不过这个操作的频率比较低,大概每小时只有几次(通过定时任务执行)。
import imaplib
#copy from
from_server = {'server': '1.1.1.1',
'username': 'j@example.com',
'password': 'pass',
'box_names': ['Sent', 'Sent Messages']}
#copy to
to_server = {'server': '2.2.2.2',
'username': 'archive',
'password': 'password',
'box_name': 'Sent'}
def connect_server(server):
conn = imaplib.IMAP4(server['server'])
conn.login(server['username'], server['password'])
print 'Logged into mail server @ %s' % server['server']
return conn
def disconnect_server(server_conn):
out = server_conn.logout()
if __name__ == '__main__':
From = connect_server(from_server)
To = connect_server(to_server)
for box in from_server['box_names']:
box_select = From.select(box, readonly = False) #open box which will have its contents copied
print 'Fetching messages from \'%s\'...' % box
resp, items = From.search(None, 'ALL') #get all messages in the box
msg_nums = items[0].split()
print '%s messages to archive' % len(msg_nums)
for msg_num in msg_nums:
resp, data = From.fetch(msg_num, "(FLAGS INTERNALDATE BODY.PEEK[])") # get email
message = data[0][1]
flags = imaplib.ParseFlags(data[0][0]) # get flags
flag_str = " ".join(flags)
date = imaplib.Time2Internaldate(imaplib.Internaldate2tuple(data[0][0])) #get date
copy_result = To.append(to_server['box_name'], flag_str, date, message) # copy to archive
if copy_result[0] == 'OK':
del_msg = From.store(msg_num, '+FLAGS', '\\Deleted') # mark for deletion
ex = From.expunge() # delete marked
print 'expunge status: %s' % ex[0]
if not ex[1][0]: # result can be ['OK', [None]] if no messages need to be deleted
print 'expunge count: 0'
else:
print 'expunge count: %s' % len(ex[1])
disconnect_server(From)
disconnect_server(To)