"""function to send email""" import os import sys from email.mime.text import MIMEText from email.header import Header from email.mime.multipart import MIMEMultipart from email.utils import formataddr import re import subprocess import filing from nitpo import Nitpo from sheets import Sheets import docing class Emailer(Nitpo): def __init__(self, do_verbose=False): super().__init__() self.sheets = Sheets() self.repis_fufi = None self.do_verbose = do_verbose # # can be set in the headers self.params = {} # # debug if on local computer self.debug = self.is_dev() # self.re_nama = re.compile(r"([^<]+)<([^@]+)@([^>]+)>\s*") self.re_nama = re.compile(r"([^<]+)<([^>]+)>\s*") def web(self, fufi, maix, empro, preps): """from the web, using prepared sheets""" head = empro + '_head' if head not in preps['sheets']: raise Exception(f"no {head} prepared") if not fufi.endswith('.xml'): raise Exception(f"{fufi} does not end in .xml") base_fufi = fufi[:-4] mail_fufi = base_fufi + '.mail' html_fufi = base_fufi + '.html' text_fufi = base_fufi + '.text' empro_head = empro + '_head' if os.path.isfile(mail_fufi): return None head_sheet = preps['sheets'][empro_head] msg = self.headers(head_sheet(maix)) empro_text = empro + '_text' empro_html = empro + '_html' if((empro_html not in preps['sheets']) and (empro_text not in preps['sheets'])): raise Exception(f"I need either {empro_html} or {empro_text}") if empro_html in preps['sheets']: sheet = preps['sheets'][empro_html] string = str(sheet(maix)) if self.is_dev(): filing.srite(html_fufi, string) else: part = MIMEText(string, "html", _charset='utf-8') msg.attach(part) if empro_text in preps['sheets']: sheet = preps['sheets'][empro_text] string = str(sheet(maix)) if self.is_dev(): filing.srite(text_fufi, string) else: part = MIMEText(string, "plain", _charset='utf-8') msg.attach(part) self.send(mail_fufi, msg, dont_send=self.is_dev()) def headers(self, doc): msg = MIMEMultipart("alternative") xp = '/n:headers/n:header' header_eles = doc.xpath(xp, namespaces={'n': self.const['ns']}) for header_ele in header_eles: if 'name' not in header_ele.attrib: show_head_ele = docing.show(header_ele) print(f"email: no name in {show_head_ele}", file=sys.stderr) continue name = header_ele.attrib['name'] if 'value' not in header_ele.attrib: show_head_ele = docing.show(header_ele) print(f"email: no value in {show_head_ele}", file=sys.stderr) continue value = header_ele.attrib['value'] # # if all uppercase names, it's a parameter if name.upper() == name: self.params[name] = value continue if self.is_it_ascii(value): msg[name] = value continue if header_ele.get('type') == 'address': out = str(self.make_address(value)) if out is not None: msg[name] = out continue msg[name] = Header(value, 'utf-8') return msg def prepare(self, maix, empro='repis', in_what='empros', dont_send=False, base='base', only=None): """send the email via file, stores the file in base""" mail_fufi = self.conf['folders']['mail'] + '/' + base + '.mail' if os.path.isfile(mail_fufi) and not dont_send: print(f"emailer sees {mail_fufi}, skip") return None # # save xml, for debugging maix_fufi = self.conf['folders']['mail'] + '/' + base + '.xml' if self.debug and empro == 'repis': filing.prepare(maix_fufi) maix_file = open(maix_fufi, 'w') string = docing.show(maix) maix_file.write(string) maix_file.close() sheet_fufi = self.get_empro_part(empro, 'head', in_what=in_what) if sheet_fufi is None: print("emailer does not see the empro head.") return None head_doc = self.sheets.via_system(sheet_fufi, maix) msg = self.headers(head_doc) if only != 'html': sheet_fufi = self.get_empro_part(empro, 'text', in_what=in_what) if sheet_fufi is not None: empro_text = empro + '_text' string = str(self.sheets.get_result(empro_text, maix)) if dont_send: print(string) part = MIMEText(string, "plain", _charset='utf-8') msg.attach(part) if only != 'text': sheet_fufi = self.get_empro_part(empro, 'html', in_what=in_what) if sheet_fufi is not None: empro_html = empro + '_html' html = str(self.sheets.get_result(empro_html, maix)) part = MIMEText(html, "html", _charset='utf-8') msg.attach(part) mail_fufi = self.conf['folders']['mail'] + '/' + base + '.mail' filing.prepare(mail_fufi) mail_file = open(mail_fufi, 'w') mail_file.write(msg.as_string()) mail_file.close() if dont_send is True: print(f"emailer wrote {mail_fufi}, not sent") return None # # for the admirable p.o'brien@... #if "'" in mail_fufi: # mail_command = f'cat "{mail_fufi}" | /usr/sbin/exim4 -t' #else: mail_command = f"cat {mail_fufi} | /usr/sbin/exim4 -t" envelope_address = self.get_envelope_address() if envelope_address is not None: mail_command += f" -f {envelope_address}" # # don't send on the test machine if self.is_dev(): print(f"emailer wrote {mail_fufi}") return None try: out = subprocess.run(mail_command, shell=True, check=True) except subprocess.CalledProcessError as error: os.remove(mail_fufi) out = mail_command + " yields " + str(error) print(out, file=sys.stderr) if empro != 'repis': print(f"emailer wrote {mail_fufi}") return out def send(self, mail_fufi, msg, dont_send=False): filing.prepare(mail_fufi) mail_file = open(mail_fufi, 'w') mail_file.write(msg.as_string()) mail_file.close() if dont_send is True: print(f"emailer wrote {mail_fufi}, not sent") return None mail_command = f"cat {mail_fufi} | /usr/sbin/exim4 -t" envelope_address = self.get_envelope_address() if envelope_address is not None: mail_command += f" -f {envelope_address}" # # don't send on the test machine if self.is_dev(): print(f"emailer wrote {mail_fufi}") return None try: out = subprocess.run(mail_command, shell=True, check=True) except subprocess.CalledProcessError as error: os.remove(mail_fufi) out = mail_command + " yields " + str(error) print(out, file=sys.stderr) #if empro != 'repis': # print(f"emailer wrote {mail_fufi}") return out def get_envelope_address(self): if 'ENVELOPE_ADDRESS' in self.params: return self.params['ENVELOPE_ADDRESS'] if self.has_conf('addresses', 'envelope'): return self.conf['addresses']['envelope'] return None def is_it_ascii(self, string): try: string.encode('ascii') except UnicodeEncodeError: return False return True def make_address(self, nama): out = self.re_nama.match(nama) if(out is None): print(f"emailer can't parse '{nama}'") return None name = out.group(1).strip() emad = out.group(2).strip() # # https://stackoverflow.com/questions/10551933/python-email-module-form-header-from-with-some-unicode-name-email address = formataddr((str(Header(name, 'utf-8')), emad)) return address def has_it_empro(self, empro, in_what='empros'): """is there is empro available""" if self.get_empro_part(empro, 'head', in_what=in_what) is None: return False if self.get_empro_part(empro, 'text', in_what=in_what) is not None: return True if self.get_empro_part(empro, 'html', in_what=in_what) is not None: return True return True def get_empro_part(self, empro, part, in_what='empros'): """empro part either by sheets conf or seen""" target = f"{empro}_{part}" ### check the configuration if self.has_conf('sheets', target): fufi = self.conf['sheets'][target] if not os.path.isfile(fufi): print(f"emailer does not see {fufi}", file=sys.stderr) return None # print(f"I (1) use {fufi}") return fufi # # 2nd: use style directory. This approach does not work. # # it leads to files not found in the style sheet, even # # though the return file can be identical with the first # # appoarch #if self.has_conf('folders', 'style'): # style_fudi = self.conf['folders']['style'] # fufi = style_fudi + '/' + empro + '_' + part + '.xslt.xml' # if os.path.isfile(fufi): # print(f"I (2) use {fufi}") # return fufi if not self.has_conf('folders', in_what): print(f"emailer: not [folders][{in_what}] configured.", file=sys.stderr) what_fudi = self.conf['folders'][in_what] fufi = f"{what_fudi}/{target}.xslt.xml" if os.path.isfile(fufi): return fufi # # try empros what_fudi = self.conf['folders']['empros'] fufi = f"{what_fudi}/{target}.xslt.xml" if not os.path.isfile(fufi): print(f"emailer does not see {fufi}", file=sys.stderr) return None return fufi