upload-pack: simplify request validation
[gitweb.git] / connect.c
index 41b7400aa92d7c07db269de9658b9f961db9becf..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,16 +110,12 @@ 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;
 }
 
-int server_supports(const char *feature)
-{
-       return !!parse_feature_request(server_capabilities, feature);
-}
-
-const char *parse_feature_request(const char *feature_list, const char *feature)
+const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
 {
        int len;
 
@@ -116,14 +127,46 @@ const char *parse_feature_request(const char *feature_list, const char *feature)
                const char *found = strstr(feature_list, feature);
                if (!found)
                        return NULL;
-               if ((feature_list == found || isspace(found[-1])) &&
-                   (!found[len] || isspace(found[len]) || found[len] == '='))
-                       return found;
+               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_feature_value(feature, NULL);
+}
+
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,