python利用imap实现伪“无痕”取信

    所谓无痕取信,目前主要是指从邮箱中把信件收取后,邮箱内状态不发生任何改变。这里的状态主要是指两部分,一部分是邮件状态不变,即已读与未读状态不变,另一部分是指邮箱记录的登陆IP不发生改变。本文中所说的伪“无痕”取信主要是指实现第一部分。
一、准备知识
1、Imaplib

IMAP4.append(mailbox, flags, date_time, message):Append message to named mailbox.

IMAP4.authenticate(mechanism, authobject):Authenticate command — requires response processing.mechanism specifies which authentication mechanism is to be used - it should appear in the instance variable capabilities in the form AUTH=mechanism.

IMAP4.check():Checkpoint mailbox on server.

IMAP4.close():Close currently selected mailbox. Deleted messages are removed from writable mailbox. This is the recommended command before LOGOUT.

IMAP4.copy(message_set, new_mailbox):Copy message_set messages onto end of new_mailbox.

IMAP4.create(mailbox):Create new mailbox named mailbox.

IMAP4.delete(mailbox):Delete old mailbox named mailbox.

IMAP4.deleteacl(mailbox, who):Delete the ACLs (remove any rights) set for who on mailbox.

IMAP4.expunge():Permanently remove deleted items from selected mailbox. Generates an EXPUNGE response for each deleted message. Returned data contains a list of EXPUNGE message numbers in order received.

IMAP4.fetch(message_set, message_parts):Fetch (parts of) messages. message_parts should be a string of message part names enclosed within parentheses, eg: "(UID BODY[TEXT])". Returned data are tuples of message part envelope and data.

IMAP4.getacl(mailbox):Get the ACLs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.getannotation(mailbox, entry, attribute):Retrieve the specified ANNOTATIONs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.getquota(root):Get the quota root‘s resource usage and limits. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.getquotaroot(mailbox):Get the list of quota roots for the named mailbox. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.list([directory[, pattern]]):List mailbox names in directory matching pattern. directory defaults to the top-level mail folder, and pattern defaults to match anything. Returned data contains a list of LIST responses.

IMAP4.login(user, password):Identify the client using a plaintext password. The password will be quoted.

IMAP4.login_cram_md5(user, password):Force use of CRAM-MD5 authentication when identifying the client to protect the password. Will only work if the server CAPABILITY response includes the phrase AUTH=CRAM-MD5.

IMAP4.logout():Shutdown connection to server. Returns server BYE response.

IMAP4.lsub([directory[, pattern]]):List subscribed mailbox names in directory matching pattern. directory defaults to the top level directory and pattern defaults to match any mailbox. Returned data are tuples of message part envelope and data.

IMAP4.myrights(mailbox):Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).

IMAP4.namespace():Returns IMAP namespaces as defined in RFC2342.

IMAP4.noop():Send NOOP to server.

IMAP4.open(host, port):Opens socket to port at host. This method is implicitly called by the IMAP4 constructor. The connection objects established by this method will be used in the read, readline, send, and shutdown methods. You may override this method.

IMAP4.partial(message_num, message_part, start, length):Fetch truncated part of a message. Returned data is a tuple of message part envelope and data.

IMAP4.proxyauth(user):Assume authentication as user. Allows an authorised administrator to proxy into any user’s mailbox.

IMAP4.read(size):Reads size bytes from the remote server. You may override this method.

IMAP4.readline():Reads one line from the remote server. You may override this method.

IMAP4.recent():Prompt server for an update. Returned data is None if no new messages, else value of RECENT response.

IMAP4.rename(oldmailbox, newmailbox):Rename mailbox named oldmailbox to newmailbox.

IMAP4.response(code):Return data for response code if received, or None. Returns the given code, instead of the usual type.

IMAP4.search(charset, criterion[, ...]):Search mailbox for matching messages. charset may be None, in which case no CHARSET will be specified in the request to the server. The IMAP protocol requires that at least one criterion be specified; an exception will be raised when the server returns an error.

IMAP4.send(data):Sends data to the remote server. You may override this method.

IMAP4.setacl(mailbox, who, what):Set an ACL for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.setannotation(mailbox, entry, attribute[, ...]):Set ANNOTATIONs for mailbox. The method is non-standard, but is supported by the Cyrus server.

