From d385649106fef9901dd33f63efb550d19c7204d4 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Thu, 10 Sep 2009 23:01:20 -0500 Subject: Add support for bugzilla installations in non-root paths Currently, git-bz assumes that all bugzilla installations are at the root of the host (e.g. http://foo.com/). It is currently impossible to use git-bz with a bugzilla installation that is hosted at e.g. foo.com/bugzilla/. This patch adds that ability. A new configuration option ('bz-tracker.alias.path') allows the path to be specified for a host. --- git-bz | 54 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/git-bz b/git-bz index 9ede172..dbcd618 100755 --- a/git-bz +++ b/git-bz @@ -329,6 +329,12 @@ def tracker_uses_https(tracker): config = get_config(tracker) return 'https' in config and config['https'] == 'true' +def tracker_get_path(tracker): + config = get_config(tracker) + if 'path' in config: + return config['path'] + return None + def get_default_fields(tracker): config = get_config(tracker) @@ -351,11 +357,21 @@ class BugParseError(Exception): # uniquely identifies a bug on a server, though until we try # to load it (and create a Bug) we don't know if it actually exists. class BugHandle: - def __init__(self, host, https, id): + def __init__(self, host, path, https, id): self.host = host + self.path = path self.https = https self.id = id + # ensure that the path to the bugzilla installation is an absolute path + # so that it will still work even if their config option specifies + # something like: + # path = bugzilla + # instead of the proper form: + # path = /bugzilla + if self.path and self.path[0] != '/': + self.path = '/' + self.path + def get_url(self): return "%s://%s/show_bug.cgi?id=%s" % ("https" if self.https else "http", self.host, @@ -363,11 +379,18 @@ class BugHandle: @staticmethod def parse(bug_reference): - m = re.match("http(s?)://([^/]+)/show_bug.cgi\?id=([^&]+)", bug_reference) + parseresult = urlparse.urlsplit (bug_reference) + + # strip off everything after the last '/', so '/bugzilla/show_bug.cgi' + # will simply become '/bugzilla' + path = parseresult.path[:parseresult.path.rfind('/')] + m = re.match("id=([^&]+)", parseresult.query) + if m: - return BugHandle(host=m.group(2), - https=m.group(1) != "", - id=m.group(3)) + return BugHandle(host=parseresult.hostname, + path=path, + https=parseresult.scheme=="https", + id=m.group(1)) colon = bug_reference.find(":") if colon > 0: @@ -382,11 +405,12 @@ class BugHandle: host = resolve_host_alias(tracker) https = tracker_uses_https(tracker) + path = tracker_get_path(tracker) if not re.match(r"^.*\.[a-zA-Z]{2,}$", host): raise BugParseError("'%s' doesn't look like a valid bugzilla host or alias" % host) - return BugHandle(host=host, https=https, id=id) + return BugHandle(host=host, path=path, https=https, id=id) @staticmethod def parse_or_die(str): @@ -713,8 +737,9 @@ def get_connection(host, https): return connections[identifier] class BugServer(object): - def __init__(self, host, https): + def __init__(self, host, path, https): self.host = host + self.path = path self.https = https self.cookies = get_bugzilla_cookies(host) @@ -729,6 +754,8 @@ class BugServer(object): headers = dict(headers) headers['Cookie'] = self.get_cookie_string() headers['User-Agent'] = "git-bz" + if self.path: + url = self.path + url seen_urls = [] connection = get_connection(self.host, self.https) @@ -798,6 +825,8 @@ class BugServer(object): def send_post(self, url, fields, files=None): content_type, body = encode_multipart_formdata(fields, files) + if self.path: + url = self.path + url return self.send_request("POST", url, data=body, headers={ 'Content-Type': content_type }) def get_xmlrpc_proxy(self): @@ -858,10 +887,10 @@ servers = {} # host/https of the server to avoid doing too many redirections, and # so the host,https we connect to may be different than what we use # to look up the server. -def get_bug_server(host, https): - identifier = (host, https) +def get_bug_server(host, path, https): + identifier = (host, path, https) if not identifier in servers: - servers[identifier] = BugServer(host, https) + servers[identifier] = BugServer(host, path, https) return servers[identifier] @@ -1109,7 +1138,7 @@ class Bug(object): @staticmethod def load(bug_reference, attachmentdata=False): - server = get_bug_server(bug_reference.host, bug_reference.https) + server = get_bug_server(bug_reference.host, bug_reference.path, bug_reference.https) bug = Bug(server) bug._load(bug_reference.id, attachmentdata) @@ -1119,9 +1148,10 @@ class Bug(object): def create(tracker, product, component, short_desc, comment): host = resolve_host_alias(tracker) https = tracker_uses_https(tracker) + path = tracker_get_path(tracker) default_fields = get_default_fields(tracker) - server = get_bug_server(host, https) + server = get_bug_server(host, path, https) bug = Bug(server) bug._create(product, component, short_desc, comment, default_fields) -- cgit v1.2.3 From 9e026a19f7d3d7acc3ac8a82a9fc56512d80452c Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Thu, 10 Sep 2009 23:35:25 -0500 Subject: Add support for bugzilla installations that require http auth This patch should support http authentication credentials specified in url form (e.g. https://user:password@foo.com) or in a configuration file. The additional configure options are: - bz-tracker..authuser - bz-tracker..authpwd --- git-bz | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/git-bz b/git-bz index dbcd618..8f9e8dc 100755 --- a/git-bz +++ b/git-bz @@ -79,6 +79,7 @@ import traceback import xmlrpclib import urlparse from xml.etree.cElementTree import ElementTree +import base64 # Globals # ======= @@ -335,6 +336,18 @@ def tracker_get_path(tracker): return config['path'] return None +def tracker_get_auth_user(tracker): + config = get_config(tracker) + if 'path' in config: + return config['authuser'] + return None + +def tracker_get_auth_password(tracker): + config = get_config(tracker) + if 'path' in config: + return config['authpwd'] + return None + def get_default_fields(tracker): config = get_config(tracker) @@ -357,11 +370,13 @@ class BugParseError(Exception): # uniquely identifies a bug on a server, though until we try # to load it (and create a Bug) we don't know if it actually exists. class BugHandle: - def __init__(self, host, path, https, id): + def __init__(self, host, path, https, id, authuser=None, authpwd=None): self.host = host self.path = path self.https = https self.id = id + self.authuser = authuser + self.authpwd = authpwd # ensure that the path to the bugzilla installation is an absolute path # so that it will still work even if their config option specifies @@ -377,10 +392,23 @@ class BugHandle: self.host, self.id) + def needs_auth(self): + return self.authuser and self.authpwd + @staticmethod def parse(bug_reference): parseresult = urlparse.urlsplit (bug_reference) + user = parseresult.username + pwd = parseresult.password + # if the url did not specify http auth credentials in the form + # https://user:pwd@host.com, check to see whether the config file + # specifies any auth credentials for this host + if not user: + user = tracker_get_auth_user(parseresult.hostname) + if not pwd: + pwd = tracker_get_auth_password(parseresult.hostname) + # strip off everything after the last '/', so '/bugzilla/show_bug.cgi' # will simply become '/bugzilla' path = parseresult.path[:parseresult.path.rfind('/')] @@ -390,7 +418,9 @@ class BugHandle: return BugHandle(host=parseresult.hostname, path=path, https=parseresult.scheme=="https", - id=m.group(1)) + id=m.group(1), + authuser=user, + authpwd=pwd) colon = bug_reference.find(":") if colon > 0: @@ -406,11 +436,13 @@ class BugHandle: host = resolve_host_alias(tracker) https = tracker_uses_https(tracker) path = tracker_get_path(tracker) + authuser = tracker_get_auth_user(tracker) + authpwd = tracker_get_auth_password(tracker) if not re.match(r"^.*\.[a-zA-Z]{2,}$", host): raise BugParseError("'%s' doesn't look like a valid bugzilla host or alias" % host) - return BugHandle(host=host, path=path, https=https, id=id) + return BugHandle(host=host, path=path, https=https, id=id, authuser=authuser, authpwd=authpwd) @staticmethod def parse_or_die(str): @@ -712,6 +744,9 @@ def die(message): print >>sys.stderr, message sys.exit(1) +def http_auth_header(user, password): + return 'Basic ' + base64.encodestring("%s:%s" % (user, password)).strip() + # Classes for bug handling # ======================== @@ -737,10 +772,12 @@ def get_connection(host, https): return connections[identifier] class BugServer(object): - def __init__(self, host, path, https): + def __init__(self, host, path, https, authuser=None, authpwd=None): self.host = host self.path = path self.https = https + self.authuser = authuser + self.authpwd = authpwd self.cookies = get_bugzilla_cookies(host) @@ -754,6 +791,8 @@ class BugServer(object): headers = dict(headers) headers['Cookie'] = self.get_cookie_string() headers['User-Agent'] = "git-bz" + if self.authuser and self.authpwd: + headers['Authorization'] = http_auth_header(self.authuser, self.authpwd) if self.path: url = self.path + url @@ -880,6 +919,7 @@ class BugTransport(xmlrpclib.Transport): def send_request(self, connection, *args): xmlrpclib.Transport.send_request(self, connection, *args) connection.putheader("Cookie", self.server.get_cookie_string()) + connection.putheader("Authorization", http_auth_header(self.server.authuser, self.server.authpwd)) servers = {} @@ -887,10 +927,10 @@ servers = {} # host/https of the server to avoid doing too many redirections, and # so the host,https we connect to may be different than what we use # to look up the server. -def get_bug_server(host, path, https): +def get_bug_server(host, path, https, authuser, authpwd): identifier = (host, path, https) if not identifier in servers: - servers[identifier] = BugServer(host, path, https) + servers[identifier] = BugServer(host, path, https, authuser, authpwd) return servers[identifier] @@ -1138,7 +1178,7 @@ class Bug(object): @staticmethod def load(bug_reference, attachmentdata=False): - server = get_bug_server(bug_reference.host, bug_reference.path, bug_reference.https) + server = get_bug_server(bug_reference.host, bug_reference.path, bug_reference.https, bug_reference.authuser, bug_reference.authpwd) bug = Bug(server) bug._load(bug_reference.id, attachmentdata) @@ -1149,9 +1189,11 @@ class Bug(object): host = resolve_host_alias(tracker) https = tracker_uses_https(tracker) path = tracker_get_path(tracker) + authuser = tracker_get_auth_user(tracker) + authpwd = tracker_get_auth_password(tracker) default_fields = get_default_fields(tracker) - server = get_bug_server(host, path, https) + server = get_bug_server(host, path, https, authuser, authpwd) bug = Bug(server) bug._create(product, component, short_desc, comment, default_fields) -- cgit v1.2.3 From feb1c4788d7399dfafa7e7f82903d12ef9dbc998 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Tue, 15 Sep 2009 14:54:02 -0500 Subject: Fix exception when parsing an alias as a url In BugHandle.parse(), we first try to parse the bug reference as a url and then fall back to interpreting it as an alias from the config file. Unfortunately, something like "gnome:123456" gets parsed as a url (with a scheme of 'gnome' and a path of '123456', and no hostname). This resulted in us passing a None hostname to tracker_get_auth_user(), which resulted in an uncaught exception. From now on, only proceed with treating the bug reference as a url if the scheme is parsed as 'http' or 'https'. --- git-bz | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/git-bz b/git-bz index 8f9e8dc..2edb3f5 100755 --- a/git-bz +++ b/git-bz @@ -399,28 +399,29 @@ class BugHandle: def parse(bug_reference): parseresult = urlparse.urlsplit (bug_reference) - user = parseresult.username - pwd = parseresult.password - # if the url did not specify http auth credentials in the form - # https://user:pwd@host.com, check to see whether the config file - # specifies any auth credentials for this host - if not user: - user = tracker_get_auth_user(parseresult.hostname) - if not pwd: - pwd = tracker_get_auth_password(parseresult.hostname) - - # strip off everything after the last '/', so '/bugzilla/show_bug.cgi' - # will simply become '/bugzilla' - path = parseresult.path[:parseresult.path.rfind('/')] - m = re.match("id=([^&]+)", parseresult.query) - - if m: - return BugHandle(host=parseresult.hostname, - path=path, - https=parseresult.scheme=="https", - id=m.group(1), - authuser=user, - authpwd=pwd) + if parseresult.scheme in ('http', 'https'): + user = parseresult.username + pwd = parseresult.password + # if the url did not specify http auth credentials in the form + # https://user:pwd@host.com, check to see whether the config file + # specifies any auth credentials for this host + if not user: + user = tracker_get_auth_user(parseresult.hostname) + if not pwd: + pwd = tracker_get_auth_password(parseresult.hostname) + + # strip off everything after the last '/', so '/bugzilla/show_bug.cgi' + # will simply become '/bugzilla' + path = parseresult.path[:parseresult.path.rfind('/')] + m = re.match("id=([^&]+)", parseresult.query) + + if m: + return BugHandle(host=parseresult.hostname, + path=path, + https=parseresult.scheme=="https", + id=m.group(1), + authuser=user, + authpwd=pwd) colon = bug_reference.find(":") if colon > 0: -- cgit v1.2.3 From 5c52c2f6afb489f7285e17662f4b5ff440e1ca0c Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Tue, 15 Sep 2009 15:01:05 -0500 Subject: Fix ' git bz attach' failure when bugzilla server has a path I had accidentally prepended the bugzilla installation path in both BugServer.send_post() and BugServer.send_request() (which is called by send_post()), so the path was getting prepended twice when trying to attach a patch. --- git-bz | 2 -- 1 file changed, 2 deletions(-) diff --git a/git-bz b/git-bz index 2edb3f5..c45b7f3 100755 --- a/git-bz +++ b/git-bz @@ -865,8 +865,6 @@ class BugServer(object): def send_post(self, url, fields, files=None): content_type, body = encode_multipart_formdata(fields, files) - if self.path: - url = self.path + url return self.send_request("POST", url, data=body, headers={ 'Content-Type': content_type }) def get_xmlrpc_proxy(self): -- cgit v1.2.3