[50fdf12] | 1 | # |
---|
| 2 | # RTEMS Tools Project (http://www.rtems.org/) |
---|
[b0fa2ae] | 3 | # Copyright 2013-2016 Chris Johns (chrisj@rtems.org) |
---|
[50fdf12] | 4 | # All rights reserved. |
---|
| 5 | # |
---|
| 6 | # This file is part of the RTEMS Tools package in 'rtems-tools'. |
---|
| 7 | # |
---|
| 8 | # Redistribution and use in source and binary forms, with or without |
---|
| 9 | # modification, are permitted provided that the following conditions are met: |
---|
| 10 | # |
---|
| 11 | # 1. Redistributions of source code must retain the above copyright notice, |
---|
| 12 | # this list of conditions and the following disclaimer. |
---|
| 13 | # |
---|
| 14 | # 2. Redistributions in binary form must reproduce the above copyright notice, |
---|
| 15 | # this list of conditions and the following disclaimer in the documentation |
---|
| 16 | # and/or other materials provided with the distribution. |
---|
| 17 | # |
---|
| 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
---|
| 19 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
| 20 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
| 21 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
---|
| 22 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
---|
| 23 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
---|
| 24 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
---|
| 25 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
---|
| 26 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
---|
| 27 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
---|
| 28 | # POSSIBILITY OF SUCH DAMAGE. |
---|
| 29 | # |
---|
| 30 | |
---|
| 31 | # |
---|
| 32 | # Manage emailing results or reports. |
---|
| 33 | # |
---|
| 34 | |
---|
[b0fa2ae] | 35 | from __future__ import print_function |
---|
| 36 | |
---|
[50fdf12] | 37 | import os |
---|
| 38 | import smtplib |
---|
| 39 | import socket |
---|
| 40 | |
---|
[b0fa2ae] | 41 | # |
---|
| 42 | # Support to handle use in a package and as a unit test. |
---|
| 43 | # If there is a better way to let us know. |
---|
| 44 | # |
---|
| 45 | try: |
---|
| 46 | from . import error |
---|
| 47 | from . import options |
---|
| 48 | from . import path |
---|
| 49 | except (ValueError, SystemError): |
---|
| 50 | import error |
---|
| 51 | import options |
---|
| 52 | import path |
---|
[50fdf12] | 53 | |
---|
[7051ba5] | 54 | _options = { |
---|
| 55 | '--mail' : 'Send email report or results.', |
---|
| 56 | '--smtp-host': 'SMTP host to send via.', |
---|
| 57 | '--mail-to' : 'Email address to send the email too.', |
---|
| 58 | '--mail-from': 'Email address the report is from.' |
---|
| 59 | } |
---|
| 60 | |
---|
[50fdf12] | 61 | def append_options(opts): |
---|
[7051ba5] | 62 | for o in _options: |
---|
| 63 | opts[o] = _options[o] |
---|
| 64 | |
---|
| 65 | def add_arguments(argsp): |
---|
| 66 | argsp.add_argument('--mail', help = _options['--mail'], action = 'store_true') |
---|
| 67 | for o in ['--smtp-host', '--mail-to', '--mail-from']: |
---|
| 68 | argsp.add_argument(o, help = _options[o], type = str) |
---|
[50fdf12] | 69 | |
---|
| 70 | class mail: |
---|
| 71 | def __init__(self, opts): |
---|
| 72 | self.opts = opts |
---|
| 73 | |
---|
[7051ba5] | 74 | def _args_are_macros(self): |
---|
| 75 | return type(self.opts) is 'command_line' |
---|
| 76 | |
---|
| 77 | def _get_arg(self, arg): |
---|
| 78 | if self._args_are_macros(): |
---|
| 79 | value = self.opts.find_arg(arg)[1] |
---|
| 80 | else: |
---|
| 81 | if arg.startswith('--'): |
---|
| 82 | arg = arg[2:] |
---|
| 83 | arg = arg.replace('-', '_') |
---|
| 84 | if arg in vars(self.opts): |
---|
| 85 | value = vars(self.opts)[arg] |
---|
| 86 | else: |
---|
| 87 | value = None |
---|
| 88 | return value |
---|
| 89 | |
---|
[50fdf12] | 90 | def from_address(self): |
---|
| 91 | |
---|
| 92 | def _clean(l): |
---|
| 93 | if '#' in l: |
---|
| 94 | l = l[:l.index('#')] |
---|
| 95 | if '\r' in l: |
---|
| 96 | l = l[:l.index('r')] |
---|
| 97 | if '\n' in l: |
---|
| 98 | l = l[:l.index('\n')] |
---|
| 99 | return l.strip() |
---|
| 100 | |
---|
[7051ba5] | 101 | addr = self._get_arg('--mail-from') |
---|
[50fdf12] | 102 | if addr is not None: |
---|
[7051ba5] | 103 | return addr |
---|
[50fdf12] | 104 | mailrc = None |
---|
| 105 | if 'MAILRC' in os.environ: |
---|
| 106 | mailrc = os.environ['MAILRC'] |
---|
| 107 | if mailrc is None and 'HOME' in os.environ: |
---|
| 108 | mailrc = path.join(os.environ['HOME'], '.mailrc') |
---|
| 109 | if mailrc is not None and path.exists(mailrc): |
---|
| 110 | # set from="Joe Blow <joe@blow.org>" |
---|
| 111 | try: |
---|
[dc5de5f] | 112 | with open(mailrc, 'r') as mrc: |
---|
| 113 | lines = mrc.readlines() |
---|
[04a5204] | 114 | except IOError as err: |
---|
[50fdf12] | 115 | raise error.general('error reading: %s' % (mailrc)) |
---|
| 116 | for l in lines: |
---|
| 117 | l = _clean(l) |
---|
| 118 | if 'from' in l: |
---|
| 119 | fa = l[l.index('from') + len('from'):] |
---|
| 120 | if '=' in fa: |
---|
| 121 | addr = fa[fa.index('=') + 1:].replace('"', ' ').strip() |
---|
| 122 | if addr is not None: |
---|
| 123 | return addr |
---|
[7051ba5] | 124 | if self._args_are_macros(): |
---|
| 125 | addr = self.opts.defaults.get_value('%{_sbgit_mail}') |
---|
| 126 | else: |
---|
| 127 | raise error.general('no valid from address for mail') |
---|
[50fdf12] | 128 | return addr |
---|
| 129 | |
---|
| 130 | def smtp_host(self): |
---|
[7051ba5] | 131 | host = self._get_arg('--smtp-host') |
---|
[50fdf12] | 132 | if host is not None: |
---|
| 133 | return host[1] |
---|
[7051ba5] | 134 | if self._args_are_macros(): |
---|
| 135 | host = self.opts.defaults.get_value('%{_mail_smtp_host}') |
---|
[50fdf12] | 136 | if host is not None: |
---|
| 137 | return host |
---|
| 138 | return 'localhost' |
---|
| 139 | |
---|
| 140 | def send(self, to_addr, subject, body): |
---|
| 141 | from_addr = self.from_address() |
---|
| 142 | msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % \ |
---|
| 143 | (from_addr, to_addr, subject) + body |
---|
| 144 | try: |
---|
| 145 | s = smtplib.SMTP(self.smtp_host()) |
---|
| 146 | s.sendmail(from_addr, [to_addr], msg) |
---|
[04a5204] | 147 | except smtplib.SMTPException as se: |
---|
[50fdf12] | 148 | raise error.general('sending mail: %s' % (str(se))) |
---|
[04a5204] | 149 | except socket.error as se: |
---|
[50fdf12] | 150 | raise error.general('sending mail: %s' % (str(se))) |
---|
| 151 | |
---|
[dc5de5f] | 152 | def send_file_as_body(self, to_addr, subject, name, intro = None): |
---|
| 153 | try: |
---|
| 154 | with open(name, 'r') as f: |
---|
| 155 | body = f.readlines() |
---|
| 156 | except IOError as err: |
---|
| 157 | raise error.general('error reading mail body: %s' % (name)) |
---|
| 158 | if intro is not None: |
---|
| 159 | body = intro + body |
---|
| 160 | self.send(to_addr, from_addr, body) |
---|
| 161 | |
---|
[50fdf12] | 162 | if __name__ == '__main__': |
---|
| 163 | import sys |
---|
| 164 | optargs = {} |
---|
| 165 | append_options(optargs) |
---|
| 166 | opts = options.load(sys.argv, optargs = optargs, defaults = 'defaults.mc') |
---|
| 167 | m = mail(opts) |
---|
[04a5204] | 168 | print('From: %s' % (m.from_address())) |
---|
| 169 | print('SMTP Host: %s' % (m.smtp_host())) |
---|
[50fdf12] | 170 | m.send(m.from_address(), 'Test mailer.py', 'This is a test') |
---|