1 /*
2 * Copyright (C) 2004-2008 Christos Tsantilas
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA.
18 */
19
20 #include "common.h"
21 #include "simple_api.h"
22 #include "debug.h"
23 #include <ctype.h>
24 #include <errno.h>
25
26
27 /*
28 int ci_resp_check_body(ci_request_t *req){
29 int i;
30 ci_encaps_entity_t **e=req->entities;
31 for(i = 0; e[i] != NULL; i++)
32 if(e[i]->type == ICAP_NULL_BODY)
33 return 0;
34 return 1;
35 }
36 */
37
38
ci_http_response_headers(ci_request_t * req)39 ci_headers_list_t * ci_http_response_headers(ci_request_t * req)
40 {
41 int i;
42 ci_encaps_entity_t **e_list;
43 e_list = req->entities;
44 for (i = 0; e_list[i] != NULL && i < 3; i++) { /*It is the first or second ellement */
45 if (e_list[i]->type == ICAP_RES_HDR)
46 return (ci_headers_list_t *) e_list[i]->entity;
47
48 }
49 return NULL;
50 }
51
ci_http_request_headers(ci_request_t * req)52 ci_headers_list_t *ci_http_request_headers(ci_request_t * req)
53 {
54 ci_encaps_entity_t **e_list;
55 e_list = req->entities;
56 if (e_list[0] != NULL && e_list[0]->type == ICAP_REQ_HDR) /*It is always the first ellement */
57 return (ci_headers_list_t *) e_list[0]->entity;
58
59 /*We did not found the request headers but maybe there are in req->trash objects
60 Trying to retrieve hoping that it were not desstroyed yet
61 */
62 if (req->trash_entities[ICAP_REQ_HDR] &&
63 req->trash_entities[ICAP_REQ_HDR]->entity &&
64 ((ci_headers_list_t *) req->trash_entities[ICAP_REQ_HDR]->entity)->used)
65 return (ci_headers_list_t *) req->trash_entities[ICAP_REQ_HDR]->entity;
66
67 return NULL;
68 }
69
ci_http_response_reset_headers(ci_request_t * req)70 int ci_http_response_reset_headers(ci_request_t * req)
71 {
72 ci_headers_list_t *heads;
73 if (!(heads = ci_http_response_headers(req)))
74 return 0;
75 ci_headers_reset(heads);
76 return 1;
77 }
78
ci_http_request_reset_headers(ci_request_t * req)79 int ci_http_request_reset_headers(ci_request_t * req)
80 {
81 ci_headers_list_t *heads;
82 if (!(heads = ci_http_request_headers(req)))
83 return 0;
84 ci_headers_reset(heads);
85 return 1;
86 }
87
88 /*
89 This function will be used when we want to responce with an error message
90 to an reqmod request or respmod request.
91 ICAP rfc says that we must responce as:
92 REQMOD response encapsulated_list: {[reshdr] resbody}
93 RESPMOD response encapsulated_list: [reshdr] resbody
94
95 */
ci_http_response_create(ci_request_t * req,int has_reshdr,int has_body)96 int ci_http_response_create(ci_request_t * req, int has_reshdr, int has_body)
97 {
98 int i = 0;
99
100 for (i = 0; i < 4; i++) {
101 if (req->entities[i]) {
102 ci_request_release_entity(req, i);
103 }
104 }
105 i = 0;
106 if (has_reshdr)
107 req->entities[i++] = ci_request_alloc_entity(req, ICAP_RES_HDR, 0);
108 if (has_body)
109 req->entities[i] = ci_request_alloc_entity(req, ICAP_RES_BODY, 0);
110 else
111 req->entities[i] = ci_request_alloc_entity(req, ICAP_NULL_BODY, 0);
112
113 return 1;
114 }
115
116
ci_http_request_create(ci_request_t * req,int has_body)117 int ci_http_request_create(ci_request_t * req, int has_body)
118 {
119 int i = 0;
120
121 for (i = 0; i < 4; i++) {
122 if (req->entities[i]) {
123 ci_request_release_entity(req, i);
124 }
125 }
126 i = 0;
127 req->entities[i++] = ci_request_alloc_entity(req, ICAP_REQ_HDR, 0);
128 if (has_body)
129 req->entities[i] = ci_request_alloc_entity(req, ICAP_REQ_BODY, 0);
130 else
131 req->entities[i] = ci_request_alloc_entity(req, ICAP_NULL_BODY, 0);
132
133 return 1;
134 }
135
136
ci_http_response_add_header(ci_request_t * req,const char * header)137 const char *ci_http_response_add_header(ci_request_t * req, const char *header)
138 {
139 ci_headers_list_t *heads;
140 if (req->packed) /*Not in edit mode*/
141 return NULL;
142 if (!(heads = ci_http_response_headers(req)))
143 return NULL;
144 return ci_headers_add(heads, header);
145 }
146
147
ci_http_request_add_header(ci_request_t * req,const char * header)148 const char *ci_http_request_add_header(ci_request_t * req, const char *header)
149 {
150 ci_headers_list_t *heads;
151 if (req->packed) /*Not in edit mode*/
152 return NULL;
153 if (!(heads = ci_http_request_headers(req)))
154 return NULL;
155 return ci_headers_add(heads, header);
156 }
157
ci_http_response_remove_header(ci_request_t * req,const char * header)158 int ci_http_response_remove_header(ci_request_t * req, const char *header)
159 {
160 ci_headers_list_t *heads;
161 if (req->packed) /*Not in edit mode*/
162 return 0;
163 if (!(heads = ci_http_response_headers(req)))
164 return 0;
165 return ci_headers_remove(heads, header);
166 }
167
168
ci_http_request_remove_header(ci_request_t * req,const char * header)169 int ci_http_request_remove_header(ci_request_t * req, const char *header)
170 {
171 ci_headers_list_t *heads;
172 if (req->packed) /*Not in edit mode*/
173 return 0;
174 if (!(heads = ci_http_request_headers(req)))
175 return 0;
176 return ci_headers_remove(heads, header);
177 }
178
179
ci_http_response_get_header(ci_request_t * req,const char * head_name)180 const char *ci_http_response_get_header(ci_request_t * req, const char *head_name)
181 {
182 ci_headers_list_t *heads;
183 const char *val;
184 if (!(heads = ci_http_response_headers(req)))
185 return NULL;
186 if (!(val = ci_headers_value(heads, head_name)))
187 return NULL;
188 return val;
189 }
190
ci_http_request_get_header(ci_request_t * req,const char * head_name)191 const char *ci_http_request_get_header(ci_request_t * req, const char *head_name)
192 {
193 ci_headers_list_t *heads;
194 const char *val;
195 if (!(heads = ci_http_request_headers(req)))
196 return NULL;
197 if (!(val = ci_headers_value(heads, head_name)))
198 return NULL;
199 return val;
200 }
201
202
ci_http_content_length(ci_request_t * req)203 ci_off_t ci_http_content_length(ci_request_t * req)
204 {
205 ci_headers_list_t *heads;
206 const char *val;
207 ci_off_t res = 0;
208 char *e;
209 if (!(heads = ci_http_response_headers(req))) {
210 /*Then maybe is a reqmod reauest, try to get request headers */
211 if (!(heads = ci_http_request_headers(req)))
212 return 0;
213 }
214 if (!(val = ci_headers_value(heads, "Content-Length")))
215 return -1;
216
217 errno = 0;
218 res = ci_strto_off_t(val, &e, 10);
219 if (errno == ERANGE && (res == CI_STRTO_OFF_T_MAX || res == CI_STRTO_OFF_T_MIN)) {
220 ci_debug_printf(4, "Content-Length: overflow\n");
221 return -2;
222 }
223 if (val == e) {
224 ci_debug_printf(4, "Content-Length: not valid value: '%s' \n", val);
225 return -2;
226 }
227 return res;
228 }
229
ci_http_request(ci_request_t * req)230 const char *ci_http_request(ci_request_t * req)
231 {
232 ci_headers_list_t *heads;
233 if (!(heads = ci_http_request_headers(req)))
234 return NULL;
235
236 if (!heads->used)
237 return NULL;
238
239 return heads->headers[0];
240 }
241
ci_icap_add_xheader(ci_request_t * req,const char * header)242 const char *ci_icap_add_xheader(ci_request_t * req, const char *header)
243 {
244 return ci_headers_add(req->xheaders, header);
245 }
246
ci_icap_append_xheaders(ci_request_t * req,ci_headers_list_t * headers)247 int ci_icap_append_xheaders(ci_request_t *req,ci_headers_list_t *headers)
248 {
249 return ci_headers_addheaders(req->xheaders, headers);
250 }
251
252 #define header_end(e) (e == '\0' || e == '\n' || e == '\r')
ci_http_request_url(ci_request_t * req,char * buf,int buf_size)253 int ci_http_request_url(ci_request_t * req, char *buf, int buf_size)
254 {
255 ci_headers_list_t *heads;
256 const char *str, *host;
257 int i, bytes;
258 /*The request must have the form:
259 GET url HTTP/X.X
260 */
261 if (!(heads = ci_http_request_headers(req)))
262 return 0;
263
264 if (!heads->used)
265 return 0;
266
267 str = heads->headers[0];
268
269 if ((str = strchr(str, ' ')) == NULL) { /*Ignore method i*/
270 return 0;
271 }
272 while (*str == ' ') /*ignore spaces*/
273 str++;
274
275 bytes = 0;
276 if (*str == '/' && (host = ci_headers_value(heads,"Host"))) {
277 /*Looks like a transparent proxy, we do not know the protocol lets try
278 to preserve the major part of the url....
279 */
280 for (i = 0; (i < buf_size-1) && !header_end(host[i]) && !isspace(host[i]); i++) {
281 buf[i] = host[i];
282 }
283 buf += i;
284 buf_size -= i;
285 bytes = i;
286 }
287
288 /*copy the url...*/
289 for (i = 0; (i < buf_size-1) && !header_end(str[i]) && !isspace(str[i]) && str[i] != '?'; i++) {
290 buf[i] = str[i];
291 }
292 buf[i] = '\0';
293 bytes += i;
294 return bytes;
295 }
296
ci_http_client_ip(ci_request_t * req)297 const ci_ip_t * ci_http_client_ip(ci_request_t * req)
298 {
299 const char *ip;
300 if (!req)
301 return NULL;
302
303 if (req->xclient_ip.family == -1) /*Already check and failed to read it*/
304 return NULL;
305
306 if (req->xclient_ip.family != 0) /*Already check, return cached*/
307 return &req->xclient_ip;
308
309 if (!(ip = ci_headers_value(req->request_header, "X-Client-IP")))
310 return NULL;
311
312 #ifdef HAVE_IPV6
313 if (strchr(ip, ':')) {
314 if (ci_inet_aton(AF_INET6, ip, &(req->xclient_ip.address))) {
315 req->xclient_ip.family = AF_INET6;
316 ci_ipv6_inaddr_hostnetmask(req->xclient_ip.netmask);
317 } else
318 req->xclient_ip.family = -1;
319 } else
320 #endif
321 {
322 if (ci_inet_aton(AF_INET, ip, &(req->xclient_ip.address))) {
323 req->xclient_ip.family = AF_INET;
324 ci_ipv4_inaddr_hostnetmask(req->xclient_ip.netmask);
325 } else
326 req->xclient_ip.family = -1;
327 }
328
329 if (req->xclient_ip.family == -1) /*Failed to read correctly*/
330 return NULL;
331
332 return &req->xclient_ip;
333 }
334
ci_http_response_content_encoding(ci_request_t * req)335 CI_DECLARE_FUNC(int) ci_http_response_content_encoding(ci_request_t *req)
336 {
337 ci_headers_list_t *headers = ci_http_response_headers(req);
338 if (headers) {
339 const char *content_encoding = ci_headers_value(headers, "Content-Encoding");
340 if (content_encoding)
341 return ci_encoding_method(content_encoding);
342 return CI_ENCODE_NONE;
343 }
344 return CI_ENCODE_UNKNOWN;
345 }
346