2 * Copyright (c) 2019 Ori Bernstein <ori@openbsd.org>
3 * Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/types.h>
25 #include "got_error.h"
28 #include "got_lib_gitproto.h"
31 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
34 static const struct got_error *
35 tokenize_refline(char **tokens, char *line, int len, int maxtokens)
37 const struct got_error *err = NULL;
41 for (i = 0; i < maxtokens; i++)
44 for (i = 0; n < len && i < maxtokens; i++) {
45 while (isspace(*line)) {
50 while (*line != '\0' && n < len &&
51 (!isspace(*line) || i == maxtokens - 1)) {
55 tokens[i] = strndup(p, line - p);
56 if (tokens[i] == NULL) {
57 err = got_error_from_errno("strndup");
60 /* Skip \0 field-delimiter at end of token. */
61 while (line[0] == '\0' && n < len) {
67 err = got_error(GOT_ERR_BAD_PACKET);
71 for (j = 0; j < i; j++) {
79 const struct got_error *
80 got_gitproto_parse_refline(char **id_str, char **refname,
81 char **server_capabilities, char *line, int len)
83 const struct got_error *err = NULL;
88 /* don't reset *server_capabilities */
90 err = tokenize_refline(tokens, line, len, nitems(tokens));
99 if (*server_capabilities == NULL) {
101 *server_capabilities = tokens[2];
102 p = strrchr(*server_capabilities, '\n');
112 static const struct got_error *
113 match_capability(char **my_capabilities, const char *capa,
114 const struct got_capability *mycapa)
119 equalsign = strchr(capa, '=');
121 if (strncmp(capa, mycapa->key, equalsign - capa) != 0)
124 if (strcmp(capa, mycapa->key) != 0)
128 if (asprintf(&s, "%s %s%s%s",
129 *my_capabilities != NULL ? *my_capabilities : "",
131 mycapa->value != NULL ? "=" : "",
132 mycapa->value != NULL ? mycapa->value : "") == -1)
133 return got_error_from_errno("asprintf");
135 free(*my_capabilities);
136 *my_capabilities = s;
140 static const struct got_error *
141 add_symref(struct got_pathlist_head *symrefs, char *capa)
143 const struct got_error *err = NULL;
144 char *colon, *name = NULL, *target = NULL;
146 /* Need at least "A:B" */
147 if (strlen(capa) < 3)
150 colon = strchr(capa, ':');
157 return got_error_from_errno("strdup");
159 target = strdup(colon + 1);
160 if (target == NULL) {
161 err = got_error_from_errno("strdup");
165 /* We can't validate the ref itself here. The main process will. */
166 err = got_pathlist_append(symrefs, name, target);
175 const struct got_error *
176 got_gitproto_match_capabilities(char **common_capabilities,
177 struct got_pathlist_head *symrefs, char *server_capabilities,
178 const struct got_capability my_capabilities[], size_t ncapa)
180 const struct got_error *err = NULL;
181 char *capa, *equalsign;
184 *common_capabilities = NULL;
186 capa = strsep(&server_capabilities, " ");
190 equalsign = strchr(capa, '=');
191 if (equalsign != NULL && symrefs != NULL &&
192 strncmp(capa, "symref", equalsign - capa) == 0) {
193 err = add_symref(symrefs, equalsign + 1);
199 for (i = 0; i < ncapa; i++) {
200 err = match_capability(common_capabilities,
201 capa, &my_capabilities[i]);
207 if (*common_capabilities == NULL) {
208 *common_capabilities = strdup("");
209 if (*common_capabilities == NULL)
210 err = got_error_from_errno("strdup");