IMAP4.setquota(root, limits):Set the quota root‘s resource limits. This method is part of the IMAP4 QUOTA extension defined in rfc2087.

IMAP4.shutdown():Close connection established in open. This method is implicitly called by IMAP4.logout(). You may override this method.

IMAP4.socket():Returns socket instance used to connect to server.

IMAP4.sort(sort_criteria, charset, search_criterion[, ...]):The sort command is a variant of search with sorting semantics for the results. Returned data contains a space separated list of matching message numbers.

IMAP4.status(mailbox, names):Request named status conditions for mailbox.

IMAP4.store(message_set, command, flag_list):Alters flag dispositions for messages in mailbox. command is specified by section 6.4.6 of RFC 2060 as being one of “FLAGS”, “+FLAGS”, or “-FLAGS”, optionally with a suffix of ”.SILENT”.

IMAP4.thread(threading_algorithm, charset, search_criterion[, ...]):The thread command is a variant of search with threading semantics for the results. Returned data contains a space separated list of thread members.

IMAP4.uid(command, arg[, ...]):Execute command args with messages identified by UID, rather than message number. Returns response appropriate to command. At least one argument must be supplied; if none are provided, the server will return an error and an exception will be raised.

IMAP4.unsubscribe(mailbox):Unsubscribe from old mailbox.

IMAP4.xatom(name[, arg[, ...]]):Allow simple extension commands notified by server in CAPABILITY response.

IMAP4_SSL.ssl():Returns SSLObject instance used for the secure connection with the server.

IMAP4.PROTOCOL_VERSION:The most recent supported protocol in the CAPABILITY response from the server.

IMAP4.debug:Integer value to control debugging output. The initialize value is taken from the module variable Debug. Values greater than three trace each command.

2、imap 命令详解

CREATE <folder>:CREATE可以创建指定名字的新邮箱。邮箱名称通常是带路径的文件夹全名。(有些IMAP客户机使用邮件夹称呼新邮箱)
C: A003 CREATE owatagusiam/                 /*创建一个新目录owatagusiam*/
S: A003 OK CREATE completed
C: A004 CREATE owatagusiam/blurdybloop 
/* 在创建的目录owatagusiam下创建一个名为blurdybloop 的邮箱,当然可以省略第一步,直接A004 CREATE owatagusiam/blurdybloop ,表示在新的目录owatagusiam 下创建了一个名为blurdybloop 的邮箱*/        
S: A004 OK CREATE completed

DELETE <folder>:DELETE命令删除指定名字的文件夹。文件夹名字通常是带路径的文件夹全名,当邮箱被删除后,其中的邮件也不复存在。
C: A683 DELETE blurdybloop
S: A683 OK DELETE completed
C: A684 DELETE foo
S: A684 NO Name "foo" has inferior hierarchical names
C: A685 DELETE foo/bar
S: A685 OK DELETE Completed

RENAME <old folder><new folder>:RENAME命令可以修改文件夹的名称,它使用两个参数:当前邮箱名和新邮箱名,两个参数的命名符合标准路径命名规则。
C: A683 RENAME blurdybloop sarasoop
S: A683 OK RENAME completed
C: A684 RENAME stuff/junk newbox         /*把stuff目录(文件夹)下的邮箱junk改名为newbox*/
S: A684 OK RENAME Completed

LIST <BASE><template>:LIST命令用于列出邮箱中已有的文件夹,有点像操作系统的列目录命令,有两个参数,邮箱路径参数BASE:表示用户登陆目录;第二个参数template:表示希望显示的邮箱名。这个命令可以包含起始的路径位置和需要列出的文件夹所符合的特征,可以使用通配符"*"。
C: A101 LIST "" ""
S: * LIST (\Noselect) "/" ""
S: A101 OK LIST Completed
C: A102 LIST #news.comp.mail.misc ""
S: * LIST (\Noselect) "." #news.
S: A102 OK LIST Completed
C: A103 LIST /usr/staff/jones ""
S: * LIST (\Noselect) "/" /
S: A103 OK LIST Completed
C: A202 LIST ~/Mail/ %
S: * LIST (\Noselect) "/" ~/Mail/foo
S: * LIST () "/" ~/Mail/meetings
S: A202 OK LIST completed

