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 err = tokenize_refline(tokens, line, len, nitems(tokens));
87 if (err)
88 return err;
90 if (tokens[0])
91 *id_str = tokens[0];
92 if (tokens[1])
93 *refname = tokens[1];
94 if (tokens[2]) {
95 char *p;
96 *server_capabilities = tokens[2];
97 p = strrchr(*server_capabilities, '\n');
98 if (p)
99 *p = '\0';
102 return NULL;
105 static const struct got_error *
106 match_capability(char **my_capabilities, const char *capa,
107 const struct got_capability *mycapa)
109 char *equalsign;
110 char *s;
112 equalsign = strchr(capa, '=');
113 if (equalsign) {
114 if (strncmp(capa, mycapa->key, equalsign - capa) != 0)
115 return NULL;
116 } else {
117 if (strcmp(capa, mycapa->key) != 0)
118 return NULL;
121 if (asprintf(&s, "%s %s%s%s",
122 *my_capabilities != NULL ? *my_capabilities : "",
123 mycapa->key,
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;
130 return NULL;
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)
141 return NULL;
143 colon = strchr(capa, ':');
144 if (colon == NULL)
145 return NULL;
147 *colon = '\0';
148 name = strdup(capa);
149 if (name == NULL)
150 return got_error_from_errno("strdup");
152 target = strdup(colon + 1);
153 if (target == NULL) {
154 err = got_error_from_errno("strdup");
155 goto done;
158 /* We can't validate the ref itself here. The main process will. */
159 err = got_pathlist_append(symrefs, name, target);
160 done:
161 if (err) {
162 free(name);
163 free(target);
165 return err;
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;
175 size_t i;
177 *common_capabilities = NULL;
178 do {
179 capa = strsep(&server_capabilities, " ");
180 if (capa == NULL)
181 return NULL;
183 equalsign = strchr(capa, '=');
184 if (equalsign != NULL && symrefs != NULL &&
185 strncmp(capa, "symref", equalsign - capa) == 0) {
186 err = add_symref(symrefs, equalsign + 1);
187 if (err)
188 break;
189 continue;
192 for (i = 0; i < ncapa; i++) {
193 err = match_capability(common_capabilities,
194 capa, &my_capabilities[i]);
195 if (err)
196 break;
198 } while (capa);
200 if (*common_capabilities == NULL) {
201 *common_capabilities = strdup("");
202 if (*common_capabilities == NULL)
203 err = got_error_from_errno("strdup");
205 return err;