ncbi_connutil.c
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:53k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: ncbi_connutil.c,v $
  4.  * PRODUCTION Revision 1000.3  2004/04/12 17:06:12  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [CATCHUP_003] Dev-tree R6.64
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: ncbi_connutil.c,v 1000.3 2004/04/12 17:06:12 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Author:  Denis Vakatov, Anton Lavrentiev
  35.  *
  36.  * File Description:
  37.  *   Auxiliary API, mostly CONN-, URL-, and MIME-related
  38.  *   (see in "ncbi_connutil.h" for more details).
  39.  *
  40.  */
  41. #include "ncbi_ansi_ext.h"
  42. #include "ncbi_priv.h"
  43. #include <connect/ncbi_connutil.h>
  44. #include <ctype.h>
  45. #include <errno.h>
  46. #include <stdlib.h>
  47. static const char* s_GetValue(const char* service, const char* param,
  48.                               char* value, size_t value_size,
  49.                               const char* def_value)
  50. {
  51.     char        key[250];
  52.     char*       sec;
  53.     const char* val;
  54.     if (!param  ||  !value  ||  value_size <= 0)
  55.         return 0;
  56.     *value = '';
  57.     if (service  &&  *service) {
  58.         /* Service-specific inquiry */
  59.         if (strlen(service) + 1 + sizeof(DEF_CONN_REG_SECTION) +
  60.             strlen(param) + 1 > sizeof(key))
  61.             return 0;
  62.         /* First, environment search for 'service_CONN_param' */
  63.         sprintf(key, "%s_" DEF_CONN_REG_SECTION "_%s", service, param);
  64.         strupr(key);
  65.         if ((val = getenv(key)) != 0)
  66.             return strncpy0(value, val, value_size - 1);
  67.         /* Next, search for 'CONN_param' in '[service]' registry section */
  68.         sprintf(key, DEF_CONN_REG_SECTION "_%s", param);
  69.         sec = key + strlen(key) + 1;
  70.         strupr(key);
  71.         strcpy(sec, service);
  72.         strupr(sec);
  73.         CORE_REG_GET(sec, key, value, value_size, 0);
  74.         if (*value)
  75.             return value;
  76.     } else {
  77.         /* Common case. Form 'CONN_param' */
  78.         if (sizeof(DEF_CONN_REG_SECTION) + strlen(param) + 1 > sizeof(key))
  79.             return 0;
  80.         sprintf(key, DEF_CONN_REG_SECTION "_%s", param);
  81.         strupr(key);
  82.     }
  83.     /* Environment search for 'CONN_param' */
  84.     if ((val = getenv(key)) != 0)
  85.         return strncpy0(value, val, value_size - 1);
  86.     /* Last resort: Search for 'param' in default registry section */
  87.     strcpy(key, param);
  88.     strupr(key);
  89.     CORE_REG_GET(DEF_CONN_REG_SECTION, key, value, value_size, def_value);
  90.     return value;
  91. }
  92. /***********************************************************************
  93.  *  EXTERNAL
  94.  ***********************************************************************/
  95. extern SConnNetInfo* ConnNetInfo_Create(const char* service)
  96. {
  97. #define REG_VALUE(name, value, def_value) 
  98.     s_GetValue(service, name, value, sizeof(value), def_value)
  99.     SConnNetInfo* info = (SConnNetInfo*) malloc(sizeof(*info) +
  100.                                                 (service  &&  *service
  101.                                                  ? strlen(service) + 1 : 0));
  102.     /* aux. storage for the string-to-int conversions, etc. */
  103.     char   str[32];
  104.     int    val;
  105.     double dbl;
  106.     char*  s;
  107.     if (!info)
  108.         return 0/*failure*/;
  109.     /* client host */
  110.     if (!SOCK_gethostbyaddr(0, info->client_host, sizeof(info->client_host)))
  111.         SOCK_gethostname(info->client_host, sizeof(info->client_host));
  112.     /* dispatcher host name */
  113.     REG_VALUE(REG_CONN_HOST, info->host, DEF_CONN_HOST);
  114.     /* dispatcher port number */
  115.     REG_VALUE(REG_CONN_PORT, str, 0);
  116.     val = atoi(str);
  117.     info->port = (unsigned short)(val > 0 ? val : DEF_CONN_PORT);
  118.     /* service path */
  119.     REG_VALUE(REG_CONN_PATH, info->path, DEF_CONN_PATH);
  120.     /* service args */
  121.     REG_VALUE(REG_CONN_ARGS, info->args, DEF_CONN_ARGS);
  122.     /* request method */
  123.     REG_VALUE(REG_CONN_REQ_METHOD, str, DEF_CONN_REQ_METHOD);
  124.     if (!*str  ||  strcasecmp(str, "ANY") == 0)
  125.         info->req_method = eReqMethod_Any;
  126.     else if (strcasecmp(str, "POST") == 0)
  127.         info->req_method = eReqMethod_Post;
  128.     else if (strcasecmp(str, "GET") == 0)
  129.         info->req_method = eReqMethod_Get;
  130.     /* connection timeout */
  131.     REG_VALUE(REG_CONN_TIMEOUT, str, 0);
  132.     if (strlen(str) > 2  &&  strncasecmp(str, "infinite", strlen(str)) == 0) {
  133.         info->timeout = 0;
  134.     } else {
  135.         info->timeout = &info->tmo;
  136.         dbl = atof(str);
  137.         if (dbl <= 0.0)
  138.             dbl = DEF_CONN_TIMEOUT;
  139.         info->timeout->sec  = (unsigned int) dbl;
  140.         info->timeout->usec = (unsigned int)
  141.             ((dbl - info->timeout->sec) * 1000000);
  142.     }
  143.     /* max. # of attempts to establish connection */
  144.     REG_VALUE(REG_CONN_MAX_TRY, str, 0);
  145.     val = atoi(str);
  146.     info->max_try = (unsigned short)(val > 0 ? val : DEF_CONN_MAX_TRY);
  147.     /* HTTP proxy server? */
  148.     REG_VALUE(REG_CONN_HTTP_PROXY_HOST, info->http_proxy_host,
  149.               DEF_CONN_HTTP_PROXY_HOST);
  150.     if (*info->http_proxy_host) {
  151.         /* yes, use the specified HTTP proxy server */
  152.         REG_VALUE(REG_CONN_HTTP_PROXY_PORT, str, 0);
  153.         val = atoi(str);
  154.         info->http_proxy_port = (unsigned short)
  155.             (val > 0 ? val : DEF_CONN_HTTP_PROXY_PORT);
  156.     } else
  157.         info->http_proxy_port = DEF_CONN_HTTP_PROXY_PORT;
  158.     /* non-transparent CERN-like firewall proxy server? */
  159.     REG_VALUE(REG_CONN_PROXY_HOST, info->proxy_host, DEF_CONN_PROXY_HOST);
  160.     /* turn on debug printout? */
  161.     REG_VALUE(REG_CONN_DEBUG_PRINTOUT, str, DEF_CONN_DEBUG_PRINTOUT);
  162.     if (*str  &&
  163.         (strcmp(str, "1") == 0  ||
  164.          strcasecmp(str, "true") == 0  ||
  165.          strcasecmp(str, "yes" ) == 0  ||
  166.          strcasecmp(str, "some") == 0)) {
  167.         info->debug_printout = eDebugPrintout_Some;
  168.     } else if (*str  &&
  169.                (strcasecmp(str, "data") == 0  ||
  170.                 strcasecmp(str, "all" ) == 0)) {
  171.         info->debug_printout = eDebugPrintout_Data;
  172.     } else
  173.         info->debug_printout = eDebugPrintout_None;
  174.     /* stateless client? */
  175.     REG_VALUE(REG_CONN_STATELESS, str, DEF_CONN_STATELESS);
  176.     info->stateless = (*str  &&
  177.                        (strcmp(str, "1") == 0  ||
  178.                         strcasecmp(str, "true") == 0  ||
  179.                         strcasecmp(str, "yes" ) == 0));
  180.     /* firewall mode? */
  181.     REG_VALUE(REG_CONN_FIREWALL, str, DEF_CONN_FIREWALL);
  182.     info->firewall = (*str  &&
  183.                       (strcmp(str, "1") == 0  ||
  184.                        strcasecmp(str, "true") == 0  ||
  185.                        strcasecmp(str, "yes" ) == 0));
  186.     /* prohibit the use of local load balancer? */
  187.     REG_VALUE(REG_CONN_LB_DISABLE, str, DEF_CONN_LB_DISABLE);
  188.     info->lb_disable = (*str  &&
  189.                         (strcmp(str, "1") == 0  ||
  190.                          strcasecmp(str, "true") == 0  ||
  191.                          strcasecmp(str, "yes" ) == 0));
  192.     /* has no user header yet... */
  193.     info->http_user_header = 0;
  194.     /* not adjusted yet... */
  195.     info->http_proxy_adjusted = 0/*false*/;
  196.     /* store service name for which this structure has been created */
  197.     if (service  &&  *service) {
  198.         s = (char*) info + sizeof(*info);
  199.         strcpy(s, service);
  200.     } else
  201.         s = 0;
  202.     info->service = s;
  203.     /* done */
  204.     return info;
  205. #undef REG_VALUE
  206. }
  207. extern int/*bool*/ ConnNetInfo_AdjustForHttpProxy(SConnNetInfo* info)
  208. {
  209.     if (info->http_proxy_adjusted  ||  !*info->http_proxy_host)
  210.         return 0/*false*/;
  211.     if (strlen(info->host) + strlen(info->path) + 16 > sizeof(info->path)) {
  212.         CORE_LOG(eLOG_Error,
  213.                  "[ConnNetInfo_AdjustForHttpProxy]  Adjusted path too long");
  214.         assert(0);
  215.         return 0/*false*/;
  216.     }
  217.     {{
  218.         char x_path[sizeof(info->path)];
  219.         sprintf(x_path, "http://%s:%hu%s%s", info->host, info->port,
  220.                 *info->path == '/' ? "" : "/", info->path);
  221.         assert(strlen(x_path) < sizeof(x_path));
  222.         strcpy(info->path, x_path);
  223.     }}
  224.     assert(sizeof(info->host) >= sizeof(info->http_proxy_host));
  225.     strncpy0(info->host, info->http_proxy_host, sizeof(info->host) - 1);
  226.     info->port = info->http_proxy_port;
  227.     info->http_proxy_adjusted = 1/*true*/;
  228.     return 1/*true*/;
  229. }
  230. extern int/*bool*/ ConnNetInfo_ParseURL(SConnNetInfo* info, const char* url)
  231. {
  232.     const char *s, *a;
  233.     char* p;
  234.     if (info->http_proxy_adjusted) {
  235.         /* undo proxy adjustment */
  236.         SConnNetInfo* temp = ConnNetInfo_Create(info->service);
  237.         if (!ConnNetInfo_ParseURL(temp, info->path)) {
  238.             ConnNetInfo_Destroy(temp);
  239.             return 0/*failure*/;
  240.         }
  241.         memcpy(info->host, temp->host, sizeof(info->host));
  242.         info->port = temp->port;
  243.         memcpy(info->path, temp->path, sizeof(info->path));
  244.         ConnNetInfo_Destroy(temp);
  245.         info->http_proxy_adjusted = 0/*false*/;
  246.     }
  247.     /* host & port first [both optional] */
  248.     if ((s = strstr(url, "://")) != 0) {
  249.         const char* h = s + 3; /* host starts here */
  250.         if (strncasecmp(url, "http://", 7) != 0)
  251.             return 0/*failure*/;
  252.         if (!(s = strchr(h, '/')))
  253.             s = h + strlen(h);
  254.         /* host ends at "a" */
  255.         if ((a = strchr(h, ':')) != 0 && a < s) {
  256.             unsigned short port;
  257.             int n;
  258.             if (sscanf(a, ":%hu%n", &port, &n) < 1 || a + n != s)
  259.                 return 0/*failure*/;
  260.             info->port = port;
  261.         } else
  262.             a = s;
  263.         if ((size_t)(a - h) < sizeof(info->host)) {
  264.             memcpy(info->host, h, (size_t)(a - h));
  265.             info->host[(size_t)(a - h)] = '';
  266.         } else {
  267.             memcpy(info->host, h, sizeof(info->host) - 1);
  268.             info->host[sizeof(info->host) - 1] = '';
  269.         }
  270.     } else
  271.         s = url;
  272.     /* arguments */
  273.     if ((a = strchr(s, '?')) != 0)
  274.         strncpy0(info->args, a + 1, sizeof(info->args) - 1);
  275.     else
  276.         a = s + strlen(s);
  277.     /* path (NB: can be relative) */
  278.     if (s != url || *s == '/' || !(p = strrchr(info->path, '/'))) {
  279.         /* absolute path */
  280.         p = info->path;
  281.         if (!*s) {
  282.             s = "/";   /* in case of an empty path we take the root '/' */
  283.             a = s + 1;
  284.         }
  285.     } else
  286.         p++;
  287.     if ((size_t)(a - s) < sizeof(info->path) - (size_t)(p - info->path)) {
  288.         memcpy(p, s, (size_t)(a - s));
  289.         p[(size_t)(a - s)] = '';
  290.     } else {
  291.         memcpy(p, s, sizeof(info->path) - (size_t)(p - info->path) - 1);
  292.         info->path[sizeof(info->path) - 1] = '';
  293.     }
  294.     return 1/*success*/;
  295. }
  296. extern int/*bool*/ ConnNetInfo_SetUserHeader(SConnNetInfo* info,
  297.                                       const char*   user_header)
  298. {
  299.     if (info->http_user_header)
  300.         free((void*) info->http_user_header);
  301.     if (user_header && *user_header) {
  302.         info->http_user_header = strdup(user_header);
  303.         return info->http_user_header ? 1/*success*/ : 0/*failure*/;
  304.     } else
  305.         info->http_user_header = 0;
  306.     return 1/*success*/;
  307. }
  308. extern int/*bool*/ ConnNetInfo_AppendUserHeader(SConnNetInfo* info,
  309.                                                 const char*   user_header)
  310. {
  311.     size_t oldlen, newlen;
  312.     char* new_header;
  313.     if (!info->http_user_header || !(oldlen = strlen(info->http_user_header)))
  314.         return ConnNetInfo_SetUserHeader(info, user_header);
  315.     if (!user_header || !(newlen = strlen(user_header)))
  316.         return 1/*success*/;
  317.     new_header = (char*)
  318.         realloc((void*) info->http_user_header, oldlen + newlen + 1);
  319.     if (!new_header)
  320.         return 0/*failure*/;
  321.     memcpy(&new_header[oldlen], user_header, newlen + 1);
  322.     info->http_user_header = new_header;
  323.     return 1/*success*/;
  324. }
  325. typedef enum {
  326.     eUserHeaderOp_Delete,
  327.     eUserHeaderOp_Extend,
  328.     eUserHeaderOp_Override
  329. } EUserHeaderOp;
  330. static int/*bool*/ s_ModifyUserHeader(SConnNetInfo* info,
  331.                                       const char*   user_header,
  332.                                       EUserHeaderOp op)
  333. {
  334.     int/*bool*/ retval;
  335.     char*  new_header;
  336.     size_t newlinelen;
  337.     size_t newhdrlen;
  338.     char*  newline;
  339.     size_t hdrlen;
  340.     char*  hdr;
  341.     if (!user_header || !(newhdrlen = strlen(user_header)))
  342.         return 1/*success*/;
  343.     if (!(hdr = (char*) info->http_user_header) || !(hdrlen = strlen(hdr))) {
  344.         if (op == eUserHeaderOp_Delete)
  345.             return 1/*success*/;
  346.         if (!hdr && !(hdr = strdup("")))
  347.             return 0/*failure*/;
  348.         hdrlen = 0;
  349.     }
  350.     if (op != eUserHeaderOp_Delete) {
  351.         if (!(new_header = (char*) malloc(newhdrlen + 1)))
  352.             return 0/*failure*/;
  353.         memcpy(new_header, user_header, newhdrlen + 1);
  354.     } else
  355.         new_header = (char*) user_header; /* we actually won't modify it! */
  356.     retval = 1/*assume best: success*/;
  357.     for (newline = new_header; *newline; newline += newlinelen) {
  358.         char*  eol = strchr(newline, 'n');
  359.         char*  eot = strchr(newline,  ':');
  360.         int/*bool*/ used = 0;
  361.         size_t newtaglen;
  362.         char*  newtagval;
  363.         size_t linelen;
  364.         char*  line;
  365.         size_t len;
  366.         size_t l;
  367.         newlinelen = (size_t)
  368.             (eol ? eol - newline + 1 : new_header + newhdrlen - newline);
  369.         if (!eot || eot >= newline + newlinelen ||
  370.             !(newtaglen = (size_t)(eot - newline)))
  371.             continue;
  372.         newtagval = newline + newtaglen + 1;
  373.         while (newtagval < newline + newlinelen) {
  374.             if (isspace((unsigned char)(*newtagval)))
  375.                 newtagval++;
  376.             else
  377.                 break;
  378.         }
  379.         switch (op) {
  380.         case eUserHeaderOp_Delete:
  381.             len = 0;
  382.             break;
  383.         case eUserHeaderOp_Extend:
  384.             len = newlinelen - (size_t)(newtagval - newline);
  385.             break;
  386.         case eUserHeaderOp_Override:
  387.             len = newtagval < newline + newlinelen ? newlinelen : 0;
  388.             break;
  389.         default:
  390.             assert(0);
  391.             retval = 0/*failure*/;
  392.             len = 0;
  393.             break;
  394.         }
  395.         for (line = hdr; *line; line += linelen) {
  396.             size_t taglen;
  397.             eol = strchr(line, 'n');
  398.             eot = strchr(line,  ':');
  399.             linelen = (size_t)(eol ? eol - line + 1 : hdr + hdrlen - line);
  400.             if (!eot || eot >= line + linelen)
  401.                 continue;
  402.             taglen = (size_t)(eot - line);
  403.             if (newtaglen != taglen || strncasecmp(newline, line, taglen) != 0)
  404.                 continue;
  405.             if (op == eUserHeaderOp_Extend) {
  406.                 l = linelen + len;
  407.                 if (len && linelen > 1 && line[linelen - 2] == 'r')
  408.                     --l;
  409.             } else
  410.                 l = len;
  411.             if (l != linelen) {
  412.                 size_t off  = (size_t)(line - hdr);
  413.                 if (l > linelen) {
  414.                     char* temp = (char*)realloc(hdr, hdrlen + l - linelen + 1);
  415.                     if (!temp) {
  416.                         retval = 0/*failure*/;
  417.                         continue;
  418.                     }
  419.                     hdr  = temp;
  420.                     line = temp + off;
  421.                 }
  422.                 hdrlen -= linelen;
  423.                 memmove(line + l, line + linelen, hdrlen - off + 1);
  424.                 hdrlen += l;
  425.             }
  426.             if (len) {
  427.                 if (op == eUserHeaderOp_Extend) {
  428.                     char* s = &line[l - len - 1];
  429.                     *s++ = ' ';
  430.                     memcpy(s, newtagval, len);
  431.                 } else
  432.                     memcpy(line, newline, len);
  433.                 linelen = l;
  434.                 used = 1;
  435.             }
  436.         }
  437.         if (op == eUserHeaderOp_Delete)
  438.             continue;
  439.         if (used || !len) {
  440.             memmove(newline, newline + newlinelen,
  441.                     newhdrlen - (size_t)(newline-new_header) - newlinelen + 1);
  442.             newhdrlen -= newlinelen;
  443.             newlinelen = 0;
  444.         }
  445.     }
  446.     info->http_user_header = hdr;
  447.     if (op != eUserHeaderOp_Delete) {
  448.         if (!ConnNetInfo_AppendUserHeader(info, new_header))
  449.             retval = 0/*failure*/;
  450.         free(new_header);
  451.     }
  452.     return retval;
  453. }
  454. extern int/*bool*/ ConnNetInfo_OverrideUserHeader(SConnNetInfo* info,
  455.                                                   const char*   header)
  456. {
  457.     return s_ModifyUserHeader(info, header, eUserHeaderOp_Override);
  458. }
  459. extern void ConnNetInfo_DeleteUserHeader(SConnNetInfo* info,
  460.                                          const char*   header)
  461. {
  462.     verify(s_ModifyUserHeader(info, header, eUserHeaderOp_Delete));
  463. }
  464. extern int/*bool*/ ConnNetInfo_ExtendUserHeader(SConnNetInfo* info,
  465.                                                 const char*   header)
  466. {
  467.     return s_ModifyUserHeader(info, header, eUserHeaderOp_Extend);
  468. }
  469. extern int/*bool*/ ConnNetInfo_AppendArg(SConnNetInfo* info,
  470.                                          const char*   arg,
  471.                                          const char*   val)
  472. {
  473.     const char* amp = "&";
  474.     if (!arg || !*arg)
  475.         return 1/*success*/;
  476.     if (!info->args[0])
  477.         amp = "";
  478.     if (strlen(info->args) + strlen(amp) + strlen(arg) +
  479.         (val ? 1 + strlen(val) : 0) >= sizeof(info->args))
  480.         return 0/*failure*/;
  481.     strcat(info->args, amp);
  482.     strcat(info->args, arg);
  483.     if (!val || !*val)
  484.         return 1/*success*/;
  485.     strcat(info->args, "=");
  486.     strcat(info->args, val);
  487.     return 1/*success*/;
  488. }
  489. extern int/*bool*/ ConnNetInfo_PrependArg(SConnNetInfo* info,
  490.                                           const char*   arg,
  491.                                           const char*   val)
  492. {
  493.     char amp = '&';
  494.     size_t off;
  495.     if (!arg || !*arg)
  496.         return 1/*success*/;
  497.     if (!info->args[0])
  498.         amp = '';
  499.     off = strlen(arg) + (val && *val ? 1 + strlen(val) : 0) + (amp ? 1 : 0);
  500.     if (off + strlen(info->args) >= sizeof(info->args))
  501.         return 0/*failure*/;
  502.     if (amp)
  503.         memmove(&info->args[off], info->args, strlen(info->args) + 1);
  504.     strcpy(info->args, arg);
  505.     if (val && *val) {
  506.         strcat(info->args, "=");
  507.         strcat(info->args, val);
  508.     }
  509.     if (amp)
  510.         info->args[off - 1] = amp;
  511.     return 1/*success*/;
  512. }
  513. extern void ConnNetInfo_DeleteArg(SConnNetInfo* info,
  514.                                   const char*   arg)
  515. {
  516.     size_t argnamelen;
  517.     size_t arglen;
  518.     char*  a;
  519.     if (!arg || !(argnamelen = strcspn(arg, "=&")))
  520.         return;
  521.     for (a = info->args; *a; a += arglen) {
  522.         if (*a == '&')
  523.             a++;
  524.         arglen = strcspn(a, "&");
  525.         if (arglen < argnamelen || strncmp(a, arg, argnamelen) != 0 ||
  526.             (a[argnamelen] && a[argnamelen] != '=' && a[argnamelen] != '&'))
  527.             continue;
  528.         if (a[arglen]) {
  529.             arglen++;     /* for intermediary args, eat '&' separator, too */
  530.             memmove(a, a + arglen, strlen(a + arglen) + 1);
  531.         } else if (a != info->args) {
  532.             *--a = '';  /* last argument in a list: remove trailing '&' */
  533.         } else {
  534.             *a = '';    /* last and the only argument removed */
  535.         }
  536.         arglen = 0;
  537.     }
  538. }
  539. extern int/*bool*/ ConnNetInfo_PreOverrideArg(SConnNetInfo* info,
  540.                                               const char*   arg,
  541.                                               const char*   val)
  542. {
  543.     if (!arg || !*arg)
  544.         return 1/*success*/;
  545.     ConnNetInfo_DeleteArg(info, arg);
  546.     return ConnNetInfo_PrependArg(info, arg, val);
  547. }
  548. extern int/*bool*/ ConnNetInfo_PostOverrideArg(SConnNetInfo* info,
  549.                                                const char*   arg,
  550.                                                const char*   val)
  551. {
  552.     if (!arg || !*arg)
  553.         return 1/*success*/;
  554.     ConnNetInfo_DeleteArg(info, arg);
  555.     return ConnNetInfo_AppendArg(info, arg, val);
  556. }
  557. extern SConnNetInfo* ConnNetInfo_Clone(const SConnNetInfo* info)
  558. {
  559.     SConnNetInfo* x_info;
  560.     if (!info)
  561.         return 0;
  562.     x_info = (SConnNetInfo*) malloc(sizeof(SConnNetInfo) +
  563.                                     (info->service
  564.                                      ? strlen(info->service) + 1 : 0));
  565.     *x_info = *info;
  566.     if (info->timeout  &&  info->timeout != kDefaultTimeout) {
  567.         x_info->tmo     = *info->timeout;
  568.         x_info->timeout = &x_info->tmo;
  569.     }
  570.     if (info->service) {
  571.         char* s = (char*) x_info + sizeof(*x_info);
  572.         strcpy(s, info->service);
  573.         x_info->service = s;
  574.     }
  575.     x_info->http_user_header = 0;
  576.     ConnNetInfo_SetUserHeader(x_info, info->http_user_header);
  577.     return x_info;
  578. }
  579. static void s_SaveString(char* s, const char* name, const char* str) {
  580.     sprintf(s + strlen(s), "%-16.16s: %s%s%sn", name,
  581.             str ? """ : "", str ? str : "NULL", str ? """ : "");
  582. }
  583. static void s_SaveULong(char* s, const char* name, unsigned long lll) {
  584.     sprintf(s + strlen(s), "%-16.16s: %lun", name, lll);
  585. }
  586. static void s_SaveBool(char* s, const char* name, int/*bool*/ bbb) {
  587.     sprintf(s + strlen(s), "%-16.16s: %sn", name, bbb ? "TRUE" : "FALSE");
  588. }
  589. extern void ConnNetInfo_Log(const SConnNetInfo* info, LOG lg)
  590. {
  591.     char* s;
  592.     if (!lg)
  593.         return;
  594.     if (!info) {
  595.         LOG_Write(lg, eLOG_Trace, 0, 0, 0, "ConnNetInfo_Log: NULL info");
  596.         return;
  597.     }
  598.     if (!(s = (char*) malloc(sizeof(*info) + 4096 +
  599.                              (info->service ? strlen(info->service) : 0) +
  600.                              (info->http_user_header
  601.                               ? strlen(info->http_user_header) : 0)))) {
  602.         LOG_WRITE(lg, eLOG_Error, "ConnNetInfo_Log: Cannot alloc temp buffer");
  603.         return;
  604.     }
  605.     strcpy(s, "ConnNetInfo_Logn"
  606.            "#################### [BEGIN] SConnNetInfo:n");
  607.     s_SaveString    (s, "service",         info->service);
  608.     s_SaveString    (s, "client_host",     info->client_host);
  609.     s_SaveString    (s, "host",            info->host);
  610.     s_SaveULong     (s, "port",            info->port);
  611.     s_SaveString    (s, "path",            info->path);
  612.     s_SaveString    (s, "args",            info->args);
  613.     s_SaveString    (s, "req_method",     (info->req_method == eReqMethod_Any
  614.                                            ? DEF_CONN_REQ_METHOD
  615.                                            : (info->req_method
  616.                                               == eReqMethod_Get
  617.                                               ? "GET"
  618.                                               : (info->req_method
  619.                                                  == eReqMethod_Post
  620.                                                  ? "POST" : "Unknown"))));
  621.     if (info->timeout) {
  622.         s_SaveULong (s, "timeout(sec)",    info->timeout->sec);
  623.         s_SaveULong (s, "timeout(usec)",   info->timeout->usec);
  624.     } else
  625.         s_SaveString(s, "timeout",         "infinite");
  626.     s_SaveULong     (s, "max_try",         info->max_try);
  627.     s_SaveString    (s, "http_proxy_host", info->http_proxy_host);
  628.     s_SaveULong     (s, "http_proxy_port", info->http_proxy_port);
  629.     s_SaveString    (s, "proxy_host",      info->proxy_host);
  630.     s_SaveString    (s, "debug_printout", (info->debug_printout
  631.                                            == eDebugPrintout_None
  632.                                            ? "NONE"
  633.                                            : (info->debug_printout
  634.                                               == eDebugPrintout_Some
  635.                                               ? "SOME"
  636.                                               : (info->debug_printout
  637.                                                  == eDebugPrintout_Data
  638.                                                  ? "DATA" : "Unknown"))));
  639.     s_SaveBool      (s, "stateless",       info->stateless);
  640.     s_SaveBool      (s, "firewall",        info->firewall);
  641.     s_SaveBool      (s, "lb_disable",      info->lb_disable);
  642.     s_SaveString    (s, "user_header",     info->http_user_header);
  643.     s_SaveBool      (s, "proxy_adjusted",  info->http_proxy_adjusted);
  644.     strcat(s, "#################### [END] SConnNetInfon");
  645.     LOG_Write(lg, eLOG_Trace, 0, 0, 0, s);
  646.     free(s);
  647. }
  648. extern void ConnNetInfo_Destroy(SConnNetInfo* info)
  649. {
  650.     if (!info)
  651.         return;
  652.     ConnNetInfo_SetUserHeader(info, 0);
  653.     free(info);
  654. }
  655. extern SOCK URL_Connect
  656. (const char*     host,
  657.  unsigned short  port,
  658.  const char*     path,
  659.  const char*     args,
  660.  EReqMethod      req_method,
  661.  size_t          content_length,
  662.  const STimeout* c_timeout,
  663.  const STimeout* rw_timeout,
  664.  const char*     user_hdr,
  665.  int/*bool*/     encode_args,
  666.  ESwitch         log)
  667. {
  668.     static const char X_REQ_Q[] = "?";
  669.     static const char X_REQ_E[] = " HTTP/1.0rn";
  670.     static const char X_HOST[]  = "Host: ";
  671.     EIO_Status  st;
  672.     BUF         buf;
  673.     SOCK        sock;
  674.     char*       header;
  675.     char        buffer[80];
  676.     size_t      headersize;
  677.     const char* x_args = 0;
  678.     const char* x_req_r; /* "POST "/"GET " */
  679.     /* check the args */
  680.     if (!host  ||  !*host  ||  !port  ||  !path  ||  !*path  ||
  681.         (user_hdr  &&  *user_hdr  &&  user_hdr[strlen(user_hdr)-1] != 'n')) {
  682.         CORE_LOG(eLOG_Error, "[URL_Connect]  Bad arguments");
  683.         assert(0);
  684.         return 0/*error*/;
  685.     }
  686.     switch (req_method) {
  687.     case eReqMethod_Any:
  688.         x_req_r = DEF_CONN_REQ_METHOD " ";
  689.         break;
  690.     case eReqMethod_Post:
  691.         x_req_r = "POST ";
  692.         break;
  693.     case eReqMethod_Get:
  694.         x_req_r = "GET ";
  695.         break;
  696.     default:
  697.         CORE_LOGF(eLOG_Error, ("[URL_Connect]  Unrecognized request method"
  698.                                " (%d)", (int) req_method));
  699.         assert(0);
  700.         return 0/*error*/;
  701.     }
  702.     if (content_length  &&  strcasecmp(x_req_r, "GET ") == 0) {
  703.         CORE_LOG(eLOG_Warning,
  704.                  "[URL_Connect]  Content length ignored with GET");
  705.         content_length = 0;
  706.     }
  707.     /* URL-encode "args", if any specified */
  708.     if (args  &&  *args) {
  709.         size_t src_size = strlen(args);
  710.         if ( encode_args ) {
  711.             size_t dst_size = 3 * src_size;
  712.             size_t src_read, dst_written;
  713.             char* xx_args = (char*) malloc(dst_size + 1);
  714.             if (!xx_args)
  715.                 return 0/*failure: no memory*/;
  716.             URL_Encode(args,    src_size, &src_read,
  717.                        xx_args, dst_size, &dst_written);
  718.             xx_args[dst_written] = '';
  719.             assert(src_read == src_size);
  720.             x_args = xx_args;
  721.         } else
  722.             x_args = args;
  723.     }
  724.     buf = 0;
  725.     errno = 0;
  726.     /* compose HTTP header */
  727.     if (/* {POST|GET} <path>?<args> HTTP/1.0rn */
  728.         !BUF_Write(&buf, x_req_r, strlen(x_req_r))            ||
  729.         !BUF_Write(&buf, path,    strlen(path))               ||
  730.         (x_args
  731.          &&  (!BUF_Write(&buf, X_REQ_Q, sizeof(X_REQ_Q) - 1)  ||
  732.               !BUF_Write(&buf, x_args,  strlen(x_args))))     ||
  733.         !BUF_Write(&buf,       X_REQ_E, sizeof(X_REQ_E) - 1)  ||
  734.         /* Host: hostrn */
  735.         !BUF_Write(&buf, X_HOST, sizeof(X_HOST) - 1)          ||
  736.         !BUF_Write(&buf, host,   strlen(host))                ||
  737.         !BUF_Write(&buf, "rn", 2)                           ||
  738.         /* <user_header> */
  739.         (user_hdr
  740.          &&  !BUF_Write(&buf, user_hdr, strlen(user_hdr)))    ||
  741.         /* Content-Length: <content_length>rnrn */
  742.         (req_method != eReqMethod_Get
  743.          &&  (sprintf(buffer, "Content-Length: %lurn",
  744.                       (unsigned long) content_length) <= 0    ||
  745.               !BUF_Write(&buf, buffer, strlen(buffer))))      ||
  746.         !BUF_Write(&buf, "rn", 2)) {
  747.         CORE_LOGF(eLOG_Error, ("[URL_Connect]  Error composing HTTP header for"
  748.                                " %s:%hu%s%s", host, port, errno ? ": " : "",
  749.                                errno ? strerror(errno) : ""));
  750.         BUF_Destroy(buf);
  751.         if (x_args  &&  x_args != args)
  752.             free((void*) x_args);
  753.         return 0/*error*/;
  754.     }
  755.     if (x_args  &&  x_args != args)
  756.         free((void*) x_args);
  757.     if (!(header = (char*) malloc(headersize = BUF_Size(buf))) ||
  758.         BUF_Read(buf, header, headersize) != headersize) {
  759.         CORE_LOGF(eLOG_Error, ("[URL_Connect]  Error storing HTTP header for"
  760.                                " %s:%hu: %s", host, port,
  761.                                errno ? strerror(errno) : "Unknown error"));
  762.         if (header)
  763.             free(header);
  764.         BUF_Destroy(buf);
  765.         return 0/*error*/;
  766.     }
  767.     BUF_Destroy(buf);
  768.     /* connect to HTTPD */
  769.     st = SOCK_CreateEx(host, port, c_timeout, &sock, header, headersize, log);
  770.     free(header);
  771.     if (st != eIO_Success) {
  772.         CORE_LOGF(eLOG_Error,
  773.                   ("[URL_Connect]  Socket connect to %s:%hu failed: %s",
  774.                    host, port, IO_StatusStr(st)));
  775.         return 0/*error*/;
  776.     }
  777.     /* setup I/O timeout for the connection */
  778.     if (SOCK_SetTimeout(sock, eIO_ReadWrite, rw_timeout) != eIO_Success) {
  779.         CORE_LOG(eLOG_Error, "[URL_Connect]  Cannot set connection timeout");
  780.         SOCK_Close(sock);
  781.         return 0/*error*/;
  782.     }
  783.     /* success */
  784.     return sock;
  785. }
  786. /* Code for the "*_StripToPattern()" functions
  787.  */
  788. typedef EIO_Status (*FDoIO)
  789.      (void*     stream,
  790.       void*     buf,
  791.       size_t    size,
  792.       size_t*   n_read,
  793.       EIO_Event what     /* eIO_Read | eIO_Write (to pushback) */
  794.       );
  795. static EIO_Status s_StripToPattern
  796. (void*       stream,
  797.  FDoIO       io_func,
  798.  const void* pattern,
  799.  size_t      pattern_size,
  800.  BUF*        buf,
  801.  size_t*     n_discarded)
  802. {
  803.     EIO_Status status;
  804.     char*      buffer;
  805.     size_t     buffer_size;
  806.     size_t     n_read = 0;
  807.     /* check args */
  808.     if ( n_discarded )
  809.         *n_discarded = 0;
  810.     if (!stream  ||  (pattern != 0) != (pattern_size != 0))
  811.         return eIO_InvalidArg;
  812.     /* allocate a temporary read buffer */
  813.     buffer_size = 2 * pattern_size;
  814.     if (buffer_size < 4096)
  815.         buffer_size = 4096;
  816.     if ( !(buffer = (char*) malloc(buffer_size)) )
  817.         return eIO_Unknown;
  818.     if ( !pattern ) {
  819.         /* read/discard until EOF */
  820.         do {
  821.             status = io_func(stream, buffer, buffer_size, &n_read, eIO_Read);
  822.             if ( buf )
  823.                 BUF_Write(buf, buffer, n_read);
  824.             if ( n_discarded )
  825.                 *n_discarded += n_read;
  826.         } while (status == eIO_Success);
  827.     } else {
  828.         for (;;) {
  829.             /* read; search for the pattern; store the discarded data */
  830.             size_t x_read, n_stored;
  831.             assert(n_read < pattern_size);
  832.             status = io_func(stream, buffer + n_read, buffer_size - n_read,
  833.                              &x_read, eIO_Read);
  834.             if ( !x_read ) {
  835.                 assert(status != eIO_Success);
  836.                 break; /*error*/
  837.             }
  838.             n_stored = n_read + x_read;
  839.             if (n_stored >= pattern_size) {
  840.                 /* search for the pattern */
  841.                 size_t n_check = n_stored - pattern_size + 1;
  842.                 const char* b;
  843.                 for (b = buffer;  n_check;  b++, n_check--) {
  844.                     if (*b != *((const char*) pattern))
  845.                         continue;
  846.                     if (memcmp(b, pattern, pattern_size) == 0)
  847.                         break; /*found*/
  848.                 }
  849.                 /* pattern found */
  850.                 if ( n_check ) {
  851.                     size_t x_discarded = (size_t)(b - buffer) + pattern_size;
  852.                     if ( buf )
  853.                         BUF_Write(buf, buffer + n_read, x_discarded - n_read);
  854.                     if ( n_discarded )
  855.                         *n_discarded += x_discarded;
  856.                     /* return unused portion to the stream */
  857.                     status = io_func(stream, buffer + x_discarded,
  858.                                      n_stored - x_discarded, 0, eIO_Write);
  859.                     break; /*finished*/
  860.                 }
  861.             }
  862.             /* pattern not found yet */
  863.             if ( buf )
  864.                 BUF_Write(buf, buffer + n_read, x_read);
  865.             if ( n_discarded )
  866.                 *n_discarded += x_read;
  867.             n_read = n_stored;
  868.             if (n_read > pattern_size) {
  869.                 size_t n_cut = n_read - pattern_size + 1;
  870.                 n_read = pattern_size - 1;
  871.                 memmove(buffer, buffer + n_cut, n_read);
  872.             }
  873.         }
  874.     }
  875.     /* cleanup & exit */
  876.     free(buffer);
  877.     return status;
  878. }
  879. static EIO_Status s_CONN_IO
  880. (void*     stream,
  881.  void*     buf,
  882.  size_t    size,
  883.  size_t*   n_read,
  884.  EIO_Event what)
  885. {
  886.     switch (what) {
  887.     case eIO_Read:
  888.         return CONN_Read((CONN) stream, buf, size, n_read, eIO_ReadPlain);
  889.     case eIO_Write:
  890.         assert(stream);
  891.         return CONN_PushBack((CONN) stream, buf, size);
  892.     default:
  893.         break;
  894.     }
  895.     return eIO_InvalidArg;
  896. }
  897. extern EIO_Status CONN_StripToPattern
  898. (CONN        conn,
  899.  const void* pattern,
  900.  size_t      pattern_size,
  901.  BUF*        buf,
  902.  size_t*     n_discarded)
  903. {
  904.     return s_StripToPattern
  905.         (conn, s_CONN_IO, pattern, pattern_size, buf, n_discarded);
  906. }
  907. static EIO_Status s_SOCK_IO
  908. (void*     stream,
  909.  void*     buf,
  910.  size_t    size,
  911.  size_t*   n_read,
  912.  EIO_Event what)
  913. {
  914.     switch (what) {
  915.     case eIO_Read:
  916.         return SOCK_Read((SOCK) stream, buf, size, n_read, eIO_ReadPlain);
  917.     case eIO_Write:
  918.         return SOCK_PushBack((SOCK) stream, buf, size);
  919.     default:
  920.         break;
  921.     }
  922.     return eIO_InvalidArg;
  923. }
  924. extern EIO_Status SOCK_StripToPattern
  925. (SOCK        sock,
  926.  const void* pattern,
  927.  size_t      pattern_size,
  928.  BUF*        buf,
  929.  size_t*     n_discarded)
  930. {
  931.     return s_StripToPattern
  932.         (sock, s_SOCK_IO, pattern, pattern_size, buf, n_discarded);
  933. }
  934. static EIO_Status s_BUF_IO
  935. (void*     stream,
  936.  void*     buf,
  937.  size_t    size,
  938.  size_t*   n_read,
  939.  EIO_Event what)
  940. {
  941.     switch (what) {
  942.     case eIO_Read:
  943.         *n_read = BUF_Read((BUF) stream, buf, size);
  944.         return *n_read ? eIO_Success : eIO_Closed;
  945.     case eIO_Write:
  946.         assert(stream);
  947.         return BUF_PushBack((BUF*) &stream, buf, size)
  948.             ? eIO_Success : eIO_Unknown;
  949.     default:
  950.         break;
  951.     }
  952.     return eIO_InvalidArg;
  953. }
  954. extern EIO_Status BUF_StripToPattern
  955. (BUF         buffer,
  956.  const void* pattern,
  957.  size_t      pattern_size,
  958.  BUF*        buf,
  959.  size_t*     n_discarded)
  960. {
  961.     return s_StripToPattern
  962.         (buffer, s_BUF_IO, pattern, pattern_size, buf, n_discarded);
  963. }
  964. /* Return integer (0..15) corresponding to the "ch" as a hex digit
  965.  * Return -1 on error
  966.  */
  967. static int s_HexChar(char ch)
  968. {
  969.     if ('0' <= ch  &&  ch <= '9')
  970.         return ch - '0';
  971.     if ('a' <= ch  &&  ch <= 'f')
  972.         return 10 + (ch - 'a');
  973.     if ('A' <= ch  &&  ch <= 'F')
  974.         return 10 + (ch - 'A');
  975.     return -1;
  976. }
  977. /* The URL-encoding table
  978.  */
  979. static const char s_Encode[256][4] = {
  980.     "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
  981.     "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F",
  982.     "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
  983.     "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F",
  984.     "+",   "!",   "%22", "%23", "$",   "%25", "%26", "'",
  985.     "(",   ")",   "*",   "%2B", ",",   "-",   ".",   "%2F",
  986.     "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",
  987.     "8",   "9",   "%3A", "%3B", "%3C", "%3D", "%3E", "%3F",
  988.     "%40", "A",   "B",   "C",   "D",   "E",   "F",   "G",
  989.     "H",   "I",   "J",   "K",   "L",   "M",   "N",   "O",
  990.     "P",   "Q",   "R",   "S",   "T",   "U",   "V",   "W",
  991.     "X",   "Y",   "Z",   "%5B", "%5C", "%5D", "%5E", "_",
  992.     "%60", "a",   "b",   "c",   "d",   "e",   "f",   "g",
  993.     "h",   "i",   "j",   "k",   "l",   "m",   "n",   "o",
  994.     "p",   "q",   "r",   "s",   "t",   "u",   "v",   "w",
  995.     "x",   "y",   "z",   "%7B", "%7C", "%7D", "%7E", "%7F",
  996.     "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
  997.     "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F",
  998.     "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
  999.     "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
  1000.     "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7",
  1001.     "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF",
  1002.     "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7",
  1003.     "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF",
  1004.     "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
  1005.     "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF",
  1006.     "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7",
  1007.     "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF",
  1008.     "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7",
  1009.     "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
  1010.     "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7",
  1011.     "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
  1012. };
  1013. #define VALID_URL_SYMBOL(ch)  (s_Encode[(unsigned char)ch][0] != '%')
  1014. extern int/*bool*/ URL_DecodeEx
  1015. (const void* src_buf,
  1016.  size_t      src_size,
  1017.  size_t*     src_read,
  1018.  void*       dst_buf,
  1019.  size_t      dst_size,
  1020.  size_t*     dst_written,
  1021.  const char* allow_symbols)
  1022. {
  1023.     unsigned char* src = (unsigned char*) src_buf;
  1024.     unsigned char* dst = (unsigned char*) dst_buf;
  1025.     *src_read    = 0;
  1026.     *dst_written = 0;
  1027.     if (!src_size  ||  !dst_size)
  1028.         return 1/*true*/;
  1029.     for ( ;  *src_read != src_size  &&  *dst_written != dst_size;
  1030.           (*src_read)++, (*dst_written)++, src++, dst++) {
  1031.         switch ( *src ) {
  1032.         case '%': {
  1033.             int i1, i2;
  1034.             if (*src_read + 2 > src_size)
  1035.                 return 1/*true*/;
  1036.             if ((i1 = s_HexChar(*(++src))) == -1)
  1037.                 return *dst_written ? 1/*true*/ : 0/*false*/;
  1038.             if (*src_read + 3 > src_size)
  1039.                 return 1/*true*/;
  1040.             if ((i2 = s_HexChar(*(++src))) == -1)
  1041.                 return *dst_written ? 1/*true*/ : 0/*false*/;
  1042.             *dst = (unsigned char)((i1 << 4) + i2);
  1043.             *src_read += 2;
  1044.             break;
  1045.         }
  1046.         case '+': {
  1047.             *dst = ' ';
  1048.             break;
  1049.         }
  1050.         default: {
  1051.             if (VALID_URL_SYMBOL(*src)  ||
  1052.                 (allow_symbols  &&  strchr(allow_symbols, *src)))
  1053.                 *dst = *src;
  1054.             else
  1055.                 return *dst_written ? 1/*true*/ : 0/*false*/;
  1056.         }
  1057.         }/*switch*/
  1058.     }
  1059.     assert(src == (unsigned char*) src_buf + *src_read   );
  1060.     assert(dst == (unsigned char*) dst_buf + *dst_written);
  1061.     return 1/*true*/;
  1062. }
  1063. extern int/*bool*/ URL_Decode
  1064. (const void* src_buf,
  1065.  size_t      src_size,
  1066.  size_t*     src_read,
  1067.  void*       dst_buf,
  1068.  size_t      dst_size,
  1069.  size_t*     dst_written)
  1070. {
  1071.     return URL_DecodeEx
  1072.         (src_buf, src_size, src_read, dst_buf, dst_size, dst_written, 0);
  1073. }
  1074. extern void URL_Encode
  1075. (const void* src_buf,
  1076.  size_t      src_size,
  1077.  size_t*     src_read,
  1078.  void*       dst_buf,
  1079.  size_t      dst_size,
  1080.  size_t*     dst_written)
  1081. {
  1082.     unsigned char* src = (unsigned char*) src_buf;
  1083.     unsigned char* dst = (unsigned char*) dst_buf;
  1084.     *src_read    = 0;
  1085.     *dst_written = 0;
  1086.     if (!src_size  ||  !dst_size)
  1087.         return;
  1088.     for ( ;  *src_read != src_size  &&  *dst_written != dst_size;
  1089.           (*src_read)++, (*dst_written)++, src++, dst++) {
  1090.         const char* subst = s_Encode[*src];
  1091.         if (*subst != '%') {
  1092.             *dst = *subst;
  1093.         } else if (*dst_written < dst_size - 2) {
  1094.             *dst = '%';
  1095.             *(++dst) = *(++subst);
  1096.             *(++dst) = *(++subst);
  1097.             *dst_written += 2;
  1098.         } else {
  1099.             return;
  1100.         }
  1101.     }
  1102.     assert(src == (unsigned char*) src_buf + *src_read   );
  1103.     assert(dst == (unsigned char*) dst_buf + *dst_written);
  1104. }
  1105. /****************************************************************************
  1106.  * NCBI-specific MIME content type and sub-types
  1107.  */
  1108. static const char* s_MIME_Type[eMIME_T_Unknown+1] = {
  1109.     "x-ncbi-data",
  1110.     "text",
  1111.     "application",
  1112.     "unknown"
  1113. };
  1114. static const char* s_MIME_SubType[eMIME_Unknown+1] = {
  1115.     "x-dispatch",
  1116.     "x-asn-text",
  1117.     "x-asn-binary",
  1118.     "x-fasta",
  1119.     "x-www-form",
  1120.     "html",
  1121.     "plain",
  1122.     "xml",
  1123.     "xml+soap",
  1124.     "x-unknown"
  1125. };
  1126. static const char* s_MIME_Encoding[eENCOD_Unknown+1] = {
  1127.     "",
  1128.     "urlencoded",
  1129.     "encoded"
  1130. };
  1131. extern char* MIME_ComposeContentTypeEx
  1132. (EMIME_Type     type,
  1133.  EMIME_SubType  subtype,
  1134.  EMIME_Encoding encoding,
  1135.  char*          buf,
  1136.  size_t         buflen)
  1137. {
  1138.     static const char s_ContentType[] = "Content-Type: ";
  1139.     const char*       x_Type          = s_MIME_Type    [(int) type];
  1140.     const char*       x_SubType       = s_MIME_SubType [(int) subtype];
  1141.     const char*       x_Encoding      = s_MIME_Encoding[(int) encoding];
  1142.     char              x_buf[MAX_CONTENT_TYPE_LEN];
  1143.     if ( *x_Encoding ) {
  1144.         assert(sizeof(s_ContentType) + strlen(x_Type) + strlen(x_SubType)
  1145.                + strlen(x_Encoding) + 4 < MAX_CONTENT_TYPE_LEN);
  1146.         sprintf(x_buf, "%s%s/%s-%srn",
  1147.                 s_ContentType, x_Type, x_SubType, x_Encoding);
  1148.     } else {
  1149.         assert(sizeof(s_ContentType) + strlen(x_Type) + strlen(x_SubType)
  1150.                + 3 < MAX_CONTENT_TYPE_LEN);
  1151.         sprintf(x_buf, "%s%s/%srn", s_ContentType, x_Type, x_SubType);
  1152.     }
  1153.     assert(strlen(x_buf) < sizeof(x_buf));
  1154.     assert(strlen(x_buf) < buflen);
  1155.     strncpy0(buf, x_buf, buflen - 1);
  1156.     return buf;
  1157. }
  1158. extern char* MIME_ComposeContentType
  1159. (EMIME_SubType  subtype,
  1160.  EMIME_Encoding encoding,
  1161.  char*          buf,
  1162.  size_t         buflen)
  1163. {
  1164.     return MIME_ComposeContentTypeEx(eMIME_T_NcbiData,
  1165.                                      subtype, encoding, buf, buflen);
  1166. }
  1167. extern int/*bool*/ MIME_ParseContentTypeEx
  1168. (const char*     str,
  1169.  EMIME_Type*     type,
  1170.  EMIME_SubType*  subtype,
  1171.  EMIME_Encoding* encoding)
  1172. {
  1173.     char* x_buf;
  1174.     char* x_type;
  1175.     char* x_subtype;
  1176.     int   i;
  1177.     if ( type )
  1178.         *type = eMIME_T_Unknown;
  1179.     if ( subtype )
  1180.         *subtype = eMIME_Unknown;
  1181.     if ( encoding )
  1182.         *encoding = eENCOD_Unknown;
  1183.     if (!str  ||  !*str)
  1184.         return 0/*false*/;
  1185.     {{
  1186.         size_t x_size = strlen(str) + 1;
  1187.         x_buf  = (char*) malloc(2 * x_size);
  1188.         x_type = x_buf  + x_size;
  1189.     }}
  1190.     strcpy(x_buf, str);
  1191.     strlwr(x_buf);
  1192.     if ((sscanf(x_buf, " content-type: %s ", x_type) != 1  &&
  1193.          sscanf(x_buf, " %s ", x_type) != 1)  ||
  1194.         (x_subtype = strchr(x_type, '/')) == 0) {
  1195.         free(x_buf);
  1196.         return 0/*false*/;
  1197.     }
  1198.     *x_subtype++ = '';
  1199.     if ( type ) {
  1200.         for (i = 0;  i < (int) eMIME_T_Unknown;  i++) {
  1201.             if ( !strcmp(x_type, s_MIME_Type[i]) ) {
  1202.                 *type = (EMIME_Type) i;
  1203.                 break;
  1204.             }
  1205.         }
  1206.     }
  1207.     for (i = 0;  i < (int) eENCOD_Unknown;  i++) {
  1208.         char* x_encoding = strstr(x_subtype, s_MIME_Encoding[i]);
  1209.         if (x_encoding  &&  *x_encoding  &&
  1210.             x_encoding != x_subtype  &&  *(x_encoding - 1) == '-'  &&
  1211.             strcmp(x_encoding, s_MIME_Encoding[i]) == 0) {
  1212.             if ( encoding ) {
  1213.                 *encoding = (EMIME_Encoding) i;
  1214.             }
  1215.             *(x_encoding - 1) = '';
  1216.             break;
  1217.         }
  1218.     }
  1219.     if (encoding  &&  *encoding == eENCOD_Unknown)
  1220.         *encoding = eENCOD_None;
  1221.     if ( subtype ) {
  1222.         for (i = 0;  i < (int) eMIME_Unknown;  i++) {
  1223.             if ( !strcmp(x_subtype, s_MIME_SubType[i]) ) {
  1224.                 *subtype = (EMIME_SubType) i;
  1225.                 break;
  1226.             }
  1227.         }
  1228.     }
  1229.     free(x_buf);
  1230.     return 1/*true*/;
  1231. }
  1232. extern int/*bool*/ MIME_ParseContentType
  1233. (const char*     str,
  1234.  EMIME_SubType*  subtype,
  1235.  EMIME_Encoding* encoding)
  1236. {
  1237.     EMIME_Type type;
  1238.     if ( !MIME_ParseContentTypeEx(str, &type, subtype, encoding) )
  1239.         return 0/*false*/;
  1240.     if (type != eMIME_T_NcbiData) {
  1241.         if ( subtype )
  1242.             *subtype  = eMIME_Unknown;
  1243.         if ( encoding )
  1244.             *encoding = eENCOD_Unknown;
  1245.         return 0/*false*/;
  1246.     }
  1247.     return 1/*true*/;
  1248. }
  1249. /****************************************************************************
  1250.  * Reading and writing [host][:port] addresses
  1251.  */
  1252. extern const char* StringToHostPort(const char*     str,
  1253.                                     unsigned int*   host,
  1254.                                     unsigned short* port)
  1255. {
  1256.     unsigned short p;
  1257.     unsigned int h;
  1258.     char abuf[256];
  1259.     const char* s;
  1260.     size_t alen;
  1261.     int n = 0;
  1262.     *host = 0;
  1263.     *port = 0;
  1264.     for (s = str; *s; s++) {
  1265.         if (isspace((unsigned char)(*s)) || *s == ':')
  1266.             break;
  1267.     }
  1268.     if ((alen = (size_t)(s - str)) > sizeof(abuf) - 1)
  1269.         return str;
  1270.     if (alen) {
  1271.         strncpy0(abuf, str, alen);
  1272.         if (!(h = SOCK_gethostbyname(abuf)))
  1273.             return str;
  1274.     } else
  1275.         h = 0;
  1276.     if (*s == ':') {
  1277.         if (sscanf(++s, "%hu%n", &p, &n) < 1 ||
  1278.             (s[n] && !isspace((unsigned char) s[n])))
  1279.             return alen ? 0 : str;
  1280.     } else
  1281.         p = 0;
  1282.     *host = h;
  1283.     *port = p;
  1284.     return s + n;
  1285. }
  1286. extern size_t HostPortToString(unsigned int   host,
  1287.                                unsigned short port,
  1288.                                char*          buf,
  1289.                                size_t         buflen)
  1290. {
  1291.     char   x_buf[16/*sizeof("255.255.255.255")*/ + 8/*:port*/];
  1292.     size_t n;
  1293.     if (!buf || !buflen)
  1294.         return 0;
  1295.     if (!host)
  1296.         *x_buf = 0;
  1297.     else if (SOCK_ntoa(host, x_buf, sizeof(x_buf)) != 0) {
  1298.         *buf = 0;
  1299.         return 0;
  1300.     }
  1301.     n = strlen(x_buf);
  1302.     if (port || !host)
  1303.         n += sprintf(x_buf + n, ":%hu", port);
  1304.     assert(n < sizeof(x_buf));
  1305.     if (n >= buflen)
  1306.         n = buflen - 1;
  1307.     memcpy(buf, x_buf, n);
  1308.     buf[n] = 0;
  1309.     return n;
  1310. }
  1311. /*
  1312.  * --------------------------------------------------------------------------
  1313.  * $Log: ncbi_connutil.c,v $
  1314.  * Revision 1000.3  2004/04/12 17:06:12  gouriano
  1315.  * PRODUCTION: UPGRADED [CATCHUP_003] Dev-tree R6.64
  1316.  *
  1317.  * Revision 6.64  2004/04/06 19:25:56  lavr
  1318.  * Fix ConnNetInfo_DeleteArg() to remove arg's trailing '&'
  1319.  *
  1320.  * Revision 6.63  2004/01/14 18:52:39  lavr
  1321.  * Recognize eMIME_XmlSoap and corresponding text representation "xml+soap"
  1322.  *
  1323.  * Revision 6.62  2004/01/07 19:23:29  lavr
  1324.  * "xml" added as a MIME subtype
  1325.  *
  1326.  * Revision 6.61  2003/11/12 17:46:12  lavr
  1327.  * HostPortToString() changed to be a little more efficient
  1328.  *
  1329.  * Revision 6.60  2003/08/27 16:27:37  lavr
  1330.  * Add "Host:" tag to be able to take advantage of Apache VHosts
  1331.  *
  1332.  * Revision 6.59  2003/08/25 14:44:43  lavr
  1333.  * Employ new k..Timeout constants
  1334.  * URL_Connect():  get rid of one avoidable malloc()
  1335.  * User header manipulation routines:  factor out some arithmetics for speed
  1336.  *
  1337.  * Revision 6.58  2003/05/31 05:13:38  lavr
  1338.  * Replace bitwise XOR with inequality [of the same effect]
  1339.  *
  1340.  * Revision 6.57  2003/05/20 21:25:24  lavr
  1341.  * Limit SConnNetInfo::max_try by reasonable "short" value
  1342.  *
  1343.  * Revision 6.56  2003/05/19 16:44:45  lavr
  1344.  * Minor style adjustements
  1345.  *
  1346.  * Revision 6.55  2003/05/14 03:51:54  lavr
  1347.  * URL_Connect() rewritten to submit HTTP header as SOCK's initial buffer
  1348.  *
  1349.  * Revision 6.54  2003/04/30 17:02:11  lavr
  1350.  * Name collision resolved in ConnNetInfo_ParseURL()
  1351.  *
  1352.  * Revision 6.53  2003/03/06 21:55:31  lavr
  1353.  * s_ModifyUserHeader(): Heed uninitted usage warning
  1354.  * HostPortToString():   Do not append :0 (for zero port) if host is not empty
  1355.  *
  1356.  * Revision 6.52  2003/02/28 14:47:41  lavr
  1357.  * Bugfix: proper bool -> eIO_Status conversion in s_BUF_IO()
  1358.  *
  1359.  * Revision 6.51  2003/01/31 21:17:04  lavr
  1360.  * More robust search for sheme in URL
  1361.  *
  1362.  * Revision 6.50  2003/01/17 19:44:46  lavr
  1363.  * Reduce dependencies
  1364.  *
  1365.  * Revision 6.49  2003/01/15 19:52:25  lavr
  1366.  * *_StripToPattern() calls modified to use Read/PushBack instead of Peek/Read
  1367.  *
  1368.  * Revision 6.48  2002/12/13 21:19:13  lavr
  1369.  * Separate header tag values with spaces as most commonly required (rfc1945)
  1370.  *
  1371.  * Revision 6.47  2002/12/10 17:34:15  lavr
  1372.  * Remove errno decoding on failed connect in URL_Connect()
  1373.  *
  1374.  * Revision 6.46  2002/12/05 21:43:31  lavr
  1375.  * Fix in assignment and compare in URL_Connect()
  1376.  *
  1377.  * Revision 6.45  2002/12/04 16:50:47  lavr
  1378.  * Use SOCK_CreateEx() in URL_Connect()
  1379.  *
  1380.  * Revision 6.44  2002/11/19 19:19:57  lavr
  1381.  * +ConnNetInfo_ExtendUserHeader()
  1382.  *
  1383.  * Revision 6.43  2002/11/13 19:54:13  lavr
  1384.  * ConnNetInfo_DeleteArg(): fix initial argument calculation size
  1385.  *
  1386.  * Revision 6.42  2002/11/12 05:50:33  lavr
  1387.  * Modify client host name discovery
  1388.  *
  1389.  * Revision 6.41  2002/11/01 20:13:50  lavr
  1390.  * Remove MAXHOSTNAMELEN and MAX_IP_ADDR_LEN macros
  1391.  *
  1392.  * Revision 6.40  2002/10/28 15:42:38  lavr
  1393.  * Use "ncbi_ansi_ext.h" privately and use strncpy0()
  1394.  *
  1395.  * Revision 6.39  2002/10/21 18:30:59  lavr
  1396.  * +ConnNetInfo_AppendArg()
  1397.  * +ConnNetInfo_PrependArg()
  1398.  * +ConnNetInfo_DeleteArg()
  1399.  * +ConnNetInfo_PreOverrideArg()
  1400.  * +ConnNetInfo_PostOverrideArg()
  1401.  *
  1402.  * Revision 6.38  2002/10/11 19:42:06  lavr
  1403.  * +ConnNetInfo_AppendUserHeader()
  1404.  * +ConnNetInfo_OverrideUserHeader()
  1405.  * +ConnNetInfo_DeleteUserHeader()
  1406.  *
  1407.  * Revision 6.37  2002/09/24 18:08:34  lavr
  1408.  * Avoid compiler warning in positioning on first but one array element
  1409.  *
  1410.  * Revision 6.36  2002/08/12 15:12:01  lavr
  1411.  * Use persistent SOCK_Write()
  1412.  *
  1413.  * Revision 6.35  2002/08/07 16:32:47  lavr
  1414.  * Changed EIO_ReadMethod enums accordingly; log moved to end
  1415.  *
  1416.  * Revision 6.34  2002/05/06 19:12:57  lavr
  1417.  * -ConnNetInfo_Print(); +ConnNetInfo_Log()
  1418.  * Addition: *_StripToPattern() now can strip until EOF (or error)
  1419.  *
  1420.  * Revision 6.33  2002/04/26 16:31:41  lavr
  1421.  * Add more space between functions to separate them better
  1422.  *
  1423.  * Revision 6.32  2002/04/13 06:36:36  lavr
  1424.  * Fix for empty path parsing in HTTP URL
  1425.  *
  1426.  * Revision 6.31  2002/03/19 22:13:09  lavr
  1427.  * Minor tweak in recognizing "infinite" (and part) as a special timeout
  1428.  *
  1429.  * Revision 6.30  2002/03/11 21:53:18  lavr
  1430.  * Recognize ALL in CONN_DEBUG_PRINTOUT; bugfix for '//' in proxy adjustement
  1431.  *
  1432.  * Revision 6.29  2002/02/20 19:12:17  lavr
  1433.  * Swapped eENCOD_Url and eENCOD_None; eENCOD_Unknown introduced
  1434.  *
  1435.  * Revision 6.28  2002/02/08 22:22:17  lavr
  1436.  * BUGFIX: sizeof(info) -> sizeof(*info) in ConnNetInfo_Create()
  1437.  *
  1438.  * Revision 6.27  2002/01/30 20:14:48  lavr
  1439.  * URL_Connect(): Print error code in some failure messages
  1440.  *
  1441.  * Revision 6.26  2002/01/28 20:21:46  lavr
  1442.  * Do not store "" as a user_header
  1443.  *
  1444.  * Revision 6.25  2001/12/30 19:40:32  lavr
  1445.  * +ConnNetInfo_ParseURL()
  1446.  * Added recordkeeping of service name for which the info was created
  1447.  *
  1448.  * Revision 6.24  2001/12/04 15:56:28  lavr
  1449.  * Use strdup() instead of explicit strcpy(malloc(...), ...)
  1450.  *
  1451.  * Revision 6.23  2001/09/24 20:27:00  lavr
  1452.  * Message corrected: "Adjusted path too long"
  1453.  *
  1454.  * Revision 6.22  2001/09/10 21:14:58  lavr
  1455.  * Added functions: StringToHostPort()
  1456.  *                  HostPortToString()
  1457.  *
  1458.  * Revision 6.21  2001/05/31 21:30:57  vakatov
  1459.  * MIME_ParseContentTypeEx() -- a more accurate parsing
  1460.  *
  1461.  * Revision 6.20  2001/05/29 21:15:43  vakatov
  1462.  * + eMIME_Plain
  1463.  *
  1464.  * Revision 6.19  2001/04/24 21:29:43  lavr
  1465.  * Special text value "infinite" accepted as infinite timeout from environment
  1466.  *
  1467.  * Revision 6.18  2001/03/26 18:37:09  lavr
  1468.  * #include <ctype.h> not used, removed
  1469.  *
  1470.  * Revision 6.17  2001/03/02 20:08:05  lavr
  1471.  * Typo fixed
  1472.  *
  1473.  * Revision 6.16  2001/01/25 16:58:33  lavr
  1474.  * ConnNetInfo_SetUserHeader now used throughout to set/reset http_user_header
  1475.  *
  1476.  * Revision 6.15  2001/01/23 23:06:18  lavr
  1477.  * SConnNetInfo.debug_printout converted from boolean to enum
  1478.  * BUF_StripToPattern() introduced
  1479.  *
  1480.  * Revision 6.14  2001/01/12 00:01:27  lavr
  1481.  * CONN_PROXY_HOST was forgotten to init in ConnNetInfo_Create
  1482.  *
  1483.  * Revision 6.13  2001/01/11 23:07:08  lavr
  1484.  * ConnNetInfo_Print() prints user-header 'as is'; pretty-printing undone
  1485.  *
  1486.  * Revision 6.12  2001/01/08 23:46:27  lavr
  1487.  * REQUEST_METHOD -> REQ_METHOD to be consistent with SConnNetInfo
  1488.  *
  1489.  * Revision 6.11  2001/01/08 22:35:56  lavr
  1490.  * Client-Mode removed; replaced by 2 separate boolean fields:
  1491.  * stateless and firewall
  1492.  *
  1493.  * Revision 6.10  2000/12/29 17:54:11  lavr
  1494.  * NCBID stuff removed; ConnNetInfo_SetUserHeader added;
  1495.  * modifications to ConnNetInfo_Print output.
  1496.  *
  1497.  * Revision 6.9  2000/11/07 23:23:18  vakatov
  1498.  * In-sync with the C Toolkit "connutil.c:R6.15", "connutil.h:R6.13"
  1499.  * (with "eMIME_Dispatch" added).
  1500.  *
  1501.  * Revision 6.8  2000/10/20 17:08:40  lavr
  1502.  * All keys capitalized for registry access
  1503.  * Search for some keywords made case insensitive
  1504.  *
  1505.  * Revision 6.7  2000/10/11 22:29:44  lavr
  1506.  * Forgotten blank added after {GET|POST} request in URL_Connect
  1507.  *
  1508.  * Revision 6.6  2000/10/05 22:35:24  lavr
  1509.  * SConnNetInfo modified to contain 'client_mode' instead of just
  1510.  * 'firewall' boolean
  1511.  *
  1512.  * Revision 6.5  2000/09/27 19:37:40  lavr
  1513.  * ncbi_ansi_ext.h included
  1514.  *
  1515.  * Revision 6.4  2000/09/26 22:01:33  lavr
  1516.  * Registry entries changed, HTTP request method added
  1517.  *
  1518.  * Revision 6.3  2000/04/21 19:42:35  vakatov
  1519.  * Several minor typo/bugs fixed
  1520.  *
  1521.  * Revision 6.2  2000/03/29 16:36:09  vakatov
  1522.  * MIME_ParseContentType() -- a fix
  1523.  *
  1524.  * Revision 6.1  2000/03/24 22:53:34  vakatov
  1525.  * Initial revision
  1526.  *
  1527.  * ==========================================================================
  1528.  */