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/queue.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;
86 err = tokenize_refline(tokens, line, len, nitems(tokens));
96 *server_capabilities = tokens[2];
97 p = strrchr(*server_capabilities, '\n');
105 static const struct got_error *
106 match_capability(char **my_capabilities, const char *capa,
107 const struct got_capability *mycapa)
112 equalsign = strchr(capa, '=');
114 if (strncmp(capa, mycapa->key, equalsign - capa) != 0)
117 if (strcmp(capa, mycapa->key) != 0)
121 if (asprintf(&s, "%s %s%s%s",
122 *my_capabilities != NULL ? *my_capabilities : "",
124 mycapa->value != NULL ? "=" : "",
125 mycapa->value != NULL ? mycapa->value : "") == -1)
126 return got_error_from_errno("asprintf");
128 free(*my_capabilities);
129 *my_capabilities = s;
133 static const struct got_error *
134 add_symref(struct got_pathlist_head *symrefs, char *capa)
136 const struct got_error *err = NULL;
137 char *colon, *name = NULL, *target = NULL;
139 /* Need at least "A:B" */
140 if (strlen(capa) < 3)
143 colon = strchr(capa, ':');
150 return got_error_from_errno("strdup");
152 target = strdup(colon + 1);
153 if (target == NULL) {
154 err = got_error_from_errno("strdup");
158 /* We can't validate the ref itself here. The main process will. */
159 err = got_pathlist_append(symrefs, name, target);
168 const struct got_error *
169 got_gitproto_match_capabilities(char **common_capabilities,
170 struct got_pathlist_head *symrefs, char *server_capabilities,
171 const struct got_capability my_capabilities[], size_t ncapa)
173 const struct got_error *err = NULL;
174 char *capa, *equalsign;
177 *common_capabilities = NULL;
179 capa = strsep(&server_capabilities, " ");
183 equalsign = strchr(capa, '=');
184 if (equalsign != NULL && symrefs != NULL &&
185 strncmp(capa, "symref", equalsign - capa) == 0) {
186 err = add_symref(symrefs, equalsign + 1);
192 for (i = 0; i < ncapa; i++) {
193 err = match_capability(common_capabilities,
194 capa, &my_capabilities[i]);
200 if (*common_capabilities == NULL) {
201 *common_capabilities = strdup("");
202 if (*common_capabilities == NULL)
203 err = got_error_from_errno("strdup");