annotate easyhg2.py @ 427:8edf76b57bd9

Alternative approach to asking for username/password
author Chris Cannam
date Thu, 23 Jun 2011 20:36:23 +0100
parents
children a47eac9c7fea
rev   line source
Chris@427 1 # -*- coding: utf-8 -*-
Chris@427 2 #
Chris@427 3 # EasyMercurial
Chris@427 4 #
Chris@427 5 # Based on hgExplorer by Jari Korhonen
Chris@427 6 # Copyright (c) 2010 Jari Korhonen
Chris@427 7 # Copyright (c) 2010 Chris Cannam
Chris@427 8 # Copyright (c) 2010 Queen Mary, University of London
Chris@427 9 #
Chris@427 10 # This program is free software; you can redistribute it and/or
Chris@427 11 # modify it under the terms of the GNU General Public License as
Chris@427 12 # published by the Free Software Foundation; either version 2 of the
Chris@427 13 # License, or (at your option) any later version. See the file
Chris@427 14 # COPYING included with this distribution for more information.
Chris@427 15
Chris@427 16 import sys
Chris@427 17
Chris@427 18 import urllib, urllib2, urlparse
Chris@427 19
Chris@427 20 from mercurial import ui, getpass, util
Chris@427 21 try:
Chris@427 22 from mercurial.url import passwordmgr
Chris@427 23 except:
Chris@427 24 from mercurial.httprepo import passwordmgr
Chris@427 25
Chris@427 26 from mercurial.i18n import _
Chris@427 27
Chris@427 28 #!!! do this later? as for Qt? also see notes on demandimport in mercurial_keyring
Chris@427 29 import keyring
Chris@427 30
Chris@427 31 # The value assigned here may be modified during installation, by
Chris@427 32 # replacing its default value with another one. We can't compare
Chris@427 33 # against its default value, because then the comparison text would
Chris@427 34 # get modified as well. So, compare using prefix only.
Chris@427 35 #
Chris@427 36 easyhg_import_path = 'NO_EASYHG_IMPORT_PATH'
Chris@427 37 if not easyhg_import_path.startswith('NO_'):
Chris@427 38 # We have an installation path: append it twice, once with
Chris@427 39 # the Python version suffixed
Chris@427 40 version_suffix = "Py" + str(sys.version_info[0]) + "." + str(sys.version_info[1]);
Chris@427 41 sys.path.append(easyhg_import_path + "/" + version_suffix)
Chris@427 42 sys.path.append(easyhg_import_path)
Chris@427 43
Chris@427 44 # Try to load the PyQt4 module that we need. If this fails, we should
Chris@427 45 # bail out later (in uisetup), because if we bail out now, Mercurial
Chris@427 46 # will just continue without us and report success. The invoking
Chris@427 47 # application needs to be able to discover whether the module load
Chris@427 48 # succeeded or not, so we need to ensure that Mercurial itself returns
Chris@427 49 # failure if it didn't.
Chris@427 50 #
Chris@427 51 easyhg_pyqt_ok = True
Chris@427 52 try:
Chris@427 53 from PyQt4 import Qt, QtGui
Chris@427 54 except ImportError:
Chris@427 55 easyhg_pyqt_ok = False
Chris@427 56
Chris@427 57 easyhg_qtapp = None
Chris@427 58
Chris@427 59 def monkeypatch_method(cls):
Chris@427 60 def decorator(func):
Chris@427 61 setattr(cls, func.__name__, func)
Chris@427 62 return func
Chris@427 63 return decorator
Chris@427 64
Chris@427 65 def uisetup(ui):
Chris@427 66 if not easyhg_pyqt_ok:
Chris@427 67 raise util.Abort(_('Failed to load PyQt4 module required by easyhg.py'))
Chris@427 68 global easyhg_qtapp
Chris@427 69 easyhg_qtapp = QtGui.QApplication([])
Chris@427 70
Chris@427 71 orig_find = passwordmgr.find_user_password
Chris@427 72
Chris@427 73 # from mercurial_keyring by Marcin Kasperski
Chris@427 74 def canonical_url(authuri):
Chris@427 75 """
Chris@427 76 Strips query params from url. Used to convert urls like
Chris@427 77 https://repo.machine.com/repos/apps/module?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between
Chris@427 78 to
Chris@427 79 https://repo.machine.com/repos/apps/module
Chris@427 80 """
Chris@427 81 parsed_url = urlparse.urlparse(authuri)
Chris@427 82 return "%s://%s%s" % (parsed_url.scheme, parsed_url.netloc,
Chris@427 83 parsed_url.path)
Chris@427 84
Chris@427 85 @monkeypatch_method(passwordmgr)
Chris@427 86 def find_user_password(self, realm, authuri):
Chris@427 87
Chris@427 88 if not self.ui.interactive():
Chris@427 89 return orig_find(self, realm, authuri)
Chris@427 90 if not easyhg_pyqt_ok:
Chris@427 91 return orig_find(self, realm, authuri)
Chris@427 92
Chris@427 93 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
Chris@427 94 self, realm, authuri)
Chris@427 95 user, passwd = authinfo
Chris@427 96
Chris@427 97 if user and passwd:
Chris@427 98 return orig_find(self, realm, authuri)
Chris@427 99
Chris@427 100 self.ui.write("want username and/or password for %s\n" % authuri)
Chris@427 101
Chris@427 102 uri = canonical_url(authuri)
Chris@427 103
Chris@427 104 from_keyring = False
Chris@427 105 keyring_key = ''
Chris@427 106 if user and not passwd:
Chris@427 107 keyring_key = '%s@@%s' % (uri, user)
Chris@427 108 self.ui.write("keyring_key is %s" % keyring_key)
Chris@427 109 # passwd = keyring.get_password('Mercurial', keyring_key)
Chris@427 110 self.ui.write("got passwd: %s\n" % passwd)
Chris@427 111 if passwd:
Chris@427 112 from_keyring = True
Chris@427 113
Chris@427 114 dialog = QtGui.QDialog()
Chris@427 115 layout = QtGui.QGridLayout()
Chris@427 116 dialog.setLayout(layout)
Chris@427 117
Chris@427 118 layout.addWidget(QtGui.QLabel(_('Please supply your user name and password for\n%s:') % uri), 0, 0, 1, 2)
Chris@427 119
Chris@427 120 userfield = QtGui.QLineEdit()
Chris@427 121 if user:
Chris@427 122 userfield.setText(user)
Chris@427 123 layout.addWidget(QtGui.QLabel(_('User:')), 1, 0)
Chris@427 124 layout.addWidget(userfield, 1, 1)
Chris@427 125
Chris@427 126 passfield = QtGui.QLineEdit()
Chris@427 127 passfield.setEchoMode(QtGui.QLineEdit.Password)
Chris@427 128 if passwd:
Chris@427 129 userfield.setText(passwd)
Chris@427 130 layout.addWidget(QtGui.QLabel(_('Password:')), 2, 0)
Chris@427 131 layout.addWidget(passfield, 2, 1)
Chris@427 132
Chris@427 133 bb = QtGui.QDialogButtonBox()
Chris@427 134 ok = bb.addButton(bb.Ok)
Chris@427 135 cancel = bb.addButton(bb.Cancel)
Chris@427 136 cancel.setDefault(False)
Chris@427 137 cancel.setAutoDefault(False)
Chris@427 138 ok.setDefault(True)
Chris@427 139 bb.connect(ok, Qt.SIGNAL("clicked()"), dialog, Qt.SLOT("accept()"))
Chris@427 140 bb.connect(cancel, Qt.SIGNAL("clicked()"), dialog, Qt.SLOT("reject()"))
Chris@427 141 layout.addWidget(bb, 3, 0, 1, 2)
Chris@427 142
Chris@427 143 dialog.setWindowTitle(_('EasyMercurial: Password'))
Chris@427 144 dialog.show()
Chris@427 145 dialog.raise_()
Chris@427 146 ok = dialog.exec_()
Chris@427 147 if ok:
Chris@427 148 self.ui.write('Dialog accepted\n')
Chris@427 149 user = userfield.text()
Chris@427 150 passwd = passfield.text()
Chris@427 151 if passwd and keyring_key != '' and not from_keyring:
Chris@427 152 keyring_key = '%s@@%s' % (uri, user)
Chris@427 153 keyring.set_password('Mercurial', keyring_key, passwd)
Chris@427 154 self.add_password(realm, authuri, user, passwd)
Chris@427 155 else:
Chris@427 156 raise util.Abort(_('password entry cancelled'))
Chris@427 157 return (user, passwd)
Chris@427 158
Chris@427 159
Chris@427 160