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 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          # anything known ?
    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          # up to date ?
    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          # not up to date
    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  # mail handling
    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          # FIXME: How to generate and send mails: a step by step tutorial
    286          # FIXME: http://groups.google.com/group/comp.lang.python/browse_thread/thread/e0793c1007361398/
    287          # FIXME: google for aspineux blog
    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          # start with a text/plain part
    316          part = writer.nextpart()
    317          body = part.startbody('text/plain')
    318          part.flushheaders()
    319          body.write(message.encode(encoding))
    320
    321          # now add the attachments
    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  # main
    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          #-----------------------------------------------------------------------
    370 -        def test_send_mail():
    371                  msg = u"""
    372  To: %s
    373  From: %s
    374  Subject: gmTools test suite mail
    375
    376  This is a test mail from the gmTools.py module.
    377  """ % (default_mail_receiver, default_mail_sender)
    378                  print "mail sending succeeded:", send_mail (
    379                          receiver = [default_mail_receiver, u'karsten.hilbert@gmx.net'],
    380                          message = msg,
    381                          auth = {'user': default_mail_sender, 'password': u'gnumed-at-gmx-net'}, # u'gm/bugs/gmx'
    382                          debug = True,
    383                          attachments = [sys.argv[0]]
    384                  )
    385          #-----------------------------------------------------------------------
    386 -        def test_check_for_update():
    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          #-----------------------------------------------------------------------
    403 -        def test_dl_data_pack():
    404                  #url = 'file:./x-data_pack.zip'
    405                  #url = 'missing-file.zip'
    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          #-----------------------------------------------------------------------
    412 -        def test_browser():
    413                  success = open_url_in_browser(sys.argv[2])
    414                  print success
    415                  open_url_in_browser(sys.argv[2], abc=222)
    416          #-----------------------------------------------------------------------
    417          #test_check_for_update()
    418          #test_send_mail()
    419          #test_dl_data_pack()
    420          test_browser()
    421
    422  #===========================================================================
    423