summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/mailcap.py223
1 files changed, 223 insertions, 0 deletions
diff --git a/Lib/mailcap.py b/Lib/mailcap.py
new file mode 100644
index 0000000..9da57d7
--- /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()