Pass unknown protocols to external protocol handlers
authorIlari Liusvaara <ilari.liusvaara@elisanet.fi>
Wed, 9 Dec 2009 15:26:29 +0000 (17:26 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Dec 2009 20:40:42 +0000 (12:40 -0800)
Change URL handling to allow external protocol handlers to implement
new protocols without the '::' syntax if helper name does not conflict
with any built-in protocol.

foo:// now invokes git-remote-foo with foo:// as the URL.

Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
transport-helper.c
transport.c

index 4b17aaa237d498dcaaac14df1730af5b67b47705..271af345e45038a0ca59e27e966ca6a6c4be9f25 100644 (file)
@@ -63,6 +63,16 @@ static void write_constant(int fd, const char *str)
                die_errno("Full write to remote helper failed");
 }
 
+const char *remove_ext_force(const char *url)
+{
+       if (url) {
+               const char *colon = strchr(url, ':');
+               if (colon && colon[1] == ':')
+                       return colon + 2;
+       }
+       return url;
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
@@ -83,7 +93,7 @@ static struct child_process *get_helper(struct transport *transport)
        strbuf_addf(&buf, "remote-%s", data->name);
        helper->argv[0] = strbuf_detach(&buf, NULL);
        helper->argv[1] = transport->remote->name;
-       helper->argv[2] = transport->url;
+       helper->argv[2] = remove_ext_force(transport->url);
        helper->git_cmd = 1;
        if (start_command(helper))
                die("Unable to run helper: git %s", helper->argv[0]);
index 3eea836a33a56aaa99eec78bb4850b02888e377b..dea37d09b2ae01669b98c020cd795814e72aa90c 100644 (file)
@@ -780,6 +780,44 @@ static int is_file(const char *url)
        return S_ISREG(buf.st_mode);
 }
 
+static int is_url(const char *url)
+{
+       const char *url2, *first_slash;
+
+       if (!url)
+               return 0;
+       url2 = url;
+       first_slash = strchr(url, '/');
+
+       /* Input with no slash at all or slash first can't be URL. */
+       if (!first_slash || first_slash == url)
+               return 0;
+       /* Character before must be : and next must be /. */
+       if (first_slash[-1] != ':' || first_slash[1] != '/')
+               return 0;
+       /* There must be something before the :// */
+       if (first_slash == url + 1)
+               return 0;
+       /*
+        * Check all characters up to first slash - 1. Only alphanum
+        * is allowed.
+        */
+       url2 = url;
+       while (url2 < first_slash - 1) {
+               if (!isalnum((unsigned char)*url2))
+                       return 0;
+               url2++;
+       }
+
+       /* Valid enough. */
+       return 1;
+}
+
+static int external_specification_len(const char *url)
+{
+       return strchr(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
        struct transport *ret = xcalloc(1, sizeof(*ret));
@@ -805,30 +843,23 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
        if (remote && remote->foreign_vcs) {
                transport_helper_init(ret, remote->foreign_vcs);
-               return ret;
-       }
-
-       if (!prefixcmp(url, "rsync:")) {
+       } else if (!prefixcmp(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
-
-       } else if (!prefixcmp(url, "http://")
-               || !prefixcmp(url, "https://")
-               || !prefixcmp(url, "ftp://")) {
-               transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-               error("git was compiled without libcurl support.");
-#endif
-
        } else if (is_local(url) && is_file(url)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
                ret->fetch = fetch_refs_from_bundle;
                ret->disconnect = close_bundle;
-
-       } else {
+       } else if (!is_url(url)
+               || !prefixcmp(url, "file://")
+               || !prefixcmp(url, "git://")
+               || !prefixcmp(url, "ssh://")
+               || !prefixcmp(url, "git+ssh://")
+               || !prefixcmp(url, "ssh+git://")) {
+               /* These are builtin smart transports. */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->set_option = set_git_option;
@@ -845,6 +876,21 @@ struct transport *transport_get(struct remote *remote, const char *url)
                data->receivepack = "git-receive-pack";
                if (remote->receivepack)
                        data->receivepack = remote->receivepack;
+       } else if (!prefixcmp(url, "http://")
+               || !prefixcmp(url, "https://")
+               || !prefixcmp(url, "ftp://")) {
+               /* These three are just plain special. */
+               transport_helper_init(ret, "curl");
+#ifdef NO_CURL
+               error("git was compiled without libcurl support.");
+#endif
+       } else {
+               /* Unknown protocol in URL. Pass to external handler. */
+               int len = external_specification_len(url);
+               char *handler = xmalloc(len + 1);
+               handler[len] = 0;
+               strncpy(handler, url, len);
+               transport_helper_init(ret, handler);
        }
 
        return ret;