1
2 __doc__ = """GNUmed internetworking tools."""
3
4
5 __version__ = "$Revision: 1.98 $"
6 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
7 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
8
9
10 import sys
11 import os.path
12 import logging
13 import urllib2 as wget
14 import urllib
15 import MimeWriter
16 import mimetypes
17 import mimetools
18 import StringIO
19 import zipfile
20 import webbrowser
21
22
23
24 if __name__ == '__main__':
25 sys.path.insert(0, '../../')
26 from Gnumed.pycommon import gmLog2
27 from Gnumed.pycommon import gmTools
28 from Gnumed.pycommon import gmShellAPI
29 from Gnumed.pycommon import gmCfg2
30
31
32 _log = logging.getLogger('gm.net')
33
34
35
36
38
39 try:
40 webbrowser.open(url, new = new, autoraise = autoraise, **kwargs)
41 except (webbrowser.Error, OSError):
42 _log.exception('error calling browser')
43 return False
44 return True
45
61
62
63
66
68
69 _log.debug('downloading data pack from: %s', pack_url)
70 dp_fname = download_file(pack_url, filename = filename, suffix = 'zip')
71 _log.debug('downloading MD5 from: %s', md5_url)
72 md5_fname = download_file(md5_url, filename = dp_fname + u'.md5')
73
74 md5_file = open(md5_fname, 'rU')
75 md5_expected = md5_file.readline().strip('\n')
76 md5_file.close()
77 _log.debug('expected MD5: %s', md5_expected)
78 md5_calculated = gmTools.file2md5(dp_fname, return_hex = True)
79 _log.debug('calculated MD5: %s', md5_calculated)
80
81 if md5_calculated != md5_expected:
82 _log.error('mismatch of expected vs calculated MD5: [%s] vs [%s]', md5_expected, md5_calculated)
83 return (False, (md5_expected, md5_calculated))
84
85 return True, dp_fname
86
88
89 unzip_dir = os.path.splitext(filename)[0]
90 _log.debug('unzipping data pack into [%s]', unzip_dir)
91 gmTools.mkdir(unzip_dir)
92 try:
93 data_pack = zipfile.ZipFile(filename, 'r')
94 except (zipfile.BadZipfile):
95 _log.exception('cannot unzip data pack [%s]', filename)
96 gmLog2.log_stack_trace()
97 return None
98
99 data_pack.extractall(unzip_dir)
100
101 return unzip_dir
102
104 from Gnumed.pycommon import gmPsql
105 psql = gmPsql.Psql(conn)
106 sql_script = os.path.join(data_pack['unzip_dir'], 'install-data-pack.sql')
107 if psql.run(sql_script) == 0:
108 curs = conn.cursor()
109 curs.execute(u'select gm.log_script_insertion(%(name)s, %(ver)s)', {'name': data_pack['pack_url'], 'ver': u'current'})
110 curs.close()
111 conn.commit()
112 return True
113
114 _log.error('error installing data pack: %s', data_pack)
115 return False
116
118
119 if target_dir is None:
120 target_dir = gmTools.get_unique_filename(prefix = 'gm-dl-')
121
122 _log.debug('downloading [%s]', url)
123 _log.debug('unpacking into [%s]', target_dir)
124
125 gmTools.mkdir(directory = target_dir)
126
127
128
129 paths = gmTools.gmPaths()
130 local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-download_data')
131
132 candidates = [u'gm-download_data', u'gm-download_data.bat', local_script, u'gm-download_data.bat']
133 args = u' %s %s' % (url, target_dir)
134
135 success = gmShellAPI.run_first_available_in_shell (
136 binaries = candidates,
137 args = args,
138 blocking = True,
139 run_last_one_anyway = True
140 )
141
142 if success:
143 return True, target_dir
144
145 _log.error('download failed')
146 return False, None
147
148
149
150 -def check_for_update(url=None, current_branch=None, current_version=None, consider_latest_branch=False):
151 """Check for new releases at <url>.
152
153 Returns (bool, text).
154 True: new release available
155 False: up to date
156 None: don't know
157 """
158 try:
159 remote_file = wget.urlopen(url)
160 except (wget.URLError, ValueError, OSError):
161 _log.exception("cannot retrieve version file from [%s]", url)
162 return (None, _('Cannot retrieve version information from:\n\n%s') % url)
163
164 _log.debug('retrieving version information from [%s]', url)
165
166 cfg = gmCfg2.gmCfgData()
167 try:
168 cfg.add_stream_source(source = 'gm-versions', stream = remote_file)
169 except (UnicodeDecodeError):
170 remote_file.close()
171 _log.exception("cannot read version file from [%s]", url)
172 return (None, _('Cannot read version information from:\n\n%s') % url)
173
174 remote_file.close()
175
176 latest_branch = cfg.get('latest branch', 'branch', source_order = [('gm-versions', 'return')])
177 latest_release_on_latest_branch = cfg.get('branch %s' % latest_branch, 'latest release', source_order = [('gm-versions', 'return')])
178 latest_release_on_current_branch = cfg.get('branch %s' % current_branch, 'latest release', source_order = [('gm-versions', 'return')])
179
180 cfg.remove_source('gm-versions')
181
182 _log.info('current release: %s', current_version)
183 _log.info('current branch: %s', current_branch)
184 _log.info('latest release on current branch: %s', latest_release_on_current_branch)
185 _log.info('latest branch: %s', latest_branch)
186 _log.info('latest release on latest branch: %s', latest_release_on_latest_branch)
187
188
189 no_release_information_available = (
190 (
191 (latest_release_on_current_branch is None) and
192 (latest_release_on_latest_branch is None)
193 ) or (
194 not consider_latest_branch and
195 (latest_release_on_current_branch is None)
196 )
197 )
198 if no_release_information_available:
199 _log.warning('no release information available')
200 msg = _('There is no version information available from:\n\n%s') % url
201 return (None, msg)
202
203
204 if consider_latest_branch:
205 _log.debug('latest branch taken into account')
206 if current_version >= latest_release_on_latest_branch:
207 _log.debug('up to date: current version >= latest version on latest branch')
208 return (False, None)
209 if latest_release_on_latest_branch is None:
210 if current_version >= latest_release_on_current_branch:
211 _log.debug('up to date: current version >= latest version on current branch and no latest branch available')
212 return (False, None)
213 else:
214 _log.debug('latest branch not taken into account')
215 if current_version >= latest_release_on_current_branch:
216 _log.debug('up to date: current version >= latest version on current branch')
217 return (False, None)
218
219 new_release_on_current_branch_available = (
220 (latest_release_on_current_branch is not None) and
221 (latest_release_on_current_branch > current_version)
222 )
223 _log.info('%snew release on current branch available', gmTools.bool2str(new_release_on_current_branch_available, '', 'no '))
224
225 new_release_on_latest_branch_available = (
226 (latest_branch is not None)
227 and
228 (
229 (latest_branch > current_branch) or (
230 (latest_branch == current_branch) and
231 (latest_release_on_latest_branch > current_version)
232 )
233 )
234 )
235 _log.info('%snew release on latest branch available', gmTools.bool2str(new_release_on_latest_branch_available, '', 'no '))
236
237 if not (new_release_on_current_branch_available or new_release_on_latest_branch_available):
238 _log.debug('up to date: no new releases available')
239 return (False, None)
240
241
242 msg = _('A new version of GNUmed is available.\n\n')
243 msg += _(' Your current version: "%s"\n') % current_version
244 if consider_latest_branch:
245 if new_release_on_current_branch_available:
246 msg += u'\n'
247 msg += _(' New version: "%s"') % latest_release_on_current_branch
248 msg += u'\n'
249 msg += _(' - bug fixes only\n')
250 msg += _(' - database fixups may be needed\n')
251 if new_release_on_latest_branch_available:
252 if current_branch != latest_branch:
253 msg += u'\n'
254 msg += _(' New version: "%s"') % latest_release_on_latest_branch
255 msg += u'\n'
256 msg += _(' - bug fixes and new features\n')
257 msg += _(' - database upgrade required\n')
258 else:
259 msg += u'\n'
260 msg += _(' New version: "%s"') % latest_release_on_current_branch
261 msg += u'\n'
262 msg += _(' - bug fixes only\n')
263 msg += _(' - database fixups may be needed\n')
264
265 msg += u'\n\n'
266 msg += _(
267 'Note, however, that this version may not yet\n'
268 'be available *pre-packaged* for your system.'
269 )
270
271 msg += u'\n\n'
272 msg += _('Details are found on <http://wiki.gnumed.de>.\n')
273 msg += u'\n'
274 msg += _('Version information loaded from:\n\n %s') % url
275
276 return (True, msg)
277
278
279
280 default_mail_sender = u'gnumed@gmx.net'
281 default_mail_receiver = u'gnumed-devel@gnu.org'
282 default_mail_server = u'mail.gmx.net'
283
284 -def send_mail(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='quoted-printable', attachments=None):
285
286
287
288
289 if message is None:
290 return False
291
292 message = message.lstrip().lstrip('\r\n').lstrip()
293
294 if sender is None:
295 sender = default_mail_sender
296
297 if receiver is None:
298 receiver = [default_mail_receiver]
299
300 if server is None:
301 server = default_mail_server
302
303 if subject is None:
304 subject = u'gmTools.py: send_mail() test'
305
306 msg = StringIO.StringIO()
307 writer = MimeWriter.MimeWriter(msg)
308 writer.addheader('To', u', '.join(receiver))
309 writer.addheader('From', sender)
310 writer.addheader('Subject', subject[:50].replace('\r', '/').replace('\n', '/'))
311 writer.addheader('MIME-Version', '1.0')
312
313 writer.startmultipartbody('mixed')
314
315
316 part = writer.nextpart()
317 body = part.startbody('text/plain')
318 part.flushheaders()
319 body.write(message.encode(encoding))
320
321
322 if attachments is not None:
323 for a in attachments:
324 filename = os.path.basename(a[0])
325 try:
326 mtype = a[1]
327 encoding = a[2]
328 except IndexError:
329 mtype, encoding = mimetypes.guess_type(a[0])
330 if mtype is None:
331 mtype = 'application/octet-stream'
332 encoding = 'base64'
333 elif mtype == 'text/plain':
334 encoding = 'quoted-printable'
335 else:
336 encoding = 'base64'
337
338 part = writer.nextpart()
339 part.addheader('Content-Transfer-Encoding', encoding)
340 body = part.startbody("%s; name=%s" % (mtype, filename))
341 mimetools.encode(open(a[0], 'rb'), body, encoding)
342
343 writer.lastpart()
344
345 import smtplib
346 session = smtplib.SMTP(server)
347 session.set_debuglevel(debug)
348 if auth is not None:
349 session.login(auth['user'], auth['password'])
350 refused = session.sendmail(sender, receiver, msg.getvalue())
351 session.quit()
352 msg.close()
353 if len(refused) != 0:
354 _log.error("refused recipients: %s" % refused)
355 return False
356
357 return True
358
359
360
361 if __name__ == '__main__':
362
363 if len(sys.argv) < 2:
364 sys.exit()
365
366 if sys.argv[1] != 'test':
367 sys.exit()
368
369
385
387
388 test_data = [
389 ('http://www.gnumed.de/downloads/gnumed-versions.txt', None, None, False),
390 ('file:///home/ncq/gm-versions.txt', None, None, False),
391 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', False),
392 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', True),
393 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.5', True)
394 ]
395
396 for test in test_data:
397 print "arguments:", test
398 found, msg = check_for_update(test[0], test[1], test[2], test[3])
399 print msg
400
401 return
402
404
405
406 url = 'gmTools.py'
407 dl_name = download_data_pack(url)
408 print url, "->", dl_name
409 unzip_dir = unzip_data_pack(dl_name)
410 print "unzipped into", unzip_dir
411
416
417
418
419
420 test_browser()
421
422
423