4 # Copyright 2007 Neil Shi <zeegeek@gmail.com>
7 # This is the IMAP protocol support module for pymailheaders.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
22 from email
.Header
import decode_header
25 from exception
import *
30 @attention: if an exception Error is thrown by any of the method, by
31 disconnecting and connecting again, the problem should be solved.
33 @warning: B{Have to call connect() method before doing anything else}
35 @note: Private member variables:
48 def __init__(self
, server
, uname
, password
, ssl
, h
, mbox
):
52 @param server: mail server address
54 @param uname: username
55 @type password: string
56 @param password: password
58 @param ssl: if this is a secure connection
60 @param h: number of messages displayable in the window
65 self
.__server
= server
68 self
.__pass
= password
74 Should log out and destroy the connection.
78 response
= self
.__connection
.logout()
79 if response
[0] != 'BYE':
80 raise Error('imapprl (__del__)', response
[1])
81 except (socket
.error
, socket
.gaierror
, imaplib
.IMAP4
.error
,
82 imaplib
.IMAP4
.abort
), strerr
:
83 raise Error('imapprl (__del__)', str(strerr
))
88 """Get the total number and the number of new messages in a mailbox.
91 @return: (total number of messages, number of new messages)
95 response
= self
.__connection
.status('INBOX', '(MESSAGES UNSEEN)')
96 if response
[0] != 'OK':
97 raise Error('imapprl (__check)', response
[1])
98 except (socket
.error
, socket
.gaierror
, imaplib
.IMAP4
.error
,
99 imaplib
.IMAP4
.abort
), strerr
:
100 raise Error('imapprl (__check)', str(strerr
))
104 num
= re
.search('\D+(\d+)\D+(\d+)', response
[1][0]).groups()
105 return (int(num
[0]), int(num
[1]))
107 def __select_mailbox(self
):
112 response
= self
.__connection
.select(self
.__mbox
, True)
113 if response
[0] != 'OK':
114 raise Error('imapprl (__select_mailbox)', response
[1])
115 except (socket
.error
, socket
.gaierror
, imaplib
.IMAP4
.error
,
116 imaplib
.IMAP4
.abort
), strerr
:
117 raise Error('imapprl (__select_mailbox)', str(strerr
))
122 """Connect to the server and log in.
124 If the connection has already established, return.
126 @attention: when exception TryAgain is thrown by this method,
127 the calling program should try to connect again.
129 @raise TryAgain: when network is temporarily unavailable
134 self
.__connection
= imaplib
.IMAP4_SSL(self
.__server
)
136 self
.__connection
= imaplib
.IMAP4(self
.__server
)
137 self
.__connection
.socket().settimeout(self
.__TIMEOUT
)
139 response
= self
.__connection
.login(self
.__uname
, self
.__pass
)
140 if response
[0] != 'OK':
141 raise Error('imapprl (connect)', response
[1])
142 except socket
.gaierror
, (socket
.EAI_AGAIN
, strerr
):
143 raise TryAgain('imapprl (connect)', strerr
)
144 except (socket
.error
, socket
.gaierror
, imaplib
.IMAP4
.error
), strerr
:
145 raise Error('imapprl (connect)', str(strerr
))
153 @return: List of tuples of flag, sender addresses and subjects.
154 newest message on top.
156 @note: flag I{B{True}} for new messages
161 self
.__select
_mailbox
()
163 # if the number of new messages is more than what the window can
164 # hold, get them all. Otherwise, fill up the whole window with old
165 # messages at the bottom.
166 if self
.__size
< num
[1]:
167 num_to_fetch
= str(num
[0] - num
[1])
169 num_to_fetch
= str(num
[0] < self
.__size
and 1 \
170 or num
[0] - self
.__size
)
171 mail_list
= self
.__connection
.fetch(num_to_fetch
+ ':' + \
172 str(num
[0]), '(FLAGS BODY.PEEK' \
173 + '[HEADER.FIELDS ' \
174 + '(FROM SUBJECT)])')
175 if mail_list
[0] != 'OK':
176 raise Error('imapprl (get_mail)', response
[1])
178 response
= self
.__connection
.close()
179 if response
[0] != 'OK':
180 raise Error('imapprl (get_mail)', response
[1])
181 except (socket
.error
, socket
.gaierror
, imaplib
.IMAP4
.error
,
182 imaplib
.IMAP4
.abort
), strerr
:
183 raise Error('imapprl (get_mail)', str(strerr
))
188 # In case the string is not compliant with the standard, let's make
191 y
= decode_header(re
.sub(r
'(=\?([^\?]*\?){3}=)', r
' \1 ', x
))
192 return ''.join(s
[1] and s
[0].decode(s
[1]) or s
[0] for s
in y
)
193 except UnicodeDecodeError:
194 raise Error('imapprl (get_mail)', 'Invalid encoding')
196 # parse sender addresses and subjects
197 def a(x
): return x
!= ')'
198 # ATTENTION: cannot rely on the order of the reply by fetch
199 # command, it's arbitrary.
201 sender
= re
.search('From: ([^\r\n]+)', x
[1].strip()).group(1)
202 # get sender's name if there's one, otherwise get the email address
203 (name
, addr
) = re
.search('("?([^"]*)"?\s)?<?(([a-zA-Z0-9_\-\.])+@(([0-2]?[0-5]?[0-5]\.[0-2]?[0-5]?[0-5]\.[0-2]?[0-5]?[0-5]\.[0-2]?[0-5]?[0-5])|((([a-zA-Z0-9\-])+\.)+([a-zA-Z\-])+)))?>?', sender
).groups()[1:3]
204 subject
= re
.search('Subject:\s*((.*\s*)+)', x
[1].strip())
205 # subject might be empty
209 subject
= re
.sub('\r?\n?', '', subject
.group(1))
210 # ATTENTION: some mail agents will clear all the flags to indicate
211 # that a message is unread
212 return (re
.search('FLAGS \(.*\\Seen.*\)', \
213 x
[0].strip()) == None, \
214 name
and d(name
.strip()) or addr
, \
216 messages
= map(b
, filter(a
, mail_list
[1]))