l10n: Update translation catalogues
[libisds.git] / test / simline / http.c
blob93e74690b0ad46821458fa5ed089ef7eefe94e55
1 #ifndef _XOPEN_SOURCE
2 #include "../../config.h"
3 #define _XOPEN_SOURCE XOPEN_SOURCE_LEVEL_FOR_STRDUP
4 #endif
6 #include "http.h"
7 #include "../test-tools.h"
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <stdio.h> /* fprintf() */
13 #include <limits.h>
14 #include <string.h> /* strdup() */
15 #include <strings.h> /* strcasecmp() */
16 #include <stdint.h> /* int8_t */
17 #include <stddef.h> /* size_t, NULL */
18 #include <ctype.h> /* isprint() */
19 #include <sys/socket.h> /* MSG_NOSIGNAL for http_send_callback() */
22 Base64 encoder is part of the libb64 project, and has been placed in the public domain.
23 For details, see http://sourceforge.net/projects/libb64
24 It's copy of ../../src/cencode.c due to symbol names.
28 typedef enum {
29 step_A, step_B, step_C
30 } base64_encodestep;
32 typedef struct {
33 base64_encodestep step;
34 int8_t result;
35 int stepcount; /* number of encoded octet triplets on a line,
36 or -1 to for end-less line */
37 } base64_encodestate;
39 static const int CHARS_PER_LINE = 72;
41 /* Initialize Base64 coder.
42 * @one_line is false for multi-line MIME encoding,
43 * true for endless one-line format. */
44 static void base64_init_encodestate(base64_encodestate* state_in, _Bool one_line) {
45 state_in->step = step_A;
46 state_in->result = 0;
47 state_in->stepcount = (one_line) ? -1 : 0;
51 static int8_t base64_encode_value(int8_t value_in) {
52 /* XXX: CHAR_BIT == 8 because of <stdint.h> */
53 static const int8_t encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54 if (value_in > 63) return '=';
55 return encoding[value_in];
59 static size_t base64_encode_block(const int8_t* plaintext_in,
60 size_t length_in, int8_t *code_out, base64_encodestate* state_in) {
61 const int8_t *plainchar = plaintext_in;
62 const int8_t* const plaintextend = plaintext_in + length_in;
63 int8_t *codechar = code_out;
64 int8_t result;
65 int8_t fragment;
67 result = state_in->result;
69 switch (state_in->step) {
70 while (1) {
71 case step_A:
72 if (plainchar == plaintextend) {
73 state_in->result = result;
74 state_in->step = step_A;
75 return codechar - code_out;
77 fragment = *plainchar++;
78 result = (fragment & 0x0fc) >> 2;
79 *codechar++ = base64_encode_value(result);
80 result = (fragment & 0x003) << 4;
81 case step_B:
82 if (plainchar == plaintextend) {
83 state_in->result = result;
84 state_in->step = step_B;
85 return codechar - code_out;
87 fragment = *plainchar++;
88 result |= (fragment & 0x0f0) >> 4;
89 *codechar++ = base64_encode_value(result);
90 result = (fragment & 0x00f) << 2;
91 case step_C:
92 if (plainchar == plaintextend) {
93 state_in->result = result;
94 state_in->step = step_C;
95 return codechar - code_out;
97 fragment = *plainchar++;
98 result |= (fragment & 0x0c0) >> 6;
99 *codechar++ = base64_encode_value(result);
100 result = (fragment & 0x03f) >> 0;
101 *codechar++ = base64_encode_value(result);
103 if (state_in->stepcount >= 0) {
104 ++(state_in->stepcount);
105 if (state_in->stepcount == CHARS_PER_LINE/4) {
106 *codechar++ = '\n';
107 state_in->stepcount = 0;
110 } /* while */
111 } /* switch */
113 /* control should not reach here */
114 return codechar - code_out;
118 static size_t base64_encode_blockend(int8_t *code_out,
119 base64_encodestate* state_in) {
120 int8_t *codechar = code_out;
122 switch (state_in->step) {
123 case step_B:
124 *codechar++ = base64_encode_value(state_in->result);
125 *codechar++ = '=';
126 *codechar++ = '=';
127 break;
128 case step_C:
129 *codechar++ = base64_encode_value(state_in->result);
130 *codechar++ = '=';
131 break;
132 case step_A:
133 break;
135 if (state_in->stepcount >= 0)
136 *codechar++ = '\n';
138 return codechar - code_out;
142 /* Encode given data into MIME Base64 encoded zero terminated string.
143 * @plain are input data (binary stream)
144 * @length is length of @plain data in bytes
145 * @one_line is false for multi-line MIME encoding,
146 * true for endless one-line format.
147 * @return allocated string of base64 encoded plain data or NULL in case of
148 * error. You must free it. */
149 /* TODO: Allow one-line format */
150 static char *base64encode(const void *plain, const size_t length,
151 _Bool one_line) {
153 base64_encodestate state;
154 size_t code_length;
155 char *buffer, *new_buffer;
157 if (!plain) {
158 if (length) return NULL;
159 /* Empty input is valid input */
160 plain = "";
163 base64_init_encodestate(&state, one_line);
165 /* Allocate buffer
166 * (4 is padding, 1 is final new line, and 1 is string terminator) */
167 buffer = malloc(length * 2 + 4 + 1 + 1);
168 if (!buffer) return NULL;
170 /* Encode plain data */
171 code_length = base64_encode_block(plain, length, (int8_t *)buffer,
172 &state);
173 code_length += base64_encode_blockend(((int8_t*)buffer) + code_length,
174 &state);
176 /* Terminate string */
177 buffer[code_length++] = '\0';
179 /* Shrink the buffer */
180 new_buffer = realloc(buffer, code_length);
181 if (new_buffer) buffer = new_buffer;
183 return buffer;
187 /* Convert hexadecimal digit to integer. Return negative value if charcter is
188 * not valid hexadecimal digit. */
189 static int hex2i(char digit) {
190 if (digit >= '0' && digit <= '9')
191 return digit - '0';
192 if (digit >= 'a' && digit <= 'f')
193 return digit - 'a' + 10;
194 if (digit >= 'A' && digit <= 'F')
195 return digit - 'A' + 10;
196 return -1;
200 /* Decode URI-coded string.
201 * @return allocated decoded string or NULL in case of error. */
202 static char *uri_decode(const char *coded) {
203 char *plain, *p;
204 int digit1, digit2;
206 if (coded == NULL) return NULL;
207 plain = malloc(strlen(coded) + 1);
208 if (plain == NULL) return NULL;
210 for (p = plain; *coded != '\0'; p++, coded++) {
211 if (*coded == '%') {
212 digit1 = hex2i(coded[1]);
213 if (digit1 < 0) {
214 free(plain);
215 return NULL;
217 digit2 = hex2i(coded[2]);
218 if (digit2< 0) {
219 free(plain);
220 return NULL;
222 *plain = (digit1 << 4) + digit2;
223 coded += 2;
224 } else {
225 *p = *coded;
228 *p = '\0';
230 return plain;
234 /* Read a line from HTTP socket.
235 * @connection is HTTP connection to read from.
236 * @line is auto-reallocated just read line. Will be NULL if EOF has been
237 * reached or error occured.
238 * @buffer is automatically reallocated buffer for the socket. It can preserve
239 * prematurately read socket data.
240 * @buffer_size is allocated size of @buffer
241 * @buffer_length is size of head of the buffer that holds read data.
242 * @return 0 in success. */
243 static int http_read_line(const struct http_connection *connection,
244 char **line, char **buffer, size_t *buffer_size,
245 size_t *buffer_used) {
246 ssize_t got;
247 char *p, *tmp;
249 if (line == NULL) return HTTP_ERROR_SERVER;
250 free(*line);
251 *line = NULL;
253 if (connection == NULL || connection->recv_callback == NULL)
254 return HTTP_ERROR_SERVER;
255 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
256 return HTTP_ERROR_SERVER;
257 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
258 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
260 #define BURST 1024
261 while (1) {
262 /* Check for EOL */
263 for (p = *buffer; p < *buffer + *buffer_used; p++)
265 if (*p != '\r')
266 continue;
267 if (!(p + 1 < *buffer + *buffer_used && p[1] == '\n'))
268 continue;
270 /* EOL found */
271 /* Crop by zero at EOL */
272 *p = '\0';
273 p += 2;
274 /* Copy read ahead data to new buffer and point line to original
275 * buffer. */
276 tmp = malloc(BURST);
277 if (tmp == NULL) return HTTP_ERROR_SERVER;
278 memcpy(tmp, p, *buffer + *buffer_used - p);
279 *line = *buffer;
280 *buffer_size = BURST;
281 *buffer_used = *buffer + *buffer_used - p;
282 *buffer = tmp;
283 /* And exit */
284 return HTTP_ERROR_SUCCESS;
287 if (*buffer_size == *buffer_used) {
288 /* Grow buffer */
289 tmp = realloc(*buffer, *buffer_size + BURST);
290 if (tmp == NULL) return HTTP_ERROR_SERVER;
291 *buffer = tmp;
292 *buffer_size += BURST;
295 /* Read data */
296 got = connection->recv_callback(connection, *buffer + *buffer_used,
297 *buffer_size - *buffer_used);
298 if (got == -1) return HTTP_ERROR_CLIENT;
300 /* Check for EOF */
301 if (got == 0) return HTTP_ERROR_CLIENT;
303 /* Move end of buffer */
304 *buffer_used += got;
306 #undef BURST
308 return HTTP_ERROR_SERVER;
312 /* Write a bulk data into HTTP socket.
313 * @connection is HTTP connection to write to.
314 * @data are bitstream to send to client.
315 * @length is size of @data in bytes.
316 * @return 0 in success. */
317 static int http_write_bulk(const struct http_connection *connection,
318 const void *data, size_t length) {
319 ssize_t written;
320 const void *end;
322 if (connection == NULL || connection->send_callback == NULL)
323 return HTTP_ERROR_SERVER;
324 if (data == NULL && length > 0) return HTTP_ERROR_SERVER;
326 for (end = data + length; data != end; data += written, length -= written) {
327 written = connection->send_callback(connection, data, length);
328 if (written == -1) return HTTP_ERROR_CLIENT;
331 return HTTP_ERROR_SUCCESS;
335 /* Write a line into HTTP socket.
336 * @connection is HTTP connection to write to.
337 * @line is NULL terminated string to send to client. HTTP EOL will be added.
338 * @return 0 in success. */
339 static int http_write_line(const struct http_connection *connection,
340 const char *line) {
341 int error;
342 if (line == NULL) return HTTP_ERROR_SERVER;
344 fprintf(stderr, "Response: <%s>\n", line);
346 /* Send the line */
347 if ((error = http_write_bulk(connection, line, strlen(line))))
348 return error;
350 /* Send EOL */
351 if ((error = http_write_bulk(connection, "\r\n", 2)))
352 return error;
354 return HTTP_ERROR_SUCCESS;
358 /* Read data of given length from HTTP socket.
359 * @connection is HTTP connection to read from.
360 * @data is auto-allocated just read data bulk. Will be NULL if EOF has been
361 * reached or error occured.
362 * @data_length is size of bytes to read.
363 * @buffer is automatically reallocated buffer for the socket. It can preserve
364 * prematurately read socket data.
365 * @buffer_size is allocated size of @buffer
366 * @buffer_length is size of head of the buffer that holds read data.
367 * @return 0 in success. */
368 static int http_read_bulk(const struct http_connection *connection,
369 void **data, size_t data_length,
370 char **buffer, size_t *buffer_size, size_t *buffer_used) {
371 ssize_t got;
372 char *tmp;
374 if (data == NULL) return HTTP_ERROR_SERVER;
375 *data = NULL;
377 if (connection == NULL || connection->recv_callback == NULL)
378 return HTTP_ERROR_SERVER;
379 if (buffer == NULL || buffer_size == NULL || buffer_used == NULL)
380 return HTTP_ERROR_SERVER;
381 if (*buffer == NULL && *buffer_size > 0) return HTTP_ERROR_SERVER;
382 if (*buffer_size < *buffer_used) return HTTP_ERROR_SERVER;
384 if (data_length <= 0) return HTTP_ERROR_SUCCESS;
386 #define BURST 1024
387 while (1) {
388 /* Check whether enought data have been read */
389 if (*buffer_used >= data_length) {
390 /* Copy read ahead data to new buffer and point data to original
391 * buffer. */
392 tmp = malloc(BURST);
393 if (tmp == NULL) return HTTP_ERROR_SERVER;
394 memcpy(tmp, *buffer + data_length, *buffer_used - data_length);
395 *data = *buffer;
396 *buffer_size = BURST;
397 *buffer_used = *buffer_used - data_length;
398 *buffer = tmp;
399 /* And exit */
400 return HTTP_ERROR_SUCCESS;
403 if (*buffer_size == *buffer_used) {
404 /* Grow buffer */
405 tmp = realloc(*buffer, *buffer_size + BURST);
406 if (tmp == NULL) return HTTP_ERROR_SERVER;
407 *buffer = tmp;
408 *buffer_size += BURST;
411 /* Read data */
412 got = connection->recv_callback(connection, *buffer + *buffer_used,
413 *buffer_size - *buffer_used);
414 if (got == -1) return HTTP_ERROR_CLIENT;
416 /* Check for EOF */
417 if (got == 0) return HTTP_ERROR_CLIENT;
419 /* Move end of buffer */
420 *buffer_used += got;
422 #undef BURST
424 return HTTP_ERROR_SERVER;
428 /* Parse HTTP request header.
429 * @request is pre-allocated HTTP request.
430 * @return 0 if success. */
431 static int http_parse_request_header(char *line,
432 struct http_request *request) {
433 char *p;
435 fprintf(stderr, "Request: <%s>\n", line);
437 /* Get method */
438 p = strchr(line, ' ');
439 if (p == NULL) return HTTP_ERROR_SERVER;
440 *p = '\0';
441 if (!strcmp(line, "GET"))
442 request->method = HTTP_METHOD_GET;
443 else if (!strcmp(line, "POST"))
444 request->method = HTTP_METHOD_POST;
445 else
446 request->method = HTTP_METHOD_UNKNOWN;
447 line = p + 1;
449 /* Get URI */
450 p = strchr(line, ' ');
451 if (p != NULL) *p = '\0';
452 request->uri = uri_decode(line);
453 if (request->uri == NULL) return HTTP_ERROR_SERVER;
455 /* Do not care about HTTP version */
457 fprintf(stderr, "Request-URI: <%s>\n", request->uri);
458 return HTTP_ERROR_SUCCESS;
462 /* Send HTTP response status line to client.
463 * @return 0 if success. */
464 static int http_write_response_status(const struct http_connection *connection,
465 const struct http_response *response) {
466 char *buffer = NULL;
467 int error;
469 if (response == NULL) return HTTP_ERROR_SERVER;
471 if (-1 == test_asprintf(&buffer, "HTTP/1.0 %u %s", response->status,
472 (response->reason == NULL) ? "" : response->reason))
473 return HTTP_ERROR_SERVER;
474 error = http_write_line(connection, buffer);
475 free(buffer);
477 return error;
481 /* Parse generic HTTP header.
482 * @request is pre-allocated HTTP request.
483 * @return 0 if success, negative value if internal error, positive value if
484 * header is not valid. */
485 static int http_parse_header(char *line, struct http_request *request) {
486 struct http_header *header;
488 if (line == NULL || request == NULL) return HTTP_ERROR_SERVER;
490 fprintf(stderr, "Header: <%s>\n", line);
492 /* Find last used header */
493 for (header = request->headers; header != NULL && header->next != NULL;
494 header = header->next);
496 if (*line == ' ' || *line == '\t') {
497 /* Line is continuation of last header */
498 if (header == NULL)
499 return HTTP_ERROR_CLIENT; /* No previous header to continue */
500 line++;
501 size_t old_length = strlen(header->value);
502 char *tmp = realloc(header->value,
503 sizeof(header->value[0]) * (old_length + strlen(line) + 1));
504 if (tmp == NULL) return HTTP_ERROR_SERVER;
505 header->value = tmp;
506 strcpy(&header->value[old_length], line);
507 } else {
508 /* New header */
509 struct http_header *new_header = calloc(sizeof(*new_header), 1);
510 if (new_header == NULL) return HTTP_ERROR_SERVER;
512 char *p = strstr(line, ": ");
513 if (p == NULL) {
514 http_header_free(&new_header);
515 return HTTP_ERROR_CLIENT;
518 size_t length = p - line;
519 new_header->name = malloc(sizeof(line[0]) * (length + 1));
520 if (new_header->name == NULL) {
521 http_header_free(&new_header);
522 return HTTP_ERROR_SERVER;
524 strncpy(new_header->name, line, length);
525 new_header->name[length] = '\0';
527 p += 2;
528 length = strlen(p);
529 new_header->value = malloc(sizeof(p[0]) * (length + 1));
530 if (new_header->value == NULL) {
531 http_header_free(&new_header);
532 return HTTP_ERROR_SERVER;
534 strcpy(new_header->value, p);
536 if (request->headers == NULL)
537 request->headers = new_header;
538 else
539 header->next = new_header;
543 /* FIXME: Decode. After parsing all headers as we could decode begining
544 * and then got encoded continuation. */
545 return HTTP_ERROR_SUCCESS;
549 /* Send HTTP header to client.
550 * @return 0 if success. */
551 static int http_write_header(const struct http_connection *connection,
552 const struct http_header *header) {
553 char *buffer = NULL;
554 int error;
556 if (header == NULL) return HTTP_ERROR_SERVER;
558 if (header->name == NULL) return HTTP_ERROR_SERVER;
560 /* TODO: Quote, split long lines */
561 if (-1 == test_asprintf(&buffer, "%s: %s", header->name,
562 (header->value == NULL) ? "" : header->value))
563 return HTTP_ERROR_SERVER;
564 error = http_write_line(connection, buffer);
565 free(buffer);
567 return error;
571 /* Find Content-Length value in HTTP request headers and set it into @request.
572 * @return 0 in success. */
573 static int find_content_length(struct http_request *request) {
574 struct http_header *header;
575 if (request == NULL) return HTTP_ERROR_SERVER;
577 for (header = request->headers; header != NULL; header = header->next) {
578 if (header->name == NULL) continue;
579 if (!strcasecmp(header->name, "Content-Length")) break;
582 if (header != NULL && header->value != NULL) {
583 char *p;
584 long long int value = strtoll(header->value, &p, 10);
585 if (*p != '\0')
586 return HTTP_ERROR_CLIENT;
587 if ((value == LLONG_MIN || value == LLONG_MAX) && errno == ERANGE)
588 return HTTP_ERROR_SERVER;
589 if (value < 0)
590 return HTTP_ERROR_CLIENT;
591 if ((unsigned long long)value > SIZE_MAX)
592 return HTTP_ERROR_SERVER;
593 request->body_length = value;
594 } else {
595 request->body_length = 0;
597 return HTTP_ERROR_SUCCESS;
601 /* Print binary stream to STDERR with some escapes */
602 static void dump_body(const void *data, size_t length) {
603 fprintf(stderr, "===BEGIN BODY===\n");
604 if (length > 0 && NULL != data) {
605 for (size_t i = 0; i < length; i++) {
606 if (isprint(((const unsigned char *)data)[i]))
607 fprintf(stderr, "%c", ((const unsigned char *)data)[i]);
608 else
609 fprintf(stderr, "\\x%02x", ((const unsigned char*)data)[i]);
611 fprintf(stderr, "\n");
613 fprintf(stderr, "===END BODY===\n");
617 /* Read a HTTP request from connection.
618 * @http_request is heap-allocated received HTTP request,
619 * or NULL in case of error.
620 * @return http_error code. */
621 http_error http_read_request(const struct http_connection *connection,
622 struct http_request **request) {
623 char *line = NULL;
624 char *buffer = NULL;
625 size_t buffer_size = 0, buffer_used = 0;
626 int error;
628 if (request == NULL) return HTTP_ERROR_SERVER;
630 *request = calloc(1, sizeof(**request));
631 if (*request == NULL) return HTTP_ERROR_SERVER;
633 /* Get request header */
634 if ((error = http_read_line(connection, &line, &buffer, &buffer_size,
635 &buffer_used)))
636 goto leave;
637 if ((error = http_parse_request_header(line, *request)))
638 goto leave;
640 /* Get other headers */
641 while (1) {
642 if ((error = http_read_line(connection, &line, &buffer, &buffer_size,
643 &buffer_used))) {
644 fprintf(stderr, "Error while reading HTTP request line\n");
645 goto leave;
648 /* Check for headers delimiter */
649 if (line == NULL || *line == '\0') {
650 break;
653 if ((error = http_parse_header(line, *request))) {
654 fprintf(stderr, "Error while parsing HTTP request line: <%s>\n",
655 line);
656 goto leave;
660 /* Get body */
661 if ((error = find_content_length(*request))) {
662 fprintf(stderr, "Could not determine length of body\n");
663 goto leave;
665 if ((error = http_read_bulk(connection,
666 &(*request)->body, (*request)->body_length,
667 &buffer, &buffer_size, &buffer_used))) {
668 fprintf(stderr, "Could not read request body\n");
669 goto leave;
671 fprintf(stderr, "Body of size %zu B has been received:\n",
672 (*request)->body_length);
673 dump_body((*request)->body, (*request)->body_length);
675 leave:
676 free(line);
677 free(buffer);
678 if (error) http_request_free(request);
679 return error;
683 /* Write a HTTP response to connection. Auto-add Content-Length header.
684 * @return 0 in case of success. */
685 int http_write_response(const struct http_connection *connection,
686 const struct http_response *response) {
687 char *buffer = NULL;
688 int error = -1;
690 if (response == NULL) return HTTP_ERROR_SERVER;
692 /* Status line */
693 error = http_write_response_status(connection, response);
694 if (error) return error;
696 /* Headers */
697 for (struct http_header *header = response->headers; header != NULL;
698 header = header->next) {
699 error = http_write_header(connection, header);
700 if (error) return error;
702 if (-1 == test_asprintf(&buffer, "Content-Length: %u",
703 response->body_length))
704 return HTTP_ERROR_SERVER;
705 error = http_write_line(connection, buffer);
706 if (error) return error;
708 /* Headers trailer */
709 error = http_write_line(connection, "");
710 if (error) return error;
712 /* Body */
713 if (response->body_length > 0) {
714 error = http_write_bulk(connection, response->body,
715 response->body_length);
716 if (error) return error;
718 fprintf(stderr, "Body of size %zu B has been sent:\n",
719 response->body_length);
720 dump_body(response->body, response->body_length);
722 free(buffer);
723 return HTTP_ERROR_SUCCESS;
727 /* Build Set-Cookie header. In case of error, return NULL. Caller must free
728 * the header. */
729 static struct http_header *http_build_setcookie_header (
730 const char *cokie_name, const char *cookie_value,
731 const char *cookie_domain, const char *cookie_path) {
732 struct http_header *header = NULL;
733 char *domain_parameter = NULL;
734 char *path_parameter = NULL;
736 if (cokie_name == NULL) goto error;
738 header = calloc(1, sizeof(*header));
739 if (header == NULL) goto error;
741 header->name = strdup("Set-Cookie");
742 if (header->name == NULL) goto error;
744 if (cookie_domain != NULL)
745 if (-1 == test_asprintf(&domain_parameter, "; Domain=%s",
746 cookie_domain))
747 goto error;
749 if (cookie_path != NULL)
750 if (-1 == test_asprintf(&path_parameter, "; Path=%s",
751 cookie_path))
752 goto error;
754 if (-1 == test_asprintf(&header->value, "%s=%s%s%s",
755 cokie_name,
756 (cookie_value == NULL) ? "" : cookie_value,
757 (domain_parameter == NULL) ? "": domain_parameter,
758 (path_parameter == NULL) ? "": path_parameter))
759 goto error;
760 goto ok;
762 error:
763 http_header_free(&header);
765 free(domain_parameter);
766 free(path_parameter);
767 return header;
771 /* Send a 200 Ok response with a cookie */
772 int http_send_response_200_cookie(const struct http_connection *connection,
773 const char *cokie_name, const char *cookie_value,
774 const char *cookie_domain, const char *cookie_path,
775 const void *body, size_t body_length, const char *type) {
776 int retval;
777 struct http_header *header_cookie = NULL;
778 struct http_header header_contenttype = {
779 .name = "Content-Type",
780 .value = (char *)type,
781 .next = NULL
783 struct http_response response = {
784 .status = 200,
785 .reason = "OK",
786 .headers = NULL,
787 .body_length = body_length,
788 .body = (void *)body
791 if (cokie_name != NULL) {
792 if (NULL == (header_cookie = http_build_setcookie_header(
793 cokie_name, cookie_value, cookie_domain, cookie_path)))
794 return http_send_response_500(connection,
795 "Could not build Set-Cookie header");
798 /* Link defined headers */
799 if (type != NULL) {
800 response.headers = &header_contenttype;
802 if (header_cookie != NULL) {
803 header_cookie->next = response.headers;
804 response.headers = header_cookie;
807 retval = http_write_response(connection, &response);
808 http_header_free(&header_cookie);
809 return retval;
813 /* Send a 200 Ok response */
814 int http_send_response_200(const struct http_connection *connection,
815 const void *body, size_t body_length, const char *type) {
816 return http_send_response_200_cookie(connection,
817 NULL, NULL, NULL, NULL,
818 body, body_length, type);
822 /* Send a 302 Found response setting a cookie */
823 int http_send_response_302_cookie(const struct http_connection *connection,
824 const char *cokie_name, const char *cookie_value,
825 const char *cookie_domain, const char *cookie_path,
826 const char *location) {
827 int retval;
828 struct http_header *header_cookie = NULL;
829 struct http_header header_location = {
830 .name = "Location",
831 .value = (char *) location,
832 .next = NULL
834 struct http_response response = {
835 .status = 302,
836 .reason = "Found",
837 .headers = NULL,
838 .body_length = 0,
839 .body = NULL
842 if (cokie_name != NULL) {
843 if (NULL == (header_cookie = http_build_setcookie_header(
844 cokie_name, cookie_value, cookie_domain, cookie_path)))
845 return http_send_response_500(connection,
846 "Could not build Set-Cookie header");
849 /* Link defined headers */
850 if (location != NULL) {
851 response.headers = &header_location;
853 if (header_cookie != NULL) {
854 header_cookie->next = response.headers;
855 response.headers = header_cookie;
858 retval = http_write_response(connection, &response);
859 http_header_free(&header_cookie);
860 return retval;
864 /* Send a 302 Found response with totp authentication scheme header */
865 int http_send_response_302_totp(const struct http_connection *connection,
866 const char *code, const char *text, const char *location) {
867 struct http_header header_code = {
868 .name = "X-Response-message-code",
869 .value = (char *) code,
870 .next = NULL
872 struct http_header header_text = {
873 .name = "X-Response-message-text",
874 .value = (char *) text,
875 .next = NULL
877 struct http_header header_location = {
878 .name = "Location",
879 .value = (char *) location,
880 .next = NULL
882 struct http_response response = {
883 .status = 302,
884 .reason = "Found",
885 .headers = NULL,
886 .body_length = 0,
887 .body = NULL
890 /* Link defined headers */
891 if (location != NULL) {
892 response.headers = &header_location;
894 if (text != NULL) {
895 header_text.next = response.headers;
896 response.headers = &header_text;
898 if (code != NULL) {
899 header_code.next = response.headers;
900 response.headers = &header_code;
903 return http_write_response(connection, &response);
907 /* Send a 400 Bad Request response.
908 * Use non-NULL @reason to override status message. */
909 int http_send_response_400(const struct http_connection *connection,
910 const char *reason) {
911 struct http_response response = {
912 .status = 400,
913 .reason = (reason == NULL) ? "Bad Request" : (char *) reason,
914 .headers = NULL,
915 .body_length = 0,
916 .body = NULL
919 return http_write_response(connection, &response);
923 /* Send a 401 Unauthorized response with Basic authentication scheme header */
924 int http_send_response_401_basic(const struct http_connection *connection) {
925 struct http_header header = {
926 .name = "WWW-Authenticate",
927 .value = "Basic realm=\"SimulatedISDSServer\"",
928 .next = NULL
930 struct http_response response = {
931 .status = 401,
932 .reason = "Unauthorized",
933 .headers = &header,
934 .body_length = 0,
935 .body = NULL
938 return http_write_response(connection, &response);
942 /* Send a 401 Unauthorized response with OTP authentication scheme header for
943 * given @method. */
944 int http_send_response_401_otp(const struct http_connection *connection,
945 const char *method, const char *code, const char *text) {
946 int retval;
947 struct http_header header = {
948 .name = "WWW-Authenticate",
949 .value = NULL,
950 .next = NULL
952 struct http_header header_code = {
953 .name = "X-Response-message-code",
954 .value = (char *) code,
955 .next = NULL
957 struct http_header header_text = {
958 .name = "X-Response-message-text",
959 .value = (char *) text,
960 .next = NULL
962 struct http_response response = {
963 .status = 401,
964 .reason = "Unauthorized",
965 .headers = &header,
966 .body_length = 0,
967 .body = NULL
970 if (-1 == test_asprintf(&header.value, "%s realm=\"SimulatedISDSServer\"",
971 method)) {
972 return http_send_response_500(connection,
973 "Could not build WWW-Authenticate header value");
976 /* Link defined headers */
977 if (code != NULL) header.next = &header_code;
978 if (text != NULL) {
979 if (code != NULL)
980 header_code.next = &header_text;
981 else
982 header.next = &header_text;
985 retval = http_write_response(connection, &response);
986 free(header.value);
987 return retval;
991 /* Send a 403 Forbidden response */
992 int http_send_response_403(const struct http_connection *connection) {
993 struct http_response response = {
994 .status = 403,
995 .reason = "Forbidden",
996 .headers = NULL,
997 .body_length = 0,
998 .body = NULL
1001 return http_write_response(connection, &response);
1005 /* Send a 500 Internal Server Error response.
1006 * Use non-NULL @reason to override status message. */
1007 int http_send_response_500(const struct http_connection *connection,
1008 const char *reason) {
1009 struct http_response response = {
1010 .status = 500,
1011 .reason = (NULL == reason) ? "Internal Server Error" : (char *) reason,
1012 .headers = NULL,
1013 .body_length = 0,
1014 .body = NULL
1017 return http_write_response(connection, &response);
1021 /* Send a 503 Service Temporarily Unavailable response */
1022 int http_send_response_503(const struct http_connection *connection,
1023 const void *body, size_t body_length, const char *type) {
1024 struct http_header header = {
1025 .name = "Content-Type",
1026 .value = (char *)type
1028 struct http_response response = {
1029 .status = 503,
1030 .reason = "Service Temporarily Unavailable",
1031 .headers = (type == NULL) ? NULL : &header,
1032 .body_length = body_length,
1033 .body = (void *)body
1036 return http_write_response(connection, &response);
1040 /* Returns Authorization header in given request */
1041 static const struct http_header *find_authorization(
1042 const struct http_request *request) {
1043 const struct http_header *header;
1044 if (request == NULL) return NULL;
1045 for (header = request->headers; header != NULL; header = header->next) {
1046 if (header->name != NULL &&
1047 !strcasecmp(header->name, "Authorization"))
1048 break;
1050 return header;
1054 /* Return true if request carries Authorization header */
1055 int http_client_authenticates(const struct http_request *request) {
1056 if (find_authorization(request))
1057 return 1;
1058 else
1059 return 0;
1063 /* Return HTTP_ERROR_SUCCESS if request carries valid Basic credentials.
1064 * NULL @username or @password equales to empty string. */
1065 http_error http_authenticate_basic(const struct http_request *request,
1066 const char *username, const char *password) {
1067 const struct http_header *header;
1068 const char *basic_cookie_client;
1069 char *basic_cookie_plain = NULL, *basic_cookie_encoded = NULL;
1070 _Bool is_valid;
1072 header = find_authorization(request);
1073 if (header == NULL) return HTTP_ERROR_CLIENT;
1075 if (strncmp(header->value, "Basic ", 6)) return HTTP_ERROR_CLIENT;
1076 basic_cookie_client = header->value + 6;
1078 if (-1 == test_asprintf(&basic_cookie_plain, "%s:%s",
1079 (username == NULL) ? "" : username,
1080 (password == NULL) ? "" : password)) {
1081 return HTTP_ERROR_SERVER;
1084 basic_cookie_encoded = base64encode(basic_cookie_plain,
1085 strlen(basic_cookie_plain), 1);
1086 if (basic_cookie_encoded == NULL) {
1087 free(basic_cookie_plain);
1088 return HTTP_ERROR_SERVER;
1091 fprintf(stderr, "Authenticating basic: got=<%s>, expected=<%s> (%s)\n",
1092 basic_cookie_client, basic_cookie_encoded, basic_cookie_plain);
1093 free(basic_cookie_plain);
1095 is_valid = !strcmp(basic_cookie_encoded, basic_cookie_client);
1096 free(basic_cookie_encoded);
1098 return (is_valid) ? HTTP_ERROR_SUCCESS : HTTP_ERROR_CLIENT;
1102 /* Return HTTP_ERROR_SUCCESS if request carries valid OTP credentials.
1103 * NULL @username or @password or @otp equal to empty string. */
1104 http_error http_authenticate_otp(const struct http_request *request,
1105 const char *username, const char *password, const char *otp) {
1106 char *basic_password = NULL;
1107 http_error retval;
1109 /* Concatenate password and OTP code */
1110 if (-1 == test_asprintf(&basic_password, "%s%s",
1111 (password == NULL) ? "": password,
1112 (otp == NULL) ? "" : otp)) {
1113 return HTTP_ERROR_SERVER;
1116 /* Use Basic authentication */
1117 /* XXX: Specification does not define authorization method string */
1118 retval = http_authenticate_basic(request, username, basic_password);
1120 free(basic_password);
1121 return retval;
1125 /* Return cookie value by name or NULL if does not present. */
1126 const char *http_find_cookie(const struct http_request *request,
1127 const char *name) {
1128 const struct http_header *header;
1129 size_t length;
1130 const char *value = NULL;
1132 if (request == NULL || name == NULL) return NULL;
1133 length = strlen(name);
1135 for (header = request->headers; header != NULL; header = header->next) {
1136 if (header->name != NULL && !strcasecmp(header->name, "Cookie") &&
1137 header->value != NULL) {
1138 if (!strncmp(header->value, name, length) &&
1139 header->value[length] == '=') {
1140 /* Return last cookie with the name */
1141 value = header->value + length + 1;
1145 return value;
1149 /* Return Host header value or NULL if does not present. Returned string is
1150 * statically allocated. */
1151 const char *http_find_host(const struct http_request *request) {
1152 const struct http_header *header;
1153 const char *value = NULL;
1155 if (request == NULL) return NULL;
1157 for (header = request->headers; header != NULL; header = header->next) {
1158 if (header->name != NULL && !strcmp(header->name, "Host")) {
1159 value = header->value;
1162 return value;
1166 /* Free a HTTP header and set it to NULL */
1167 void http_header_free(struct http_header **header) {
1168 if (header == NULL || *header == NULL) return;
1169 free((*header)->name);
1170 free((*header)->value);
1171 free(*header);
1172 *header = NULL;
1176 /* Free linked list of HTTP headers and set it to NULL */
1177 void http_headers_free(struct http_header **headers) {
1178 struct http_header *header, *next;
1180 if (headers == NULL || *headers == NULL) return;
1182 for (header = *headers; header != NULL;) {
1183 next = header->next;
1184 http_header_free(&header);
1185 header = next;
1188 *headers = NULL;
1191 /* Free HTTP request and set it to NULL */
1192 void http_request_free(struct http_request **request) {
1193 if (request == NULL || *request == NULL) return;
1194 free((*request)->uri);
1195 http_headers_free(&((*request)->headers));
1196 free((*request)->body);
1197 free(*request);
1198 *request = NULL;
1201 /* Free HTTP response and set it to NULL */
1202 void http_response_free(struct http_response **response) {
1203 if (response == NULL || *response == NULL) return;
1204 free((*response)->reason);
1205 http_headers_free(&((*response)->headers));
1206 free((*response)->body);
1207 free(*response);
1208 *response = NULL;