Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
eda960a1
Commit
eda960a1
authored
Jun 18, 1998
by
Guido van Rossum
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Piers' latest version -- authentication added by Donn Cave.
parent
faac0136
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
175 additions
and
41 deletions
+175
-41
Lib/imaplib.py
Lib/imaplib.py
+175
-41
No files found.
Lib/imaplib.py
View file @
eda960a1
...
...
@@ -4,6 +4,8 @@ Based on RFC 2060.
Author: Piers Lauder <piers@cs.su.oz.au> December 1997.
Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998.
Public class: IMAP4
Public variable: Debug
Public functions: Internaldate2tuple
...
...
@@ -11,8 +13,12 @@ Public functions: Internaldate2tuple
ParseFlags
Time2Internaldate
"""
#
# $Header$
#
__version__
=
"$Revision$"
import
re
,
socket
,
string
,
time
,
random
import
binascii
,
re
,
socket
,
string
,
time
,
random
# Globals
...
...
@@ -41,6 +47,7 @@ Commands = {
'LOGOUT'
:
(
'NONAUTH'
,
'AUTH'
,
'SELECTED'
,
'LOGOUT'
),
'LSUB'
:
(
'AUTH'
,
'SELECTED'
),
'NOOP'
:
(
'NONAUTH'
,
'AUTH'
,
'SELECTED'
,
'LOGOUT'
),
'PARTIAL'
:
(
'SELECTED'
,),
'RENAME'
:
(
'AUTH'
,
'SELECTED'
),
'SEARCH'
:
(
'SELECTED'
,),
'SELECT'
:
(
'AUTH'
,
'SELECTED'
),
...
...
@@ -53,7 +60,7 @@ Commands = {
# Patterns to match server responses
Continuation
=
re
.
compile
(
r'\
+
(?P<d
ata>.*)
'
)
Continuation
=
re
.
compile
(
r'\
+
( (?P<d
ata>.*))?
'
)
Flags
=
re
.
compile
(
r'.*FLAGS \
((?P<
flags>[^\
)]*)
\)'
)
InternalDate
=
re
.
compile
(
r'.*INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])'
...
...
@@ -62,7 +69,7 @@ InternalDate = re.compile(r'.*INTERNALDATE "'
r'"'
)
Literal
=
re
.
compile
(
r'(?P<data>.*) {(?P<size>\
d+)}$
')
Response_code = re.compile(r'
\
[(
?
P
<
type
>
[
A
-
Z
-
]
+
)(
(
?
P
<
data
>
[
^
\
]]
*
))
?
\
]
')
Untagged_response = re.compile(r'
\
*
(
?
P
<
type
>
[
A
-
Z
-
]
+
)
(
?
P
<
data
>
.
*
)
')
Untagged_response = re.compile(r'
\
*
(
?
P
<
type
>
[
A
-
Z
-
]
+
)
(
(
?
P
<
data
>
.
*
))
?
')
Untagged_status = re.compile(r'
\
*
(
?
P
<
data
>
\
d
+
)
(
?
P
<
type
>
[
A
-
Z
-
]
+
)(
(
?
P
<
data2
>
.
*
))
?
')
...
...
@@ -81,8 +88,9 @@ class IMAP4:
All
arguments
to
commands
are
converted
to
strings
,
except
for
the
last
argument
to
APPEND
which
is
passed
as
an
IMAP4
literal
.
If
necessary
(
the
string
isn
't enclosed with either
parentheses or double quotes) each converted string is quoted.
literal
.
If
necessary
(
the
string
contains
white
-
space
and
isn
't enclosed with either parentheses or double quotes) each
string is quoted.
Each command returns a tuple: (type, [data, ...]) where '
type
'
is usually '
OK
' or '
NO
', and '
data
' is either the text from the
...
...
@@ -91,6 +99,11 @@ class IMAP4:
Errors raise the exception class <instance>.error("<reason>").
IMAP4 server errors raise <instance>.abort("<reason>"),
which is a sub-class of '
error
'.
Note: to use this module, you must read the RFCs pertaining
to the IMAP4 protocol, as the semantics of the arguments to
each IMAP4 command are left to the invoker, not to mention
the results.
"""
class error(Exception): pass # Logical errors - debug required
...
...
@@ -110,9 +123,7 @@ class IMAP4:
# Open socket to server.
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self.host, self.port)
self.file = self.sock.makefile('r')
self.open(host, port)
# Create unique tag for this session,
# and compile tagged response matcher.
...
...
@@ -156,6 +167,13 @@ class IMAP4:
raise self.error('
server
not
IMAP4
compliant
')
def open(self, host, port):
"""Setup '
self
.
sock
' and '
self
.
file
'."""
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(self.host, self.port)
self.file = self.sock.makefile('r')
def __getattr__(self, attr):
"""Allow UPPERCASE variants of all following IMAP4 commands."""
if Commands.has_key(attr):
...
...
@@ -173,6 +191,7 @@ class IMAP4:
"""
name = '
APPEND
'
if flags:
if (flags[0],flags[-1]) != ('
(
','
)
'):
flags = '
(
%
s
)
' % flags
else:
flags = None
...
...
@@ -184,12 +203,32 @@ class IMAP4:
return self._simple_command(name, mailbox, flags, date_time)
def authenticate(self,
func
):
def authenticate(self,
mechanism, authobject
):
"""Authenticate command - requires response processing.
UNIMPLEMENTED
'
mechanism
' specifies which authentication mechanism is to
be used - it must appear in <instance>.capabilities in the
form AUTH=<mechanism>.
'
authobject
' must be a callable object:
data = authobject(response)
It will be called to process server continuation responses.
It should return data that will be encoded and sent to server.
It should return None if the client abort response '
*
' should
be sent instead.
"""
raise self.error('
UNIMPLEMENTED
')
mech = string.upper(mechanism)
cap = '
AUTH
=%
s
' % mech
if not cap in self.capabilities:
raise self.error("Server doesn'
t
allow
%
s
authentication
.
" % mech)
self.literal = _Authenticator(authobject).process
typ, dat = self._simple_command('AUTHENTICATE', mech)
if typ != 'OK':
raise self.error(dat)
self.state = 'AUTH'
return typ, dat
def check(self):
...
...
@@ -324,18 +363,32 @@ class IMAP4:
(typ, data) = <instance>.noop()
"""
if __debug__ and self.debug >= 3:
print '
\
t
untagged responses: %s' % `self.untagged_responses`
return self._simple_command('NOOP')
def partial(self, message_num, message_part, start, length):
"""Fetch truncated part of a message.
(typ, [data, ...]) = <instance>.partial(message_num, message_part, start, length)
'data' is tuple of message part envelope and data.
"""
name = 'PARTIAL'
typ, dat = self._simple_command(name, message_num, message_part, start, length)
return self._untagged_response(typ, 'FETCH')
def recent(self):
"""Return most recent '
RECENT
' response
if it exists
,
"""Return most recent 'RECENT' response
s if any exist
,
else prompt server for an update using the 'NOOP' command,
and flush all untagged responses.
(typ, [data]) = <instance>.recent()
'data' is None if no new messages,
else
value of RECENT response
.
else
list of RECENT responses, most recent last
.
"""
name = 'RECENT'
typ, dat = self._untagged_response('OK', name)
...
...
@@ -361,7 +414,7 @@ class IMAP4:
(code, [data]) = <instance>.response(code)
"""
return self._untagged_response(code,
code
)
return self._untagged_response(code,
string.upper(code)
)
def search(self, charset, criteria):
...
...
@@ -403,6 +456,14 @@ class IMAP4:
return typ, self.untagged_responses.get('EXISTS', [None])
def socket(self):
"""Return socket instance used to connect to IMAP4 server.
socket = <instance>.socket()
"""
return self.sock
def status(self, mailbox, names):
"""Request named status conditions for mailbox.
...
...
@@ -440,8 +501,14 @@ class IMAP4:
Returns response appropriate to 'command'.
"""
command = string.upper(command)
if not Commands.has_key(command):
raise self.error("
Unknown
IMAP4
UID
command
:
%
s
" % command)
if self.state not in Commands[command]:
raise self.error('command %s illegal in state %s'
% (command, self.state))
name = 'UID'
typ, dat = apply(self._simple_command, (
'
UID
'
, command) + args)
typ, dat = apply(self._simple_command, (
name
, command) + args)
if command == 'SEARCH':
name = 'SEARCH'
else:
...
...
@@ -476,13 +543,13 @@ class IMAP4:
def _append_untagged(self, typ, dat):
if self.untagged_responses.has_key(typ):
self.untagged_responses[typ].append(dat)
ur = self.untagged_responses
if ur.has_key(typ):
ur[typ].append(dat)
else:
self.untagged_responses[typ] = [dat]
ur[typ] = [dat]
if __debug__ and self.debug >= 5:
print '
\
tuntagged_responses
[
%
s
]
+=
%
.
20
s
..
' % (typ, `dat`
)
print '
\
t
untagged_responses[%s]
%s += %s' % (typ, len(`ur[typ]`), _trunc(20, `dat`)
)
def _command(self, name, *args):
...
...
@@ -492,6 +559,9 @@ class IMAP4:
raise self.error(
'command %s illegal in state %s' % (name, self.state))
if self.untagged_responses.has_key('OK'):
del self.untagged_responses['OK']
tag = self._new_tag()
data = '%s %s' % (tag, name)
for d in args:
...
...
@@ -508,6 +578,10 @@ class IMAP4:
literal = self.literal
if literal is not None:
self.literal = None
if type(literal) is type(self._command):
literator = literal
else:
literator = None
data = '%s {%s}' % (data, len(literal))
try:
...
...
@@ -521,6 +595,7 @@ class IMAP4:
if literal is None:
return tag
while 1:
# Wait for continuation response
while self._get_response():
...
...
@@ -529,6 +604,9 @@ class IMAP4:
# Send literal
if literator:
literal = literator(self.continuation_response)
if __debug__ and self.debug >= 4:
print '
\
t
write literal size %s' % len(literal)
...
...
@@ -538,6 +616,9 @@ class IMAP4:
except socket.error, val:
raise self.abort('socket error: %s' % val)
if not literator:
break
return tag
...
...
@@ -590,10 +671,11 @@ class IMAP4:
self.continuation_response = self.mo.group('data')
return None # NB: indicates continuation
raise self.abort(
'
unexpected
response
:
%
s
'
% resp)
raise self.abort(
"
unexpected
response
:
'%s'"
% resp)
typ = self.mo.group('type')
dat = self.mo.group('data')
if dat is None: dat = '' # Null untagged response
if dat2: dat = dat + ' ' + dat2
# Is there a literal to come?
...
...
@@ -679,12 +761,56 @@ class IMAP4:
return typ, [None]
data = self.untagged_responses[name]
if __debug__ and self.debug >= 5:
print '
\
tuntagged_responses
[
%
s
]
=>
%
.
20
s
..
' % (name, `data`
)
print '
\
t
untagged_responses[%s] => %
s' % (name, _trunc(20, `data`)
)
del self.untagged_responses[name]
return typ, data
class _Authenticator:
"""Private class to provide en/decoding
for base64-based authentication conversation.
"""
def __init__(self, mechinst):
self.mech = mechinst # Callable object to provide/process data
def process(self, data):
ret = self.mech(self.decode(data))
if ret is None:
return '*' # Abort conversation
return self.encode(ret)
def encode(self, inp):
#
# Invoke binascii.b2a_base64 iteratively with
# short even length buffers, strip the trailing
# line feed from the result and append. "
Even
"
# means a number that factors to both 6 and 8,
# so when it gets to the end of the 8-bit input
# there's no partial 6-bit output.
#
oup = ''
while inp:
if len(inp) > 48:
t = inp[:48]
inp = inp[48:]
else:
t = inp
inp = ''
e = binascii.b2a_base64(t)
if e:
oup = oup + e[:-1]
return oup
def decode(self, inp):
if not inp:
return ''
return binascii.a2b_base64(inp)
Mon2num = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
...
...
@@ -779,6 +905,14 @@ def Time2Internaldate(date_time):
if __debug__:
def _trunc(m, s):
if len(s) <= m: return s
return '%.*s..' % (m, s)
if __debug__ and __name__ == '__main__':
host = ''
...
...
@@ -798,8 +932,8 @@ if __debug__ and __name__ == '__main__':
('CREATE', ('/tmp/yyz 2',)),
('append', ('/tmp/yyz 2', None, None, 'From: anon@x.y.z
\
n
\
n
data...')),
('select', ('/tmp/yyz 2',)),
('
uid
', ('
SEARCH
', '
ALL
')),
('
fetch
', ('
1
', '
(
INTERNALDATE
RFC822
)
'
)),
('
search', (None, '(TO zork)
')),
('
partial', ('1', 'RFC822', 1, 1024
)),
('store', ('1', 'FLAGS', '(
\
Dele
t
ed)')),
('expunge', ()),
('recent', ()),
...
...
@@ -820,7 +954,7 @@ if __debug__ and __name__ == '__main__':
print ' %s %s
\
n
=> %s %s' % (cmd, args, typ, dat)
return dat
Debug =
4
Debug =
5
M = IMAP4(host)
print 'PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION
...
...
@@ -839,6 +973,6 @@ if __debug__ and __name__ == '__main__':
if (cmd,args) != ('
uid
', ('
SEARCH
', '
ALL
')):
continue
uid = string.split(dat[
0
])[-1]
uid = string.split(dat[
-1
])[-1]
run('
uid
', ('
FETCH
', '
%
s
' % uid,
'(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822)'))
'
(
FLAGS
INTERNALDATE
RFC822
.
SIZE
RFC822
.
HEADER
RFC822
.
TEXT
)
'))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment