請注意!!此信件無法直接回覆。
Please do not reply to this email address.
星期六, 7月 25, 2009
星期日, 7月 19, 2009
IMAP in Python - 8 (Reading Flags)
Twisted提供簡單的函式可以直接讀取message flag:
使用fetcgFlags(range)就可以取得範圍內message的flag。當然Twisted也提供了setFlags()、addFlags()、removeFlags(),都只需要提供一個list of strings,每個string都是一個flag即可。OUTPUT:
程式連結
class IMAPLogic:
def __init__(self, proto):
self.proto = proto
self.factory = proto.factory
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(lambda x: self.proto.examine('INBOX'))
d.addCallback(lambda x: self.proto.fetchFlags('1:*'))
d.addCallback(self.handleflags)
d.addCallback(self.logout)
d.addCallback(self.stopreactor)
d.addErrback(self.errorhappened)
def handleflags(self, flags):
for num, flaglist in flags.items():
print "Message %s has flags %s" % (num, ", ".join(flaglist['FLAGS']))
使用fetcgFlags(range)就可以取得範圍內message的flag。當然Twisted也提供了setFlags()、addFlags()、removeFlags(),都只需要提供一個list of strings,每個string都是一個flag即可。OUTPUT:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ tflag.py mx.mgt.ncu.edu.tw cacaegg
Enter password:
Message 1 has flags \Recent
Message 2 has flags \Recent
程式連結
星期六, 7月 18, 2009
IMAP in Python - 7 (Downloading Message Individually)
由於一次全部下載完,信件一多就浪費許多記憶體,因此來分封下載。可以先發出request來查看message number,來看程式碼:
可以看到選定folder以後,我們呼叫fetchUID來抓取folder所有的UID,但是回傳的結構其實是如此的dictionary[uid -> Dictionary[]]。也就是每封信本身也都是一個dictionary,來看看如何處理:
對於每封信的dictionary,我們取用其中的'UID'部份,而fetchSpectific的uid = 1是告訴此函式把第一個參數當UID看而非message number,最後加到deferred list中。deferred list會等到所有deferred結束後,再把結果一次傳給第一個Callback。最後來看gotmessage():
與之前不同的是,這次找message body text並不是寫死的固定位置,因為可能會因server不同而回傳資料有所不同,不過相同的是text都是在'body'後面的第二個位置,所以就直接用算的算出來。最後來看程式的output:
程式連結
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(lambda x: self.proto.examine('INBOX'))
d.addCallback(lambda x: self.proto.fetchUID('1:*'))
d.addCallback(self.handleuids)
可以看到選定folder以後,我們呼叫fetchUID來抓取folder所有的UID,但是回傳的結構其實是如此的dictionary[uid -> Dictionary[]]。也就是每封信本身也都是一個dictionary,來看看如何處理:
def handleuids(self, uids):
dlist = []
destfd = open(sys.argv[3], "at")
for data in uids.values():
uid = data['UID']
d = self.proto.fetchSpecific(uid, uid =1, peek = 1)
d.addCallback(self.gotmessage, destfd, uid)
dlist.append(d)
dl = defer.DeferredList(dlist)
dl.addCallback(lambda x, fd: fd.close(), destfd)
return dl
對於每封信的dictionary,我們取用其中的'UID'部份,而fetchSpectific的uid = 1是告訴此函式把第一個參數當UID看而非message number,最後加到deferred list中。deferred list會等到所有deferred結束後,再把結果一次傳給第一個Callback。最後來看gotmessage():
def gotmessage(self, data, destfd, uid):
print "Received message UID", uid
for key, value in data.items():
print "Writing message", key
i = value[0].index('BODY') + 2
msg = email.message_from_string(value[0][i])
destfd.write(msg.as_string(unixfrom = 1))
destfd.write("\n")
與之前不同的是,這次找message body text並不是寫死的固定位置,因為可能會因server不同而回傳資料有所不同,不過相同的是text都是在'body'後面的第二個位置,所以就直接用算的算出來。最後來看程式的output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ tdownload.py mx.mgt.ncu.edu.tw cacaegg mailbox
Enter password:
Received message UID 15024
Writing message 1
Received message UID 15025
Writing message 2
程式連結
IMAP in Python - 6 (Downloading an Entire Mailbox)
開始來下載信件囉!最簡單的方式,就是一次全部下載,只是此方式,twisted需要把信件cache在記憶體中,因此對於大信箱來說,此種方式不太可行。
來看看怎麼使用吧:
可以看到使用protocol class的fetchSpecific()就可以抓取信件。而第一個參數'1:*'則表示抓取的範圍是從第1封到最後一封,也就是所有的信件;第二個參數把peek設定為1,是要server不要在我們把信抓下來後就加上/seen已讀的flag(預設是會加的)。
再來就是看看gotmessages如何處理信件:
fetchSpecific傳給data,data會是dictionary。key是message number,而value就是components of the list。我們把value中[0][2]是代表entire message直接寫入目標檔案中,就算完成囉!
來看output:
程式連結
來看看怎麼使用吧:
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(lambda x: self.proto.examine('INBOX'))
d.addCallback(lambda x: self.proto.fetchSpecific('1:*', peek = 1))
d.addCallback(self.gotmessages)
d.addCallback(self.logout)
d.addCallback(self.stopreactor)
可以看到使用protocol class的fetchSpecific()就可以抓取信件。而第一個參數'1:*'則表示抓取的範圍是從第1封到最後一封,也就是所有的信件;第二個參數把peek設定為1,是要server不要在我們把信抓下來後就加上/seen已讀的flag(預設是會加的)。
再來就是看看gotmessages如何處理信件:
def gotmessages(self, data):
destfd = open(sys.argv[3], 'at')
for key, value in data.items():
print "Writing message", key
msg = email.message_from_string(value[0][2])
destfd.write(msg.as_string(unixfrom = 1))
destfd.write("\n")
destfd.close()
fetchSpecific傳給data,data會是dictionary。key是message number,而value就是components of the list。我們把value中[0][2]是代表entire message直接寫入目標檔案中,就算完成囉!
來看output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ tdlbig.py mx.mgt.ncu.edu.tw cacaegg mailbox
Enter password:
Writing message 1
Writing message 2
Writing message 3
程式連結
IMAP in Python - 5 (Summary Information)
在對folder進行select或examine以後,server會回傳summary information。
來看看如何使用:
在此例中,檢查了INBOX後,把結果傳給了examineresult,examineresult會把結果顯示出來。
程式跑出來的結果如下:
來解釋一下常用的item
EXISTS:folder中的message數目
FLAGS:可以放在此folder上的flag
RECENT:上次select之後,大約有多少新進的message
UIDVALIDITY:將此id與上次session比較,就可以用做uid驗證,確保uid相同的一樣的message
程式連結
來看看如何使用:
class IMAPLogic:
def __init__(self, proto):
self.proto = proto
self.factory = proto.factory
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(lambda x:self.proto.examine('INBOX'))
d.addCallback(self.examineresult)
d.addCallback(self.logout)
d.addCallback(self.stopreactor)
d.addErrback(self.errorhappened)
def examineresult(self, data):
for key, value in data.items():
if isinstance(value, tuple):
print "%s: %s" % (key, ",".join(value))
else:
print "%s: %s" % (key, value)
在此例中,檢查了INBOX後,把結果傳給了examineresult,examineresult會把結果顯示出來。
程式跑出來的結果如下:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ texamine.py mx.mgt.ncu.edu.tw cacaegg
Enter password:
EXISTS: 0
PERMANENTFLAGS:
READ-WRITE: 0
UIDNEXT: 15020
FLAGS: \Answered,\Flagged,\Deleted,\Seen,\Draft,Junk,NonJunk,$MDNSent,$Forwarded,NotJunk
UIDVALIDITY: 1153809236
RECENT: 0
來解釋一下常用的item
EXISTS:folder中的message數目
FLAGS:可以放在此folder上的flag
RECENT:上次select之後,大約有多少新進的message
UIDVALIDITY:將此id與上次session比較,就可以用做uid驗證,確保uid相同的一樣的message
程式連結
星期五, 7月 17, 2009
IMAP in Python - 4 (Scaning the Folder List)
IMAP支援多個mailbox,也就是多個folder。可以用protocol class中的list(ref, pattern)來取得。來看一下例子:
可以看到呼叫了proto.list()並且傳了兩個參數,第一個是reference,有某些特殊用途,例如要取得Usenet等,大多時候是留白的;第二個參數是folder pattern,也就是要找的folder名字是什麼。在此支援wild card,所以*就表示了所有的folder都要。
來看一下程式輸出:
可以看到list()回傳的是"a list of tuples"。而每組tuple都有3個item,分別是folder name、Delimiter以及Flag。在此簡介一下常看到的flag:
\Noinferiors:沒有subfolder也不可能會有subfolder。
\Noselect:不能進行select or examine,也就是folder內沒有訊息。
\unmarked:沒有新訊息(相對的\marked就可能是有,不過要看server怎麼用)。
程式連結
class IMAPLogic:
def __init__(self, proto):
self.proto = proto
self.factory = proto.factory
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(lambda x: self.proto.list('', '*'))
d.addCallback(self.listresult)
d.addCallback(self.logout)
d.addCallback(self.stopreactor)
d.addErrback(self.errorhappened)
def listresult(self, data):
print "%-35s %-5s %-37s" % ('Mailbox Name', 'Delim', 'Mailbox Flags')
print '-' * 35, '-' * 5, '-' * 37
for box in data:
flags, delim, name = box
print "%-35s %-5s %-37s" % (name, delim, ','.join(flags))
可以看到呼叫了proto.list()並且傳了兩個參數,第一個是reference,有某些特殊用途,例如要取得Usenet等,大多時候是留白的;第二個參數是folder pattern,也就是要找的folder名字是什麼。在此支援wild card,所以*就表示了所有的folder都要。
來看一下程式輸出:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ tlist.py mx.mgt.ncu.edu.tw cacaegg
Enter password:
Mailbox Name Delim Mailbox Flags
----------------------------------- ----- -------------------------------------
virus-mail / \NoInferiors,\UnMarked
spam-mail / \NoInferiors,\UnMarked
mail-trash / \NoInferiors,\UnMarked
Sent Items / \NoInferiors,\UnMarked
Deleted Items / \NoInferiors,\UnMarked
Junk E-mail / \NoInferiors,\UnMarked
Drafts~ / \NoInferiors,\UnMarked
sent-mail-feb-2008 / \NoInferiors,\UnMarked
INBOX / \NoInferiors,\UnMarked
可以看到list()回傳的是"a list of tuples"。而每組tuple都有3個item,分別是folder name、Delimiter以及Flag。在此簡介一下常看到的flag:
\Noinferiors:沒有subfolder也不可能會有subfolder。
\Noselect:不能進行select or examine,也就是folder內沒有訊息。
\unmarked:沒有新訊息(相對的\marked就可能是有,不過要看server怎麼用)。
程式連結
IMAP in Python - 3 (Error Handling)
前面的例子無法處理錯誤出現的情形,如果出現了錯誤,程式就會因為沒有呼叫到reactor.stop()造成程式卡住,再來看看twisted中如何處理錯誤呢?由於錯誤發生的時候,控制權可能已經不是當下那個function的了,所以一樣使用callback,而處理錯誤的callback稱之為errback。
直接來看例子:
只要對defered object呼叫addErrback,此errback function就會負責處理當時以上的function,也就是說loggedin()發生的錯誤由loginerror()處理,而logout()與stopreactor()的錯誤也由errorhappen()來處理。其實,如果在login()發生了在loginerror()無法處理的錯誤時,twisted會直接把錯誤丟給下一個errback,此例是loginerror()無法處理的錯誤就傳給errorhappen()>
那就再來看一下那兩個errback function:
在這邊處理的是只有登入超過三次失敗的情形,如果超過三次等情形,就會直接return failure把錯誤傳接下去給errorhappen()。在此function的結尾有增加defered object呼叫自己,如果沒成功就繼續回來此function。
此function是單純把failure的訊息印出來,然後logout(),並且無論logout成功失敗都要呼叫stopreactor()來把reactor.run()中止,避免程式掛著。
以下是程式的兩種output:
程式連結
直接來看例子:
class IMAPLogic:
def __init__(self, proto):
self.proto = proto
self.factory = proto.factory
self.logintries = 1
d = self.login()
d.addCallback(self.loggedin)
d.addErrback(self.loginerror)
d.addCallback(self.logout)
d.addCallback(self.stopreactor)
d.addErrback(self.errorhappen)
print "IMAPLogic.__init__returning."
只要對defered object呼叫addErrback,此errback function就會負責處理當時以上的function,也就是說loggedin()發生的錯誤由loginerror()處理,而logout()與stopreactor()的錯誤也由errorhappen()來處理。其實,如果在login()發生了在loginerror()無法處理的錯誤時,twisted會直接把錯誤丟給下一個errback,此例是loginerror()無法處理的錯誤就傳給errorhappen()>
那就再來看一下那兩個errback function:
def loginerror(self, failure):
print "Your loging failed (attemp %d times)." % self.logintries
if self.logintries >= 3:
print "You have tried to log in three times; I'm giving up."
return failure
self.logintries += 1
sys.stdout.write("username:")
self.factory.username = sys.stdin.readline().strip()
self.factory.password = getpass.getpass("password:")
d = self.login()
d.addErrback(self.loginerror)
return d
在這邊處理的是只有登入超過三次失敗的情形,如果超過三次等情形,就會直接return failure把錯誤傳接下去給errorhappen()。在此function的結尾有增加defered object呼叫自己,如果沒成功就繼續回來此function。
def errorhappen(self, failure):
print "An error occurred:", failure.getErrorMessage()
print "Because of the error, I am logging out and stopping reactor..."
d = self.logout()
d.addBoth(self.stopreactor)
return failure
此function是單純把failure的訊息印出來,然後logout(),並且無論logout成功失敗都要呼叫stopreactor()來把reactor.run()中止,避免程式掛著。
以下是程式的兩種output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ t-error.py mx.mgt.ncu.edu.tw cacaegg
Enter password for cacaegg on mx.mgt.ncu.edu.tw:
I have successfully connected to the server!
Logging in...
IMAPLogic.__init__returning.
connectionMade returning
I'm logged in!
Logging out.
Stopping reactor.
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ t-error.py mx.mgt.ncu.edu.tw cacaegg
Enter password for cacaegg on mx.mgt.ncu.edu.tw:
I have successfully connected to the server!
Logging in...
IMAPLogic.__init__returning.
connectionMade returning
Your loging failed (attemp 1 times).
username:ewfwfewf
password:
Logging in...
Your loging failed (attemp 2 times).
username:efwfew
password:
Logging in...
Your loging failed (attemp 3 times).
You have tried to log in three times; I'm giving up.
An error occurred: Authentication failed.
Because of the error, I am logging out and stopping reactor...
Logging out.
Unhandled error in Deferred:
Traceback (most recent call last):
Failure: twisted.mail.imap4.IMAP4Exception: Authentication failed.
Stopping reactor.
程式連結
IMAP in Python - 2 (Logging In)
再來看如何使用twisted來進行登入,以及defered object的chain callback。
此次分成3個主要的class:Protocol、Factory、Logic。分別來看一下。
Protocol Class:
主要就是把邏輯部份從原本的protocol中分離出來了,此protocol class現在變成主要就是處理關於協定的callback。
此class會在__init__時就把帳號密碼存入,以待登入時使用。
此邏輯class在__init__時,就會先把protocol class & factory變成member。然後呼叫protocol的login(),在此會回傳第1個defered object,對此object設定兩個callback。這樣就成為"Chain callback",對於每個子callback,如果還有其他defered物件,twisted會將其都處理完才會呼叫原parent defered object的下一個子callback,且把最後一次callback的結果當作參數送給下一個。因此在此例子中,執行的順序是loggedin() --> logout() --> proto.logout() --> stopreactor() --> reactor.stop(),如此執行。
以下為程式output:
程式連結
此次分成3個主要的class:Protocol、Factory、Logic。分別來看一下。
Protocol Class:
class IMAPClient(IMAP4Client):
def connectionMade(self):
print "I have successfully connected to the server!"
IMAPLogic(self)
print "connectionMake returning"
主要就是把邏輯部份從原本的protocol中分離出來了,此protocol class現在變成主要就是處理關於協定的callback。
class IMAPFactory(protocol.ClientFactory):
protocol = IMAPClient
def __init__(self, username, password):
self.username = username
self.password = password
def clientConnectionFailed(self, connector, reason):
print "Client connection failed:", reason
reactor.stop()
此class會在__init__時就把帳號密碼存入,以待登入時使用。
class IMAPLogic:
def __init__(self, proto):
self.proto = proto
self.factory = proto.factory
d = self.proto.login(self.factory.username, self.factory.password)
d.addCallback(self.loggedin)
d.addCallback(self.stopreactor)
print "IMAPLogic.__init__returning."
def loggedin(self, data):
print "I'm logged in!"
return self.logout()
def logout(self):
print "Logging out."
d = self.proto.logout()
return d
def stopreactor(self, data = None):
print "Stopping reactor."
reactor.stop()
此邏輯class在__init__時,就會先把protocol class & factory變成member。然後呼叫protocol的login(),在此會回傳第1個defered object,對此object設定兩個callback。這樣就成為"Chain callback",對於每個子callback,如果還有其他defered物件,twisted會將其都處理完才會呼叫原parent defered object的下一個子callback,且把最後一次callback的結果當作參數送給下一個。因此在此例子中,執行的順序是loggedin() --> logout() --> proto.logout() --> stopreactor() --> reactor.stop(),如此執行。
以下為程式output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ tlogin.py mx.mgt.ncu.edu.tw cacaegg
Enter password for cacaegg on mx.mgt.ncu.edu.tw
I have successfully connected to the server!
IMAPLogic.__init__returning.
connectionMake returning
I'm logged in!
Logging out.
Stopping reactor.
程式連結
星期四, 7月 16, 2009
IMAP in Python - 1 (Understanding Twisted Basics)
Python本身也有imaplib可供操作,不過在此使用一套挺有名的framework--Twisted。
Twisted有一個很大的特色就是以Event-based來進行程式設計,也就是會使用到callback function。
直接用程式碼來簡單介紹如何使用:
分為兩種class:
IMAPClient-作為Protocol class之用途,負責與server的protocol conversation
IMAPFactory-作為Connection class之用途,負責connection的部份
而reactor是Twisted中的Network event handler,在主程式第1行先用reactor.connectTCP進行連線,
成功後會呼叫connectionMade()來進行後續處理。
在connectionMade裏面,呼叫繼承而來的getCapabilities()來取得有關server支援IMAP選項的參數,且會回傳defer物件。要用此物件來告訴reactor等到有事件發生時該呼叫誰,在此使用了d.addCallback告訴reactor等到有事件時,需要呼叫self.gotcapabilities來處理。(注意在此沒有用括號,因為是callback沒有需要立刻呼叫)
接著進行reactor.run(),此function會等到有人呼叫reactor.stop()才會return,否則就不斷等待事件,事件發生了後,就按照預定的呼叫gotcapabilities(),印出有關的參數,然後logout(),最後stop()結束程式。
此程式的output:
Twisted有一個很大的特色就是以Event-based來進行程式設計,也就是會使用到callback function。
直接用程式碼來簡單介紹如何使用:
#!/usr/bin/env python
from twisted.internet import defer, reactor, protocol
from twisted.mail.imap4 import IMAP4Client
import sys
class IMAPClient(IMAP4Client):
def connectionMade(self):
print "I have successfully connected to the server!"
d = self.getCapabilities()
d.addCallback(self.gotcapabilities)
def gotcapabilities(self, caps):
if caps == None:
print "Server did not return a capability list."
else:
for key, value in caps.items():
print "%s: %s" % (key, str(value))
self.logout()
reactor.stop()
class IMAPFactory(protocol.ClientFactory):
protocol = IMAPClient
def clientConnectionFailed(self, connector, reason):
print "Client connection failed:", reason
reactor.stop()
reactor.connectTCP(sys.argv[1], 143, IMAPFactory())
reactor.run()
分為兩種class:
IMAPClient-作為Protocol class之用途,負責與server的protocol conversation
IMAPFactory-作為Connection class之用途,負責connection的部份
而reactor是Twisted中的Network event handler,在主程式第1行先用reactor.connectTCP進行連線,
成功後會呼叫connectionMade()來進行後續處理。
在connectionMade裏面,呼叫繼承而來的getCapabilities()來取得有關server支援IMAP選項的參數,且會回傳defer物件。要用此物件來告訴reactor等到有事件發生時該呼叫誰,在此使用了d.addCallback告訴reactor等到有事件時,需要呼叫self.gotcapabilities來處理。(注意在此沒有用括號,因為是callback沒有需要立刻呼叫)
接著進行reactor.run(),此function會等到有人呼叫reactor.stop()才會return,否則就不斷等待事件,事件發生了後,就按照預定的呼叫gotcapabilities(),印出有關的參數,然後logout(),最後stop()結束程式。
此程式的output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/IMAP$ sudo ./tconn.py mx.mgt.ncu.edu.tw
I have successfully connected to the server!
SASL-IR: None
SORT: None
THREAD: ['REFERENCES']
STARTTLS: None
UNSELECT: None
NAMESPACE: None
IDLE: None
AUTH: ['PLAIN']
IMAP4rev1: None
LOGIN-REFERRALS: None
MULTIAPPEND: None
LITERAL+: None
CHILDREN: None
星期三, 7月 15, 2009
Introduce IMAP
此篇的目的在簡略列出一些IMAP的特色,比起POP3有哪些優勢
。支援多mail folders,不只單一的user inbox
。可以儲存Flag(例如read、replied、seen、deleted)
。可在server side進行搜尋郵件的動作
。支援server內folder之間信件的複製
。可以新增信件到遠端folder
。message有了unique id
。可選擇郵件的某部份下載就好(例如附件)
。支援不同機器上同一個client的同步(一個帳號可以在不同機器上作業)
最大的好處:same mail in the same state from multiple machines at any time.
。支援多mail folders,不只單一的user inbox
。可以儲存Flag(例如read、replied、seen、deleted)
。可在server side進行搜尋郵件的動作
。支援server內folder之間信件的複製
。可以新增信件到遠端folder
。message有了unique id
。可選擇郵件的某部份下載就好(例如附件)
。支援不同機器上同一個client的同步(一個帳號可以在不同機器上作業)
最大的好處:same mail in the same state from multiple machines at any time.
標籤:
Network
POP3 in Python - 4 (Deleting Message)
呼叫POP3Object.dele(msgnum)就可以刪除指定的信件了。
只是大多時候,server會等到呼叫quit()時才正式把信件刪除,
在此先用一個dellist把所有要刪的存入
在這裡進行刪除:
最後呼叫quit()就可以順利刪除了。
來看看程式的output:
附件:程式連結
[P.S 跑此程式不要用自己還有正在使用的正常信箱,請用測試的]
只是大多時候,server會等到呼叫quit()時才正式把信件刪除,
mblist = p.list()[1]
dellist = []
for item in mblist:
number, octets = item.split(' ')
log("Downloading message %s (%s bytes)...\n" % (number,octets))
lines = p.retr(number)[1]
msg = email.message_from_string("\n".join(lines))
destfd.write(msg.as_string(unixfrom = 1))
destfd.write("\n")
dellist.append(number)
在此先用一個dellist把所有要刪的存入
在這裡進行刪除:
counter = 0
for number in dellist:
counter += 1
log("Deleting message %d of %d...\n" % (counter, len(dellist)))
p.dele(number)
最後呼叫quit()就可以順利刪除了。
來看看程式的output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/POP$ ./down-and-del.py mx.mgt.ncu.edu.tw cacaegg testbox
Password:
Connecting to mx.mgt.ncu.edu.tw...
Logging on...Success.
Scanning INBOX... 2 messages.
Downloading message 1 (2467 bytes)...
Downloading message 2 (2468 bytes)...
Deleting message 1 of 2...
Deleting message 2 of 2...
Successfully deleted 2 messages from server.
Closing connection... done.
附件:程式連結
[P.S 跑此程式不要用自己還有正在使用的正常信箱,請用測試的]
POP3 in Python - 3 (Download Message)
再來使用POP3Object.retr(msgnum)來下載信件,一次只能抓一封,
抓完以後把他寫入暫時的mailbox中。
retr()會回傳result code & message,值得注意的是message是用list方式給的,而非string,所以
要再行join一次。
程式的output:
然後就把信件寫入testbox的檔案中囉!
另外在此並沒有把server上的信件刪掉,所以每次跑程式都會重新download一次!
抓完以後把他寫入暫時的mailbox中。
#!/usr/bin/env python
import getpass, poplib, sys, email
(host, user, dest) = sys.argv[1:]
passwd = getpass.getpass()
destfd = open(dest, "at")
p = poplib.POP3(host)
try:
p.user(user)
p.pass_(passwd)
except poplib.error_proto, e:
print "Login failed:", e
sys.exit(1)
for item in p.list()[1]:
number, octets = item.split(' ')
print "Downloading message %s (%s bytes)" % (number, octets)
lines = p.retr(number)[1]
msg = email.message_from_string("\n".join(lines))
destfd.write(msg.as_string(unixfrom = 1))
destfd.write("\n")
p.quit()
destfd.close()
retr()會回傳result code & message,值得注意的是message是用list方式給的,而非string,所以
要再行join一次。
程式的output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/POP$ ./download.py mx.mgt.ncu.edu.tw cacaegg testmbox
Password:
Downloading message 1 (2466 bytes)
然後就把信件寫入testbox的檔案中囉!
另外在此並沒有把server上的信件刪掉,所以每次跑程式都會重新download一次!
Python Challenge - Level 2
Level 2
圖下給的提示為
recognize the characters. maybe they are in the book,
but MAYBE they are in the page source
於是直接打開page source看看網頁原始碼,可以看到有註解部份,寫著:
於是就需要找出一堆奇怪字元中的字母,用urllib2打開後,再用HTMLParser裏面的handle_comment來處理,就OK了!
跑出來的output:
Next :D
Level 3
圖下給的提示為
recognize the characters. maybe they are in the book,
but MAYBE they are in the page source
於是直接打開page source看看網頁原始碼,可以看到有註解部份,寫著:
&1t;!--
find rare characters in the mess below:
--&1t;
&1t;!--
%%$@_$^__#)^)&!_+]!*@&^}@[@%]()%+$&[(_@%+%$*^@$^!+]!&_#)_*}{}}!}_]$[%}@[{_@#_^{*
+_*{@+[$!!@%$+{_&(#^(([&[][[&@#+}_]&&]}^*&$&)#_^$@$((%)}+{}$#+{+^}&[#[#_+${#[#]{
(@@[%}[}$%+*#$+[%(**!$+@$@&+$_$#!_&&&&{***+)}][}#^!%#&$*)$!%}*}}##(^_%^]{+]&&]
}^]#^(}@]&$]*_][])$]{_+})^_}]))......
--&1t;
於是就需要找出一堆奇怪字元中的字母,用urllib2打開後,再用HTMLParser裏面的handle_comment來處理,就OK了!
#!/usr/bin/env python
from HTMLParser import HTMLParser
import urllib2
class Parser(HTMLParser):
def handle_comment(self, data):
s = ""
for x in data:
if x.isalpha():
s = s + x
print s
page = urllib2.urlopen("http://www.pythonchallenge.com/pc/def/ocr.html")
p = Parser()
p.feed(page.read())
跑出來的output:
cacaegg@cacabook:~/workspace/Python Challenge/src$ ./level_2.py
findrarecharactersinthemessbelow
equality
Next :D
Level 3
POP3 in Python - 2 (Get Mailbox info)
取得連線後,可用POP3Object.list()取得messages的詳細資料
list()會回傳兩個element的tuple:
1.response code:通常忽略,因為有錯會有exception
2.list:包含兩樣,分別是message number & message size,用空白分開。number不一定連續,每次POP3連線的number也不一定一樣。
程式的output:
list()會回傳兩個element的tuple:
1.response code:通常忽略,因為有錯會有exception
2.list:包含兩樣,分別是message number & message size,用空白分開。number不一定連續,每次POP3連線的number也不一定一樣。
#!/usr/bin/env python
import getpass, poplib, sys
(host, user) = sys.argv[1:]
passwd = getpass.getpass()
p = poplib.POP3(host)
try:
p.user(user)
p.pass_(passwd)
except poplib.error_proto, e:
print "Login failed.", e
sys.exit(1)
status = p.stat()
print "Mailbox has %d messages for a total of %d bytes" % (status[0], status[1])
for item in p.list()[1]:
number, octets = item.split(' ')
print "Message %s: %s bytes" % (number, octets)
p.quit()
程式的output:
cacaegg@cacabook:~/workspace/NetworkProgram/src/POP$ ./mailbox.py mx.mgt.ncu.edu.tw cacaegg
Password:
Mailbox has 1 messages for a total of 2484 bytes
Message 1: 2484 bytes
標籤:
Python
Python Challenge - Level 1
Level 1
這關要把字母做shift一下。
畫面上有
K --> M
O --> Q
E --> G
可以推出都往後兩個字母
先做個dicionary,然後把下面看起來像是亂碼的部份"g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
來進行轉換。
轉換後說把這用在url上,所以就
到下一關囉
Level 2
這關要把字母做shift一下。
畫面上有
K --> M
O --> Q
E --> G
可以推出都往後兩個字母
先做個dicionary,然後把下面看起來像是亂碼的部份"g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
來進行轉換。
>>> string = ""
>>> for x in "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj.":
if x.isalpha():
string = string + li[x]
else:
string = string + x
>>> print string
i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url.
轉換後說把這用在url上,所以就
>>> string = ""
>>> for x in "map":
if x.isalpha():
string = string + li[x]
else:
string = string + x
>>> print string
ocr
到下一關囉
Level 2
標籤:
Python
星期二, 7月 14, 2009
POP3 in Python - 1 (連接及身份驗證)
Python提供了poplib
pop有兩種常見的身份驗證模式
1. username/password
2. APOP
先看第1種
可分三步驟:
1.create POP3 object,要傳remote host (& port) 作為參數
2.呼叫POP3Object.user(user) & POP3Object.pass_(passwd)
分別傳送名稱及密碼來進行驗證
3.如果驗證不通過,會有poplib.error_proto的例外;如果沒有例外,
則可以呼叫POP3Object.stat(),會回傳tuple。裏面分別是inbox
中有多少message & 總共message大小。
4.最後POP3Object.quit()來中斷連線。
程式不大,直接貼上。
Note:
1.此程式不會對mailbox進行download或修改的動作,但是有些server還是會在一登入之後直接把信標示為已讀。
2.APOP會在驗證時候進行加密,減少sniff的風險
3.一旦login成功,server就會lock mailbox直到quit()為止。期間不會有mail送入,或是其他修改動作。
pop有兩種常見的身份驗證模式
1. username/password
2. APOP
先看第1種
可分三步驟:
1.create POP3 object,要傳remote host (& port) 作為參數
2.呼叫POP3Object.user(user) & POP3Object.pass_(passwd)
分別傳送名稱及密碼來進行驗證
3.如果驗證不通過,會有poplib.error_proto的例外;如果沒有例外,
則可以呼叫POP3Object.stat(),會回傳tuple。裏面分別是inbox
中有多少message & 總共message大小。
4.最後POP3Object.quit()來中斷連線。
程式不大,直接貼上。
#!/usr/bin/env python
import getpass, poplib, sys
(host, user) = sys.argv[1:]
passwd = getpass.getpass()
p = poplib.POP3(host)
try:
p.user(user)
p.pass_(passwd)
except poplib.error_proto, e:
print "Login failed:", e
sys.exit(1)
status = p.stat()
print "Mailbox has %d messages for a total of %d bytes" % (status[0], status[1])
p.quit()
Note:
1.此程式不會對mailbox進行download或修改的動作,但是有些server還是會在一登入之後直接把信標示為已讀。
2.APOP會在驗證時候進行加密,減少sniff的風險
3.一旦login成功,server就會lock mailbox直到quit()為止。期間不會有mail送入,或是其他修改動作。
SMTP in Python - 3 (結合身份驗證)
大多server要寄信以前都需要先進行身份驗證
python也提供了套簡單的方法來進行。
在取得username & password以後,就可以直接呼叫SMTPObject.login(name, pass)
來進行驗證,如果拋出了SMTPException就是驗證失敗囉!
當然也可以先進行SMTPObject.ehlo()後,看有沒有SMTPObject.has_extn('auth'),
來先確認server是否支援身份驗證的。
附件:完整script
python也提供了套簡單的方法來進行。
sys.stdout.write("Enter username: ")
username = sys.stdin.readline().strip()
password = getpass("Enter password: ")
try:
s = smtplib.SMTP(server)
try:
s.login(username, password)
except smtplib.SMTPException, e:
print "Authentication failed:", e
sys.exit(1)
s.sendmail(frmaddr, toaddrs, message)
在取得username & password以後,就可以直接呼叫SMTPObject.login(name, pass)
來進行驗證,如果拋出了SMTPException就是驗證失敗囉!
當然也可以先進行SMTPObject.ehlo()後,看有沒有SMTPObject.has_extn('auth'),
來先確認server是否支援身份驗證的。
附件:完整script
星期五, 7月 10, 2009
SMTP in Python - 2 (從EHLO取資訊)
由於某些SMTP server會限制信件大小,也可直接在交談中取得關於限制大小的資訊。
首先來看一下SMTP有所謂的extension,就是ESMTP,提供了強的conversation。
如何分辨server是否支援呢?
在conversation開始時,用ehlo而回傳正常資訊的,就是有支援ESMTP,
否則沒支援的話就用原本的helo來打招呼。
不同於前例使用sendmail()函式來寄送此資訊,而是要在sendmail()之前就得知檔案超過大小才行。
此例中先用s.eclo()[0]來得知ehlo測試後的return code,
如果在不在正常範圍內(200~299),再改用s.helo()[0]來進行conversation。
如果真的都不行了,就raise exception來處理。
此段會在server支援esmtp而且server有回傳max size後執行,
可以直接用s.esmtp_features['size']來取得此變數,
如果信件長度過大,則離開程式。
以下是執行結果:
附件:完整python檔
首先來看一下SMTP有所謂的extension,就是ESMTP,提供了強的conversation。
如何分辨server是否支援呢?
在conversation開始時,用ehlo而回傳正常資訊的,就是有支援ESMTP,
否則沒支援的話就用原本的helo來打招呼。
不同於前例使用sendmail()函式來寄送此資訊,而是要在sendmail()之前就得知檔案超過大小才行。
try:
s = smtplib.SMTP(server)
code = s.ehlo()[0]
usesesmtp = 1
if not (200 <= code <= 299):
usesesmtp = 0
code = s.helo()[0]
if not (200 <= code <= 299):
raise SMTPHeloError(code, resp)
此例中先用s.eclo()[0]來得知ehlo測試後的return code,
如果在不在正常範圍內(200~299),再改用s.helo()[0]來進行conversation。
如果真的都不行了,就raise exception來處理。
if usesesmtp and s.has_extn('size'):
print "Maximum message size is", s.esmtp_features['size']
if len(message) > int(s.esmtp_features['size']):
print "Message too large; aborting."
sys.exit(2)
s.sendmail(frmaddr, toaddrs, message)
此段會在server支援esmtp而且server有回傳max size後執行,
可以直接用s.esmtp_features['size']來取得此變數,
如果信件長度過大,則離開程式。
以下是執行結果:
cacaegg@cacabook:~/workspace/NetworkProgram/src/SMTP$ ehlo.py localhost cacaegg@localhost cacaegg@localhost
Maximum message size is 10240000
Message successfully sent to 1 recipient(s)
附件:完整python檔
SMTP in Python - 1
要寄信很簡單,只要先import smtplib即可。
server:寄信的伺服器
fromaddr:寄件人
toaddrs:收件人(可以是list)
message:信件內容
再來看看關於SMTP的protocol conversation
平時smtpobj預設是關閉debug的,因此要另外打開。
多加了s.set_debuglevel(1)執行此程式就會顯示SMTP conversation messages
整支程式跑起來如下:
最後簡單註解模擬一下SMTP conversation protocol ([]為註解)
send: 'ehlo [127.0.1.1]\r\n'
[client:server 你好,我是127.0.1.1]
reply: '250-ENHANCEDSTATUSCODES\r\n'
...bala
reply: '250 HELP\r\n'
reply: retcode (250); Msg: cacabook Hello localhost [127.0.0.1], pleased to meet you
[server: cacabook 你好,我有這些提供這些服務的選項,來參考看看]
ENHANCEDSTATUSCODES
...bala
HELP
[client murmur:那我就選這些 bala...]
send: 'mail FROM: size=155\r\n'
[client: 這封信是從cacaegg@localhost來的,大小是155 byte]
reply: '250 2.1.0... Sender ok\r\n'
reply: retcode (250); Msg: 2.1.0... Sender ok
[server: 寄件人... 好的]
send: 'rcpt TO:\r\n'
[client: 我要寄給cacaegg@localhost]
reply: '250 2.1.5... Recipient ok\r\n'
reply: retcode (250); Msg: 2.1.5... Recipient ok
[server: 收件人... 好的]
send: 'data\r\n'
[client: 我要開始說信件內容囉]
reply: '354 Enter mail, end with "." on a line by itself\r\n'
reply: retcode (354); Msg: Enter mail, end with "." on a line by itself
[server: 請說!說完以後就在打上.我就知道你說完了!]
data: (354, 'Enter mail, end with "." on a line by itself')
send: 'To: cacaegg@localhost\r\nFrom: cacaegg@localhost\r\nSubject: Test Message from simple.py\r\n\r\nHello,\r\n\r\nThis is a test message sent to you fromsimple.py and smtplib.\r\n.\r\n'
[client: 我的內容是balabala....]
[client: .]
reply: '250 2.0.0 n6A2nMKj013583 Message accepted for delivery\r\n'
reply: retcode (250); Msg: 2.0.0 n6A2nMKj013583 Message accepted for delivery
[server: 好的,我會幫你寄送!]
其實server說好也不一定代表信就會寄出,可能原因有很多種,舉些例子
1.server判斷你是spam,就不幫你寄信了
2.server只是轉寄站,還要繼續轉寄到其他的mail server,可能到最後還是無法到達。
這情況通常會有bounce message
附件:為整支程式,可以抓下來直接跑!
import smtplib
s = smtplib.SMTP(server)
s.sendmail(fromaddr, toaddrs, message)
server:寄信的伺服器
fromaddr:寄件人
toaddrs:收件人(可以是list)
message:信件內容
再來看看關於SMTP的protocol conversation
平時smtpobj預設是關閉debug的,因此要另外打開。
try:
s = smtplib.SMTP(server)
s.set_debuglevel(1)
s.sendmail(frmaddr, toaddr, message)
except (socket.gaierror, socket.error, socket.herror, smtplib.SMTPException), e:
print " *** Your message may have not been sent!"
print e
sys.exit(1)
print "Message successfully sent to %d recipient(s)" % (len(toaddr))
多加了s.set_debuglevel(1)執行此程式就會顯示SMTP conversation messages
整支程式跑起來如下:
cacaegg@cacabook:~/workspace/NetworkProgram/src/SMTP$ debug.py localhost cacaegg@localhost cacaegg@localhost
send: 'ehlo [127.0.1.1]\r\n'
reply: '250-cacabook Hello localhost [127.0.0.1], pleased to meet you\r\n'
reply: '250-ENHANCEDSTATUSCODES\r\n'
reply: '250-PIPELINING\r\n'
reply: '250-EXPN\r\n'
reply: '250-VERB\r\n'
reply: '250-8BITMIME\r\n'
reply: '250-SIZE\r\n'
reply: '250-DSN\r\n'
reply: '250-ETRN\r\n'
reply: '250-AUTH DIGEST-MD5 CRAM-MD5\r\n'
reply: '250-DELIVERBY\r\n'
reply: '250 HELP\r\n'
reply: retcode (250); Msg: cacabook Hello localhost [127.0.0.1], pleased to meet you
ENHANCEDSTATUSCODES
PIPELINING
EXPN
VERB
8BITMIME
SIZE
DSN
ETRN
AUTH DIGEST-MD5 CRAM-MD5
DELIVERBY
HELP
send: 'mail FROM:size=155\r\n'
reply: '250 2.1.0... Sender ok\r\n'
reply: retcode (250); Msg: 2.1.0... Sender ok
send: 'rcpt TO:\r\n'
reply: '250 2.1.5... Recipient ok\r\n'
reply: retcode (250); Msg: 2.1.5... Recipient ok
send: 'data\r\n'
reply: '354 Enter mail, end with "." on a line by itself\r\n'
reply: retcode (354); Msg: Enter mail, end with "." on a line by itself
data: (354, 'Enter mail, end with "." on a line by itself')
send: 'To: cacaegg@localhost\r\nFrom: cacaegg@localhost\r\nSubject: Test Message from simple.py\r\n\r\nHello,\r\n\r\nThis is a test message sent to you fromsimple.py and smtplib.\r\n.\r\n'
reply: '250 2.0.0 n6A2nMKj013583 Message accepted for delivery\r\n'
reply: retcode (250); Msg: 2.0.0 n6A2nMKj013583 Message accepted for delivery
data: (250, '2.0.0 n6A2nMKj013583 Message accepted for delivery')
Message successfully sent to 1 recipient(s)
最後簡單註解模擬一下SMTP conversation protocol ([]為註解)
send: 'ehlo [127.0.1.1]\r\n'
[client:server 你好,我是127.0.1.1]
reply: '250-ENHANCEDSTATUSCODES\r\n'
...bala
reply: '250 HELP\r\n'
reply: retcode (250); Msg: cacabook Hello localhost [127.0.0.1], pleased to meet you
[server: cacabook 你好,我有這些提供這些服務的選項,來參考看看]
ENHANCEDSTATUSCODES
...bala
HELP
[client murmur:那我就選這些 bala...]
send: 'mail FROM:
[client: 這封信是從cacaegg@localhost來的,大小是155 byte]
reply: '250 2.1.0
reply: retcode (250); Msg: 2.1.0
[server: 寄件人... 好的]
send: 'rcpt TO:
[client: 我要寄給cacaegg@localhost]
reply: '250 2.1.5
reply: retcode (250); Msg: 2.1.5
[server: 收件人... 好的]
send: 'data\r\n'
[client: 我要開始說信件內容囉]
reply: '354 Enter mail, end with "." on a line by itself\r\n'
reply: retcode (354); Msg: Enter mail, end with "." on a line by itself
[server: 請說!說完以後就在打上.我就知道你說完了!]
data: (354, 'Enter mail, end with "." on a line by itself')
send: 'To: cacaegg@localhost\r\nFrom: cacaegg@localhost\r\nSubject: Test Message from simple.py\r\n\r\nHello,\r\n\r\nThis is a test message sent to you fromsimple.py and smtplib.\r\n.\r\n'
[client: 我的內容是balabala....]
[client: .]
reply: '250 2.0.0 n6A2nMKj013583 Message accepted for delivery\r\n'
reply: retcode (250); Msg: 2.0.0 n6A2nMKj013583 Message accepted for delivery
[server: 好的,我會幫你寄送!]
其實server說好也不一定代表信就會寄出,可能原因有很多種,舉些例子
1.server判斷你是spam,就不幫你寄信了
2.server只是轉寄站,還要繼續轉寄到其他的mail server,可能到最後還是無法到達。
這情況通常會有bounce message
附件:為整支程式,可以抓下來直接跑!
標籤:
Python
Text-based mail client
想在ubuntu的console下用文字收信,有個老牌的軟體pine
只是apt中好像把他停用了,找到了另外一個alpine
就幾乎跟pine一模一樣了。
就可以使用囉!
只是apt中好像把他停用了,找到了另外一個alpine
就幾乎跟pine一模一樣了。
cacaegg@cacabook:~$ sudo apt-get install alpine
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
libswfdec-0.8-0
Use 'apt-get autoremove' to remove them.
The following NEW packages will be installed:
alpine
0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
Need to get 2913kB of archives.
After this operation, 6636kB of additional disk space will be used.
Get:1 http://tw.archive.ubuntu.com jaunty/universe alpine 2.00+dfsg-2ubuntu2 [2913kB]
Fetched 2913kB in 2s (976kB/s)
Selecting previously deselected package alpine.
(Reading database ... 259353 files and directories currently installed.)
Unpacking alpine (from .../alpine_2.00+dfsg-2ubuntu2_i386.deb) ...
Processing triggers for man-db ...
Setting up alpine (2.00+dfsg-2ubuntu2) ...
cacaegg@cacabook:~$ alpine
就可以使用囉!
星期五, 7月 03, 2009
KDE 下鍵盤失效
在打完帳號密碼,登入以後
鍵盤通通都失效了,只剩滑鼠可以使用
加上許多icon都不見了(例如shutdown, logout)
乾脆直接把KDE的設定全部刪掉
在打帳號密碼時,進入console login
%rm -r ~/.kde
就可以了
鍵盤通通都失效了,只剩滑鼠可以使用
加上許多icon都不見了(例如shutdown, logout)
乾脆直接把KDE的設定全部刪掉
在打帳號密碼時,進入console login
%rm -r ~/.kde
就可以了
訂閱:
文章 (Atom)