Example #1
0
int HttpCgiTool::buildCommonEnv( IEnv * pEnv, HttpConnection * pConn )
{
    int count = 0;
    HttpReq * pReq = pConn->getReq();
    const char * pTemp;
    int n;
    int i;
    char buf[128];

    pTemp = pReq->getAuthUser();
    if (  *pTemp )
    {
        //FIXME: only Basic is support now
        pEnv->add( "AUTH_TYPE", 9, "Basic", 5 );
        pEnv->add( "REMOTE_USER", 11, pTemp, strlen( pTemp ) );
        count += 2;
    }
    //ADD_ENV("REMOTE_IDENT", "" )        //FIXME: not supported yet
    //extensions of CGI/1.1
    const AutoStr2 * pDocRoot = pReq->getDocRoot();
    pEnv->add( "DOCUMENT_ROOT", 13,
            pDocRoot->c_str(), pDocRoot->len()-1 );
    pEnv->add( "REMOTE_ADDR", 11, pConn->getPeerAddrString(),
            pConn->getPeerAddrStrLen() );
    
    n = safe_snprintf( buf, 10, "%hu", pConn->getRemotePort() );
    pEnv->add( "REMOTE_PORT", 11, buf, n );

    n = pConn->getServerAddrStr( buf, 128 );
    
    pEnv->add( "SERVER_ADDR", 11, buf, n );
    
    pEnv->add( "SERVER_NAME", 11, pReq->getHostStr(),  pReq->getHostStrLen() );
    const AutoStr2 &sPort = pReq->getPortStr();
    pEnv->add( "SERVER_PORT", 11, sPort.c_str(), sPort.len() );
    pEnv->add( "REQUEST_URI", 11, pReq->getOrgReqURL(), pReq->getOrgReqURLLen() );
    count += 7;
    
    n = pReq->getPathInfoLen();
    if ( n > 0)
    {
        int m;
        char achTranslated[10240];
        m =  pReq->translatePath( pReq->getPathInfo(), n,
                        achTranslated, sizeof( achTranslated ) );
        if ( m != -1 );
        {
            pEnv->add( "PATH_TRANSLATED", 15, achTranslated, m );
            ++count;
        }
        pEnv->add( "PATH_INFO", 9, pReq->getPathInfo(), n);
        ++count;
    }
    
    //add geo IP env here
    if ( pReq->isGeoIpOn() )
    {
        GeoInfo * pInfo = pConn->getClientInfo()->getGeoInfo();
        if ( pInfo )
        {
            pEnv->add( "GEOIP_ADDR", 10, pConn->getPeerAddrString(),
                    pConn->getPeerAddrStrLen() );
            count += pInfo->addGeoEnv( pEnv )+1;
        }
    }    

    n = pReq->getEnvCount();
    count += n;
    for( i = 0; i < n; ++i )
    {
        const char * pKey;
        const char * pVal;
        int keyLen;
        int valLen;
        pKey = pReq->getEnvByIndex( i, keyLen, pVal, valLen );
        if ( pKey )
            pEnv->add( pKey, keyLen, pVal, valLen );
    }
    
    if ( pConn->isSSL() )
    {
        SSLConnection * pSSL = pConn->getSSL();
        pEnv->add( "HTTPS", 5, "on",  2 );
        const char * pVersion = pSSL->getVersion();
        n = strlen( pVersion );
        pEnv->add( "SSL_VERSION", 11, pVersion, n );
        count += 2;
        SSL_SESSION * pSession = pSSL->getSession();
        if ( pSession )
        {
            int idLen = SSLConnection::getSessionIdLen( pSession );
            n = idLen * 2;
            assert( n < (int)sizeof( buf ) );
            StringTool::hexEncode(
                (char *)SSLConnection::getSessionId( pSession ),
                idLen, buf );
            pEnv->add( "SSL_SESSION_ID", 14, buf, n );
            ++count;
        }

        const SSL_CIPHER * pCipher = pSSL->getCurrentCipher();
        if ( pCipher )
        {
            const char * pName = pSSL->getCipherName();
            n = strlen( pName );
            pEnv->add( "SSL_CIPHER", 10, pName, n );
            int algkeysize;
            int keysize = SSLConnection::getCipherBits( pCipher, &algkeysize );
            n = safe_snprintf( buf, 20, "%d", keysize );
            pEnv->add( "SSL_CIPHER_USEKEYSIZE", 21, buf, n );
            n = safe_snprintf( buf, 20, "%d", algkeysize );
            pEnv->add( "SSL_CIPHER_ALGKEYSIZE", 21, buf, n );
            count += 3;
        }

        X509 * pClientCert = pSSL->getPeerCertificate();
        if ( pClientCert )
        {
            //IMPROVE: too many deep copy here.
            char achBuf[4096];
            n = SSLCert::PEMWriteCert( pClientCert, achBuf, 4096 );
            if ((n>0)&&( n <= 4096 ))
            {
                pEnv->add( "SSL_CLIENT_CERT", 15, achBuf, n );
                ++count;
            }
        }
        
    }    
    return count;
}
int RequestVars::getReqVar( HttpSession *pSession, int type, char * &pValue, int bufLen)
{
    HttpReq * pReq = pSession->getReq();
    int i;
    char *p;
    if ( type < REF_STRING )
    {
        pValue = (char *)pReq->getHeader( type );
        if ( *pValue )
            return pReq->getHeaderLen( type );
        else
            return 0;
    }
    switch( type )
    {
    case REF_REMOTE_HOST:
        //FIXME: use remote addr for now
    case REF_REMOTE_ADDR:
        pValue = (char *)pSession->getPeerAddrString();
        return pSession->getPeerAddrStrLen();
    case REF_REMOTE_PORT:
        return snprintf( pValue, bufLen, "%hu", pSession->getRemotePort() );
    case REF_REMOTE_USER:
        pValue = (char *)pReq->getAuthUser();
        return strlen( pValue );
    case REF_REMOTE_IDENT:
        //do not support;
        return 0;
    case REF_REQ_METHOD:
        i = pReq->getMethod();
        strcpy(pValue, HttpMethod::get( i ) );
        return HttpMethod::getLen( i );
    case REF_QUERY_STRING:
        pValue = (char *)pReq->getQueryString();
        return pReq->getQueryStringLen();
    case REF_AUTH_TYPE:
        //FIXME: hard code for now
        strncpy( pValue, "Basic", 6 );
        return 5;
    case REF_REQUST_FN:
    case REF_SCRIPTFILENAME:
    case REF_SCRIPT_BASENAME:
    case REF_REQ_BASENAME: 
    {
        const AutoStr2 * psTemp = pReq->getRealPath();
        if ( psTemp )
        {
            if (( type == REF_SCRIPT_BASENAME )||
                ( type == REF_REQ_BASENAME ))
            {
                const char * pEnd = psTemp->c_str() + psTemp->len();
                pValue = (char *)pEnd;
                while( pValue[-1] != '/' )
                    --pValue;
                return pEnd - pValue;
            }
            pValue = (char *)psTemp->c_str();
            return psTemp->len();
        }
        else
            return 0;
    }
    case REF_SCRIPT_UID:
    case REF_SCRIPT_GID:
    case REF_SCRIPT_USERNAME:
    case REF_SCRIPT_GRPNAME:
    case REF_SCRIPT_MODE:
    {
        const AutoStr2 * psTemp = pReq->getRealPath();
        if ( psTemp )
        {
            struct stat& st = pReq->getFileStat();
            if ( type == REF_SCRIPT_UID )
            {
                return snprintf( pValue, bufLen, "%d", st.st_uid );
            }
            else if ( type == REF_SCRIPT_GID )
            {
                return snprintf( pValue, bufLen, "%d", st.st_gid );
            }
            else if ( type == REF_SCRIPT_MODE )
            {
                return snprintf( pValue, bufLen, "%o", st.st_mode );
            }
            else if ( type == REF_SCRIPT_USERNAME )
            {
                struct passwd * pw = getpwuid( st.st_uid );
                if ( pw )
                    return snprintf( pValue, bufLen, "%s", pw->pw_name );
            }
            else
            {
                struct group * gr = getgrgid( st.st_gid );
                if ( gr )
                    return snprintf( pValue, bufLen, "%s", gr->gr_name );
            }
        }
        return 0;
    }
    case REF_PATH_INFO:
        pValue = (char *)pReq->getPathInfo();
        return pReq->getPathInfoLen();

    case REF_SCRIPT_NAME:
        pValue = (char *)pReq->getURI();
        return pReq->getScriptNameLen();
    case REF_SCRIPT_URI:
        p = pValue;
        if ( pSession->isSSL() )
        {
            strcpy( p, "https://" );
            p += 8;
        }
        else
        {
            strcpy( p, "http://" );
            p += 7;
        }
        i = pReq->getHeaderLen( HttpHeader::H_HOST );
        memmove( p, pReq->getHeader( HttpHeader::H_HOST ), 
                    i );
        p += i;

        i = pReq->getOrgURILen();
        memmove( p, pReq->getOrgURI(), i );
        p += i;
        return p - pValue;

    case REF_ORG_REQ_URI:
        pValue = (char *)pReq->getOrgReqURL();
        return pReq->getOrgReqURILen();
    case REF_DOCUMENT_URI:
        return pReq->getDecodedOrgReqURI( pValue );
    case REF_REQ_URI:
        pValue = (char *)pReq->getOrgReqURL();
        return pReq->getOrgReqURLLen();

    case REF_DOC_ROOT:
        pValue = (char *)pReq->getDocRoot()->c_str();
        return pReq->getDocRoot()->len()-1;

    case REF_SERVER_ADMIN:
        if ( pReq->getVHost() )
        {
            const AutoStr2 * pEmail = pReq->getVHost()->getAdminEmails();
            pValue = (char *)pEmail->c_str();
            return pEmail->len();
        }
        return 0;
    case REF_VH_CNAME:
        if ( pReq->getVHost() )
        {
            pValue = (char *)pReq->getVHost()->getVhName( i );
            return i;
        }
        return 0;

    case REF_SERVER_NAME:
        pValue = (char *)pReq->getHostStr();
        return pReq->getHostStrLen();
    case REF_SERVER_ADDR:
        pValue = (char *)pReq->getLocalAddrStr()->c_str();
        return pReq->getLocalAddrStr()->len();
    case REF_SERVER_PORT:
        pValue = (char *)pReq->getPortStr().c_str();
        return pReq->getPortStr().len();
    case REF_SERVER_PROTO:
        i = pReq->getVersion();
        pValue = (char *)HttpVer::getVersionString( i );
        return HttpVer::getVersionStringLen( i );
    case REF_SERVER_SOFT:
        pValue = (char *)HttpServerVersion::getVersion();
        return HttpServerVersion::getVersionLen();
    case REF_REQ_LINE:
        pValue = (char *)pReq->getOrgReqLine();
        return pReq->getOrgReqLineLen();
    case REF_IS_SUBREQ:
        strcpy( pValue, "false" );
        return 5;

    case REF_RESP_BYTES:
        i = StringTool::str_off_t( pValue, bufLen, pSession->getResp()->getBodySent() );
        return i;
    //case REF_COOKIE_VAL
    //case REF_STRFTIME        155
    //case REF_CONN_STATE:
    case REF_REQ_TIME_MS:
    {
        struct timeval tv;
        gettimeofday( &tv, NULL );
        DateTime::s_curTime = tv.tv_sec;
        DateTime::s_curTimeUs = tv.tv_usec;

        long lReqTime =  (DateTime::s_curTime - pSession->getReqTime())*1000000 +
                    (DateTime::s_curTimeUs - pSession->getReqTimeUs());
        i = snprintf( pValue, bufLen, "%ld", lReqTime );
        return i;
    }
    case REF_REQ_TIME_SEC:
        i = snprintf( pValue, bufLen, "%ld",
                    (DateTime::s_curTime - pSession->getReqTime()) );
        return i;
    case REF_DUMMY:
        return 0;
    case REF_PID:
        i = snprintf( pValue, bufLen, "%d", getpid() );
        return i;
    case REF_STATUS_CODE:
        memmove( pValue, HttpStatusCode::getCodeString( pReq->getStatusCode() )+1, 3 );
        pValue[3] = 0;
        return 3;
    
    case REF_CUR_URI:
        pValue = (char *)pReq->getURI();
        i = pReq->getURILen();
        return i;
    case REF_BYTES_IN:
        i = StringTool::str_off_t( pValue, bufLen, pSession->getBytesRecv() );
        return i;
    case REF_BYTES_OUT:
        i = StringTool::str_off_t( pValue, bufLen, pSession->getBytesSent() );
        return i;

    case REF_HTTPS:
        i = snprintf( pValue, bufLen, "%s", pSession->isSSL()?"on":"off" );
        return i;

    case REF_DATE_GMT:
    case REF_DATE_LOCAL:
    case REF_LAST_MODIFIED:
    {
        time_t mtime = DateTime::s_curTime;
        struct tm * tm;
        if ( type == REF_LAST_MODIFIED )
        {
            if ( pReq->getSSIRuntime() && pReq->getSSIRuntime()->getCurrentScript() )
            {
                mtime = pReq->getSSIRuntime()->getCurrentScript()->getLastMod();
            }
            else
                mtime = pReq->getFileStat().st_mtime;
        }
        if ( type == REF_DATE_GMT )
            tm = gmtime( &mtime );
        else
            tm = localtime( &mtime );
        char fmt[101]; 
        memccpy( fmt, pValue, 0, 100 );
        fmt[100] = 0;
        i = strftime( pValue, bufLen, fmt, tm );
        return i;
    } 
    case REF_DOCUMENT_NAME:
    {
        const AutoStr2 * psTemp = pReq->getRealPath();
        if ( psTemp )
        {
            pValue = (char *)psTemp->c_str() + psTemp->len();
            while( *(pValue -1) != '/' )
                --pValue;
            return psTemp->c_str() + psTemp->len() - pValue;
        }
        else
            return 0;
    }

    case REF_QS_UNESCAPED:
    {
        int qsLen = pReq->getQueryStringLen();
        const char * pQS = pReq->getQueryString();
        if ( qsLen > 0 )
        {
            qsLen = HttpUtil::unescape_n( pQS, qsLen, pValue, bufLen );
        }
        return qsLen;
    }
    case REF_RESP_CONTENT_TYPE:
        i = 0;
        pValue = (char*)pSession->getResp()->getContentTypeHeader( i );
        return i;
    case REF_RESP_CONTENT_LENGTH:
    {   off_t l = pSession->getResp()->getContentLen();
        if ( l <= 0 )
            l = 0;
        i = StringTool::str_off_t( pValue, bufLen, l );
        return i;
    }
    case REF_RESP_BODY:
        return 0;
    default:
        if ( type >= REF_TIME )
        {
            time_t t = time(NULL);
            struct tm *tm = localtime(&t);
            switch( type )
            {
            case REF_TIME:
                i = snprintf( pValue, bufLen,
                            "%04d%02d%02d%02d%02d%02d", tm->tm_year + 1900,
                            tm->tm_mon+1, tm->tm_mday,
                            tm->tm_hour, tm->tm_min, tm->tm_sec);
                break;
            case REF_TIME_YEAR:
                i = snprintf( pValue, bufLen, "%04d", tm->tm_year + 1900);
                break;
            case REF_TIME_MON:
                i = snprintf( pValue, bufLen, "%02d", tm->tm_mon+1 );
                break;
            case REF_TIME_DAY:
                i = snprintf( pValue, bufLen, "%02d", tm->tm_mday);
                break;
            case REF_TIME_HOUR:
                i = snprintf( pValue, bufLen, "%02d", tm->tm_hour);
                break;
            case REF_TIME_MIN:
                i = snprintf( pValue, bufLen, "%02d", tm->tm_min);
                break;
            case REF_TIME_SEC:
                i = snprintf( pValue, bufLen, "%02d", tm->tm_sec);
                break;
            case REF_TIME_WDAY:
                i = snprintf( pValue, bufLen, "%d", tm->tm_wday);
                break;
            default:
                return 0;
            }
            return i;
        }
        return 0;
    }
    return 0;
    
}
//Only for types from LSI_REQ_SSL_VERSION to LSI_REQ_PATH_TRANSLATED which are defined in ls.h
int RequestVars::getReqVar2( HttpSession *pSession, int type, char * &pValue, int bufLen)
{
    HttpReq * pReq = pSession->getReq();
    int ret = 0;
    
    if (type >= LSI_REQ_SSL_VERSION && type <= LSI_REQ_SSL_CLIENT_CERT)
    {
        if( !pSession->isSSL() )
            return 0;
        
        SSLConnection *pSSL = pSession->getSSL();
        if( type == LSI_REQ_SSL_VERSION)
        {
            pValue = (char *)pSSL->getVersion();
            ret = strlen( pValue );
            return ret;
        }    
        else if( type == LSI_REQ_SSL_SESSION_ID )
        {
            SSL_SESSION *pSession = pSSL->getSession();
            if ( pSession )
            {
                int idLen = SSLConnection::getSessionIdLen( pSession );
                ret = idLen * 2;
                if ( ret > bufLen )
                    ret = bufLen;
                StringTool::hexEncode((char *)SSLConnection::getSessionId( pSession ), ret / 2, pValue );
            }
            return ret;
        }
        else if( type == LSI_REQ_SSL_CLIENT_CERT )
        {
            X509 * pClientCert = pSSL->getPeerCertificate();
            if ( pClientCert )
                ret = SSLCert::PEMWriteCert( pClientCert, pValue, bufLen );
            
            return ret;
        }
        else
        {
            const SSL_CIPHER * pCipher = pSSL->getCurrentCipher();
            if ( pCipher )
            {
                if( type == LSI_REQ_SSL_CIPHER )
                {
                    pValue = (char *)pSSL->getCipherName();
                    ret = strlen( pValue );
                }
                else
                {
                    int algkeysize;
                    int keysize = SSLConnection::getCipherBits( pCipher, &algkeysize );
                    if( type == LSI_REQ_SSL_CIPHER_USEKEYSIZE )
                        ret = safe_snprintf( pValue, 20, "%d", keysize );
                    else //LSI_REQ_SSL_CIPHER_ALGKEYSIZE
                        ret = safe_snprintf( pValue, 20, "%d", algkeysize );
                }
            }
            return ret;
        }
    }
    else if( type == LSI_REQ_GEOIP_ADDR)
    {
        ret = pSession->getPeerAddrStrLen();
        pValue = (char *)pSession->getPeerAddrString();
        return ret;
    }
    else if( type == LSI_REQ_PATH_TRANSLATED)
    {
        int n = pReq->getPathInfoLen();
        if ( n > 0)
            ret =  pReq->translatePath( pReq->getPathInfo(), n, pValue, bufLen);
        return ret;
    }
    else
        return 0;
}
int HttpCgiTool::buildCommonEnv(IEnv *pEnv, HttpSession *pSession)
{
    int count = 0;
    HttpReq *pReq = pSession->getReq();
    const char *pTemp;
    int n;
    char buf[128];
    RadixNode *pNode;

    pTemp = pReq->getAuthUser();
    if (pTemp)
    {
        //NOTE: only Basic is support now
        pEnv->add("AUTH_TYPE", 9, "Basic", 5);
        pEnv->add("REMOTE_USER", 11, pTemp, strlen(pTemp));
        count += 2;
    }
    //ADD_ENV("REMOTE_IDENT", "" )        //TODO: not supported yet
    //extensions of CGI/1.1
    const AutoStr2 *pDocRoot = pReq->getDocRoot();
    pEnv->add("DOCUMENT_ROOT", 13,
              pDocRoot->c_str(), pDocRoot->len() - 1);
    pEnv->add("REMOTE_ADDR", 11, pSession->getPeerAddrString(),
              pSession->getPeerAddrStrLen());

    n = ls_snprintf(buf, 10, "%hu", pSession->getRemotePort());
    pEnv->add("REMOTE_PORT", 11, buf, n);

    n = pSession->getServerAddrStr(buf, 128);

    pEnv->add("SERVER_ADDR", 11, buf, n);

    pEnv->add("SERVER_NAME", 11, pReq->getHostStr(),  pReq->getHostStrLen());
    const AutoStr2 &sPort = pReq->getPortStr();
    pEnv->add("SERVER_PORT", 11, sPort.c_str(), sPort.len());
    pEnv->add("REQUEST_URI", 11, pReq->getOrgReqURL(),
              pReq->getOrgReqURLLen());
    count += 7;

    n = pReq->getPathInfoLen();
    if (n > 0)
    {
        int m;
        char achTranslated[10240];
        m =  pReq->translatePath(pReq->getPathInfo(), n,
                                 achTranslated, sizeof(achTranslated));
        if (m != -1)
        {
            pEnv->add("PATH_TRANSLATED", 15, achTranslated, m);
            ++count;
        }
        pEnv->add("PATH_INFO", 9, pReq->getPathInfo(), n);
        ++count;
    }

    //add geo IP env here
    if (pReq->isGeoIpOn())
    {
        GeoInfo *pInfo = pSession->getClientInfo()->getGeoInfo();
        if (pInfo)
        {
            pEnv->add("GEOIP_ADDR", 10, pSession->getPeerAddrString(),
                      pSession->getPeerAddrStrLen());
            count += pInfo->addGeoEnv(pEnv) + 1;
        }
    }
    n = pReq->getEnvCount();
    count += n;
    if ((pNode = (RadixNode *)pReq->getEnvNode()) != NULL)
        pNode->for_each2(addEnv, pEnv);

    if (pSession->getStream()->isSpdy())
    {
        const char *pProto = HioStream::getProtocolName((HiosProtocol)
                             pSession->getStream()->getProtocol());
        pEnv->add("X_SPDY", 6, pProto, strlen(pProto));
        ++count;
    }

    if (pSession->isSSL())
    {
        SslConnection *pSSL = pSession->getSSL();
        pEnv->add("HTTPS", 5, "on",  2);
        const char *pVersion = pSSL->getVersion();
        n = strlen(pVersion);
        pEnv->add("SSL_VERSION", 11, pVersion, n);
        count += 2;
        SSL_SESSION *pSession = pSSL->getSession();
        if (pSession)
        {
            int idLen = SslConnection::getSessionIdLen(pSession);
            n = idLen * 2;
            assert(n < (int)sizeof(buf));
            StringTool::hexEncode(
                (char *)SslConnection::getSessionId(pSession),
                idLen, buf);
            pEnv->add("SSL_SESSION_ID", 14, buf, n);
            ++count;
        }

        const SSL_CIPHER *pCipher = pSSL->getCurrentCipher();
        if (pCipher)
        {
            const char *pName = pSSL->getCipherName();
            n = strlen(pName);
            pEnv->add("SSL_CIPHER", 10, pName, n);
            int algkeysize;
            int keysize = SslConnection::getCipherBits(pCipher, &algkeysize);
            n = ls_snprintf(buf, 20, "%d", keysize);
            pEnv->add("SSL_CIPHER_USEKEYSIZE", 21, buf, n);
            n = ls_snprintf(buf, 20, "%d", algkeysize);
            pEnv->add("SSL_CIPHER_ALGKEYSIZE", 21, buf, n);
            count += 3;
        }

        int i = pSSL->getVerifyMode();
        if (i != 0)
        {
            char achBuf[4096];
            X509 *pClientCert = pSSL->getPeerCertificate();
            if (pSSL->isVerifyOk())
            {
                if (pClientCert)
                {
                    //IMPROVE: too many deep copy here.
                    //n = SslCert::PEMWriteCert( pClientCert, achBuf, 4096 );
                    //if ((n>0)&&( n <= 4096 ))
                    //{
                    //    pEnv->add( "SSL_CLIENT_CERT", 15, achBuf, n );
                    //    ++count;
                    //}
                    n = snprintf(achBuf, sizeof(achBuf), "%lu",
                                 X509_get_version(pClientCert) + 1);
                    pEnv->add("SSL_CLIENT_M_VERSION", 20, achBuf, n);
                    ++count;
                    n = lookup_ssl_cert_serial(pClientCert, achBuf, 4096);
                    if (n != -1)
                    {
                        pEnv->add("SSL_CLIENT_M_SERIAL", 19, achBuf, n);
                        ++count;
                    }
                    X509_NAME_oneline(X509_get_subject_name(pClientCert), achBuf, 4096);
                    pEnv->add("SSL_CLIENT_S_DN", 15, achBuf, strlen(achBuf));
                    ++count;
                    X509_NAME_oneline(X509_get_issuer_name(pClientCert), achBuf, 4096);
                    pEnv->add("SSL_CLIENT_I_DN", 15, achBuf, strlen(achBuf));
                    ++count;
                    if (SslConnection::isClientVerifyOptional(i))
                    {
                        strcpy(achBuf, "GENEROUS");
                        n = 8;
                    }
                    else
                    {
                        strcpy(achBuf, "SUCCESS");
                        n = 7;
                    }
                }
                else
                {
                    strcpy(achBuf, "NONE");
                    n = 4;
                }
            }
            else
                n = pSSL->buildVerifyErrorString(achBuf, sizeof(achBuf));
            pEnv->add("SSL_CLIENT_VERIFY", 17, achBuf, n);
            ++count;
        }

    }
    return count;
}