python3.2
3.2.2
|
00001 """Mailcap file handling. See RFC 1524.""" 00002 00003 import os 00004 00005 __all__ = ["getcaps","findmatch"] 00006 00007 # Part 1: top-level interface. 00008 00009 def getcaps(): 00010 """Return a dictionary containing the mailcap database. 00011 00012 The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain') 00013 to a list of dictionaries corresponding to mailcap entries. The list 00014 collects all the entries for that MIME type from all available mailcap 00015 files. Each dictionary contains key-value pairs for that MIME type, 00016 where the viewing command is stored with the key "view". 00017 00018 """ 00019 caps = {} 00020 for mailcap in listmailcapfiles(): 00021 try: 00022 fp = open(mailcap, 'r') 00023 except IOError: 00024 continue 00025 morecaps = readmailcapfile(fp) 00026 fp.close() 00027 for key, value in morecaps.items(): 00028 if not key in caps: 00029 caps[key] = value 00030 else: 00031 caps[key] = caps[key] + value 00032 return caps 00033 00034 def listmailcapfiles(): 00035 """Return a list of all mailcap files found on the system.""" 00036 # XXX Actually, this is Unix-specific 00037 if 'MAILCAPS' in os.environ: 00038 str = os.environ['MAILCAPS'] 00039 mailcaps = str.split(':') 00040 else: 00041 if 'HOME' in os.environ: 00042 home = os.environ['HOME'] 00043 else: 00044 # Don't bother with getpwuid() 00045 home = '.' # Last resort 00046 mailcaps = [home + '/.mailcap', '/etc/mailcap', 00047 '/usr/etc/mailcap', '/usr/local/etc/mailcap'] 00048 return mailcaps 00049 00050 00051 # Part 2: the parser. 00052 00053 def readmailcapfile(fp): 00054 """Read a mailcap file and return a dictionary keyed by MIME type. 00055 00056 Each MIME type is mapped to an entry consisting of a list of 00057 dictionaries; the list will contain more than one such dictionary 00058 if a given MIME type appears more than once in the mailcap file. 00059 Each dictionary contains key-value pairs for that MIME type, where 00060 the viewing command is stored with the key "view". 00061 """ 00062 caps = {} 00063 while 1: 00064 line = fp.readline() 00065 if not line: break 00066 # Ignore comments and blank lines 00067 if line[0] == '#' or line.strip() == '': 00068 continue 00069 nextline = line 00070 # Join continuation lines 00071 while nextline[-2:] == '\\\n': 00072 nextline = fp.readline() 00073 if not nextline: nextline = '\n' 00074 line = line[:-2] + nextline 00075 # Parse the line 00076 key, fields = parseline(line) 00077 if not (key and fields): 00078 continue 00079 # Normalize the key 00080 types = key.split('/') 00081 for j in range(len(types)): 00082 types[j] = types[j].strip() 00083 key = '/'.join(types).lower() 00084 # Update the database 00085 if key in caps: 00086 caps[key].append(fields) 00087 else: 00088 caps[key] = [fields] 00089 return caps 00090 00091 def parseline(line): 00092 """Parse one entry in a mailcap file and return a dictionary. 00093 00094 The viewing command is stored as the value with the key "view", 00095 and the rest of the fields produce key-value pairs in the dict. 00096 """ 00097 fields = [] 00098 i, n = 0, len(line) 00099 while i < n: 00100 field, i = parsefield(line, i, n) 00101 fields.append(field) 00102 i = i+1 # Skip semicolon 00103 if len(fields) < 2: 00104 return None, None 00105 key, view, rest = fields[0], fields[1], fields[2:] 00106 fields = {'view': view} 00107 for field in rest: 00108 i = field.find('=') 00109 if i < 0: 00110 fkey = field 00111 fvalue = "" 00112 else: 00113 fkey = field[:i].strip() 00114 fvalue = field[i+1:].strip() 00115 if fkey in fields: 00116 # Ignore it 00117 pass 00118 else: 00119 fields[fkey] = fvalue 00120 return key, fields 00121 00122 def parsefield(line, i, n): 00123 """Separate one key-value pair in a mailcap entry.""" 00124 start = i 00125 while i < n: 00126 c = line[i] 00127 if c == ';': 00128 break 00129 elif c == '\\': 00130 i = i+2 00131 else: 00132 i = i+1 00133 return line[start:i].strip(), i 00134 00135 00136 # Part 3: using the database. 00137 00138 def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): 00139 """Find a match for a mailcap entry. 00140 00141 Return a tuple containing the command line, and the mailcap entry 00142 used; (None, None) if no match is found. This may invoke the 00143 'test' command of several matching entries before deciding which 00144 entry to use. 00145 00146 """ 00147 entries = lookup(caps, MIMEtype, key) 00148 # XXX This code should somehow check for the needsterminal flag. 00149 for e in entries: 00150 if 'test' in e: 00151 test = subst(e['test'], filename, plist) 00152 if test and os.system(test) != 0: 00153 continue 00154 command = subst(e[key], MIMEtype, filename, plist) 00155 return command, e 00156 return None, None 00157 00158 def lookup(caps, MIMEtype, key=None): 00159 entries = [] 00160 if MIMEtype in caps: 00161 entries = entries + caps[MIMEtype] 00162 MIMEtypes = MIMEtype.split('/') 00163 MIMEtype = MIMEtypes[0] + '/*' 00164 if MIMEtype in caps: 00165 entries = entries + caps[MIMEtype] 00166 if key is not None: 00167 entries = [e for e in entries if key in e] 00168 return entries 00169 00170 def subst(field, MIMEtype, filename, plist=[]): 00171 # XXX Actually, this is Unix-specific 00172 res = '' 00173 i, n = 0, len(field) 00174 while i < n: 00175 c = field[i]; i = i+1 00176 if c != '%': 00177 if c == '\\': 00178 c = field[i:i+1]; i = i+1 00179 res = res + c 00180 else: 00181 c = field[i]; i = i+1 00182 if c == '%': 00183 res = res + c 00184 elif c == 's': 00185 res = res + filename 00186 elif c == 't': 00187 res = res + MIMEtype 00188 elif c == '{': 00189 start = i 00190 while i < n and field[i] != '}': 00191 i = i+1 00192 name = field[start:i] 00193 i = i+1 00194 res = res + findparam(name, plist) 00195 # XXX To do: 00196 # %n == number of parts if type is multipart/* 00197 # %F == list of alternating type and filename for parts 00198 else: 00199 res = res + '%' + c 00200 return res 00201 00202 def findparam(name, plist): 00203 name = name.lower() + '=' 00204 n = len(name) 00205 for p in plist: 00206 if p[:n].lower() == name: 00207 return p[n:] 00208 return '' 00209 00210 00211 # Part 4: test program. 00212 00213 def test(): 00214 import sys 00215 caps = getcaps() 00216 if not sys.argv[1:]: 00217 show(caps) 00218 return 00219 for i in range(1, len(sys.argv), 2): 00220 args = sys.argv[i:i+2] 00221 if len(args) < 2: 00222 print("usage: mailcap [MIMEtype file] ...") 00223 return 00224 MIMEtype = args[0] 00225 file = args[1] 00226 command, e = findmatch(caps, MIMEtype, 'view', file) 00227 if not command: 00228 print("No viewer found for", type) 00229 else: 00230 print("Executing:", command) 00231 sts = os.system(command) 00232 if sts: 00233 print("Exit status:", sts) 00234 00235 def show(caps): 00236 print("Mailcap files:") 00237 for fn in listmailcapfiles(): print("\t" + fn) 00238 print() 00239 if not caps: caps = getcaps() 00240 print("Mailcap entries:") 00241 print() 00242 ckeys = sorted(caps) 00243 for type in ckeys: 00244 print(type) 00245 entries = caps[type] 00246 for e in entries: 00247 keys = sorted(e) 00248 for k in keys: 00249 print(" %-15s" % k, e[k]) 00250 print() 00251 00252 if __name__ == '__main__': 00253 test()