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