Add new remote-hg transport helper
[gitweb.git] / connect.c
index 2a0a0401af6e070125f1ae6d97004f4036af35f6..49e56ba35a645359b0d7c1f7bbc9e2108b3424d9 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -49,6 +49,16 @@ static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1
        extra->nr++;
 }
 
+static void die_initial_contact(int got_at_least_one_head)
+{
+       if (got_at_least_one_head)
+               die("The remote end hung up upon initial contact");
+       else
+               die("Could not read from remote repository.\n\n"
+                   "Please make sure you have the correct access rights\n"
+                   "and the repository exists.");
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -56,6 +66,8 @@ struct ref **get_remote_heads(int in, struct ref **list,
                              unsigned int flags,
                              struct extra_have_objects *extra_have)
 {
+       int got_at_least_one_head = 0;
+
        *list = NULL;
        for (;;) {
                struct ref *ref;
@@ -64,7 +76,10 @@ struct ref **get_remote_heads(int in, struct ref **list,
                char *name;
                int len, name_len;
 
-               len = packet_read_line(in, buffer, sizeof(buffer));
+               len = packet_read(in, buffer, sizeof(buffer));
+               if (len < 0)
+                       die_initial_contact(got_at_least_one_head);
+
                if (!len)
                        break;
                if (buffer[len-1] == '\n')
@@ -95,14 +110,61 @@ struct ref **get_remote_heads(int in, struct ref **list,
                hashcpy(ref->old_sha1, old_sha1);
                *list = ref;
                list = &ref->next;
+               got_at_least_one_head = 1;
        }
        return list;
 }
 
+const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
+{
+       int len;
+
+       if (!feature_list)
+               return NULL;
+
+       len = strlen(feature);
+       while (*feature_list) {
+               const char *found = strstr(feature_list, feature);
+               if (!found)
+                       return NULL;
+               if (feature_list == found || isspace(found[-1])) {
+                       const char *value = found + len;
+                       /* feature with no value (e.g., "thin-pack") */
+                       if (!*value || isspace(*value)) {
+                               if (lenp)
+                                       *lenp = 0;
+                               return value;
+                       }
+                       /* feature with a value (e.g., "agent=git/1.2.3") */
+                       else if (*value == '=') {
+                               value++;
+                               if (lenp)
+                                       *lenp = strcspn(value, " \t\n");
+                               return value;
+                       }
+                       /*
+                        * otherwise we matched a substring of another feature;
+                        * keep looking
+                        */
+               }
+               feature_list = found + 1;
+       }
+       return NULL;
+}
+
+int parse_feature_request(const char *feature_list, const char *feature)
+{
+       return !!parse_feature_value(feature_list, feature, NULL);
+}
+
+const char *server_feature_value(const char *feature, int *len)
+{
+       return parse_feature_value(server_capabilities, feature, len);
+}
+
 int server_supports(const char *feature)
 {
-       return server_capabilities &&
-               strstr(server_capabilities, feature) != NULL;
+       return !!server_feature_value(feature, NULL);
 }
 
 enum protocol {
@@ -151,6 +213,15 @@ static void get_host_and_port(char **host, const char **port)
        }
 }
 
+static void enable_keepalive(int sockfd)
+{
+       int ka = 1;
+
+       if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
+               fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n",
+                       strerror(errno));
+}
+
 #ifndef NO_IPV6
 
 static const char *ai_name(const struct addrinfo *ai)
@@ -215,6 +286,8 @@ static int git_tcp_connect_sock(char *host, int flags)
        if (sockfd < 0)
                die("unable to connect to %s:\n%s", host, error_message.buf);
 
+       enable_keepalive(sockfd);
+
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "done.\n");
 
@@ -288,6 +361,8 @@ static int git_tcp_connect_sock(char *host, int flags)
        if (sockfd < 0)
                die("unable to connect to %s:\n%s", host, error_message.buf);
 
+       enable_keepalive(sockfd);
+
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "done.\n");
 
@@ -504,7 +579,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         * Add support for ssh port: ssh://host.xy:<port>/...
         */
        if (protocol == PROTO_SSH && host != url)
-               port = get_port(host);
+               port = get_port(end);
 
        if (protocol == PROTO_GIT) {
                /* These underlying connection commands die() if they
@@ -595,47 +670,3 @@ 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;
-}