Package Gnumed :: Package pycommon :: Module gmNetworkTools
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmNetworkTools

  1  # -*- coding: utf8 -*- 
  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  # std libs 
 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  # GNUmed libs 
 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  # browser access 
 36  #--------------------------------------------------------------------------- 
37 -def open_url_in_browser(url, new=2, autoraise=True, *args, **kwargs):
38 # url, new=0, autoraise=True 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 #===========================================================================
46 -def download_file(url, filename=None, suffix=None):
47 48 if filename is None: 49 filename = gmTools.get_unique_filename(prefix = 'gm-dl-', suffix = suffix) 50 _log.debug('downloading [%s] into [%s]', url, filename) 51 52 try: 53 dl_name, headers = urllib.urlretrieve(url, filename) 54 except (ValueError, OSError, IOError): 55 _log.exception('cannot download from [%s]', url) 56 gmLog2.log_stack_trace() 57 return None 58 59 _log.debug(u'%s' % headers) 60 return dl_name
61 #=========================================================================== 62 # data pack handling 63 #---------------------------------------------------------------------------
64 -def download_data_packs_list(url, filename=None):
65 return download_file(url, filename = filename, suffix = 'conf')
66 #---------------------------------------------------------------------------
67 -def download_data_pack(pack_url, filename=None, md5_url=None):
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 #---------------------------------------------------------------------------
87 -def unzip_data_pack(filename=None):
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 #---------------------------------------------------------------------------
103 -def install_data_pack(data_pack=None, conn=None):
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 #---------------------------------------------------------------------------
117 -def download_data_pack_old(url, target_dir=None):
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 # FIXME: rewrite to use urllib.urlretrieve() and 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 # client update handling 149 #---------------------------------------------------------------------------
150 -def compare_versions(left_version, right_version):
151 """ 152 0: left == right 153 -1: left < right 154 1: left > right 155 """ 156 if left_version == right_version: 157 _log.debug('same version: [%s] = [%s]', left_version, right_version) 158 return 0 159 160 left_parts = left_version.split('.') 161 right_parts = right_version.split('.') 162 163 tmp, left_major = gmTools.input2decimal(u'%s.%s' % (left_parts[0], left_parts[1])) 164 tmp, right_major = gmTools.input2decimal(u'%s.%s' % (right_parts[0], right_parts[1])) 165 166 if left_major < right_major: 167 _log.debug('left version [%s] < right version [%s]: major part', left_version, right_version) 168 return -1 169 170 if left_major > right_major: 171 _log.debug('left version [%s] > right version [%s]: major part', left_version, right_version) 172 return 1 173 174 tmp, left_part3 = gmTools.input2decimal(left_parts[2].replace('rc', '0.')) 175 tmp, right_part3 = gmTools.input2decimal(right_parts[2].replace('rc', '0.')) 176 177 if left_part3 < right_part3: 178 _log.debug('left version [%s] < right version [%s]: minor part', left_version, right_version) 179 return -1 180 181 if left_part3 > right_part3: 182 _log.debug('left version [%s] > right version [%s]: minor part', left_version, right_version) 183 return 1 184 185 return 0
186 #---------------------------------------------------------------------------
187 -def check_for_update(url=None, current_branch=None, current_version=None, consider_latest_branch=False):
188 """Check for new releases at <url>. 189 190 Returns (bool, text). 191 True: new release available 192 False: up to date 193 None: don't know 194 """ 195 if current_version == u'GIT HEAD': 196 _log.debug('GIT HEAD always up to date') 197 return (False, None) 198 199 try: 200 remote_file = wget.urlopen(url) 201 except (wget.URLError, ValueError, OSError): 202 _log.exception("cannot retrieve version file from [%s]", url) 203 return (None, _('Cannot retrieve version information from:\n\n%s') % url) 204 205 _log.debug('retrieving version information from [%s]', url) 206 207 cfg = gmCfg2.gmCfgData() 208 try: 209 cfg.add_stream_source(source = 'gm-versions', stream = remote_file) 210 except (UnicodeDecodeError): 211 remote_file.close() 212 _log.exception("cannot read version file from [%s]", url) 213 return (None, _('Cannot read version information from:\n\n%s') % url) 214 215 remote_file.close() 216 217 latest_branch = cfg.get('latest branch', 'branch', source_order = [('gm-versions', 'return')]) 218 latest_release_on_latest_branch = cfg.get('branch %s' % latest_branch, 'latest release', source_order = [('gm-versions', 'return')]) 219 latest_release_on_current_branch = cfg.get('branch %s' % current_branch, 'latest release', source_order = [('gm-versions', 'return')]) 220 221 cfg.remove_source('gm-versions') 222 223 _log.info('current release: %s', current_version) 224 _log.info('current branch: %s', current_branch) 225 _log.info('latest release on current branch: %s', latest_release_on_current_branch) 226 _log.info('latest branch: %s', latest_branch) 227 _log.info('latest release on latest branch: %s', latest_release_on_latest_branch) 228 229 # anything known ? 230 no_release_information_available = ( 231 ( 232 (latest_release_on_current_branch is None) and 233 (latest_release_on_latest_branch is None) 234 ) or ( 235 not consider_latest_branch and 236 (latest_release_on_current_branch is None) 237 ) 238 ) 239 if no_release_information_available: 240 _log.warning('no release information available') 241 msg = _('There is no version information available from:\n\n%s') % url 242 return (None, msg) 243 244 # up to date ? 245 if consider_latest_branch: 246 _log.debug('latest branch taken into account') 247 if latest_release_on_latest_branch is None: 248 if compare_versions(latest_release_on_current_branch, current_version) in [-1, 0]: 249 _log.debug('up to date: current version >= latest version on current branch and no latest branch available') 250 return (False, None) 251 else: 252 if compare_versions(latest_release_on_latest_branch, current_version) in [-1, 0]: 253 _log.debug('up to date: current version >= latest version on latest branch') 254 return (False, None) 255 else: 256 _log.debug('latest branch not taken into account') 257 if compare_versions(latest_release_on_current_branch, current_version) in [-1, 0]: 258 _log.debug('up to date: current version >= latest version on current branch') 259 return (False, None) 260 261 new_release_on_current_branch_available = ( 262 (latest_release_on_current_branch is not None) and 263 (compare_versions(latest_release_on_current_branch, current_version) == 1) 264 ) 265 _log.info('%snew release on current branch available', gmTools.bool2str(new_release_on_current_branch_available, '', 'no ')) 266 267 new_release_on_latest_branch_available = ( 268 (latest_branch is not None) 269 and 270 ( 271 (latest_branch > current_branch) or ( 272 (latest_branch == current_branch) and 273 (compare_versions(latest_release_on_latest_branch, current_version) == 1) 274 ) 275 ) 276 ) 277 _log.info('%snew release on latest branch available', gmTools.bool2str(new_release_on_latest_branch_available, '', 'no ')) 278 279 if not (new_release_on_current_branch_available or new_release_on_latest_branch_available): 280 _log.debug('up to date: no new releases available') 281 return (False, None) 282 283 # not up to date 284 msg = _('A new version of GNUmed is available.\n\n') 285 msg += _(' Your current version: "%s"\n') % current_version 286 if consider_latest_branch: 287 if new_release_on_current_branch_available: 288 msg += u'\n' 289 msg += _(' New version: "%s"') % latest_release_on_current_branch 290 msg += u'\n' 291 msg += _(' - bug fixes only\n') 292 msg += _(' - database fixups may be needed\n') 293 if new_release_on_latest_branch_available: 294 if current_branch != latest_branch: 295 msg += u'\n' 296 msg += _(' New version: "%s"') % latest_release_on_latest_branch 297 msg += u'\n' 298 msg += _(' - bug fixes and new features\n') 299 msg += _(' - database upgrade required\n') 300 else: 301 msg += u'\n' 302 msg += _(' New version: "%s"') % latest_release_on_current_branch 303 msg += u'\n' 304 msg += _(' - bug fixes only\n') 305 msg += _(' - database fixups may be needed\n') 306 307 msg += u'\n\n' 308 msg += _( 309 'Note, however, that this version may not yet\n' 310 'be available *pre-packaged* for your system.' 311 ) 312 313 msg += u'\n\n' 314 msg += _('Details are found on <http://wiki.gnumed.de>.\n') 315 msg += u'\n' 316 msg += _('Version information loaded from:\n\n %s') % url 317 318 return (True, msg)
319 #=========================================================================== 320 # mail handling 321 #--------------------------------------------------------------------------- 322 default_mail_sender = u'gnumed@gmx.net' 323 default_mail_receiver = u'gnumed-devel@gnu.org' 324 default_mail_server = u'mail.gmx.net' 325
326 -def send_mail(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='quoted-printable', attachments=None):
327 # FIXME: How to generate and send mails: a step by step tutorial 328 # FIXME: http://groups.google.com/group/comp.lang.python/browse_thread/thread/e0793c1007361398/ 329 # FIXME: google for aspineux blog 330 331 if message is None: 332 return False 333 334 message = message.lstrip().lstrip('\r\n').lstrip() 335 336 if sender is None: 337 sender = default_mail_sender 338 339 if receiver is None: 340 receiver = [default_mail_receiver] 341 342 if server is None: 343 server = default_mail_server 344 345 if subject is None: 346 subject = u'gmTools.py: send_mail() test' 347 348 msg = StringIO.StringIO() 349 writer = MimeWriter.MimeWriter(msg) 350 writer.addheader('To', u', '.join(receiver)) 351 writer.addheader('From', sender) 352 writer.addheader('Subject', subject[:50].replace('\r', '/').replace('\n', '/')) 353 writer.addheader('MIME-Version', '1.0') 354 355 writer.startmultipartbody('mixed') 356 357 # start with a text/plain part 358 part = writer.nextpart() 359 body = part.startbody('text/plain') 360 part.flushheaders() 361 body.write(message.encode(encoding)) 362 363 # now add the attachments 364 if attachments is not None: 365 for a in attachments: 366 filename = os.path.basename(a[0]) 367 try: 368 mtype = a[1] 369 encoding = a[2] 370 except IndexError: 371 mtype, encoding = mimetypes.guess_type(a[0]) 372 if mtype is None: 373 mtype = 'application/octet-stream' 374 encoding = 'base64' 375 elif mtype == 'text/plain': 376 encoding = 'quoted-printable' 377 else: 378 encoding = 'base64' 379 380 part = writer.nextpart() 381 part.addheader('Content-Transfer-Encoding', encoding) 382 body = part.startbody("%s; name=%s" % (mtype, filename)) 383 mimetools.encode(open(a[0], 'rb'), body, encoding) 384 385 writer.lastpart() 386 387 import smtplib 388 session = smtplib.SMTP(server) 389 session.set_debuglevel(debug) 390 if auth is not None: 391 session.login(auth['user'], auth['password']) 392 refused = session.sendmail(sender, receiver, msg.getvalue()) 393 session.quit() 394 msg.close() 395 if len(refused) != 0: 396 _log.error("refused recipients: %s" % refused) 397 return False 398 399 return True
400 #=========================================================================== 401 # main 402 #--------------------------------------------------------------------------- 403 if __name__ == '__main__': 404 405 if len(sys.argv) < 2: 406 sys.exit() 407 408 if sys.argv[1] != 'test': 409 sys.exit() 410 411 #-----------------------------------------------------------------------
412 - def test_send_mail():
413 msg = u""" 414 To: %s 415 From: %s 416 Subject: gmTools test suite mail 417 418 This is a test mail from the gmTools.py module. 419 """ % (default_mail_receiver, default_mail_sender) 420 print "mail sending succeeded:", send_mail ( 421 receiver = [default_mail_receiver, u'karsten.hilbert@gmx.net'], 422 message = msg, 423 auth = {'user': default_mail_sender, 'password': u'gnumed-at-gmx-net'}, # u'gm/bugs/gmx' 424 debug = True, 425 attachments = [sys.argv[0]] 426 )
427 #-----------------------------------------------------------------------
428 - def test_check_for_update():
429 430 test_data = [ 431 ('http://www.gnumed.de/downloads/gnumed-versions.txt', None, None, False), 432 ('file:///home/ncq/gm-versions.txt', None, None, False), 433 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', False), 434 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', True), 435 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.5', True) 436 ] 437 438 for test in test_data: 439 print "arguments:", test 440 found, msg = check_for_update(test[0], test[1], test[2], test[3]) 441 print msg 442 443 return
444 #-----------------------------------------------------------------------
445 - def test_dl_data_pack():
446 #url = 'file:./x-data_pack.zip' 447 #url = 'missing-file.zip' 448 url = 'gmTools.py' 449 dl_name = download_data_pack(url) 450 print url, "->", dl_name 451 unzip_dir = unzip_data_pack(dl_name) 452 print "unzipped into", unzip_dir
453 #-----------------------------------------------------------------------
454 - def test_browser():
455 success = open_url_in_browser(sys.argv[2]) 456 print success 457 open_url_in_browser(sys.argv[2], abc=222)
458 #----------------------------------------------------------------------- 459 #test_check_for_update() 460 #test_send_mail() 461 #test_dl_data_pack() 462 test_browser() 463 464 #=========================================================================== 465