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/types.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include "got_error.h"
26 #include "got_path.h"
28 #include "got_lib_gitproto.h"
30 #ifndef nitems
31 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
32 #endif
34 static const struct got_error *
35 tokenize_refline(char **tokens, char *line, int len, int maxtokens)
36 {
37 const struct got_error *err = NULL;
38 char *p;
39 size_t i, n = 0;
41 for (i = 0; i < maxtokens; i++)
42 tokens[i] = NULL;
44 for (i = 0; n < len && i < maxtokens; i++) {
45 while (isspace(*line)) {
46 line++;
47 n++;
48 }
49 p = line;
50 while (*line != '\0' && n < len &&
51 (!isspace(*line) || i == maxtokens - 1)) {
52 line++;
53 n++;
54 }
55 tokens[i] = strndup(p, line - p);
56 if (tokens[i] == NULL) {
57 err = got_error_from_errno("strndup");
58 goto done;
59 }
60 /* Skip \0 field-delimiter at end of token. */
61 while (line[0] == '\0' && n < len) {
62 line++;
63 n++;
64 }
65 }
66 if (i <= 2)
67 err = got_error(GOT_ERR_BAD_PACKET);
68 done:
69 if (err) {
70 int j;
71 for (j = 0; j < i; j++) {
72 free(tokens[j]);
73 tokens[j] = NULL;
74 }
75 }
76 return err;
77 }
79 const struct got_error *
80 got_gitproto_parse_refline(char **id_str, char **refname,
81 char **server_capabilities, char *line, int len)
82 {
83 const struct got_error *err = NULL;
84 char *tokens[3];
86 *id_str = NULL;
87 *refname = NULL;
88 /* don't reset *server_capabilities */
90 err = tokenize_refline(tokens, line, len, nitems(tokens));
91 if (err)
92 return err;
94 if (tokens[0])
95 *id_str = tokens[0];
96 if (tokens[1])
97 *refname = tokens[1];
98 if (tokens[2]) {
99 if (*server_capabilities == NULL) {
100 char *p;
101 *server_capabilities = tokens[2];
102 p = strrchr(*server_capabilities, '\n');
103 if (p)
104 *p = '\0';
105 } else
106 free(tokens[2]);
109 return NULL;
112 static const struct got_error *
113 match_capability(char **my_capabilities, const char *capa,
114 const struct got_capability *mycapa)
116 char *equalsign;
117 char *s;
119 equalsign = strchr(capa, '=');
120 if (equalsign) {
121 if (strncmp(capa, mycapa->key, equalsign - capa) != 0)
122 return NULL;
123 } else {
124 if (strcmp(capa, mycapa->key) != 0)
125 return NULL;
128 if (asprintf(&s, "%s %s%s%s",
129 *my_capabilities != NULL ? *my_capabilities : "",
130 mycapa->key,
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;
137 return NULL;
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)
148 return NULL;
150 colon = strchr(capa, ':');
151 if (colon == NULL)
152 return NULL;
154 *colon = '\0';
155 name = strdup(capa);
156 if (name == NULL)
157 return got_error_from_errno("strdup");
159 target = strdup(colon + 1);
160 if (target == NULL) {
161 err = got_error_from_errno("strdup");
162 goto done;
165 /* We can't validate the ref itself here. The main process will. */
166 err = got_pathlist_append(symrefs, name, target);
167 done:
168 if (err) {
169 free(name);
170 free(target);
172 return err;
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;
182 size_t i;
184 *common_capabilities = NULL;
185 do {
186 capa = strsep(&server_capabilities, " ");
187 if (capa == NULL)
188 return NULL;
190 equalsign = strchr(capa, '=');
191 if (equalsign != NULL && symrefs != NULL &&
192 strncmp(capa, "symref", equalsign - capa) == 0) {
193 err = add_symref(symrefs, equalsign + 1);
194 if (err)
195 break;
196 continue;
199 for (i = 0; i < ncapa; i++) {
200 err = match_capability(common_capabilities,
201 capa, &my_capabilities[i]);
202 if (err)
203 break;
205 } while (capa);
207 if (*common_capabilities == NULL) {
208 *common_capabilities = strdup("");
209 if (*common_capabilities == NULL)
210 err = got_error_from_errno("strdup");
212 return err;