|
NCBI Home IEB Home C Toolkit docs C++ Toolkit source browser C Toolkit source browser (2) |
NCBI C Toolkit Cross ReferenceC/connect/ncbi_connutil.c |
source navigation diff markup identifier search freetext search file search |
1 /* $Id: ncbi_connutil.c,v 6.137 2009/10/30 14:39:33 kazimird Exp $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Denis Vakatov, Anton Lavrentiev
27 *
28 * File Description:
29 * Auxiliary API, mostly CONN-, URL-, and MIME-related
30 * (see in "ncbi_connutil.h" for more details).
31 *
32 */
33
34 #include "ncbi_ansi_ext.h"
35 #include "ncbi_priv.h"
36 #include <connect/ncbi_connutil.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdlib.h>
40
41 #define NCBI_USE_ERRCODE_X Connect_Util
42
43
44 extern const char* ConnNetInfo_GetValue(const char* service, const char* param,
45 char* value, size_t value_size,
46 const char* def_value)
47 {
48 char buf[128];
49 const char* val;
50 char* s;
51
52 if (!value || value_size <= 0)
53 return 0;
54 *value = '\0';
55
56 if (!param || !*param)
57 return 0;
58
59 if (service && *service) {
60 /* Service-specific inquiry */
61 size_t slen = strlen(service);
62 size_t plen = strlen(param) + 1;
63 if (slen + 1 + sizeof(DEF_CONN_REG_SECTION) + plen > sizeof(buf))
64 return 0;
65
66 /* First, environment search for 'service_CONN_param' */
67 s = (char*) memcpy(buf, service, slen) + slen;
68 *s++ = '_';
69 memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
70 s += sizeof(DEF_CONN_REG_SECTION) - 1;
71 *s++ = '_';
72 memcpy(s, param, plen);
73 if ((val = getenv(buf)) != 0 || (val = getenv(strupr(buf))) != 0)
74 return strncpy0(value, val, value_size - 1);
75
76 /* Next, search for 'CONN_param' in '[service]' registry section */
77 buf[slen++] = '\0';
78 s = buf + slen;
79 CORE_REG_GET(buf, s, value, value_size, 0);
80 if (*value)
81 return value;
82 } else {
83 /* Common case. Form 'CONN_param' */
84 size_t plen = strlen(param) + 1;
85 if (sizeof(DEF_CONN_REG_SECTION) + plen > sizeof(buf))
86 return 0;
87 s = buf;
88 memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
89 s += sizeof(DEF_CONN_REG_SECTION) - 1;
90 *s++ = '_';
91 memcpy(s, param, plen);
92 s = strupr(buf);
93 }
94
95 /* Environment search for 'CONN_param' */
96 if ((val = getenv(s)) != 0)
97 return strncpy0(value, val, value_size - 1);
98
99 /* Last resort: Search for 'param' in default registry section */
100 s += sizeof(DEF_CONN_REG_SECTION);
101 CORE_REG_GET(DEF_CONN_REG_SECTION, s, value, value_size, def_value);
102 return value;
103 }
104
105
106 static EURLScheme s_ParseScheme(const char* str, size_t len)
107 {
108 if (len == 5 && strncasecmp(str, "https", len) == 0)
109 return eURL_Https;
110 if (len == 4 && strncasecmp(str, "http", len) == 0)
111 return eURL_Http;
112 if (len == 4 && strncasecmp(str, "file", len) == 0)
113 return eURL_File;
114 if (len == 3 && strncasecmp(str, "ftp", len) == 0)
115 return eURL_Ftp;
116 return eURL_Unspec;
117 }
118
119
120 static const char* s_Scheme(EURLScheme scheme)
121 {
122 switch (scheme) {
123 case eURL_Unspec:
124 break;
125 case eURL_Https:
126 return "https";
127 case eURL_Http:
128 return "http";
129 case eURL_File:
130 return "file";
131 case eURL_Ftp:
132 return "ftp";
133 }
134 return 0;
135 }
136
137
138 /****************************************************************************
139 * ConnNetInfo API
140 */
141
142
143 extern SConnNetInfo* ConnNetInfo_Create(const char* service)
144 {
145 #define REG_VALUE(name, value, def_value) \
146 ConnNetInfo_GetValue(service, name, value, sizeof(value), def_value)
147
148 SConnNetInfo* info = (SConnNetInfo*) malloc(sizeof(*info) +
149 (service && *service
150 ? strlen(service) + 1 : 0));
151 size_t len;
152 /* aux. storage for the string-to-int conversions, etc. */
153 char str[1024];
154 int val;
155 double dbl;
156 char* e;
157
158 if (!info)
159 return 0/*failure*/;
160
161 /* client host: default */
162 info->client_host[0] = '\0';
163
164 /* scheme */
165 REG_VALUE(REG_CONN_SCHEME, str, DEF_CONN_SCHEME);
166 info->scheme = s_ParseScheme(str, strlen(str));
167
168 /* username */
169 REG_VALUE(REG_CONN_USER, info->user, DEF_CONN_USER);
170
171 /* password */
172 REG_VALUE(REG_CONN_PASS, info->pass, DEF_CONN_PASS);
173
174 /* hostname */
175 REG_VALUE(REG_CONN_HOST, info->host, DEF_CONN_HOST);
176
177 /* port # */
178 REG_VALUE(REG_CONN_PORT, str, DEF_CONN_PORT);
179 errno = 0;
180 if (*str && (val = strtoul(str, &e, 10)) > 0 && !errno
181 && !*e && val < (1 << 16)) {
182 info->port = val;
183 } else
184 info->port = 0/*default*/;
185
186 /* path */
187 REG_VALUE(REG_CONN_PATH, info->path, DEF_CONN_PATH);
188
189 /* args */
190 REG_VALUE(REG_CONN_ARGS, info->args, DEF_CONN_ARGS);
191
192 /* request method */
193 REG_VALUE(REG_CONN_REQ_METHOD, str, DEF_CONN_REQ_METHOD);
194 if (!*str || strcasecmp(str, "ANY") == 0)
195 info->req_method = eReqMethod_Any;
196 else if (strcasecmp(str, "POST") == 0)
197 info->req_method = eReqMethod_Post;
198 else if (strcasecmp(str, "GET") == 0)
199 info->req_method = eReqMethod_Get;
200
201 /* connection timeout */
202 REG_VALUE(REG_CONN_TIMEOUT, str, 0);
203 len = strlen(str);
204 if (len > 2 && len < 9 && strncasecmp(str, "infinite", len) == 0) {
205 info->timeout = 0;
206 } else {
207 info->timeout = &info->tmo;
208 if (!*str || (dbl = atof(str)) < 0.0)
209 dbl = DEF_CONN_TIMEOUT;
210 info->timeout->sec = (unsigned int) dbl;
211 info->timeout->usec = (unsigned int)
212 ((dbl - info->timeout->sec) * 1000000.0);
213 }
214
215 /* max. # of attempts to establish connection */
216 REG_VALUE(REG_CONN_MAX_TRY, str, 0);
217 val = atoi(str);
218 info->max_try = (unsigned short)(val > 0 ? val : DEF_CONN_MAX_TRY);
219
220 /* HTTP proxy server? */
221 REG_VALUE(REG_CONN_HTTP_PROXY_HOST, info->http_proxy_host,
222 DEF_CONN_HTTP_PROXY_HOST);
223 if (*info->http_proxy_host) {
224 /* yes, use the specified HTTP proxy server */
225 REG_VALUE(REG_CONN_HTTP_PROXY_PORT, str, DEF_CONN_HTTP_PROXY_PORT);
226 errno = 0;
227 if (*str && (val = strtoul(str, &e, 10)) > 0
228 && !errno && !*e && val < (1 << 16)) {
229 info->http_proxy_port = val;
230 } else
231 info->http_proxy_port = 0/*default*/;
232 } else
233 info->http_proxy_port = 0;
234
235 /* non-transparent CERN-like firewall proxy server? */
236 REG_VALUE(REG_CONN_PROXY_HOST, info->proxy_host, DEF_CONN_PROXY_HOST);
237
238 /* turn on debug printout? */
239 REG_VALUE(REG_CONN_DEBUG_PRINTOUT, str, DEF_CONN_DEBUG_PRINTOUT);
240 if (*str && (strcmp (str, "1") == 0 ||
241 strcasecmp(str, "on") == 0 ||
242 strcasecmp(str, "yes") == 0 ||
243 strcasecmp(str, "true") == 0 ||
244 strcasecmp(str, "some") == 0)) {
245 info->debug_printout = eDebugPrintout_Some;
246 } else if (*str && (strcasecmp(str, "all") == 0 ||
247 strcasecmp(str, "data") == 0)) {
248 info->debug_printout = eDebugPrintout_Data;
249 } else
250 info->debug_printout = eDebugPrintout_None;
251
252 /* stateless client? */
253 REG_VALUE(REG_CONN_STATELESS, str, DEF_CONN_STATELESS);
254 info->stateless = (*str && (strcmp (str, "1") == 0 ||
255 strcasecmp(str, "on") == 0 ||
256 strcasecmp(str, "yes") == 0 ||
257 strcasecmp(str, "true") == 0));
258
259 /* firewall mode? */
260 REG_VALUE(REG_CONN_FIREWALL, str, DEF_CONN_FIREWALL);
261 info->firewall = (*str && (strcmp (str, "1") == 0 ||
262 strcasecmp(str, "on") == 0 ||
263 strcasecmp(str, "yes") == 0 ||
264 strcasecmp(str, "true") == 0));
265
266 /* prohibit the use of local load balancer? */
267 REG_VALUE(REG_CONN_LB_DISABLE, str, DEF_CONN_LB_DISABLE);
268 info->lb_disable = (*str && (strcmp (str, "1") == 0 ||
269 strcasecmp(str, "on" ) == 0 ||
270 strcasecmp(str, "yes" ) == 0 ||
271 strcasecmp(str, "true") == 0));
272
273 /* user header (with optional '\r\n' added automagically) */
274 REG_VALUE(REG_CONN_HTTP_USER_HEADER, str, DEF_CONN_HTTP_USER_HEADER);
275 if (*str) {
276 size_t len = strlen(str);
277 if (str[len - 1] != '\n' && len < sizeof(str) - 2) {
278 str[len++] = '\r';
279 str[len++] = '\n';
280 str[len] = '\0';
281 }
282 info->http_user_header = strdup(str);
283 } else
284 info->http_user_header = 0;
285
286 /* default referer */
287 ConnNetInfo_GetValue(0, REG_CONN_HTTP_REFERER, str, sizeof(str),
288 DEF_CONN_HTTP_REFERER);
289 info->http_referer = *str ? strdup(str) : 0;
290
291 /* not adjusted yet... */
292 info->http_proxy_adjusted = 0/*false*/;
293 /* store the service name for which this structure has been created */
294 info->service = (service && *service
295 ? strcpy((char*) info + sizeof(*info), service)
296 : 0);
297
298 /* done */
299 return info;
300 #undef REG_VALUE
301 }
302
303
304 extern int/*bool*/ ConnNetInfo_AdjustForHttpProxy(SConnNetInfo* info)
305 {
306 char port[10];
307 size_t hostlen;
308 size_t portlen;
309 size_t pathlen;
310 size_t len;
311
312 if (!info)
313 return 0/*false*/;
314
315 if (!*info->http_proxy_host || info->http_proxy_adjusted)
316 return 1/*true*/;
317
318 if (info->scheme != eURL_Unspec && info->scheme != eURL_Http) {
319 CORE_LOGF_X(13, eLOG_Error,
320 ("[ConnNetInfo_AdjustForHttpProxy] "
321 " Cannot adjust for scheme \"%s\"",
322 s_Scheme(info->scheme)));
323 assert(0);
324 return 0/*false*/;
325 }
326
327 hostlen = strlen(info->host);
328 portlen = info->port ? (size_t) sprintf(port, ":%hu", info->port) : 0;
329 if (*info->path != '/')
330 port[portlen++] = '/';
331 pathlen = strlen(info->path);
332
333 len = 7/*http://*/ + hostlen + portlen + pathlen + 1;
334
335 if (len >= sizeof(info->path)) {
336 CORE_LOGF_X(14, eLOG_Error,
337 ("[ConnNetInfo_AdjustForHttpProxy] "
338 " Adjusted path too long (%lu)",
339 (unsigned long) len));
340 assert(0);
341 return 0/*false*/;
342 }
343 len -= pathlen + 1;
344 memmove(info->path + len, info->path, pathlen + 1);
345 len -= portlen;
346 memcpy (info->path + len, port, portlen);
347 assert(len - hostlen == 7);
348 memcpy (info->path + 7, info->host, hostlen);
349 memcpy (info->path, "http://", 7);
350
351 assert(sizeof(info->host) >= sizeof(info->http_proxy_host));
352 strncpy0(info->host, info->http_proxy_host, sizeof(info->host) - 1);
353 info->port = info->http_proxy_port;
354 info->http_proxy_adjusted = 1/*true*/;
355 info->scheme = eURL_Http;
356 return 1/*true*/;
357 }
358
359
360 extern int/*bool*/ ConnNetInfo_ParseURL(SConnNetInfo* info, const char* url)
361 {
362 const char *s, *a;
363 size_t len;
364 char* p;
365
366 if (info->http_proxy_adjusted) {
367 /* undo proxy adjustment */
368 SConnNetInfo* temp = ConnNetInfo_Create(info->service);
369 if (!ConnNetInfo_ParseURL(temp, info->path)) {
370 ConnNetInfo_Destroy(temp);
371 return 0/*failure*/;
372 }
373 memcpy(info->host, temp->host, sizeof(info->host));
374 info->port = temp->port;
375 memcpy(info->path, temp->path, sizeof(info->path));
376 ConnNetInfo_Destroy(temp);
377 info->http_proxy_adjusted = 0/*false*/;
378 }
379
380 /* "user:pass@host:port" first [any optional] */
381 if ((s = strstr(url, "://")) != 0) {
382 const char* h = s + 3; /*host starts here*/
383
384 len = (size_t)(s - url);
385 if ((info->scheme = s_ParseScheme(url, len)) == eURL_Unspec)
386 return 0/*failure*/;
387
388 /* find the end of host spec */
389 len = strcspn(h, "/?#");
390 s = h + len;
391
392 /* username:password */
393 if (!(a = (const char*) memchr(h, '@', len))) {
394 info->user[0] = '\0';
395 info->pass[0] = '\0';
396 } else {
397 size_t ulen = (size_t)(a - h);
398 const char* t = (const char*) memchr(h, ':', ulen);
399 if (t)
400 ulen = (size_t)(t - h);
401 if (ulen < sizeof(info->user)) {
402 memcpy(info->user, h, ulen);
403 info->user[ulen] = '\0';
404 } else {
405 memcpy(info->user, h, sizeof(info->user) - 1);
406 info->user[sizeof(info->user) - 1] = '\0';
407 }
408 if (t && ulen) {
409 /* take pass only if user non-empty */
410 len = (size_t)(a - ++t);
411 if (len < sizeof(info->pass)) {
412 memcpy(info->pass, t, len);
413 info->pass[len] = '\0';
414 } else {
415 memcpy(info->pass, t, sizeof(info->pass) - 1);
416 info->pass[sizeof(info->pass) - 1] = '\0';
417 }
418 } else
419 info->pass[0] = '\0';
420 h = ++a;
421 len = (size_t)(s - h);
422 }
423
424 /* host ends at "s" here */
425 if (!(a = (const char*) memchr(h, ':', len))) {
426 info->port = 0/*default*/;
427 /*len remains unchanged*/
428 } else if (isdigit(a[1])) {
429 unsigned short port;
430 int n;
431 if (sscanf(a, ":%hu%n", &port, &n) < 1 || a + n != s || !port)
432 return 0/*failure*/;
433 info->port = port;
434 len = (size_t)(a - h);
435 } else
436 return 0/*failure*/;
437 if (len < sizeof(info->host)) {
438 memcpy(info->host, h, len);
439 info->host[len] = '\0';
440 } else {
441 memcpy(info->host, h, sizeof(info->host) - 1);
442 info->host[sizeof(info->host) - 1] = '\0';
443 }
444 } else
445 s = url;
446
447 a = s + strcspn(s, "?#");
448 len = (size_t)(a - s);
449
450 /* path (NB: can be relative); "len" holds the path length */
451 if (s != url || *s == '/' || !(p = strrchr(info->path, '/'))) {
452 /* absolute path */
453 p = info->path;
454 if (!len) {
455 s = "/"; /* in case of an empty path we take the root '/' */
456 len = 1;
457 }
458 } else
459 p++;
460 if (len < sizeof(info->path) - (size_t)(p - info->path)) {
461 memcpy(p, s, len);
462 p[len] = '\0';
463 } else {
464 memcpy(p, s, sizeof(info->path) - (size_t)(p - info->path) - 1);
465 info->path[sizeof(info->path) - 1] = '\0';
466 }
467
468 /* arguments and fragment */
469 if (*a) {
470 if (*a == '#') {
471 len = 0/*unused*/;
472 s = a;
473 } else if (!(s = strchr(++a/*NB: *a=='?'*/, '#'))) {
474 len = strlen(a);
475 s = a + len;
476 } else
477 len = (size_t)(s - a);
478 /* "len" holds the length of new args("a", w/o the leading "?") */
479 assert(!*s || *s == '#');
480 assert(*a != '?');
481
482 if (*s) {
483 /* if there is a new fragment, the entire args get overridden */
484 if (!s[1]) {
485 /* don't store the empty fragment # */
486 len = (size_t)(s - a);
487 if (len > sizeof(info->args) - 1)
488 len = sizeof(info->args) - 1;
489 } else
490 len = sizeof(info->args) - 1;
491 strncpy0(info->args, a, len);
492 } else if (!(p = strchr(info->args, '#'))
493 || len >= sizeof(info->args) - 1) {
494 /* there is no new fragment and: either there is no old fragment,
495 * or the old one will not fit -- in both cases, replace */
496 strncpy0(info->args, a, sizeof(info->args) - 1);
497 } else {
498 /* there is no new fragment, but there was an old one -- keep it */
499 size_t move = strlen(p);
500 if (move > sizeof(info->args) - 1 - len)
501 move = sizeof(info->args) - 1 - len;
502 memmove(info->args + len, p, move);
503 memcpy (info->args, a, len);
504 info->args[len + move] = '\0';
505 }
506 } else if ((p = strchr(info->args, '#')) != 0) {
507 /* keep the old fragment, if any, but drop all args */
508 memmove(info->args, p, strlen(p) + 1);
509 }
510
511 return 1/*success*/;
512 }
513
514
515 extern int/*bool*/ ConnNetInfo_SetUserHeader(SConnNetInfo* info,
516 const char* user_header)
517 {
518 if (info->http_user_header)
519 free((void*) info->http_user_header);
520 if (user_header && *user_header) {
521 info->http_user_header = strdup(user_header);
522 return info->http_user_header ? 1/*success*/ : 0/*failure*/;
523 } else
524 info->http_user_header = 0;
525 return 1/*success*/;
526 }
527
528
529 extern int/*bool*/ ConnNetInfo_AppendUserHeader(SConnNetInfo* info,
530 const char* user_header)
531 {
532 size_t oldlen, newlen;
533 char* new_header;
534
535 if (!info->http_user_header || !(oldlen = strlen(info->http_user_header)))
536 return ConnNetInfo_SetUserHeader(info, user_header);
537
538 if (!user_header || !(newlen = strlen(user_header)))
539 return 1/*success*/;
540
541 new_header = (char*)
542 realloc((void*) info->http_user_header, oldlen + newlen + 1);
543 if (!new_header)
544 return 0/*failure*/;
545
546 memcpy(&new_header[oldlen], user_header, newlen + 1);
547 info->http_user_header = new_header;
548 return 1/*success*/;
549 }
550
551
552 typedef enum {
553 eUserHeaderOp_Delete,
554 eUserHeaderOp_Extend,
555 eUserHeaderOp_Override
556 } EUserHeaderOp;
557
558
559 static int/*bool*/ s_ModifyUserHeader(SConnNetInfo* info,
560 const char* user_header,
561 EUserHeaderOp op)
562 {
563 int/*bool*/ retval;
564 char* new_header;
565 size_t newlinelen;
566 size_t newhdrlen;
567 char* newline;
568 size_t hdrlen;
569 char* hdr;
570
571 if (!user_header || !(newhdrlen = strlen(user_header)))
572 return 1/*success*/;
573
574 if (!(hdr = (char*) info->http_user_header) || !(hdrlen = strlen(hdr))) {
575 if (op == eUserHeaderOp_Delete)
576 return 1/*success*/;
577 if (!hdr && !(hdr = strdup("")))
578 return 0/*failure*/;
579 hdrlen = 0;
580 }
581
582 if (op != eUserHeaderOp_Delete) {
583 if (!(new_header = (char*) malloc(newhdrlen + 1)))
584 return 0/*failure*/;
585 memcpy(new_header, user_header, newhdrlen + 1);
586 } else
587 new_header = (char*) user_header; /* we actually won't modify it! */
588
589 retval = 1/*assume best: success*/;
590 for (newline = new_header; *newline; newline += newlinelen) {
591 char* eol = strchr(newline, '\n');
592 char* eot = strchr(newline, ':');
593 int/*bool*/ used = 0;
594 size_t newtaglen;
595 char* newtagval;
596 size_t linelen;
597 char* line;
598 size_t len;
599 size_t l;
600
601 newlinelen = (size_t)
602 (eol ? eol - newline + 1 : new_header + newhdrlen - newline);
603 if (!eot || eot >= newline + newlinelen ||
604 !(newtaglen = (size_t)(eot - newline)))
605 continue;
606
607 newtagval = newline + newtaglen + 1;
608 while (newtagval < newline + newlinelen) {
609 if (isspace((unsigned char)(*newtagval)))
610 newtagval++;
611 else
612 break;
613 }
614 switch (op) {
615 case eUserHeaderOp_Delete:
616 len = 0;
617 break;
618 case eUserHeaderOp_Extend:
619 len = newlinelen - (size_t)(newtagval - newline);
620 break;
621 case eUserHeaderOp_Override:
622 len = newtagval < newline + newlinelen ? newlinelen : 0;
623 break;
624 default:
625 assert(0);
626 retval = 0/*failure*/;
627 len = 0;
628 break;
629 }
630
631 for (line = hdr; *line; line += linelen) {
632 size_t taglen;
633
634 eol = strchr(line, '\n');
635 eot = strchr(line, ':');
636
637 linelen = (size_t)(eol ? eol - line + 1 : hdr + hdrlen - line);
638 if (!eot || eot >= line + linelen)
639 continue;
640
641 taglen = (size_t)(eot - line);
642 if (newtaglen != taglen || strncasecmp(newline, line, taglen) != 0)
643 continue;
644
645 if (op == eUserHeaderOp_Extend) {
646 if (linelen - taglen >= len
647 && strncasecmp(line + linelen - len, newtagval, len) == 0
648 && (linelen - taglen == len ||
649 isspace((unsigned char) line[linelen - len - 1]))) {
650 len = 0;
651 }
652 l = linelen + len;
653 if (len && linelen > 1 && line[linelen - 2] == '\r')
654 --l;
655 } else
656 l = len;
657 if (l != linelen) {
658 size_t off = (size_t)(line - hdr);
659 if (l > linelen) {
660 char* temp = (char*)realloc(hdr, hdrlen + l - linelen + 1);
661 if (!temp) {
662 retval = 0/*failure*/;
663 continue;
664 }
665 hdr = temp;
666 line = temp + off;
667 }
668 hdrlen -= linelen;
669 memmove(line + l, line + linelen, hdrlen - off + 1);
670 hdrlen += l;
671 }
672
673 if (len) {
674 if (op == eUserHeaderOp_Extend) {
675 char* s = &line[l - len - 1];
676 *s++ = ' ';
677 memcpy(s, newtagval, len);
678 } else
679 memcpy(line, newline, len);
680 linelen = l;
681 used = 1;
682 } else if (op == eUserHeaderOp_Extend)
683 used = 1;
684 }
685
686 if (op == eUserHeaderOp_Delete)
687 continue;
688
689 if (used || !len) {
690 memmove(newline, newline + newlinelen,
691 newhdrlen - (size_t)(newline-new_header) - newlinelen + 1);
692 newhdrlen -= newlinelen;
693 newlinelen = 0;
694 }
695 }
696
697 info->http_user_header = hdr;
698 if (op != eUserHeaderOp_Delete) {
699 if (!ConnNetInfo_AppendUserHeader(info, new_header))
700 retval = 0/*failure*/;
701 free(new_header);
702 }
703 return retval;
704 }
705
706
707 extern int/*bool*/ ConnNetInfo_OverrideUserHeader(SConnNetInfo* info,
708 const char* header)
709 {
710 return s_ModifyUserHeader(info, header, eUserHeaderOp_Override);
711 }
712
713
714 extern void ConnNetInfo_DeleteUserHeader(SConnNetInfo* info,
715 const char* header)
716 {
717 verify(s_ModifyUserHeader(info, header, eUserHeaderOp_Delete));
718 }
719
720
721 extern int/*bool*/ ConnNetInfo_ExtendUserHeader(SConnNetInfo* info,
722 const char* header)
723 {
724 return s_ModifyUserHeader(info, header, eUserHeaderOp_Extend);
725 }
726
727
728 extern int/*bool*/ ConnNetInfo_AppendArg(SConnNetInfo* info,
729 const char* arg,
730 const char* val)
731 {
732 size_t len, used;
733
734 if (!arg || !*arg)
735 return 1/*success*/;
736
737 used = strlen(info->args);
738 len = strlen(arg);
739
740 if (used + (used ? 1/*&*/ : 0) + len +
741 (val && *val ? 1/*=*/ + strlen(val) : 0) >= sizeof(info->args)) {
742 return 0/*failure*/;
743 }
744
745 if (used)
746 info->args[used++] = '&';
747 strcpy(info->args + used, arg);
748 if (val && *val) {
749 used += len;
750 info->args[used++] = '=';
751 strcpy(info->args + used, val);
752 }
753 return 1/*success*/;
754 }
755
756
757 extern int/*bool*/ ConnNetInfo_PrependArg(SConnNetInfo* info,
758 const char* arg,
759 const char* val)
760 {
761 size_t len, off, used;
762
763 if (!arg || !*arg)
764 return 1/*success*/;
765
766 used = strlen(info->args);
767 len = strlen(arg);
768 off = len + (val && *val ? 1/*=*/ + strlen(val) : 0) + (used? 1/*&*/ : 0);
769
770 if (off + used >= sizeof(info->args))
771 return 0/*failure*/;
772
773 if (used)
774 memmove(info->args + off, info->args, used + 1);
775 strcpy(info->args, arg);
776 if (val && *val) {
777 info->args[len++] = '=';
778 strcpy(info->args + len, val);
779 }
780 if (used)
781 info->args[off - 1] = '&';
782 return 1/*success*/;
783 }
784
785
786 extern void ConnNetInfo_DeleteArg(SConnNetInfo* info,
787 const char* arg)
788 {
789 size_t argnamelen;
790 size_t arglen;
791 char* a;
792
793 if (!arg || !(argnamelen = strcspn(arg, "=&")))
794 return;
795 for (a = info->args; *a; a += arglen) {
796 if (*a == '&')
797 a++;
798 arglen = strcspn(a, "&");
799 if (arglen < argnamelen || strncasecmp(a, arg, argnamelen) != 0 ||
800 (a[argnamelen] && a[argnamelen] != '=' && a[argnamelen] != '&'))
801 continue;
802 if (a[arglen]) {
803 arglen++; /* for intermediary args, eat '&' separator, too */
804 memmove(a, a + arglen, strlen(a + arglen) + 1);
805 } else if (a != info->args) {
806 *--a = '\0'; /* last argument in a list: remove trailing '&' */
807 } else {
808 *a = '\0'; /* last and the only argument removed */
809 }
810 arglen = 0;
811 }
812 }
813
814
815 extern void ConnNetInfo_DeleteAllArgs(SConnNetInfo* info,
816 const char* args)
817 {
818 char* temp;
819 char* arg;
820 if (!args || !*args || !(temp = strdup(args))) {
821 if (args && *args)
822 *info->args = '\0';
823 return;
824 }
825 arg = temp;
826 while (*arg) {
827 char* end = strchr(arg, '&');
828 if (!end)
829 end = arg + strlen(arg);
830 else
831 *end++ = '\0';
832 ConnNetInfo_DeleteArg(info, arg);
833 arg = end;
834 }
835 free(temp);
836 }
837
838
839 extern int/*bool*/ ConnNetInfo_PreOverrideArg(SConnNetInfo* info,
840 const char* arg,
841 const char* val)
842 {
843 if (!arg || !*arg)
844 return 1/*success*/;
845 ConnNetInfo_DeleteAllArgs(info, arg);
846 return ConnNetInfo_PrependArg(info, arg, val);
847 }
848
849
850 extern int/*bool*/ ConnNetInfo_PostOverrideArg(SConnNetInfo* info,
851 const char* arg,
852 const char* val)
853 {
854 if (!arg || !*arg)
855 return 1/*success*/;
856 ConnNetInfo_DeleteAllArgs(info, arg);
857 return ConnNetInfo_AppendArg(info, arg, val);
858 }
859
860
861 static int/*bool*/ s_IsSufficientAddress(const char* addr)
862 {
863 const char*c;
864 return (SOCK_isip(addr) ||
865 ((c = strchr(addr, '.')) != 0 && c[1] &&
866 (c = strchr(c + 2, '.')) != 0 && c[1]));
867 }
868
869
870 static const char* s_ClientAddress(const char* client_host,
871 int/*bool*/ local_host)
872 {
873 const char* c = client_host;
874 unsigned int ip;
875 char addr[80];
876 char* s;
877
878 assert(client_host);
879 strncpy0(addr, client_host, sizeof(addr) - 1);
880 if (UTIL_NcbiLocalHostName(addr) && (s = strdup(addr)) != 0)
881 client_host = s;
882 if (s_IsSufficientAddress(client_host) ||
883 !(ip = *client_host && !local_host
884 ? SOCK_gethostbyname(client_host)
885 : SOCK_GetLocalHostAddress(eDefault)) ||
886 SOCK_ntoa(ip, addr, sizeof(addr)) != 0 ||
887 !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) {
888 return client_host;
889 }
890 sprintf(s, "%s(%s)", client_host, addr);
891 if (c != client_host)
892 free((void*) client_host);
893 return s;
894 }
895
896
897 extern int/*bool*/ ConnNetInfo_SetupStandardArgs(SConnNetInfo* info,
898 const char* service)
899 {
900 static const char kService[] = "service";
901 static const char kAddress[] = "address";
902 static const char kPlatform[] = "platform";
903 int/*bool*/ local_host;
904 const char* arch;
905 const char* addr;
906
907 if (!info)
908 return 0/*failed*/;
909 if (!service || !*service) {
910 assert(0);
911 return 0/*failed*/;
912 }
913 /* Dispatcher CGI args (may sacrifice some if they don't fit altogether) */
914 if (!(arch = CORE_GetPlatform()) || !*arch)
915 ConnNetInfo_DeleteArg(info, kPlatform);
916 else
917 ConnNetInfo_PreOverrideArg(info, kPlatform, arch);
918 local_host = !info->client_host[0];
919 if (local_host &&
920 !SOCK_gethostbyaddr(0, info->client_host, sizeof(info->client_host))) {
921 SOCK_gethostname(info->client_host, sizeof(info->client_host));
922 }
923 if (!(addr = s_ClientAddress(info->client_host, local_host)) || !*addr)
924 ConnNetInfo_DeleteArg(info, kAddress);
925 else
926 ConnNetInfo_PreOverrideArg(info, kAddress, addr);
927 if (addr != info->client_host)
928 free((void*) addr);
929 if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
930 ConnNetInfo_DeleteArg(info, kPlatform);
931 if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
932 ConnNetInfo_DeleteArg(info, kAddress);
933 if (!ConnNetInfo_PreOverrideArg(info, kService, service))
934 return 0/*failed*/;
935 }
936 }
937 return 1/*succeeded*/;
938 }
939
940
941 extern SConnNetInfo* ConnNetInfo_Clone(const SConnNetInfo* info)
942 {
943 SConnNetInfo* x_info;
944 if (!info)
945 return 0;
946
947 x_info = (SConnNetInfo*) malloc(sizeof(SConnNetInfo) +
948 (info->service
949 ? strlen(info->service) + 1 : 0));
950 *x_info = *info;
951 if (info->timeout && info->timeout != kDefaultTimeout) {
952 x_info->tmo = *info->timeout;
953 x_info->timeout = &x_info->tmo;
954 }
955 if (info->service) {
956 char* s = (char*) x_info + sizeof(*x_info);
957 strcpy(s, info->service);
958 x_info->service = s;
959 }
960 x_info->http_user_header = 0;
961 ConnNetInfo_SetUserHeader(x_info, info->http_user_header);
962 x_info->http_referer = info->http_referer ? strdup(info->http_referer) : 0;
963 return x_info;
964 }
965
966
967 static const char* s_SchemeStr(EURLScheme scheme, char buf[])
968 {
969 switch (scheme) {
970 case eURL_Ftp:
971 return "ftp";
972 case eURL_File:
973 return "file";
974 case eURL_Http:
975 return "http";
976 case eURL_Https:
977 return "https";
978 case eURL_Unspec:
979 break;
980 default:
981 sprintf(buf, "?#0x%08X", (int) scheme);
982 return buf;
983 }
984 return 0;
985 }
986
987 static const char* s_PortStr(unsigned short port, char buf[])
988 {
989 if (port) {
990 sprintf(buf, "%hu", port);
991 return buf;
992 }
993 return "<default>";
994 }
995
996 static void s_SaveStringQuot(char* s, const char* name,
997 const char* str, int/*bool*/ quote)
998 {
999 sprintf(s + strlen(s), "%-16.16s: %s%s%s\n", name,
1000 str && quote ? "\"" : "",
1001 str ? str : "NULL",
1002 str && quote ? "\"" : "");
1003 }
1004
1005 static void s_SaveString(char* s, const char* name, const char* str)
1006 {
1007 s_SaveStringQuot(s, name, str, 1);
1008 }
1009
1010 static void s_SaveKeyval(char* s, const char* name, const char* str)
1011 {
1012 s_SaveStringQuot(s, name, str, 0);
1013 }
1014
1015 static void s_SaveULong(char* s, const char* name, unsigned long lll)
1016 {
1017 sprintf(s + strlen(s), "%-16.16s: %lu\n", name, lll);
1018 }
1019
1020 static void s_SaveBool(char* s, const char* name, int/*bool*/ bbb)
1021 {
1022 sprintf(s + strlen(s), "%-16.16s: %s\n", name, bbb ? "TRUE" : "FALSE");
1023 }
1024
1025 static void s_SaveUserHeader(char* s, const char* name,
1026 const char* uh, size_t uhlen)
1027 {
1028 s += strlen(s);
1029 s += sprintf(s, "%-16.16s: ", name);
1030 if (uh) {
1031 *s++ = '"';
1032 memcpy(UTIL_PrintableString(uh, uhlen, s, 0/*reduce*/), "\"\n", 3);
1033 } else
1034 memcpy(s, "NULL\n", 6);
1035 }
1036
1037 extern void ConnNetInfo_Log(const SConnNetInfo* info, LOG lg)
1038 {
1039 char scheme[32];
1040 char port[16];
1041 size_t uhlen;
1042 char* s;
1043
1044 if (!lg)
1045 return;
1046
1047 if (!info) {
1048 LOG_Write(lg, NCBI_C_ERRCODE_X, 10, eLOG_Trace, 0, 0, 0,
1049 "ConnNetInfo_Log: NULL info", 0, 0);
1050 return;
1051 }
1052
1053 uhlen = info->http_user_header ? strlen(info->http_user_header) : 0;
1054
1055 if (!(s = (char*) malloc(sizeof(*info) + 1024/*slack for all labels*/ +
1056 (info->service ? strlen(info->service) : 0) +
1057 UTIL_PrintableStringSize(info->http_user_header,
1058 uhlen) +
1059 (info->http_referer
1060 ? strlen(info->http_referer) : 0)))) {
1061 LOG_WRITE(lg, NCBI_C_ERRCODE_X, 11, eLOG_Error,
1062 "ConnNetInfo_Log: Cannot allocate temporary buffer");
1063 return;
1064 }
1065
1066 strcpy(s, "ConnNetInfo_Log\n"
1067 "#################### [BEGIN] SConnNetInfo:\n");
1068 s_SaveString (s, "service", info->service);
1069 if (*info->client_host)
1070 s_SaveString(s, "client_host", info->client_host);
1071 else
1072 s_SaveKeyval(s, "client_host", "<default>");
1073 s_SaveString (s, "scheme", s_SchemeStr(info->scheme, scheme));
1074 s_SaveString (s, "user", info->user);
1075 s_SaveString (s, "pass", info->pass);
1076 s_SaveString (s, "host", info->host);
1077 s_SaveKeyval (s, "port", s_PortStr(info->port, port));
1078 s_SaveString (s, "path", info->path);
1079 s_SaveString (s, "args", info->args);
1080 s_SaveKeyval (s, "req_method", (info->req_method == eReqMethod_Any
1081 ? "ANY"
1082 : (info->req_method
1083 == eReqMethod_Get
1084 ? "GET"
1085 : (info->req_method
1086 == eReqMethod_Post
1087 ? "POST" : "<unknown>"))));
1088 if (info->timeout) {
1089 s_SaveULong (s, "timeout(sec)", info->timeout->sec);
1090 s_SaveULong (s, "timeout(usec)", info->timeout->usec);
1091 } else
1092 s_SaveKeyval(s, "timeout", "INFINITE");
1093 s_SaveULong (s, "max_try", info->max_try);
1094 s_SaveString (s, "http_proxy_host", info->http_proxy_host);
1095 s_SaveKeyval (s, "http_proxy_port", s_PortStr(info->http_proxy_port,
1096 port));
1097 s_SaveString (s, "proxy_host", info->proxy_host);
1098 s_SaveKeyval (s, "debug_printout", (info->debug_printout
1099 == eDebugPrintout_None
1100 ? "NONE"
1101 : (info->debug_printout
1102 == eDebugPrintout_Some
1103 ? "SOME"
1104 : (info->debug_printout
1105 == eDebugPrintout_Data
1106 ? "DATA" : "<unknown>"))));
1107 s_SaveBool (s, "stateless", info->stateless);
1108 s_SaveBool (s, "firewall", info->firewall);
1109 s_SaveBool (s, "lb_disable", info->lb_disable);
1110 s_SaveUserHeader(s, "http_user_header",info->http_user_header, uhlen);
1111 s_SaveString (s, "http_referer", info->http_referer);
1112 s_SaveBool (s, "proxy_adjusted", info->http_proxy_adjusted);
1113 strcat(s, "#################### [END] SConnNetInfo\n");
1114
1115 LOG_Write(lg, NCBI_C_ERRCODE_X, 12, eLOG_Trace, 0, 0, 0, s, 0, 0);
1116 free(s);
1117 }
1118
1119
1120 extern char* ConnNetInfo_URL(const SConnNetInfo* info)
1121 {
1122 const char* scheme;
1123 size_t len;
1124 char* url;
1125
1126 if (!info || info->scheme == eURL_Unspec
1127 || !(scheme = s_Scheme(info->scheme))) {
1128 return 0;
1129 }
1130
1131 len = strlen(scheme) + 3/*"://"*/ + strlen(info->host) +
1132 (info->port ? 6/*:port*/ : 0) +
1133 (info->http_proxy_adjusted ? 2 : 0) +
1134 strlen(info->path) + 1 +
1135 (*info->args ? strlen(info->args) + 2 : 1);
1136 url = (char*) malloc(len);
1137
1138 if (url) {
1139 len = (size_t) sprintf(url, "%s://%s", scheme, info->host);
1140 if (info->port)
1141 len += sprintf(url + len, ":%hu", info->port);
1142 sprintf(url + len, "%s%s%s%s%s", info->http_proxy_adjusted
1143 ? "<" : *info->path != '/' ? "/" : "", info->path,
1144 &"?"[!*info->args || *info->args == '#'], info->args,
1145 ">" + !info->http_proxy_adjusted);
1146 }
1147 return url;
1148 }
1149
1150
1151 extern void ConnNetInfo_Destroy(SConnNetInfo* info)
1152 {
1153 if (!info)
1154 return;
1155 ConnNetInfo_SetUserHeader(info, 0);
1156 if (info->http_referer) {
1157 free((void*) info->http_referer);
1158 info->http_referer = 0;
1159 }
1160 free(info);
1161 }
1162
1163
1164
1165 /****************************************************************************
1166 * URL_Connect
1167 */
1168
1169
1170 extern EIO_Status URL_ConnectEx
1171 (const char* host,
1172 unsigned short port,
1173 const char* path,
1174 const char* args,
1175 EReqMethod req_method,
1176 size_t content_length,
1177 const STimeout* c_timeout,
1178 const STimeout* rw_timeout,
1179 const char* user_hdr,
1180 int/*bool*/ encode_args,
1181 TSOCK_Flags flags,
1182 SOCK* sock)
1183 {
1184 static const char X_REQ_Q[] = "?";
1185 static const char X_REQ_E[] = " HTTP/1.0\r\n";
1186 static const char X_HOST[] = "Host: ";
1187
1188 EIO_Status st;
1189 BUF buf;
1190 char* header;
1191 size_t hdrsize;
1192 char strbuf[80];
1193 const char* x_args = 0;
1194 size_t x_args_len = args && *args ? strcspn(args, "#") : 0;
1195 size_t user_hdr_len = user_hdr && *user_hdr ? strlen(user_hdr) : 0;
1196 const char* x_req_method; /* "POST "/"GET " */
1197
1198 /* check the args */
1199 if (!sock || !host || !*host ||
1200 (user_hdr && *user_hdr && user_hdr[user_hdr_len - 1] != '\n')) {
1201 CORE_LOG_X(2, eLOG_Error, "[URL_Connect] Bad arguments");
1202 assert(0);
1203 return eIO_InvalidArg;
1204 }
1205
1206 /* select request method and its verbal representation */
1207 if (req_method == eReqMethod_Any) {
1208 req_method = content_length ? eReqMethod_Post : eReqMethod_Get;
1209 } else if (req_method == eReqMethod_Get && content_length) {
1210 CORE_LOG_X(3, eLOG_Warning,
1211 "[URL_Connect] Content length ignored with method GET");
1212 content_length = 0;
1213 }
1214 switch (req_method) {
1215 case eReqMethod_Post:
1216 x_req_method = "POST ";
1217 break;
1218 case eReqMethod_Get:
1219 x_req_method = "GET ";
1220 break;
1221 default:
1222 CORE_LOGF_X(4, eLOG_Error,
1223 ("[URL_Connect] Unrecognized request method (#%u)",
1224 (unsigned int) req_method));
1225 assert(0);
1226 return eIO_InvalidArg;
1227 }
1228
1229 /* URL-encode "args", if any specified */
1230 if (x_args_len) {
1231 if ( encode_args ) {
1232 size_t rd_len, wr_len;
1233 size_t size = 3 * x_args_len;
1234 char* xx_args = (char*) malloc(size);
1235 if (!xx_args) {
1236 CORE_LOGF_ERRNO_X(8, eLOG_Error, errno,
1237 ("[URL_Connect] Out of memory (%lu)",
1238 (unsigned long) size));
1239 return eIO_Unknown;
1240 }
1241 URL_Encode(args, x_args_len, &rd_len, xx_args, size, &wr_len);
1242 assert(x_args_len == rd_len);
1243 x_args_len = wr_len;
1244 x_args = xx_args;
1245 } else
1246 x_args = args;
1247 }
1248
1249 buf = 0;
1250 errno = 0;
1251 if (!port) {
1252 *strbuf = '\0';
1253 port = flags & fSOCK_Secure ? 443 : 80;
1254 } else
1255 sprintf(strbuf, ":%hu", port);
1256
1257 /* compose HTTP header */
1258 if (/* {POST|GET} <path>?<args> HTTP/1.0\r\n */
1259 !BUF_Write(&buf, x_req_method, strlen(x_req_method)) ||
1260 !BUF_Write(&buf, path, strlen(path)) ||
1261 (x_args_len
1262 && (!BUF_Write(&buf, X_REQ_Q, sizeof(X_REQ_Q) - 1) ||
1263 !BUF_Write(&buf, x_args, x_args_len))) ||
1264 !BUF_Write(&buf, X_REQ_E, sizeof(X_REQ_E) - 1) ||
1265
1266 /* Host: host[:port]\r\n */
1267 !BUF_Write(&buf, X_HOST, sizeof(X_HOST) - 1) ||
1268 !BUF_Write(&buf, host, strlen(host)) ||
1269 !BUF_Write(&buf, strbuf, strlen(strbuf)) ||
1270 !BUF_Write(&buf, "\r\n", 2) ||
1271
1272 /* <user_header> */
1273 (user_hdr_len
1274 && !BUF_Write(&buf, user_hdr, user_hdr_len)) ||
1275
1276 /* Content-Length: <content_length>\r\n\r\n */
1277 (req_method != eReqMethod_Get
1278 && (sprintf(strbuf, "Content-Length: %lu\r\n",
1279 (unsigned long) content_length) <= 0 ||
1280 !BUF_Write(&buf, strbuf, strlen(strbuf)))) ||
1281
1282 !BUF_Write(&buf, "\r\n", 2)) {
1283 int x_errno = errno;
1284 CORE_LOGF_ERRNO_X(5, eLOG_Error, x_errno,
1285 ("[URL_Connect] Error building HTTP header for"
1286 " %s:%hu", host, port));
1287 BUF_Destroy(buf);
1288 if (x_args && x_args != args)
1289 free((void*) x_args);
1290 return eIO_Unknown;
1291 }
1292 if (x_args && x_args != args)
1293 free((void*) x_args);
1294
1295 if (!(header = (char*) malloc(hdrsize = BUF_Size(buf)))
1296 || BUF_Read(buf, header, hdrsize) != hdrsize) {
1297 int x_errno = errno;
1298 CORE_LOGF_ERRNO_X(6, eLOG_Error, x_errno,
1299 ("[URL_Connect] Error storing HTTP header for"
1300 " %s:%hu", host, port));
1301 if (header)
1302 free(header);
1303 BUF_Destroy(buf);
1304 return eIO_Unknown;
1305 }
1306 BUF_Destroy(buf);
1307
1308 /* connect to HTTPD */
1309 st = SOCK_CreateEx(host, port, c_timeout, sock, header, hdrsize, flags);
1310 free(header);
1311 if (st != eIO_Success) {
1312 char temp[80];
1313 assert(!*sock);
1314 if (st == eIO_Timeout && c_timeout) {
1315 sprintf(temp, "[%u.%06u]",
1316 (unsigned int)(c_timeout->sec + c_timeout->usec/1000000),
1317 (unsigned int) (c_timeout->usec%1000000));
1318 } else
1319 *temp = '\0';
1320 CORE_LOGF_X(7, eLOG_Error,
1321 ("[URL_Connect] Socket connect to %s:%hu failed: %s%s",
1322 host, port, IO_StatusStr(st), temp));
1323 } else
1324 verify(SOCK_SetTimeout(*sock, eIO_ReadWrite, rw_timeout)==eIO_Success);
1325 return st;
1326 }
1327
1328
1329 extern SOCK URL_Connect
1330 (const char* host,
1331 unsigned short port,
1332 const char* path,
1333 const char* args,
1334 EReqMethod req_method,
1335 size_t content_length,
1336 const STimeout* c_timeout,
1337 const STimeout* rw_timeout,
1338 const char* user_hdr,
1339 int/*bool*/ encode_args,
1340 TSOCK_Flags flags)
1341 {
1342 SOCK sock;
1343 EIO_Status st = URL_ConnectEx(host, port, path, args,
1344 req_method, content_length,
1345 c_timeout, rw_timeout,
1346 user_hdr, encode_args, flags, &sock);
1347 return st == eIO_Success ? sock : 0;
1348 }
1349
1350
1351
1352 /****************************************************************************
1353 * StripToPattern()
1354 */
1355
1356
1357 typedef EIO_Status (*FDoIO)
1358 (void* stream,
1359 void* buf,
1360 size_t size,
1361 size_t* n_read,
1362 EIO_Event what /* eIO_Read | eIO_Write (to pushback) */
1363 );
1364
1365 static EIO_Status s_StripToPattern
1366 (void* stream,
1367 FDoIO io_func,
1368 const void* pattern,
1369 size_t pattern_size,
1370 BUF* buf,
1371 size_t* n_discarded)
1372 {
1373 EIO_Status status;
1374 char* buffer;
1375 size_t buffer_size;
1376 size_t n_read = 0;
1377
1378 /* check args */
1379 if ( n_discarded )
1380 *n_discarded = 0;
1381 if (!stream || (pattern != 0) != (pattern_size != 0))
1382 return eIO_InvalidArg;
1383
1384 /* allocate a temporary read buffer */
1385 buffer_size = 2 * pattern_size;
1386 if (buffer_size < 4096)
1387 buffer_size = 4096;
1388 if ( !(buffer = (char*) malloc(buffer_size)) )
1389 return eIO_Unknown;
1390
1391 if ( !pattern ) {
1392 /* read/discard until EOF */
1393 do {
1394 status = io_func(stream, buffer, buffer_size, &n_read, eIO_Read);
1395 if ( buf )
1396 BUF_Write(buf, buffer, n_read);
1397 if ( n_discarded )
1398 *n_discarded += n_read;
1399 } while (status == eIO_Success);
1400 } else {
1401 for (;;) {
1402 /* read; search for the pattern; store the discarded data */
1403 size_t x_read, n_stored;
1404
1405 assert(n_read < pattern_size);
1406 status = io_func(stream, buffer + n_read, buffer_size - n_read,
1407 &x_read, eIO_Read);
1408 if ( !x_read ) {
1409 assert(status != eIO_Success);
1410 break; /*error*/
1411 }
1412 n_stored = n_read + x_read;
1413
1414 if (n_stored >= pattern_size) {
1415 /* search for the pattern */
1416 size_t n_check = n_stored - pattern_size + 1;
1417 const char* b;
1418 for (b = buffer; n_check; b++, n_check--) {
1419 if (*b != *((const char*) pattern))
1420 continue;
1421 if (memcmp(b, pattern, pattern_size) == 0)
1422 break; /*found*/
1423 }
1424 /* pattern found */
1425 if ( n_check ) {
1426 size_t x_discarded = (size_t)(b - buffer) + pattern_size;
1427 if ( buf )
1428 BUF_Write(buf, buffer + n_read, x_discarded - n_read);
1429 if ( n_discarded )
1430 *n_discarded += x_discarded;
1431 /* return unused portion to the stream */
1432 status = io_func(stream, buffer + x_discarded,
1433 n_stored - x_discarded, 0, eIO_Write);
1434 break; /*finished*/
1435 }
1436 }
1437
1438 /* pattern not found yet */
1439 if ( buf )
1440 BUF_Write(buf, buffer + n_read, x_read);
1441 if ( n_discarded )
1442 *n_discarded += x_read;
1443 n_read = n_stored;
1444
1445 if (n_read > pattern_size) {
1446 size_t n_cut = n_read - pattern_size + 1;
1447 n_read = pattern_size - 1;
1448 memmove(buffer, buffer + n_cut, n_read);
1449 }
1450 }
1451 }
1452
1453 /* cleanup & exit */
1454 free(buffer);
1455 return status;
1456 }
1457
1458
1459 static EIO_Status s_CONN_IO
1460 (void* stream,
1461 void* buf,
1462 size_t size,
1463 size_t* n_read,
1464 EIO_Event what)
1465 {
1466 switch (what) {
1467 case eIO_Read:
1468 return CONN_Read((CONN) stream, buf, size, n_read, eIO_ReadPlain);
1469 case eIO_Write:
1470 assert(stream);
1471 return CONN_PushBack((CONN) stream, buf, size);
1472 default:
1473 break;
1474 }
1475 return eIO_InvalidArg;
1476 }
1477
1478 extern EIO_Status CONN_StripToPattern
1479 (CONN conn,
1480 const void* pattern,
1481 size_t pattern_size,
1482 BUF* buf,
1483 size_t* n_discarded)
1484 {
1485 return s_StripToPattern
1486 (conn, s_CONN_IO, pattern, pattern_size, buf, n_discarded);
1487 }
1488
1489
1490 static EIO_Status s_SOCK_IO
1491 (void* stream,
1492 void* buf,
1493 size_t size,
1494 size_t* n_read,
1495 EIO_Event what)
1496 {
1497 switch (what) {
1498 case eIO_Read:
1499 return SOCK_Read((SOCK) stream, buf, size, n_read, eIO_ReadPlain);
1500 case eIO_Write:
1501 return SOCK_PushBack((SOCK) stream, buf, size);
1502 default:
1503 break;
1504 }
1505 return eIO_InvalidArg;
1506 }
1507
1508 extern EIO_Status SOCK_StripToPattern
1509 (SOCK sock,
1510 const void* pattern,
1511 size_t pattern_size,
1512 BUF* buf,
1513 size_t* n_discarded)
1514 {
1515 return s_StripToPattern
1516 (sock, s_SOCK_IO, pattern, pattern_size, buf, n_discarded);
1517 }
1518
1519
1520 static EIO_Status s_BUF_IO
1521 (void* stream,
1522 void* buf,
1523 size_t size,
1524 size_t* n_read,
1525 EIO_Event what)
1526 {
1527 BUF b;
1528 switch (what) {
1529 case eIO_Read:
1530 *n_read = BUF_Read((BUF) stream, buf, size);
1531 return *n_read ? eIO_Success : eIO_Closed;
1532 case eIO_Write:
1533 assert(stream);
1534 b = (BUF) stream;
1535 return BUF_PushBack(&b, buf, size) ? eIO_Success : eIO_Unknown;
1536 default:
1537 break;
1538 }
1539 return eIO_InvalidArg;
1540 }
1541
1542 extern EIO_Status BUF_StripToPattern
1543 (BUF buffer,
1544 const void* pattern,
1545 size_t pattern_size,
1546 BUF* buf,
1547 size_t* n_discarded)
1548 {
1549 return s_StripToPattern
1550 (buffer, s_BUF_IO, pattern, pattern_size, buf, n_discarded);
1551 }
1552
1553
1554
1555
1556 /****************************************************************************
1557 * URL- Encoding/Decoding
1558 */
1559
1560
1561 /* Return integer (0..15) corresponding to the "ch" as a hex digit
1562 * Return -1 on error
1563 */
1564 static int s_HexChar(char ch)
1565 {
1566 unsigned int rc = ch - '0';
1567 if (rc <= 9)
1568 return rc;
1569 rc = (ch | ' ') - 'a';
1570 return rc <= 5 ? (int) rc + 10 : -1;
1571 }
1572
1573
1574 /* The URL-encoding table
1575 */
1576 static const char s_EncodeTable[256][4] = {
1577 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
1578 "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
1579 "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
1580 "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
1581 "+", "!", "%22", "%23", "$", "%25", "%26", "'",
1582 "(", ")", "*", "%2B", ",", "-", ".", "%2F",
1583 "0", "1", "2", "3", "4", "5", "6", "7",
1584 "8", "9", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
1585 "%40", "A", "B", "C", "D", "E", "F", "G",
1586 "H", "I", "J", "K", "L", "M", "N", "O",
1587 "P", "Q", "R", "S", "T", "U", "V", "W",
1588 "X", "Y", "Z", "%5B", "%5C", "%5D", "%5E", "_",
1589 "%60", "a", "b", "c", "d", "e", "f", "g",
1590 "h", "i", "j", "k", "l", "m", "n", "o",
1591 "p", "q", "r", "s", "t", "u", "v", "w",
1592 "x", "y", "z", "%7B", "%7C", "%7D", "%7E", "%7F",
1593 "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
1594 "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
1595 "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
1596 "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
1597 "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
1598 "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
1599 "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
1600 "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
1601 "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
1602 "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
1603 "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
1604 "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
1605 "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
1606 "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
1607 "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
1608 "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
1609 };
1610
1611 #define VALID_URL_SYMBOL(ch) (s_EncodeTable[(unsigned char)ch][0] != '%')
1612
1613
1614 extern int/*bool*/ URL_DecodeEx
1615 (const void* src_buf,
1616 size_t src_size,
1617 size_t* src_read,
1618 void* dst_buf,
1619 size_t dst_size,
1620 size_t* dst_written,
1621 const char* allow_symbols)
1622 {
1623 unsigned char* src = (unsigned char*) src_buf;
1624 unsigned char* dst = (unsigned char*) dst_buf;
1625
1626 *src_read = 0;
1627 *dst_written = 0;
1628 if (!src_size || !dst_size)
1629 return 1/*true*/;
1630
1631 for ( ; *src_read != src_size && *dst_written != dst_size;
1632 (*src_read)++, (*dst_written)++, src++, dst++) {
1633 switch ( *src ) {
1634 case '%': {
1635 int i1, i2;
1636 if (*src_read + 2 > src_size)
1637 return 1/*true*/;
1638 if ((i1 = s_HexChar(*(++src))) == -1)
1639 return *dst_written ? 1/*true*/ : 0/*false*/;
1640 if (*src_read + 3 > src_size)
1641 return 1/*true*/;
1642 if ((i2 = s_HexChar(*(++src))) == -1)
1643 return *dst_written ? 1/*true*/ : 0/*false*/;
1644
1645 *dst = (unsigned char)((i1 << 4) + i2);
1646 *src_read += 2;
1647 break;
1648 }
1649 case '+': {
1650 *dst = ' ';
1651 break;
1652 }
1653 default: {
1654 if (VALID_URL_SYMBOL(*src) ||
1655 (allow_symbols && strchr(allow_symbols, *src)))
1656 *dst = *src;
1657 else
1658 return *dst_written ? 1/*true*/ : 0/*false*/;
1659 }
1660 }/*switch*/
1661 }
1662
1663 assert(src == (unsigned char*) src_buf + *src_read );
1664 assert(dst == (unsigned char*) dst_buf + *dst_written);
1665 return 1/*true*/;
1666 }
1667
1668
1669 extern int/*bool*/ URL_Decode
1670 (const void* src_buf,
1671 size_t src_size,
1672 size_t* src_read,
1673 void* dst_buf,
1674 size_t dst_size,
1675 size_t* dst_written)
1676 {
1677 return URL_DecodeEx
1678 (src_buf, src_size, src_read, dst_buf, dst_size, dst_written, 0);
1679 }
1680
1681
1682 extern void URL_Encode
1683 (const void* src_buf,
1684 size_t src_size,
1685 size_t* src_read,
1686 void* dst_buf,
1687 size_t dst_size,
1688 size_t* dst_written)
1689 {
1690 unsigned char* src = (unsigned char*) src_buf;
1691 unsigned char* dst = (unsigned char*) dst_buf;
1692
1693 *src_read = 0;
1694 *dst_written = 0;
1695 if (!src_size || !dst_size)
1696 return;
1697
1698 for ( ; *src_read != src_size && *dst_written != dst_size;
1699 (*src_read)++, (*dst_written)++, src++, dst++) {
1700 const char* subst = s_EncodeTable[*src];
1701 if (*subst != '%') {
1702 *dst = *subst;
1703 } else if (*dst_written < dst_size - 2) {
1704 *dst = '%';
1705 *(++dst) = *(++subst);
1706 *(++dst) = *(++subst);
1707 *dst_written += 2;
1708 } else {
1709 return;
1710 }
1711 }
1712 assert(src == (unsigned char*) src_buf + *src_read );
1713 assert(dst == (unsigned char*) dst_buf + *dst_written);
1714 }
1715
1716
1717 /****************************************************************************
1718 * NCBI-specific MIME content type and sub-types
1719 */
1720
1721
1722 static const char* s_MIME_Type[eMIME_T_Unknown+1] = {
1723 "x-ncbi-data",
1724 "text",
1725 "application",
1726 "unknown"
1727 };
1728
1729 static const char* s_MIME_SubType[eMIME_Unknown+1] = {
1730 "x-dispatch",
1731 "x-asn-text",
1732 "x-asn-binary",
1733 "x-fasta",
1734 "x-www-form",
1735 "html",
1736 "plain",
1737 "xml",
1738 "xml+soap",
1739 "octet-stream",
1740 "x-unknown"
1741 };
1742
1743 static const char* s_MIME_Encoding[eENCOD_Unknown+1] = {
1744 "",
1745 "urlencoded",
1746 "encoded"
1747 };
1748
1749
1750 extern char* MIME_ComposeContentTypeEx
1751 (EMIME_Type type,
1752 EMIME_SubType subtype,
1753 EMIME_Encoding encoding,
1754 char* buf,
1755 size_t buflen)
1756 {
1757 static const char s_ContentType[] = "Content-Type: ";
1758 const char* x_type;
1759 const char* x_subtype;
1760 const char* x_encoding;
1761 char x_buf[MAX_CONTENT_TYPE_LEN];
1762
1763 assert(buf && buflen);
1764
1765 if (type == eMIME_T_Undefined || subtype == eMIME_Undefined)
1766 return 0;
1767 if (type >= eMIME_T_Unknown)
1768 type = eMIME_T_Unknown;
1769 if (subtype >= eMIME_Unknown)
1770 subtype = eMIME_Unknown;
1771 if (encoding >= eENCOD_Unknown)
1772 encoding = eENCOD_Unknown;
1773
1774 x_type = s_MIME_Type [type];
1775 x_subtype = s_MIME_SubType [subtype];
1776 x_encoding = s_MIME_Encoding[encoding];
1777
1778 if ( *x_encoding ) {
1779 assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
1780 + strlen(x_encoding) + 4 < MAX_CONTENT_TYPE_LEN);
1781 sprintf(x_buf, "%s%s/%s-%s\r\n",
1782 s_ContentType, x_type, x_subtype, x_encoding);
1783 } else {
1784 assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
1785 + 3 < MAX_CONTENT_TYPE_LEN);
1786 sprintf(x_buf, "%s%s/%s\r\n", s_ContentType, x_type, x_subtype);
1787 }
1788 assert(strlen(x_buf) < sizeof(x_buf));
1789 assert(strlen(x_buf) < buflen);
1790 strncpy0(buf, x_buf, buflen - 1);
1791 return buf;
1792 }
1793
1794
1795 extern int/*bool*/ MIME_ParseContentTypeEx
1796 (const char* str,
1797 EMIME_Type* type,
1798 EMIME_SubType* subtype,
1799 EMIME_Encoding* encoding)
1800 {
1801 char* x_buf;
1802 size_t x_size;
1803 char* x_type;
1804 char* x_subtype;
1805 int i;
1806
1807 if ( type )
1808 *type = eMIME_T_Undefined;
1809 if ( subtype )
1810 *subtype = eMIME_Undefined;
1811 if ( encoding )
1812 *encoding = eENCOD_None;
1813
1814 x_size = str && *str ? strlen(str) + 1 : 0;
1815 if (!x_size)
1816 return 0/*false*/;
1817
1818 if (!(x_buf = (char*) malloc(x_size << 1)))
1819 return 0/*false*/;
1820 x_type = x_buf + x_size;
1821
1822 strlwr(strcpy(x_buf, str));
1823
1824 if ((sscanf(x_buf, " content-type: %s ", x_type) != 1 &&
1825 sscanf(x_buf, " %s ", x_type) != 1) ||
1826 (x_subtype = strchr(x_type, '/')) == 0) {
1827 free(x_buf);
1828 return 0/*false*/;
1829 }
1830 *x_subtype++ = '\0';
1831 x_size = strlen(x_subtype);
1832
1833 if ( type ) {
1834 for (i = 0; i < (int) eMIME_T_Unknown; i++) {
1835 if (strcmp(x_type, s_MIME_Type[i]) == 0)
1836 break;
1837 }
1838 *type = (EMIME_Type) i;
1839 }
1840
1841 for (i = 1; i <= (int) eENCOD_Unknown; i++) {
1842 size_t len = strlen(s_MIME_Encoding[i]);
1843 if (len < x_size) {
1844 char* x_encoding = x_subtype + x_size - len;
1845 if (x_encoding[-1] == '-'
1846 && strcmp(x_encoding, s_MIME_Encoding[i]) == 0) {
1847 if ( encoding ) {
1848 *encoding = (i == (int) eENCOD_Unknown
1849 ? eENCOD_None : (EMIME_Encoding) i);
1850 }
1851 x_encoding[-1] = '\0';
1852 break;
1853 }
1854 }
1855 }
1856
1857 if ( subtype ) {
1858 for (i = 0; i < (int) eMIME_Unknown; i++) {
1859 if (strcmp(x_subtype, s_MIME_SubType[i]) == 0)
1860 break;
1861 }
1862 *subtype = (EMIME_SubType) i;
1863 }
1864
1865 free(x_buf);
1866 return 1/*true*/;
1867 }
1868
1869
1870 /* DEPRECATED and scheduled for removal */
1871 extern char* MIME_ComposeContentType
1872 (EMIME_SubType subtype,
1873 EMIME_Encoding encoding,
1874 char* buf,
1875 size_t buflen)
1876 {
1877 return MIME_ComposeContentTypeEx(eMIME_T_NcbiData,
1878 subtype, encoding, buf, buflen);
1879 }
1880
1881
1882 /* DEPRECATED and scheduled for removal */
1883 extern int/*bool*/ MIME_ParseContentType
1884 (const char* str,
1885 EMIME_SubType* subtype,
1886 EMIME_Encoding* encoding)
1887 {
1888 EMIME_Type type;
1889 if ( !MIME_ParseContentTypeEx(str, &type, subtype, encoding) )
1890 return 0/*false*/;
1891
1892 if (type != eMIME_T_NcbiData) {
1893 if ( subtype )
1894 *subtype = eMIME_Unknown;
1895 if ( encoding )
1896 *encoding = eENCOD_Unknown;
1897 return 0/*false*/;
1898 }
1899
1900 return 1/*true*/;
1901 }
1902 |
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more information. |