Blob


1 /*
2 * Copyright (c) 2019 Ori Bernstein <ori@openbsd.org>
3 * Copyright (c) 2021 Stefan Sperling <stsp@openbsd.org>
4 *
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.
8 *
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.
16 */
18 #include <sys/queue.h>
19 #include <sys/types.h>
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
26 #include "got_error.h"
27 #include "got_path.h"
29 #include "got_lib_gitproto.h"
31 #ifndef nitems
32 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
33 #endif
35 static const struct got_error *
36 tokenize_refline(char **tokens, char *line, int len, int maxtokens)
37 {
38 const struct got_error *err = NULL;
39 char *p;
40 size_t i, n = 0;
42 for (i = 0; i < maxtokens; i++)
43 tokens[i] = NULL;
45 for (i = 0; n < len && i < maxtokens; i++) {
46 while (isspace(*line)) {
47 line++;
48 n++;
49 }
50 p = line;
51 while (*line != '\0' && n < len &&
52 (!isspace(*line) || i == maxtokens - 1)) {
53 line++;
54 n++;
55 }
56 tokens[i] = strndup(p, line - p);
57 if (tokens[i] == NULL) {
58 err = got_error_from_errno("strndup");
59 goto done;
60 }
61 /* Skip \0 field-delimiter at end of token. */
62 while (line[0] == '\0' && n < len) {
63 line++;
64 n++;
65 }
66 }
67 if (i <= 2)
68 err = got_error(GOT_ERR_BAD_PACKET);
69 done:
70 if (err) {
71 int j;
72 for (j = 0; j < i; j++) {
73 free(tokens[j]);
74 tokens[j] = NULL;
75 }
76 }
77 return err;
78 }
80 const struct got_error *
81 got_gitproto_parse_refline(char **id_str, char **refname,
82 char **server_capabilities, char *line, int len)
83 {
84 const struct got_error *err = NULL;
85 char *tokens[3];
87 *id_str = NULL;
88 *refname = NULL;
89 /* don't reset *server_capabilities */
91 err = tokenize_refline(tokens, line, len, nitems(tokens));
92 if (err)
93 return err;
95 if (tokens[0])
96 *id_str = tokens[0];
97 if (tokens[1])
98 *refname = tokens[1];
99 if (tokens[2]) {
100 if (*server_capabilities == NULL) {
101 char *p;
102 *server_capabilities = tokens[2];
103 p = strrchr(*server_capabilities, '\n');
104 if (p)
105 *p = '\0';
106 } else
107 free(tokens[2]);
110 return NULL;
113 static const struct got_error *
114 match_capability(char **my_capabilities, const char *capa,
115 const struct got_capability *mycapa)
117 char *equalsign;
118 char *s;
120 equalsign = strchr(capa, '=');
121 if (equalsign) {
122 if (strncmp(capa, mycapa->key, equalsign - capa) != 0)
123 return NULL;
124 } else {
125 if (strcmp(capa, mycapa->key) != 0)
126 return NULL;
129 if (asprintf(&s, "%s %s%s%s",
130 *my_capabilities != NULL ? *my_capabilities : "",
131 mycapa->key,
132 mycapa->value != NULL ? "=" : "",
133 mycapa->value != NULL ? mycapa->value : "") == -1)
134 return got_error_from_errno("asprintf");
136 free(*my_capabilities);
137 *my_capabilities = s;
138 return NULL;
141 static const struct got_error *
142 add_symref(struct got_pathlist_head *symrefs, char *capa)
144 const struct got_error *err = NULL;
145 char *colon, *name = NULL, *target = NULL;
147 /* Need at least "A:B" */
148 if (strlen(capa) < 3)
149 return NULL;
151 colon = strchr(capa, ':');
152 if (colon == NULL)
153 return NULL;
155 *colon = '\0';
156 name = strdup(capa);
157 if (name == NULL)
158 return got_error_from_errno("strdup");
160 target = strdup(colon + 1);
161 if (target == NULL) {
162 err = got_error_from_errno("strdup");
163 goto done;
166 /* We can't validate the ref itself here. The main process will. */
167 err = got_pathlist_append(symrefs, name, target);
168 done:
169 if (err) {
170 free(name);
171 free(target);
173 return err;
176 const struct got_error *
177 got_gitproto_match_capabilities(char **common_capabilities,
178 struct got_pathlist_head *symrefs, char *server_capabilities,
179 const struct got_capability my_capabilities[], size_t ncapa)
181 const struct got_error *err = NULL;
182 char *capa, *equalsign;
183 size_t i;
185 *common_capabilities = NULL;
186 do {
187 capa = strsep(&server_capabilities, " ");
188 if (capa == NULL)
189 return NULL;
191 equalsign = strchr(capa, '=');
192 if (equalsign != NULL && symrefs != NULL &&
193 strncmp(capa, "symref", equalsign - capa) == 0) {
194 err = add_symref(symrefs, equalsign + 1);
195 if (err)
196 break;
197 continue;
200 for (i = 0; i < ncapa; i++) {
201 err = match_capability(common_capabilities,
202 capa, &my_capabilities[i]);
203 if (err)
204 break;
206 } while (capa);
208 if (*common_capabilities == NULL) {
209 *common_capabilities = strdup("");
210 if (*common_capabilities == NULL)
211 err = got_error_from_errno("strdup");
213 return err;