APPEND <folder><attributes><date/time><size><mail data>:APPEND命令允许Client上载一个邮件到指定的Folder(文件夹/邮箱)中。命令中包含了新邮件的属性、日期/时间、大小,随后是邮件数据。
C: A003 APPEND saved-messages (\Seen) {310}
C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
C: From: Fred Foobar <[email protected]>
C: Subject: afternoon meeting
C: To: [email protected]
C: Message-Id: <[email protected]>
C: MIME-Version: 1.0
C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
C:
C: Hello Joe, do you think we can meet at 3:30 tomorrow?
C:
S: A003 OK APPEND completed

SELECT <folder>:SELECT命令让Client选定某个邮箱(Folder),表示即将对该邮箱(Folder)内的邮件作操作。邮箱标志的当前状态也返回给了用户,同时返回的还有一些关于邮件和邮箱的附加信息。
C: A142 SELECT INBOX
S: * 172 EXISTS
S: * 1 RECENT
S: * OK [UNSEEN 12] Message 12 is first unseen
S: * OK [UIDVALIDITY 3857529045] UIDs valid
S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
S: A142 OK [READ-WRITE] SELECT completed

FETCH <mail id><datanames>:FETCH 命令用于读取邮件的文本信息,且仅用于显示的目的。包含两个参数,messageset:表示希望读取的邮件号列表,IAMP服务器邮箱中的每个邮件都有 一个唯一的ID标识,(邮件号列表参数可以是一个邮件号,也可以是由逗号分隔的多个邮件号,或者由冒号间隔的一个范围),IMAP服务器返回邮件号列表中 全部邮件的指定数据项内容。
数据名参数确定能够被独立返回的邮件的一部分,下面我们看看各参数返回的邮件信息:
ALL:只返回按照一定格式的邮件摘要,包括邮件标志、RFC822.SIZE、自身的时间和信封信息。IMAP客户机能够将标准邮件解析成这些信息并显示出来。
BODY:只返回邮件体文本格式和大小的摘要信息。IMAP客户机可以识别这些细腻,并向用户显示详细的关于邮件的信息。其实是一些非扩展的BODYSTRUCTURE的信息。
FAST:只返回邮件的一些摘要,包括邮件标志、RFC822.SIZE、和自身的时间。
FULL:同样的还是一些摘要信息,包括邮件标志、RFC822.SIZE、自身的时间和BODYSTRUCTURE的信息。
BODYSTRUCTUR: 是邮件的[MIME-IMB]的体结构。这是服务器通过解析[RFC-2822]头中的[MIME-IMB]各字段和[MIME-IMB]头信息得出来 的。包括的内容有:邮件正文的类型、字符集、编码方式等和各附件的类型、字符集、编码方式、文件名称等等。
ENVELOPE:信息的信封结构。是服务器通过解析[RFC-2822]头中的[MIME-IMB]各字段得出来的,默认各字段都是需要的。主要包括:自身的时间、附件数、收件人、发件人等。
FLAGS:此邮件的标志。
INTERNALDATE:自身的时间。
RFC822.SIZE:邮件的[RFC-2822]大小
RFC822.HEADER:在功能上等同于BODY.PEEK[HEADER],
RFC822:功能上等同于BODY[]。
RFC822.TEXT:功能上等同于BODY[TEXT]
UID:返回邮件的UID号,UID号是唯一标识邮件的一个号码。
BODY[section] <<partial>>:返回邮件的中的某一指定部分,返回的部分用section来表示,section部分包含的信息通常是 代表某一部分的一个数字或者是下面的某一个部分:HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, and TEXT。如果section部分是空的话,那就代表返回全部的信息,包括头信息。
BODY[HEADER]返回完整的文件头信息。
BODY[HEADER.FIELDS ()]:在小括号里面可以指定返回的特定字段。
BODY[HEADER.FIELDS.NOT ()]:在小括号里面可以指定不需要返回的特定字段。
BODY[MIME]:返回邮件的[MIME-IMB]的头信息,在正常情况下跟BODY[HEADER]没有区别。
BODY[TEXT]:返回整个邮件体,这里的邮件体并不包括邮件头。

