1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include "esp_log.h"
14 #include "http_header.h"
15 #include "http_utils.h"
16 
17 static const char *TAG = "HTTP_HEADER";
18 #define HEADER_BUFFER (1024)
19 
20 /**
21  * dictionary item struct, with key-value pair
22  */
23 typedef struct http_header_item {
24     char *key;                          /*!< key */
25     char *value;                        /*!< value */
26     STAILQ_ENTRY(http_header_item) next;   /*!< Point to next entry */
27 } http_header_item_t;
28 
29 STAILQ_HEAD(http_header, http_header_item);
30 
31 
http_header_init(void)32 http_header_handle_t http_header_init(void)
33 {
34     http_header_handle_t header = calloc(1, sizeof(struct http_header));
35     HTTP_MEM_CHECK(TAG, header, return NULL);
36     STAILQ_INIT(header);
37     return header;
38 }
39 
http_header_destroy(http_header_handle_t header)40 esp_err_t http_header_destroy(http_header_handle_t header)
41 {
42     esp_err_t err = http_header_clean(header);
43     free(header);
44     return err;
45 }
46 
http_header_get_item(http_header_handle_t header,const char * key)47 http_header_item_handle_t http_header_get_item(http_header_handle_t header, const char *key)
48 {
49     http_header_item_handle_t item;
50     if (header == NULL || key == NULL) {
51         return NULL;
52     }
53     STAILQ_FOREACH(item, header, next) {
54         if (strcasecmp(item->key, key) == 0) {
55             return item;
56         }
57     }
58     return NULL;
59 }
60 
http_header_get(http_header_handle_t header,const char * key,char ** value)61 esp_err_t http_header_get(http_header_handle_t header, const char *key, char **value)
62 {
63     http_header_item_handle_t item;
64 
65     item = http_header_get_item(header, key);
66     if (item) {
67         *value = item->value;
68     } else {
69         *value = NULL;
70     }
71 
72     return ESP_OK;
73 }
74 
http_header_new_item(http_header_handle_t header,const char * key,const char * value)75 static esp_err_t http_header_new_item(http_header_handle_t header, const char *key, const char *value)
76 {
77     http_header_item_handle_t item;
78 
79     item = calloc(1, sizeof(http_header_item_t));
80     HTTP_MEM_CHECK(TAG, item, return ESP_ERR_NO_MEM);
81     http_utils_assign_string(&item->key, key, -1);
82     HTTP_MEM_CHECK(TAG, item->key, goto _header_new_item_exit);
83     http_utils_trim_whitespace(&item->key);
84     http_utils_assign_string(&item->value, value, -1);
85     HTTP_MEM_CHECK(TAG, item->value, goto _header_new_item_exit);
86     http_utils_trim_whitespace(&item->value);
87     STAILQ_INSERT_TAIL(header, item, next);
88     return ESP_OK;
89 _header_new_item_exit:
90     free(item->key);
91     free(item->value);
92     free(item);
93     return ESP_ERR_NO_MEM;
94 }
95 
http_header_set(http_header_handle_t header,const char * key,const char * value)96 esp_err_t http_header_set(http_header_handle_t header, const char *key, const char *value)
97 {
98     http_header_item_handle_t item;
99 
100     if (value == NULL) {
101         return http_header_delete(header, key);
102     }
103 
104     item = http_header_get_item(header, key);
105 
106     if (item) {
107         free(item->value);
108         item->value = strdup(value);
109         http_utils_trim_whitespace(&item->value);
110         return ESP_OK;
111     }
112     return http_header_new_item(header, key, value);
113 }
114 
http_header_set_from_string(http_header_handle_t header,const char * key_value_data)115 esp_err_t http_header_set_from_string(http_header_handle_t header, const char *key_value_data)
116 {
117     char *eq_ch;
118     char *p_str;
119 
120     p_str = strdup(key_value_data);
121     HTTP_MEM_CHECK(TAG, p_str, return ESP_ERR_NO_MEM);
122     eq_ch = strchr(p_str, ':');
123     if (eq_ch == NULL) {
124         free(p_str);
125         return ESP_ERR_INVALID_ARG;
126     }
127     *eq_ch = 0;
128 
129     http_header_set(header, p_str, eq_ch + 1);
130     free(p_str);
131     return ESP_OK;
132 }
133 
134 
http_header_delete(http_header_handle_t header,const char * key)135 esp_err_t http_header_delete(http_header_handle_t header, const char *key)
136 {
137     http_header_item_handle_t item = http_header_get_item(header, key);
138     if (item) {
139         STAILQ_REMOVE(header, item, http_header_item, next);
140         free(item->key);
141         free(item->value);
142         free(item);
143     } else {
144         return ESP_ERR_NOT_FOUND;
145     }
146     return ESP_OK;
147 }
148 
149 
http_header_set_format(http_header_handle_t header,const char * key,const char * format,...)150 int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...)
151 {
152     va_list argptr;
153     int len = 0;
154     char *buf = NULL;
155     va_start(argptr, format);
156     len = vasprintf(&buf, format, argptr);
157     va_end(argptr);
158     HTTP_MEM_CHECK(TAG, buf, return 0);
159     if (buf == NULL) {
160         return 0;
161     }
162     http_header_set(header, key, buf);
163     free(buf);
164     return len;
165 }
166 
http_header_generate_string(http_header_handle_t header,int index,char * buffer,int * buffer_len)167 int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len)
168 {
169     http_header_item_handle_t item;
170     int siz = 0;
171     int idx = 0;
172     int ret_idx = -1;
173     bool is_end = false;
174 
175     // iterate over the header entries to calculate buffer size and determine last item
176     STAILQ_FOREACH(item, header, next) {
177         if (item->value && idx >= index) {
178             siz += strlen(item->key);
179             siz += strlen(item->value);
180             siz += 4; //': ' and '\r\n'
181         }
182         idx ++;
183 
184         if (siz + 1 > *buffer_len - 2) {
185             // if this item would not fit to the buffer, return the index of the last fitting one
186             ret_idx = idx - 1;
187             ESP_LOGE(TAG, "Buffer length is small to fit all the headers");
188             break;
189         }
190     }
191 
192     if (siz == 0) {
193         return 0;
194     }
195     if (ret_idx < 0) {
196         // all items would fit, mark this as the end of http header string
197         ret_idx = idx;
198         is_end = true;
199     }
200 
201     // iterate again over the header entries to write only the fitting indeces
202     int str_len = 0;
203     idx = 0;
204     STAILQ_FOREACH(item, header, next) {
205         if (item->value && idx >= index && idx < ret_idx) {
206             str_len += snprintf(buffer + str_len, *buffer_len - str_len, "%s: %s\r\n", item->key, item->value);
207         }
208         idx ++;
209     }
210     if (is_end) {
211         // write the http header terminator if all header entries have been written in this function call
212         str_len += snprintf(buffer + str_len, *buffer_len - str_len, "\r\n");
213     }
214     *buffer_len = str_len;
215     return ret_idx;
216 }
217 
http_header_clean(http_header_handle_t header)218 esp_err_t http_header_clean(http_header_handle_t header)
219 {
220     http_header_item_handle_t item = STAILQ_FIRST(header), tmp;
221     while (item != NULL) {
222         tmp = STAILQ_NEXT(item, next);
223         free(item->key);
224         free(item->value);
225         free(item);
226         item = tmp;
227     }
228     STAILQ_INIT(header);
229     return ESP_OK;
230 }
231 
http_header_count(http_header_handle_t header)232 int http_header_count(http_header_handle_t header)
233 {
234     http_header_item_handle_t item;
235     int count = 0;
236     STAILQ_FOREACH(item, header, next) {
237         count ++;
238     }
239     return count;
240 }
241