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