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, div):
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 self.div = div
50
51 - def fetchImage(self, url):
52 return self.slurp(url).read()
53
54 - def absolute(self, url):
55 url = url.strip()
56 if url.startswith('http:'):
57 return url
58 if url.startswith('/'):
59 base = list(urlparse(self.base))
60 base[2] = url
61 return urlunparse(base[0:3] + ['', '',''])
62 return sibling(self.base, url)
63
64 - def alter(self, attrs, name, function):
65 result = []
66 for a, v in attrs:
67 if a.lower() == name:
68 v = function(v)
69 result.append( (a, v) )
70 return result
71
72 - def updateSrc(self, attrs):
73 def cache(v):
74 if v not in self.images:
75 v = self.absolute(v)
76 name = 'img%s.png' % md5.md5(v).hexdigest()
77 self.images[v] = (name, self.fetchImage(v))
78 v, _ = self.images[v]
79 return 'cid:%s' % v
80 return self.alter(attrs, 'src', cache)
81
82 - def updateHref(self, attrs):
83 return self.alter(attrs, 'href', self.absolute)
84
85 - def handle_starttag(self, tag, attrs):
86 tag = tag.upper()
87 if tag == 'TITLE':
88 self.inTitle = True
89 if tag == 'IMG':
90 attrs = self.updateSrc(attrs)
91 if tag == 'A':
92 attrs = self.updateHref(attrs)
93 if tag == 'DIV':
94 if ('id',self.div) in attrs:
95 self.contentPane = 1
96 elif self.contentPane:
97 self.contentPane += 1
98 if self.contentPane:
99 attrs = ' '.join([("%s=%s" % (a, repr(v))) for a, v in attrs])
100 if attrs: attrs = ' ' + attrs
101 self.html.append('<%s%s>' % (tag, attrs))
102
103 - def handle_endtag(self, tag):
104 tag = tag.upper()
105 if tag == 'TITLE':
106 self.inTitle = False
107 if self.contentPane:
108 self.html.append('</%s>' % tag.upper())
109 if tag == 'DIV':
110 if self.contentPane:
111 self.contentPane -= 1
112
113 - def handle_data(self, data):
114 if self.contentPane:
115 self.html.append(data)
116 if self.inTitle:
117 self.title += data
118
119 - def slurp(self, url):
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 try:
124 result = urllib2.urlopen(req)
125 except urllib2.HTTPError:
126 import StringIO
127 result = StringIO.StringIO('')
128 return result
129
130 - def fetch(self, url):
131 self.base = url.strip()
132 self.feed(self.slurp(url).read())
133
135 msg = MIMEMultipart('related')
136 msg.preamble = 'This is a multi-part message in MIME format'
137 txt = MIMEText(''.join(self.html), 'html')
138 msg.attach(txt)
139 for url, (name, img) in self.images.items():
140 ctype, encoding = mimetypes.guess_type(url)
141 if ctype == None:
142 ctype = 'image/png'
143 maintype, subtype = ctype.split('/', 1)
144 img = MIMEImage(img, subtype)
145 img.add_header('Content-ID', '<%s>' % name)
146 msg.attach(img)
147 msg['Subject'] = self.title
148 return msg
149
152
154
156 'Fetch a report by URL and post as a mime encoded email'
157 self.connect()
158 o = self.options
159 if not o.passwd and not o.url:
160 self.log.error("No zenoss password or url provided")
161 sys.exit(1)
162 try:
163 user = self.dmd.ZenUsers._getOb(o.user)
164 except AttributeError:
165 self.log.error("Unknown user %s" % o.user)
166 sys.exit(1)
167
168 if not o.addresses and user.email:
169 o.addresses = [user.email]
170 if not o.addresses:
171 self.log.error("No address for user %s" % o.user)
172 sys.exit(1)
173 page = Page(o.user, o.passwd, o.div)
174 page.fetch(o.url)
175 msg = page.mail()
176 if o.subject:
177 msg['Subject'] = o.subject
178 msg['From'] = o.fromAddress
179 msg['To'] = ', '.join(o.addresses)
180 result, errorMsg = Utils.sendEmail(msg,
181 self.dmd.smtpHost,
182 self.dmd.smtpPort,
183 self.dmd.smtpUseTLS,
184 self.dmd.smtpUser,
185 self.dmd.smtpPass)
186 if result:
187 self.log.debug("sent email: %s to:%s", msg.as_string(), o.addresses)
188 else:
189 self.log.info("failed to send email to %s: %s %s",
190 o.addresses, msg.as_string(), errorMsg)
191 sys.exit(1)
192 sys.exit(0)
193
195 ZenScriptBase.buildOptions(self)
196 self.parser.add_option('--url', '-u',
197 dest='url',
198 default=None,
199 help='URL of report to send')
200 self.parser.add_option('--user', '-U',
201 dest='user',
202 default='admin',
203 help="User to log into Zenoss")
204 self.parser.add_option('--passwd', '-p',
205 dest='passwd',
206 help="Password to log into Zenoss")
207 self.parser.add_option('--address', '-a',
208 dest='addresses',
209 default=[],
210 action='append',
211 help='Email address destination '
212 '(may be given more than once). Default value'
213 "comes from the user's profile.")
214 self.parser.add_option('--subject', '-s',
215 dest='subject',
216 default='',
217 help='Subject line for email message.'
218 'Default value is the title of the html page.')
219 self.parser.add_option('--from', '-f',
220 dest='fromAddress',
221 default='zenoss@localhost',
222 help='Origination address')
223 self.parser.add_option('--div', '-d',
224 dest='div',
225 default='contentPane',
226 help='DIV to extract from URL')
227
228
229 if __name__ == '__main__':
230 ReportMail().run()
231