diff --git a/Lib/mailcap.py b/Lib/mailcap.py new file mode 100644 index 00000000000..9da57d704ee --- /dev/null +++ b/Lib/mailcap.py @@ -0,0 +1,223 @@ +# Mailcap file handling. See RFC 1524. + +import os +import string +import tempfile + + +# Part 1: top-level interface. + +def getcaps(): + caps = {} + for mailcap in listmailcapfiles(): + try: + fp = open(mailcap, 'r') + except: + continue + morecaps = readmailcapfile(fp) + fp.close() + for key in morecaps.keys(): + if not caps.has_key(key): + caps[key] = morecaps[key] + else: + caps[key] = caps[key] + morecaps[key] + return caps + +def listmailcapfiles(): + # XXX Actually, this is Unix-specific + if os.environ.has_key('MAILCAPS'): + str = os.environ['MAILCAPS'] + mailcaps = string.splitfields(str, ':') + else: + if os.environ.has_key('HOME'): + home = os.environ['HOME'] + else: + # Don't bother with getpwuid() + home = '.' # Last resort + mailcaps = [home + '/.mailcap', '/etc/mailcap', + '/usr/etc/mailcap', '/usr/local/etc/mailcap'] + return mailcaps + + +# Part 2: the parser. + +def readmailcapfile(fp): + caps = {} + while 1: + line = fp.readline() + if not line: break + # Ignore comments and blank lines + if line[0] == '#' or string.strip(line) == '': + continue + nextline = line + # Join continuation lines + while nextline[-2:] == '\\\n': + nextline = fp.readline() + if not nextline: nextline = '\n' + line = line[:-2] + nextline + # Parse the line + key, fields = parseline(line) + if not (key and fields): + cotinue + # Normalize the key + types = string.splitfields(key, '/') + for j in range(len(types)): + types[j] = string.strip(types[j]) + key = string.lower(string.joinfields(types, '/')) + # Update the database + if caps.has_key(key): + caps[key].append(fields) + else: + caps[key] = [fields] + return caps + +def parseline(line): + fields = [] + i, n = 0, len(line) + while i < n: + field, i = parsefield(line, i, n) + fields.append(field) + i = i+1 # Skip semicolon + if len(fields) < 2: + return None, None + key, view, rest = fields[0], fields[1], fields[2:] + fields = {'view': view} + for field in rest: + i = string.find(field, '=') + if i < 0: + fkey = field + fvalue = "" + else: + fkey = string.strip(field[:i]) + fvalue = string.strip(field[i+1:]) + if fields.has_key(fkey): + # Ignore it + pass + else: + fields[fkey] = fvalue + return key, fields + +def parsefield(line, i, n): + start = i + while i < n: + c = line[i] + if c == ';': + break + elif c == '\\': + i = i+2 + else: + i = i+1 + return string.strip(line[start:i]), i + + +# Part 3: using the database. + +def findmatch(caps, type, key='view', filename="/dev/null", plist=[]): + entries = lookup(caps, type, key) + for e in entries: + if e.has_key('test'): + test = subst(e['test'], filename, plist) + if test and os.system(test) != 0: + continue + command = subst(e[key], type, filename, plist) + return command, e + return None, None + +def lookup(caps, type, key=None): + entries = [] + if caps.has_key(type): + entries = entries + caps[type] + types = string.splitfields(type, '/') + type = types[0] + '/*' + if caps.has_key(type): + entries = entries + caps[type] + if key is not None: + entries = filter(lambda e, key=key: e.has_key(key), entries) + return entries + +def subst(field, type, filename, plist=[]): + # XXX Actually, this is Unix-specific + res = '' + i, n = 0, len(field) + while i < n: + c = field[i]; i = i+1 + if c <> '%': + if c == '\\': + c = field[i:i+1]; i = i+1 + res = res + c + else: + c = field[i]; i = i+1 + if c == '%': + res = res + c + elif c == 's': + res = res + filename + elif c == 't': + res = res + type + elif c == '{': + start = i + while i < n and field[i] <> '}': + i = i+1 + name = field[start:i] + i = i+1 + res = res + findparam(name, plist) + # XXX To do: + # %n == number of parts if type is multipart/* + # %F == list of alternating type and filename for parts + else: + res = res + '%' + c + return res + +def findparam(name, plist): + name = string.lower(name) + '=' + n = len(name) + for p in plist: + if string.lower(p[:n]) == name: + return p[n:] + return '' + + +# Part 4: test program. + +def test(): + import sys + caps = getcaps() + if not sys.argv[1:]: + show(caps) + return + for i in range(1, len(sys.argv), 2): + args = sys.argv[i:i+2] + if len(args) < 2: + print "usage: mailcap [type file] ..." + return + type = args[0] + file = args[1] + command, e = findmatch(caps, type, 'view', file) + if not command: + print "No viewer found for", type + else: + print "Executing:", command + sts = os.system(command) + if sts: + print "Exit status:", sts + +def show(caps): + print "Mailcap files:" + for fn in listmailcapfiles(): print "\t" + fn + print + if not caps: caps = getcaps() + print "Mailcap entries:" + print + ckeys = caps.keys() + ckeys.sort() + for type in ckeys: + print type + entries = caps[type] + for e in entries: + keys = e.keys() + keys.sort() + for k in keys: + print " %-15s" % k, e[k] + print + +if __name__ == '__main__': + test()