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

Generated at 2024-04-27 using  doxygen