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