git-remote-testgit: fix error handling
[gitweb.git] / connect.c
index 9f39038f6f0f0cd4faca81a9451623466f197318..2119c3f74edcd2a976ff0e375c5d8d60d40da2ec 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "url.h"
 
 static char *server_capabilities;
 
@@ -131,7 +132,7 @@ int path_match(const char *path, int nr, char **match)
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
-       PROTO_GIT,
+       PROTO_GIT
 };
 
 static enum protocol get_protocol(const char *name)
@@ -394,26 +395,28 @@ static int git_use_proxy(const char *host)
        return (git_proxy_command && *git_proxy_command);
 }
 
-static void git_proxy_connect(int fd[2], char *host)
+static struct child_process *git_proxy_connect(int fd[2], char *host)
 {
        const char *port = STR(DEFAULT_GIT_PORT);
-       const char *argv[4];
-       struct child_process proxy;
+       const char **argv;
+       struct child_process *proxy;
 
        get_host_and_port(&host, &port);
 
+       argv = xmalloc(sizeof(*argv) * 4);
        argv[0] = git_proxy_command;
        argv[1] = host;
        argv[2] = port;
        argv[3] = NULL;
-       memset(&proxy, 0, sizeof(proxy));
-       proxy.argv = argv;
-       proxy.in = -1;
-       proxy.out = -1;
-       if (start_command(&proxy))
+       proxy = xcalloc(1, sizeof(*proxy));
+       proxy->argv = argv;
+       proxy->in = -1;
+       proxy->out = -1;
+       if (start_command(proxy))
                die("cannot start proxy %s", argv[0]);
-       fd[0] = proxy.out; /* read from proxy stdout */
-       fd[1] = proxy.in;  /* write to proxy stdin */
+       fd[0] = proxy->out; /* read from proxy stdout */
+       fd[1] = proxy->in;  /* write to proxy stdin */
+       return proxy;
 }
 
 #define MAX_CMD_LEN 1024
@@ -450,11 +453,11 @@ static struct child_process no_fork;
 struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
-       char *url = xstrdup(url_orig);
+       char *url;
        char *host, *path;
        char *end;
        int c;
-       struct child_process *conn;
+       struct child_process *conn = &no_fork;
        enum protocol protocol = PROTO_LOCAL;
        int free_path = 0;
        char *port = NULL;
@@ -466,6 +469,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         */
        signal(SIGCHLD, SIG_DFL);
 
+       if (is_url(url_orig))
+               url = url_decode(url_orig);
+       else
+               url = xstrdup(url_orig);
+
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
@@ -534,7 +542,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                 */
                char *target_host = xstrdup(host);
                if (git_use_proxy(host))
-                       git_proxy_connect(fd, host);
+                       conn = git_proxy_connect(fd, host);
                else
                        git_tcp_connect(fd, host, flags);
                /*
@@ -552,7 +560,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                free(url);
                if (free_path)
                        free(path);
-               return &no_fork;
+               return conn;
        }
 
        conn = xcalloc(1, sizeof(*conn));
@@ -582,18 +590,8 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                *arg++ = host;
        }
        else {
-               /* remove these from the environment */
-               const char *env[] = {
-                       ALTERNATE_DB_ENVIRONMENT,
-                       DB_ENVIRONMENT,
-                       GIT_DIR_ENVIRONMENT,
-                       GIT_WORK_TREE_ENVIRONMENT,
-                       GRAFT_ENVIRONMENT,
-                       INDEX_ENVIRONMENT,
-                       NO_REPLACE_OBJECTS_ENVIRONMENT,
-                       NULL
-               };
-               conn->env = env;
+               /* remove repo-local variables from the environment */
+               conn->env = local_repo_env;
                conn->use_shell = 1;
        }
        *arg++ = cmd.buf;
@@ -611,10 +609,15 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
        return conn;
 }
 
+int git_connection_is_socket(struct child_process *conn)
+{
+       return conn == &no_fork;
+}
+
 int finish_connect(struct child_process *conn)
 {
        int code;
-       if (!conn || conn == &no_fork)
+       if (!conn || git_connection_is_socket(conn))
                return 0;
 
        code = finish_command(conn);
@@ -622,3 +625,47 @@ int finish_connect(struct child_process *conn)
        free(conn);
        return code;
 }
+
+char *git_getpass(const char *prompt)
+{
+       const char *askpass;
+       struct child_process pass;
+       const char *args[3];
+       static struct strbuf buffer = STRBUF_INIT;
+
+       askpass = getenv("GIT_ASKPASS");
+       if (!askpass)
+               askpass = askpass_program;
+       if (!askpass)
+               askpass = getenv("SSH_ASKPASS");
+       if (!askpass || !(*askpass)) {
+               char *result = getpass(prompt);
+               if (!result)
+                       die_errno("Could not read password");
+               return result;
+       }
+
+       args[0] = askpass;
+       args[1] = prompt;
+       args[2] = NULL;
+
+       memset(&pass, 0, sizeof(pass));
+       pass.argv = args;
+       pass.out = -1;
+
+       if (start_command(&pass))
+               exit(1);
+
+       strbuf_reset(&buffer);
+       if (strbuf_read(&buffer, pass.out, 20) < 0)
+               die("failed to read password from %s\n", askpass);
+
+       close(pass.out);
+
+       if (finish_command(&pass))
+               exit(1);
+
+       strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
+
+       return buffer.buf;
+}