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, *args, 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 return True
109
110 _log.error('error installing data pack: %s', data_pack)
111 return False
112
114
115 if target_dir is None:
116 target_dir = gmTools.get_unique_filename(prefix = 'gm-dl-')
117
118 _log.debug('downloading [%s]', url)
119 _log.debug('unpacking into [%s]', target_dir)
120
121 gmTools.mkdir(directory = target_dir)
122
123
124
125 paths = gmTools.gmPaths()
126 local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-download_data')
127
128 candidates = [u'gm-download_data', u'gm-download_data.bat', local_script, u'gm-download_data.bat']
129 args = u' %s %s' % (url, target_dir)
130
131 success = gmShellAPI.run_first_available_in_shell (
132 binaries = candidates,
133 args = args,
134 blocking = True,
135 run_last_one_anyway = True
136 )
137
138 if success:
139 return True, target_dir
140
141 _log.error('download failed')
142 return False, None
143
144
145
146 -def check_for_update(url=None, current_branch=None, current_version=None, consider_latest_branch=False):
147 """Check for new releases at <url>.
148
149 Returns (bool, text).
150 True: new release available
151 False: up to date
152 None: don't know
153 """
154 try:
155 remote_file = wget.urlopen(url)
156 except (wget.URLError, ValueError, OSError):
157 _log.exception("cannot retrieve version file from [%s]", url)
158 return (None, _('Cannot retrieve version information from:\n\n%s') % url)
159
160 _log.debug('retrieving version information from [%s]', url)
161
162 cfg = gmCfg2.gmCfgData()
163 try:
164 cfg.add_stream_source(source = 'gm-versions', stream = remote_file)
165 except (UnicodeDecodeError):
166 remote_file.close()
167 _log.exception("cannot read version file from [%s]", url)
168 return (None, _('Cannot read version information from:\n\n%s') % url)
169
170 remote_file.close()
171
172 latest_branch = cfg.get('latest branch', 'branch', source_order = [('gm-versions', 'return')])
173 latest_release_on_latest_branch = cfg.get('branch %s' % latest_branch, 'latest release', source_order = [('gm-versions', 'return')])
174 latest_release_on_current_branch = cfg.get('branch %s' % current_branch, 'latest release', source_order = [('gm-versions', 'return')])
175
176 cfg.remove_source('gm-versions')
177
178 _log.info('current release: %s', current_version)
179 _log.info('current branch: %s', current_branch)
180 _log.info('latest release on current branch: %s', latest_release_on_current_branch)
181 _log.info('latest branch: %s', latest_branch)
182 _log.info('latest release on latest branch: %s', latest_release_on_latest_branch)
183
184
185 no_release_information_available = (
186 (
187 (latest_release_on_current_branch is None) and
188 (latest_release_on_latest_branch is None)
189 ) or (
190 not consider_latest_branch and
191 (latest_release_on_current_branch is None)
192 )
193 )
194 if no_release_information_available:
195 _log.warning('no release information available')
196 msg = _('There is no version information available from:\n\n%s') % url
197 return (None, msg)
198
199
200 if consider_latest_branch:
201 _log.debug('latest branch taken into account')
202 if current_version >= latest_release_on_latest_branch:
203 _log.debug('up to date: current version >= latest version on latest branch')
204 return (False, None)
205 if latest_release_on_latest_branch is None:
206 if current_version >= latest_release_on_current_branch:
207 _log.debug('up to date: current version >= latest version on current branch and no latest branch available')
208 return (False, None)
209 else:
210 _log.debug('latest branch not taken into account')
211 if current_version >= latest_release_on_current_branch:
212 _log.debug('up to date: current version >= latest version on current branch')
213 return (False, None)
214
215 new_release_on_current_branch_available = (
216 (latest_release_on_current_branch is not None) and
217 (latest_release_on_current_branch > current_version)
218 )
219 _log.info('%snew release on current branch available', gmTools.bool2str(new_release_on_current_branch_available, '', 'no '))
220
221 new_release_on_latest_branch_available = (
222 (latest_branch is not None)
223 and
224 (
225 (latest_branch > current_branch) or (
226 (latest_branch == current_branch) and
227 (latest_release_on_latest_branch > current_version)
228 )
229 )
230 )
231 _log.info('%snew release on latest branch available', gmTools.bool2str(new_release_on_latest_branch_available, '', 'no '))
232
233 if not (new_release_on_current_branch_available or new_release_on_latest_branch_available):
234 _log.debug('up to date: no new releases available')
235 return (False, None)
236
237
238 msg = _('A new version of GNUmed is available.\n\n')
239 msg += _(' Your current version: "%s"\n') % current_version
240 if consider_latest_branch:
241 if new_release_on_current_branch_available:
242 msg += u'\n'
243 msg += _(' New version: "%s"') % latest_release_on_current_branch
244 msg += u'\n'
245 msg += _(' - bug fixes only\n')
246 msg += _(' - database fixups may be needed\n')
247 if new_release_on_latest_branch_available:
248 if current_branch != latest_branch:
249 msg += u'\n'
250 msg += _(' New version: "%s"') % latest_release_on_latest_branch
251 msg += u'\n'
252 msg += _(' - bug fixes and new features\n')
253 msg += _(' - database upgrade required\n')
254 else:
255 msg += u'\n'
256 msg += _(' New version: "%s"') % latest_release_on_current_branch
257 msg += u'\n'
258 msg += _(' - bug fixes only\n')
259 msg += _(' - database fixups may be needed\n')
260
261 msg += u'\n\n'
262 msg += _(
263 'Note, however, that this version may not yet\n'
264 'be available *pre-packaged* for your system.'
265 )
266
267 msg += u'\n\n'
268 msg += _('Details are found on <http://wiki.gnumed.de>.\n')
269 msg += u'\n'
270 msg += _('Version information loaded from:\n\n %s') % url
271
272 return (True, msg)
273
274
275
276 default_mail_sender = u'gnumed@gmx.net'
277 default_mail_receiver = u'gnumed-devel@gnu.org'
278 default_mail_server = u'mail.gmx.net'
279
280 -def send_mail(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='quoted-printable', attachments=None):
281
282
283
284
285 if message is None:
286 return False
287
288 message = message.lstrip().lstrip('\r\n').lstrip()
289
290 if sender is None:
291 sender = default_mail_sender
292
293 if receiver is None:
294 receiver = [default_mail_receiver]
295
296 if server is None:
297 server = default_mail_server
298
299 if subject is None:
300 subject = u'gmTools.py: send_mail() test'
301
302 msg = StringIO.StringIO()
303 writer = MimeWriter.MimeWriter(msg)
304 writer.addheader('To', u', '.join(receiver))
305 writer.addheader('From', sender)
306 writer.addheader('Subject', subject[:50].replace('\r', '/').replace('\n', '/'))
307 writer.addheader('MIME-Version', '1.0')
308
309 writer.startmultipartbody('mixed')
310
311
312 part = writer.nextpart()
313 body = part.startbody('text/plain')
314 part.flushheaders()
315 body.write(message.encode(encoding))
316
317
318 if attachments is not None:
319 for a in attachments:
320 filename = os.path.basename(a[0])
321 try:
322 mtype = a[1]
323 encoding = a[2]
324 except IndexError:
325 mtype, encoding = mimetypes.guess_type(a[0])
326 if mtype is None:
327 mtype = 'application/octet-stream'
328 encoding = 'base64'
329 elif mtype == 'text/plain':
330 encoding = 'quoted-printable'
331 else:
332 encoding = 'base64'
333
334 part = writer.nextpart()
335 part.addheader('Content-Transfer-Encoding', encoding)
336 body = part.startbody("%s; name=%s" % (mtype, filename))
337 mimetools.encode(open(a[0], 'rb'), body, encoding)
338
339 writer.lastpart()
340
341 import smtplib
342 session = smtplib.SMTP(server)
343 session.set_debuglevel(debug)
344 if auth is not None:
345 session.login(auth['user'], auth['password'])
346 refused = session.sendmail(sender, receiver, msg.getvalue())
347 session.quit()
348 msg.close()
349 if len(refused) != 0:
350 _log.error("refused recipients: %s" % refused)
351 return False
352
353 return True
354
355
356
357 if __name__ == '__main__':
358
359 if len(sys.argv) < 2:
360 sys.exit()
361
362 if sys.argv[1] != 'test':
363 sys.exit()
364
365
381
383
384 test_data = [
385 ('http://www.gnumed.de/downloads/gnumed-versions.txt', None, None, False),
386 ('file:///home/ncq/gm-versions.txt', None, None, False),
387 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', False),
388 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', True),
389 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.5', True)
390 ]
391
392 for test in test_data:
393 print "arguments:", test
394 found, msg = check_for_update(test[0], test[1], test[2], test[3])
395 print msg
396
397 return
398
400
401
402 url = 'gmTools.py'
403 dl_name = download_data_pack(url)
404 print url, "->", dl_name
405 unzip_dir = unzip_data_pack(dl_name)
406 print "unzipped into", unzip_dir
407
412
413
414
415
416 test_browser()
417
418
419