NCBI C Toolkit Cross Reference

C/connect/ncbi_connutil.c


  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 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.