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