xref: /third_party/curl/lib/headers.c (revision 13498266)
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
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_header_store *hs,
42                                 size_t index,
43                                 size_t amount,
44                                 struct Curl_llist_element *e,
45                                 struct curl_header *hout)
46{
47  struct curl_header *h = hout;
48  h->name = hs->name;
49  h->value = hs->value;
50  h->amount = amount;
51  h->index = index;
52  /* this will randomly OR a reserved bit for the sole purpose of making it
53     impossible for applications to do == comparisons, as that would otherwise
54     be very tempting and then lead to the reserved bits not being reserved
55     anymore. */
56  h->origin = hs->type | (1<<27);
57  h->anchor = e;
58}
59
60/* public API */
61CURLHcode curl_easy_header(CURL *easy,
62                           const char *name,
63                           size_t nameindex,
64                           unsigned int type,
65                           int request,
66                           struct curl_header **hout)
67{
68  struct Curl_llist_element *e;
69  struct Curl_llist_element *e_pick = NULL;
70  struct Curl_easy *data = easy;
71  size_t match = 0;
72  size_t amount = 0;
73  struct Curl_header_store *hs = NULL;
74  struct Curl_header_store *pick = NULL;
75  if(!name || !hout || !data ||
76     (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
77              CURLH_PSEUDO)) || !type || (request < -1))
78    return CURLHE_BAD_ARGUMENT;
79  if(!Curl_llist_count(&data->state.httphdrs))
80    return CURLHE_NOHEADERS; /* no headers available */
81  if(request > data->state.requests)
82    return CURLHE_NOREQUEST;
83  if(request == -1)
84    request = data->state.requests;
85
86  /* we need a first round to count amount of this header */
87  for(e = data->state.httphdrs.head; e; e = e->next) {
88    hs = e->ptr;
89    if(strcasecompare(hs->name, name) &&
90       (hs->type & type) &&
91       (hs->request == request)) {
92      amount++;
93      pick = hs;
94      e_pick = e;
95    }
96  }
97  if(!amount)
98    return CURLHE_MISSING;
99  else if(nameindex >= amount)
100    return CURLHE_BADINDEX;
101
102  if(nameindex == amount - 1)
103    /* if the last or only occurrence is what's asked for, then we know it */
104    hs = pick;
105  else {
106    for(e = data->state.httphdrs.head; e; e = e->next) {
107      hs = e->ptr;
108      if(strcasecompare(hs->name, name) &&
109         (hs->type & type) &&
110         (hs->request == request) &&
111         (match++ == nameindex)) {
112        e_pick = e;
113        break;
114      }
115    }
116    if(!e) /* this shouldn't happen */
117      return CURLHE_MISSING;
118  }
119  /* this is the name we want */
120  copy_header_external(hs, nameindex, amount, e_pick,
121                       &data->state.headerout[0]);
122  *hout = &data->state.headerout[0];
123  return CURLHE_OK;
124}
125
126/* public API */
127struct curl_header *curl_easy_nextheader(CURL *easy,
128                                         unsigned int type,
129                                         int request,
130                                         struct curl_header *prev)
131{
132  struct Curl_easy *data = easy;
133  struct Curl_llist_element *pick;
134  struct Curl_llist_element *e;
135  struct Curl_header_store *hs;
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(hs, index, amount, pick,
183                       &data->state.headerout[1]);
184  return &data->state.headerout[1];
185}
186
187static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
188                          char **name, char **value)
189{
190  char *end = header + hlen - 1; /* point to the last byte */
191  DEBUGASSERT(hlen);
192  *name = header;
193
194  if(type == CURLH_PSEUDO) {
195    if(*header != ':')
196      return CURLE_BAD_FUNCTION_ARGUMENT;
197    header++;
198  }
199
200  /* Find the end of the header name */
201  while(*header && (*header != ':'))
202    ++header;
203
204  if(*header)
205    /* Skip over colon, null it */
206    *header++ = 0;
207  else
208    return CURLE_BAD_FUNCTION_ARGUMENT;
209
210  /* skip all leading space letters */
211  while(*header && ISBLANK(*header))
212    header++;
213
214  *value = header;
215
216  /* skip all trailing space letters */
217  while((end > header) && ISSPACE(*end))
218    *end-- = 0; /* nul terminate */
219  return CURLE_OK;
220}
221
222static CURLcode unfold_value(struct Curl_easy *data, const char *value,
223                             size_t vlen)  /* length of the incoming header */
224{
225  struct Curl_header_store *hs;
226  struct Curl_header_store *newhs;
227  size_t olen; /* length of the old value */
228  size_t oalloc; /* length of the old name + value + separator */
229  size_t offset;
230  DEBUGASSERT(data->state.prevhead);
231  hs = data->state.prevhead;
232  olen = strlen(hs->value);
233  offset = hs->value - hs->buffer;
234  oalloc = olen + offset + 1;
235
236  /* skip all trailing space letters */
237  while(vlen && ISSPACE(value[vlen - 1]))
238    vlen--;
239
240  /* save only one leading space */
241  while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
242    vlen--;
243    value++;
244  }
245
246  /* since this header block might move in the realloc below, it needs to
247     first be unlinked from the list and then re-added again after the
248     realloc */
249  Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
250
251  /* new size = struct + new value length + old name+value length */
252  newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
253  if(!newhs)
254    return CURLE_OUT_OF_MEMORY;
255  /* ->name' and ->value point into ->buffer (to keep the header allocation
256     in a single memory block), which now potentially have moved. Adjust
257     them. */
258  newhs->name = newhs->buffer;
259  newhs->value = &newhs->buffer[offset];
260
261  /* put the data at the end of the previous data, not the newline */
262  memcpy(&newhs->value[olen], value, vlen);
263  newhs->value[olen + vlen] = 0; /* null-terminate at newline */
264
265  /* insert this node into the list of headers */
266  Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
267                         newhs, &newhs->node);
268  data->state.prevhead = newhs;
269  return CURLE_OK;
270}
271
272
273/*
274 * Curl_headers_push() gets passed a full HTTP header to store. It gets called
275 * immediately before the header callback. The header is CRLF terminated.
276 */
277CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
278                           unsigned char type)
279{
280  char *value = NULL;
281  char *name = NULL;
282  char *end;
283  size_t hlen; /* length of the incoming header */
284  struct Curl_header_store *hs;
285  CURLcode result = CURLE_OUT_OF_MEMORY;
286
287  if((header[0] == '\r') || (header[0] == '\n'))
288    /* ignore the body separator */
289    return CURLE_OK;
290
291  end = strchr(header, '\r');
292  if(!end) {
293    end = strchr(header, '\n');
294    if(!end)
295      /* neither CR nor LF as terminator is not a valid header */
296      return CURLE_WEIRD_SERVER_REPLY;
297  }
298  hlen = end - header;
299
300  if((header[0] == ' ') || (header[0] == '\t')) {
301    if(data->state.prevhead)
302      /* line folding, append value to the previous header's value */
303      return unfold_value(data, header, hlen);
304    else {
305      /* Can't unfold without a previous header. Instead of erroring, just
306         pass the leading blanks. */
307      while(hlen && ISBLANK(*header)) {
308        header++;
309        hlen--;
310      }
311      if(!hlen)
312        return CURLE_WEIRD_SERVER_REPLY;
313    }
314  }
315
316  hs = calloc(1, sizeof(*hs) + hlen);
317  if(!hs)
318    return CURLE_OUT_OF_MEMORY;
319  memcpy(hs->buffer, header, hlen);
320  hs->buffer[hlen] = 0; /* nul terminate */
321
322  result = namevalue(hs->buffer, hlen, type, &name, &value);
323  if(!result) {
324    hs->name = name;
325    hs->value = value;
326    hs->type = type;
327    hs->request = data->state.requests;
328
329    /* insert this node into the list of headers */
330    Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
331                           hs, &hs->node);
332    data->state.prevhead = hs;
333  }
334  else
335    free(hs);
336  return result;
337}
338
339/*
340 * Curl_headers_init(). Init the headers subsystem.
341 */
342static void headers_init(struct Curl_easy *data)
343{
344  Curl_llist_init(&data->state.httphdrs, NULL);
345  data->state.prevhead = NULL;
346}
347
348/*
349 * Curl_headers_cleanup(). Free all stored headers and associated memory.
350 */
351CURLcode Curl_headers_cleanup(struct Curl_easy *data)
352{
353  struct Curl_llist_element *e;
354  struct Curl_llist_element *n;
355
356  for(e = data->state.httphdrs.head; e; e = n) {
357    struct Curl_header_store *hs = e->ptr;
358    n = e->next;
359    free(hs);
360  }
361  headers_init(data);
362  return CURLE_OK;
363}
364
365#else /* HTTP-disabled builds below */
366
367CURLHcode curl_easy_header(CURL *easy,
368                           const char *name,
369                           size_t index,
370                           unsigned int origin,
371                           int request,
372                           struct curl_header **hout)
373{
374  (void)easy;
375  (void)name;
376  (void)index;
377  (void)origin;
378  (void)request;
379  (void)hout;
380  return CURLHE_NOT_BUILT_IN;
381}
382
383struct curl_header *curl_easy_nextheader(CURL *easy,
384                                         unsigned int type,
385                                         int request,
386                                         struct curl_header *prev)
387{
388  (void)easy;
389  (void)type;
390  (void)request;
391  (void)prev;
392  return NULL;
393}
394#endif
395