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 "c-icap.h"
22 #include <errno.h>
23 #include <ctype.h>
24 #include <time.h>
25 #include <assert.h>
26 #include "debug.h"
27 #include "request.h"
28 #include "simple_api.h"
29 #include "util.h"
30 #include "body.h"
31 
32 const char *CI_DEFAULT_USER_AGENT = "C-ICAP-Client-Library/" VERSION;
33 char *CI_USER_AGENT = NULL;
34 
_os_malloc(int size)35 static void * _os_malloc(int size)
36 {
37     return malloc(size);
38 }
39 
_os_free(void * ptr)40 static void _os_free(void *ptr)
41 {
42     free(ptr);
43 }
44 
45 void *(*__intl_malloc)(int) = _os_malloc;
46 void (*__intl_free)(void *) = _os_free;
47 
48 
49 /* struct buf functions*/
ci_buf_init(struct ci_buf * buf)50 void ci_buf_init(struct ci_buf *buf)
51 {
52     buf->buf = NULL;
53     buf->size = 0;
54     buf->used = 0;
55 }
56 
ci_buf_reset(struct ci_buf * buf)57 void ci_buf_reset(struct ci_buf *buf)
58 {
59     buf->used = 0;
60 }
ci_buf_mem_alloc(struct ci_buf * buf,int size)61 int ci_buf_mem_alloc(struct ci_buf *buf, int size)
62 {
63     if (!(buf->buf = __intl_malloc(size * sizeof(char))))
64         return 0;
65     buf->size = size;
66     buf->used = 0;
67     return size;
68 }
69 
ci_buf_mem_free(struct ci_buf * buf)70 void ci_buf_mem_free(struct ci_buf *buf)
71 {
72     __intl_free(buf->buf);
73     buf->buf = NULL;
74     buf->size = 0;
75     buf->used = 0;
76 }
77 
78 
ci_buf_write(struct ci_buf * buf,char * data,int len)79 int ci_buf_write(struct ci_buf *buf, char *data, int len)
80 {
81     if (len > (buf->size - buf->used))
82         return -1;
83     memcpy(buf->buf + buf->used, data, len);
84     buf->used += len;
85     return len;
86 }
87 
ci_buf_reset_size(struct ci_buf * buf,int req_size)88 int ci_buf_reset_size(struct ci_buf *buf, int req_size)
89 {
90     if (buf->size > req_size)
91         return req_size;
92     if (buf->buf)
93         __intl_free(buf->buf);
94     return ci_buf_mem_alloc(buf, req_size);
95 }
96 
ci_request_t_pack(ci_request_t * req,int is_request)97 void ci_request_t_pack(ci_request_t * req, int is_request)
98 {
99     ci_encaps_entity_t **elist, *e;
100     char buf[512];
101     int i, added;
102 
103     req->packed = 1;
104 
105     if (is_request && req->preview >= 0) {
106         sprintf(buf, "Preview: %d", req->preview);
107         ci_headers_add(req->request_header, buf);
108     }
109 
110     elist = req->entities;
111 
112     if (elist[0] != NULL) {
113         elist[0]->start = 0;
114 
115         if (elist[1] != NULL) {
116             elist[1]->start = sizeofencaps(elist[0]);
117 
118             if (elist[2] != NULL) {
119                 elist[2]->start = sizeofencaps(elist[1]) + elist[1]->start;
120             }
121         }
122     }
123 
124 
125     if (elist[0] == NULL) {
126         sprintf(buf, "Encapsulated: null-body=0");
127     } else {
128         added = snprintf(buf, sizeof(buf), "Encapsulated: ");
129         for (i = 0; elist[i] != NULL && i < 3 && added < sizeof(buf); ++i)
130             added += snprintf(buf + added, sizeof(buf) - added,
131                               "%s%s=%d",
132                               (i != 0 ? ", " : ""),
133                               ci_encaps_entity_string(elist[i]->type),
134                               elist[i]->start);
135     }
136     if (is_request)
137         ci_headers_add(req->request_header, buf);
138     else
139         ci_headers_add(req->response_header, buf);
140 
141     while ((e = *elist++) != NULL) {
142         if (e->type == ICAP_REQ_HDR || e->type == ICAP_RES_HDR)
143             ci_headers_pack((ci_headers_list_t *) e->entity);
144     }
145     /*e_list is not usable now !!!!!!! */
146     if (is_request)
147         ci_headers_pack(req->request_header);
148     else
149         ci_headers_pack(req->response_header);
150 }
151 
ci_request_pack(ci_request_t * req)152 void ci_request_pack(ci_request_t *req)
153 {
154     ci_request_t_pack(req, 1);
155 }
156 
ci_response_pack(ci_request_t * req)157 void ci_response_pack(ci_request_t *req)
158 {
159     ci_request_t_pack(req, 0);
160 }
161 
block_write_nonblock(ci_connection_t * conn,char ** block,int * block_size,uint64_t * counter)162 static int block_write_nonblock(ci_connection_t *conn, char **block, int *block_size, uint64_t *counter)
163 {
164     if (!block || !(*block) || !(block_size))
165         return CI_ERROR;
166 
167     if (!*block_size)
168         return CI_OK;
169 
170     int ret = ci_connection_write_nonblock(conn, *block, *block_size);
171     if (ret < 0)
172         return CI_ERROR;
173 
174     *counter += ret;
175     *block_size -= ret;
176     *block += ret;
177     return *block_size > 0 ? CI_NEEDS_MORE : CI_OK;
178 }
179 
180 /*
181 Valid forms of encapsulated entities
182 
183    REQMOD  request  encapsulated_list: [reqhdr] reqbody
184    REQMOD  response encapsulated_list: {[reqhdr] reqbody} |
185                                        {[reshdr] resbody}
186    RESPMOD request  encapsulated_list: [reqhdr] [reshdr] resbody
187    RESPMOD response encapsulated_list: [reshdr] resbody
188    OPTIONS request  encapsulated_list: [optbody]
189    OPTIONS response encapsulated_list: optbody
190 
191 TODO:
192    The following function must chech request and if the encapsulated entity is valid
193    must put it to the right position in req->entities .........
194 */
195 
196 //alloc_an_entity
ci_request_alloc_entity(ci_request_t * req,int type,int val)197 ci_encaps_entity_t *ci_request_alloc_entity(ci_request_t * req, int type, int val)
198 {
199     ci_encaps_entity_t *e = NULL;
200 
201     if (type > ICAP_OPT_BODY || type < 0) {    //
202         return NULL;
203     }
204 
205     if (req->trash_entities[type]) {
206         e = req->trash_entities[type];
207         req->trash_entities[type] = NULL;
208         e->type = type;
209         e->start = val;
210         if (e->type == ICAP_REQ_HDR
211                 || e->type == ICAP_RES_HDR) {
212             if (e->entity)
213                 ci_headers_reset((ci_headers_list_t *)e->entity);
214         }
215         ci_debug_printf(8, "Get entity from trash....\n");
216         return e;
217     }
218 
219     //Else there is no available entity to trash_entities so make a new....
220     ci_debug_printf(8, "Allocate a new entity of type %d\n", type);
221     return mk_encaps_entity(type, val);
222 }
223 
224 
ci_request_release_entity(ci_request_t * req,int pos)225 int ci_request_release_entity(ci_request_t * req, int pos)
226 {
227     int type = 0;
228     if (!req->entities[pos])
229         return 0;
230 
231     type = req->entities[pos]->type;
232     if (type > ICAP_OPT_BODY || type < 0) {    //?????????
233         destroy_encaps_entity(req->entities[pos]);
234         req->entities[pos] = NULL;
235         return 0;
236     }
237 
238     if (req->trash_entities[type] != NULL) {
239         ci_debug_printf(3,
240                         "ERROR!!!!! There is an entity of type %d to trash..... ",
241                         type);
242         destroy_encaps_entity(req->trash_entities[type]);
243     }
244     req->trash_entities[type] = req->entities[pos];
245     req->entities[pos] = NULL;
246     return 1;
247 }
248 
249 
ci_request_alloc(ci_connection_t * connection)250 ci_request_t *ci_request_alloc(ci_connection_t * connection)
251 {
252     ci_request_t *req;
253     int i;
254     req = (ci_request_t *) __intl_malloc(sizeof(ci_request_t));
255     if (!req)
256         return NULL;
257 
258     req->connection = connection;
259     req->packed = 0;
260     req->user[0] = '\0';
261 
262     req->access_type = 0;
263 
264     req->service[0] = '\0';
265     req->req_server[0] = '\0';
266     req->current_service_mod = NULL;
267     req->service_data = NULL;
268     req->args[0] = '\0';
269     req->type = -1;
270     req->preview = -1;
271     ci_buf_init(&(req->preview_data));
272 
273     req->keepalive = 1;        /*Keep alive connection is the default behaviour for icap protocol. */
274     req->allow204 = 0;
275     req->allow206 = 0;
276     req->hasbody = 0;
277     req->responce_hasbody = 0;
278     req->eof_received = 0;
279     req->eof_sent = 0;
280 
281     req->request_header = ci_headers_create();
282     req->response_header = ci_headers_create();
283     req->xheaders = ci_headers_create();
284     req->status = SEND_NOTHING;
285     req->return_code = -1;
286 
287     req->pstrblock_read = NULL;
288     req->pstrblock_read_len = 0;
289     req->current_chunk_len = 0;
290     req->chunk_bytes_read = 0;
291     req->write_to_module_pending = 0;
292 
293     req->pstrblock_responce = NULL;
294     req->remain_send_block_bytes = 0;
295     req->data_locked = 1;
296     req->i206_use_original_body = -1;
297 
298     req->echo_body = NULL;
299 
300     req->preview_data_type = -1;
301     req->auth_required = 0;
302 
303 
304     req->log_str = NULL;
305     req->attributes = NULL;
306     memset(&(req->xclient_ip), 0, sizeof(ci_ip_t));
307 
308     req->bytes_in = 0;
309     req->bytes_out = 0;
310     req->request_bytes_in = 0;
311     req->http_bytes_in = 0;
312     req->http_bytes_out = 0;
313     req->body_bytes_in = 0;
314     req->body_bytes_out = 0;
315 
316     for (i = 0; i < 5; i++)    //
317         req->entities[i] = NULL;
318     for (i = 0; i < 7; i++)    //
319         req->trash_entities[i] = NULL;
320 
321     return req;
322 
323 }
324 
325 /*reset_request simply reset request to use it with tunneled requests
326   The req->access_type must not be reset!!!!!
327 
328   Also buffers where the pstrblock_read points must not free or reset.
329 */
ci_request_reset(ci_request_t * req)330 void ci_request_reset(ci_request_t * req)
331 {
332     int i;
333     req->packed = 0;
334     req->user[0] = '\0';
335     req->service[0] = '\0';
336     req->current_service_mod = NULL;
337     req->service_data = NULL;
338     req->args[0] = '\0';
339     req->type = -1;
340     req->preview = -1;
341     ci_buf_reset(&(req->preview_data));
342 
343     req->keepalive = 1;        /*Keep alive connection is the default behaviour for icap protocol. */
344     req->allow204 = 0;
345     req->allow206 = 0;
346     req->hasbody = 0;
347     req->responce_hasbody = 0;
348     ci_headers_reset(req->request_header);
349     ci_headers_reset(req->response_header);
350     ci_headers_reset(req->xheaders);
351     req->eof_received = 0;
352     req->eof_sent = 0;
353 
354     req->status = SEND_NOTHING;
355     req->return_code = -1;
356 
357     req->pstrblock_read = NULL;
358     req->pstrblock_read_len = 0;
359     req->current_chunk_len = 0;
360     req->chunk_bytes_read = 0;
361     req->pstrblock_responce = NULL;
362     req->remain_send_block_bytes = 0;
363     req->write_to_module_pending = 0;
364     req->data_locked = 1;
365     req->i206_use_original_body = -1;
366 
367     if (req->echo_body) {
368         ci_ring_buf_destroy(req->echo_body);
369         req->echo_body = NULL;
370     }
371 
372     req->preview_data_type = -1;
373     req->auth_required = 0;
374 
375     if (req->log_str)
376         __intl_free(req->log_str);
377     req->log_str = NULL;
378 
379     if (req->attributes)
380         ci_array_destroy(req->attributes);
381     req->attributes = NULL;
382     memset(&(req->xclient_ip), 0, sizeof(ci_ip_t));
383 
384     req->bytes_in = 0;
385     req->bytes_out = 0;
386     req->request_bytes_in = 0;
387     req->http_bytes_in = 0;
388     req->http_bytes_out = 0;
389     req->body_bytes_in = 0;
390     req->body_bytes_out = 0;
391 
392     for (i = 0; req->entities[i] != NULL; i++) {
393         ci_request_release_entity(req, i);
394     }
395 
396     /*Reset the encapsulated response or request headers*/
397     if (req->trash_entities[ICAP_REQ_HDR] &&
398             req->trash_entities[ICAP_REQ_HDR]->entity)
399         ci_headers_reset((ci_headers_list_t *)req->trash_entities[ICAP_REQ_HDR]->entity);
400     if (req->trash_entities[ICAP_RES_HDR] &&
401             req->trash_entities[ICAP_RES_HDR]->entity)
402         ci_headers_reset((ci_headers_list_t *)req->trash_entities[ICAP_RES_HDR]->entity);
403 }
404 
ci_request_destroy(ci_request_t * req)405 void ci_request_destroy(ci_request_t * req)
406 {
407     int i;
408     if (req->connection)
409         __intl_free(req->connection);
410 
411     ci_buf_mem_free(&(req->preview_data));
412     ci_headers_destroy(req->request_header);
413     ci_headers_destroy(req->response_header);
414     ci_headers_destroy(req->xheaders);
415     for (i = 0; req->entities[i] != NULL; i++)
416         destroy_encaps_entity(req->entities[i]);
417 
418     for (i = 0; i < 7; i++) {
419         if (req->trash_entities[i])
420             destroy_encaps_entity(req->trash_entities[i]);
421     }
422 
423     if (req->echo_body) {
424         ci_ring_buf_destroy(req->echo_body);
425         req->echo_body = NULL;
426     }
427 
428     if (req->log_str)
429         __intl_free(req->log_str);
430 
431     if (req->attributes)
432         ci_array_destroy(req->attributes);
433 
434     __intl_free(req);
435 }
436 
ci_request_set_log_str(ci_request_t * req,char * logstr)437 char *ci_request_set_log_str(ci_request_t *req, char *logstr)
438 {
439     int size;
440     if (req->log_str)
441         __intl_free(req->log_str);
442 
443     size = strlen(logstr) + 1;
444     req->log_str = __intl_malloc(size*sizeof(char));
445     if (!req->log_str)
446         return NULL;
447     strcpy(req->log_str, logstr);
448     return req->log_str;
449 }
450 
ci_request_set_str_attribute(ci_request_t * req,const char * name,const char * value)451 int  ci_request_set_str_attribute(ci_request_t *req, const char *name, const char *value)
452 {
453     if (req->attributes == NULL) {
454         req->attributes = ci_array_new(4096);
455         if (!req->attributes) {
456             ci_debug_printf(1, "Error allocating request attributes array!\n");
457             return 0;
458         }
459     }
460 
461     if (!ci_str_array_add(req->attributes, name, value)) {
462         ci_debug_printf(1, "Not enough space to add attribute %s:%s for service %s\n", name, value, req->service);
463         return 0;
464     }
465     return 1;
466 }
467 
ci_request_206_origin_body(ci_request_t * req,uint64_t offset)468 int ci_request_206_origin_body(ci_request_t *req, uint64_t offset)
469 {
470     if (!req)
471         return 0;
472 
473     if (!req->allow206) {
474         ci_debug_printf(1, "Request does not support allow206 responses! Can not set use-original-body extension\n");
475         return 0;
476     }
477 
478 #if 0
479     if (req->body_bytes_in < offset) {
480         ci_debug_printf(1, "Can not set use-original-body extension to offset longer than the known body size");
481         return 0;
482     }
483 #endif
484     req->i206_use_original_body = offset;
485     return 1;
486 }
487 
process_encapsulated(ci_request_t * req,const char * buf)488 int process_encapsulated(ci_request_t * req, const char *buf)
489 {
490     const char *start;
491     const char *pos;
492     char *end;
493     int type = 0, num = 0, val = 0;
494     int hasbody = 1;           /*Assume that we have a resbody or reqbody or optbody */
495     start = buf + 13; /*strlen("Encapsulated:")*/
496     pos = start;
497     end = (char *)start;
498     for (; isspace(*pos) && *pos != '\0'; ++pos);
499     while (*pos != '\0') {
500         type = get_encaps_type(pos, &val, &end);
501         if (type < 0) /*parse error - return "400 bad request"*/
502             return EC_400;
503         if (num > 5)          /*In practice there is an error here .... */
504             break;
505         if (type == ICAP_NULL_BODY)
506             hasbody = 0;     /*We have not a body */
507         req->entities[num++] = ci_request_alloc_entity(req, type, val);
508 
509         assert(start != end);
510         pos = end;            /* points after the value of entity.... */
511         for (; (isspace(*pos) || *pos == ',') && *pos != '\0'; ++pos);
512     }
513     req->hasbody = hasbody;
514     return EC_100;
515 }
516 
517 
518 enum chunk_status { READ_CHUNK_DEF = 1, READ_CHUNK_DATA };
519 
520 
521 /*
522   maybe the wdata must moved to the ci_request_t and write_to_module_pending must replace wdata_len
523 */
parse_chunk_data(ci_request_t * req,char ** wdata)524 int parse_chunk_data(ci_request_t * req, char **wdata)
525 {
526     char *end;
527     const char *eofChunk;
528     int chunkLen, remains, tmp;
529     int read_status = 0;
530 
531     *wdata = NULL;
532     if (req->write_to_module_pending) {
533         /*We must not here if the chunk buffer did not flashed */
534         return CI_ERROR;
535     }
536 
537     while (1) {
538         if (req->current_chunk_len == req->chunk_bytes_read)
539             read_status = READ_CHUNK_DEF;
540         else
541             read_status = READ_CHUNK_DATA;
542 
543         if (read_status == READ_CHUNK_DEF) {
544             if ((eofChunk = strnstr(req->pstrblock_read, "\r\n", req->pstrblock_read_len)) == NULL) {
545                 /*Check for wrong protocol data, or possible parse error*/
546                 if (req->pstrblock_read_len >= BUFSIZE)
547                     return CI_ERROR; /* To big chunk definition?*/
548                 return CI_NEEDS_MORE;
549             }
550             eofChunk += 2;
551             chunkLen = eofChunk - req->pstrblock_read;
552             // Count parse data
553             req->request_bytes_in += (eofChunk - req->pstrblock_read);
554 
555             errno = 0;
556             tmp = strtol(req->pstrblock_read, &end, 16);
557             /*here must check for erron */
558             if (tmp == 0 && req->pstrblock_read == end) {    /*Oh .... an error ... */
559                 ci_debug_printf(5, "Parse error:count=%d,start=%c\n",
560                                 tmp, req->pstrblock_read[0]);
561                 return CI_ERROR;
562             }
563             req->current_chunk_len = tmp;
564             req->chunk_bytes_read = 0;
565 
566             while (*end == ' ' || *end == '\t') ++end; /*ignore spaces*/
567             if (req->current_chunk_len == 0) {
568                 remains = req->pstrblock_read_len - chunkLen;
569                 if (remains < 2) /*missing the \r\n of the 0[; ...]\r\n\r\n of the eof chunk*/
570                     return CI_NEEDS_MORE;
571 
572                 if (*eofChunk != '\r' && *(eofChunk + 1) != '\n')
573                     return CI_ERROR; /* Not an \r\n\r\n eof chunk definition. Parse Error*/
574 
575                 eofChunk += 2;
576                 chunkLen += 2;
577                 req->request_bytes_in += 2; /*count 2 extra chars on parsed data*/
578 
579                 if (*end == ';') {
580                     end++;
581                     while (*end == ' ' || *end == '\t') ++end; /*ignore spaces*/
582                     remains = req->pstrblock_read_len - (end - req->pstrblock_read);
583                     if (remains >= 18 && strncmp(end, "use-original-body=", 18) == 0) {
584                         req->i206_use_original_body = strtol(end + 18, &end, 10);
585                     } else if (remains >= 4 && strncmp(end, "ieof", 4) != 0)
586                         return CI_ERROR;
587                     // ignore any char after ';'
588                     while (*end != '\r') ++end;
589                     req->eof_received = 1;
590                 }
591 
592             } else {
593                 read_status = READ_CHUNK_DATA;
594                 /*include the \r\n end of chunk data */
595                 req->current_chunk_len += 2;
596             }
597 
598             /*The end pointing after the number and extensions. Should point to \r\n*/
599             if (*end != '\r' || *(end + 1) != '\n') {
600                 /*Extra chars in chunk definition!*/
601                 return CI_ERROR;
602             }
603 
604             req->pstrblock_read_len -= chunkLen;
605             req->pstrblock_read += chunkLen;
606         }
607         if (req->current_chunk_len == 0) /*zero chunk received, stop for now*/
608             return CI_EOF;
609 
610         /*if we have data for service leaving this function now */
611         if (req->write_to_module_pending)
612             return CI_OK;
613         if (read_status == READ_CHUNK_DATA) {
614             if (req->pstrblock_read_len <= 0) {
615                 return CI_NEEDS_MORE;
616             }
617             *wdata = req->pstrblock_read;
618             remains = req->current_chunk_len - req->chunk_bytes_read;
619             if (remains <= req->pstrblock_read_len) {        /*we have all the chunks data */
620                 if (remains > 2) {
621                     req->write_to_module_pending = remains - 2;
622                     req->http_bytes_in += req->write_to_module_pending;
623                     req->body_bytes_in += req->write_to_module_pending;
624                 } else      /*we are in all or part of the \r\n end of chunk data */
625                     req->write_to_module_pending = 0;
626                 req->chunk_bytes_read += remains;
627                 req->pstrblock_read += remains;
628                 req->pstrblock_read_len -= remains;
629                 req->request_bytes_in += remains; //append parsed data
630             } else {
631                 tmp = remains - req->pstrblock_read_len;
632                 if (tmp < 2) {
633                     req->write_to_module_pending =
634                         req->pstrblock_read_len - tmp;
635                 } else {
636                     req->write_to_module_pending = req->pstrblock_read_len;
637                 }
638                 req->http_bytes_in += req->write_to_module_pending;
639                 req->body_bytes_in += req->write_to_module_pending;
640                 req->request_bytes_in += req->pstrblock_read_len; //append parsed data
641 
642                 req->chunk_bytes_read += req->pstrblock_read_len;
643                 req->pstrblock_read += req->pstrblock_read_len;
644                 req->pstrblock_read_len -= req->pstrblock_read_len;
645             }
646         }
647         if (req->pstrblock_read_len == 0)
648             return CI_NEEDS_MORE;
649     }
650 
651     return CI_OK;
652 }
653 
net_data_read(ci_request_t * req)654 int net_data_read(ci_request_t * req)
655 {
656     int bytes;
657 
658     if (req->pstrblock_read != req->rbuf) {
659         /*... put the current data to the begining of buf .... */
660         if (req->pstrblock_read_len)
661             memmove(req->rbuf, req->pstrblock_read, req->pstrblock_read_len);
662         req->pstrblock_read = req->rbuf;
663     }
664 
665     bytes = BUFSIZE - req->pstrblock_read_len;
666     if (bytes <= 0) {
667         ci_debug_printf(5,
668                         "Not enough space to read data! Is this a bug (%d %d)?????\n",
669                         req->pstrblock_read_len, BUFSIZE);
670         return CI_ERROR;
671     }
672 
673     if ((bytes = ci_connection_read_nonblock(req->connection, req->rbuf + req->pstrblock_read_len, bytes)) < 0) {    /*... read some data... */
674         ci_debug_printf(5, "Error reading data (read return=%d, errno=%d) \n", bytes, errno);
675         return CI_ERROR;
676     }
677     req->pstrblock_read_len += bytes;  /* ... (size of data is readed plus old)... */
678     req->bytes_in += bytes;
679     return CI_OK;
680 }
681 
682 
683 
684 
685 /*************************************************************************/
686 /* ICAP client functions                                                 */
687 
688 
ci_client_request(ci_connection_t * conn,const char * server,const char * service)689 ci_request_t *ci_client_request(ci_connection_t * conn, const char *server,
690                                 const char *service)
691 {
692     ci_request_t *req;
693     req = ci_request_alloc(conn);
694     if (!req) {
695         ci_debug_printf(1, "Error allocation ci_request_t object(ci_client_request())\n");
696         return NULL;
697     }
698     strncpy(req->req_server, server, CI_MAXHOSTNAMELEN);
699     req->req_server[CI_MAXHOSTNAMELEN] = '\0';
700     strncpy(req->service, service, MAX_SERVICE_NAME);
701     req->service[MAX_SERVICE_NAME] = '\0';
702     return req;
703 }
704 
705 
ci_client_request_reuse(ci_request_t * req)706 void ci_client_request_reuse(ci_request_t * req)
707 {
708     int i;
709 
710     req->packed = 0;
711     req->args[0] = '\0';
712     req->type = -1;
713     ci_buf_reset(&(req->preview_data));
714 
715     req->hasbody = 0;
716     req->responce_hasbody = 0;
717     ci_headers_reset(req->request_header);
718     ci_headers_reset(req->response_header);
719     ci_headers_reset(req->xheaders);
720     req->eof_received = 0;
721     req->eof_sent = 0;
722     req->status = 0;
723 
724     req->pstrblock_read = NULL;
725     req->pstrblock_read_len = 0;
726     req->current_chunk_len = 0;
727     req->chunk_bytes_read = 0;
728     req->pstrblock_responce = NULL;
729     req->remain_send_block_bytes = 0;
730     req->write_to_module_pending = 0;
731     req->data_locked = 1;
732 
733     req->allow204 = 0;
734     req->allow206 = 0;
735     req->i206_use_original_body = -1;
736 
737     req->bytes_in = 0;
738     req->bytes_out = 0;
739     req->http_bytes_in = 0;
740     req->http_bytes_out = 0;
741     req->body_bytes_in = 0;
742     req->body_bytes_out = 0;
743 
744     for (i = 0; req->entities[i] != NULL; i++) {
745         ci_request_release_entity(req, i);
746     }
747 
748 }
749 
750 
751 
client_create_request(ci_request_t * req,char * servername,char * service,int reqtype)752 static int client_create_request(ci_request_t * req, char *servername, char *service,
753                                  int reqtype)
754 {
755     char buf[256];
756 
757     if (reqtype != ICAP_OPTIONS && reqtype != ICAP_REQMOD
758             && reqtype != ICAP_RESPMOD)
759         return CI_ERROR;
760 
761     req->type = reqtype;
762     snprintf(buf, 255, "%s icap://%s/%s ICAP/1.0",
763              ci_method_string(reqtype), servername, service);
764     buf[255] = '\0';
765     ci_headers_add(req->request_header, buf);
766     snprintf(buf, 255, "Host: %s", servername);
767     buf[255] = '\0';
768     ci_headers_add(req->request_header, buf);
769 
770     const char* useragent = NULL;
771     if (CI_USER_AGENT) {
772         if (CI_USER_AGENT[0])
773             useragent = CI_USER_AGENT;
774     } else {
775         useragent = CI_DEFAULT_USER_AGENT;
776     }
777 
778     if (useragent) {
779         snprintf(buf, 255, "User-Agent: %s", useragent);
780         ci_headers_add(req->request_header, buf);
781     }
782 
783     if (ci_allow204(req) && ci_allow206(req))
784         ci_headers_add(req->request_header, "Allow: 204, 206");
785     else if (ci_allow204(req))
786         ci_headers_add(req->request_header, "Allow: 204");
787     if (ci_allow206(req))
788         ci_headers_add(req->request_header, "Allow: 206");
789 
790     if (!ci_headers_is_empty(req->xheaders)) {
791         ci_headers_addheaders(req->request_header, req->xheaders);
792     }
793 
794     return CI_OK;
795 }
796 
get_request_options(ci_request_t * req,ci_headers_list_t * h)797 static int get_request_options(ci_request_t * req, ci_headers_list_t * h)
798 {
799     const char *pstr;
800 
801     if ((pstr = ci_headers_value(h, "Preview")) != NULL) {
802         req->preview = strtol(pstr, NULL, 10);
803     } else
804         req->preview = -1;
805 
806 
807     req->allow204 = 0;
808     req->allow206 = 0;
809     if ((pstr = ci_headers_value(h, "Allow")) != NULL) {
810         if (strstr(pstr, "204"))
811             req->allow204 = 1;
812         if (strstr(pstr, "206"))
813             req->allow206 = 1;
814     }
815 
816     if ((pstr = ci_headers_value(h, "Connection")) != NULL
817             && strncmp(pstr, "close", 5) == 0) {
818         req->keepalive = 0;
819     }
820 
821     /*Moreover we are interested for the followings */
822     if ((pstr = ci_headers_value(h, "Transfer-Preview")) != NULL) {
823         /*Not implemented yet */
824     }
825 
826     if ((pstr = ci_headers_value(h, "Transfer-Ignore")) != NULL) {
827         /*Not implemented yet */
828     }
829 
830     if ((pstr = ci_headers_value(h, "Transfer-Complete")) != NULL) {
831         /*Not implemented yet */
832     }
833 
834     /*
835        The headers Max-Connections and  Options-TTL are not needed in this client
836        but if this functions moves to a general client api must be implemented
837      */
838 
839     return CI_OK;
840 }
841 
get_headers_from_entities(ci_encaps_entity_t ** entities,int type)842 static ci_headers_list_t* get_headers_from_entities(ci_encaps_entity_t** entities, int type)
843 {
844     ci_encaps_entity_t* e;
845     while ((e = *entities++) != NULL) {
846         if (e->type == type)
847             return (ci_headers_list_t*)(e->entity);
848     }
849     return NULL;
850 }
851 
852 
client_send_request_headers(ci_request_t * req,int has_eof)853 static int client_send_request_headers(ci_request_t * req, int has_eof)
854 {
855     int ret;
856 
857     if (req->status == CLIENT_SEND_HEADERS_WRITE_NOTHING) {
858         ci_request_pack(req);
859         req->status = CLIENT_SEND_HEADERS_WRITE_ICAP_HEADERS;
860         req->pstrblock_responce = req->request_header->buf;
861         req->remain_send_block_bytes = req->request_header->bufused;
862     }
863 
864     if (!req->remain_send_block_bytes)
865         return CI_OK;
866 
867     ret = block_write_nonblock(req->connection, &req->pstrblock_responce, &req->remain_send_block_bytes, &req->bytes_out);
868     if (ret != CI_OK)
869         return ret;
870 
871     if (req->status == CLIENT_SEND_HEADERS_WRITE_ICAP_HEADERS) {
872         req->status = CLIENT_SEND_HEADERS_WRITE_REQ_HEADERS;
873         ci_headers_list_t *headers = get_headers_from_entities(req->entities, ICAP_REQ_HDR);
874         if (headers) {
875             req->pstrblock_responce = headers->buf;
876             req->remain_send_block_bytes = headers->bufused;
877             return CI_NEEDS_MORE;
878         }
879     }
880 
881     if (req->status == CLIENT_SEND_HEADERS_WRITE_REQ_HEADERS) {
882         req->status = CLIENT_SEND_HEADERS_WRITE_RES_HEADERS;
883         ci_headers_list_t *headers = get_headers_from_entities(req->entities, ICAP_RES_HDR);
884         if (headers) {
885             req->pstrblock_responce = headers->buf;
886             req->remain_send_block_bytes = headers->bufused;
887             return CI_NEEDS_MORE;
888         }
889     }
890 
891     if (req->status == CLIENT_SEND_HEADERS_WRITE_RES_HEADERS) {
892 
893         if (req->preview > 0 && req->preview_data.used > 0) {
894             int bytes = sprintf(req->wbuf, "%x\r\n", req->preview);
895             req->status = CLIENT_SEND_HEADERS_WRITE_PREVIEW_INFO;
896             req->pstrblock_responce = req->wbuf;
897             req->remain_send_block_bytes = bytes;
898             return CI_NEEDS_MORE;
899         } else if (req->preview == 0) {
900             int bytes = sprintf(req->wbuf, "0%s\r\n\r\n", has_eof ? "; ieof" : "");
901             req->status = CLIENT_SEND_HEADERS_WRITE_EOF_INFO;
902             req->pstrblock_responce = req->wbuf;
903             req->remain_send_block_bytes = bytes;
904             return CI_NEEDS_MORE;
905         } else {
906             req->status = CLIENT_PROCESS_DATA;
907             assert(req->remain_send_block_bytes == 0);
908         }
909     }
910 
911     if (req->status == CLIENT_SEND_HEADERS_WRITE_PREVIEW_INFO) {
912         req->status = CLIENT_SEND_HEADERS_WRITE_PREVIEW;
913         req->pstrblock_responce = req->preview_data.buf;
914         req->remain_send_block_bytes = req->preview_data.used;
915         return CI_NEEDS_MORE;
916     }
917 
918     if (req->status == CLIENT_SEND_HEADERS_WRITE_PREVIEW) {
919         req->status = CLIENT_SEND_HEADERS_WRITE_EOF_INFO;
920         int bytes = sprintf(req->wbuf, "\r\n0%s\r\n\r\n", has_eof ? "; ieof" : "");
921         req->pstrblock_responce = req->wbuf;
922         req->remain_send_block_bytes = bytes;
923         return CI_NEEDS_MORE;
924     }
925 
926     if (req->status == CLIENT_SEND_HEADERS_WRITE_EOF_INFO) {
927         if (has_eof)
928             req->eof_sent = 1;
929 
930         req->status = CLIENT_PROCESS_DATA;
931     }
932 
933     return CI_OK;
934 }
935 
936 
937 /*this function check if there is enough space in buffer buf ....*/
check_realloc(char ** buf,int * size,int used,int mustadded)938 static int check_realloc(char **buf, int *size, int used, int mustadded)
939 {
940     char *newbuf;
941     int len;
942     while (*size - used < mustadded) {
943         len = *size + HEADSBUFSIZE;
944         newbuf = realloc(*buf, len);
945         if (!newbuf) {
946             return EC_500;
947         }
948         *buf = newbuf;
949         *size = *size + HEADSBUFSIZE;
950     }
951     return CI_OK;
952 }
953 
954 
client_parse_icap_header(ci_request_t * req,ci_headers_list_t * h)955 static int client_parse_icap_header(ci_request_t * req, ci_headers_list_t * h)
956 {
957     int readed = 0, eoh = 0;
958     char *buf;
959     const char *end;
960     if (req->pstrblock_read_len < 4)   /*we need 4 bytes for the end of headers "\r\n\r\n" string */
961         return CI_NEEDS_MORE;
962     if ((end = strnstr(req->pstrblock_read, "\r\n\r\n", req->pstrblock_read_len)) != NULL) {
963         readed = end - req->pstrblock_read + 4;
964         eoh = 1;
965     } else
966         readed = req->pstrblock_read_len - 3;
967 
968     if (check_realloc(&(h->buf), &(h->bufsize), h->bufused, readed) != CI_OK)
969         return CI_ERROR;
970 
971     buf = h->buf + h->bufused;
972     memcpy(buf, req->pstrblock_read, readed);
973     h->bufused += readed;
974     req->pstrblock_read += readed;
975     req->pstrblock_read_len -= readed;
976 
977     if (!eoh)
978         return CI_NEEDS_MORE;
979 
980     h->bufused -= 2;           /*We keep the first \r\n  of the eohead sequence and the other dropped
981                                    So stupid but for the time never mind.... */
982 
983     /* Zero-terminate buffer to avoid valgrind errors */
984     h->buf[h->bufused] = '\0';
985     return CI_OK;
986 }
987 
client_parse_encaps_header(ci_request_t * req,ci_headers_list_t * h,int size)988 static int client_parse_encaps_header(ci_request_t * req, ci_headers_list_t * h, int size)
989 {
990     int remains, readed = 0;
991     char *buf_end = NULL;
992 
993 //     readed = h->bufused;
994     remains = size - h->bufused;
995     if (remains < 0)           /*is it possible ????? */
996         return CI_ERROR;
997     if (remains == 0)
998         return CI_OK;
999 
1000     if (req->pstrblock_read_len > 0) {
1001         readed =
1002             (remains >
1003              req->pstrblock_read_len ? req->pstrblock_read_len : remains);
1004         memcpy(h->buf + h->bufused, req->pstrblock_read, readed);
1005         h->bufused += readed;
1006         req->pstrblock_read = (req->pstrblock_read) + readed;
1007         req->pstrblock_read_len = (req->pstrblock_read_len) - readed;
1008 
1009     }
1010 
1011     if (h->bufused < size)
1012         return CI_NEEDS_MORE;
1013 
1014     buf_end = h->buf + h->bufused;
1015     if (strncmp(buf_end - 4, "\r\n\r\n", 4) == 0) {
1016         h->bufused -= 2;      /*eat the last 2 bytes of "\r\n\r\n" */
1017         return CI_OK;
1018     } else {
1019         ci_debug_printf(1, "Error parsing encapsulated headers,"
1020                         "no \\r\\n\\r\\n at the end of headers:%s!\n",
1021                         buf_end);
1022     }
1023 
1024     return CI_ERROR;
1025 
1026 }
1027 
ci_client_get_server_options_nonblocking(ci_request_t * req)1028 int ci_client_get_server_options_nonblocking(ci_request_t * req)
1029 {
1030     int ret, i;
1031 
1032     if (req->status == CLIENT_INIT) {
1033         if (CI_OK != client_create_request(req, req->req_server,
1034                                            req->service, ICAP_OPTIONS)) {
1035             return CI_ERROR;
1036         }
1037         req->status = CLIENT_SEND_HEADERS;
1038     }
1039 
1040     if (req->status >= CLIENT_SEND_HEADERS && req->status < CLIENT_SEND_HEADERS_FINISHED) {
1041         ret = client_send_request_headers(req, 0);
1042         if (ret == CI_NEEDS_MORE)
1043             return NEEDS_TO_WRITE_TO_ICAP;
1044         else if (ret == CI_ERROR)
1045             return CI_ERROR;
1046 
1047         req->status = CLIENT_SEND_HEADERS_FINISHED;
1048     }
1049 
1050     if (req->status == CLIENT_SEND_HEADERS_FINISHED) {
1051         // Reset encaps entities to receive new entities from server
1052         for (i = 0; req->entities[i] != NULL; i++)
1053             ci_request_release_entity(req, i);
1054 
1055         req->status = CLIENT_PROCESS_DATA;
1056         return NEEDS_TO_READ_FROM_ICAP;
1057     }
1058 
1059     if (req->status >= CLIENT_PROCESS_DATA) {
1060         if (net_data_read(req) == CI_ERROR)
1061             return CI_ERROR;
1062 
1063         ret = client_parse_icap_header(req, req->response_header);
1064 
1065         if (ret == CI_NEEDS_MORE)
1066             return NEEDS_TO_READ_FROM_ICAP;
1067         else if (ret == CI_ERROR)
1068             return CI_ERROR;
1069         ci_headers_unpack(req->response_header);
1070         get_request_options(req, req->response_header);
1071     }
1072     return 0;
1073 }
1074 
ci_client_get_server_options(ci_request_t * req,int timeout)1075 int ci_client_get_server_options(ci_request_t * req, int timeout)
1076 {
1077     int status;
1078     int wait;
1079     do {
1080         status = ci_client_get_server_options_nonblocking(req);
1081         if (status == CI_ERROR)
1082             return CI_ERROR;
1083 
1084         wait = 0;
1085         if (status & NEEDS_TO_READ_FROM_ICAP)
1086             wait |= ci_wait_for_read;
1087 
1088         if (status & NEEDS_TO_WRITE_TO_ICAP)
1089             wait |= ci_wait_for_write;
1090 
1091         if (wait) {
1092             int ret;
1093             do {
1094                 ret = ci_connection_wait(req->connection, timeout, wait);
1095                 if (ret <= 0)
1096                     return CI_ERROR;
1097             } while (ret & ci_wait_should_retry);
1098         }
1099     } while (status);
1100 
1101     return CI_OK;
1102 }
1103 
1104 
ci_client_connect_to(char * servername,int port,int proto)1105 ci_connection_t *ci_client_connect_to(char *servername, int port, int proto)
1106 {
1107     return ci_connect_to(servername, port, proto, -1);
1108 }
1109 
1110 
client_prepere_body_chunk(ci_request_t * req,void * data,int (* readdata)(void * data,char *,int))1111 static int client_prepere_body_chunk(ci_request_t * req, void *data,
1112                                      int (*readdata) (void *data, char *, int))
1113 {
1114     int chunksize, def_bytes;
1115     char *wbuf = NULL;
1116     char tmpbuf[EXTRA_CHUNK_SIZE];
1117 
1118 
1119     wbuf = req->wbuf + EXTRA_CHUNK_SIZE;       /*Let size of EXTRA_CHUNK_SIZE space in the beggining of chunk */
1120     chunksize = (*readdata) (data, wbuf, MAX_CHUNK_SIZE);
1121     if (chunksize == CI_EOF || chunksize == 0) {
1122         req->remain_send_block_bytes = 0;
1123         return chunksize == CI_EOF ? CI_EOF : CI_NEEDS_MORE;
1124     } else if (chunksize < 0)
1125         return chunksize;
1126 
1127     wbuf += chunksize;         /*Put the "\r\n" sequence at the end of chunk */
1128     *(wbuf++) = '\r';
1129     *wbuf = '\n';
1130 
1131     def_bytes = snprintf(tmpbuf, EXTRA_CHUNK_SIZE, "%x\r\n", chunksize);
1132     wbuf = req->wbuf + EXTRA_CHUNK_SIZE - def_bytes;   /*Copy the chunk define in the beggining of chunk ..... */
1133     memcpy(wbuf, tmpbuf, def_bytes);
1134 
1135     req->pstrblock_responce = wbuf;
1136     req->remain_send_block_bytes = def_bytes + chunksize + 2;
1137 
1138     return CI_OK;
1139 }
1140 
1141 
client_parse_incoming_data(ci_request_t * req,void * data_dest,int (* dest_write)(void *,char *,int))1142 static int client_parse_incoming_data(ci_request_t * req, void *data_dest,
1143                                       int (*dest_write) (void *, char *, int))
1144 {
1145     int ret, v1, v2, status, bytes, size;
1146     char *buf;
1147     const char *val;
1148     ci_headers_list_t *resp_heads;
1149 
1150     if (req->status == CLIENT_PROCESS_DATA_GET_NOTHING) {
1151         /*And reading the new ..... */
1152         ret = client_parse_icap_header(req, req->response_header);
1153         if (ret != CI_OK)
1154             return ret;
1155         if (3 != sscanf(req->response_header->buf, "ICAP/%d.%d %3d", &v1, &v2, &status))
1156             return CI_ERROR;
1157         req->return_code = status;
1158         ci_debug_printf(3, "Response was with status:%d \n", status);
1159         ci_headers_unpack(req->response_header);
1160 
1161         if (ci_allow204(req) && status == 204) {
1162             req->status = CLIENT_PROCESS_DATA_GET_EOF;
1163             return 204;
1164         }
1165 
1166         if ((val = ci_headers_search(req->response_header, "Encapsulated")) == NULL) {
1167             ci_debug_printf(1, "No encapsulated entities!\n");
1168             return CI_ERROR;
1169         }
1170         process_encapsulated(req, val);
1171 
1172         if (!req->entities[0])
1173             return CI_ERROR;
1174 
1175         if (!req->entities[1]) {      /*Then we have only body */
1176             req->status = CLIENT_PROCESS_DATA_GET_BODY;
1177             if (req->pstrblock_read_len == 0)
1178                 return CI_NEEDS_MORE;
1179         } else {
1180             req->status = CLIENT_PROCESS_DATA_GET_HEADERS;
1181             size = req->entities[1]->start - req->entities[0]->start;
1182             resp_heads = req->entities[0]->entity;
1183             if (!ci_headers_setsize(resp_heads, size))
1184                 return CI_ERROR;
1185         }
1186 
1187 //        return CI_NEEDS_MORE;
1188     }
1189 
1190     /*read encups headers */
1191 
1192     /*Non option responce has one or two entities:
1193        "req-headers req-body|null-body" or [resp-headers] resp-body||null-body
1194        So here client_parse_encaps_header will be called for one headers block
1195      */
1196 
1197     if (req->status == CLIENT_PROCESS_DATA_GET_HEADERS) {
1198         size = req->entities[1]->start - req->entities[0]->start;
1199         resp_heads = req->entities[0]->entity;
1200         if ((ret =
1201                     client_parse_encaps_header(req, resp_heads, size)) != CI_OK)
1202             return ret;
1203 
1204 
1205         ci_headers_unpack(resp_heads);
1206         ci_debug_printf(5, "OK reading headers, going to read body\n");
1207 
1208         /*reseting body chunks related variables */
1209         req->current_chunk_len = 0;
1210         req->chunk_bytes_read = 0;
1211         req->write_to_module_pending = 0;
1212 
1213         if (req->entities[1]->type == ICAP_NULL_BODY) {
1214             req->status = CLIENT_PROCESS_DATA_GET_EOF;
1215             return CI_OK;
1216         } else {
1217             req->status = CLIENT_PROCESS_DATA_GET_BODY;
1218             if (req->pstrblock_read_len == 0)
1219                 return CI_NEEDS_MORE;
1220         }
1221     }
1222 
1223     if (req->status == CLIENT_PROCESS_DATA_GET_BODY) {
1224         do {
1225             if ((ret = parse_chunk_data(req, &buf)) == CI_ERROR) {
1226                 ci_debug_printf(1,
1227                                 "Error parsing chunks, current chunk len: %d, read: %d, readlen: %d, str: %.*s\n",
1228                                 req->current_chunk_len,
1229                                 req->chunk_bytes_read,
1230                                 req->pstrblock_read_len,
1231                                 (req->pstrblock_read_len < 64 ? req->pstrblock_read_len : 64),
1232                                 req->pstrblock_read);
1233                 return CI_ERROR;
1234             }
1235 
1236             while (req->write_to_module_pending > 0) {
1237                 bytes =
1238                     (*dest_write) (data_dest, buf,
1239                                    req->write_to_module_pending);
1240                 if (bytes < 0) {
1241                     ci_debug_printf(1, "Error writing to output file!\n");
1242                     return CI_ERROR;
1243                 }
1244                 req->write_to_module_pending -= bytes;
1245             }
1246 
1247             if (ret == CI_EOF) {
1248                 req->status = CLIENT_PROCESS_DATA_GET_EOF;
1249                 return CI_OK;
1250             }
1251         } while (ret != CI_NEEDS_MORE);
1252 
1253         return CI_NEEDS_MORE;
1254     }
1255 
1256     return CI_OK;
1257 }
1258 
1259 static const char *eof_str = "0\r\n\r\n";
1260 
ci_client_send_and_get_data(ci_request_t * req,void * data_source,int (* source_read)(void *,char *,int),void * data_dest,int (* dest_write)(void *,char *,int),int io_action_in)1261 static int ci_client_send_and_get_data(ci_request_t * req,
1262                                        void *data_source, int (*source_read) (void *, char *,
1263                                                int),
1264                                        void *data_dest, int (*dest_write) (void *, char *,
1265                                                int),
1266                                        int io_action_in
1267                                       )
1268 {
1269     int read_status, bytes;
1270     int needs_user_data = 0;
1271     if (!data_source)
1272         req->eof_sent = 1;
1273 
1274     if (io_action_in & ci_wait_for_write) {
1275         if (req->remain_send_block_bytes == 0) {
1276             if (!req->eof_sent && data_source) {
1277                 switch (client_prepere_body_chunk(req, data_source, source_read)) {
1278                 case CI_ERROR:
1279                     return CI_ERROR;
1280                 case CI_NEEDS_MORE:
1281                     needs_user_data = 1;
1282                     break;
1283                 case CI_EOF:
1284                     req->eof_sent = 1;
1285                     req->pstrblock_responce = (char *) eof_str;
1286                     req->remain_send_block_bytes = 5;
1287                     break;
1288                 }
1289             }
1290         }
1291         if (req->remain_send_block_bytes > 0) {
1292             bytes = ci_connection_write_nonblock(req->connection,
1293                                                  req->pstrblock_responce,
1294                                                  req->remain_send_block_bytes);
1295             if (bytes < 0)
1296                 return CI_ERROR;
1297             req->bytes_out += bytes;
1298             req->pstrblock_responce += bytes;
1299             req->remain_send_block_bytes -= bytes;
1300         }
1301     }
1302 
1303     if (io_action_in & ci_wait_for_read) {
1304         if (net_data_read(req) == CI_ERROR)
1305             return CI_ERROR;
1306 
1307         read_status = client_parse_incoming_data(req, data_dest, dest_write);
1308         if (read_status == CI_ERROR)
1309             return CI_ERROR;
1310 
1311         if (read_status == 204) {
1312             req->return_code = 204;
1313             return 0;
1314         }
1315     }
1316 
1317     int client_action = 0;
1318 
1319     if (needs_user_data)
1320         client_action |= NEEDS_TO_READ_USER_DATA;
1321 
1322     if (req->write_to_module_pending > 0)
1323         client_action |= NEEDS_TO_WRITE_USER_DATA;
1324 
1325     if (!req->eof_sent || req->remain_send_block_bytes)
1326         client_action |= NEEDS_TO_WRITE_TO_ICAP;
1327 
1328     if (req->status != CLIENT_PROCESS_DATA_GET_EOF)
1329         client_action |= NEEDS_TO_READ_FROM_ICAP;
1330 
1331     return client_action;
1332 }
1333 
client_build_headers(ci_request_t * req,int has_reqhdr,int has_reshdr,int has_body)1334 static int client_build_headers(ci_request_t *req, int has_reqhdr, int has_reshdr, int has_body)
1335 {
1336     int i = 0;
1337 
1338     for (i = 0; i < 4; i++) {
1339         if (req->entities[i]) {
1340             ci_request_release_entity(req, i);
1341         }
1342     }
1343     i = 0;
1344 
1345     if (has_reqhdr)
1346         req->entities[i++] = ci_request_alloc_entity(req, ICAP_REQ_HDR, 0);
1347     if (has_reshdr)
1348         req->entities[i++] = ci_request_alloc_entity(req, ICAP_RES_HDR, 0);
1349     if (has_body)
1350         req->entities[i] = ci_request_alloc_entity(req, req->type == ICAP_RESPMOD ? ICAP_RES_BODY : ICAP_REQ_BODY, 0);
1351     else
1352         req->entities[i] = ci_request_alloc_entity(req, ICAP_NULL_BODY, 0);
1353     return 1;
1354 }
1355 
1356 
ci_client_handle_previewed_response(ci_request_t * req,int * preview_status)1357 static int ci_client_handle_previewed_response(ci_request_t * req, int *preview_status)
1358 {
1359     int v1, v2, ret;
1360     const char *val;
1361 
1362     if (net_data_read(req) == CI_ERROR)
1363         return CI_ERROR;
1364 
1365     ret = client_parse_icap_header(req, req->response_header);
1366     if (ret != CI_OK)
1367         return ret;
1368 
1369     if (3 != sscanf(req->response_header->buf, "ICAP/%d.%d %3d", &v1, &v2, preview_status))
1370         return CI_ERROR;
1371 
1372     ci_debug_printf(3, "Preview response was with status: %d \n",
1373                     *preview_status);
1374     if (*preview_status == 204) {
1375         ci_headers_unpack(req->response_header);
1376     } else if ((req->eof_sent && *preview_status == 200) || *preview_status == 206) {
1377         ci_headers_unpack(req->response_header);
1378         if ((val = ci_headers_search(req->response_header, "Encapsulated")) == NULL) {
1379             ci_debug_printf(1, "No encapsulated entities!\n");
1380             return CI_ERROR;
1381         }
1382         process_encapsulated(req, val);
1383         if (!req->entities[1])   /*Then we have only body */
1384             req->status = CLIENT_PROCESS_DATA_GET_BODY;
1385         else
1386             req->status = CLIENT_PROCESS_DATA_GET_HEADERS;
1387     } else {
1388         if (*preview_status == 100)
1389             req->status = CLIENT_PROCESS_DATA;
1390         ci_headers_reset(req->response_header);
1391     }
1392     return CI_OK;
1393 }
1394 
prepare_headers(ci_request_t * req,ci_headers_list_t * req_headers,ci_headers_list_t * resp_headers,void * data_source,int (* source_read)(void *,char *,int))1395 static int prepare_headers(ci_request_t * req,
1396                            ci_headers_list_t * req_headers,
1397                            ci_headers_list_t * resp_headers,
1398                            void *data_source, int (*source_read) (void *, char *, int))
1399 {
1400     int i, ret, remains, pre_eof = 0;
1401     char *buf;
1402 
1403     if (CI_OK !=
1404             client_create_request(req, req->req_server, req->service,
1405                                   req->type)) {
1406         ci_debug_printf(1, "Error making respmod request ....\n");
1407         return CI_ERROR;
1408     }
1409 
1410     if (!data_source)
1411         req->preview = -1;
1412 
1413     if (req->preview > 0) {    /*The preview data will be send with headers.... */
1414         ci_buf_mem_alloc(&(req->preview_data), req->preview); /*Alloc mem for preview data */
1415         buf = req->preview_data.buf;
1416         remains = req->preview;
1417         while (remains && !pre_eof) { /*Getting the preview data */
1418             ret = (*source_read) (data_source, buf, remains);
1419             if (ret == CI_EOF || ret == 0)
1420                 pre_eof = 1;
1421             else if (ret < 0)
1422                 return ret;
1423             else
1424                 remains -= ret;
1425         }
1426         req->preview -= remains;
1427         req->preview_data.used = req->preview;
1428     }
1429     if (pre_eof)
1430         req->eof_sent = 1;
1431 
1432     /*Bulid client request structure*/
1433     if (!client_build_headers(req, (req_headers != NULL), (resp_headers != NULL), (data_source != NULL)))
1434         return CI_ERROR;
1435 
1436     /*Add the user supplied headers */
1437     if (req_headers) {
1438         ci_debug_printf(5, "Going to add %d request headers\n", req_headers->used);
1439         for (i = 0; i < req_headers->used; i++) {
1440             ci_debug_printf(8, "Add request header: %s\n", req_headers->headers[i]);
1441             ci_http_request_add_header(req, req_headers->headers[i]);
1442         }
1443     }
1444     if (resp_headers) {
1445         ci_debug_printf(5, "Going to add %d response headers\n", resp_headers->used);
1446         for (i = 0; i < resp_headers->used; i++) {
1447             ci_debug_printf(8, "Add resp header: %s\n", resp_headers->headers[i]);
1448             ci_http_response_add_header(req, resp_headers->headers[i]);
1449         }
1450     }
1451 
1452     req->status = CLIENT_SEND_HEADERS;
1453     return CI_OK;
1454 }
1455 
ci_client_icapfilter_nonblocking(ci_request_t * req,int io_action,ci_headers_list_t * req_headers,ci_headers_list_t * resp_headers,void * data_source,int (* source_read)(void *,char *,int),void * data_dest,int (* dest_write)(void *,char *,int))1456 int ci_client_icapfilter_nonblocking(ci_request_t * req, int io_action,
1457                                      ci_headers_list_t * req_headers,
1458                                      ci_headers_list_t * resp_headers,
1459                                      void *data_source,
1460                                      int (*source_read) (void *, char *, int),
1461                                      void *data_dest,
1462                                      int (*dest_write) (void *, char *, int))
1463 {
1464     int ret, preview_status, i;
1465 
1466     if (req->status == CLIENT_INIT) {
1467         ret = prepare_headers(req, req_headers, resp_headers,
1468                               data_source, source_read);
1469         if (ret == CI_NEEDS_MORE)
1470             return NEEDS_TO_READ_USER_DATA;
1471         if (ret != CI_OK)
1472             return ret;
1473         req->status = CLIENT_SEND_HEADERS;
1474     }
1475 
1476     if (req->status >= CLIENT_SEND_HEADERS && req->status < CLIENT_SEND_HEADERS_FINISHED) {
1477         ret = client_send_request_headers(req, req->eof_sent);
1478         if (ret == CI_NEEDS_MORE)
1479             return NEEDS_TO_WRITE_TO_ICAP;
1480         else if (ret == CI_ERROR)
1481             return CI_ERROR;
1482 
1483         req->status = CLIENT_SEND_HEADERS_FINISHED;
1484     }
1485 
1486     if (req->status == CLIENT_SEND_HEADERS_FINISHED) {
1487         // Reset encaps entities to receive new entities from server
1488         for (i = 0; req->entities[i] != NULL; i++)
1489             ci_request_release_entity(req, i);
1490 
1491         if (req->preview >= 0) {   /*we must wait for ICAP responce here..... */
1492             req->status = CLIENT_READ_PREVIEW_RESPONSE;
1493             return NEEDS_TO_READ_FROM_ICAP;
1494         } else
1495             req->status = CLIENT_PROCESS_DATA;
1496     }
1497 
1498     if (req->preview >= 0 && req->status == CLIENT_READ_PREVIEW_RESPONSE) {
1499         preview_status = 100;
1500         ret = ci_client_handle_previewed_response(req, &preview_status);
1501         if (ret == CI_NEEDS_MORE)
1502             return NEEDS_TO_READ_FROM_ICAP;
1503         else if (ret == CI_ERROR)
1504             return CI_ERROR;
1505 
1506         req->return_code = preview_status;
1507         if (preview_status == 204 || preview_status == 206)
1508             return 0;
1509 
1510         if (preview_status == 100)
1511             return NEEDS_TO_WRITE_TO_ICAP;
1512     }
1513 
1514     if (req->status >= CLIENT_PROCESS_DATA) {
1515         ret = ci_client_send_and_get_data(req, data_source, source_read,
1516                                           data_dest, dest_write, io_action);
1517         return ret;
1518     }
1519 
1520     return 0;
1521 }
1522 
ci_client_icapfilter(ci_request_t * req,int timeout,ci_headers_list_t * req_headers,ci_headers_list_t * resp_headers,void * data_source,int (* source_read)(void *,char *,int),void * data_dest,int (* dest_write)(void *,char *,int))1523 int ci_client_icapfilter(ci_request_t * req, int timeout,
1524                          ci_headers_list_t * req_headers,
1525                          ci_headers_list_t * resp_headers,
1526                          void *data_source, int (*source_read) (void *, char *, int),
1527                          void *data_dest, int (*dest_write) (void *, char *, int))
1528 {
1529     int io_action = 0;
1530 
1531     for (;;) {
1532         int ret = ci_client_icapfilter_nonblocking(req, io_action, req_headers, resp_headers,
1533                   data_source, source_read, data_dest, dest_write);
1534         if (ret < 0)
1535             return CI_ERROR;
1536 
1537         if (ret == 0)
1538             return req->return_code;
1539 
1540         int wait = 0;
1541         if (ret & NEEDS_TO_READ_FROM_ICAP)
1542             wait |= ci_wait_for_read;
1543 
1544         if (ret & NEEDS_TO_WRITE_TO_ICAP)
1545             wait |= ci_wait_for_write;
1546 
1547         if (wait != 0) {
1548             do {
1549                 io_action = ci_connection_wait(req->connection, timeout, wait);
1550                 if (io_action <= 0)
1551                     return CI_ERROR;
1552             } while (io_action & ci_wait_should_retry);
1553         } /*Else probably NEEDS_TO_READ_USER_DATA|NEEDS_TO_WRITE_USER_DATA*/
1554     }
1555 
1556     return CI_ERROR; /*not reached*/
1557 }
1558 
1559 /**
1560  * Return 1 if the HTTP headers (if any) are read completely
1561  * otherwise return 0;
1562  */
ci_client_http_headers_completed(ci_request_t * req)1563 int ci_client_http_headers_completed(ci_request_t * req)
1564 {
1565     return (req->status >= CLIENT_PROCESS_DATA_HEADERS_FINISHED);
1566 }
1567 
ci_client_set_user_agent(const char * agent)1568 void ci_client_set_user_agent(const char *agent)
1569 {
1570     if (!agent)
1571         return  ;
1572 
1573     if (CI_USER_AGENT)
1574         free(CI_USER_AGENT);
1575 
1576     CI_USER_AGENT = strdup(agent);
1577 }
1578 
1579 
ci_client_library_init()1580 void ci_client_library_init()
1581 {
1582     ci_cfg_lib_init();
1583 }
1584 
ci_client_library_release()1585 void ci_client_library_release()
1586 {
1587     if (CI_USER_AGENT) {
1588         free(CI_USER_AGENT);
1589         CI_USER_AGENT = NULL;
1590     }
1591 }
1592