1*cda5da8dSAndroid Build Coastguard Worker"""Mailcap file handling. See RFC 1524.""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerimport os 4*cda5da8dSAndroid Build Coastguard Workerimport warnings 5*cda5da8dSAndroid Build Coastguard Workerimport re 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker__all__ = ["getcaps","findmatch"] 8*cda5da8dSAndroid Build Coastguard Worker 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker_DEPRECATION_MSG = ('The {name} module is deprecated and will be removed in ' 11*cda5da8dSAndroid Build Coastguard Worker 'Python {remove}. See the mimetypes module for an ' 12*cda5da8dSAndroid Build Coastguard Worker 'alternative.') 13*cda5da8dSAndroid Build Coastguard Workerwarnings._deprecated(__name__, _DEPRECATION_MSG, remove=(3, 13)) 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Worker 16*cda5da8dSAndroid Build Coastguard Workerdef lineno_sort_key(entry): 17*cda5da8dSAndroid Build Coastguard Worker # Sort in ascending order, with unspecified entries at the end 18*cda5da8dSAndroid Build Coastguard Worker if 'lineno' in entry: 19*cda5da8dSAndroid Build Coastguard Worker return 0, entry['lineno'] 20*cda5da8dSAndroid Build Coastguard Worker else: 21*cda5da8dSAndroid Build Coastguard Worker return 1, 0 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker_find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Workerclass UnsafeMailcapInput(Warning): 26*cda5da8dSAndroid Build Coastguard Worker """Warning raised when refusing unsafe input""" 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard Worker# Part 1: top-level interface. 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Workerdef getcaps(): 32*cda5da8dSAndroid Build Coastguard Worker """Return a dictionary containing the mailcap database. 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard Worker The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain') 35*cda5da8dSAndroid Build Coastguard Worker to a list of dictionaries corresponding to mailcap entries. The list 36*cda5da8dSAndroid Build Coastguard Worker collects all the entries for that MIME type from all available mailcap 37*cda5da8dSAndroid Build Coastguard Worker files. Each dictionary contains key-value pairs for that MIME type, 38*cda5da8dSAndroid Build Coastguard Worker where the viewing command is stored with the key "view". 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard Worker """ 41*cda5da8dSAndroid Build Coastguard Worker caps = {} 42*cda5da8dSAndroid Build Coastguard Worker lineno = 0 43*cda5da8dSAndroid Build Coastguard Worker for mailcap in listmailcapfiles(): 44*cda5da8dSAndroid Build Coastguard Worker try: 45*cda5da8dSAndroid Build Coastguard Worker fp = open(mailcap, 'r') 46*cda5da8dSAndroid Build Coastguard Worker except OSError: 47*cda5da8dSAndroid Build Coastguard Worker continue 48*cda5da8dSAndroid Build Coastguard Worker with fp: 49*cda5da8dSAndroid Build Coastguard Worker morecaps, lineno = _readmailcapfile(fp, lineno) 50*cda5da8dSAndroid Build Coastguard Worker for key, value in morecaps.items(): 51*cda5da8dSAndroid Build Coastguard Worker if not key in caps: 52*cda5da8dSAndroid Build Coastguard Worker caps[key] = value 53*cda5da8dSAndroid Build Coastguard Worker else: 54*cda5da8dSAndroid Build Coastguard Worker caps[key] = caps[key] + value 55*cda5da8dSAndroid Build Coastguard Worker return caps 56*cda5da8dSAndroid Build Coastguard Worker 57*cda5da8dSAndroid Build Coastguard Workerdef listmailcapfiles(): 58*cda5da8dSAndroid Build Coastguard Worker """Return a list of all mailcap files found on the system.""" 59*cda5da8dSAndroid Build Coastguard Worker # This is mostly a Unix thing, but we use the OS path separator anyway 60*cda5da8dSAndroid Build Coastguard Worker if 'MAILCAPS' in os.environ: 61*cda5da8dSAndroid Build Coastguard Worker pathstr = os.environ['MAILCAPS'] 62*cda5da8dSAndroid Build Coastguard Worker mailcaps = pathstr.split(os.pathsep) 63*cda5da8dSAndroid Build Coastguard Worker else: 64*cda5da8dSAndroid Build Coastguard Worker if 'HOME' in os.environ: 65*cda5da8dSAndroid Build Coastguard Worker home = os.environ['HOME'] 66*cda5da8dSAndroid Build Coastguard Worker else: 67*cda5da8dSAndroid Build Coastguard Worker # Don't bother with getpwuid() 68*cda5da8dSAndroid Build Coastguard Worker home = '.' # Last resort 69*cda5da8dSAndroid Build Coastguard Worker mailcaps = [home + '/.mailcap', '/etc/mailcap', 70*cda5da8dSAndroid Build Coastguard Worker '/usr/etc/mailcap', '/usr/local/etc/mailcap'] 71*cda5da8dSAndroid Build Coastguard Worker return mailcaps 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Worker# Part 2: the parser. 75*cda5da8dSAndroid Build Coastguard Workerdef readmailcapfile(fp): 76*cda5da8dSAndroid Build Coastguard Worker """Read a mailcap file and return a dictionary keyed by MIME type.""" 77*cda5da8dSAndroid Build Coastguard Worker warnings.warn('readmailcapfile is deprecated, use getcaps instead', 78*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning, 2) 79*cda5da8dSAndroid Build Coastguard Worker caps, _ = _readmailcapfile(fp, None) 80*cda5da8dSAndroid Build Coastguard Worker return caps 81*cda5da8dSAndroid Build Coastguard Worker 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Workerdef _readmailcapfile(fp, lineno): 84*cda5da8dSAndroid Build Coastguard Worker """Read a mailcap file and return a dictionary keyed by MIME type. 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker Each MIME type is mapped to an entry consisting of a list of 87*cda5da8dSAndroid Build Coastguard Worker dictionaries; the list will contain more than one such dictionary 88*cda5da8dSAndroid Build Coastguard Worker if a given MIME type appears more than once in the mailcap file. 89*cda5da8dSAndroid Build Coastguard Worker Each dictionary contains key-value pairs for that MIME type, where 90*cda5da8dSAndroid Build Coastguard Worker the viewing command is stored with the key "view". 91*cda5da8dSAndroid Build Coastguard Worker """ 92*cda5da8dSAndroid Build Coastguard Worker caps = {} 93*cda5da8dSAndroid Build Coastguard Worker while 1: 94*cda5da8dSAndroid Build Coastguard Worker line = fp.readline() 95*cda5da8dSAndroid Build Coastguard Worker if not line: break 96*cda5da8dSAndroid Build Coastguard Worker # Ignore comments and blank lines 97*cda5da8dSAndroid Build Coastguard Worker if line[0] == '#' or line.strip() == '': 98*cda5da8dSAndroid Build Coastguard Worker continue 99*cda5da8dSAndroid Build Coastguard Worker nextline = line 100*cda5da8dSAndroid Build Coastguard Worker # Join continuation lines 101*cda5da8dSAndroid Build Coastguard Worker while nextline[-2:] == '\\\n': 102*cda5da8dSAndroid Build Coastguard Worker nextline = fp.readline() 103*cda5da8dSAndroid Build Coastguard Worker if not nextline: nextline = '\n' 104*cda5da8dSAndroid Build Coastguard Worker line = line[:-2] + nextline 105*cda5da8dSAndroid Build Coastguard Worker # Parse the line 106*cda5da8dSAndroid Build Coastguard Worker key, fields = parseline(line) 107*cda5da8dSAndroid Build Coastguard Worker if not (key and fields): 108*cda5da8dSAndroid Build Coastguard Worker continue 109*cda5da8dSAndroid Build Coastguard Worker if lineno is not None: 110*cda5da8dSAndroid Build Coastguard Worker fields['lineno'] = lineno 111*cda5da8dSAndroid Build Coastguard Worker lineno += 1 112*cda5da8dSAndroid Build Coastguard Worker # Normalize the key 113*cda5da8dSAndroid Build Coastguard Worker types = key.split('/') 114*cda5da8dSAndroid Build Coastguard Worker for j in range(len(types)): 115*cda5da8dSAndroid Build Coastguard Worker types[j] = types[j].strip() 116*cda5da8dSAndroid Build Coastguard Worker key = '/'.join(types).lower() 117*cda5da8dSAndroid Build Coastguard Worker # Update the database 118*cda5da8dSAndroid Build Coastguard Worker if key in caps: 119*cda5da8dSAndroid Build Coastguard Worker caps[key].append(fields) 120*cda5da8dSAndroid Build Coastguard Worker else: 121*cda5da8dSAndroid Build Coastguard Worker caps[key] = [fields] 122*cda5da8dSAndroid Build Coastguard Worker return caps, lineno 123*cda5da8dSAndroid Build Coastguard Worker 124*cda5da8dSAndroid Build Coastguard Workerdef parseline(line): 125*cda5da8dSAndroid Build Coastguard Worker """Parse one entry in a mailcap file and return a dictionary. 126*cda5da8dSAndroid Build Coastguard Worker 127*cda5da8dSAndroid Build Coastguard Worker The viewing command is stored as the value with the key "view", 128*cda5da8dSAndroid Build Coastguard Worker and the rest of the fields produce key-value pairs in the dict. 129*cda5da8dSAndroid Build Coastguard Worker """ 130*cda5da8dSAndroid Build Coastguard Worker fields = [] 131*cda5da8dSAndroid Build Coastguard Worker i, n = 0, len(line) 132*cda5da8dSAndroid Build Coastguard Worker while i < n: 133*cda5da8dSAndroid Build Coastguard Worker field, i = parsefield(line, i, n) 134*cda5da8dSAndroid Build Coastguard Worker fields.append(field) 135*cda5da8dSAndroid Build Coastguard Worker i = i+1 # Skip semicolon 136*cda5da8dSAndroid Build Coastguard Worker if len(fields) < 2: 137*cda5da8dSAndroid Build Coastguard Worker return None, None 138*cda5da8dSAndroid Build Coastguard Worker key, view, rest = fields[0], fields[1], fields[2:] 139*cda5da8dSAndroid Build Coastguard Worker fields = {'view': view} 140*cda5da8dSAndroid Build Coastguard Worker for field in rest: 141*cda5da8dSAndroid Build Coastguard Worker i = field.find('=') 142*cda5da8dSAndroid Build Coastguard Worker if i < 0: 143*cda5da8dSAndroid Build Coastguard Worker fkey = field 144*cda5da8dSAndroid Build Coastguard Worker fvalue = "" 145*cda5da8dSAndroid Build Coastguard Worker else: 146*cda5da8dSAndroid Build Coastguard Worker fkey = field[:i].strip() 147*cda5da8dSAndroid Build Coastguard Worker fvalue = field[i+1:].strip() 148*cda5da8dSAndroid Build Coastguard Worker if fkey in fields: 149*cda5da8dSAndroid Build Coastguard Worker # Ignore it 150*cda5da8dSAndroid Build Coastguard Worker pass 151*cda5da8dSAndroid Build Coastguard Worker else: 152*cda5da8dSAndroid Build Coastguard Worker fields[fkey] = fvalue 153*cda5da8dSAndroid Build Coastguard Worker return key, fields 154*cda5da8dSAndroid Build Coastguard Worker 155*cda5da8dSAndroid Build Coastguard Workerdef parsefield(line, i, n): 156*cda5da8dSAndroid Build Coastguard Worker """Separate one key-value pair in a mailcap entry.""" 157*cda5da8dSAndroid Build Coastguard Worker start = i 158*cda5da8dSAndroid Build Coastguard Worker while i < n: 159*cda5da8dSAndroid Build Coastguard Worker c = line[i] 160*cda5da8dSAndroid Build Coastguard Worker if c == ';': 161*cda5da8dSAndroid Build Coastguard Worker break 162*cda5da8dSAndroid Build Coastguard Worker elif c == '\\': 163*cda5da8dSAndroid Build Coastguard Worker i = i+2 164*cda5da8dSAndroid Build Coastguard Worker else: 165*cda5da8dSAndroid Build Coastguard Worker i = i+1 166*cda5da8dSAndroid Build Coastguard Worker return line[start:i].strip(), i 167*cda5da8dSAndroid Build Coastguard Worker 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker# Part 3: using the database. 170*cda5da8dSAndroid Build Coastguard Worker 171*cda5da8dSAndroid Build Coastguard Workerdef findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): 172*cda5da8dSAndroid Build Coastguard Worker """Find a match for a mailcap entry. 173*cda5da8dSAndroid Build Coastguard Worker 174*cda5da8dSAndroid Build Coastguard Worker Return a tuple containing the command line, and the mailcap entry 175*cda5da8dSAndroid Build Coastguard Worker used; (None, None) if no match is found. This may invoke the 176*cda5da8dSAndroid Build Coastguard Worker 'test' command of several matching entries before deciding which 177*cda5da8dSAndroid Build Coastguard Worker entry to use. 178*cda5da8dSAndroid Build Coastguard Worker 179*cda5da8dSAndroid Build Coastguard Worker """ 180*cda5da8dSAndroid Build Coastguard Worker if _find_unsafe(filename): 181*cda5da8dSAndroid Build Coastguard Worker msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,) 182*cda5da8dSAndroid Build Coastguard Worker warnings.warn(msg, UnsafeMailcapInput) 183*cda5da8dSAndroid Build Coastguard Worker return None, None 184*cda5da8dSAndroid Build Coastguard Worker entries = lookup(caps, MIMEtype, key) 185*cda5da8dSAndroid Build Coastguard Worker # XXX This code should somehow check for the needsterminal flag. 186*cda5da8dSAndroid Build Coastguard Worker for e in entries: 187*cda5da8dSAndroid Build Coastguard Worker if 'test' in e: 188*cda5da8dSAndroid Build Coastguard Worker test = subst(e['test'], filename, plist) 189*cda5da8dSAndroid Build Coastguard Worker if test is None: 190*cda5da8dSAndroid Build Coastguard Worker continue 191*cda5da8dSAndroid Build Coastguard Worker if test and os.system(test) != 0: 192*cda5da8dSAndroid Build Coastguard Worker continue 193*cda5da8dSAndroid Build Coastguard Worker command = subst(e[key], MIMEtype, filename, plist) 194*cda5da8dSAndroid Build Coastguard Worker if command is not None: 195*cda5da8dSAndroid Build Coastguard Worker return command, e 196*cda5da8dSAndroid Build Coastguard Worker return None, None 197*cda5da8dSAndroid Build Coastguard Worker 198*cda5da8dSAndroid Build Coastguard Workerdef lookup(caps, MIMEtype, key=None): 199*cda5da8dSAndroid Build Coastguard Worker entries = [] 200*cda5da8dSAndroid Build Coastguard Worker if MIMEtype in caps: 201*cda5da8dSAndroid Build Coastguard Worker entries = entries + caps[MIMEtype] 202*cda5da8dSAndroid Build Coastguard Worker MIMEtypes = MIMEtype.split('/') 203*cda5da8dSAndroid Build Coastguard Worker MIMEtype = MIMEtypes[0] + '/*' 204*cda5da8dSAndroid Build Coastguard Worker if MIMEtype in caps: 205*cda5da8dSAndroid Build Coastguard Worker entries = entries + caps[MIMEtype] 206*cda5da8dSAndroid Build Coastguard Worker if key is not None: 207*cda5da8dSAndroid Build Coastguard Worker entries = [e for e in entries if key in e] 208*cda5da8dSAndroid Build Coastguard Worker entries = sorted(entries, key=lineno_sort_key) 209*cda5da8dSAndroid Build Coastguard Worker return entries 210*cda5da8dSAndroid Build Coastguard Worker 211*cda5da8dSAndroid Build Coastguard Workerdef subst(field, MIMEtype, filename, plist=[]): 212*cda5da8dSAndroid Build Coastguard Worker # XXX Actually, this is Unix-specific 213*cda5da8dSAndroid Build Coastguard Worker res = '' 214*cda5da8dSAndroid Build Coastguard Worker i, n = 0, len(field) 215*cda5da8dSAndroid Build Coastguard Worker while i < n: 216*cda5da8dSAndroid Build Coastguard Worker c = field[i]; i = i+1 217*cda5da8dSAndroid Build Coastguard Worker if c != '%': 218*cda5da8dSAndroid Build Coastguard Worker if c == '\\': 219*cda5da8dSAndroid Build Coastguard Worker c = field[i:i+1]; i = i+1 220*cda5da8dSAndroid Build Coastguard Worker res = res + c 221*cda5da8dSAndroid Build Coastguard Worker else: 222*cda5da8dSAndroid Build Coastguard Worker c = field[i]; i = i+1 223*cda5da8dSAndroid Build Coastguard Worker if c == '%': 224*cda5da8dSAndroid Build Coastguard Worker res = res + c 225*cda5da8dSAndroid Build Coastguard Worker elif c == 's': 226*cda5da8dSAndroid Build Coastguard Worker res = res + filename 227*cda5da8dSAndroid Build Coastguard Worker elif c == 't': 228*cda5da8dSAndroid Build Coastguard Worker if _find_unsafe(MIMEtype): 229*cda5da8dSAndroid Build Coastguard Worker msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,) 230*cda5da8dSAndroid Build Coastguard Worker warnings.warn(msg, UnsafeMailcapInput) 231*cda5da8dSAndroid Build Coastguard Worker return None 232*cda5da8dSAndroid Build Coastguard Worker res = res + MIMEtype 233*cda5da8dSAndroid Build Coastguard Worker elif c == '{': 234*cda5da8dSAndroid Build Coastguard Worker start = i 235*cda5da8dSAndroid Build Coastguard Worker while i < n and field[i] != '}': 236*cda5da8dSAndroid Build Coastguard Worker i = i+1 237*cda5da8dSAndroid Build Coastguard Worker name = field[start:i] 238*cda5da8dSAndroid Build Coastguard Worker i = i+1 239*cda5da8dSAndroid Build Coastguard Worker param = findparam(name, plist) 240*cda5da8dSAndroid Build Coastguard Worker if _find_unsafe(param): 241*cda5da8dSAndroid Build Coastguard Worker msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name) 242*cda5da8dSAndroid Build Coastguard Worker warnings.warn(msg, UnsafeMailcapInput) 243*cda5da8dSAndroid Build Coastguard Worker return None 244*cda5da8dSAndroid Build Coastguard Worker res = res + param 245*cda5da8dSAndroid Build Coastguard Worker # XXX To do: 246*cda5da8dSAndroid Build Coastguard Worker # %n == number of parts if type is multipart/* 247*cda5da8dSAndroid Build Coastguard Worker # %F == list of alternating type and filename for parts 248*cda5da8dSAndroid Build Coastguard Worker else: 249*cda5da8dSAndroid Build Coastguard Worker res = res + '%' + c 250*cda5da8dSAndroid Build Coastguard Worker return res 251*cda5da8dSAndroid Build Coastguard Worker 252*cda5da8dSAndroid Build Coastguard Workerdef findparam(name, plist): 253*cda5da8dSAndroid Build Coastguard Worker name = name.lower() + '=' 254*cda5da8dSAndroid Build Coastguard Worker n = len(name) 255*cda5da8dSAndroid Build Coastguard Worker for p in plist: 256*cda5da8dSAndroid Build Coastguard Worker if p[:n].lower() == name: 257*cda5da8dSAndroid Build Coastguard Worker return p[n:] 258*cda5da8dSAndroid Build Coastguard Worker return '' 259*cda5da8dSAndroid Build Coastguard Worker 260*cda5da8dSAndroid Build Coastguard Worker 261*cda5da8dSAndroid Build Coastguard Worker# Part 4: test program. 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Workerdef test(): 264*cda5da8dSAndroid Build Coastguard Worker import sys 265*cda5da8dSAndroid Build Coastguard Worker caps = getcaps() 266*cda5da8dSAndroid Build Coastguard Worker if not sys.argv[1:]: 267*cda5da8dSAndroid Build Coastguard Worker show(caps) 268*cda5da8dSAndroid Build Coastguard Worker return 269*cda5da8dSAndroid Build Coastguard Worker for i in range(1, len(sys.argv), 2): 270*cda5da8dSAndroid Build Coastguard Worker args = sys.argv[i:i+2] 271*cda5da8dSAndroid Build Coastguard Worker if len(args) < 2: 272*cda5da8dSAndroid Build Coastguard Worker print("usage: mailcap [MIMEtype file] ...") 273*cda5da8dSAndroid Build Coastguard Worker return 274*cda5da8dSAndroid Build Coastguard Worker MIMEtype = args[0] 275*cda5da8dSAndroid Build Coastguard Worker file = args[1] 276*cda5da8dSAndroid Build Coastguard Worker command, e = findmatch(caps, MIMEtype, 'view', file) 277*cda5da8dSAndroid Build Coastguard Worker if not command: 278*cda5da8dSAndroid Build Coastguard Worker print("No viewer found for", type) 279*cda5da8dSAndroid Build Coastguard Worker else: 280*cda5da8dSAndroid Build Coastguard Worker print("Executing:", command) 281*cda5da8dSAndroid Build Coastguard Worker sts = os.system(command) 282*cda5da8dSAndroid Build Coastguard Worker sts = os.waitstatus_to_exitcode(sts) 283*cda5da8dSAndroid Build Coastguard Worker if sts: 284*cda5da8dSAndroid Build Coastguard Worker print("Exit status:", sts) 285*cda5da8dSAndroid Build Coastguard Worker 286*cda5da8dSAndroid Build Coastguard Workerdef show(caps): 287*cda5da8dSAndroid Build Coastguard Worker print("Mailcap files:") 288*cda5da8dSAndroid Build Coastguard Worker for fn in listmailcapfiles(): print("\t" + fn) 289*cda5da8dSAndroid Build Coastguard Worker print() 290*cda5da8dSAndroid Build Coastguard Worker if not caps: caps = getcaps() 291*cda5da8dSAndroid Build Coastguard Worker print("Mailcap entries:") 292*cda5da8dSAndroid Build Coastguard Worker print() 293*cda5da8dSAndroid Build Coastguard Worker ckeys = sorted(caps) 294*cda5da8dSAndroid Build Coastguard Worker for type in ckeys: 295*cda5da8dSAndroid Build Coastguard Worker print(type) 296*cda5da8dSAndroid Build Coastguard Worker entries = caps[type] 297*cda5da8dSAndroid Build Coastguard Worker for e in entries: 298*cda5da8dSAndroid Build Coastguard Worker keys = sorted(e) 299*cda5da8dSAndroid Build Coastguard Worker for k in keys: 300*cda5da8dSAndroid Build Coastguard Worker print(" %-15s" % k, e[k]) 301*cda5da8dSAndroid Build Coastguard Worker print() 302*cda5da8dSAndroid Build Coastguard Worker 303*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 304*cda5da8dSAndroid Build Coastguard Worker test() 305