STORE <mail id><new attributes>:STORE 命令用于修改指定邮件的属性,包括给邮件打上已读标记、删除标记,等等。STORE命令当前只有两个数据项类型可用,FLAGS:表示邮件的一组标志; FLAGS.SLIENT,表示一组邮件的标志,通过在两种数据项前加上加号或者减号可以进一步改变它们的执行情况,加号表示数据项的值添加到邮件中,减 号表示将数据项的值从邮件中删除。
C: A003 STORE 2:4 +FLAGS (\Deleted)      /*冒号表示间隔的一个范围:给从2到4的邮件设置Deleted属性*/
S: * 2 FETCH FLAGS (\Deleted \Seen)
S: * 3 FETCH FLAGS (\Deleted)
S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
S: A003 OK STORE completed
同时改命令还可以用于把邮件标记为未读等等。在web上可以对邮件进行的操作在imap中都可以实现。

CLOSE:CLOSE命令表示Client结束对当前Folder(文件夹/邮箱)的访问,关闭邮箱该邮箱中所有标志为、DELETED的邮件就被从物理上删除。CLOSE没有命令参数。随后可以SELECT另一Folder。
C: A341 CLOSE
S: A341 OK CLOSE completed


EXPUNGE:EXPUNGE命令在不关闭邮箱的情况下删除所有的标志为、DELETED的邮件。EXPUNGE删除的邮件将不可以恢复。
C: A202 EXPUNGE
S: * 3 EXPUNGE
S: * 3 EXPUNGE
S: * 5 EXPUNGE
S: * 8 EXPUNGE
S: A202 OK EXPUNGE completed

LOGOUT:LOGOUT命令结束本次IMAP会话。
C: A023 LOGOUT
S: * BYE IMAP4rev1 Server logging out
S: A023 OK LOGOUT completed
(Server and client then close the connection)

EXAMINE <mailbox>:
EXAMINE命令以只读方式打开邮箱,参数是需要打开的邮箱的名字,使用EXAMINE命令打开的邮箱不允许对邮件进行改动,因此不能增加或删除邮件的标志。

SUBSCRIBE <mailbox>:SUBSCRIBE命令用来在客户机的活动邮箱列表中增加一个邮箱,该命令只有一个参数,希望添加的邮箱名。
C: A114 SUBSCRIBE new/anotherbox
S: A114 OK SUBSCRIBE completed

UNSUBSCRIBE <mailbox>:UNSUBSCRIBE命令用来从活动列表中去掉一个邮箱,一个参数:希望去掉的邮箱名。
C: A115 UNSUBSCRIBE new/anotherbox
S: A115 OK SUBSCRIBE completed

LSUB <folder><mailbox>:LSUB命令修正了LIST命令,LIST返回用户$HOME目录下所有的文件,但LSUB命令只显示那些使用SUBSCRIBE命令设置为活动邮箱的文件。两个参数:邮箱路径和邮箱名。
C: A116 LSUB “” *
S:* LSUB () “/” stuff/junk
S:* LSUB () “/” neebox
S:* LSUB () “/” new/anotherbox
S: A116 OK LSUB completed

STATUS <mailbox>(<parameter1>  < parameter2>  ……<parameter5>):STATUS命令查询邮箱的当前状态。第一个参数是需要查询的邮箱名,第二个参数是客户机需要查询的项目列表(要查询显示的信息),当在圆括号中。STATUS可以在不使用SELECT命令(打开邮箱)或者EXAMINE(以只读方式打开邮箱)前提下获取邮箱的信息。
STATUS命令可以获得的数据项
项 目                 说  明
MESSAGE         邮箱中的邮件总数
RECENT         邮箱中标志为\RECENT的邮件数
UIDNEXT         可以分配给新邮件的下一个UID
UIDVALIDITY         邮箱的UID有效性标志
UNSEEN         邮箱中没有被标志为\UNSEEN的邮件数
C: A117 STATUS inbox  (message recent unseen)
S:* STATUS inbox (MESSAGE 1 RECENT 0 UNSENN 0)
S: A117 OK STATUS completed
C: A118 STATUS newbox  (message recent unseen)
S:* STATUS inbox (MESSAGE 1 RECENT 0 UNSENN 2)
S: A118 OK STATUS completed

CHECK :CHECK命令用来在邮箱设置一个检查点。没有参数。就是IMAP中的sync命令。任何未完成的操作,例如从服务器内存向硬盘写数据,都将会被做完以保持邮箱的一致性状态。该命令确保乃村中的磁盘缓冲数据都被写到了磁盘上。

