1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2022, Daniel Stenberg, <[email protected]>, 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
27#include "urldata.h"
28#include "strdup.h"
29#include "strcase.h"
30#include "headers.h"
31
32/* The last 3 #include files should be in this order */
33#include "curl_printf.h"
34#include "curl_memory.h"
35#include "memdebug.h"
36
37#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
38
39/* Generate the curl_header struct for the user. This function MUST assign all
40 struct fields in the output struct. */
41static void copy_header_external(struct Curl_easy *data,
42 struct Curl_header_store *hs,
43 size_t index,
44 size_t amount,
45 struct Curl_llist_element *e,
46 struct curl_header **hout)
47{
48 struct curl_header *h = *hout = &data->state.headerout;
49 h->name = hs->name;
50 h->value = hs->value;
51 h->amount = amount;
52 h->index = index;
53 /* this will randomly OR a reserved bit for the sole purpose of making it
54 impossible for applications to do == comparisons, as that would otherwise
55 be very tempting and then lead to the reserved bits not being reserved
56 anymore. */
57 h->origin = hs->type | (1<<27);
58 h->anchor = e;
59}
60
61/* public API */
62CURLHcode curl_easy_header(CURL *easy,
63 const char *name,
64 size_t nameindex,
65 unsigned int type,
66 int request,
67 struct curl_header **hout)
68{
69 struct Curl_llist_element *e;
70 struct Curl_llist_element *e_pick = NULL;
71 struct Curl_easy *data = easy;
72 size_t match = 0;
73 size_t amount = 0;
74 struct Curl_header_store *hs = NULL;
75 struct Curl_header_store *pick = NULL;
76 if(!name || !hout || !data ||
77 (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
78 CURLH_PSEUDO)) || !type || (request < -1))
79 return CURLHE_BAD_ARGUMENT;
80 if(!Curl_llist_count(&data->state.httphdrs))
81 return CURLHE_NOHEADERS; /* no headers available */
82 if(request > data->state.requests)
83 return CURLHE_NOREQUEST;
84 if(request == -1)
85 request = data->state.requests;
86
87 /* we need a first round to count amount of this header */
88 for(e = data->state.httphdrs.head; e; e = e->next) {
89 hs = e->ptr;
90 if(strcasecompare(hs->name, name) &&
91 (hs->type & type) &&
92 (hs->request == request)) {
93 amount++;
94 pick = hs;
95 e_pick = e;
96 }
97 }
98 if(!amount)
99 return CURLHE_MISSING;
100 else if(nameindex >= amount)
101 return CURLHE_BADINDEX;
102
103 if(nameindex == amount - 1)
104 /* if the last or only occurrence is what's asked for, then we know it */
105 hs = pick;
106 else {
107 for(e = data->state.httphdrs.head; e; e = e->next) {
108 hs = e->ptr;
109 if(strcasecompare(hs->name, name) &&
110 (hs->type & type) &&
111 (hs->request == request) &&
112 (match++ == nameindex)) {
113 e_pick = e;
114 break;
115 }
116 }
117 if(!e) /* this shouldn't happen */
118 return CURLHE_MISSING;
119 }
120 /* this is the name we want */
121 copy_header_external(data, hs, nameindex, amount, e_pick, hout);
122 return CURLHE_OK;
123}
124
125/* public API */
126struct curl_header *curl_easy_nextheader(CURL *easy,
127 unsigned int type,
128 int request,
129 struct curl_header *prev)
130{
131 struct Curl_easy *data = easy;
132 struct Curl_llist_element *pick;
133 struct Curl_llist_element *e;
134 struct Curl_header_store *hs;
135 struct curl_header *hout;
136 size_t amount = 0;
137 size_t index = 0;
138
139 if(request > data->state.requests)
140 return NULL;
141 if(request == -1)
142 request = data->state.requests;
143
144 if(prev) {
145 pick = prev->anchor;
146 if(!pick)
147 /* something is wrong */
148 return NULL;
149 pick = pick->next;
150 }
151 else
152 pick = data->state.httphdrs.head;
153
154 if(pick) {
155 /* make sure it is the next header of the desired type */
156 do {
157 hs = pick->ptr;
158 if((hs->type & type) && (hs->request == request))
159 break;
160 pick = pick->next;
161 } while(pick);
162 }
163
164 if(!pick)
165 /* no more headers available */
166 return NULL;
167
168 hs = pick->ptr;
169
170 /* count number of occurrences of this name within the mask and figure out
171 the index for the currently selected entry */
172 for(e = data->state.httphdrs.head; e; e = e->next) {
173 struct Curl_header_store *check = e->ptr;
174 if(strcasecompare(hs->name, check->name) &&
175 (check->request == request) &&
176 (check->type & type))
177 amount++;
178 if(e == pick)
179 index = amount - 1;
180 }
181
182 copy_header_external(data, hs, index, amount, pick, &hout);
183 return hout;
184}
185
186static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
187 char **name, char **value)
188{
189 char *end = header + hlen - 1; /* point to the last byte */
190 DEBUGASSERT(hlen);
191 *name = header;
192
193 if(type == CURLH_PSEUDO) {
194 if(*header != ':')
195 return CURLE_BAD_FUNCTION_ARGUMENT;
196 header++;
197 }
198
199 /* Find the end of the header name */
200 while(*header && (*header != ':'))
201 ++header;
202
203 if(*header)
204 /* Skip over colon, null it */
205 *header++ = 0;
206 else
207 return CURLE_BAD_FUNCTION_ARGUMENT;
208
209 /* skip all leading space letters */
210 while(*header && ISSPACE(*header))
211 header++;
212
213 *value = header;
214
215 /* skip all trailing space letters */
216 while((end > header) && ISSPACE(*end))
217 *end-- = 0; /* nul terminate */
218 return CURLE_OK;
219}
220
221static CURLcode unfold_value(struct Curl_easy *data, const char *value,
222 size_t vlen) /* length of the incoming header */
223{
224 struct Curl_header_store *hs;
225 struct Curl_header_store *newhs;
226 size_t olen; /* length of the old value */
227 size_t oalloc; /* length of the old name + value + separator */
228 size_t offset;
229 DEBUGASSERT(data->state.prevhead);
230 hs = data->state.prevhead;
231 olen = strlen(hs->value);
232 offset = hs->value - hs->buffer;
233 oalloc = olen + offset + 1;
234
235 /* skip all trailing space letters */
236 while(vlen && ISSPACE(value[vlen - 1]))
237 vlen--;
238
239 /* save only one leading space */
240 while((vlen > 1) && ISSPACE(value[0]) && ISSPACE(value[1])) {
241 vlen--;
242 value++;
243 }
244
245 /* since this header block might move in the realloc below, it needs to
246 first be unlinked from the list and then re-added again after the
247 realloc */
248 Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
249
250 /* new size = struct + new value length + old name+value length */
251 newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
252 if(!newhs)
253 return CURLE_OUT_OF_MEMORY;
254 /* ->name' and ->value point into ->buffer (to keep the header allocation
255 in a single memory block), which now potentially have moved. Adjust
256 them. */
257 newhs->name = newhs->buffer;
258 newhs->value = &newhs->buffer[offset];
259
260 /* put the data at the end of the previous data, not the newline */
261 memcpy(&newhs->value[olen], value, vlen);
262 newhs->value[olen + vlen] = 0; /* zero terminate at newline */
263
264 /* insert this node into the list of headers */
265 Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
266 newhs, &newhs->node);
267 data->state.prevhead = newhs;
268 return CURLE_OK;
269}
270
271
272/*
273 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
274 * immediately before the header callback. The header is CRLF terminated.
275 */
276CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
277 unsigned char type)
278{
279 char *value = NULL;
280 char *name = NULL;
281 char *end;
282 size_t hlen; /* length of the incoming header */
283 struct Curl_header_store *hs;
284 CURLcode result = CURLE_OUT_OF_MEMORY;
285
286 if((header[0] == '\r') || (header[0] == '\n'))
287 /* ignore the body separator */
288 return CURLE_OK;
289
290 end = strchr(header, '\r');
291 if(!end) {
292 end = strchr(header, '\n');
293 if(!end)
294 return CURLE_BAD_FUNCTION_ARGUMENT;
295 }
296 hlen = end - header + 1;
297
298 if((header[0] == ' ') || (header[0] == '\t')) {
299 if(data->state.prevhead)
300 /* line folding, append value to the previous header's value */
301 return unfold_value(data, header, hlen);
302 else
303 /* can't unfold without a previous header */
304 return CURLE_BAD_FUNCTION_ARGUMENT;
305 }
306
307 hs = calloc(1, sizeof(*hs) + hlen);
308 if(!hs)
309 return CURLE_OUT_OF_MEMORY;
310 memcpy(hs->buffer, header, hlen);
311 hs->buffer[hlen] = 0; /* nul terminate */
312
313 result = namevalue(hs->buffer, hlen, type, &name, &value);
314 if(result)
315 goto fail;
316
317 hs->name = name;
318 hs->value = value;
319 hs->type = type;
320 hs->request = data->state.requests;
321
322 /* insert this node into the list of headers */
323 Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
324 hs, &hs->node);
325 data->state.prevhead = hs;
326 return CURLE_OK;
327 fail:
328 free(hs);
329 return result;
330}
331
332/*
333 * Curl_headers_init(). Init the headers subsystem.
334 */
335static void headers_init(struct Curl_easy *data)
336{
337 Curl_llist_init(&data->state.httphdrs, NULL);
338}
339
340/*
341 * Curl_headers_cleanup(). Free all stored headers and associated memory.
342 */
343CURLcode Curl_headers_cleanup(struct Curl_easy *data)
344{
345 struct Curl_llist_element *e;
346 struct Curl_llist_element *n;
347
348 for(e = data->state.httphdrs.head; e; e = n) {
349 struct Curl_header_store *hs = e->ptr;
350 n = e->next;
351 free(hs);
352 }
353 headers_init(data);
354 return CURLE_OK;
355}
356
357#else /* HTTP-disabled builds below */
358
359CURLHcode curl_easy_header(CURL *easy,
360 const char *name,
361 size_t index,
362 unsigned int origin,
363 int request,
364 struct curl_header **hout)
365{
366 (void)easy;
367 (void)name;
368 (void)index;
369 (void)origin;
370 (void)request;
371 (void)hout;
372 return CURLHE_NOT_BUILT_IN;
373}
374
375struct curl_header *curl_easy_nextheader(CURL *easy,
376 unsigned int type,
377 int request,
378 struct curl_header *prev)
379{
380 (void)easy;
381 (void)type;
382 (void)request;
383 (void)prev;
384 return NULL;
385}
386#endif
387