1
2
3
4
5
6
7
8
9
10
11
12
13
14 import sys
15 import base64
16 import urllib2
17 from HTMLParser import HTMLParser
18 from urlparse import urlparse, urlunparse
19 import mimetypes
20 from email.MIMEText import MIMEText
21 from email.MIMEMultipart import MIMEMultipart
22 from email.MIMEImage import MIMEImage
23
24 import Globals
25 from Products.ZenUtils.ZenScriptBase import ZenScriptBase
26 from Products.ZenUtils import Utils
27 import md5
28
30 parts = list(urlparse(url))
31 parts[2] = '/'.join(parts[2].split('/')[:-1] + [path])
32 return urlunparse(parts[0:3] + ['', '',''])
33
34 -class Page(HTMLParser):
35 """Turn an html page into a mime-encoded multi-part email.
36 Turn the <title> into the subject and keep only the text of the
37 content pane. Url references are turned into absolute references,
38 and images are sent with the page."""
39
40 - def __init__(self, user, passwd):
41 HTMLParser.__init__(self)
42 self.user = user
43 self.passwd = passwd
44 self.html = []
45 self.images = {}
46 self.contentPane = 0
47 self.inTitle = False
48 self.title = ''
49
50 - def fetchImage(self, url):
51 return self.slurp(url).read()
52
53 - def absolute(self, url):
54 url = url.strip()
55 if url.startswith('http:'):
56 return url
57 if url.startswith('/'):
58 base = list(urlparse(self.base))
59 base[2] = url
60 return urlunparse(base[0:3] + ['', '',''])
61 return sibling(self.base, url)
62
63 - def alter(self, attrs, name, function):
64 result = []
65 for a, v in attrs:
66 if a.lower() == name:
67 v = function(v)
68 result.append( (a, v) )
69 return result
70
71 - def updateSrc(self, attrs):
72 def cache(v):
73 if v not in self.images:
74 v = self.absolute(v)
75 name = 'img%s.png' % md5.md5(v).hexdigest()
76 self.images[v] = (name, self.fetchImage(v))
77 v, _ = self.images[v]
78 return 'cid:%s' % v
79 return self.alter(attrs, 'src', cache)
80
81 - def updateHref(self, attrs):
82 return self.alter(attrs, 'href', self.absolute)
83
84 - def handle_starttag(self, tag, attrs):
85 tag = tag.upper()
86 if tag == 'TITLE':
87 self.inTitle = True
88 if tag == 'IMG':
89 attrs = self.updateSrc(attrs)
90 if tag == 'A':
91 attrs = self.updateHref(attrs)
92 if tag == 'DIV':
93 if ('id','contentPane') in attrs:
94 self.contentPane = 1
95 elif self.contentPane:
96 self.contentPane += 1
97 if self.contentPane:
98 attrs = ' '.join([("%s=%s" % (a, repr(v))) for a, v in attrs])
99 if attrs: attrs = ' ' + attrs
100 self.html.append('<%s%s>' % (tag, attrs))
101
102 - def handle_endtag(self, tag):
103 tag = tag.upper()
104 if tag == 'TITLE':
105 self.inTitle = False
106 if self.contentPane:
107 self.html.append('</%s>' % tag.upper())
108 if tag == 'DIV':
109 if self.contentPane:
110 self.contentPane -= 1
111
112 - def handle_data(self, data):
113 if self.contentPane:
114 self.html.append(data)
115 if self.inTitle:
116 self.title += data
117
118 - def slurp(self, url):
119 self.base = url.strip()
120 req = urllib2.Request(url)
121 encoded = base64.encodestring('%s:%s' % (self.user, self.passwd))[:-1]
122 req.add_header("Authorization", "Basic %s" % encoded)
123 return urllib2.urlopen(req)
124
125 - def fetch(self, url):
126 self.feed(self.slurp(url).read())
127
129 msg = MIMEMultipart('related')
130 msg.preamble = 'This is a multi-part message in MIME format'
131 txt = MIMEText(''.join(self.html), 'html')
132 msg.attach(txt)
133 for url, (name, img) in self.images.items():
134 ctype, encoding = mimetypes.guess_type(url)
135 if ctype == None:
136 ctype = 'image/png'
137 maintype, subtype = ctype.split('/', 1)
138 img = MIMEImage(img, subtype)
139 fname = url.rsplit('/', 1)[1]
140 img.add_header('Content-ID', '<%s>' % name)
141 msg.attach(img)
142 msg['Subject'] = self.title
143 return msg
144
147
149
151 'Fetch a report by URL and post as a mime encoded email'
152 self.connect()
153 o = self.options
154 if not o.passwd and not o.url:
155 self.log.error("No zenoss password or url provided")
156 sys.exit(1)
157 try:
158 user = self.dmd.ZenUsers._getOb(o.user)
159 except AttributeError:
160 self.log.error("Unknown user %s" % o.user)
161 sys.exit(1)
162
163 if not o.addresses and user.email:
164 o.addresses = [user.email]
165 if not o.addresses:
166 self.log.error("No address for user %s" % o.user)
167 sys.exit(1)
168 page = Page(o.user, o.passwd)
169 page.fetch(o.url)
170 msg = page.mail()
171 if o.subject:
172 msg['Subject'] = o.subject
173 msg['From'] = o.fromAddress
174 msg['To'] = ', '.join(o.addresses)
175 result, errorMsg = Utils.sendEmail(msg,
176 self.dmd.smtpHost,
177 self.dmd.smtpPort,
178 self.dmd.smtpUseTLS,
179 self.dmd.smtpUser,
180 self.dmd.smtpPass)
181 if result:
182 self.log.debug("sent email: %s to:%s", msg.as_string(), o.addresses)
183 else:
184 self.log.info("failed to send email to %s: %s %s",
185 o.addresses, msg.as_string(), errorMsg)
186 sys.exit(1)
187 sys.exit(0)
188
190 ZenScriptBase.buildOptions(self)
191 self.parser.add_option('--url', '-u',
192 dest='url',
193 default=None,
194 help='URL of report to send')
195 self.parser.add_option('--user', '-U',
196 dest='user',
197 default='admin',
198 help="User to log into Zenoss")
199 self.parser.add_option('--passwd', '-p',
200 dest='passwd',
201 help="Password to log into Zenoss")
202 self.parser.add_option('--address', '-a',
203 dest='addresses',
204 default=[],
205 action='append',
206 help='Email address destination '
207 '(may be given more than once). Default value'
208 "comes from the user's profile.")
209 self.parser.add_option('--subject', '-s',
210 dest='subject',
211 default='',
212 help='Subject line for email message.'
213 'Default value is the title of the html page.')
214 self.parser.add_option('--from', '-f',
215 dest='fromAddress',
216 default='zenoss@localhost',
217 help='Origination address')
218
219
220 if __name__ == '__main__':
221 ReportMail().run()
222