http.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /*! \file
3  * \brief Minimalistic WWW client
4  *
5  * Copyright (c) 2015-2024 by the developers. See the LICENSE file for details.
6  *
7  * If nothing else is specified, functions return zero to indicate success
8  * and a negative value to indicate an error.
9  */
10 
11 
12 /* ========================================================================== */
13 /* Include headers */
14 
15 #include "posix.h" /* Include this first because of feature test macros */
16 
17 #include <string.h>
18 
19 #include "encoding.h"
20 #include "fileutils.h"
21 #include "http.h"
22 #include "inet.h"
23 #include "main.h"
24 
25 
26 /* ========================================================================== */
27 /*! \defgroup HTTP HTTP: Hyper Text Transfer Protocol
28  *
29  * This module should behave RFC 3986, RFC 7230, RFC 7231 and RFC 7234
30  * conformant.
31  *
32  * This module implements a minimalistic WWW client to download X.509 CRLs via
33  * HTTP/1.1 protocol (for certificate revocation checks of the TLS module).
34  */
35 /*! @{ */
36 
37 
38 /* ========================================================================== */
39 /* Data types */
40 
41 struct http_uri_data
42 {
43  const char* authority; /* The DNS name or IP address of the HTTP server */
44  const char* path; /* The path to the target file on the server */
45 };
46 
47 
48 struct http_status
49 {
50  char digit[3]; /* The 3 single digits of the status code */
51  int code; /* The whole status code as single number */
52 };
53 
54 
55 /* ========================================================================== */
56 /* Constants */
57 
58 /*! \brief Message prefix for HTTP module */
59 #define MAIN_ERR_PREFIX "HTTP: "
60 
61 /*! \brief Permissions for downloaded files that are created by this module */
62 #define HTTP_PERM (api_posix_mode_t) (API_POSIX_S_IRUSR | API_POSIX_S_IWUSR)
63 
64 /*! \brief At least 8000 octets recommended by RFC 7230 */
65 #define HTTP_LINE_MAX (size_t) 8192
66 
67 
68 /* ========================================================================== */
69 /* Create, open and lock local target file */
70 
71 static int http_prepare_local_file(int* fd, const char* lpn)
72 {
73  int res = -1;
74 
75  res = fu_open_file(lpn, fd,
76  API_POSIX_O_RDWR | API_POSIX_O_CREAT| API_POSIX_O_TRUNC,
77  HTTP_PERM);
78  if(!res) { res = fu_lock_file(*fd); }
79 
80  /* Check for error */
81  if(res) { PRINT_ERROR("Unable to create and lock target file"); }
82 
83  return(res);
84 }
85 
86 
87 /* ========================================================================== */
88 /* URI data destructor
89  *
90  * \param[in,out] uri_data Pointer to structure with URI components
91  *
92  * \note
93  * After all memory is released, \c NULL is written to the pointer pointed to
94  * by \e uri_data to indicate that there is no longer data present.
95  */
96 
97 static void http_uri_data_destructor(struct http_uri_data** uri_data)
98 {
99  if(NULL != *uri_data)
100  {
101  api_posix_free((void*) (*uri_data)->authority);
102  api_posix_free((void*) (*uri_data)->path);
103  api_posix_free((void*) *uri_data);
104  *uri_data = NULL;
105  }
106 }
107 
108 
109 /* ========================================================================== */
110 /* URI decoder
111  *
112  * \param[out] uri_data Pointer to structure with extracted URI components
113  * \param[in] uri URI of file on WWW server to download
114  *
115  * \note
116  * On success the caller is responsible to release the memory allocated for the
117  * URI object. Use the function \ref http_uri_data_destructor() for this
118  * purpose.
119  *
120  * \return
121  * - Zero on success
122  * - -1 on unspecified error
123  * - -2 if URI scheme is not supported
124  */
125 
126 static int http_uri_data_constructor(struct http_uri_data** uri_data,
127  const char* uri)
128 {
129  int res = -1;
130  const char* p;
131  char* q;
132  char* t;
133  size_t len;
134 
135  *uri_data = (struct http_uri_data*)
136  api_posix_malloc(sizeof(struct http_uri_data));
137  if(NULL != *uri_data)
138  {
139  (*uri_data)->authority = NULL;
140  (*uri_data)->path = NULL;
141  /* Check scheme */
142  if(strncmp(uri, "http://", 7)) { res = -2; }
143  else
144  {
145  /* Extract authority */
146  p = &uri[7];
147  q = strchr(p, (int) '/');
148  if(NULL != q)
149  {
150  len = (size_t) (q - p);
151  t = (char*) api_posix_malloc(len + (size_t) 1);
152  if(NULL != t)
153  {
154  strncpy(t, p, len);
155  t[len] = 0;
156  (*uri_data)->authority = t;
157  /* Extract path */
158  len = strlen(q);
159  t = (char*) api_posix_malloc(len + (size_t) 1);
160  if(NULL != t)
161  {
162  memcpy(t, q, len);
163  t[len] = 0;
164  (*uri_data)->path = t;
165  res = 0;
166  }
167  }
168  }
169  }
170  }
171 
172  /* Check for error */
173  if(res)
174  {
175  PRINT_ERROR("Decoding of URI failed");
176  http_uri_data_destructor(uri_data);
177  }
178 
179  return(res);
180 }
181 
182 
183 /* ========================================================================== */
184 /* Connect to HTTP service of host
185  *
186  * \param[out] sd Pointer to socket descriptor
187  * \param[in] authority URI of file on WWW server to download
188  *
189  * \note
190  * The value \c -1 is written to \e sd on error.
191  *
192  * \return
193  * - Zero on success (the socket descriptor was written to \e sd )
194  * - -1 on unspecified error
195  * - -3 if authority of URI is not reachable
196  */
197 
198 static int http_connect(int* sd, const char* authority)
199 {
200  int res = 0;
201  int af = API_POSIX_AF_UNSPEC;
202  const char* host = authority;
203  const char* service = "www";
204  const char* p;
205  char* q = NULL;
206  size_t len;
207  size_t i;
208  int connection_failed = 0;
209 
210  /* Prepare for error */
211  *sd = -1;
212 
213  /* Extract host and service from authority */
214  p = strchr(authority, (int) ':');
215  if(NULL != p)
216  {
217  i = (size_t) (p - authority);
218  len = strlen(authority);
219  q = (char*) api_posix_malloc(len + (size_t) 1);
220  if(NULL == q) { res = -1; }
221  else
222  {
223  memcpy(q, authority, len);
224  q[len] = 0;
225  host = q;
226  q[i] = 0;
227  service = &q[++i];
228  }
229  }
230 
231  /* Open connection */
232  if(!res)
233  {
234 #if 0
235  /* For debugging */
236  printf("----------------------\n");
237  printf("Host : %s\n", host);
238  printf("Service: %s\n", service);
239  printf("----------------------\n");
240 #endif
241  if(!res)
242  {
243  res = inet_connect(sd, &af, host, service);
244  if(INET_ERR_CONN == res) { connection_failed = 1; }
245  }
246  }
247 
248  /* Check for error */
249  if(res)
250  {
251  PRINT_ERROR("Cannot connect to server");
252  inet_close(sd);
253  if(connection_failed) { res = -3; }
254  else { res = -1; }
255  }
256 
257  if(NULL != q) { api_posix_free((void*) q); }
258 
259  return(res);
260 }
261 
262 
263 /* ========================================================================== */
264 /* Check for transfer encoding in header of response
265  *
266  * \param[in] rh HTTP response header
267  *
268  * \return
269  * - Zero if no transfer encoding was detected
270  * - 1 if unsupported transfer encoding was detected
271  * - 2 if \c chunked transfer encoding was detected
272  * - -1 on error
273  */
274 
275 static int http_te_check(char* resp_header)
276 {
277  int res = 0;
278  api_posix_locale_t lcp = 0;
279  const char* te_hfn = "Transfer-Encoding:";
280  size_t te_hfn_len = strlen(te_hfn);
281  const char* te_chunked = "chunked";
282  size_t te_chunked_len = strlen(te_chunked);
283  char* rh = resp_header;
284  char* p;
285  size_t i;
286 
287  /* Create a locale object with LC_CTYPE == POSIX */
288  lcp = api_posix_newlocale(API_POSIX_LC_CTYPE_MASK, "POSIX",
289  (api_posix_locale_t) 0);
290  if((api_posix_locale_t) 0 == lcp)
291  {
292  PRINT_ERROR("Cannot create locale object");
293  res = -1;
294  }
295 
296  /* Search for "Transfer-Encoding" header field */
297  if(!res)
298  {
299  do
300  {
301  p = strchr(rh, 0x0A);
302  if(NULL != p)
303  {
304  rh = p + 1;
305  if(!api_posix_strncasecmp_l(rh, te_hfn, te_hfn_len, lcp))
306  {
307  /* "Transfer-Encoding" header field found */
308  i = te_hfn_len;
309  while(0x09 == (int) rh[i] || 0x20 == (int) rh[i]) { ++i; }
310  if(!api_posix_strncasecmp_l(&rh[i], te_chunked, te_chunked_len,
311  lcp))
312  {
313  /*
314  * The "chunked" transfer encoding is defined as mandatory
315  * for HTTP/1.1 by RFC 7230
316  */
317  printf("%s: %sResponse has chunked transfer encoding\n",
318  CFG_NAME, MAIN_ERR_PREFIX);
319  res = 2;
320  }
321  else
322  {
323  PRINT_ERROR("Transfer encoding not supported");
324  res = 1;
325  }
326  }
327  }
328  }
329  while(NULL != p);
330  }
331 
332  /* Release memory allocated for locale object */
333  if((api_posix_locale_t) 0 != lcp) { api_posix_freelocale(lcp); }
334 
335  return(res);
336 }
337 
338 
339 /* ========================================================================== */
340 /* Add line to memory buffer
341  *
342  * \param[in.out] buf Data buffer
343  * \param[in,out] l Pointer to size of \e buf
344  * \param[in] line Line that should be added to \e buf
345  *
346  * \return
347  * - Zero on success (data was written to \e buf )
348  * - -1 if memory allocation failed
349  */
350 
351 static int http_add_to_buffer(char** buf, size_t* l, const char* line)
352 {
353  int res = -1;
354  size_t len;
355 
356  len = strlen(line);
357  *buf = api_posix_realloc(*buf, *l + len);
358  if(NULL == buf)
359  {
360  PRINT_ERROR("Out of memory while adding data to buffer");
361  }
362  else
363  {
364  strcpy(&(*buf)[*l - (size_t) 1], line);
365  *l += len;
366  res = 0;
367  }
368 
369  return(res);
370 }
371 
372 
373 /* ========================================================================== */
374 /* Send GET request
375  *
376  * \param[in] sd Network socket descriptor
377  * \param[in] uri_data Data extracted from URI
378  *
379  * \return
380  * - Zero on success
381  * - -1 on unspecified error
382  */
383 
384 static int http_get_tx(int sd, struct http_uri_data* uri_data)
385 {
386  int res = -1;
387  int rv;
388  char line[HTTP_LINE_MAX];
389  size_t l = 1; /* One byte for string termination */
390  char* buf = NULL;
391  size_t len;
392  size_t i;
393  ssize_t rv2;
394 
395  /* Prepare GET request */
396  rv = api_posix_snprintf(line, HTTP_LINE_MAX, "GET %s HTTP/1.1\r\n",
397  uri_data->path);
398  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
399  {
400  res = http_add_to_buffer(&buf, &l, line);
401  }
402  if(!res)
403  {
404  res = -1;
405  rv = api_posix_snprintf(line, HTTP_LINE_MAX, "Host: %s\r\n",
406  uri_data->authority);
407  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
408  {
409  res = http_add_to_buffer(&buf, &l, line);
410  }
411  }
412  if(!res)
413  {
414  res = -1;
415  rv = api_posix_snprintf(line, HTTP_LINE_MAX, "Connection: close\r\n");
416  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
417  {
418  res = http_add_to_buffer(&buf, &l, line);
419  }
420  }
421  if(!res)
422  {
423  res = -1;
424  /* Accept cached results if they are not too old */
425  rv = api_posix_snprintf(line, HTTP_LINE_MAX,
426  "Cache-Control: max-age=1800\r\n");
427  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
428  {
429  res = http_add_to_buffer(&buf, &l, line);
430  }
431  }
432 #if 1 /* Disable this field for general use (download of non-CRL content) */
433  if(!res)
434  {
435  res = -1;
436  /*
437  * Request for X.509 CRL content only
438  * The IANA registered MIME type 'application/pkix-crl' (specified by
439  * RFC 2585 chapter 4) is preferred for the response.
440  * Legacy Netscape MIME types are accepted too (for backward
441  * compatibility) but associated with lower quality factors.
442  */
443  rv = api_posix_snprintf(line, HTTP_LINE_MAX,
444  "Accept: application/pkix-crl, "
445  "application/pkcs7-crl;q=0.8, "
446  "application/x-pkcs7-crl;q=0.2\r\n");
447  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
448  {
449  res = http_add_to_buffer(&buf, &l, line);
450  }
451  }
452 #endif
453  if(!res)
454  {
455  res = -1;
456  rv = api_posix_snprintf(line, HTTP_LINE_MAX,
457  "Accept-Encoding: identity\r\n");
458  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
459  {
460  res = http_add_to_buffer(&buf, &l, line);
461  }
462  }
463  if(!res)
464  {
465  res = -1;
466  rv = api_posix_snprintf(line, HTTP_LINE_MAX, "User-Agent: %s/%s\r\n",
467  CFG_NAME, CFG_VERSION);
468  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
469  {
470  res = http_add_to_buffer(&buf, &l, line);
471  }
472  }
473  if(!res)
474  {
475  res = -1;
476  rv = api_posix_snprintf(line, HTTP_LINE_MAX, "\r\n");
477  if(0 <= rv && HTTP_LINE_MAX - (size_t) 1 > (size_t) rv)
478  {
479  res = http_add_to_buffer(&buf, &l, line);
480  }
481  }
482 #if 0
483  /* For debugging (print header of request) */
484  printf("----------------------\n");
485  if(!res) { printf("%s", buf); }
486  printf("----------------------\n");
487 #endif
488 
489  /* Send GET request */
490  if(!res)
491  {
492  len = l - (size_t) 1;
493  i = 0;
494  while(i < len)
495  {
496  do { rv2 = api_posix_send(sd, &buf[i], len - i, 0); }
497  while((ssize_t) -1 == rv2 && API_POSIX_EINTR == api_posix_errno);
498  if((ssize_t) -1 == rv2)
499  {
500  PRINT_ERROR("Failed to send GET request");
501  res = -1;
502  break;
503  }
504  i += (size_t) rv2;
505  }
506  }
507 
508  /* Release memory for request buffer */
509  api_posix_free((void*) buf);
510 
511  return(res);
512 }
513 
514 
515 /* ========================================================================== */
516 /* Receive header of response of GET request
517  *
518  * \param[out] sc Status information
519  * \param[in] sd Network socket descriptor
520  * \param[out] cte Pointer to flag indicating "chunked" transfer encoding
521  *
522  * On success, the value 1 is written to \e cte if \c chunked transfer encoding
523  * was detected.
524  *
525  * \return
526  * - Zero on success
527  * - -1 on unspecified error
528  */
529 
530 static int http_get_rx_header(struct http_status* sc, int sd, int* cte)
531 {
532  int res = 0;
533  int eoh = 0; /* End of header flag */
534  char* resp = NULL; /* Response data buffer */
535  size_t len = 512; /* Length of buffer at address 'resp' */
536  size_t ri = 0; /* Index for received data */
537  size_t pi; /* Index for peek (look ahead) data */
538  api_posix_ssize_t rv;
539  size_t i;
540  char* p;
541  int df[3] = { 100, 10, 1 }; /* Decimal digit factors */
542 
543  /*
544  * Note:
545  * The line lengths inside the header may be arbitrary.
546  * We allocate 512Byte at the beginning and expontially increase the size
547  * if required.
548  */
549  resp = (char*) api_posix_malloc(len);
550  if(NULL == resp)
551  {
552  PRINT_ERROR("Out of memory while receiving response");
553  res = -1;
554  }
555 
556  /* Receive response and write it to local file */
557  while(!res && !eoh)
558  {
559  while(!res && !eoh && len > ri)
560  {
561  /* Peek into next block of input */
562  pi = ri;
563  do
564  {
565  rv = api_posix_recv(sd, &resp[ri], len - ri, API_POSIX_MSG_PEEK);
566  }
567  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
568  if(-1 == rv)
569  {
570  PRINT_ERROR("Reading from socket failed");
571  res = -1;
572  break;
573  }
574  pi += (size_t) rv;
575  /* 'pi' now points behind the last character */
576  /* Check for EOH (End Of Header) */
577  i = ri;
578  while(pi > i)
579  {
580  /* Check for EOL */
581  if('\n' == resp[i])
582  {
583  if(1 == i && '\r' == resp[i - 1])
584  {
585  /* Empty header */
586  eoh = 1;
587  res = -1;
588  break;
589  }
590  else if(3 <= i)
591  {
592  if('\r' == resp[i - 1]
593  && '\n' == resp[i - 2] && '\r' == resp[i - 3])
594  {
595  eoh = 1;
596  break;
597  }
598  }
599  }
600  ++i;
601  }
602  if(eoh)
603  {
604  ++i;
605  }
606  /* 'i' now points behind last character of empty separation line */
607  /* Read next block of input (up to but not including index 'i') */
608  while(i > ri)
609  {
610  do { rv = api_posix_recv(sd, &resp[ri], i - ri, 0); }
611  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
612  if(-1 == rv)
613  {
614  PRINT_ERROR("Reading from socket failed");
615  res = -1;
616  break;
617  }
618  ri += (size_t) rv;
619  }
620  /* 'ri' now points behind the last character of ESL */
621  }
622  /* Allocate more memory if required */
623  if(!res && !eoh)
624  {
625  len *= 2;
626  p = (char*) api_posix_realloc(resp, len);
627  if(NULL == p)
628  {
629  PRINT_ERROR("Out of memory while receiving response");
630  res = -1;
631  }
632  else { resp = p; }
633  }
634  }
635  if(!res && !eoh) { res = -1; }
636  if(!res)
637  {
638  /* Write zero termination to header buffer */
639  len = ri;
640  resp[len] = 0;
641 #if 0
642  /* For debugging (print header of received response) */
643  printf("----------------------\n");
644  if(!res) { printf("%s", resp); }
645  printf("----------------------\n");
646 #endif
647  /* Check for transfer encoding */
648  *cte = 0;
649  res = http_te_check(resp);
650  if(res)
651  {
652  if(2 == res)
653  {
654  /* Support for chunked transfer encoding is mandatory */
655  *cte = 1;
656  res = 0;
657  }
658  else { res = -1; }
659  }
660  if(!res)
661  {
662  /* Extract status code */
663  p = strchr(resp, (int) ' ');
664  if(NULL == p) { res = -1; }
665  else
666  {
667  if((size_t) 3 > strlen(++p)) { res = -1; }
668  else
669  {
670  sc->code = 0;
671  for(i = 0; i < 3; ++i)
672  {
673  res = enc_ascii_check_digit(&p[i]);
674  if(res) { break; }
675  sc->digit[i] = p[i] - (char) 48;
676  sc->code += df[i] * (int) (sc->digit[i]);
677  }
678  }
679  }
680  }
681  }
682  api_posix_free((void*) resp);
683 
684  return(res);
685 }
686 
687 
688 /* ========================================================================== */
689 /* Receive header of chunk and extract chunk size
690  *
691  * \param[out] cs Chunk size
692  * \param[in] sd Network socket descriptor
693  *
694  * \return
695  * - Zero on success (chunk size was written to \e cs )
696  * - -1 on unspecified error
697  */
698 
699 static int http_get_chunk_size(size_t* cs, int sd)
700 {
701  int res = 0;
702  char line[HTTP_LINE_MAX];
703  api_posix_ssize_t rv;
704  size_t i = 0;
705  unsigned long int value;
706 
707  /* Read first line of chunk */
708  do
709  {
710  if(HTTP_LINE_MAX <= i) { res = -1; break; }
711  do { rv = api_posix_recv(sd, &line[i], 1, 0); }
712  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
713  if(0 > rv)
714  {
715  res = -1;
716  break;
717  }
718  }
719  while(0x0A != (int) line[i++]);
720 
721  /* Extract chunk size */
722  if(!res)
723  {
724  res = sscanf(line, "%lx", &value);
725  if(1 != res) { res = -1; }
726  else
727  {
728  if((unsigned long int) API_POSIX_UINT_MAX < value) { res = -1; }
729  else
730  {
731  *cs = (size_t) value;
732  res = 0;
733  }
734  }
735  }
736 
737  /* Check for error */
738  if(res) { PRINT_ERROR("Reading chunk size failed"); }
739 
740  return(res);
741 }
742 
743 
744 /* ========================================================================== */
745 /* Receive response of GET request
746  *
747  * \param[in] fd Local target file descriptor
748  * \param[in] sd Network socket descriptor
749  * \param[in] cte Flag indicating "chunked" transfer encoding
750  *
751  * The data received from socket \e sd are written to the file \e fd .
752  *
753  * \return
754  * - Zero on success (data was written to \e fs )
755  * - -1 on unspecified error
756  */
757 
758 static int http_get_rx_body(int fd, int sd, int cte)
759 {
760  int res = 0;
761  api_posix_ssize_t rv;
762  char* chunk = NULL;
763  size_t chunk_size = 4096;
764  size_t len;
765  char* p;
766  char crlf[2];
767 
768  if(!cte)
769  {
770  /* No transfer encoding */
771  chunk = (char*) api_posix_malloc(chunk_size);
772  if(NULL == chunk) { res = -1; }
773  while(!res)
774  {
775  /* Read data from socket */
776  do { rv = api_posix_recv(sd, chunk, chunk_size, 0); }
777  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
778  if(0 > rv)
779  {
780  PRINT_ERROR("Reading from socket failed");
781  res = -1;
782  break;
783  }
784  len = (size_t) rv;
785  /* Check for EOD */
786  if(!len) { break; }
787  /* Write data to local file */
788  res = fu_write_to_filedesc(fd, chunk, len);
789  if(0 > res) { res = -1; }
790  }
791  }
792  else
793  {
794  /* Decode "chunked" transfer encoding */
795  do
796  {
797  /* Read header of next chunk */
798  if(http_get_chunk_size(&chunk_size, sd)) { res = -1; }
799  else if(chunk_size)
800  {
801  /* Reallocate buffer for next chunk */
802  p = (char*) api_posix_realloc(chunk, chunk_size);
803  if(NULL == p) { res = -1; }
804  else
805  {
806  /* Read data of next chunk */
807  chunk = p;
808  len = 0;
809  while(chunk_size > len)
810  {
811  /* Read data from socket */
812  do
813  {
814  rv = api_posix_recv(sd, &chunk[len], chunk_size - len, 0);
815  }
816  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
817  if(0 >= rv)
818  {
819  PRINT_ERROR("Reading from socket failed");
820  res = -1;
821  break;
822  }
823  len += (size_t) rv;
824  }
825  /* Write data to local file */
826  if(!res)
827  {
828  res = fu_write_to_filedesc(fd, chunk, chunk_size);
829  if(0 > res) { res = -1; }
830  }
831  }
832  /* Read trailing CR+LF of chunk */
833  if(!res)
834  {
835  len = 0;
836  while(2 > len)
837  {
838  do { rv = api_posix_recv(sd, &crlf[len], 2 - len, 0); }
839  while(-1 == rv && API_POSIX_EINTR == api_posix_errno);
840  if(0 >= rv)
841  {
842  PRINT_ERROR("Reading from socket failed");
843  res = -1;
844  break;
845  }
846  len += (size_t) rv;
847  }
848  if(!res && (0x0D != (int) crlf[0] || 0x0A != crlf[1]))
849  {
850  PRINT_ERROR("Invalid end of chunk");
851  res = -1;
852  }
853  }
854  }
855  }
856  while(!res && chunk_size);
857  }
858 
859  /* Release memory allocated for buffer */
860  api_posix_free((void*) chunk);
861 
862  return(res);
863 }
864 
865 
866 /* ========================================================================== */
867 /*! \brief Download file from WWW via HTTP
868  *
869  * \param[in] uri URI of file on WWW server to download
870  * \param[in] lpn Local pathname where the file should be stored
871  *
872  * \attention
873  * The caller is responsible to verify that \e uri is a RFC 3986 conformant URI.
874  *
875  * \attention
876  * The caller is responsible that write permission to \e lpn is granted. If the
877  * file already exist, it is overwritten and truncated.
878  *
879  * \return
880  * - Zero on success (file is now stored at \e lpn )
881  * - -1 on unspecified error
882  * - -2 if URI scheme is not supported
883  * - -3 if authority of URI is not reachable
884  * - -4 if requested file not available via path of URI
885  */
886 
887 int http_download_file(const char* uri, const char* lpn)
888 {
889  int res;
890  int fd = -1;
891  struct http_uri_data* uri_data = NULL;
892  int sd = -1;
893  struct http_status sc;
894  int fc = 0; /* "file created" flag */
895  int cte = 0; /* Flag indicating "chunked" transfer encoding */
896 
897  /* Init status code */
898  sc.digit[0] = '5';
899  sc.digit[1] = '0';
900  sc.digit[2] = '0';
901  sc.code = 500;
902 
903  /* Prepare local target file */
904  res = http_prepare_local_file(&fd, lpn);
905  if(!res) { fc = 1; }
906 
907  /* Connect to server */
908  if(!res) { res = http_uri_data_constructor(&uri_data, uri); }
909  if(!res) { res = http_connect(&sd, uri_data->authority); }
910 
911  /* HTTP protocol handling */
912  if(!res) { res = http_get_tx(sd, uri_data); }
913  if(!res) { res = http_get_rx_header(&sc, sd, &cte); }
914  if(!res)
915  {
916  /* Check header of response */
917  if(2 != (int) sc.digit[0])
918  {
919  res = -1;
920  if(5 == (int) sc.digit[0])
921  {
922  PRINT_ERROR("Server reported error status");
923  }
924  if(404 == sc.code) { res = -4; }
925  }
926  }
927  if(!res) { res = http_get_rx_body(fd, sd, cte); }
928 
929  /* Destroy socket (if present) */
930  inet_close(&sd);
931 
932  /* Destroy URI data (if present) */
933  http_uri_data_destructor(&uri_data);
934 
935  /* Close local target file (if present) */
936  fu_close_file(&fd, NULL);
937 
938  /* Check for error */
939  if(res)
940  {
941  PRINT_ERROR("Download failed");
942  if(fc)
943  {
944  /* Unlink local target file */
945  if(fu_unlink_file(lpn))
946  {
947  PRINT_ERROR("Unlinking target file failed");
948  }
949  }
950  }
951  else
952  {
953  printf("%s: %sFile successfully downloaded\n", CFG_NAME, MAIN_ERR_PREFIX);
954  }
955 
956  return(res);
957 }
958 
959 
960 /*! @} */
961 
962 /* EOF */
fu_write_to_filedesc
int fu_write_to_filedesc(int filedesc, const char *buffer, size_t len)
Write data block to filedescriptor.
Definition: fileutils.c:552
HTTP_LINE_MAX
#define HTTP_LINE_MAX
At least 8000 octets recommended by RFC 7230.
Definition: http.c:65
fu_lock_file
int fu_lock_file(int filedesc)
Lock file for writing.
Definition: fileutils.c:335
MAIN_ERR_PREFIX
#define MAIN_ERR_PREFIX
Message prefix for HTTP module.
Definition: http.c:59
http_download_file
int http_download_file(const char *uri, const char *lpn)
Download file from WWW via HTTP.
Definition: http.c:887
inet_close
void inet_close(int *)
Close connection and destroy socket.
Definition: inet.c:548
enc_ascii_check_digit
int enc_ascii_check_digit(const char *s)
Check for ASCII digit characters.
Definition: encoding.c:3973
INET_ERR_CONN
#define INET_ERR_CONN
Connection failed.
Definition: inet.h:19
fu_unlink_file
int fu_unlink_file(const char *pathname)
Unlink file.
Definition: fileutils.c:362
PRINT_ERROR
#define PRINT_ERROR(s)
Prepend module prefix and print error message.
Definition: main.h:19
HTTP_PERM
#define HTTP_PERM
Permissions for downloaded files that are created by this module.
Definition: http.c:62
fu_close_file
void fu_close_file(int *filedesc, FILE **stream)
Close file (and potentially associated I/O stream)
Definition: fileutils.c:297
fu_open_file
int fu_open_file(const char *pathname, int *filedesc, int mode, api_posix_mode_t perm)
Open file.
Definition: fileutils.c:246
inet_connect
int inet_connect(int *, int *, const char *, const char *)
Establish stream oriented connection.
Definition: inet.c:265

Generated at 2026-01-27 using  doxygen