1#!/usr/bin/python 2# 3# Usage: faxnotify log-file image-file [image-file ...] 4# 5# This script converts received fax TIFF files to a PDF 6# file and delivers them according to the fax preferences. 7# 8 9import os, sys, string 10import shutil, errno, socket 11 12from email import Encoders 13from email.Message import Message 14from email.MIMEBase import MIMEBase 15from email.MIMEMultipart import MIMEMultipart 16 17 18def defaults_read(key, domain = '/Library/Preferences/com.apple.print.FaxPrefs'): 19 """Read the value of 'key' from the defaults system""" 20 i,o,e = os.popen3('/usr/bin/defaults read ' + domain + ' ' + key) 21 i.close() 22 value = string.rstrip(o.read()) 23 o.close() 24 e.close() 25 return value 26 27 28def quote(x): 29 """Add quotes to a shell command argument string""" 30 if '\'' not in x : 31 return '\'' + x + '\'' 32 s = '"' 33 for c in x: 34 if c in '\\$"`': 35 s = s + '\\' 36 s = s + c 37 s = s + '"' 38 return s 39 40 41def phone_filter(c): 42 """Limit a phone number's character set so it can always be used in file names""" 43 return c in ' #()*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz' 44 45def phone_from_log(logfilename): 46 """Find the sender's phone number so it can be used in the PDF name""" 47 phone_number = '' 48 PHONE_KEY = 'remote ID ->' 49 logfile = file(logfilename) 50 for line in logfile : 51 index = line.find(PHONE_KEY) 52 if index != -1 : 53 phone_number = string.strip(line[index + len(PHONE_KEY):]) 54 phone_number = filter(phone_filter, phone_number) 55 break 56 logfile.close() 57 if len(phone_number) == 0 : 58 phone_number = 'Unknown' 59 return phone_number 60 61 62def copy_versioned_file(srcfile, dstdir): 63 """Copy a file to a directory adding versioning as needed""" 64 fsrc = None 65 fdst = None 66 67 if not os.path.isdir(dstdir) : 68 print >> sys.stderr, 'faxnotify: Save to directory missing ' + quote(dstdir.encode('unicode-escape')) 69 return -1 70 71 old_uid = os.getuid() 72 old_mask = os.umask(002) 73 74 try: 75 # open src as root 76 fsrc = open(srcfile, 'rb') 77 78 # get the owner of the destination directory 79 dstdir_owner = os.lstat(dstdir).st_uid 80 81 # step down to the dir owner's uid before creating the file 82 os.seteuid(dstdir_owner) 83 84 # create the full path for the destination file and seperate 85 # the components in case we have to version it 86 dstfile = dstdir + '/' + os.path.split(srcfile)[1] 87 name, ext = os.path.splitext(dstfile) 88 89 for i in xrange(1, 10000): 90 try: 91 fd = os.open(dstfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL) 92 fdst = os.fdopen(fd, 'wb') 93 break 94 except (OSError, IOError), e: 95 if e.errno != errno.EEXIST: 96 raise 97 98 # file exists, find next available version 99 dstfile = '%s %d%s' % (name, i, ext) 100 101 # we've created a new unique file, now copy the contents of src into it 102 shutil.copyfileobj(fsrc, fdst) 103 104 except (OSError, IOError), e: 105 print >> sys.stderr, 'faxnotify: Save to failed;' , e 106 except: 107 print >> sys.stderr, 'faxnotify: Save to ' + quote(dstdir.encode('unicode-escape')) + ' failed' 108 pass 109 110 if fsrc: 111 fsrc.close() 112 if fdst: 113 fdst.close() 114 os.umask(old_mask) 115 os.seteuid(old_uid) 116 117 return 0 118 119 120def email_fax(receipents, sender, pdffile): 121 """Send email to the receipents with an attached file""" 122 msg = MIMEMultipart() 123 msg['Subject'] = pdffile 124 msg['To'] = receipents 125 msg['From'] = sender 126 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n' 127 # To guarantee the message ends with a newline 128 msg.epilogue = '' 129 130 fp = open(pdffile) 131 pdf = MIMEBase('application', 'pdf') 132 pdf.set_payload(fp.read()) 133 fp.close() 134 135 # Encode the payload using Base64 136 Encoders.encode_base64(pdf) 137 138 # Set the filename parameter 139 pdf.add_header('Content-Disposition', 'attachment', filename=pdffile) 140 msg.attach(pdf) 141 142 stdout, stdin = os.popen2('/usr/sbin/sendmail -oi -t -r' + quote(sender)) 143 stdout.write(msg.as_string()) 144 stdout.close() 145 stdin.close() 146 147 148# 149# Make sure we have enough arguments 150# 151if len(sys.argv) == 1 : 152 print >> sys.stderr, 'Usage: faxnotify log-file image-file [image-file ...]' 153 sys.exit(2) 154 155# If there are no tiff files to process just exit quietly. 156if len(sys.argv) == 2 : 157 sys.exit(0) 158 159tiff_files = string.join(map(quote, sys.argv[2:])) 160pdf_file = 'FAX from ' + phone_from_log(sys.argv[1]) + '.pdf' 161 162try: 163 # Convert TIFFs into a single PDF 164 command = '/usr/libexec/fax/imagestopdf ' + tiff_files + ' ' + quote(pdf_file) 165 166 if os.system(command) : 167 print >> sys.stderr, 'faxnotify: ' + command + ' failed' 168 sys.exit(1) 169 170 # Print on printer 171 if defaults_read('PrintFax') == '1' : 172 printer = quote(defaults_read('PrinterID')) 173 faxuser = defaults_read('FaxUser') 174 if faxuser == '' : 175 faxuser = 'FaxNotify' 176 faxuser = quote(faxuser) 177 178 # Add paper size and scale to fit options 179 options = '' 180 paper_id = defaults_read('DefaultPaperID') 181 if paper_id != '' : 182 options = '-o media=' + quote(paper_id) 183 options = options + ' -o fitplot' 184 185 command = '/usr/bin/lpr -P ' + printer + ' -U ' + faxuser + ' ' + options + ' ' + quote(pdf_file) 186 if os.system(command) : 187 print >> sys.stderr, 'faxnotify: Print on printer ' + printer + ' failed' 188 189 # Save to 190 if defaults_read('SaveFax') == '1' : 191 copy_versioned_file(pdf_file, unicode(defaults_read('SavePath'), 'unicode-escape')) 192 193 # Email to 194 if defaults_read('EmailFax') == '1' : 195 recipients = defaults_read('EmailRecipient') 196 sender = defaults_read('EmailSender') 197 if sender == '' : 198 hostname = socket.gethostname() 199 if hostname.endswith('.local') or hostname.endswith('.local.') : 200 sender = recipients.split(',')[0].strip(string.whitespace + '<>') 201 else : 202 sender = 'FaxNotify' 203 email_fax(recipients, sender, pdf_file) 204 205finally: 206 # Remove the TIFFs and converted PDF 207 map(os.remove, sys.argv[2:]) 208 if os.path.exists(pdf_file): 209 os.remove(pdf_file) 210