Commit 13a2c279 authored by Fred Drake's avatar Fred Drake

Untabify to pass the -tt test.

parent 857c4c36
...@@ -44,27 +44,27 @@ HTTPS_PORT = 443 ...@@ -44,27 +44,27 @@ HTTPS_PORT = 443
class FakeSocket: class FakeSocket:
def __init__(self, sock, ssl): def __init__(self, sock, ssl):
self.__sock = sock self.__sock = sock
self.__ssl = ssl self.__ssl = ssl
return return
def makefile(self, mode): # hopefully, never have to write def makefile(self, mode): # hopefully, never have to write
msgbuf = "" msgbuf = ""
while 1: while 1:
try: try:
msgbuf = msgbuf + self.__ssl.read() msgbuf = msgbuf + self.__ssl.read()
except socket.sslerror, msg: except socket.sslerror, msg:
break break
return StringIO(msgbuf) return StringIO(msgbuf)
def send(self, stuff, flags = 0): def send(self, stuff, flags = 0):
return self.__ssl.write(stuff) return self.__ssl.write(stuff)
def recv(self, len = 1024, flags = 0): def recv(self, len = 1024, flags = 0):
return self.__ssl.read(len) return self.__ssl.read(len)
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self.__sock, attr) return getattr(self.__sock, attr)
class HTTP: class HTTP:
"""This class manages a connection to an HTTP server.""" """This class manages a connection to an HTTP server."""
......
...@@ -8,269 +8,269 @@ import os ...@@ -8,269 +8,269 @@ import os
class _Mailbox: class _Mailbox:
def __init__(self, fp): def __init__(self, fp):
self.fp = fp self.fp = fp
self.seekp = 0 self.seekp = 0
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
if whence==1: # Relative to current position if whence==1: # Relative to current position
self.pos = self.pos + pos self.pos = self.pos + pos
if whence==2: # Relative to file's end if whence==2: # Relative to file's end
self.pos = self.stop + pos self.pos = self.stop + pos
else: # Default - absolute position else: # Default - absolute position
self.pos = self.start + pos self.pos = self.start + pos
def next(self): def next(self):
while 1: while 1:
self.fp.seek(self.seekp) self.fp.seek(self.seekp)
try: try:
self._search_start() self._search_start()
except EOFError: except EOFError:
self.seekp = self.fp.tell() self.seekp = self.fp.tell()
return None return None
start = self.fp.tell() start = self.fp.tell()
self._search_end() self._search_end()
self.seekp = stop = self.fp.tell() self.seekp = stop = self.fp.tell()
if start <> stop: if start <> stop:
break break
return rfc822.Message(_Subfile(self.fp, start, stop)) return rfc822.Message(_Subfile(self.fp, start, stop))
class _Subfile: class _Subfile:
def __init__(self, fp, start, stop): def __init__(self, fp, start, stop):
self.fp = fp self.fp = fp
self.start = start self.start = start
self.stop = stop self.stop = stop
self.pos = self.start self.pos = self.start
def read(self, length = None): def read(self, length = None):
if self.pos >= self.stop: if self.pos >= self.stop:
return '' return ''
remaining = self.stop - self.pos remaining = self.stop - self.pos
if length is None or length < 0: if length is None or length < 0:
length = remaining length = remaining
elif length > remaining: elif length > remaining:
length = remaining length = remaining
self.fp.seek(self.pos) self.fp.seek(self.pos)
data = self.fp.read(length) data = self.fp.read(length)
self.pos = self.fp.tell() self.pos = self.fp.tell()
return data return data
def readline(self, length = None): def readline(self, length = None):
if self.pos >= self.stop: if self.pos >= self.stop:
return '' return ''
if length is None: if length is None:
length = self.stop - self.pos length = self.stop - self.pos
self.fp.seek(self.pos) self.fp.seek(self.pos)
data = self.fp.readline(length) data = self.fp.readline(length)
self.pos = self.fp.tell() self.pos = self.fp.tell()
return data return data
def readlines(self, sizehint = -1): def readlines(self, sizehint = -1):
lines = [] lines = []
while 1: while 1:
line = self.readline() line = self.readline()
if not line: if not line:
break break
lines.append(line) lines.append(line)
if sizehint >= 0: if sizehint >= 0:
sizehint = sizehint - len(line) sizehint = sizehint - len(line)
if sizehint <= 0: if sizehint <= 0:
break break
return lines return lines
def tell(self): def tell(self):
return self.pos - self.start return self.pos - self.start
def seek(self, pos, whence=0): def seek(self, pos, whence=0):
if whence == 0: if whence == 0:
self.pos = self.start + pos self.pos = self.start + pos
elif whence == 1: elif whence == 1:
self.pos = self.pos + pos self.pos = self.pos + pos
elif whence == 2: elif whence == 2:
self.pos = self.stop + pos self.pos = self.stop + pos
def close(self): def close(self):
del self.fp del self.fp
class UnixMailbox(_Mailbox): class UnixMailbox(_Mailbox):
def _search_start(self): def _search_start(self):
while 1: while 1:
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
raise EOFError raise EOFError
if line[:5] == 'From ' and self._isrealfromline(line): if line[:5] == 'From ' and self._isrealfromline(line):
return return
def _search_end(self): def _search_end(self):
while 1: while 1:
pos = self.fp.tell() pos = self.fp.tell()
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
return return
if line[:5] == 'From ' and self._isrealfromline(line): if line[:5] == 'From ' and self._isrealfromline(line):
self.fp.seek(pos) self.fp.seek(pos)
return return
# An overridable mechanism to test for From-line-ness. # An overridable mechanism to test for From-line-ness.
# You can either specify a different regular expression # You can either specify a different regular expression
# or define a whole new _isrealfromline() method. # or define a whole new _isrealfromline() method.
# Note that this only gets called for lines starting with # Note that this only gets called for lines starting with
# the 5 characters "From ". # the 5 characters "From ".
_fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \ _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$" r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
_regexp = None _regexp = None
def _isrealfromline(self, line): def _isrealfromline(self, line):
if not self._regexp: if not self._regexp:
import re import re
self._regexp = re.compile(self._fromlinepattern) self._regexp = re.compile(self._fromlinepattern)
return self._regexp.match(line) return self._regexp.match(line)
class MmdfMailbox(_Mailbox): class MmdfMailbox(_Mailbox):
def _search_start(self): def _search_start(self):
while 1: while 1:
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
raise EOFError raise EOFError
if line[:5] == '\001\001\001\001\n': if line[:5] == '\001\001\001\001\n':
return return
def _search_end(self): def _search_end(self):
while 1: while 1:
pos = self.fp.tell() pos = self.fp.tell()
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
return return
if line == '\001\001\001\001\n': if line == '\001\001\001\001\n':
self.fp.seek(pos) self.fp.seek(pos)
return return
class MHMailbox: class MHMailbox:
def __init__(self, dirname): def __init__(self, dirname):
import re import re
pat = re.compile('^[0-9][0-9]*$') pat = re.compile('^[0-9][0-9]*$')
self.dirname = dirname self.dirname = dirname
files = os.listdir(self.dirname) files = os.listdir(self.dirname)
self.boxes = [] self.boxes = []
for f in files: for f in files:
if pat.match(f): if pat.match(f):
self.boxes.append(f) self.boxes.append(f)
def next(self): def next(self):
if not self.boxes: if not self.boxes:
return None return None
fn = self.boxes[0] fn = self.boxes[0]
del self.boxes[0] del self.boxes[0]
fp = open(os.path.join(self.dirname, fn)) fp = open(os.path.join(self.dirname, fn))
return rfc822.Message(fp) return rfc822.Message(fp)
class Maildir: class Maildir:
# Qmail directory mailbox # Qmail directory mailbox
def __init__(self, dirname): def __init__(self, dirname):
import string import string
self.dirname = dirname self.dirname = dirname
self.boxes = [] self.boxes = []
# check for new mail # check for new mail
newdir = os.path.join(self.dirname, 'new') newdir = os.path.join(self.dirname, 'new')
for file in os.listdir(newdir): for file in os.listdir(newdir):
if len(string.split(file, '.')) > 2: if len(string.split(file, '.')) > 2:
self.boxes.append(os.path.join(newdir, file)) self.boxes.append(os.path.join(newdir, file))
# Now check for current mail in this maildir # Now check for current mail in this maildir
curdir = os.path.join(self.dirname, 'cur') curdir = os.path.join(self.dirname, 'cur')
for file in os.listdir(curdir): for file in os.listdir(curdir):
if len(string.split(file, '.')) > 2: if len(string.split(file, '.')) > 2:
self.boxes.append(os.path.join(curdir, file)) self.boxes.append(os.path.join(curdir, file))
def next(self): def next(self):
if not self.boxes: if not self.boxes:
return None return None
fn = self.boxes[0] fn = self.boxes[0]
del self.boxes[0] del self.boxes[0]
fp = open(os.path.join(self.dirname, fn)) fp = open(os.path.join(self.dirname, fn))
return rfc822.Message(fp) return rfc822.Message(fp)
class BabylMailbox(_Mailbox): class BabylMailbox(_Mailbox):
def _search_start(self): def _search_start(self):
while 1: while 1:
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
raise EOFError raise EOFError
if line == '*** EOOH ***\n': if line == '*** EOOH ***\n':
return return
def _search_end(self): def _search_end(self):
while 1: while 1:
pos = self.fp.tell() pos = self.fp.tell()
line = self.fp.readline() line = self.fp.readline()
if not line: if not line:
return return
if line == '\037\014\n': if line == '\037\014\n':
self.fp.seek(pos) self.fp.seek(pos)
return return
def _test(): def _test():
import time import time
import sys import sys
import string import string
import os import os
args = sys.argv[1:] args = sys.argv[1:]
if not args: if not args:
for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER': for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
if os.environ.has_key(key): if os.environ.has_key(key):
mbox = os.environ[key] mbox = os.environ[key]
break break
else: else:
print "$MAIL, $LOGNAME nor $USER set -- who are you?" print "$MAIL, $LOGNAME nor $USER set -- who are you?"
return return
else: else:
mbox = args[0] mbox = args[0]
if mbox[:1] == '+': if mbox[:1] == '+':
mbox = os.environ['HOME'] + '/Mail/' + mbox[1:] mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
elif not '/' in mbox: elif not '/' in mbox:
mbox = '/usr/mail/' + mbox mbox = '/usr/mail/' + mbox
if os.path.isdir(mbox): if os.path.isdir(mbox):
if os.path.isdir(os.path.join(mbox, 'cur')): if os.path.isdir(os.path.join(mbox, 'cur')):
mb = Maildir(mbox) mb = Maildir(mbox)
else: else:
mb = MHMailbox(mbox) mb = MHMailbox(mbox)
else: else:
fp = open(mbox, 'r') fp = open(mbox, 'r')
mb = UnixMailbox(fp) mb = UnixMailbox(fp)
msgs = [] msgs = []
while 1: while 1:
msg = mb.next() msg = mb.next()
if msg is None: if msg is None:
break break
msgs.append(msg) msgs.append(msg)
if len(args) <= 1: if len(args) <= 1:
msg.fp = None msg.fp = None
if len(args) > 1: if len(args) > 1:
num = string.atoi(args[1]) num = string.atoi(args[1])
print 'Message %d body:'%num print 'Message %d body:'%num
msg = msgs[num-1] msg = msgs[num-1]
msg.rewindbody() msg.rewindbody()
sys.stdout.write(msg.fp.read()) sys.stdout.write(msg.fp.read())
else: else:
print 'Mailbox',mbox,'has',len(msgs),'messages:' print 'Mailbox',mbox,'has',len(msgs),'messages:'
for msg in msgs: for msg in msgs:
f = msg.getheader('from') or "" f = msg.getheader('from') or ""
s = msg.getheader('subject') or "" s = msg.getheader('subject') or ""
d = msg.getheader('date') or "" d = msg.getheader('date') or ""
print '%20.20s %18.18s %-30.30s'%(f, d[5:], s) print '%20.20s %18.18s %-30.30s'%(f, d[5:], s)
if __name__ == '__main__': if __name__ == '__main__':
_test() _test()
...@@ -30,8 +30,8 @@ import urllib ...@@ -30,8 +30,8 @@ import urllib
knownfiles = [ knownfiles = [
"/usr/local/etc/httpd/conf/mime.types", "/usr/local/etc/httpd/conf/mime.types",
"/usr/local/lib/netscape/mime.types", "/usr/local/lib/netscape/mime.types",
"/usr/local/etc/httpd/conf/mime.types", # Apache 1.2 "/usr/local/etc/httpd/conf/mime.types", # Apache 1.2
"/usr/local/etc/mime.types", # Apache 1.3 "/usr/local/etc/mime.types", # Apache 1.3
] ]
inited = 0 inited = 0
...@@ -56,24 +56,24 @@ def guess_type(url): ...@@ -56,24 +56,24 @@ def guess_type(url):
init() init()
scheme, url = urllib.splittype(url) scheme, url = urllib.splittype(url)
if scheme == 'data': if scheme == 'data':
# syntax of data URLs: # syntax of data URLs:
# dataurl := "data:" [ mediatype ] [ ";base64" ] "," data # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
# mediatype := [ type "/" subtype ] *( ";" parameter ) # mediatype := [ type "/" subtype ] *( ";" parameter )
# data := *urlchar # data := *urlchar
# parameter := attribute "=" value # parameter := attribute "=" value
# type/subtype defaults to "text/plain" # type/subtype defaults to "text/plain"
comma = string.find(url, ',') comma = string.find(url, ',')
if comma < 0: if comma < 0:
# bad data URL # bad data URL
return None, None return None, None
semi = string.find(url, ';', 0, comma) semi = string.find(url, ';', 0, comma)
if semi >= 0: if semi >= 0:
type = url[:semi] type = url[:semi]
else: else:
type = url[:comma] type = url[:comma]
if '=' in type or '/' not in type: if '=' in type or '/' not in type:
type = 'text/plain' type = 'text/plain'
return type, None # never compressed, so encoding is None return type, None # never compressed, so encoding is None
base, ext = posixpath.splitext(url) base, ext = posixpath.splitext(url)
while suffix_map.has_key(ext): while suffix_map.has_key(ext):
base, ext = posixpath.splitext(base + suffix_map[ext]) base, ext = posixpath.splitext(base + suffix_map[ext])
......
...@@ -332,11 +332,11 @@ class Message: ...@@ -332,11 +332,11 @@ class Message:
""" """
raw = [] raw = []
for h in self.getallmatchingheaders(name): for h in self.getallmatchingheaders(name):
if h[0] in ' \t': if h[0] in ' \t':
raw.append(h) raw.append(h)
else: else:
if raw: if raw:
raw.append(', ') raw.append(', ')
i = string.find(h, ':') i = string.find(h, ':')
if i > 0: if i > 0:
addr = h[i+1:] addr = h[i+1:]
...@@ -786,7 +786,7 @@ def dump_address_pair(pair): ...@@ -786,7 +786,7 @@ def dump_address_pair(pair):
_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', _monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
'aug', 'sep', 'oct', 'nov', 'dec', 'aug', 'sep', 'oct', 'nov', 'dec',
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'january', 'february', 'march', 'april', 'may', 'june', 'july',
'august', 'september', 'october', 'november', 'december'] 'august', 'september', 'october', 'november', 'december']
_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] _daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
...@@ -837,16 +837,16 @@ def parsedate_tz(data): ...@@ -837,16 +837,16 @@ def parsedate_tz(data):
mm = _monthnames.index(mm)+1 mm = _monthnames.index(mm)+1
if mm > 12: mm = mm - 12 if mm > 12: mm = mm - 12
if dd[-1] == ',': if dd[-1] == ',':
dd = dd[:-1] dd = dd[:-1]
i = string.find(yy, ':') i = string.find(yy, ':')
if i > 0: if i > 0:
yy, tm = tm, yy yy, tm = tm, yy
if yy[-1] == ',': if yy[-1] == ',':
yy = yy[:-1] yy = yy[:-1]
if yy[0] not in string.digits: if yy[0] not in string.digits:
yy, tz = tz, yy yy, tz = tz, yy
if tm[-1] == ',': if tm[-1] == ',':
tm = tm[:-1] tm = tm[:-1]
tm = string.splitfields(tm, ':') tm = string.splitfields(tm, ':')
if len(tm) == 2: if len(tm) == 2:
[thh, tmm] = tm [thh, tmm] = tm
......
...@@ -10,19 +10,19 @@ are ordinary strings. ...@@ -10,19 +10,19 @@ are ordinary strings.
To summarize the interface (key is a string, data is an arbitrary To summarize the interface (key is a string, data is an arbitrary
object): object):
import shelve import shelve
d = shelve.open(filename) # open, with (g)dbm filename -- no suffix d = shelve.open(filename) # open, with (g)dbm filename -- no suffix
d[key] = data # store data at key (overwrites old data if d[key] = data # store data at key (overwrites old data if
# using an existing key) # using an existing key)
data = d[key] # retrieve data at key (raise KeyError if no data = d[key] # retrieve data at key (raise KeyError if no
# such key) # such key)
del d[key] # delete data stored at key (raises KeyError del d[key] # delete data stored at key (raises KeyError
# if no such key) # if no such key)
flag = d.has_key(key) # true if the key exists flag = d.has_key(key) # true if the key exists
list = d.keys() # a list of all existing keys (slow!) list = d.keys() # a list of all existing keys (slow!)
d.close() # close it d.close() # close it
Dependent on the implementation, closing a persistent dictionary may Dependent on the implementation, closing a persistent dictionary may
or may not be necessary to flush changes to disk. or may not be necessary to flush changes to disk.
...@@ -31,127 +31,127 @@ or may not be necessary to flush changes to disk. ...@@ -31,127 +31,127 @@ or may not be necessary to flush changes to disk.
# Try using cPickle and cStringIO if available. # Try using cPickle and cStringIO if available.
try: try:
from cPickle import Pickler, Unpickler from cPickle import Pickler, Unpickler
except ImportError: except ImportError:
from pickle import Pickler, Unpickler from pickle import Pickler, Unpickler
try: try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError: except ImportError:
from StringIO import StringIO from StringIO import StringIO
class Shelf: class Shelf:
"""Base class for shelf implementations. """Base class for shelf implementations.
This is initialized with a dictionary-like object. This is initialized with a dictionary-like object.
See the module's __doc__ string for an overview of the interface. See the module's __doc__ string for an overview of the interface.
""" """
def __init__(self, dict): def __init__(self, dict):
self.dict = dict self.dict = dict
def keys(self): def keys(self):
return self.dict.keys() return self.dict.keys()
def __len__(self): def __len__(self):
return len(self.dict) return len(self.dict)
def has_key(self, key): def has_key(self, key):
return self.dict.has_key(key) return self.dict.has_key(key)
def get(self, key, default=None): def get(self, key, default=None):
if self.dict.has_key(key): if self.dict.has_key(key):
return self[key] return self[key]
return default return default
def __getitem__(self, key): def __getitem__(self, key):
f = StringIO(self.dict[key]) f = StringIO(self.dict[key])
return Unpickler(f).load() return Unpickler(f).load()
def __setitem__(self, key, value): def __setitem__(self, key, value):
f = StringIO() f = StringIO()
p = Pickler(f) p = Pickler(f)
p.dump(value) p.dump(value)
self.dict[key] = f.getvalue() self.dict[key] = f.getvalue()
def __delitem__(self, key): def __delitem__(self, key):
del self.dict[key] del self.dict[key]
def close(self): def close(self):
try: try:
self.dict.close() self.dict.close()
except: except:
pass pass
self.dict = 0 self.dict = 0
def __del__(self): def __del__(self):
self.close() self.close()
def sync(self): def sync(self):
if hasattr(self.dict, 'sync'): if hasattr(self.dict, 'sync'):
self.dict.sync() self.dict.sync()
class BsdDbShelf(Shelf): class BsdDbShelf(Shelf):
"""Shelf implementation using the "BSD" db interface. """Shelf implementation using the "BSD" db interface.
This adds methods first(), next(), previous(), last() and This adds methods first(), next(), previous(), last() and
set_location() that have no counterpart in [g]dbm databases. set_location() that have no counterpart in [g]dbm databases.
The actual database must be opened using one of the "bsddb" The actual database must be opened using one of the "bsddb"
modules "open" routines (i.e. bsddb.hashopen, bsddb.btopen or modules "open" routines (i.e. bsddb.hashopen, bsddb.btopen or
bsddb.rnopen) and passed to the constructor. bsddb.rnopen) and passed to the constructor.
See the module's __doc__ string for an overview of the interface. See the module's __doc__ string for an overview of the interface.
""" """
def __init__(self, dict): def __init__(self, dict):
Shelf.__init__(self, dict) Shelf.__init__(self, dict)
def set_location(self, key): def set_location(self, key):
(key, value) = self.dict.set_location(key) (key, value) = self.dict.set_location(key)
f = StringIO(value) f = StringIO(value)
return (key, Unpickler(f).load()) return (key, Unpickler(f).load())
def next(self): def next(self):
(key, value) = self.dict.next() (key, value) = self.dict.next()
f = StringIO(value) f = StringIO(value)
return (key, Unpickler(f).load()) return (key, Unpickler(f).load())
def previous(self): def previous(self):
(key, value) = self.dict.previous() (key, value) = self.dict.previous()
f = StringIO(value) f = StringIO(value)
return (key, Unpickler(f).load()) return (key, Unpickler(f).load())
def first(self): def first(self):
(key, value) = self.dict.first() (key, value) = self.dict.first()
f = StringIO(value) f = StringIO(value)
return (key, Unpickler(f).load()) return (key, Unpickler(f).load())
def last(self): def last(self):
(key, value) = self.dict.last() (key, value) = self.dict.last()
f = StringIO(value) f = StringIO(value)
return (key, Unpickler(f).load()) return (key, Unpickler(f).load())
class DbfilenameShelf(Shelf): class DbfilenameShelf(Shelf):
"""Shelf implementation using the "anydbm" generic dbm interface. """Shelf implementation using the "anydbm" generic dbm interface.
This is initialized with the filename for the dbm database. This is initialized with the filename for the dbm database.
See the module's __doc__ string for an overview of the interface. See the module's __doc__ string for an overview of the interface.
""" """
def __init__(self, filename, flag='c'): def __init__(self, filename, flag='c'):
import anydbm import anydbm
Shelf.__init__(self, anydbm.open(filename, flag)) Shelf.__init__(self, anydbm.open(filename, flag))
def open(filename, flag='c'): def open(filename, flag='c'):
"""Open a persistent dictionary for reading and writing. """Open a persistent dictionary for reading and writing.
Argument is the filename for the dbm database. Argument is the filename for the dbm database.
See the module's __doc__ string for an overview of the interface. See the module's __doc__ string for an overview of the interface.
""" """
return DbfilenameShelf(filename, flag) return DbfilenameShelf(filename, flag)
...@@ -199,10 +199,10 @@ def atof(s): ...@@ -199,10 +199,10 @@ def atof(s):
""" """
if type(s) == _StringType: if type(s) == _StringType:
return _float(s) return _float(s)
else: else:
raise TypeError('argument 1: expected string, %s found' % raise TypeError('argument 1: expected string, %s found' %
type(s).__name__) type(s).__name__)
# Convert string to integer # Convert string to integer
def atoi(*args): def atoi(*args):
...@@ -217,18 +217,18 @@ def atoi(*args): ...@@ -217,18 +217,18 @@ def atoi(*args):
""" """
try: try:
s = args[0] s = args[0]
except IndexError: except IndexError:
raise TypeError('function requires at least 1 argument: %d given' % raise TypeError('function requires at least 1 argument: %d given' %
len(args)) len(args))
# Don't catch type error resulting from too many arguments to int(). The # Don't catch type error resulting from too many arguments to int(). The
# error message isn't compatible but the error type is, and this function # error message isn't compatible but the error type is, and this function
# is complicated enough already. # is complicated enough already.
if type(s) == _StringType: if type(s) == _StringType:
return _apply(_int, args) return _apply(_int, args)
else: else:
raise TypeError('argument 1: expected string, %s found' % raise TypeError('argument 1: expected string, %s found' %
type(s).__name__) type(s).__name__)
# Convert string to long integer # Convert string to long integer
...@@ -245,18 +245,18 @@ def atol(*args): ...@@ -245,18 +245,18 @@ def atol(*args):
""" """
try: try:
s = args[0] s = args[0]
except IndexError: except IndexError:
raise TypeError('function requires at least 1 argument: %d given' % raise TypeError('function requires at least 1 argument: %d given' %
len(args)) len(args))
# Don't catch type error resulting from too many arguments to long(). The # Don't catch type error resulting from too many arguments to long(). The
# error message isn't compatible but the error type is, and this function # error message isn't compatible but the error type is, and this function
# is complicated enough already. # is complicated enough already.
if type(s) == _StringType: if type(s) == _StringType:
return _apply(_long, args) return _apply(_long, args)
else: else:
raise TypeError('argument 1: expected string, %s found' % raise TypeError('argument 1: expected string, %s found' %
type(s).__name__) type(s).__name__)
# Left-justify a string # Left-justify a string
...@@ -298,8 +298,8 @@ def center(s, width): ...@@ -298,8 +298,8 @@ def center(s, width):
if n <= 0: return s if n <= 0: return s
half = n/2 half = n/2
if n%2 and width%2: if n%2 and width%2:
# This ensures that center(center(s, i), j) = center(s, j) # This ensures that center(center(s, i), j) = center(s, j)
half = half+1 half = half+1
return ' '*half + s + ' '*(n-half) return ' '*half + s + ' '*(n-half)
# Zero-fill a number, e.g., (12, 3) --> '012' and (-3, 3) --> '-03' # Zero-fill a number, e.g., (12, 3) --> '012' and (-3, 3) --> '-03'
...@@ -318,7 +318,7 @@ def zfill(x, width): ...@@ -318,7 +318,7 @@ def zfill(x, width):
if n >= width: return s if n >= width: return s
sign = '' sign = ''
if s[0] in ('-', '+'): if s[0] in ('-', '+'):
sign, s = s[0], s[1:] sign, s = s[0], s[1:]
return sign + '0'*(width-n) + s return sign + '0'*(width-n) + s
# Expand tabs in a string. # Expand tabs in a string.
...@@ -333,12 +333,12 @@ def expandtabs(s, tabsize=8): ...@@ -333,12 +333,12 @@ def expandtabs(s, tabsize=8):
""" """
res = line = '' res = line = ''
for c in s: for c in s:
if c == '\t': if c == '\t':
c = ' '*(tabsize - len(line) % tabsize) c = ' '*(tabsize - len(line) % tabsize)
line = line + c line = line + c
if c == '\n': if c == '\n':
res = res + line res = res + line
line = '' line = ''
return res + line return res + line
# Character translation through look-up table. # Character translation through look-up table.
...@@ -387,14 +387,14 @@ def maketrans(fromstr, tostr): ...@@ -387,14 +387,14 @@ def maketrans(fromstr, tostr):
""" """
if len(fromstr) != len(tostr): if len(fromstr) != len(tostr):
raise ValueError, "maketrans arguments must have same length" raise ValueError, "maketrans arguments must have same length"
global _idmapL global _idmapL
if not _idmapL: if not _idmapL:
_idmapL = map(None, _idmap) _idmapL = map(None, _idmap)
L = _idmapL[:] L = _idmapL[:]
fromstr = map(ord, fromstr) fromstr = map(ord, fromstr)
for i in range(len(fromstr)): for i in range(len(fromstr)):
L[fromstr[i]] = tostr[i] L[fromstr[i]] = tostr[i]
return joinfields(L, "") return joinfields(L, "")
# Substring replacement (global) # Substring replacement (global)
...@@ -428,4 +428,4 @@ try: ...@@ -428,4 +428,4 @@ try:
from strop import maketrans, lowercase, uppercase, whitespace from strop import maketrans, lowercase, uppercase, whitespace
letters = lowercase + uppercase letters = lowercase + uppercase
except ImportError: except ImportError:
pass # Use the original versions pass # Use the original versions
...@@ -148,57 +148,57 @@ class URLError(IOError): ...@@ -148,57 +148,57 @@ class URLError(IOError):
# URLError is a sub-type of IOError, but it doesn't share any of # URLError is a sub-type of IOError, but it doesn't share any of
# the implementation. need to override __init__ and __str__ # the implementation. need to override __init__ and __str__
def __init__(self, reason): def __init__(self, reason):
self.reason = reason self.reason = reason
def __str__(self): def __str__(self):
return '<urlopen error %s>' % self.reason return '<urlopen error %s>' % self.reason
class HTTPError(URLError, addinfourl): class HTTPError(URLError, addinfourl):
"""Raised when HTTP error occurs, but also acts like non-error return""" """Raised when HTTP error occurs, but also acts like non-error return"""
def __init__(self, url, code, msg, hdrs, fp): def __init__(self, url, code, msg, hdrs, fp):
addinfourl.__init__(self, fp, hdrs, url) addinfourl.__init__(self, fp, hdrs, url)
self.code = code self.code = code
self.msg = msg self.msg = msg
self.hdrs = hdrs self.hdrs = hdrs
self.fp = fp self.fp = fp
# XXX # XXX
self.filename = url self.filename = url
def __str__(self): def __str__(self):
return 'HTTP Error %s: %s' % (self.code, self.msg) return 'HTTP Error %s: %s' % (self.code, self.msg)
def __del__(self): def __del__(self):
# XXX is this safe? what if user catches exception, then # XXX is this safe? what if user catches exception, then
# extracts fp and discards exception? # extracts fp and discards exception?
self.fp.close() self.fp.close()
class GopherError(URLError): class GopherError(URLError):
pass pass
class Request: class Request:
def __init__(self, url, data=None, headers={}): def __init__(self, url, data=None, headers={}):
# unwrap('<URL:type://host/path>') --> 'type://host/path' # unwrap('<URL:type://host/path>') --> 'type://host/path'
self.__original = unwrap(url) self.__original = unwrap(url)
self.type = None self.type = None
# self.__r_type is what's left after doing the splittype # self.__r_type is what's left after doing the splittype
self.host = None self.host = None
self.port = None self.port = None
self.data = data self.data = data
self.headers = {} self.headers = {}
self.headers.update(headers) self.headers.update(headers)
def __getattr__(self, attr): def __getattr__(self, attr):
# XXX this is a fallback mechanism to guard against these # XXX this is a fallback mechanism to guard against these
# methods getting called in a non-standard order. this may be # methods getting called in a non-standard order. this may be
# too complicated and/or unnecessary. # too complicated and/or unnecessary.
# XXX should the __r_XXX attributes be public? # XXX should the __r_XXX attributes be public?
if attr[:12] == '_Request__r_': if attr[:12] == '_Request__r_':
name = attr[12:] name = attr[12:]
if hasattr(Request, 'get_' + name): if hasattr(Request, 'get_' + name):
getattr(self, 'get_' + name)() getattr(self, 'get_' + name)()
return getattr(self, attr) return getattr(self, attr)
raise AttributeError, attr raise AttributeError, attr
def add_data(self, data): def add_data(self, data):
self.data = data self.data = data
...@@ -213,34 +213,34 @@ class Request: ...@@ -213,34 +213,34 @@ class Request:
return self.__original return self.__original
def get_type(self): def get_type(self):
if self.type is None: if self.type is None:
self.type, self.__r_type = splittype(self.__original) self.type, self.__r_type = splittype(self.__original)
return self.type return self.type
def get_host(self): def get_host(self):
if self.host is None: if self.host is None:
self.host, self.__r_host = splithost(self.__r_type) self.host, self.__r_host = splithost(self.__r_type)
if self.host: if self.host:
self.host = unquote(self.host) self.host = unquote(self.host)
return self.host return self.host
def get_selector(self): def get_selector(self):
return self.__r_host return self.__r_host
def set_proxy(self, proxy): def set_proxy(self, proxy):
self.__proxy = proxy self.__proxy = proxy
# XXX this code is based on urllib, but it doesn't seem # XXX this code is based on urllib, but it doesn't seem
# correct. specifically, if the proxy has a port number then # correct. specifically, if the proxy has a port number then
# splittype will return the hostname as the type and the port # splittype will return the hostname as the type and the port
# will be include with everything else # will be include with everything else
self.type, self.__r_type = splittype(self.__proxy) self.type, self.__r_type = splittype(self.__proxy)
self.host, XXX = splithost(self.__r_type) self.host, XXX = splithost(self.__r_type)
self.host = unquote(self.host) self.host = unquote(self.host)
self.__r_host = self.__original self.__r_host = self.__original
def add_header(self, key, val): def add_header(self, key, val):
# useful for something like authentication # useful for something like authentication
self.headers[key] = val self.headers[key] = val
class OpenerDirector: class OpenerDirector:
def __init__(self): def __init__(self):
...@@ -302,21 +302,21 @@ class OpenerDirector: ...@@ -302,21 +302,21 @@ class OpenerDirector:
return result return result
def open(self, fullurl, data=None): def open(self, fullurl, data=None):
# accept a URL or a Request object # accept a URL or a Request object
if type(fullurl) == types.StringType: if type(fullurl) == types.StringType:
req = Request(fullurl, data) req = Request(fullurl, data)
else: else:
req = fullurl req = fullurl
if data is not None: if data is not None:
req.add_data(data) req.add_data(data)
assert isinstance(req, Request) # really only care about interface assert isinstance(req, Request) # really only care about interface
result = self._call_chain(self.handle_open, 'default', result = self._call_chain(self.handle_open, 'default',
'default_open', req) 'default_open', req)
if result: if result:
return result return result
type_ = req.get_type() type_ = req.get_type()
result = self._call_chain(self.handle_open, type_, type_ + \ result = self._call_chain(self.handle_open, type_, type_ + \
'_open', req) '_open', req)
if result: if result:
...@@ -350,11 +350,11 @@ def is_callable(obj): ...@@ -350,11 +350,11 @@ def is_callable(obj):
# not quite like builtin callable (which I didn't know existed), # not quite like builtin callable (which I didn't know existed),
# not entirely sure it needs to be different # not entirely sure it needs to be different
if type(obj) in (types.BuiltinFunctionType, if type(obj) in (types.BuiltinFunctionType,
types.BuiltinMethodType, types.LambdaType, types.BuiltinMethodType, types.LambdaType,
types.MethodType): types.MethodType):
return 1 return 1
if type(obj) == types.InstanceType: if type(obj) == types.InstanceType:
return hasattr(obj, '__call__') return hasattr(obj, '__call__')
return 0 return 0
def get_methods(inst): def get_methods(inst):
...@@ -370,8 +370,8 @@ def get_methods(inst): ...@@ -370,8 +370,8 @@ def get_methods(inst):
if type(attr) == types.UnboundMethodType: if type(attr) == types.UnboundMethodType:
methods[name] = 1 methods[name] = 1
for name in dir(inst): for name in dir(inst):
if is_callable(getattr(inst, name)): if is_callable(getattr(inst, name)):
methods[name] = 1 methods[name] = 1
return methods.keys() return methods.keys()
# XXX probably also want an abstract factory that knows things like # XXX probably also want an abstract factory that knows things like
...@@ -423,7 +423,7 @@ class BaseHandler: ...@@ -423,7 +423,7 @@ class BaseHandler:
class HTTPDefaultErrorHandler(BaseHandler): class HTTPDefaultErrorHandler(BaseHandler):
def http_error_default(self, req, fp, code, msg, hdrs): def http_error_default(self, req, fp, code, msg, hdrs):
raise HTTPError(req.get_full_url(), code, msg, hdrs, fp) raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
class HTTPRedirectHandler(BaseHandler): class HTTPRedirectHandler(BaseHandler):
# Implementation note: To avoid the server sending us into an # Implementation note: To avoid the server sending us into an
...@@ -461,114 +461,114 @@ class HTTPRedirectHandler(BaseHandler): ...@@ -461,114 +461,114 @@ class HTTPRedirectHandler(BaseHandler):
class ProxyHandler(BaseHandler): class ProxyHandler(BaseHandler):
def __init__(self, proxies=None): def __init__(self, proxies=None):
if proxies is None: if proxies is None:
proxies = getproxies() proxies = getproxies()
assert hasattr(proxies, 'has_key'), "proxies must be a mapping" assert hasattr(proxies, 'has_key'), "proxies must be a mapping"
self.proxies = proxies self.proxies = proxies
for type, url in proxies.items(): for type, url in proxies.items():
setattr(self, '%s_open' % type, setattr(self, '%s_open' % type,
lambda r, proxy=url, type=type, meth=self.proxy_open: \ lambda r, proxy=url, type=type, meth=self.proxy_open: \
meth(r, proxy, type)) meth(r, proxy, type))
def proxy_open(self, req, proxy, type): def proxy_open(self, req, proxy, type):
orig_type = req.get_type() orig_type = req.get_type()
req.set_proxy(proxy) req.set_proxy(proxy)
if orig_type == type: if orig_type == type:
# let other handlers take care of it # let other handlers take care of it
# XXX this only makes sense if the proxy is before the # XXX this only makes sense if the proxy is before the
# other handlers # other handlers
return None return None
else: else:
# need to start over, because the other handlers don't # need to start over, because the other handlers don't
# grok the proxy's URL type # grok the proxy's URL type
return self.parent.open(req) return self.parent.open(req)
# feature suggested by Duncan Booth # feature suggested by Duncan Booth
# XXX custom is not a good name # XXX custom is not a good name
class CustomProxy: class CustomProxy:
# either pass a function to the constructor or override handle # either pass a function to the constructor or override handle
def __init__(self, proto, func=None, proxy_addr=None): def __init__(self, proto, func=None, proxy_addr=None):
self.proto = proto self.proto = proto
self.func = func self.func = func
self.addr = proxy_addr self.addr = proxy_addr
def handle(self, req): def handle(self, req):
if self.func and self.func(req): if self.func and self.func(req):
return 1 return 1
def get_proxy(self): def get_proxy(self):
return self.addr return self.addr
class CustomProxyHandler(BaseHandler): class CustomProxyHandler(BaseHandler):
def __init__(self, *proxies): def __init__(self, *proxies):
self.proxies = {} self.proxies = {}
def proxy_open(self, req): def proxy_open(self, req):
proto = req.get_type() proto = req.get_type()
try: try:
proxies = self.proxies[proto] proxies = self.proxies[proto]
except KeyError: except KeyError:
return None return None
for p in proxies: for p in proxies:
if p.handle(req): if p.handle(req):
req.set_proxy(p.get_proxy()) req.set_proxy(p.get_proxy())
return self.parent.open(req) return self.parent.open(req)
return None return None
def do_proxy(self, p, req): def do_proxy(self, p, req):
p p
return self.parent.open(req) return self.parent.open(req)
def add_proxy(self, cpo): def add_proxy(self, cpo):
if self.proxies.has_key(cpo.proto): if self.proxies.has_key(cpo.proto):
self.proxies[cpo.proto].append(cpo) self.proxies[cpo.proto].append(cpo)
else: else:
self.proxies[cpo.proto] = [cpo] self.proxies[cpo.proto] = [cpo]
class HTTPPasswordMgr: class HTTPPasswordMgr:
def __init__(self): def __init__(self):
self.passwd = {} self.passwd = {}
def add_password(self, realm, uri, user, passwd): def add_password(self, realm, uri, user, passwd):
# uri could be a single URI or a sequence # uri could be a single URI or a sequence
if type(uri) == types.StringType: if type(uri) == types.StringType:
uri = [uri] uri = [uri]
uri = tuple(map(self.reduce_uri, uri)) uri = tuple(map(self.reduce_uri, uri))
if not self.passwd.has_key(realm): if not self.passwd.has_key(realm):
self.passwd[realm] = {} self.passwd[realm] = {}
self.passwd[realm][uri] = (user, passwd) self.passwd[realm][uri] = (user, passwd)
def find_user_password(self, realm, authuri): def find_user_password(self, realm, authuri):
domains = self.passwd.get(realm, {}) domains = self.passwd.get(realm, {})
authuri = self.reduce_uri(authuri) authuri = self.reduce_uri(authuri)
for uris, authinfo in domains.items(): for uris, authinfo in domains.items():
for uri in uris: for uri in uris:
if self.is_suburi(uri, authuri): if self.is_suburi(uri, authuri):
return authinfo return authinfo
return None, None return None, None
def reduce_uri(self, uri): def reduce_uri(self, uri):
"""Accept netloc or URI and extract only the netloc and path""" """Accept netloc or URI and extract only the netloc and path"""
parts = urlparse.urlparse(uri) parts = urlparse.urlparse(uri)
if parts[1]: if parts[1]:
return parts[1], parts[2] or '/' return parts[1], parts[2] or '/'
else: else:
return parts[2], '/' return parts[2], '/'
def is_suburi(self, base, test): def is_suburi(self, base, test):
"""Check if test is below base in a URI tree """Check if test is below base in a URI tree
Both args must be URIs in reduced form. Both args must be URIs in reduced form.
""" """
if base == test: if base == test:
return 1 return 1
if base[0] != test[0]: if base[0] != test[0]:
return 0 return 0
common = os.path.commonprefix((base[1], test[1])) common = os.path.commonprefix((base[1], test[1]))
if len(common) == len(base[1]): if len(common) == len(base[1]):
return 1 return 1
return 0 return 0
class HTTPBasicAuthHandler(BaseHandler): class HTTPBasicAuthHandler(BaseHandler):
rx = re.compile('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"') rx = re.compile('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"')
...@@ -579,15 +579,15 @@ class HTTPBasicAuthHandler(BaseHandler): ...@@ -579,15 +579,15 @@ class HTTPBasicAuthHandler(BaseHandler):
def __init__(self): def __init__(self):
self.passwd = HTTPPasswordMgr() self.passwd = HTTPPasswordMgr()
self.add_password = self.passwd.add_password self.add_password = self.passwd.add_password
self.__current_realm = None self.__current_realm = None
# if __current_realm is not None, then the server must have # if __current_realm is not None, then the server must have
# refused our name/password and is asking for authorization # refused our name/password and is asking for authorization
# again. must be careful to set it to None on successful # again. must be careful to set it to None on successful
# return. # return.
def http_error_401(self, req, fp, code, msg, headers): def http_error_401(self, req, fp, code, msg, headers):
# XXX could be mult. headers # XXX could be mult. headers
authreq = headers.get('www-authenticate', None) authreq = headers.get('www-authenticate', None)
if authreq: if authreq:
mo = HTTPBasicAuthHandler.rx.match(authreq) mo = HTTPBasicAuthHandler.rx.match(authreq)
...@@ -597,23 +597,23 @@ class HTTPBasicAuthHandler(BaseHandler): ...@@ -597,23 +597,23 @@ class HTTPBasicAuthHandler(BaseHandler):
return self.retry_http_basic_auth(req, realm) return self.retry_http_basic_auth(req, realm)
def retry_http_basic_auth(self, req, realm): def retry_http_basic_auth(self, req, realm):
if self.__current_realm is None: if self.__current_realm is None:
self.__current_realm = realm self.__current_realm = realm
else: else:
self.__current_realm = realm self.__current_realm = realm
return None return None
# XXX host isn't really the correct URI? # XXX host isn't really the correct URI?
host = req.get_host() host = req.get_host()
user,pw = self.passwd.find_user_password(realm, host) user,pw = self.passwd.find_user_password(realm, host)
if pw: if pw:
raw = "%s:%s" % (user, pw) raw = "%s:%s" % (user, pw)
auth = string.strip(base64.encodestring(raw)) auth = string.strip(base64.encodestring(raw))
req.add_header('Authorization', 'Basic %s' % auth) req.add_header('Authorization', 'Basic %s' % auth)
resp = self.parent.open(req) resp = self.parent.open(req)
self.__current_realm = None self.__current_realm = None
return resp return resp
else: else:
self.__current_realm = None self.__current_realm = None
return None return None
class HTTPDigestAuthHandler(BaseHandler): class HTTPDigestAuthHandler(BaseHandler):
...@@ -624,111 +624,111 @@ class HTTPDigestAuthHandler(BaseHandler): ...@@ -624,111 +624,111 @@ class HTTPDigestAuthHandler(BaseHandler):
""" """
def __init__(self): def __init__(self):
self.passwd = HTTPPasswordMgr() self.passwd = HTTPPasswordMgr()
self.add_password = self.passwd.add_password self.add_password = self.passwd.add_password
self.__current_realm = None self.__current_realm = None
def http_error_401(self, req, fp, code, msg, headers): def http_error_401(self, req, fp, code, msg, headers):
# XXX could be mult. headers # XXX could be mult. headers
authreq = headers.get('www-authenticate', None) authreq = headers.get('www-authenticate', None)
if authreq: if authreq:
kind = string.split(authreq)[0] kind = string.split(authreq)[0]
if kind == 'Digest': if kind == 'Digest':
return self.retry_http_digest_auth(req, authreq) return self.retry_http_digest_auth(req, authreq)
def retry_http_digest_auth(self, req, auth): def retry_http_digest_auth(self, req, auth):
token, challenge = string.split(auth, ' ', 1) token, challenge = string.split(auth, ' ', 1)
chal = parse_keqv_list(parse_http_list(challenge)) chal = parse_keqv_list(parse_http_list(challenge))
auth = self.get_authorization(req, chal) auth = self.get_authorization(req, chal)
if auth: if auth:
req.add_header('Authorization', 'Digest %s' % auth) req.add_header('Authorization', 'Digest %s' % auth)
resp = self.parent.open(req) resp = self.parent.open(req)
self.__current_realm = None self.__current_realm = None
return resp return resp
def get_authorization(self, req, chal): def get_authorization(self, req, chal):
try: try:
realm = chal['realm'] realm = chal['realm']
nonce = chal['nonce'] nonce = chal['nonce']
algorithm = chal.get('algorithm', 'MD5') algorithm = chal.get('algorithm', 'MD5')
# mod_digest doesn't send an opaque, even though it isn't # mod_digest doesn't send an opaque, even though it isn't
# supposed to be optional # supposed to be optional
opaque = chal.get('opaque', None) opaque = chal.get('opaque', None)
except KeyError: except KeyError:
return None return None
if self.__current_realm is None: if self.__current_realm is None:
self.__current_realm = realm self.__current_realm = realm
else: else:
self.__current_realm = realm self.__current_realm = realm
return None return None
H, KD = self.get_algorithm_impls(algorithm) H, KD = self.get_algorithm_impls(algorithm)
if H is None: if H is None:
return None return None
user, pw = self.passwd.find_user_password(realm, user, pw = self.passwd.find_user_password(realm,
req.get_full_url()) req.get_full_url())
if user is None: if user is None:
return None return None
# XXX not implemented yet # XXX not implemented yet
if req.has_data(): if req.has_data():
entdig = self.get_entity_digest(req.get_data(), chal) entdig = self.get_entity_digest(req.get_data(), chal)
else: else:
entdig = None entdig = None
A1 = "%s:%s:%s" % (user, realm, pw) A1 = "%s:%s:%s" % (user, realm, pw)
A2 = "%s:%s" % (req.has_data() and 'POST' or 'GET', A2 = "%s:%s" % (req.has_data() and 'POST' or 'GET',
# XXX selector: what about proxies and full urls # XXX selector: what about proxies and full urls
req.get_selector()) req.get_selector())
respdig = KD(H(A1), "%s:%s" % (nonce, H(A2))) respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
# XXX should the partial digests be encoded too? # XXX should the partial digests be encoded too?
base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
'response="%s"' % (user, realm, nonce, req.get_selector(), 'response="%s"' % (user, realm, nonce, req.get_selector(),
respdig) respdig)
if opaque: if opaque:
base = base + ', opaque="%s"' % opaque base = base + ', opaque="%s"' % opaque
if entdig: if entdig:
base = base + ', digest="%s"' % entdig base = base + ', digest="%s"' % entdig
if algorithm != 'MD5': if algorithm != 'MD5':
base = base + ', algorithm="%s"' % algorithm base = base + ', algorithm="%s"' % algorithm
return base return base
def get_algorithm_impls(self, algorithm): def get_algorithm_impls(self, algorithm):
# lambdas assume digest modules are imported at the top level # lambdas assume digest modules are imported at the top level
if algorithm == 'MD5': if algorithm == 'MD5':
H = lambda x, e=encode_digest:e(md5.new(x).digest()) H = lambda x, e=encode_digest:e(md5.new(x).digest())
elif algorithm == 'SHA': elif algorithm == 'SHA':
H = lambda x, e=encode_digest:e(sha.new(x).digest()) H = lambda x, e=encode_digest:e(sha.new(x).digest())
# XXX MD5-sess # XXX MD5-sess
KD = lambda s, d, H=H: H("%s:%s" % (s, d)) KD = lambda s, d, H=H: H("%s:%s" % (s, d))
return H, KD return H, KD
def get_entity_digest(self, data, chal): def get_entity_digest(self, data, chal):
# XXX not implemented yet # XXX not implemented yet
return None return None
def encode_digest(digest): def encode_digest(digest):
hexrep = [] hexrep = []
for c in digest: for c in digest:
n = (ord(c) >> 4) & 0xf n = (ord(c) >> 4) & 0xf
hexrep.append(hex(n)[-1]) hexrep.append(hex(n)[-1])
n = ord(c) & 0xf n = ord(c) & 0xf
hexrep.append(hex(n)[-1]) hexrep.append(hex(n)[-1])
return string.join(hexrep, '') return string.join(hexrep, '')
class HTTPHandler(BaseHandler): class HTTPHandler(BaseHandler):
def http_open(self, req): def http_open(self, req):
# XXX devise a new mechanism to specify user/password # XXX devise a new mechanism to specify user/password
host = req.get_host() host = req.get_host()
if not host: if not host:
raise URLError('no host given') raise URLError('no host given')
h = httplib.HTTP(host) # will parse host:port h = httplib.HTTP(host) # will parse host:port
## h.set_debuglevel(1) ## h.set_debuglevel(1)
if req.has_data(): if req.has_data():
data = req.get_data() data = req.get_data()
h.putrequest('POST', req.get_selector()) h.putrequest('POST', req.get_selector())
...@@ -740,8 +740,8 @@ class HTTPHandler(BaseHandler): ...@@ -740,8 +740,8 @@ class HTTPHandler(BaseHandler):
h.putheader('Host', host) h.putheader('Host', host)
for args in self.parent.addheaders: for args in self.parent.addheaders:
apply(h.putheader, args) apply(h.putheader, args)
for k, v in req.headers.items(): for k, v in req.headers.items():
h.putheader(k, v) h.putheader(k, v)
h.endheaders() h.endheaders()
if req.has_data(): if req.has_data():
h.send(data + '\r\n') h.send(data + '\r\n')
...@@ -761,17 +761,17 @@ class HTTPHandler(BaseHandler): ...@@ -761,17 +761,17 @@ class HTTPHandler(BaseHandler):
class UnknownHandler(BaseHandler): class UnknownHandler(BaseHandler):
def unknown_open(self, req): def unknown_open(self, req):
type = req.get_type() type = req.get_type()
raise URLError('unknown url type: %s' % type) raise URLError('unknown url type: %s' % type)
def parse_keqv_list(l): def parse_keqv_list(l):
"""Parse list of key=value strings where keys are not duplicated.""" """Parse list of key=value strings where keys are not duplicated."""
parsed = {} parsed = {}
for elt in l: for elt in l:
k, v = string.split(elt, '=', 1) k, v = string.split(elt, '=', 1)
if v[0] == '"' and v[-1] == '"': if v[0] == '"' and v[-1] == '"':
v = v[1:-1] v = v[1:-1]
parsed[k] = v parsed[k] = v
return parsed return parsed
def parse_http_list(s): def parse_http_list(s):
...@@ -789,104 +789,104 @@ def parse_http_list(s): ...@@ -789,104 +789,104 @@ def parse_http_list(s):
inquote = 0 inquote = 0
start = 0 start = 0
while i < end: while i < end:
cur = s[i:] cur = s[i:]
c = string.find(cur, ',') c = string.find(cur, ',')
q = string.find(cur, '"') q = string.find(cur, '"')
if c == -1: if c == -1:
list.append(s[start:]) list.append(s[start:])
break break
if q == -1: if q == -1:
if inquote: if inquote:
raise ValueError, "unbalanced quotes" raise ValueError, "unbalanced quotes"
else: else:
list.append(s[start:i+c]) list.append(s[start:i+c])
i = i + c + 1 i = i + c + 1
continue continue
if inquote: if inquote:
if q < c: if q < c:
list.append(s[start:i+c]) list.append(s[start:i+c])
i = i + c + 1 i = i + c + 1
start = i start = i
inquote = 0 inquote = 0
else: else:
i = i + q i = i + q
else: else:
if c < q: if c < q:
list.append(s[start:i+c]) list.append(s[start:i+c])
i = i + c + 1 i = i + c + 1
start = i start = i
else: else:
inquote = 1 inquote = 1
i = i + q + 1 i = i + q + 1
return map(string.strip, list) return map(string.strip, list)
class FileHandler(BaseHandler): class FileHandler(BaseHandler):
# Use local file or FTP depending on form of URL # Use local file or FTP depending on form of URL
def file_open(self, req): def file_open(self, req):
url = req.get_selector() url = req.get_selector()
if url[:2] == '//' and url[2:3] != '/': if url[:2] == '//' and url[2:3] != '/':
req.type = 'ftp' req.type = 'ftp'
return self.parent.open(req) return self.parent.open(req)
else: else:
return self.open_local_file(req) return self.open_local_file(req)
# names for the localhost # names for the localhost
names = None names = None
def get_names(self): def get_names(self):
if FileHandler.names is None: if FileHandler.names is None:
FileHandler.names = (socket.gethostbyname('localhost'), FileHandler.names = (socket.gethostbyname('localhost'),
socket.gethostbyname(socket.gethostname())) socket.gethostbyname(socket.gethostname()))
return FileHandler.names return FileHandler.names
# not entirely sure what the rules are here # not entirely sure what the rules are here
def open_local_file(self, req): def open_local_file(self, req):
mtype = mimetypes.guess_type(req.get_selector())[0] mtype = mimetypes.guess_type(req.get_selector())[0]
headers = mimetools.Message(StringIO('Content-Type: %s\n' \ headers = mimetools.Message(StringIO('Content-Type: %s\n' \
% (mtype or 'text/plain'))) % (mtype or 'text/plain')))
host = req.get_host() host = req.get_host()
file = req.get_selector() file = req.get_selector()
if host: if host:
host, port = splitport(host) host, port = splitport(host)
if not host or \ if not host or \
(not port and socket.gethostbyname(host) in self.get_names()): (not port and socket.gethostbyname(host) in self.get_names()):
return addinfourl(open(url2pathname(file), 'rb'), return addinfourl(open(url2pathname(file), 'rb'),
headers, 'file:'+file) headers, 'file:'+file)
raise URLError('file not on local host') raise URLError('file not on local host')
class FTPHandler(BaseHandler): class FTPHandler(BaseHandler):
def ftp_open(self, req): def ftp_open(self, req):
host = req.get_host() host = req.get_host()
if not host: if not host:
raise IOError, ('ftp error', 'no host given') raise IOError, ('ftp error', 'no host given')
# XXX handle custom username & password # XXX handle custom username & password
host = socket.gethostbyname(host) host = socket.gethostbyname(host)
host, port = splitport(host) host, port = splitport(host)
if port is None: if port is None:
port = ftplib.FTP_PORT port = ftplib.FTP_PORT
path, attrs = splitattr(req.get_selector()) path, attrs = splitattr(req.get_selector())
path = unquote(path) path = unquote(path)
dirs = string.splitfields(path, '/') dirs = string.splitfields(path, '/')
dirs, file = dirs[:-1], dirs[-1] dirs, file = dirs[:-1], dirs[-1]
if dirs and not dirs[0]: if dirs and not dirs[0]:
dirs = dirs[1:] dirs = dirs[1:]
user = passwd = '' # XXX user = passwd = '' # XXX
try: try:
fw = self.connect_ftp(user, passwd, host, port, dirs) fw = self.connect_ftp(user, passwd, host, port, dirs)
type = file and 'I' or 'D' type = file and 'I' or 'D'
for attr in attrs: for attr in attrs:
attr, value = splitattr(attr) attr, value = splitattr(attr)
if string.lower(attr) == 'type' and \ if string.lower(attr) == 'type' and \
value in ('a', 'A', 'i', 'I', 'd', 'D'): value in ('a', 'A', 'i', 'I', 'd', 'D'):
type = string.upper(value) type = string.upper(value)
fp, retrlen = fw.retrfile(file, type) fp, retrlen = fw.retrfile(file, type)
if retrlen is not None and retrlen >= 0: if retrlen is not None and retrlen >= 0:
sf = StringIO('Content-Length: %d\n' % retrlen) sf = StringIO('Content-Length: %d\n' % retrlen)
headers = mimetools.Message(sf) headers = mimetools.Message(sf)
else: else:
headers = noheaders() headers = noheaders()
return addinfourl(fp, headers, req.get_full_url()) return addinfourl(fp, headers, req.get_full_url())
except ftplib.all_errors, msg: except ftplib.all_errors, msg:
raise IOError, ('ftp error', msg), sys.exc_info()[2] raise IOError, ('ftp error', msg), sys.exc_info()[2]
def connect_ftp(self, user, passwd, host, port, dirs): def connect_ftp(self, user, passwd, host, port, dirs):
fw = ftpwrapper(user, passwd, host, port, dirs) fw = ftpwrapper(user, passwd, host, port, dirs)
...@@ -901,13 +901,13 @@ class CacheFTPHandler(FTPHandler): ...@@ -901,13 +901,13 @@ class CacheFTPHandler(FTPHandler):
self.timeout = {} self.timeout = {}
self.soonest = 0 self.soonest = 0
self.delay = 60 self.delay = 60
self.max_conns = 16 self.max_conns = 16
def setTimeout(self, t): def setTimeout(self, t):
self.delay = t self.delay = t
def setMaxConns(self, m): def setMaxConns(self, m):
self.max_conns = m self.max_conns = m
def connect_ftp(self, user, passwd, host, port, dirs): def connect_ftp(self, user, passwd, host, port, dirs):
key = user, passwd, host, port key = user, passwd, host, port
...@@ -916,11 +916,11 @@ class CacheFTPHandler(FTPHandler): ...@@ -916,11 +916,11 @@ class CacheFTPHandler(FTPHandler):
else: else:
self.cache[key] = ftpwrapper(user, passwd, host, port, dirs) self.cache[key] = ftpwrapper(user, passwd, host, port, dirs)
self.timeout[key] = time.time() + self.delay self.timeout[key] = time.time() + self.delay
self.check_cache() self.check_cache()
return self.cache[key] return self.cache[key]
def check_cache(self): def check_cache(self):
# first check for old ones # first check for old ones
t = time.time() t = time.time()
if self.soonest <= t: if self.soonest <= t:
for k, v in self.timeout.items(): for k, v in self.timeout.items():
...@@ -931,56 +931,56 @@ class CacheFTPHandler(FTPHandler): ...@@ -931,56 +931,56 @@ class CacheFTPHandler(FTPHandler):
self.soonest = min(self.timeout.values()) self.soonest = min(self.timeout.values())
# then check the size # then check the size
if len(self.cache) == self.max_conns: if len(self.cache) == self.max_conns:
for k, v in self.timeout.items(): for k, v in self.timeout.items():
if v == self.soonest: if v == self.soonest:
del self.cache[k] del self.cache[k]
del self.timeout[k] del self.timeout[k]
break break
self.soonest = min(self.timeout.values()) self.soonest = min(self.timeout.values())
class GopherHandler(BaseHandler): class GopherHandler(BaseHandler):
def gopher_open(self, req): def gopher_open(self, req):
host = req.get_host() host = req.get_host()
if not host: if not host:
raise GopherError('no host given') raise GopherError('no host given')
host = unquote(host) host = unquote(host)
selector = req.get_selector() selector = req.get_selector()
type, selector = splitgophertype(selector) type, selector = splitgophertype(selector)
selector, query = splitquery(selector) selector, query = splitquery(selector)
selector = unquote(selector) selector = unquote(selector)
if query: if query:
query = unquote(query) query = unquote(query)
fp = gopherlib.send_query(selector, query, host) fp = gopherlib.send_query(selector, query, host)
else: else:
fp = gopherlib.send_selector(selector, host) fp = gopherlib.send_selector(selector, host)
return addinfourl(fp, noheaders(), req.get_full_url()) return addinfourl(fp, noheaders(), req.get_full_url())
#bleck! don't use this yet #bleck! don't use this yet
class OpenerFactory: class OpenerFactory:
default_handlers = [UnknownHandler, HTTPHandler, default_handlers = [UnknownHandler, HTTPHandler,
HTTPDefaultErrorHandler, HTTPRedirectHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler,
FTPHandler, FileHandler] FTPHandler, FileHandler]
proxy_handlers = [ProxyHandler] proxy_handlers = [ProxyHandler]
handlers = [] handlers = []
replacement_handlers = [] replacement_handlers = []
def add_proxy_handler(self, ph): def add_proxy_handler(self, ph):
self.proxy_handlers = self.proxy_handlers + [ph] self.proxy_handlers = self.proxy_handlers + [ph]
def add_handler(self, h): def add_handler(self, h):
self.handlers = self.handlers + [h] self.handlers = self.handlers + [h]
def replace_handler(self, h): def replace_handler(self, h):
pass pass
def build_opener(self): def build_opener(self):
opener = OpenerDirectory() opener = OpenerDirectory()
for ph in self.proxy_handlers: for ph in self.proxy_handlers:
if type(ph) == types.ClassType: if type(ph) == types.ClassType:
ph = ph() ph = ph()
opener.add_handler(ph) opener.add_handler(ph)
if __name__ == "__main__": if __name__ == "__main__":
# XXX some of the test code depends on machine configurations that # XXX some of the test code depends on machine configurations that
...@@ -993,24 +993,24 @@ if __name__ == "__main__": ...@@ -993,24 +993,24 @@ if __name__ == "__main__":
else: else:
localhost = None localhost = None
urls = [ urls = [
# Thanks to Fred for finding these! # Thanks to Fred for finding these!
'gopher://gopher.lib.ncsu.edu/11/library/stacks/Alex', 'gopher://gopher.lib.ncsu.edu/11/library/stacks/Alex',
'gopher://gopher.vt.edu:10010/10/33', 'gopher://gopher.vt.edu:10010/10/33',
'file:/etc/passwd', 'file:/etc/passwd',
'file://nonsensename/etc/passwd', 'file://nonsensename/etc/passwd',
'ftp://www.python.org/pub/tmp/httplib.py', 'ftp://www.python.org/pub/tmp/httplib.py',
'ftp://www.python.org/pub/tmp/imageop.c', 'ftp://www.python.org/pub/tmp/imageop.c',
'ftp://www.python.org/pub/tmp/blat', 'ftp://www.python.org/pub/tmp/blat',
'http://www.espn.com/', # redirect 'http://www.espn.com/', # redirect
'http://www.python.org/Spanish/Inquistion/', 'http://www.python.org/Spanish/Inquistion/',
('http://grail.cnri.reston.va.us/cgi-bin/faqw.py', ('http://grail.cnri.reston.va.us/cgi-bin/faqw.py',
'query=pythonistas&querytype=simple&casefold=yes&req=search'), 'query=pythonistas&querytype=simple&casefold=yes&req=search'),
'http://www.python.org/', 'http://www.python.org/',
'ftp://prep.ai.mit.edu/welcome.msg', 'ftp://prep.ai.mit.edu/welcome.msg',
'ftp://www.python.org/pub/tmp/figure.prn', 'ftp://www.python.org/pub/tmp/figure.prn',
'ftp://www.python.org/pub/tmp/interp.pl', 'ftp://www.python.org/pub/tmp/interp.pl',
'http://checkproxy.cnri.reston.va.us/test/test.html', 'http://checkproxy.cnri.reston.va.us/test/test.html',
] ]
if localhost is not None: if localhost is not None:
...@@ -1034,10 +1034,10 @@ if __name__ == "__main__": ...@@ -1034,10 +1034,10 @@ if __name__ == "__main__":
# XXX try out some custom proxy objects too! # XXX try out some custom proxy objects too!
def at_cnri(req): def at_cnri(req):
host = req.get_host() host = req.get_host()
print host print host
if host[-18:] == '.cnri.reston.va.us': if host[-18:] == '.cnri.reston.va.us':
return 1 return 1
p = CustomProxy('http', at_cnri, 'proxy.cnri.reston.va.us') p = CustomProxy('http', at_cnri, 'proxy.cnri.reston.va.us')
ph = CustomProxyHandler(p) ph = CustomProxyHandler(p)
...@@ -1052,9 +1052,9 @@ if __name__ == "__main__": ...@@ -1052,9 +1052,9 @@ if __name__ == "__main__":
try: try:
f = urlopen(url, req) f = urlopen(url, req)
except IOError, err: except IOError, err:
print "IOError:", err print "IOError:", err
except socket.error, err: except socket.error, err:
print "socket.error:", err print "socket.error:", err
else: else:
buf = f.read() buf = f.read()
f.close() f.close()
......
...@@ -56,7 +56,7 @@ def whichdb(filename): ...@@ -56,7 +56,7 @@ def whichdb(filename):
# BSD hash v2 has a 12-byte NULL pad in front of the file type # BSD hash v2 has a 12-byte NULL pad in front of the file type
try: try:
(magic,) = struct.unpack("=l", s16[-4:]) (magic,) = struct.unpack("=l", s16[-4:])
except struct.error: except struct.error:
return "" return ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment