1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26#include "dynhds.h"
27#include "strcase.h"
28
29/* The last 3 #include files should be in this order */
30#include "curl_printf.h"
31#include "curl_memory.h"
32#include "memdebug.h"
33
34
35static struct dynhds_entry *
36entry_new(const char *name, size_t namelen,
37 const char *value, size_t valuelen, int opts)
38{
39 struct dynhds_entry *e;
40 char *p;
41
42 DEBUGASSERT(name);
43 DEBUGASSERT(value);
44 e = calloc(1, sizeof(*e) + namelen + valuelen + 2);
45 if(!e)
46 return NULL;
47 e->name = p = ((char *)e) + sizeof(*e);
48 memcpy(dest: p, src: name, n: namelen);
49 e->namelen = namelen;
50 e->value = p += namelen + 1; /* leave a \0 at the end of name */
51 memcpy(dest: p, src: value, n: valuelen);
52 e->valuelen = valuelen;
53 if(opts & DYNHDS_OPT_LOWERCASE)
54 Curl_strntolower(dest: e->name, src: e->name, n: e->namelen);
55 return e;
56}
57
58static struct dynhds_entry *
59entry_append(struct dynhds_entry *e,
60 const char *value, size_t valuelen)
61{
62 struct dynhds_entry *e2;
63 size_t valuelen2 = e->valuelen + 1 + valuelen;
64 char *p;
65
66 DEBUGASSERT(value);
67 e2 = calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2);
68 if(!e2)
69 return NULL;
70 e2->name = p = ((char *)e2) + sizeof(*e2);
71 memcpy(dest: p, src: e->name, n: e->namelen);
72 e2->namelen = e->namelen;
73 e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */
74 memcpy(dest: p, src: e->value, n: e->valuelen);
75 p += e->valuelen;
76 p[0] = ' ';
77 memcpy(dest: p + 1, src: value, n: valuelen);
78 e2->valuelen = valuelen2;
79 return e2;
80}
81
82static void entry_free(struct dynhds_entry *e)
83{
84 free(e);
85}
86
87void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries,
88 size_t max_strs_size)
89{
90 DEBUGASSERT(dynhds);
91 DEBUGASSERT(max_strs_size);
92 dynhds->hds = NULL;
93 dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0;
94 dynhds->max_entries = max_entries;
95 dynhds->max_strs_size = max_strs_size;
96 dynhds->opts = 0;
97}
98
99void Curl_dynhds_free(struct dynhds *dynhds)
100{
101 DEBUGASSERT(dynhds);
102 if(dynhds->hds && dynhds->hds_len) {
103 size_t i;
104 DEBUGASSERT(dynhds->hds);
105 for(i = 0; i < dynhds->hds_len; ++i) {
106 entry_free(e: dynhds->hds[i]);
107 }
108 }
109 Curl_safefree(dynhds->hds);
110 dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0;
111}
112
113void Curl_dynhds_reset(struct dynhds *dynhds)
114{
115 DEBUGASSERT(dynhds);
116 if(dynhds->hds_len) {
117 size_t i;
118 DEBUGASSERT(dynhds->hds);
119 for(i = 0; i < dynhds->hds_len; ++i) {
120 entry_free(e: dynhds->hds[i]);
121 dynhds->hds[i] = NULL;
122 }
123 }
124 dynhds->hds_len = dynhds->strs_len = 0;
125}
126
127size_t Curl_dynhds_count(struct dynhds *dynhds)
128{
129 return dynhds->hds_len;
130}
131
132void Curl_dynhds_set_opts(struct dynhds *dynhds, int opts)
133{
134 dynhds->opts = opts;
135}
136
137struct dynhds_entry *Curl_dynhds_getn(struct dynhds *dynhds, size_t n)
138{
139 DEBUGASSERT(dynhds);
140 return (n < dynhds->hds_len)? dynhds->hds[n] : NULL;
141}
142
143struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name,
144 size_t namelen)
145{
146 size_t i;
147 for(i = 0; i < dynhds->hds_len; ++i) {
148 if(dynhds->hds[i]->namelen == namelen &&
149 strncasecompare(dynhds->hds[i]->name, name, namelen)) {
150 return dynhds->hds[i];
151 }
152 }
153 return NULL;
154}
155
156struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name)
157{
158 return Curl_dynhds_get(dynhds, name, namelen: strlen(s: name));
159}
160
161CURLcode Curl_dynhds_add(struct dynhds *dynhds,
162 const char *name, size_t namelen,
163 const char *value, size_t valuelen)
164{
165 struct dynhds_entry *entry = NULL;
166 CURLcode result = CURLE_OUT_OF_MEMORY;
167
168 DEBUGASSERT(dynhds);
169 if(dynhds->max_entries && dynhds->hds_len >= dynhds->max_entries)
170 return CURLE_OUT_OF_MEMORY;
171 if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size)
172 return CURLE_OUT_OF_MEMORY;
173
174entry = entry_new(name, namelen, value, valuelen, opts: dynhds->opts);
175 if(!entry)
176 goto out;
177
178 if(dynhds->hds_len + 1 >= dynhds->hds_allc) {
179 size_t nallc = dynhds->hds_len + 16;
180 struct dynhds_entry **nhds;
181
182 if(dynhds->max_entries && nallc > dynhds->max_entries)
183 nallc = dynhds->max_entries;
184
185 nhds = calloc(nallc, sizeof(struct dynhds_entry *));
186 if(!nhds)
187 goto out;
188 if(dynhds->hds) {
189 memcpy(dest: nhds, src: dynhds->hds,
190 n: dynhds->hds_len * sizeof(struct dynhds_entry *));
191 Curl_safefree(dynhds->hds);
192 }
193 dynhds->hds = nhds;
194 dynhds->hds_allc = nallc;
195 }
196 dynhds->hds[dynhds->hds_len++] = entry;
197 entry = NULL;
198 dynhds->strs_len += namelen + valuelen;
199 result = CURLE_OK;
200
201out:
202 if(entry)
203 entry_free(e: entry);
204 return result;
205}
206
207CURLcode Curl_dynhds_cadd(struct dynhds *dynhds,
208 const char *name, const char *value)
209{
210 return Curl_dynhds_add(dynhds, name, namelen: strlen(s: name), value, valuelen: strlen(s: value));
211}
212
213CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
214 const char *line, size_t line_len)
215{
216 const char *p;
217 const char *name;
218 size_t namelen;
219 const char *value;
220 size_t valuelen, i;
221
222 if(!line || !line_len)
223 return CURLE_OK;
224
225 if((line[0] == ' ') || (line[0] == '\t')) {
226 struct dynhds_entry *e, *e2;
227 /* header continuation, yikes! */
228 if(!dynhds->hds_len)
229 return CURLE_BAD_FUNCTION_ARGUMENT;
230
231 while(line_len && ISBLANK(line[0])) {
232 ++line;
233 --line_len;
234 }
235 if(!line_len)
236 return CURLE_BAD_FUNCTION_ARGUMENT;
237 e = dynhds->hds[dynhds->hds_len-1];
238 e2 = entry_append(e, value: line, valuelen: line_len);
239 if(!e2)
240 return CURLE_OUT_OF_MEMORY;
241 dynhds->hds[dynhds->hds_len-1] = e2;
242 entry_free(e);
243 return CURLE_OK;
244 }
245 else {
246 p = memchr(s: line, c: ':', n: line_len);
247 if(!p)
248 return CURLE_BAD_FUNCTION_ARGUMENT;
249 name = line;
250 namelen = p - line;
251 p++; /* move past the colon */
252 for(i = namelen + 1; i < line_len; ++i, ++p) {
253 if(!ISBLANK(*p))
254 break;
255 }
256 value = p;
257 valuelen = line_len - i;
258
259 p = memchr(s: value, c: '\r', n: valuelen);
260 if(!p)
261 p = memchr(s: value, c: '\n', n: valuelen);
262 if(p)
263 valuelen = (size_t)(p - value);
264
265 return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
266 }
267}
268
269CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line)
270{
271 return Curl_dynhds_h1_add_line(dynhds, line, line_len: line? strlen(s: line) : 0);
272}
273
274#ifdef DEBUGBUILD
275/* used by unit2602.c */
276
277bool Curl_dynhds_contains(struct dynhds *dynhds,
278 const char *name, size_t namelen)
279{
280 return !!Curl_dynhds_get(dynhds, name, namelen);
281}
282
283bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name)
284{
285 return Curl_dynhds_contains(dynhds, name, strlen(name));
286}
287
288size_t Curl_dynhds_count_name(struct dynhds *dynhds,
289 const char *name, size_t namelen)
290{
291 size_t n = 0;
292 if(dynhds->hds_len) {
293 size_t i;
294 for(i = 0; i < dynhds->hds_len; ++i) {
295 if((namelen == dynhds->hds[i]->namelen) &&
296 strncasecompare(name, dynhds->hds[i]->name, namelen))
297 ++n;
298 }
299 }
300 return n;
301}
302
303size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name)
304{
305 return Curl_dynhds_count_name(dynhds, name, strlen(name));
306}
307
308CURLcode Curl_dynhds_set(struct dynhds *dynhds,
309 const char *name, size_t namelen,
310 const char *value, size_t valuelen)
311{
312 Curl_dynhds_remove(dynhds, name, namelen);
313 return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
314}
315
316size_t Curl_dynhds_remove(struct dynhds *dynhds,
317 const char *name, size_t namelen)
318{
319 size_t n = 0;
320 if(dynhds->hds_len) {
321 size_t i, len;
322 for(i = 0; i < dynhds->hds_len; ++i) {
323 if((namelen == dynhds->hds[i]->namelen) &&
324 strncasecompare(name, dynhds->hds[i]->name, namelen)) {
325 ++n;
326 --dynhds->hds_len;
327 dynhds->strs_len -= (dynhds->hds[i]->namelen +
328 dynhds->hds[i]->valuelen);
329 entry_free(dynhds->hds[i]);
330 len = dynhds->hds_len - i; /* remaining entries */
331 if(len) {
332 memmove(&dynhds->hds[i], &dynhds->hds[i + 1],
333 len * sizeof(dynhds->hds[i]));
334 }
335 --i; /* do this index again */
336 }
337 }
338 }
339 return n;
340}
341
342size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name)
343{
344 return Curl_dynhds_remove(dynhds, name, strlen(name));
345}
346
347#endif
348
349CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
350{
351 CURLcode result = CURLE_OK;
352 size_t i;
353
354 if(!dynhds->hds_len)
355 return result;
356
357 for(i = 0; i < dynhds->hds_len; ++i) {
358 result = Curl_dyn_addf(s: dbuf, fmt: "%.*s: %.*s\r\n",
359 (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
360 (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value);
361 if(result)
362 break;
363 }
364
365 return result;
366}
367
368