SEARCH [CHARSET specification] (search criteria):命 令可以根据搜索条件在处于活动状态的邮箱中搜索邮件,然后显示匹配的邮件编号。字符集标志参数[CHARSET specification]由CHARSET和注册的字符集标志符组成,缺省的标志符是US-ASCⅡ,所以该参数长省略。search criteria:查询条件参数,明确查询的关键字和值。查询关键字有几十种。
C: A119 SEARCH header subject another
S: SEARCH 1 2
S:* A119 OK SEARCH completed
C: A120 SEARCH header subject another
S: *SEARCH 2
S: A120 OK SEARCH completed
C: A121 SEARCH UNSEEN
S: *SEARCH 1 2
S: A120 OK SEARCH completed
以上每个例子都在邮件头的Subject:字段中查询一个不同的单词。服务器返回条件的邮件号列表,如果没有匹配邮件则返回不带UID的SEARCH单词。

COPY <mail id><mailboxname>:COPY命令可以把邮件从一个邮箱复制到另一个邮箱,两个参数:mail id是希望从活动邮箱中复制的邮件的标号,mailboxname是希望邮件被复制到的邮箱。
IAMP没有定义移动邮件的命令,移动操作相当于先把邮件复制到新邮箱中,然后对源邮箱中的邮件设置\DELETED标志。下一次执行检查点过后,新邮箱中的邮件被删除,新邮件就被显示出来。

UID:UID 命令和FETCH、COPY、STORE命令或者SEARCH命令一起使用,它允许这些命令使用邮件的UID号而不是在邮箱中的顺序号。UID号是唯一标 识邮件系统中邮件的32位证书。通常这些命令都使用顺序号来标识邮箱中的邮件,使用UID可以使IMAP客户机记住不同IMAP会话中的邮件。

CAPABILITY:CAPABILITY命令请求返回IMAP服务器支持的功能列表,服务器收到客户机发送的CAPABILITY命令后将返回该服务器所支持的功能。无参数。
C: A122 CAPABILITY
S:*A122 CAPABILITY IMAP4 IMAP4REVl NAMESPACE IDLE SCAN SORT   MAILBOX
--REFERRALS [ic:ccc] LOGIN-REFERRALS AUTH=LOGIN THREAD=
ORDERDSUBJECT
S: A122 OK CAPABILITY completed

NOOP:NOOP命令什么也不做,用来向服务器发送自动命令,防止因长时间处于不活动状态而导致连接中断,服务器对该命令的响应始终为肯定。无参数。

LOGOUT:LOGOUT命令使当前登陆用户退出登陆并关闭所有打开的邮箱,任何做了\DELETED标志的邮件都将在这个时候被删除。

二、以yahoo邮箱为例进行伪无痕收信
    要做到邮件状态不发生变化,实现上关键在fetch的参数上。如果我们仅是读取邮件头BODY.PEEK[HEADER],那么邮件的状态是不发生变化的。如果我们要读取全部邮件内容,那么必须使用BODY.PEEK[],这样才能保证不发生变化。如下面的例子:

#-*- coding:UTF-8 -*-
#@小五义 http://www.cnblogs.com/xiaowuyi

import imaplib, string, email
import os
M = imaplib.IMAP4_SSL("imap.mail.yahoo.com","993")
t=0
try:
    try:
        M.login('[email protected]','YYYY')####YYYY为密码
    except Exception,e:
        #print "wrong!"
        print 'login error: %s' % e
        M.close()
    
    M.select('INBOX',False)
    
   # result, message = M.select()
    typ, data = M.search(None, 'ALL')
    for num in string.split(data[0]):
        try:
            typ, data = M.fetch(num, '(UID BODY.PEEK[])')
            msg = email.message_from_string(data[0][1])
            t=t+1
            filename=str(t)+".eml"
            f=open(filename,'wb')
            f.write(str(msg))
            f.close
        except Exception,e:
            print 'got msg error: %s' % e            
    print "OK!"
    M.logout()
except Exception, e:
    print 'imap error: %s' % e
    M.close()

前面说的有些抽象,typ, data = M.fetch(num, '(UID BODY.PEEK[])')在这一句中,可以偿试着将UID BODY.PEEK[HEADER]、UID BODY[]、RFC822、RFC822.HEADER等进行收信比较,将会更加直观。这里就不再一一解释。

你可能感兴趣的:(python)