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