src/connect/ncbi_connutil.c

Go to the documentation of this file.
00001 /* $Id: ncbi_connutil.c 174740 2009-10-30 14:36:13Z lavr $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Author:  Denis Vakatov, Anton Lavrentiev
00027  *
00028  * File Description:
00029  *   Auxiliary API, mostly CONN-, URL-, and MIME-related
00030  *   (see in "ncbi_connutil.h" for more details).
00031  *
00032  */
00033 
00034 #include "ncbi_ansi_ext.h"
00035 #include "ncbi_priv.h"
00036 #include <connect/ncbi_connutil.h>
00037 #include <ctype.h>
00038 #include <errno.h>
00039 #include <stdlib.h>
00040 
00041 #define NCBI_USE_ERRCODE_X   Connect_Util
00042 
00043 
00044 extern const char* ConnNetInfo_GetValue(const char* service, const char* param,
00045                                         char* value, size_t value_size,
00046                                         const char* def_value)
00047 {
00048     char        buf[128];
00049     const char* val;
00050     char*       s;
00051 
00052     if (!value  ||  value_size <= 0)
00053         return 0;
00054     *value = '\0';
00055 
00056     if (!param  ||  !*param)
00057         return 0;
00058 
00059     if (service  &&  *service) {
00060         /* Service-specific inquiry */
00061         size_t slen = strlen(service);
00062         size_t plen = strlen(param) + 1;
00063         if (slen + 1 + sizeof(DEF_CONN_REG_SECTION) + plen > sizeof(buf))
00064             return 0;
00065 
00066         /* First, environment search for 'service_CONN_param' */
00067         s = (char*) memcpy(buf, service, slen) + slen;
00068         *s++ = '_';
00069         memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
00070         s += sizeof(DEF_CONN_REG_SECTION) - 1;
00071         *s++ = '_';
00072         memcpy(s, param, plen);
00073         if ((val = getenv(buf)) != 0  ||  (val = getenv(strupr(buf))) != 0)
00074             return strncpy0(value, val, value_size - 1);
00075 
00076         /* Next, search for 'CONN_param' in '[service]' registry section */
00077         buf[slen++] = '\0';
00078         s = buf + slen;
00079         CORE_REG_GET(buf, s, value, value_size, 0);
00080         if (*value)
00081             return value;
00082     } else {
00083         /* Common case. Form 'CONN_param' */
00084         size_t plen = strlen(param) + 1;
00085         if (sizeof(DEF_CONN_REG_SECTION) + plen > sizeof(buf))
00086             return 0;
00087         s = buf;
00088         memcpy(s, DEF_CONN_REG_SECTION, sizeof(DEF_CONN_REG_SECTION) - 1);
00089         s += sizeof(DEF_CONN_REG_SECTION) - 1;
00090         *s++ = '_';
00091         memcpy(s, param, plen);
00092         s = strupr(buf);
00093     }
00094 
00095     /* Environment search for 'CONN_param' */
00096     if ((val = getenv(s)) != 0)
00097         return strncpy0(value, val, value_size - 1);
00098 
00099     /* Last resort: Search for 'param' in default registry section */
00100     s += sizeof(DEF_CONN_REG_SECTION);
00101     CORE_REG_GET(DEF_CONN_REG_SECTION, s, value, value_size, def_value);
00102     return value;
00103 }
00104 
00105 
00106 static EURLScheme s_ParseScheme(const char* str, size_t len)
00107 {
00108     if (len == 5  &&  strncasecmp(str, "https", len) == 0)
00109         return eURL_Https;
00110     if (len == 4  &&  strncasecmp(str, "http",  len) == 0)
00111         return eURL_Http;
00112     if (len == 4  &&  strncasecmp(str, "file",  len) == 0)
00113         return eURL_File;
00114     if (len == 3  &&  strncasecmp(str, "ftp",   len) == 0)
00115         return eURL_Ftp;
00116     return eURL_Unspec;
00117 }
00118 
00119 
00120 static const char* s_Scheme(EURLScheme scheme)
00121 {
00122     switch (scheme) {
00123     case eURL_Unspec:
00124         break;
00125     case eURL_Https:
00126         return "https";
00127     case eURL_Http:
00128         return "http";
00129     case eURL_File:
00130         return "file";
00131     case eURL_Ftp:
00132         return "ftp";
00133     }
00134     return 0;
00135 }
00136 
00137 
00138 /****************************************************************************
00139  * ConnNetInfo API
00140  */
00141 
00142 
00143 extern SConnNetInfo* ConnNetInfo_Create(const char* service)
00144 {
00145 #define REG_VALUE(name, value, def_value) \
00146     ConnNetInfo_GetValue(service, name, value, sizeof(value), def_value)
00147 
00148     SConnNetInfo* info = (SConnNetInfo*) malloc(sizeof(*info) +
00149                                                 (service  &&  *service
00150                                                  ? strlen(service) + 1 : 0));
00151     size_t len;
00152     /* aux. storage for the string-to-int conversions, etc. */
00153     char   str[1024];
00154     int    val;
00155     double dbl;
00156     char*  e;
00157 
00158     if (!info)
00159         return 0/*failure*/;
00160 
00161     /* client host: default */
00162     info->client_host[0] = '\0';
00163 
00164     /* scheme */
00165     REG_VALUE(REG_CONN_SCHEME, str, DEF_CONN_SCHEME);
00166     info->scheme = s_ParseScheme(str, strlen(str));
00167 
00168     /* username */
00169     REG_VALUE(REG_CONN_USER, info->user, DEF_CONN_USER);
00170 
00171     /* password */
00172     REG_VALUE(REG_CONN_PASS, info->pass, DEF_CONN_PASS);
00173 
00174     /* hostname */
00175     REG_VALUE(REG_CONN_HOST, info->host, DEF_CONN_HOST);
00176 
00177     /* port # */
00178     REG_VALUE(REG_CONN_PORT, str, DEF_CONN_PORT);
00179     errno = 0;
00180     if (*str  &&  (val = strtoul(str, &e, 10)) > 0  &&  !errno
00181         &&  !*e  &&  val < (1 << 16)) {
00182         info->port = val;
00183     } else
00184         info->port = 0/*default*/;
00185 
00186     /* path */
00187     REG_VALUE(REG_CONN_PATH, info->path, DEF_CONN_PATH);
00188 
00189     /* args */
00190     REG_VALUE(REG_CONN_ARGS, info->args, DEF_CONN_ARGS);
00191 
00192     /* request method */
00193     REG_VALUE(REG_CONN_REQ_METHOD, str, DEF_CONN_REQ_METHOD);
00194     if (!*str  ||  strcasecmp(str, "ANY") == 0)
00195         info->req_method = eReqMethod_Any;
00196     else if (strcasecmp(str, "POST") == 0)
00197         info->req_method = eReqMethod_Post;
00198     else if (strcasecmp(str, "GET") == 0)
00199         info->req_method = eReqMethod_Get;
00200 
00201     /* connection timeout */
00202     REG_VALUE(REG_CONN_TIMEOUT, str, 0);
00203     len = strlen(str);
00204     if (len > 2  &&  len < 9  &&  strncasecmp(str, "infinite", len) == 0) {
00205         info->timeout = 0;
00206     } else {
00207         info->timeout = &info->tmo;
00208         if (!*str  ||  (dbl = atof(str)) < 0.0)
00209             dbl = DEF_CONN_TIMEOUT;
00210         info->timeout->sec  = (unsigned int) dbl;
00211         info->timeout->usec = (unsigned int)
00212             ((dbl - info->timeout->sec) * 1000000.0);
00213     }
00214 
00215     /* max. # of attempts to establish connection */
00216     REG_VALUE(REG_CONN_MAX_TRY, str, 0);
00217     val = atoi(str);
00218     info->max_try = (unsigned short)(val > 0 ? val : DEF_CONN_MAX_TRY);
00219 
00220     /* HTTP proxy server? */
00221     REG_VALUE(REG_CONN_HTTP_PROXY_HOST, info->http_proxy_host,
00222               DEF_CONN_HTTP_PROXY_HOST);
00223     if (*info->http_proxy_host) {
00224         /* yes, use the specified HTTP proxy server */
00225         REG_VALUE(REG_CONN_HTTP_PROXY_PORT, str, DEF_CONN_HTTP_PROXY_PORT);
00226         errno = 0;
00227         if (*str  &&  (val = strtoul(str, &e, 10)) > 0
00228             &&  !errno  &&  !*e  &&  val < (1 << 16)) {
00229             info->http_proxy_port = val;
00230         } else
00231             info->http_proxy_port = 0/*default*/;
00232     } else
00233         info->http_proxy_port = 0;
00234 
00235     /* non-transparent CERN-like firewall proxy server? */
00236     REG_VALUE(REG_CONN_PROXY_HOST, info->proxy_host, DEF_CONN_PROXY_HOST);
00237 
00238     /* turn on debug printout? */
00239     REG_VALUE(REG_CONN_DEBUG_PRINTOUT, str, DEF_CONN_DEBUG_PRINTOUT);
00240     if (*str  &&  (strcmp    (str, "1")    == 0  ||
00241                    strcasecmp(str, "on")   == 0  ||
00242                    strcasecmp(str, "yes")  == 0  ||
00243                    strcasecmp(str, "true") == 0  ||
00244                    strcasecmp(str, "some") == 0)) {
00245         info->debug_printout = eDebugPrintout_Some;
00246     } else if (*str  &&  (strcasecmp(str, "all")  == 0  ||
00247                           strcasecmp(str, "data") == 0)) {
00248         info->debug_printout = eDebugPrintout_Data;
00249     } else
00250         info->debug_printout = eDebugPrintout_None;
00251 
00252     /* stateless client? */
00253     REG_VALUE(REG_CONN_STATELESS, str, DEF_CONN_STATELESS);
00254     info->stateless = (*str  &&  (strcmp    (str, "1")    == 0  ||
00255                                   strcasecmp(str, "on")   == 0  ||
00256                                   strcasecmp(str, "yes")  == 0  ||
00257                                   strcasecmp(str, "true") == 0));
00258 
00259     /* firewall mode? */
00260     REG_VALUE(REG_CONN_FIREWALL, str, DEF_CONN_FIREWALL);
00261     info->firewall = (*str  &&  (strcmp    (str, "1")    == 0  ||
00262                                  strcasecmp(str, "on")   == 0  ||
00263                                  strcasecmp(str, "yes")  == 0  ||
00264                                  strcasecmp(str, "true") == 0));
00265 
00266     /* prohibit the use of local load balancer? */
00267     REG_VALUE(REG_CONN_LB_DISABLE, str, DEF_CONN_LB_DISABLE);
00268     info->lb_disable = (*str  &&  (strcmp    (str, "1")    == 0  ||
00269                                    strcasecmp(str, "on"  ) == 0  ||
00270                                    strcasecmp(str, "yes" ) == 0  ||
00271                                    strcasecmp(str, "true") == 0));
00272 
00273     /* user header (with optional '\r\n' added automagically) */
00274     REG_VALUE(REG_CONN_HTTP_USER_HEADER, str, DEF_CONN_HTTP_USER_HEADER);
00275     if (*str) {
00276         size_t len = strlen(str);
00277         if (str[len - 1] != '\n'  &&  len < sizeof(str) - 2) {
00278             str[len++] = '\r';
00279             str[len++] = '\n';
00280             str[len]   = '\0';
00281         }
00282         info->http_user_header = strdup(str);
00283     } else
00284         info->http_user_header = 0;
00285 
00286     /* default referer */
00287     ConnNetInfo_GetValue(0, REG_CONN_HTTP_REFERER, str, sizeof(str),
00288                          DEF_CONN_HTTP_REFERER);
00289     info->http_referer = *str ? strdup(str) : 0;
00290 
00291     /* not adjusted yet... */
00292     info->http_proxy_adjusted = 0/*false*/;
00293     /* store the service name for which this structure has been created */
00294     info->service = (service  &&  *service
00295                      ? strcpy((char*) info + sizeof(*info), service)
00296                      : 0);
00297 
00298     /* done */
00299     return info;
00300 #undef REG_VALUE
00301 }
00302 
00303 
00304 extern int/*bool*/ ConnNetInfo_AdjustForHttpProxy(SConnNetInfo* info)
00305 {
00306     char   port[10];
00307     size_t hostlen;
00308     size_t portlen;
00309     size_t pathlen;
00310     size_t len;
00311 
00312     if (!info)
00313         return 0/*false*/;
00314 
00315     if (!*info->http_proxy_host  ||  info->http_proxy_adjusted)
00316         return 1/*true*/;
00317 
00318     if (info->scheme != eURL_Unspec  &&  info->scheme != eURL_Http) {
00319         CORE_LOGF_X(13, eLOG_Error,
00320                     ("[ConnNetInfo_AdjustForHttpProxy] "
00321                      " Cannot adjust for scheme \"%s\"",
00322                      s_Scheme(info->scheme)));
00323         assert(0);
00324         return 0/*false*/;
00325     }
00326 
00327     hostlen = strlen(info->host);
00328     portlen = info->port ? (size_t) sprintf(port, ":%hu", info->port) : 0;
00329     if (*info->path != '/')
00330         port[portlen++] = '/';
00331     pathlen = strlen(info->path);
00332 
00333     len = 7/*http://*/ + hostlen + portlen + pathlen + 1;
00334 
00335     if (len >= sizeof(info->path)) {
00336         CORE_LOGF_X(14, eLOG_Error,
00337                     ("[ConnNetInfo_AdjustForHttpProxy] "
00338                      "  Adjusted path too long (%lu)",
00339                      (unsigned long) len));
00340         assert(0);
00341         return 0/*false*/;
00342     }
00343     len -= pathlen + 1;
00344     memmove(info->path + len, info->path, pathlen + 1);
00345     len -= portlen;
00346     memcpy (info->path + len, port,       portlen);
00347     assert(len - hostlen == 7);
00348     memcpy (info->path + 7,   info->host, hostlen);
00349     memcpy (info->path,       "http://",  7);
00350 
00351     assert(sizeof(info->host) >= sizeof(info->http_proxy_host));
00352     strncpy0(info->host, info->http_proxy_host, sizeof(info->host) - 1);
00353     info->port = info->http_proxy_port;
00354     info->http_proxy_adjusted = 1/*true*/;
00355     info->scheme = eURL_Http;
00356     return 1/*true*/;
00357 }
00358 
00359 
00360 extern int/*bool*/ ConnNetInfo_ParseURL(SConnNetInfo* info, const char* url)
00361 {
00362     const char *s, *a;
00363     size_t len;
00364     char* p;
00365 
00366     if (info->http_proxy_adjusted) {
00367         /* undo proxy adjustment */
00368         SConnNetInfo* temp = ConnNetInfo_Create(info->service);
00369         if (!ConnNetInfo_ParseURL(temp, info->path)) {
00370             ConnNetInfo_Destroy(temp);
00371             return 0/*failure*/;
00372         }
00373         memcpy(info->host, temp->host, sizeof(info->host));
00374         info->port = temp->port;
00375         memcpy(info->path, temp->path, sizeof(info->path));
00376         ConnNetInfo_Destroy(temp);
00377         info->http_proxy_adjusted = 0/*false*/;
00378     }
00379 
00380     /* "user:pass@host:port" first [any optional] */
00381     if ((s = strstr(url, "://")) != 0) {
00382         const char* h = s + 3; /*host starts here*/
00383 
00384         len = (size_t)(s - url);
00385         if ((info->scheme = s_ParseScheme(url, len)) == eURL_Unspec)
00386             return 0/*failure*/;
00387 
00388         /* find the end of host spec */
00389         len = strcspn(h, "/?#");
00390         s = h + len;
00391 
00392         /* username:password */
00393         if (!(a = (const char*) memchr(h, '@', len))) {
00394             info->user[0] = '\0';
00395             info->pass[0] = '\0';
00396         } else {
00397             size_t   ulen = (size_t)(a - h);
00398             const char* t = (const char*) memchr(h, ':', ulen);
00399             if (t)
00400                 ulen = (size_t)(t - h);
00401             if (ulen < sizeof(info->user)) {
00402                 memcpy(info->user, h, ulen);
00403                 info->user[ulen] = '\0';
00404             } else {
00405                 memcpy(info->user, h, sizeof(info->user) - 1);
00406                 info->user[sizeof(info->user) - 1] = '\0';
00407             }
00408             if (t  &&  ulen) {
00409                 /* take pass only if user non-empty */
00410                 len = (size_t)(a - ++t);
00411                 if (len < sizeof(info->pass)) {
00412                     memcpy(info->pass, t, len);
00413                     info->pass[len] = '\0';
00414                 } else {
00415                     memcpy(info->pass, t, sizeof(info->pass) - 1);
00416                     info->pass[sizeof(info->pass) - 1] = '\0';
00417                 }
00418             } else
00419                 info->pass[0] = '\0';
00420             h = ++a;
00421             len = (size_t)(s - h);
00422         }
00423 
00424         /* host ends at "s" here */
00425         if (!(a = (const char*) memchr(h, ':', len))) {
00426             info->port = 0/*default*/;
00427             /*len remains unchanged*/
00428         } else if (isdigit(a[1])) {
00429             unsigned short port;
00430             int n;
00431             if (sscanf(a, ":%hu%n", &port, &n) < 1  ||  a + n != s  ||  !port)
00432                 return 0/*failure*/;
00433             info->port = port;
00434             len = (size_t)(a - h);
00435         } else
00436             return 0/*failure*/;
00437         if (len < sizeof(info->host)) {
00438             memcpy(info->host, h, len);
00439             info->host[len] = '\0';
00440         } else {
00441             memcpy(info->host, h, sizeof(info->host) - 1);
00442             info->host[sizeof(info->host) - 1] = '\0';
00443         }
00444     } else
00445         s = url;
00446 
00447     a = s + strcspn(s, "?#");
00448     len = (size_t)(a - s);
00449 
00450     /* path (NB: can be relative); "len" holds the path length */
00451     if (s != url  ||  *s == '/'  ||  !(p = strrchr(info->path, '/'))) {
00452         /* absolute path */
00453         p = info->path;
00454         if (!len) {
00455             s = "/";   /* in case of an empty path we take the root '/' */
00456             len = 1;
00457         }
00458     } else
00459         p++;
00460     if (len < sizeof(info->path) - (size_t)(p - info->path)) {
00461         memcpy(p, s, len);
00462         p[len] = '\0';
00463     } else {
00464         memcpy(p, s, sizeof(info->path) - (size_t)(p - info->path) - 1);
00465         info->path[sizeof(info->path) - 1] = '\0';
00466     }
00467 
00468     /* arguments and fragment */
00469     if (*a) {
00470         if (*a == '#') {
00471             len = 0/*unused*/;
00472             s = a;
00473         } else if (!(s = strchr(++a/*NB: *a=='?'*/, '#'))) {
00474             len = strlen(a);
00475             s = a + len;
00476         } else
00477             len = (size_t)(s - a);
00478         /* "len" holds the length of new args("a", w/o the leading "?") */
00479         assert(!*s  ||  *s == '#');
00480         assert(*a != '?');
00481 
00482         if (*s) {
00483             /* if there is a new fragment, the entire args get overridden */
00484             if (!s[1]) {
00485                 /* don't store the empty fragment # */
00486                 len = (size_t)(s - a);
00487                 if (len > sizeof(info->args) - 1)
00488                     len = sizeof(info->args) - 1;
00489             } else
00490                 len = sizeof(info->args) - 1;
00491             strncpy0(info->args, a, len);
00492         } else if (!(p = strchr(info->args, '#'))
00493                    ||  len >= sizeof(info->args) - 1) {
00494             /* there is no new fragment and: either there is no old fragment,
00495              * or the old one will not fit -- in both cases, replace */
00496             strncpy0(info->args, a, sizeof(info->args) - 1);
00497         } else {
00498             /* there is no new fragment, but there was an old one -- keep it */
00499             size_t move = strlen(p);
00500             if (move > sizeof(info->args) - 1 - len)
00501                 move = sizeof(info->args) - 1 - len;
00502             memmove(info->args + len, p, move);
00503             memcpy (info->args,       a, len);
00504             info->args[len + move] = '\0';
00505         }
00506     } else if ((p = strchr(info->args, '#')) != 0) {
00507         /* keep the old fragment, if any, but drop all args */
00508         memmove(info->args, p, strlen(p) + 1);
00509     }
00510 
00511     return 1/*success*/;
00512 }
00513 
00514 
00515 extern int/*bool*/ ConnNetInfo_SetUserHeader(SConnNetInfo* info,
00516                                              const char*   user_header)
00517 {
00518     if (info->http_user_header)
00519         free((void*) info->http_user_header);
00520     if (user_header && *user_header) {
00521         info->http_user_header = strdup(user_header);
00522         return info->http_user_header ? 1/*success*/ : 0/*failure*/;
00523     } else
00524         info->http_user_header = 0;
00525     return 1/*success*/;
00526 }
00527 
00528 
00529 extern int/*bool*/ ConnNetInfo_AppendUserHeader(SConnNetInfo* info,
00530                                                 const char*   user_header)
00531 {
00532     size_t oldlen, newlen;
00533     char* new_header;
00534 
00535     if (!info->http_user_header || !(oldlen = strlen(info->http_user_header)))
00536         return ConnNetInfo_SetUserHeader(info, user_header);
00537 
00538     if (!user_header || !(newlen = strlen(user_header)))
00539         return 1/*success*/;
00540 
00541     new_header = (char*)
00542         realloc((void*) info->http_user_header, oldlen + newlen + 1);
00543     if (!new_header)
00544         return 0/*failure*/;
00545 
00546     memcpy(&new_header[oldlen], user_header, newlen + 1);
00547     info->http_user_header = new_header;
00548     return 1/*success*/;
00549 }
00550 
00551 
00552 typedef enum {
00553     eUserHeaderOp_Delete,
00554     eUserHeaderOp_Extend,
00555     eUserHeaderOp_Override
00556 } EUserHeaderOp;
00557 
00558 
00559 static int/*bool*/ s_ModifyUserHeader(SConnNetInfo* info,
00560                                       const char*   user_header,
00561                                       EUserHeaderOp op)
00562 {
00563     int/*bool*/ retval;
00564     char*  new_header;
00565     size_t newlinelen;
00566     size_t newhdrlen;
00567     char*  newline;
00568     size_t hdrlen;
00569     char*  hdr;
00570 
00571     if (!user_header || !(newhdrlen = strlen(user_header)))
00572         return 1/*success*/;
00573 
00574     if (!(hdr = (char*) info->http_user_header) || !(hdrlen = strlen(hdr))) {
00575         if (op == eUserHeaderOp_Delete)
00576             return 1/*success*/;
00577         if (!hdr && !(hdr = strdup("")))
00578             return 0/*failure*/;
00579         hdrlen = 0;
00580     }
00581 
00582     if (op != eUserHeaderOp_Delete) {
00583         if (!(new_header = (char*) malloc(newhdrlen + 1)))
00584             return 0/*failure*/;
00585         memcpy(new_header, user_header, newhdrlen + 1);
00586     } else
00587         new_header = (char*) user_header; /* we actually won't modify it! */
00588 
00589     retval = 1/*assume best: success*/;
00590     for (newline = new_header; *newline; newline += newlinelen) {
00591         char*  eol = strchr(newline, '\n');
00592         char*  eot = strchr(newline,  ':');
00593         int/*bool*/ used = 0;
00594         size_t newtaglen;
00595         char*  newtagval;
00596         size_t linelen;
00597         char*  line;
00598         size_t len;
00599         size_t l;
00600 
00601         newlinelen = (size_t)
00602             (eol ? eol - newline + 1 : new_header + newhdrlen - newline);
00603         if (!eot || eot >= newline + newlinelen ||
00604             !(newtaglen = (size_t)(eot - newline)))
00605             continue;
00606 
00607         newtagval = newline + newtaglen + 1;
00608         while (newtagval < newline + newlinelen) {
00609             if (isspace((unsigned char)(*newtagval)))
00610                 newtagval++;
00611             else
00612                 break;
00613         }
00614         switch (op) {
00615         case eUserHeaderOp_Delete:
00616             len = 0;
00617             break;
00618         case eUserHeaderOp_Extend:
00619             len = newlinelen - (size_t)(newtagval - newline);
00620             break;
00621         case eUserHeaderOp_Override:
00622             len = newtagval < newline + newlinelen ? newlinelen : 0;
00623             break;
00624         default:
00625             assert(0);
00626             retval = 0/*failure*/;
00627             len = 0;
00628             break;
00629         }
00630 
00631         for (line = hdr; *line; line += linelen) {
00632             size_t taglen;
00633 
00634             eol = strchr(line, '\n');
00635             eot = strchr(line,  ':');
00636 
00637             linelen = (size_t)(eol ? eol - line + 1 : hdr + hdrlen - line);
00638             if (!eot || eot >= line + linelen)
00639                 continue;
00640 
00641             taglen = (size_t)(eot - line);
00642             if (newtaglen != taglen || strncasecmp(newline, line, taglen) != 0)
00643                 continue;
00644 
00645             if (op == eUserHeaderOp_Extend) {
00646                 if (linelen - taglen >= len
00647                     &&  strncasecmp(line + linelen - len, newtagval, len) == 0
00648                     &&  (linelen - taglen == len  ||
00649                          isspace((unsigned char) line[linelen - len - 1]))) {
00650                     len = 0;
00651                 }
00652                 l = linelen + len;
00653                 if (len && linelen > 1 && line[linelen - 2] == '\r')
00654                     --l;
00655             } else
00656                 l = len;
00657             if (l != linelen) {
00658                 size_t off = (size_t)(line - hdr);
00659                 if (l > linelen) {
00660                     char* temp = (char*)realloc(hdr, hdrlen + l - linelen + 1);
00661                     if (!temp) {
00662                         retval = 0/*failure*/;
00663                         continue;
00664                     }
00665                     hdr  = temp;
00666                     line = temp + off;
00667                 }
00668                 hdrlen -= linelen;
00669                 memmove(line + l, line + linelen, hdrlen - off + 1);
00670                 hdrlen += l;
00671             }
00672 
00673             if (len) {
00674                 if (op == eUserHeaderOp_Extend) {
00675                     char* s = &line[l - len - 1];
00676                     *s++ = ' ';
00677                     memcpy(s, newtagval, len);
00678                 } else
00679                     memcpy(line, newline, len);
00680                 linelen = l;
00681                 used = 1;
00682             } else if (op == eUserHeaderOp_Extend)
00683                 used = 1;
00684         }
00685 
00686         if (op == eUserHeaderOp_Delete)
00687             continue;
00688 
00689         if (used || !len) {
00690             memmove(newline, newline + newlinelen,
00691                     newhdrlen - (size_t)(newline-new_header) - newlinelen + 1);
00692             newhdrlen -= newlinelen;
00693             newlinelen = 0;
00694         }
00695     }
00696 
00697     info->http_user_header = hdr;
00698     if (op != eUserHeaderOp_Delete) {
00699         if (!ConnNetInfo_AppendUserHeader(info, new_header))
00700             retval = 0/*failure*/;
00701         free(new_header);
00702     }
00703     return retval;
00704 }
00705 
00706 
00707 extern int/*bool*/ ConnNetInfo_OverrideUserHeader(SConnNetInfo* info,
00708                                                   const char*   header)
00709 {
00710     return s_ModifyUserHeader(info, header, eUserHeaderOp_Override);
00711 }
00712 
00713 
00714 extern void ConnNetInfo_DeleteUserHeader(SConnNetInfo* info,
00715                                          const char*   header)
00716 {
00717     verify(s_ModifyUserHeader(info, header, eUserHeaderOp_Delete));
00718 }
00719 
00720 
00721 extern int/*bool*/ ConnNetInfo_ExtendUserHeader(SConnNetInfo* info,
00722                                                 const char*   header)
00723 {
00724     return s_ModifyUserHeader(info, header, eUserHeaderOp_Extend);
00725 }
00726 
00727 
00728 extern int/*bool*/ ConnNetInfo_AppendArg(SConnNetInfo* info,
00729                                          const char*   arg,
00730                                          const char*   val)
00731 {
00732     size_t len, used;
00733 
00734     if (!arg || !*arg)
00735         return 1/*success*/;
00736 
00737     used = strlen(info->args);
00738     len  = strlen(arg);
00739     
00740     if (used + (used ? 1/*&*/ : 0) + len +
00741         (val && *val ? 1/*=*/ + strlen(val) : 0) >= sizeof(info->args)) {
00742         return 0/*failure*/;
00743     }
00744 
00745     if (used)
00746         info->args[used++] = '&';
00747     strcpy(info->args + used, arg);
00748     if (val && *val) {
00749         used += len;
00750         info->args[used++] = '=';
00751         strcpy(info->args + used, val);
00752     }
00753     return 1/*success*/;
00754 }
00755 
00756 
00757 extern int/*bool*/ ConnNetInfo_PrependArg(SConnNetInfo* info,
00758                                           const char*   arg,
00759                                           const char*   val)
00760 {
00761     size_t len, off, used;
00762 
00763     if (!arg || !*arg)
00764         return 1/*success*/;
00765 
00766     used = strlen(info->args);
00767     len  = strlen(arg);
00768     off  = len + (val && *val ? 1/*=*/ + strlen(val) : 0) + (used? 1/*&*/ : 0);
00769 
00770     if (off + used >= sizeof(info->args))
00771         return 0/*failure*/;
00772 
00773     if (used)
00774         memmove(info->args + off, info->args, used + 1);
00775     strcpy(info->args, arg);
00776     if (val && *val) {
00777         info->args[len++] = '=';
00778         strcpy(info->args + len, val);
00779     }
00780     if (used)
00781         info->args[off - 1] = '&';
00782     return 1/*success*/;
00783 }
00784 
00785 
00786 extern void ConnNetInfo_DeleteArg(SConnNetInfo* info,
00787                                   const char*   arg)
00788 {
00789     size_t argnamelen;
00790     size_t arglen;
00791     char*  a;
00792 
00793     if (!arg || !(argnamelen = strcspn(arg, "=&")))
00794         return;
00795     for (a = info->args; *a; a += arglen) {
00796         if (*a == '&')
00797             a++;
00798         arglen = strcspn(a, "&");
00799         if (arglen < argnamelen  ||  strncasecmp(a, arg, argnamelen) != 0  ||
00800             (a[argnamelen] && a[argnamelen] != '=' && a[argnamelen] != '&'))
00801             continue;
00802         if (a[arglen]) {
00803             arglen++;     /* for intermediary args, eat '&' separator, too */
00804             memmove(a, a + arglen, strlen(a + arglen) + 1);
00805         } else if (a != info->args) {
00806             *--a = '\0';  /* last argument in a list: remove trailing '&' */
00807         } else {
00808             *a = '\0';    /* last and the only argument removed */
00809         }
00810         arglen = 0;
00811     }
00812 }
00813 
00814 
00815 extern void ConnNetInfo_DeleteAllArgs(SConnNetInfo* info,
00816                                       const char*   args)
00817 {
00818     char* temp;
00819     char* arg;
00820     if (!args || !*args || !(temp = strdup(args))) {
00821         if (args && *args)
00822             *info->args = '\0';
00823         return;
00824     }
00825     arg = temp;
00826     while (*arg) {
00827         char* end = strchr(arg, '&');
00828         if (!end)
00829             end = arg + strlen(arg);
00830         else
00831             *end++ = '\0';
00832         ConnNetInfo_DeleteArg(info, arg);
00833         arg = end;
00834     }
00835     free(temp);
00836 }
00837 
00838 
00839 extern int/*bool*/ ConnNetInfo_PreOverrideArg(SConnNetInfo* info,
00840                                               const char*   arg,
00841                                               const char*   val)
00842 {
00843     if (!arg || !*arg)
00844         return 1/*success*/;
00845     ConnNetInfo_DeleteAllArgs(info, arg);
00846     return ConnNetInfo_PrependArg(info, arg, val);
00847 }
00848 
00849 
00850 extern int/*bool*/ ConnNetInfo_PostOverrideArg(SConnNetInfo* info,
00851                                                const char*   arg,
00852                                                const char*   val)
00853 {
00854     if (!arg || !*arg)
00855         return 1/*success*/;
00856     ConnNetInfo_DeleteAllArgs(info, arg);
00857     return ConnNetInfo_AppendArg(info, arg, val);
00858 }
00859 
00860 
00861 static int/*bool*/ s_IsSufficientAddress(const char* addr)
00862 {
00863     const char*c;
00864     return (SOCK_isip(addr)  ||
00865             ((c = strchr(addr, '.'))  != 0  &&  c[1]  &&
00866              (c = strchr(c + 2, '.')) != 0  &&  c[1]));
00867 }
00868 
00869 
00870 static const char* s_ClientAddress(const char* client_host,
00871                                    int/*bool*/ local_host)
00872 {
00873     const char* c = client_host;
00874     unsigned int ip;
00875     char addr[80];
00876     char* s;
00877 
00878     assert(client_host);
00879     strncpy0(addr, client_host, sizeof(addr) - 1);
00880     if (UTIL_NcbiLocalHostName(addr)  &&  (s = strdup(addr)) != 0)
00881         client_host = s;
00882     if (s_IsSufficientAddress(client_host)                          ||
00883         !(ip = *client_host  &&  !local_host
00884           ? SOCK_gethostbyname(client_host)
00885           : SOCK_GetLocalHostAddress(eDefault))                     ||
00886         SOCK_ntoa(ip, addr, sizeof(addr)) != 0                      ||
00887         !(s = (char*) malloc(strlen(client_host) + strlen(addr) + 3))) {
00888         return client_host;
00889     }
00890     sprintf(s, "%s(%s)", client_host, addr);
00891     if (c != client_host)
00892         free((void*) client_host);
00893     return s;
00894 }
00895 
00896 
00897 extern int/*bool*/ ConnNetInfo_SetupStandardArgs(SConnNetInfo* info,
00898                                                  const char*   service)
00899 {
00900     static const char kService[]  = "service";
00901     static const char kAddress[]  = "address";
00902     static const char kPlatform[] = "platform";
00903     int/*bool*/ local_host;
00904     const char* arch;
00905     const char* addr;
00906 
00907     if (!info)
00908         return 0/*failed*/;
00909     if (!service  ||  !*service) {
00910         assert(0);
00911         return 0/*failed*/;
00912     }
00913     /* Dispatcher CGI args (may sacrifice some if they don't fit altogether) */
00914     if (!(arch = CORE_GetPlatform())  ||  !*arch)
00915         ConnNetInfo_DeleteArg(info, kPlatform);
00916     else
00917         ConnNetInfo_PreOverrideArg(info, kPlatform, arch);
00918     local_host = !info->client_host[0];
00919     if (local_host  &&
00920         !SOCK_gethostbyaddr(0, info->client_host, sizeof(info->client_host))) {
00921         SOCK_gethostname(info->client_host, sizeof(info->client_host));
00922     }
00923     if (!(addr = s_ClientAddress(info->client_host, local_host))  ||  !*addr)
00924         ConnNetInfo_DeleteArg(info, kAddress);
00925     else
00926         ConnNetInfo_PreOverrideArg(info, kAddress, addr);
00927     if (addr != info->client_host)
00928         free((void*) addr);
00929     if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
00930         ConnNetInfo_DeleteArg(info, kPlatform);
00931         if (!ConnNetInfo_PreOverrideArg(info, kService, service)) {
00932             ConnNetInfo_DeleteArg(info, kAddress);
00933             if (!ConnNetInfo_PreOverrideArg(info, kService, service))
00934                 return 0/*failed*/;
00935         }
00936     }
00937     return 1/*succeeded*/;
00938 }
00939 
00940 
00941 extern SConnNetInfo* ConnNetInfo_Clone(const SConnNetInfo* info)
00942 {
00943     SConnNetInfo* x_info;
00944     if (!info)
00945         return 0;
00946 
00947     x_info = (SConnNetInfo*) malloc(sizeof(SConnNetInfo) +
00948                                     (info->service
00949                                      ? strlen(info->service) + 1 : 0));
00950     *x_info = *info;
00951     if (info->timeout  &&  info->timeout != kDefaultTimeout) {
00952         x_info->tmo     = *info->timeout;
00953         x_info->timeout = &x_info->tmo;
00954     }
00955     if (info->service) {
00956         char* s = (char*) x_info + sizeof(*x_info);
00957         strcpy(s, info->service);
00958         x_info->service = s;
00959     }
00960     x_info->http_user_header = 0;
00961     ConnNetInfo_SetUserHeader(x_info, info->http_user_header);
00962     x_info->http_referer = info->http_referer ? strdup(info->http_referer) : 0;
00963     return x_info;
00964 }
00965 
00966 
00967 static const char* s_SchemeStr(EURLScheme scheme, char buf[])
00968 {
00969     switch (scheme) {
00970     case eURL_Ftp:
00971         return "ftp";
00972     case eURL_File:
00973         return "file";
00974     case eURL_Http:
00975         return "http";
00976     case eURL_Https:
00977         return "https";
00978     case eURL_Unspec:
00979         break;
00980     default:
00981         sprintf(buf, "?#0x%08X", (int) scheme);
00982         return buf;
00983     }
00984     return 0;
00985 }
00986 
00987 static const char* s_PortStr(unsigned short port, char buf[])
00988 {
00989     if (port) {
00990         sprintf(buf, "%hu", port);
00991         return buf;
00992     }
00993     return "<default>";
00994 }
00995 
00996 static void s_SaveStringQuot(char* s, const char* name,
00997                              const char* str, int/*bool*/ quote)
00998 {
00999     sprintf(s + strlen(s), "%-16.16s: %s%s%s\n", name,
01000             str && quote ? "\"" : "",
01001             str          ? str  : "NULL",
01002             str && quote ? "\"" : "");
01003 }
01004 
01005 static void s_SaveString(char* s, const char* name, const char* str)
01006 {
01007     s_SaveStringQuot(s, name, str, 1);
01008 }
01009 
01010 static void s_SaveKeyval(char* s, const char* name, const char* str)
01011 {
01012     s_SaveStringQuot(s, name, str, 0);
01013 }
01014 
01015 static void s_SaveULong(char* s, const char* name, unsigned long lll)
01016 {
01017     sprintf(s + strlen(s), "%-16.16s: %lu\n", name, lll);
01018 }
01019 
01020 static void s_SaveBool(char* s, const char* name, int/*bool*/ bbb)
01021 {
01022     sprintf(s + strlen(s), "%-16.16s: %s\n", name, bbb ? "TRUE" : "FALSE");
01023 }
01024 
01025 static void s_SaveUserHeader(char* s, const char* name,
01026                              const char* uh, size_t uhlen)
01027 {
01028     s += strlen(s);
01029     s += sprintf(s, "%-16.16s: ", name);
01030     if (uh) {
01031         *s++ = '"';
01032         memcpy(UTIL_PrintableString(uh, uhlen, s, 0/*reduce*/), "\"\n", 3);
01033     } else
01034         memcpy(s, "NULL\n", 6);
01035 }
01036 
01037 extern void ConnNetInfo_Log(const SConnNetInfo* info, LOG lg)
01038 {
01039     char   scheme[32];
01040     char   port[16];
01041     size_t uhlen;
01042     char*  s;
01043 
01044     if (!lg)
01045         return;
01046 
01047     if (!info) {
01048         LOG_Write(lg, NCBI_C_ERRCODE_X, 10, eLOG_Trace, 0, 0, 0,
01049                   "ConnNetInfo_Log: NULL info", 0, 0);
01050         return;
01051     }
01052 
01053     uhlen = info->http_user_header ? strlen(info->http_user_header) : 0;
01054        
01055     if (!(s = (char*) malloc(sizeof(*info) + 1024/*slack for all labels*/ +
01056                              (info->service ? strlen(info->service) : 0) +
01057                              UTIL_PrintableStringSize(info->http_user_header,
01058                                                       uhlen) +
01059                              (info->http_referer
01060                               ? strlen(info->http_referer) : 0)))) {
01061         LOG_WRITE(lg, NCBI_C_ERRCODE_X, 11, eLOG_Error,
01062                   "ConnNetInfo_Log: Cannot allocate temporary buffer");
01063         return;
01064     }
01065 
01066     strcpy(s, "ConnNetInfo_Log\n"
01067            "#################### [BEGIN] SConnNetInfo:\n");
01068     s_SaveString    (s, "service",         info->service);
01069     if (*info->client_host)
01070         s_SaveString(s, "client_host",     info->client_host);
01071     else
01072         s_SaveKeyval(s, "client_host",     "<default>");
01073     s_SaveString    (s, "scheme",          s_SchemeStr(info->scheme, scheme));
01074     s_SaveString    (s, "user",            info->user);
01075     s_SaveString    (s, "pass",            info->pass);
01076     s_SaveString    (s, "host",            info->host);
01077     s_SaveKeyval    (s, "port",            s_PortStr(info->port, port));
01078     s_SaveString    (s, "path",            info->path);
01079     s_SaveString    (s, "args",            info->args);
01080     s_SaveKeyval    (s, "req_method",     (info->req_method == eReqMethod_Any
01081                                            ? "ANY"
01082                                            : (info->req_method
01083                                               == eReqMethod_Get
01084                                               ? "GET"
01085                                               : (info->req_method
01086                                                  == eReqMethod_Post
01087                                                  ? "POST" : "<unknown>"))));
01088     if (info->timeout) {
01089         s_SaveULong (s, "timeout(sec)",    info->timeout->sec);
01090         s_SaveULong (s, "timeout(usec)",   info->timeout->usec);
01091     } else
01092         s_SaveKeyval(s, "timeout",         "INFINITE");
01093     s_SaveULong     (s, "max_try",         info->max_try);
01094     s_SaveString    (s, "http_proxy_host", info->http_proxy_host);
01095     s_SaveKeyval    (s, "http_proxy_port", s_PortStr(info->http_proxy_port,
01096                                                      port));
01097     s_SaveString    (s, "proxy_host",      info->proxy_host);
01098     s_SaveKeyval    (s, "debug_printout", (info->debug_printout
01099                                            == eDebugPrintout_None
01100                                            ? "NONE"
01101                                            : (info->debug_printout
01102                                               == eDebugPrintout_Some
01103                                               ? "SOME"
01104                                               : (info->debug_printout
01105                                                  == eDebugPrintout_Data
01106                                                  ? "DATA" : "<unknown>"))));
01107     s_SaveBool      (s, "stateless",       info->stateless);
01108     s_SaveBool      (s, "firewall",        info->firewall);
01109     s_SaveBool      (s, "lb_disable",      info->lb_disable);
01110     s_SaveUserHeader(s, "http_user_header",info->http_user_header, uhlen);
01111     s_SaveString    (s, "http_referer",    info->http_referer);
01112     s_SaveBool      (s, "proxy_adjusted",  info->http_proxy_adjusted);
01113     strcat(s, "#################### [END] SConnNetInfo\n");
01114 
01115     LOG_Write(lg, NCBI_C_ERRCODE_X, 12, eLOG_Trace, 0, 0, 0, s, 0, 0);
01116     free(s);
01117 }
01118 
01119 
01120 extern char* ConnNetInfo_URL(const SConnNetInfo* info)
01121 {
01122     const char* scheme;
01123     size_t      len;
01124     char*       url;
01125 
01126     if (!info  ||  info->scheme == eURL_Unspec
01127         ||  !(scheme = s_Scheme(info->scheme))) {
01128         return 0;
01129     }
01130 
01131     len = strlen(scheme) + 3/*"://"*/ + strlen(info->host) +
01132         (info->port                ? 6/*:port*/ : 0) +
01133         (info->http_proxy_adjusted ? 2 : 0) +
01134         strlen(info->path) + 1 +
01135         (*info->args ? strlen(info->args) + 2 : 1);
01136     url = (char*) malloc(len);
01137 
01138     if (url) {
01139         len = (size_t) sprintf(url, "%s://%s", scheme, info->host);
01140         if (info->port)
01141             len += sprintf(url + len, ":%hu", info->port);
01142         sprintf(url + len, "%s%s%s%s%s", info->http_proxy_adjusted
01143                 ? "<" : *info->path != '/' ? "/" : "", info->path,
01144                 &"?"[!*info->args  ||  *info->args == '#'], info->args,
01145                 ">" + !info->http_proxy_adjusted);
01146     }
01147     return url;
01148 }
01149 
01150 
01151 extern void ConnNetInfo_Destroy(SConnNetInfo* info)
01152 {
01153     if (!info)
01154         return;
01155     ConnNetInfo_SetUserHeader(info, 0);
01156     if (info->http_referer) {
01157         free((void*) info->http_referer);
01158         info->http_referer = 0;
01159     }
01160     free(info);
01161 }
01162 
01163 
01164 
01165 /****************************************************************************
01166  * URL_Connect
01167  */
01168 
01169 
01170 extern EIO_Status URL_ConnectEx
01171 (const char*     host,
01172  unsigned short  port,
01173  const char*     path,
01174  const char*     args,
01175  EReqMethod      req_method,
01176  size_t          content_length,
01177  const STimeout* c_timeout,
01178  const STimeout* rw_timeout,
01179  const char*     user_hdr,
01180  int/*bool*/     encode_args,
01181  TSOCK_Flags     flags,
01182  SOCK*           sock)
01183 {
01184     static const char X_REQ_Q[] = "?";
01185     static const char X_REQ_E[] = " HTTP/1.0\r\n";
01186     static const char X_HOST[]  = "Host: ";
01187 
01188     EIO_Status  st;
01189     BUF         buf;
01190     char*       header;
01191     size_t      hdrsize;
01192     char        strbuf[80];
01193     const char* x_args = 0;
01194     size_t      x_args_len = args  &&  *args ? strcspn(args, "#") : 0;
01195     size_t      user_hdr_len = user_hdr  &&  *user_hdr ? strlen(user_hdr) : 0;
01196     const char* x_req_method; /* "POST "/"GET " */
01197 
01198     /* check the args */
01199     if (!sock  ||  !host  ||  !*host  ||
01200         (user_hdr  &&  *user_hdr  &&  user_hdr[user_hdr_len - 1] != '\n')) {
01201         CORE_LOG_X(2, eLOG_Error, "[URL_Connect]  Bad arguments");
01202         assert(0);
01203         return eIO_InvalidArg;
01204     }
01205 
01206     /* select request method and its verbal representation */
01207     if (req_method == eReqMethod_Any) {
01208         req_method = content_length ? eReqMethod_Post : eReqMethod_Get;
01209     } else if (req_method == eReqMethod_Get  &&  content_length) {
01210         CORE_LOG_X(3, eLOG_Warning,
01211                    "[URL_Connect]  Content length ignored with method GET");
01212         content_length = 0;
01213     }
01214     switch (req_method) {
01215     case eReqMethod_Post:
01216         x_req_method = "POST ";
01217         break;
01218     case eReqMethod_Get:
01219         x_req_method = "GET ";
01220         break;
01221     default:
01222         CORE_LOGF_X(4, eLOG_Error,
01223                     ("[URL_Connect]  Unrecognized request method (#%u)",
01224                      (unsigned int) req_method));
01225         assert(0);
01226         return eIO_InvalidArg;
01227     }
01228 
01229     /* URL-encode "args", if any specified */
01230     if (x_args_len) {
01231         if ( encode_args ) {
01232             size_t rd_len, wr_len;
01233             size_t size = 3 * x_args_len;
01234             char* xx_args = (char*) malloc(size);
01235             if (!xx_args) {
01236                 CORE_LOGF_ERRNO_X(8, eLOG_Error, errno,
01237                                   ("[URL_Connect]  Out of memory (%lu)",
01238                                    (unsigned long) size));
01239                 return eIO_Unknown;
01240             }
01241             URL_Encode(args, x_args_len, &rd_len, xx_args, size, &wr_len);
01242             assert(x_args_len == rd_len);
01243             x_args_len = wr_len;
01244             x_args = xx_args;
01245         } else
01246             x_args = args;
01247     }
01248 
01249     buf = 0;
01250     errno = 0;
01251     if (!port) {
01252         *strbuf = '\0';
01253         port = flags & fSOCK_Secure ? 443 : 80;
01254     } else
01255         sprintf(strbuf, ":%hu", port);
01256 
01257     /* compose HTTP header */
01258     if (/* {POST|GET} <path>?<args> HTTP/1.0\r\n */
01259         !BUF_Write(&buf, x_req_method, strlen(x_req_method))  ||
01260         !BUF_Write(&buf, path,         strlen(path))          ||
01261         (x_args_len
01262          &&  (!BUF_Write(&buf, X_REQ_Q, sizeof(X_REQ_Q) - 1)  ||
01263               !BUF_Write(&buf, x_args,  x_args_len)))         ||
01264         !BUF_Write(&buf,       X_REQ_E, sizeof(X_REQ_E) - 1)  ||
01265 
01266         /* Host: host[:port]\r\n */
01267         !BUF_Write(&buf, X_HOST, sizeof(X_HOST) - 1)          ||
01268         !BUF_Write(&buf, host,   strlen(host))                ||
01269         !BUF_Write(&buf, strbuf, strlen(strbuf))              ||
01270         !BUF_Write(&buf, "\r\n", 2)                           ||
01271 
01272         /* <user_header> */
01273         (user_hdr_len
01274          &&  !BUF_Write(&buf, user_hdr, user_hdr_len))        ||
01275 
01276         /* Content-Length: <content_length>\r\n\r\n */
01277         (req_method != eReqMethod_Get
01278          &&  (sprintf(strbuf, "Content-Length: %lu\r\n",
01279                       (unsigned long) content_length) <= 0    ||
01280               !BUF_Write(&buf, strbuf, strlen(strbuf))))      ||
01281 
01282         !BUF_Write(&buf, "\r\n", 2)) {
01283         int x_errno = errno;
01284         CORE_LOGF_ERRNO_X(5, eLOG_Error, x_errno,
01285                           ("[URL_Connect]  Error building HTTP header for"
01286                            " %s:%hu", host, port));
01287         BUF_Destroy(buf);
01288         if (x_args  &&  x_args != args)
01289             free((void*) x_args);
01290         return eIO_Unknown;
01291     }
01292     if (x_args  &&  x_args != args)
01293         free((void*) x_args);
01294 
01295     if (!(header = (char*) malloc(hdrsize = BUF_Size(buf)))
01296         ||  BUF_Read(buf, header, hdrsize) != hdrsize) {
01297         int x_errno = errno;
01298         CORE_LOGF_ERRNO_X(6, eLOG_Error, x_errno,
01299                           ("[URL_Connect]  Error storing HTTP header for"
01300                            " %s:%hu", host, port));
01301         if (header)
01302             free(header);
01303         BUF_Destroy(buf);
01304         return eIO_Unknown;
01305     }
01306     BUF_Destroy(buf);
01307 
01308     /* connect to HTTPD */
01309     st = SOCK_CreateEx(host, port, c_timeout, sock, header, hdrsize, flags);
01310     free(header);
01311     if (st != eIO_Success) {
01312         char temp[80];
01313         assert(!*sock);
01314         if (st == eIO_Timeout  &&  c_timeout) {
01315             sprintf(temp, "[%u.%06u]",
01316                     (unsigned int)(c_timeout->sec + c_timeout->usec/1000000),
01317                     (unsigned int)                 (c_timeout->usec%1000000));
01318         } else
01319             *temp = '\0';
01320         CORE_LOGF_X(7, eLOG_Error,
01321                     ("[URL_Connect]  Socket connect to %s:%hu failed: %s%s",
01322                      host, port, IO_StatusStr(st), temp));
01323     } else
01324         verify(SOCK_SetTimeout(*sock, eIO_ReadWrite, rw_timeout)==eIO_Success);
01325     return st;
01326 }
01327 
01328 
01329 extern SOCK URL_Connect
01330 (const char*     host,
01331  unsigned short  port,
01332  const char*     path,
01333  const char*     args,
01334  EReqMethod      req_method,
01335  size_t          content_length,
01336  const STimeout* c_timeout,
01337  const STimeout* rw_timeout,
01338  const char*     user_hdr,
01339  int/*bool*/     encode_args,
01340  TSOCK_Flags     flags)
01341 {
01342     SOCK sock;
01343     EIO_Status st = URL_ConnectEx(host, port, path, args,
01344                                   req_method, content_length,
01345                                   c_timeout, rw_timeout,
01346                                   user_hdr, encode_args, flags, &sock);
01347     return st == eIO_Success ? sock : 0;
01348 }
01349 
01350 
01351 
01352 /****************************************************************************
01353  * StripToPattern()
01354  */
01355 
01356 
01357 typedef EIO_Status (*FDoIO)
01358      (void*     stream,
01359       void*     buf,
01360       size_t    size,
01361       size_t*   n_read,
01362       EIO_Event what     /* eIO_Read | eIO_Write (to pushback) */
01363       );
01364 
01365 static EIO_Status s_StripToPattern
01366 (void*       stream,
01367  FDoIO       io_func,
01368  const void* pattern,
01369  size_t      pattern_size,
01370  BUF*        buf,
01371  size_t*     n_discarded)
01372 {
01373     EIO_Status status;
01374     char*      buffer;
01375     size_t     buffer_size;
01376     size_t     n_read = 0;
01377 
01378     /* check args */
01379     if ( n_discarded )
01380         *n_discarded = 0;
01381     if (!stream  ||  (pattern != 0) != (pattern_size != 0))
01382         return eIO_InvalidArg;
01383 
01384     /* allocate a temporary read buffer */
01385     buffer_size = 2 * pattern_size;
01386     if (buffer_size < 4096)
01387         buffer_size = 4096;
01388     if ( !(buffer = (char*) malloc(buffer_size)) )
01389         return eIO_Unknown;
01390 
01391     if ( !pattern ) {
01392         /* read/discard until EOF */
01393         do {
01394             status = io_func(stream, buffer, buffer_size, &n_read, eIO_Read);
01395             if ( buf )
01396                 BUF_Write(buf, buffer, n_read);
01397             if ( n_discarded )
01398                 *n_discarded += n_read;
01399         } while (status == eIO_Success);
01400     } else {
01401         for (;;) {
01402             /* read; search for the pattern; store the discarded data */
01403             size_t x_read, n_stored;
01404 
01405             assert(n_read < pattern_size);
01406             status = io_func(stream, buffer + n_read, buffer_size - n_read,
01407                              &x_read, eIO_Read);
01408             if ( !x_read ) {
01409                 assert(status != eIO_Success);
01410                 break; /*error*/
01411             }
01412             n_stored = n_read + x_read;
01413 
01414             if (n_stored >= pattern_size) {
01415                 /* search for the pattern */
01416                 size_t n_check = n_stored - pattern_size + 1;
01417                 const char* b;
01418                 for (b = buffer;  n_check;  b++, n_check--) {
01419                     if (*b != *((const char*) pattern))
01420                         continue;
01421                     if (memcmp(b, pattern, pattern_size) == 0)
01422                         break; /*found*/
01423                 }
01424                 /* pattern found */
01425                 if ( n_check ) {
01426                     size_t x_discarded = (size_t)(b - buffer) + pattern_size;
01427                     if ( buf )
01428                         BUF_Write(buf, buffer + n_read, x_discarded - n_read);
01429                     if ( n_discarded )
01430                         *n_discarded += x_discarded;
01431                     /* return unused portion to the stream */
01432                     status = io_func(stream, buffer + x_discarded,
01433                                      n_stored - x_discarded, 0, eIO_Write);
01434                     break; /*finished*/
01435                 }
01436             }
01437 
01438             /* pattern not found yet */
01439             if ( buf )
01440                 BUF_Write(buf, buffer + n_read, x_read);
01441             if ( n_discarded )
01442                 *n_discarded += x_read;
01443             n_read = n_stored;
01444 
01445             if (n_read > pattern_size) {
01446                 size_t n_cut = n_read - pattern_size + 1;
01447                 n_read = pattern_size - 1;
01448                 memmove(buffer, buffer + n_cut, n_read);
01449             }
01450         }
01451     }
01452 
01453     /* cleanup & exit */
01454     free(buffer);
01455     return status;
01456 }
01457 
01458 
01459 static EIO_Status s_CONN_IO
01460 (void*     stream,
01461  void*     buf,
01462  size_t    size,
01463  size_t*   n_read,
01464  EIO_Event what)
01465 {
01466     switch (what) {
01467     case eIO_Read:
01468         return CONN_Read((CONN) stream, buf, size, n_read, eIO_ReadPlain);
01469     case eIO_Write:
01470         assert(stream);
01471         return CONN_PushBack((CONN) stream, buf, size);
01472     default:
01473         break;
01474     }
01475     return eIO_InvalidArg;
01476 }
01477 
01478 extern EIO_Status CONN_StripToPattern
01479 (CONN        conn,
01480  const void* pattern,
01481  size_t      pattern_size,
01482  BUF*        buf,
01483  size_t*     n_discarded)
01484 {
01485     return s_StripToPattern
01486         (conn, s_CONN_IO, pattern, pattern_size, buf, n_discarded);
01487 }
01488 
01489 
01490 static EIO_Status s_SOCK_IO
01491 (void*     stream,
01492  void*     buf,
01493  size_t    size,
01494  size_t*   n_read,
01495  EIO_Event what)
01496 {
01497     switch (what) {
01498     case eIO_Read:
01499         return SOCK_Read((SOCK) stream, buf, size, n_read, eIO_ReadPlain);
01500     case eIO_Write:
01501         return SOCK_PushBack((SOCK) stream, buf, size);
01502     default:
01503         break;
01504     }
01505     return eIO_InvalidArg;
01506 }
01507 
01508 extern EIO_Status SOCK_StripToPattern
01509 (SOCK        sock,
01510  const void* pattern,
01511  size_t      pattern_size,
01512  BUF*        buf,
01513  size_t*     n_discarded)
01514 {
01515     return s_StripToPattern
01516         (sock, s_SOCK_IO, pattern, pattern_size, buf, n_discarded);
01517 }
01518 
01519 
01520 static EIO_Status s_BUF_IO
01521 (void*     stream,
01522  void*     buf,
01523  size_t    size,
01524  size_t*   n_read,
01525  EIO_Event what)
01526 {
01527     BUF b;
01528     switch (what) {
01529     case eIO_Read:
01530         *n_read = BUF_Read((BUF) stream, buf, size);
01531         return *n_read ? eIO_Success : eIO_Closed;
01532     case eIO_Write:
01533         assert(stream);
01534         b = (BUF) stream;
01535         return BUF_PushBack(&b, buf, size) ? eIO_Success : eIO_Unknown;
01536     default:
01537         break;
01538     }
01539     return eIO_InvalidArg;
01540 }
01541 
01542 extern EIO_Status BUF_StripToPattern
01543 (BUF         buffer,
01544  const void* pattern,
01545  size_t      pattern_size,
01546  BUF*        buf,
01547  size_t*     n_discarded)
01548 {
01549     return s_StripToPattern
01550         (buffer, s_BUF_IO, pattern, pattern_size, buf, n_discarded);
01551 }
01552 
01553 
01554 
01555 
01556 /****************************************************************************
01557  * URL- Encoding/Decoding
01558  */
01559 
01560 
01561 /* Return integer (0..15) corresponding to the "ch" as a hex digit
01562  * Return -1 on error
01563  */
01564 static int s_HexChar(char ch)
01565 {
01566     unsigned int rc = ch - '0';
01567     if (rc <= 9)
01568         return rc;
01569     rc = (ch | ' ') - 'a';
01570     return rc <= 5 ? (int) rc + 10 : -1;
01571 }
01572 
01573 
01574 /* The URL-encoding table
01575  */
01576 static const char s_EncodeTable[256][4] = {
01577     "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
01578     "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
01579     "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
01580     "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
01581     "+",   "!",   "%22", "%23", "$",   "%25", "%26", "'",
01582     "(",   ")",   "*",   "%2B", ",",   "-",   ".",   "%2F",
01583     "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",
01584     "8",   "9",   "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
01585     "%40", "A",   "B",   "C",   "D",   "E",   "F",   "G",
01586     "H",   "I",   "J",   "K",   "L",   "M",   "N",   "O",
01587     "P",   "Q",   "R",   "S",   "T",   "U",   "V",   "W",
01588     "X",   "Y",   "Z",   "%5B", "%5C", "%5D", "%5E", "_",
01589     "%60", "a",   "b",   "c",   "d",   "e",   "f",   "g",
01590     "h",   "i",   "j",   "k",   "l",   "m",   "n",   "o",
01591     "p",   "q",   "r",   "s",   "t",   "u",   "v",   "w",
01592     "x",   "y",   "z",   "%7B", "%7C", "%7D", "%7E", "%7F",
01593     "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
01594     "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
01595     "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
01596     "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
01597     "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
01598     "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
01599     "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
01600     "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
01601     "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
01602     "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
01603     "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
01604     "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
01605     "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
01606     "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
01607     "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
01608     "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
01609 };
01610 
01611 #define VALID_URL_SYMBOL(ch)  (s_EncodeTable[(unsigned char)ch][0] != '%')
01612 
01613 
01614 extern int/*bool*/ URL_DecodeEx
01615 (const void* src_buf,
01616  size_t      src_size,
01617  size_t*     src_read,
01618  void*       dst_buf,
01619  size_t      dst_size,
01620  size_t*     dst_written,
01621  const char* allow_symbols)
01622 {
01623     unsigned char* src = (unsigned char*) src_buf;
01624     unsigned char* dst = (unsigned char*) dst_buf;
01625 
01626     *src_read    = 0;
01627     *dst_written = 0;
01628     if (!src_size  ||  !dst_size)
01629         return 1/*true*/;
01630 
01631     for ( ;  *src_read != src_size  &&  *dst_written != dst_size;
01632           (*src_read)++, (*dst_written)++, src++, dst++) {
01633         switch ( *src ) {
01634         case '%': {
01635             int i1, i2;
01636             if (*src_read + 2 > src_size)
01637                 return 1/*true*/;
01638             if ((i1 = s_HexChar(*(++src))) == -1)
01639                 return *dst_written ? 1/*true*/ : 0/*false*/;
01640             if (*src_read + 3 > src_size)
01641                 return 1/*true*/;
01642             if ((i2 = s_HexChar(*(++src))) == -1)
01643                 return *dst_written ? 1/*true*/ : 0/*false*/;
01644 
01645             *dst = (unsigned char)((i1 << 4) + i2);
01646             *src_read += 2;
01647             break;
01648         }
01649         case '+': {
01650             *dst = ' ';
01651             break;
01652         }
01653         default: {
01654             if (VALID_URL_SYMBOL(*src)  ||
01655                 (allow_symbols  &&  strchr(allow_symbols, *src)))
01656                 *dst = *src;
01657             else
01658                 return *dst_written ? 1/*true*/ : 0/*false*/;
01659         }
01660         }/*switch*/
01661     }
01662 
01663     assert(src == (unsigned char*) src_buf + *src_read   );
01664     assert(dst == (unsigned char*) dst_buf + *dst_written);
01665     return 1/*true*/;
01666 }
01667 
01668 
01669 extern int/*bool*/ URL_Decode
01670 (const void* src_buf,
01671  size_t      src_size,
01672  size_t*     src_read,
01673  void*       dst_buf,
01674  size_t      dst_size,
01675  size_t*     dst_written)
01676 {
01677     return URL_DecodeEx
01678         (src_buf, src_size, src_read, dst_buf, dst_size, dst_written, 0);
01679 }
01680 
01681 
01682 extern void URL_Encode
01683 (const void* src_buf,
01684  size_t      src_size,
01685  size_t*     src_read,
01686  void*       dst_buf,
01687  size_t      dst_size,
01688  size_t*     dst_written)
01689 {
01690     unsigned char* src = (unsigned char*) src_buf;
01691     unsigned char* dst = (unsigned char*) dst_buf;
01692 
01693     *src_read    = 0;
01694     *dst_written = 0;
01695     if (!src_size  ||  !dst_size)
01696         return;
01697 
01698     for ( ;  *src_read != src_size  &&  *dst_written != dst_size;
01699           (*src_read)++, (*dst_written)++, src++, dst++) {
01700         const char* subst = s_EncodeTable[*src];
01701         if (*subst != '%') {
01702             *dst = *subst;
01703         } else if (*dst_written < dst_size - 2) {
01704             *dst = '%';
01705             *(++dst) = *(++subst);
01706             *(++dst) = *(++subst);
01707             *dst_written += 2;
01708         } else {
01709             return;
01710         }
01711     }
01712     assert(src == (unsigned char*) src_buf + *src_read   );
01713     assert(dst == (unsigned char*) dst_buf + *dst_written);
01714 }
01715 
01716 
01717 /****************************************************************************
01718  * NCBI-specific MIME content type and sub-types
01719  */
01720 
01721 
01722 static const char* s_MIME_Type[eMIME_T_Unknown+1] = {
01723     "x-ncbi-data",
01724     "text",
01725     "application",
01726     "unknown"
01727 };
01728 
01729 static const char* s_MIME_SubType[eMIME_Unknown+1] = {
01730     "x-dispatch",
01731     "x-asn-text",
01732     "x-asn-binary",
01733     "x-fasta",
01734     "x-www-form",
01735     "html",
01736     "plain",
01737     "xml",
01738     "xml+soap",
01739     "octet-stream",
01740     "x-unknown"
01741 };
01742 
01743 static const char* s_MIME_Encoding[eENCOD_Unknown+1] = {
01744     "",
01745     "urlencoded",
01746     "encoded"
01747 };
01748 
01749 
01750 extern char* MIME_ComposeContentTypeEx
01751 (EMIME_Type     type,
01752  EMIME_SubType  subtype,
01753  EMIME_Encoding encoding,
01754  char*          buf,
01755  size_t         buflen)
01756 {
01757     static const char s_ContentType[] = "Content-Type: ";
01758     const char* x_type;
01759     const char* x_subtype;
01760     const char* x_encoding;
01761     char        x_buf[MAX_CONTENT_TYPE_LEN];
01762 
01763     assert(buf  &&  buflen);
01764 
01765     if (type == eMIME_T_Undefined  ||  subtype == eMIME_Undefined)
01766         return 0;
01767     if (type >= eMIME_T_Unknown)
01768         type  = eMIME_T_Unknown;
01769     if (subtype >= eMIME_Unknown)
01770         subtype  = eMIME_Unknown;
01771     if (encoding >= eENCOD_Unknown)
01772         encoding  = eENCOD_Unknown;
01773 
01774     x_type     = s_MIME_Type    [type];
01775     x_subtype  = s_MIME_SubType [subtype];
01776     x_encoding = s_MIME_Encoding[encoding];
01777 
01778     if ( *x_encoding ) {
01779         assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
01780                + strlen(x_encoding) + 4 < MAX_CONTENT_TYPE_LEN);
01781         sprintf(x_buf, "%s%s/%s-%s\r\n",
01782                 s_ContentType, x_type, x_subtype, x_encoding);
01783     } else {
01784         assert(sizeof(s_ContentType) + strlen(x_type) + strlen(x_subtype)
01785                + 3 < MAX_CONTENT_TYPE_LEN);
01786         sprintf(x_buf, "%s%s/%s\r\n", s_ContentType, x_type, x_subtype);
01787     }
01788     assert(strlen(x_buf) < sizeof(x_buf));
01789     assert(strlen(x_buf) < buflen);
01790     strncpy0(buf, x_buf, buflen - 1);
01791     return buf;
01792 }
01793 
01794 
01795 extern int/*bool*/ MIME_ParseContentTypeEx
01796 (const char*     str,
01797  EMIME_Type*     type,
01798  EMIME_SubType*  subtype,
01799  EMIME_Encoding* encoding)
01800 {
01801     char*  x_buf;
01802     size_t x_size;
01803     char*  x_type;
01804     char*  x_subtype;
01805     int    i;
01806 
01807     if ( type )
01808         *type = eMIME_T_Undefined;
01809     if ( subtype )
01810         *subtype = eMIME_Undefined;
01811     if ( encoding )
01812         *encoding = eENCOD_None;
01813 
01814     x_size = str  &&  *str ? strlen(str) + 1 : 0;
01815     if (!x_size)
01816         return 0/*false*/;
01817 
01818     if (!(x_buf = (char*) malloc(x_size << 1)))
01819         return 0/*false*/;
01820     x_type = x_buf + x_size;
01821 
01822     strlwr(strcpy(x_buf, str));
01823 
01824     if ((sscanf(x_buf, " content-type: %s ", x_type) != 1  &&
01825          sscanf(x_buf, " %s ", x_type) != 1)  ||
01826         (x_subtype = strchr(x_type, '/')) == 0) {
01827         free(x_buf);
01828         return 0/*false*/;
01829     }
01830     *x_subtype++ = '\0';
01831     x_size = strlen(x_subtype);
01832 
01833     if ( type ) {
01834         for (i = 0;  i < (int) eMIME_T_Unknown;  i++) {
01835             if (strcmp(x_type, s_MIME_Type[i]) == 0)
01836                 break;
01837         }
01838         *type = (EMIME_Type) i;
01839     }
01840 
01841     for (i = 1;  i <= (int) eENCOD_Unknown;  i++) {
01842         size_t len = strlen(s_MIME_Encoding[i]);
01843         if (len < x_size) {
01844             char* x_encoding = x_subtype + x_size - len;
01845             if (x_encoding[-1] == '-'
01846                 &&  strcmp(x_encoding, s_MIME_Encoding[i]) == 0) {
01847                 if ( encoding ) {
01848                     *encoding = (i == (int) eENCOD_Unknown
01849                                  ? eENCOD_None : (EMIME_Encoding) i);
01850                 }
01851                 x_encoding[-1] = '\0';
01852                 break;
01853             }
01854         }
01855     }
01856 
01857     if ( subtype ) {
01858         for (i = 0;  i < (int) eMIME_Unknown;  i++) {
01859             if (strcmp(x_subtype, s_MIME_SubType[i]) == 0)
01860                 break;
01861         }
01862         *subtype = (EMIME_SubType) i;
01863     }
01864 
01865     free(x_buf);
01866     return 1/*true*/;
01867 }
01868 
01869 
01870 /* DEPRECATED and scheduled for removal */
01871 extern char* MIME_ComposeContentType
01872 (EMIME_SubType  subtype,
01873  EMIME_Encoding encoding,
01874  char*          buf,
01875  size_t         buflen)
01876 {
01877     return MIME_ComposeContentTypeEx(eMIME_T_NcbiData,
01878                                      subtype, encoding, buf, buflen);
01879 }
01880 
01881 
01882 /* DEPRECATED and scheduled for removal */
01883 extern int/*bool*/ MIME_ParseContentType
01884 (const char*     str,
01885  EMIME_SubType*  subtype,
01886  EMIME_Encoding* encoding)
01887 {
01888     EMIME_Type type;
01889     if ( !MIME_ParseContentTypeEx(str, &type, subtype, encoding) )
01890         return 0/*false*/;
01891 
01892     if (type != eMIME_T_NcbiData) {
01893         if ( subtype )
01894             *subtype  = eMIME_Unknown;
01895         if ( encoding )
01896             *encoding = eENCOD_Unknown;
01897         return 0/*false*/;
01898     }
01899 
01900     return 1/*true*/;
01901 }
01902 
01903 

Generated on Wed Dec 9 04:13:47 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Wed Dec 09 08:17:55 2009 by modify_doxy.py rev. 173732