def _curl_perform(self, curl, header, more_headers=[]): """Perform curl operation and translate exceptions.""" try: # There's no way in http/1.0 to say "must # revalidate"; we don't want to force it to always # retrieve. so just turn off the default Pragma # provided by Curl. headers = [ 'Cache-control: max-age=0', 'Pragma: no-cache', 'Connection: Keep-Alive' ] curl.setopt(pycurl.HTTPHEADER, headers + more_headers) curl.perform() except pycurl.error, e: url = curl.getinfo(pycurl.EFFECTIVE_URL) trace.mutter('got pycurl error: %s, %s, %s, url: %s ', e[0], e[1], e, url) if e[0] in ( CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_RESOLVE_PROXY, CURLE_COULDNT_CONNECT, CURLE_FTP_WEIRD_SERVER_REPLY, CURLE_GOT_NOTHING, CURLE_SSL_CACERT, CURLE_SSL_CACERT_BADFILE, ): raise errors.ConnectionError( 'curl connection error (%s)\non %s' % (e[1], url)) elif e[0] == CURLE_RECV_ERROR: raise errors.ConnectionReset( 'curl connection error (%s)\non %s' % (e[1], url)) elif e[0] == CURLE_PARTIAL_FILE: # Pycurl itself has detected a short read. We do not have all # the information for the ShortReadvError, but that should be # enough raise errors.ShortReadvError( url, offset='unknown', length='unknown', actual='unknown', extra='Server aborted the request') raise
supported_auth_types = e.allowed_types except paramiko.SSHException, e: # Don't know what happened, but just ignore it pass # We treat 'keyboard-interactive' and 'password' auth methods identically, # because Paramiko's auth_password method will automatically try # 'keyboard-interactive' auth (using the password as the response) if # 'password' auth is not available. Apparently some Debian and Gentoo # OpenSSH servers require this. # XXX: It's possible for a server to require keyboard-interactive auth that # requires something other than a single password, but we currently don't # support that. if ('password' not in supported_auth_types and 'keyboard-interactive' not in supported_auth_types): raise errors.ConnectionError('Unable to authenticate to SSH host as' '\n %s@%s\nsupported auth types: %s' % (username, host, supported_auth_types)) if password: try: paramiko_transport.auth_password(username, password) return except paramiko.SSHException, e: pass # give up and ask for a password password = auth.get_password('ssh', host, username, port=port) # get_password can still return None, which means we should not prompt if password is not None: try: paramiko_transport.auth_password(username, password)
class LaunchpadService(object): """A service to talk to Launchpad via XMLRPC. See http://wiki.bazaar.canonical.com/Specs/LaunchpadRpc for the methods we can call. """ LAUNCHPAD_DOMAINS = { 'production': 'launchpad.net', 'staging': 'staging.launchpad.net', 'qastaging': 'qastaging.launchpad.net', 'demo': 'demo.launchpad.net', 'dev': 'launchpad.dev', } # NB: these should always end in a slash to avoid xmlrpclib appending # '/RPC2' LAUNCHPAD_INSTANCE = {} for instance, domain in LAUNCHPAD_DOMAINS.iteritems(): LAUNCHPAD_INSTANCE[instance] = 'https://xmlrpc.%s/bazaar/' % domain # We use production as the default because edge has been deprecated circa # 2010-11 (see bug https://bugs.launchpad.net/bzr/+bug/583667) DEFAULT_INSTANCE = 'production' DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE[DEFAULT_INSTANCE] transport = None registrant_email = None registrant_password = None def __init__(self, transport=None, lp_instance=None): """Construct a new service talking to the launchpad rpc server""" self._lp_instance = lp_instance if transport is None: uri_type = urllib.splittype(self.service_url)[0] transport = XMLRPCTransport(uri_type) transport.user_agent = 'bzr/%s (xmlrpclib/%s)' \ % (_bzrlib_version, xmlrpclib.__version__) self.transport = transport @property def service_url(self): """Return the http or https url for the xmlrpc server. This does not include the username/password credentials. """ key = 'BZR_LP_XMLRPC_URL' if key in os.environ: return os.environ[key] elif self._lp_instance is not None: try: return self.LAUNCHPAD_INSTANCE[self._lp_instance] except KeyError: raise InvalidLaunchpadInstance(self._lp_instance) else: return self.DEFAULT_SERVICE_URL @classmethod def for_url(cls, url, **kwargs): """Return the Launchpad service corresponding to the given URL.""" result = urlsplit(url) lp_instance = result[1] if lp_instance == '': lp_instance = None elif lp_instance not in cls.LAUNCHPAD_INSTANCE: raise errors.InvalidURL(path=url) return cls(lp_instance=lp_instance, **kwargs) def get_proxy(self, authenticated): """Return the proxy for XMLRPC requests.""" if authenticated: # auth info must be in url # TODO: if there's no registrant email perhaps we should # just connect anonymously? scheme, hostinfo, path = urlsplit(self.service_url)[:3] if '@' in hostinfo: raise AssertionError(hostinfo) if self.registrant_email is None: raise AssertionError() if self.registrant_password is None: raise AssertionError() # TODO: perhaps fully quote the password to make it very slightly # obscured # TODO: can we perhaps add extra Authorization headers # directly to the request, rather than putting this into # the url? perhaps a bit more secure against accidentally # revealing it. std66 s3.2.1 discourages putting the # password in the url. hostinfo = '%s:%s@%s' % (urlutils.quote( self.registrant_email), urlutils.quote( self.registrant_password), hostinfo) url = urlunsplit((scheme, hostinfo, path, '', '')) else: url = self.service_url return xmlrpclib.ServerProxy(url, transport=self.transport) def gather_user_credentials(self): """Get the password from the user.""" the_config = config.GlobalConfig() self.registrant_email = the_config.user_email() if self.registrant_password is None: auth = config.AuthenticationConfig() scheme, hostinfo = urlsplit(self.service_url)[:2] prompt = 'launchpad.net password for %s: ' % \ self.registrant_email # We will reuse http[s] credentials if we can, prompt user # otherwise self.registrant_password = auth.get_password(scheme, hostinfo, self.registrant_email, prompt=prompt) def send_request(self, method_name, method_params, authenticated): proxy = self.get_proxy(authenticated) method = getattr(proxy, method_name) try: result = method(*method_params) except xmlrpclib.ProtocolError, e: if e.errcode == 301: # TODO: This can give a ProtocolError representing a 301 error, whose # e.headers['location'] tells where to go and e.errcode==301; should # probably log something and retry on the new url. raise NotImplementedError( "should resend request to %s, but this isn't implemented" % e.headers.get('Location', 'NO-LOCATION-PRESENT')) else: # we don't want to print the original message because its # str representation includes the plaintext password. # TODO: print more headers to help in tracking down failures raise errors.BzrError( "xmlrpc protocol error connecting to %s: %s %s" % (self.service_url, e.errcode, e.errmsg)) except socket.gaierror, e: raise errors.ConnectionError("Could not resolve '%s'" % self.domain, orig_error=e)
return if password: try: paramiko_transport.auth_password(username, password) return except paramiko.SSHException, e: pass # give up and ask for a password password = auth.get_password('ssh', host, username, port=port) try: paramiko_transport.auth_password(username, password) except paramiko.SSHException, e: raise errors.ConnectionError( 'Unable to authenticate to SSH host as %s@%s' % (username, host), e) def _try_pkey_auth(paramiko_transport, pkey_class, username, filename): filename = os.path.expanduser('~/.ssh/' + filename) try: key = pkey_class.from_private_key_file(filename) paramiko_transport.auth_publickey(username, key) return True except paramiko.PasswordRequiredException: password = ui.ui_factory.get_password( prompt='SSH %(filename)s password', filename=filename) try: key = pkey_class.from_private_key_file(filename, password) paramiko_transport.auth_publickey(username, key)