/* kex_agree_crypt
 * Agree on a cipher algo
 */
static int kex_agree_crypt(LIBSSH2_SESSION * session,
                           libssh2_endpoint_data *endpoint,
                           unsigned char *crypt,
                           unsigned long crypt_len)
{
    const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods();
    unsigned char *s;

    (void) session;

    if(endpoint->crypt_prefs) {
        s = (unsigned char *) endpoint->crypt_prefs;

        while(s && *s) {
            unsigned char *p = (unsigned char *) strchr((char *) s, ',');
            size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));

            if(kex_agree_instr(crypt, crypt_len, s, method_len)) {
                const LIBSSH2_CRYPT_METHOD *method =
                    (const LIBSSH2_CRYPT_METHOD *)
                    kex_get_method_by_name((char *) s, method_len,
                                           (const LIBSSH2_COMMON_METHOD **)
                                           cryptp);

                if(!method) {
                    /* Invalid method -- Should never be reached */
                    return -1;
                }

                endpoint->crypt = method;
                return 0;
            }

            s = p ? p + 1 : NULL;
        }
        return -1;
    }

    while(*cryptp && (*cryptp)->name) {
        s = kex_agree_instr(crypt, crypt_len,
                            (unsigned char *) (*cryptp)->name,
                            strlen((*cryptp)->name));
        if(s) {
            endpoint->crypt = *cryptp;
            return 0;
        }
        cryptp++;
    }


userauth_publickey_frommemory(LIBSSH2_SESSION *session,
                              const char *username,
                              size_t username_len,
                              const char *publickeydata,
                              size_t publickeydata_len,
                              const char *privatekeydata,
                              size_t privatekeydata_len,
                              const char *passphrase)
{
    unsigned char *pubkeydata = NULL;
    size_t pubkeydata_len = 0;
    struct privkey_file privkey_file;
    void *abstract = &privkey_file;
    int rc;

#if !LIBSSH2_RSA
    return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
                          "RSA is not supported by crypto backend");
#endif

    privkey_file.filename = privatekeydata;
    privkey_file.passphrase = passphrase;

    if(session->userauth_pblc_state == libssh2_NB_state_idle) {
        if(publickeydata_len && publickeydata) {
            rc = memory_read_publickey(session, &session->userauth_pblc_method,
                                       &session->userauth_pblc_method_len,
                                       &pubkeydata, &pubkeydata_len,
                                       publickeydata, publickeydata_len);
            if(rc)
                return rc;
        }
        else if(privatekeydata_len && privatekeydata) {
            /* Compute public key from private key. */
            if(_libssh2_pub_priv_keyfilememory(session,
                                                &session->userauth_pblc_method,
                                                &session->userauth_pblc_method_len,
                                                &pubkeydata, &pubkeydata_len,
                                                privatekeydata, privatekeydata_len,
                                                passphrase))
                return _libssh2_error(session, LIBSSH2_ERROR_FILE,
                                      "Unable to extract public key "
                                      "from private key.");
        }
        else {
            return _libssh2_error(session, LIBSSH2_ERROR_FILE,
                                  "Invalid data in public and private key.");
        }
    }

    rc = _libssh2_userauth_publickey(session, username, username_len,
                                     pubkeydata, pubkeydata_len,
                                     sign_frommemory, &abstract);
    if(pubkeydata)
        LIBSSH2_FREE(session, pubkeydata);

    return rc;
}


 * Diffie Hellman Key Exchange, Group Agnostic
 */
static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
                                 _libssh2_bn *g,
                                 _libssh2_bn *p,
                                 int group_order,
                                 unsigned char packet_type_init,
                                 unsigned char packet_type_reply,
                                 unsigned char *midhash,
                                 unsigned long midhash_len,
                                 kmdhgGPshakex_state_t *exchange_state)
{
    int ret = 0;
    int rc;
    libssh2_sha256_ctx exchange_hash_ctx;

    if(exchange_state->state == libssh2_NB_state_idle) {
        /* Setup initial values */
        exchange_state->e_packet = NULL;
        exchange_state->s_packet = NULL;
        exchange_state->k_value = NULL;
        exchange_state->ctx = _libssh2_bn_ctx_new();
        libssh2_dh_init(&exchange_state->x);
        exchange_state->e = _libssh2_bn_init(); /* g^x mod p */
        exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */
        exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */

        /* Zero the whole thing out */
        memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t));

        /* Generate x and e */
        rc = libssh2_dh_key_pair(&exchange_state->x, exchange_state->e, g, p,
                                 group_order, exchange_state->ctx);
        if(rc)
            goto clean_exit;

        /* Send KEX init */
        /* packet_type(1) + String Length(4) + leading 0(1) */
        exchange_state->e_packet_len =
            _libssh2_bn_bytes(exchange_state->e) + 6;
        if(_libssh2_bn_bits(exchange_state->e) % 8) {
            /* Leading 00 not needed */
            exchange_state->e_packet_len--;
        }

        exchange_state->e_packet =
            LIBSSH2_ALLOC(session, exchange_state->e_packet_len);
        if(!exchange_state->e_packet) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                 "Out of memory error");
            goto clean_exit;
        }
        exchange_state->e_packet[0] = packet_type_init;
        _libssh2_htonu32(exchange_state->e_packet + 1,
                         exchange_state->e_packet_len - 5);
        if(_libssh2_bn_bits(exchange_state->e) % 8) {
            _libssh2_bn_to_bin(exchange_state->e,
                               exchange_state->e_packet + 5);
        }
        else {
            exchange_state->e_packet[5] = 0;
            _libssh2_bn_to_bin(exchange_state->e,
                               exchange_state->e_packet + 6);
        }

        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d",
                       (int) packet_type_init);
        exchange_state->state = libssh2_NB_state_created;
    }

    if(exchange_state->state == libssh2_NB_state_created) {
        rc = _libssh2_transport_send(session, exchange_state->e_packet,
                                     exchange_state->e_packet_len,
                                     NULL, 0);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc,
                                 "Unable to send KEX init message");
            goto clean_exit;
        }
        exchange_state->state = libssh2_NB_state_sent;
    }

    if(exchange_state->state == libssh2_NB_state_sent) {
        if(session->burn_optimistic_kexinit) {
            /* The first KEX packet to come along will be the guess initially
             * sent by the server.  That guess turned out to be wrong so we
             * need to silently ignore it */
            int burn_type;

            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                           "Waiting for badly guessed KEX packet (to be ignored)");
            burn_type =
                _libssh2_packet_burn(session, &exchange_state->burn_state);
            if(burn_type == LIBSSH2_ERROR_EAGAIN) {
                return burn_type;
            }
            else if(burn_type <= 0) {
                /* Failed to receive a packet */
                ret = burn_type;
                goto clean_exit;
            }
            session->burn_optimistic_kexinit = 0;

            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                           "Burnt packet of type: %02x",
                           (unsigned int) burn_type);
        }

        exchange_state->state = libssh2_NB_state_sent1;
    }

    if(exchange_state->state == libssh2_NB_state_sent1) {
        /* Wait for KEX reply */
        rc = _libssh2_packet_require(session, packet_type_reply,
                                     &exchange_state->s_packet,
                                     &exchange_state->s_packet_len, 0, NULL,
                                     0, &exchange_state->req_state);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        if(rc) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
                                 "Timed out waiting for KEX reply");
            goto clean_exit;
        }

        /* Parse KEXDH_REPLY */
        if(exchange_state->s_packet_len < 5) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
                                 "Unexpected packet length");
            goto clean_exit;
        }

        exchange_state->s = exchange_state->s_packet + 1;

        session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s);
        exchange_state->s += 4;

        if(session->server_hostkey_len > exchange_state->s_packet_len - 5) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
                                 "Host key length out of bounds");
            goto clean_exit;
        }

        if(session->server_hostkey)
            LIBSSH2_FREE(session, session->server_hostkey);

        session->server_hostkey =
            LIBSSH2_ALLOC(session, session->server_hostkey_len);
        if(!session->server_hostkey) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                 "Unable to allocate memory for a copy "
                                 "of the host key");
            goto clean_exit;
        }
        memcpy(session->server_hostkey, exchange_state->s,
               session->server_hostkey_len);
        exchange_state->s += session->server_hostkey_len;

#if LIBSSH2_MD5
        {
            libssh2_md5_ctx fingerprint_ctx;

            if(libssh2_md5_init(&fingerprint_ctx)) {
                libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
                                   session->server_hostkey_len);
                libssh2_md5_final(fingerprint_ctx,
                                  session->server_hostkey_md5);
                session->server_hostkey_md5_valid = TRUE;
            }
            else {
                session->server_hostkey_md5_valid = FALSE;
            }
        }
#ifdef LIBSSH2DEBUG
        {
            char fingerprint[50], *fprint = fingerprint;
            int i;
            for(i = 0; i < 16; i++, fprint += 3) {
                snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
            }
            *(--fprint) = '\0';
            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                           "Server's MD5 Fingerprint: %s", fingerprint);
        }
#endif /* LIBSSH2DEBUG */
#endif /* ! LIBSSH2_MD5 */

        {
            libssh2_sha1_ctx fingerprint_ctx;

            if(libssh2_sha1_init(&fingerprint_ctx)) {
                libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
                                    session->server_hostkey_len);
                libssh2_sha1_final(fingerprint_ctx,
                                   session->server_hostkey_sha1);
                session->server_hostkey_sha1_valid = TRUE;
            }
            else {
                session->server_hostkey_sha1_valid = FALSE;
            }
        }
#ifdef LIBSSH2DEBUG
        {
            char fingerprint[64], *fprint = fingerprint;
            int i;

            for(i = 0; i < 20; i++, fprint += 3) {
                snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
            }
            *(--fprint) = '\0';
            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                           "Server's SHA1 Fingerprint: %s", fingerprint);
        }
#endif /* LIBSSH2DEBUG */

        {
            libssh2_sha256_ctx fingerprint_ctx;

            if(libssh2_sha256_init(&fingerprint_ctx)) {
                libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
                                      session->server_hostkey_len);
                libssh2_sha256_final(fingerprint_ctx,
                                     session->server_hostkey_sha256);
                session->server_hostkey_sha256_valid = TRUE;
            }
            else {
                session->server_hostkey_sha256_valid = FALSE;
            }
        }
#ifdef LIBSSH2DEBUG
        {
            char *base64Fingerprint = NULL;
            _libssh2_base64_encode(session, (const char *)session->server_hostkey_sha256,
                                   SHA256_DIGEST_LENGTH, &base64Fingerprint);
            if(base64Fingerprint != NULL) {
                _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                               "Server's SHA256 Fingerprint: %s", base64Fingerprint);
                LIBSSH2_FREE(session, base64Fingerprint);
            }
        }
#endif /* LIBSSH2DEBUG */

        if(session->hostkey->init(session, session->server_hostkey,
                                   session->server_hostkey_len,
                                   &session->server_hostkey_abstract)) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
                                 "Unable to initialize hostkey importer");
            goto clean_exit;
        }

        exchange_state->f_value_len = _libssh2_ntohu32(exchange_state->s);
        exchange_state->s += 4;
        exchange_state->f_value = exchange_state->s;
        exchange_state->s += exchange_state->f_value_len;
        _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
                             exchange_state->f_value);

        exchange_state->h_sig_len = _libssh2_ntohu32(exchange_state->s);
        exchange_state->s += 4;
        exchange_state->h_sig = exchange_state->s;

        /* Compute the shared secret */
        libssh2_dh_secret(&exchange_state->x, exchange_state->k,
                          exchange_state->f, p, exchange_state->ctx);
        exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
        if(_libssh2_bn_bits(exchange_state->k) % 8) {
            /* don't need leading 00 */
            exchange_state->k_value_len--;
        }
        exchange_state->k_value =
            LIBSSH2_ALLOC(session, exchange_state->k_value_len);
        if(!exchange_state->k_value) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                 "Unable to allocate buffer for K");
            goto clean_exit;
        }
        _libssh2_htonu32(exchange_state->k_value,
                         exchange_state->k_value_len - 4);
        if(_libssh2_bn_bits(exchange_state->k) % 8) {
            _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
        }
        else {
            exchange_state->k_value[4] = 0;
            _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
        }

        exchange_state->exchange_hash = (void *)&exchange_hash_ctx;
        libssh2_sha256_init(&exchange_hash_ctx);

        if(session->local.banner) {
            _libssh2_htonu32(exchange_state->h_sig_comp,
                             strlen((char *) session->local.banner) - 2);
            libssh2_sha256_update(exchange_hash_ctx,
                                  exchange_state->h_sig_comp, 4);
            libssh2_sha256_update(exchange_hash_ctx,
                                  session->local.banner,
                                  strlen((char *) session->local.banner) - 2);
        }
        else {
            _libssh2_htonu32(exchange_state->h_sig_comp,
                             sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
            libssh2_sha256_update(exchange_hash_ctx,
                                  exchange_state->h_sig_comp, 4);
            libssh2_sha256_update(exchange_hash_ctx,
                                  (const unsigned char *)LIBSSH2_SSH_DEFAULT_BANNER,
                                  sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
        }

        _libssh2_htonu32(exchange_state->h_sig_comp,
                         strlen((char *) session->remote.banner));
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->h_sig_comp, 4);
        libssh2_sha256_update(exchange_hash_ctx,
                              session->remote.banner,
                              strlen((char *) session->remote.banner));

        _libssh2_htonu32(exchange_state->h_sig_comp,
                         session->local.kexinit_len);
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->h_sig_comp, 4);
        libssh2_sha256_update(exchange_hash_ctx,
                              session->local.kexinit,
                              session->local.kexinit_len);

        _libssh2_htonu32(exchange_state->h_sig_comp,
                         session->remote.kexinit_len);
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->h_sig_comp, 4);
        libssh2_sha256_update(exchange_hash_ctx,
                              session->remote.kexinit,
                              session->remote.kexinit_len);

        _libssh2_htonu32(exchange_state->h_sig_comp,
                         session->server_hostkey_len);
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->h_sig_comp, 4);
        libssh2_sha256_update(exchange_hash_ctx,
                              session->server_hostkey,
                              session->server_hostkey_len);

        if(packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) {
            /* diffie-hellman-group-exchange hashes additional fields */
#ifdef LIBSSH2_DH_GEX_NEW
            _libssh2_htonu32(exchange_state->h_sig_comp,
                             LIBSSH2_DH_GEX_MINGROUP);
            _libssh2_htonu32(exchange_state->h_sig_comp + 4,
                             LIBSSH2_DH_GEX_OPTGROUP);
            _libssh2_htonu32(exchange_state->h_sig_comp + 8,
                             LIBSSH2_DH_GEX_MAXGROUP);
            libssh2_sha256_update(exchange_hash_ctx,
                                  exchange_state->h_sig_comp, 12);
#else
            _libssh2_htonu32(exchange_state->h_sig_comp,
                             LIBSSH2_DH_GEX_OPTGROUP);
            libssh2_sha256_update(exchange_hash_ctx,
                                  exchange_state->h_sig_comp, 4);
#endif
        }

        if(midhash) {
            libssh2_sha256_update(exchange_hash_ctx, midhash,
                                  midhash_len);
        }

        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->e_packet + 1,
                              exchange_state->e_packet_len - 1);

        _libssh2_htonu32(exchange_state->h_sig_comp,
                         exchange_state->f_value_len);
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->h_sig_comp, 4);
        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->f_value,
                              exchange_state->f_value_len);

        libssh2_sha256_update(exchange_hash_ctx,
                              exchange_state->k_value,
                              exchange_state->k_value_len);

        libssh2_sha256_final(exchange_hash_ctx,
                             exchange_state->h_sig_comp);

        if(session->hostkey->
            sig_verify(session, exchange_state->h_sig,
                       exchange_state->h_sig_len, exchange_state->h_sig_comp,
                       SHA256_DIGEST_LENGTH, &session->server_hostkey_abstract)) {
            ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
                                 "Unable to verify hostkey signature");
            goto clean_exit;
        }

        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message");
        exchange_state->c = SSH_MSG_NEWKEYS;

        exchange_state->state = libssh2_NB_state_sent2;
    }

    if(exchange_state->state == libssh2_NB_state_sent2) {
        rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message");
            goto clean_exit;
        }

        exchange_state->state = libssh2_NB_state_sent3;
    }

    if(exchange_state->state == libssh2_NB_state_sent3) {
        rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
                                     &exchange_state->tmp,
                                     &exchange_state->tmp_len, 0, NULL, 0,
                                     &exchange_state->req_state);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
            goto clean_exit;
        }
        /* The first key exchange has been performed,
           switch to active crypt/comp/mac mode */
        session->state |= LIBSSH2_STATE_NEWKEYS;
        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");

        /* This will actually end up being just packet_type(1)
           for this packet type anyway */
        LIBSSH2_FREE(session, exchange_state->tmp);

        if(!session->session_id) {
            session->session_id = LIBSSH2_ALLOC(session, SHA256_DIGEST_LENGTH);
            if(!session->session_id) {
                ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                     "Unable to allocate buffer for SHA digest");
                goto clean_exit;
            }
            memcpy(session->session_id, exchange_state->h_sig_comp,
                   SHA256_DIGEST_LENGTH);
            session->session_id_len = SHA256_DIGEST_LENGTH;
            _libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated");
        }

        /* Cleanup any existing cipher */
        if(session->local.crypt->dtor) {
            session->local.crypt->dtor(session,
                                       &session->local.crypt_abstract);
        }

        /* Calculate IV/Secret/Key for each direction */
        if(session->local.crypt->init) {
            unsigned char *iv = NULL, *secret = NULL;
            int free_iv = 0, free_secret = 0;

            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
                                              session->local.crypt->
                                              iv_len,
                                              (const unsigned char *)"A");
            if(!iv) {
                ret = -1;
                goto clean_exit;
            }
            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
                                              session->local.crypt->
                                              secret_len,
                                              (const unsigned char *)"C");
            if(!secret) {
                LIBSSH2_FREE(session, iv);
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
            if(session->local.crypt->
                init(session, session->local.crypt, iv, &free_iv, secret,
                     &free_secret, 1, &session->local.crypt_abstract)) {
                LIBSSH2_FREE(session, iv);
                LIBSSH2_FREE(session, secret);
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }

            if(free_iv) {
                _libssh2_explicit_zero(iv, session->local.crypt->iv_len);
                LIBSSH2_FREE(session, iv);
            }

            if(free_secret) {
                _libssh2_explicit_zero(secret, session->local.crypt->secret_len);
                LIBSSH2_FREE(session, secret);
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Client to Server IV and Key calculated");

        if(session->remote.crypt->dtor) {
            /* Cleanup any existing cipher */
            session->remote.crypt->dtor(session,
                                        &session->remote.crypt_abstract);
        }

        if(session->remote.crypt->init) {
            unsigned char *iv = NULL, *secret = NULL;
            int free_iv = 0, free_secret = 0;

            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
                                              session->remote.crypt->
                                              iv_len,
                                              (const unsigned char *)"B");
            if(!iv) {
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
                                              session->remote.crypt->
                                              secret_len,
                                              (const unsigned char *)"D");
            if(!secret) {
                LIBSSH2_FREE(session, iv);
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
            if(session->remote.crypt->
                init(session, session->remote.crypt, iv, &free_iv, secret,
                     &free_secret, 0, &session->remote.crypt_abstract)) {
                LIBSSH2_FREE(session, iv);
                LIBSSH2_FREE(session, secret);
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }

            if(free_iv) {
                _libssh2_explicit_zero(iv, session->remote.crypt->iv_len);
                LIBSSH2_FREE(session, iv);
            }

            if(free_secret) {
                _libssh2_explicit_zero(secret, session->remote.crypt->secret_len);
                LIBSSH2_FREE(session, secret);
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Server to Client IV and Key calculated");

        if(session->local.mac->dtor) {
            session->local.mac->dtor(session, &session->local.mac_abstract);
        }

        if(session->local.mac->init) {
            unsigned char *key = NULL;
            int free_key = 0;

            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
                                              session->local.mac->
                                              key_len,
                                              (const unsigned char *)"E");
            if(!key) {
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
            session->local.mac->init(session, key, &free_key,
                                     &session->local.mac_abstract);

            if(free_key) {
                _libssh2_explicit_zero(key, session->local.mac->key_len);
                LIBSSH2_FREE(session, key);
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Client to Server HMAC Key calculated");

        if(session->remote.mac->dtor) {
            session->remote.mac->dtor(session, &session->remote.mac_abstract);
        }

        if(session->remote.mac->init) {
            unsigned char *key = NULL;
            int free_key = 0;

            LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
                                              session->remote.mac->
                                              key_len,
                                              (const unsigned char *)"F");
            if(!key) {
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
            session->remote.mac->init(session, key, &free_key,
                                      &session->remote.mac_abstract);

            if(free_key) {
                _libssh2_explicit_zero(key, session->remote.mac->key_len);
                LIBSSH2_FREE(session, key);
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Server to Client HMAC Key calculated");

        /* Initialize compression for each direction */

        /* Cleanup any existing compression */
        if(session->local.comp && session->local.comp->dtor) {
            session->local.comp->dtor(session, 1,
                                      &session->local.comp_abstract);
        }

        if(session->local.comp && session->local.comp->init) {
            if(session->local.comp->init(session, 1,
                                          &session->local.comp_abstract)) {
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Client to Server compression initialized");

        if(session->remote.comp && session->remote.comp->dtor) {
            session->remote.comp->dtor(session, 0,
                                       &session->remote.comp_abstract);
        }

        if(session->remote.comp && session->remote.comp->init) {
            if(session->remote.comp->init(session, 0,
                                           &session->remote.comp_abstract)) {
                ret = LIBSSH2_ERROR_KEX_FAILURE;
                goto clean_exit;
            }
        }
        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Server to Client compression initialized");

    }

  clean_exit:
    libssh2_dh_dtor(&exchange_state->x);
    _libssh2_bn_free(exchange_state->e);
    exchange_state->e = NULL;
    _libssh2_bn_free(exchange_state->f);
    exchange_state->f = NULL;
    _libssh2_bn_free(exchange_state->k);
    exchange_state->k = NULL;
    _libssh2_bn_ctx_free(exchange_state->ctx);
    exchange_state->ctx = NULL;

    if(exchange_state->e_packet) {
        LIBSSH2_FREE(session, exchange_state->e_packet);
        exchange_state->e_packet = NULL;
    }

    if(exchange_state->s_packet) {
        LIBSSH2_FREE(session, exchange_state->s_packet);
        exchange_state->s_packet = NULL;
    }

    if(exchange_state->k_value) {
        LIBSSH2_FREE(session, exchange_state->k_value);
        exchange_state->k_value = NULL;
    }

    exchange_state->state = libssh2_NB_state_idle;


file_read_privatekey(LIBSSH2_SESSION * session,
                     const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
                     void **hostkey_abstract,
                     const unsigned char *method, int method_len,
                     const char *privkeyfile, const char *passphrase)
{
    const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
        libssh2_hostkey_methods();

    _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading private key file: %s",
                   privkeyfile);
    *hostkey_method = NULL;
    *hostkey_abstract = NULL;
    while(*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
        if((*hostkey_methods_avail)->initPEM
            && strncmp((*hostkey_methods_avail)->name, (const char *) method,
                       method_len) == 0) {
            *hostkey_method = *hostkey_methods_avail;
            break;
        }
        hostkey_methods_avail++;
    }
    if(!*hostkey_method) {
        return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
                              "No handler for specified private key");
    }

    if((*hostkey_method)->
        initPEM(session, privkeyfile, (unsigned char *) passphrase,
                hostkey_abstract)) {
        return _libssh2_error(session, LIBSSH2_ERROR_FILE,
                              "Unable to initialize private key from file");
    }

    return 0;
}


packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data,
                      unsigned long datalen,
                      packet_queue_listener_state_t *listen_state)
{
    /*
     * Look for a matching listener
     */
    /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
    unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1);
    unsigned char *p;
    LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners);
    char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
    int rc;

    (void) datalen;

    if(listen_state->state == libssh2_NB_state_idle) {
        unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5;
        listen_state->sender_channel = _libssh2_ntohu32(s);
        s += 4;

        listen_state->initial_window_size = _libssh2_ntohu32(s);
        s += 4;
        listen_state->packet_size = _libssh2_ntohu32(s);
        s += 4;

        listen_state->host_len = _libssh2_ntohu32(s);
        s += 4;
        listen_state->host = s;
        s += listen_state->host_len;
        listen_state->port = _libssh2_ntohu32(s);
        s += 4;

        listen_state->shost_len = _libssh2_ntohu32(s);
        s += 4;
        listen_state->shost = s;
        s += listen_state->shost_len;
        listen_state->sport = _libssh2_ntohu32(s);

        _libssh2_debug(session, LIBSSH2_TRACE_CONN,
                       "Remote received connection from %s:%ld to %s:%ld",
                       listen_state->shost, listen_state->sport,
                       listen_state->host, listen_state->port);

        listen_state->state = libssh2_NB_state_allocated;
    }

    if(listen_state->state != libssh2_NB_state_sent) {
        while(listn) {
            if((listn->port == (int) listen_state->port) &&
                (strlen(listn->host) == listen_state->host_len) &&
                (memcmp (listn->host, listen_state->host,
                         listen_state->host_len) == 0)) {
                /* This is our listener */
                LIBSSH2_CHANNEL *channel = NULL;
                listen_state->channel = NULL;

                if(listen_state->state == libssh2_NB_state_allocated) {
                    if(listn->queue_maxsize &&
                        (listn->queue_maxsize <= listn->queue_size)) {
                        /* Queue is full */
                        failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
                        _libssh2_debug(session, LIBSSH2_TRACE_CONN,
                                       "Listener queue full, ignoring");
                        listen_state->state = libssh2_NB_state_sent;
                        break;
                    }

                    channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
                    if(!channel) {
                        _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                       "Unable to allocate a channel for "
                                       "new connection");
                        failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
                        listen_state->state = libssh2_NB_state_sent;
                        break;
                    }
                    listen_state->channel = channel;

                    channel->session = session;
                    channel->channel_type_len = sizeof("forwarded-tcpip") - 1;
                    channel->channel_type = LIBSSH2_ALLOC(session,
                                                          channel->
                                                          channel_type_len +
                                                          1);
                    if(!channel->channel_type) {
                        _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
                                       "Unable to allocate a channel for new"
                                       " connection");
                        LIBSSH2_FREE(session, channel);
                        failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
                        listen_state->state = libssh2_NB_state_sent;
                        break;
                    }
                    memcpy(channel->channel_type, "forwarded-tcpip",
                           channel->channel_type_len + 1);

                    channel->remote.id = listen_state->sender_channel;
                    channel->remote.window_size_initial =
                        LIBSSH2_CHANNEL_WINDOW_DEFAULT;
                    channel->remote.window_size =
                        LIBSSH2_CHANNEL_WINDOW_DEFAULT;
                    channel->remote.packet_size =
                        LIBSSH2_CHANNEL_PACKET_DEFAULT;

                    channel->local.id = _libssh2_channel_nextid(session);
                    channel->local.window_size_initial =
                        listen_state->initial_window_size;
                    channel->local.window_size =
                        listen_state->initial_window_size;
                    channel->local.packet_size = listen_state->packet_size;

                    _libssh2_debug(session, LIBSSH2_TRACE_CONN,
                                   "Connection queued: channel %lu/%lu "
                                   "win %lu/%lu packet %lu/%lu",
                                   channel->local.id, channel->remote.id,
                                   channel->local.window_size,
                                   channel->remote.window_size,
                                   channel->local.packet_size,
                                   channel->remote.packet_size);

                    p = listen_state->packet;
                    *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
                    _libssh2_store_u32(&p, channel->remote.id);
                    _libssh2_store_u32(&p, channel->local.id);
                    _libssh2_store_u32(&p,
                                       channel->remote.window_size_initial);
                    _libssh2_store_u32(&p, channel->remote.packet_size);

                    listen_state->state = libssh2_NB_state_created;
                }

                if(listen_state->state == libssh2_NB_state_created) {
                    rc = _libssh2_transport_send(session, listen_state->packet,
                                                 17, NULL, 0);
                    if(rc == LIBSSH2_ERROR_EAGAIN)
                        return rc;
                    else if(rc) {
                        listen_state->state = libssh2_NB_state_idle;
                        return _libssh2_error(session, rc,
                                              "Unable to send channel "
                                              "open confirmation");
                    }

                    /* Link the channel into the end of the queue list */
                    if(listen_state->channel) {
                        _libssh2_list_add(&listn->queue,
                                          &listen_state->channel->node);
                        listn->queue_size++;
                    }

                    listen_state->state = libssh2_NB_state_idle;
                    return 0;
                }
            }

            listn = _libssh2_list_next(&listn->node);
        }

        listen_state->state = libssh2_NB_state_sent;
    }

    /* We're not listening to you */
    p = listen_state->packet;
    *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
    _libssh2_store_u32(&p, listen_state->sender_channel);
    _libssh2_store_u32(&p, failure_code);
    _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1);
    _libssh2_htonu32(p, 0);

    rc = _libssh2_transport_send(session, listen_state->packet,
                                 packet_len, NULL, 0);
    if(rc == LIBSSH2_ERROR_EAGAIN) {
        return rc;
    }
    else if(rc) {
        listen_state->state = libssh2_NB_state_idle;
        return _libssh2_error(session, rc, "Unable to send open failure");

    }
    listen_state->state = libssh2_NB_state_idle;
    return 0;
}


libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock)
{
    int rc;

    BLOCK_ADJUST(rc, session, session_startup(session, sock) );

    return rc;
}


get_scaled_gray_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
/* This version is for reading raw-byte-format PGM files with any maxval */
{
  ppm_source_ptr source = (ppm_source_ptr)sinfo;
  register JSAMPROW ptr;
  register U_CHAR *bufferptr;
  register JSAMPLE *rescale = source->rescale;
  JDIMENSION col;

  if (!ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
    ERREXIT(cinfo, JERR_INPUT_EOF);
  ptr = source->pub.buffer[0];
  bufferptr = source->iobuffer;
  for (col = cinfo->image_width; col > 0; col--) {
    *ptr++ = rescale[UCH(*bufferptr++)];
  }
  return 1;
}


static int hostget_cmd(void) {
	char n[MAX_FNAME];
	FILE *f;

	if (!glue_strings(n, sizeof n, SPOOL_DIR, CRON_HOSTNAME, '/')) {
		fprintf(stderr, "path too long\n");
		return (-2);
	}

	if (!(f = fopen(n, "r"))) {
		if (errno == ENOENT)
			fprintf(stderr, "File %s not found\n", n);
		else
			perror(n);
		return (-2);
	}

	if (get_string(Host, sizeof Host, f, "\n") == EOF) {
		fprintf(stderr, "Error reading from %s\n", n);
		fclose(f);
		return (-2);
	}

	fclose(f);

	printf("%s\n", Host);
	fflush(stdout);

	log_it(RealUser, Pid, "GET HOST", Host, 0);
	return (0);
}


int set_debug_flags(const char *flags) {
	/* debug flags are of the form    flag[,flag ...]
	 *
	 * if an error occurs, print a message to stdout and return FALSE.
	 * otherwise return TRUE after setting ERROR_FLAGS.
	 */

#if !DEBUGGING

	printf("this program was compiled without debugging enabled\n");
	return (FALSE);

#else /* DEBUGGING */

	const char *pc = flags;

	DebugFlags = 0;

	while (*pc) {
		const char **test;
		int mask;

		/* try to find debug flag name in our list.
		 */
		for (test = DebugFlagNames, mask = 1;
			*test != NULL && strcmp_until(*test, pc, ','); test++, mask <<= 1) ;

		if (!*test) {
			fprintf(stderr, "unrecognized debug flag <%s> <%s>\n", flags, pc);
			return (FALSE);
		}

		DebugFlags |= mask;

		/* skip to the next flag
		 */
		while (*pc && *pc != ',')
			pc++;
		if (*pc == ',')
			pc++;
	}

	if (DebugFlags) {
		int flag;

		fprintf(stderr, "debug flags enabled:");

		for (flag = 0; DebugFlagNames[flag]; flag++)
			if (DebugFlags & (1 << flag))
				fprintf(stderr, " %s", DebugFlagNames[flag]);
		fprintf(stderr, "\n");
	}

	return (TRUE);

#endif /* DEBUGGING */
}


void set_cron_uid(void) {
#if defined(BSD) || defined(POSIX)
	if (seteuid(ROOT_UID) < OK) {
		perror("seteuid");
		exit(ERROR_EXIT);
	}
#else
	if (setuid(ROOT_UID) < OK) {
		perror("setuid");
		exit(ERROR_EXIT);
	}
#endif
}


long get_gmtoff(time_t * clock, struct tm *local) {
	struct tm gmt;
	long offset;

	gmt = *gmtime(clock);
	if (local == NULL)
		local = localtime(clock);

	offset = (local->tm_sec - gmt.tm_sec) +
		((local->tm_min - gmt.tm_min) * 60) +
		((local->tm_hour - gmt.tm_hour) * 3600);

	/* Timezone may cause year rollover to happen on a different day. */
	if (local->tm_year < gmt.tm_year)
		offset -= 24 * 3600;
	else if (local->tm_year > gmt.tm_year)
		offset += 24 * 3600;
	else if (local->tm_yday < gmt.tm_yday)
		offset -= 24 * 3600;
	else if (local->tm_yday > gmt.tm_yday)
		offset += 24 * 3600;

	return (offset);
}


char *first_word(const char *s, const char *t) {
	static char retbuf[2][MAX_TEMPSTR + 1];	/* sure wish C had GC */
	static int retsel = 0;
	char *rb, *rp;

	/* select a return buffer */
	retsel = 1 - retsel;
	rb = &retbuf[retsel][0];
	rp = rb;

	/* skip any leading terminators */
	while (*s && (NULL != strchr(t, *s))) {
		s++;
	}

	/* copy until next terminator or full buffer */
	while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
		*rp++ = *s++;
	}

	/* finish the return-string and return it */
	*rp = '\0';
	return (rb);
}


    caca_get_dither_antialias_list(caca_dither_t const *d)
{
    static char const * const list[] =
    {
        "none", "No antialiasing",
        "prefilter", "Prefilter antialiasing",
        NULL, NULL
    };

    return list;
}


static void increment_ordered8_dither(void)
{
    ordered8_index = (ordered8_index + 1) % 8;
}


float caca_get_dither_brightness(caca_dither_t const *d)
{
    return d->brightness;
}


int caca_free_dither(caca_dither_t *d)
{
    if(!d)
        return 0;

    free(d);

    return 0;
}


static void get_rgba_default(caca_dither_t const *d, uint8_t const *pixels,
                             int x, int y, unsigned int *rgba)
{
    uint32_t bits;

    pixels += (d->bpp / 8) * x + d->pitch * y;

    switch(d->bpp / 8)
    {
        case 4:
            bits = *(uint32_t const *)pixels;
            break;
        case 3:
        {
#if defined(HAVE_ENDIAN_H)
            if(__BYTE_ORDER == __BIG_ENDIAN)
#else
            /* This is compile-time optimised with at least -O1 or -Os */
            uint32_t const tmp = 0x12345678;
            if(*(uint8_t const *)&tmp == 0x12)
#endif
                bits = ((uint32_t)pixels[0] << 16) |
                       ((uint32_t)pixels[1] << 8) |
                       ((uint32_t)pixels[2]);
            else
                bits = ((uint32_t)pixels[2] << 16) |
                       ((uint32_t)pixels[1] << 8) |
                       ((uint32_t)pixels[0]);
            break;
        }
        case 2:
            bits = *(uint16_t const *)pixels;
            break;
        case 1:
        default:
            bits = pixels[0];
            break;
    }

    if(d->has_palette)
    {
        rgba[0] += d->gammatab[d->red[bits]];
        rgba[1] += d->gammatab[d->green[bits]];
        rgba[2] += d->gammatab[d->blue[bits]];
        rgba[3] += d->alpha[bits];
    }
    else
    {
        rgba[0] += d->gammatab[((bits & d->rmask) >> d->rright) << d->rleft];
        rgba[1] += d->gammatab[((bits & d->gmask) >> d->gright) << d->gleft];
        rgba[2] += d->gammatab[((bits & d->bmask) >> d->bright) << d->bleft];
        rgba[3] += ((bits & d->amask) >> d->aright) << d->aleft;
    }
}


g_socket_client_tls_handshake_callback (GObject      *object,
					GAsyncResult *result,
					gpointer      user_data)
{
  GSocketClientAsyncConnectData *data = user_data;

  if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object),
					 result,
					 &data->last_error))
    {
      g_object_unref (data->connection);
      data->connection = G_IO_STREAM (object);

      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKED, data->connectable, data->connection);
      g_socket_client_async_connect_complete (data);
    }
  else
    {
      g_object_unref (object);
      enumerator_next_async (data, FALSE);
    }
}


g_socket_client_set_timeout (GSocketClient *client,
			     guint          timeout)
{
  if (client->priv->timeout == timeout)
    return;

  client->priv->timeout = timeout;
  g_object_notify (G_OBJECT (client), "timeout");
}


g_socket_client_set_socket_type (GSocketClient *client,
				 GSocketType    type)
{
  if (client->priv->type == type)
    return;

  client->priv->type = type;
  g_object_notify (G_OBJECT (client), "type");
}


g_socket_client_connect_to_service (GSocketClient  *client,
				    const gchar    *domain,
				    const gchar    *service,
				    GCancellable   *cancellable,
				    GError        **error)
{
  GSocketConnectable *connectable;
  GSocketConnection *connection;

  connectable = g_network_service_new (service, "tcp", domain);
  connection = g_socket_client_connect (client, connectable,
					cancellable, error);
  g_object_unref (connectable);

  return connection;
}


enumerator_next_async (GSocketClientAsyncConnectData *data,
                       gboolean                       add_task_ref)
{
  /* We need to cleanup the state */
  g_clear_object (&data->socket);
  g_clear_object (&data->proxy_addr);
  g_clear_object (&data->connection);

  /* Each enumeration takes a ref. This arg just avoids repeated unrefs when
     an enumeration starts another enumeration */
  if (add_task_ref)
    g_object_ref (data->task);

  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
  g_socket_address_enumerator_next_async (data->enumerator,
					  g_task_get_cancellable (data->task),
					  g_socket_client_enumerator_callback,
					  data);
}


static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
{
	php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
	size_t ret;

	assert(data != NULL);

	if (data->fd >= 0) {
#ifdef PHP_WIN32
		php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;

		if ((self->is_pipe || self->is_process_pipe) && !self->is_pipe_blocking) {
			HANDLE ph = (HANDLE)_get_osfhandle(data->fd);
			int retry = 0;
			DWORD avail_read = 0;

			do {
				/* Look ahead to get the available data amount to read. Do the same
					as read() does, however not blocking forever. In case it failed,
					no data will be read (better than block). */
				if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) {
					break;
				}
				/* If there's nothing to read, wait in 10ms periods. */
				if (0 == avail_read) {
					usleep(10);
				}
			} while (0 == avail_read && retry++ < 3200000);

			/* Reduce the required data amount to what is available, otherwise read()
				will block.*/
			if (avail_read < count) {
				count = avail_read;
			}
		}
#endif
		ret = read(data->fd, buf,  PLAIN_WRAP_BUF_SIZE(count));

		if (ret == (size_t)-1 && errno == EINTR) {
			/* Read was interrupted, retry once,
			   If read still fails, giveup with feof==0
			   so script can retry if desired */
			ret = read(data->fd, buf,  PLAIN_WRAP_BUF_SIZE(count));
		}

		stream->eof = (ret == 0 || (ret == (size_t)-1 && errno != EWOULDBLOCK && errno != EINTR && errno != EBADF));

	} else {
#if HAVE_FLUSHIO
		if (!data->is_pipe && data->last_op == 'w')
			zend_fseek(data->file, 0, SEEK_CUR);
		data->last_op = 'r';
#endif

		ret = fread(buf, 1, count, data->file);

		stream->eof = feof(data->file);
	}
	return ret;
}


static int php_stdiop_flush(php_stream *stream)
{
	php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;

	assert(data != NULL);

	/*
	 * stdio buffers data in user land. By calling fflush(3), this
	 * data is send to the kernel using write(2). fsync'ing is
	 * something completely different.
	 */
	if (data->file) {
		return fflush(data->file);
	}
	return 0;
}


static php_stream *_php_stream_fopen_from_file_int(FILE *file, const char *mode STREAMS_DC)
{
	php_stdio_stream_data *self;

	self = emalloc_rel_orig(sizeof(*self));
	memset(self, 0, sizeof(*self));
	self->file = file;
	self->is_pipe = 0;
	self->lock_flag = LOCK_UN;
	self->is_process_pipe = 0;
	self->temp_name = NULL;
	self->fd = fileno(file);
#ifdef PHP_WIN32
	self->is_pipe_blocking = 0;
#endif

	return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode);
}


PHP_MINFO_FUNCTION(exif)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "EXIF Support", "enabled");
	php_info_print_table_row(2, "Supported EXIF Version", "0220");
	php_info_print_table_row(2, "Supported filetypes", "JPEG, TIFF");

	if (zend_hash_str_exists(&module_registry, "mbstring", sizeof("mbstring")-1)) {
		php_info_print_table_row(2, "Multibyte decoding support using mbstring", "enabled");
	} else {
		php_info_print_table_row(2, "Multibyte decoding support using mbstring", "disabled");
	}

	php_info_print_table_row(2, "Extended EXIF tag formats", "Canon, Casio, Fujifilm, Nikon, Olympus, Samsung, Panasonic, DJI, Sony, Pentax, Minolta, Sigma, Foveon, Kyocera, Ricoh, AGFA, Epson");
	php_info_print_table_end();

	DISPLAY_INI_ENTRIES();
}


static void add_assoc_image_info(zval *value, int sub_array, image_info_type *image_info, int section_index)
{
	char    buffer[64], *val, *name, uname[64];
	int     i, ap, l, b, idx=0, unknown=0;
#ifdef EXIF_DEBUG
	int     info_tag;
#endif
	image_info_value *info_value;
	image_info_data  *info_data;
	zval 			 tmpi, array;

#ifdef EXIF_DEBUG
/*		php_error_docref(NULL, E_NOTICE, "Adding %d infos from section %s", image_info->info_list[section_index].count, exif_get_sectionname(section_index));*/
#endif
	if (image_info->info_list[section_index].count) {
		if (sub_array) {
			array_init(&tmpi);
		} else {
			ZVAL_COPY_VALUE(&tmpi, value);
		}

		for(i=0; i<image_info->info_list[section_index].count; i++) {
			info_data  = &image_info->info_list[section_index].list[i];
#ifdef EXIF_DEBUG
			info_tag   = info_data->tag; /* conversion */
#endif
			info_value = &info_data->value;
			if (!(name = info_data->name)) {
				snprintf(uname, sizeof(uname), "%d", unknown++);
				name = uname;
			}
#ifdef EXIF_DEBUG
/*		php_error_docref(NULL, E_NOTICE, "Adding infos: tag(0x%04X,%12s,L=0x%04X): %s", info_tag, exif_get_tagname(info_tag, buffer, -12, exif_get_tag_table(section_index)), info_data->length, info_data->format==TAG_FMT_STRING?(info_value&&info_value->s?info_value->s:"<no data>"):exif_get_tagformat(info_data->format));*/
#endif
			if (info_data->length==0) {
				add_assoc_null(&tmpi, name);
			} else {
				switch (info_data->format) {
					default:
						/* Standard says more types possible but skip them...
						 * but allow users to handle data if they know how to
						 * So not return but use type UNDEFINED
						 * return;
						 */
					case TAG_FMT_BYTE:
					case TAG_FMT_SBYTE:
					case TAG_FMT_UNDEFINED:
						if (!info_value->s) {
							add_assoc_stringl(&tmpi, name, "", 0);
						} else {
							add_assoc_stringl(&tmpi, name, info_value->s, info_data->length);
						}
						break;

					case TAG_FMT_STRING:
						if (!(val = info_value->s)) {
							val = "";
						}
						if (section_index==SECTION_COMMENT) {
							add_index_string(&tmpi, idx++, val);
						} else {
							add_assoc_string(&tmpi, name, val);
						}
						break;

					case TAG_FMT_URATIONAL:
					case TAG_FMT_SRATIONAL:
					/*case TAG_FMT_BYTE:
					case TAG_FMT_SBYTE:*/
					case TAG_FMT_USHORT:
					case TAG_FMT_SSHORT:
					case TAG_FMT_SINGLE:
					case TAG_FMT_DOUBLE:
					case TAG_FMT_ULONG:
					case TAG_FMT_SLONG:
						/* now the rest, first see if it becomes an array */
						if ((l = info_data->length) > 1) {
							array_init(&array);
						}
						for(ap=0; ap<l; ap++) {
							if (l>1) {
								info_value = &info_data->value.list[ap];
							}
							switch (info_data->format) {
								case TAG_FMT_BYTE:
									if (l>1) {
										info_value = &info_data->value;
										for (b=0;b<l;b++) {
											add_index_long(&array, b, (int)(info_value->s[b]));
										}
										break;
									}
								case TAG_FMT_USHORT:
								case TAG_FMT_ULONG:
									if (l==1) {
										add_assoc_long(&tmpi, name, (int)info_value->u);
									} else {
										add_index_long(&array, ap, (int)info_value->u);
									}
									break;

								case TAG_FMT_URATIONAL:
									snprintf(buffer, sizeof(buffer), "%u/%u", info_value->ur.num, info_value->ur.den);
									if (l==1) {
										add_assoc_string(&tmpi, name, buffer);
									} else {
										add_index_string(&array, ap, buffer);
									}
									break;

								case TAG_FMT_SBYTE:
									if (l>1) {
										info_value = &info_data->value;
										for (b=0;b<l;b++) {
											add_index_long(&array, ap, (int)info_value->s[b]);
										}
										break;
									}
								case TAG_FMT_SSHORT:
								case TAG_FMT_SLONG:
									if (l==1) {
										add_assoc_long(&tmpi, name, info_value->i);
									} else {
										add_index_long(&array, ap, info_value->i);
									}
									break;

								case TAG_FMT_SRATIONAL:
									snprintf(buffer, sizeof(buffer), "%i/%i", info_value->sr.num, info_value->sr.den);
									if (l==1) {
										add_assoc_string(&tmpi, name, buffer);
									} else {
										add_index_string(&array, ap, buffer);
									}
									break;

								case TAG_FMT_SINGLE:
									if (l==1) {
										add_assoc_double(&tmpi, name, info_value->f);
									} else {
										add_index_double(&array, ap, info_value->f);
									}
									break;

								case TAG_FMT_DOUBLE:
									if (l==1) {
										add_assoc_double(&tmpi, name, info_value->d);
									} else {
										add_index_double(&array, ap, info_value->d);
									}
									break;
							}
							info_value = &info_data->value.list[ap];
						}
						if (l>1) {
							add_assoc_zval(&tmpi, name, &array);
						}
						break;
				}
			}
		}
		if (sub_array) {
			add_assoc_zval(value, exif_get_sectionname(section_index), &tmpi);
		}
	}
}


static int exif_read_from_impl(image_info_type *ImageInfo, php_stream *stream, int read_thumbnail, int read_all)
{
	int ret;
	zend_stat_t st;

	/* Start with an empty image information structure. */
	memset(ImageInfo, 0, sizeof(*ImageInfo));

	ImageInfo->motorola_intel	= -1; /* flag as unknown */
	ImageInfo->infile			= stream;
	ImageInfo->FileName			= NULL;

	if (php_stream_is(ImageInfo->infile, PHP_STREAM_IS_STDIO)) {
		if (VCWD_STAT(stream->orig_path, &st) >= 0) {
			zend_string *base;
			if ((st.st_mode & S_IFMT) != S_IFREG) {
				exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Not a file");
				ImageInfo->infile = NULL;
				return FALSE;
			}

			/* Store file name */
			base = php_basename(stream->orig_path, strlen(stream->orig_path), NULL, 0);
			ImageInfo->FileName = estrndup(ZSTR_VAL(base), ZSTR_LEN(base));

			zend_string_release_ex(base, 0);

			/* Store file date/time. */
			ImageInfo->FileDateTime = st.st_mtime;
			ImageInfo->FileSize = st.st_size;
			/*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Opened stream is file: %d", ImageInfo->FileSize);*/
		}
	} else {
		if (!ImageInfo->FileSize) {
			php_stream_seek(ImageInfo->infile, 0, SEEK_END);
			ImageInfo->FileSize = php_stream_tell(ImageInfo->infile);
			php_stream_seek(ImageInfo->infile, 0, SEEK_SET);
		}
	}

	ImageInfo->read_thumbnail		= read_thumbnail;
	ImageInfo->read_all				= read_all;
	ImageInfo->Thumbnail.filetype	= IMAGE_FILETYPE_UNKNOWN;

	ImageInfo->encode_unicode		= estrdup(EXIF_G(encode_unicode));
	ImageInfo->decode_unicode_be	= estrdup(EXIF_G(decode_unicode_be));
	ImageInfo->decode_unicode_le	= estrdup(EXIF_G(decode_unicode_le));
	ImageInfo->encode_jis			= estrdup(EXIF_G(encode_jis));
	ImageInfo->decode_jis_be	 	= estrdup(EXIF_G(decode_jis_be));
	ImageInfo->decode_jis_le		= estrdup(EXIF_G(decode_jis_le));

	ImageInfo->ifd_nesting_level = 0;

	/* Scan the headers */
	ret = exif_scan_FILE_header(ImageInfo);

	return ret;
}


static int phar_tar_writeheaders(zval *zv, void *argument) /* {{{ */
{
	return phar_tar_writeheaders_int(Z_PTR_P(zv), argument);
}


static inline void context_set_translation_type(struct context_entry *context,
						unsigned long value)
{
	context->lo &= (((u64)-1) << 4) | 3;
	context->lo |= (value & 3) << 2;
}


static ssize_t intel_iommu_show_ndoms_used(struct device *dev,
					   struct device_attribute *attr,
					   char *buf)
{
	struct intel_iommu *iommu = dev_to_intel_iommu(dev);
	return sprintf(buf, "%d\n", bitmap_weight(iommu->domain_ids,
						  cap_ndoms(iommu->cap)));
}


static int identity_mapping(struct device *dev)
{
	struct device_domain_info *info;

	if (likely(!iommu_identity_mapping))
		return 0;

	info = dev->archdata.iommu;
	if (info && info != DUMMY_DEVICE_DOMAIN_INFO)
		return (info->domain == si_domain);

	return 0;
}


static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
					    dma_addr_t iova)
{
	struct dmar_domain *dmar_domain = to_dmar_domain(domain);
	struct dma_pte *pte;
	int level = 0;
	u64 phys = 0;

	pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level);
	if (pte)
		phys = dma_pte_addr(pte);

	return phys;
}


static void dma_pte_free_pagetable(struct dmar_domain *domain,
				   unsigned long start_pfn,
				   unsigned long last_pfn,
				   int retain_level)
{
	BUG_ON(!domain_pfn_supported(domain, start_pfn));
	BUG_ON(!domain_pfn_supported(domain, last_pfn));
	BUG_ON(start_pfn > last_pfn);

	dma_pte_clear_range(domain, start_pfn, last_pfn);

	/* We don't need lock here; nobody else touches the iova range */
	dma_pte_free_level(domain, agaw_to_level(domain->agaw), retain_level,
			   domain->pgd, 0, start_pfn, last_pfn);

	/* free pgd */
	if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
		free_pgtable_page(domain->pgd);
		domain->pgd = NULL;
	}
}


iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu,
			 u8 bus, u8 devfn)
{
	struct device_domain_info *info;

	assert_spin_locked(&device_domain_lock);

	if (!iommu->qi)
		return NULL;

	list_for_each_entry(info, &domain->devices, link)
		if (info->iommu == iommu && info->bus == bus &&
		    info->devfn == devfn) {
			if (info->ats_supported && info->dev)
				return info;
			break;
		}

	return NULL;
}


static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
{
	struct dmar_domain *domain;
	unsigned long start_pfn, last_pfn;
	unsigned long nrpages;
	unsigned long iova_pfn;
	struct intel_iommu *iommu;
	struct page *freelist;

	if (iommu_no_mapping(dev))
		return;

	domain = find_domain(dev);
	BUG_ON(!domain);

	iommu = domain_get_iommu(domain);

	iova_pfn = IOVA_PFN(dev_addr);

	nrpages = aligned_nrpages(dev_addr, size);
	start_pfn = mm_to_dma_pfn(iova_pfn);
	last_pfn = start_pfn + nrpages - 1;

	dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn);

	freelist = domain_unmap(domain, start_pfn, last_pfn);

	if (intel_iommu_strict) {
		iommu_flush_iotlb_psi(iommu, domain, start_pfn,
				      nrpages, !freelist, 0);
		/* free iova */
		free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(nrpages));
		dma_free_pagelist(freelist);
	} else {
		queue_iova(&domain->iovad, iova_pfn, nrpages,
			   (unsigned long)freelist);
		/*
		 * queue up the release of the unmap to save the 1/6th of the
		 * cpu used up by the iotlb flush operation...
		 */
	}
}


static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
				  u64 addr, unsigned mask)
{
	u16 sid, qdep;
	unsigned long flags;
	struct device_domain_info *info;

	if (!domain->has_iotlb_device)
		return;

	spin_lock_irqsave(&device_domain_lock, flags);
	list_for_each_entry(info, &domain->devices, link) {
		if (!info->ats_enabled)
			continue;

		sid = info->bus << 8 | info->devfn;
		qdep = info->ats_qdep;
		qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
				qdep, addr, mask);
	}
	spin_unlock_irqrestore(&device_domain_lock, flags);
}


static inline int width_to_agaw(int width)
{
	return DIV_ROUND_UP(width - 30, LEVEL_STRIDE);
}


static int domain_context_mapped(struct device *dev)
{
	struct intel_iommu *iommu;
	u8 bus, devfn;

	iommu = device_to_iommu(dev, &bus, &devfn);
	if (!iommu)
		return -ENODEV;

	if (!dev_is_pci(dev))
		return device_context_mapped(iommu, bus, devfn);

	return !pci_for_each_dma_alias(to_pci_dev(dev),
				       domain_context_mapped_cb, iommu);
}


static inline int iommu_domain_cache_init(void)
{
	int ret = 0;

	iommu_domain_cache = kmem_cache_create("iommu_domain",
					 sizeof(struct dmar_domain),
					 0,
					 SLAB_HWCACHE_ALIGN,

					 NULL);
	if (!iommu_domain_cache) {
		pr_err("Couldn't create iommu_domain cache\n");
		ret = -ENOMEM;
	}

	return ret;
}


static struct page *domain_unmap(struct dmar_domain *domain,
				 unsigned long start_pfn,
				 unsigned long last_pfn)
{
	struct page *freelist;

	BUG_ON(!domain_pfn_supported(domain, start_pfn));
	BUG_ON(!domain_pfn_supported(domain, last_pfn));
	BUG_ON(start_pfn > last_pfn);

	/* we don't need lock here; nobody else touches the iova range */
	freelist = dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
				       domain->pgd, 0, start_pfn, last_pfn, NULL);

	/* free pgd */
	if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
		struct page *pgd_page = virt_to_page(domain->pgd);
		pgd_page->freelist = freelist;
		freelist = pgd_page;

		domain->pgd = NULL;
	}

	return freelist;
}


static int __init si_domain_init(int hw)
{
	int nid, ret;

	si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY);
	if (!si_domain)
		return -EFAULT;

	if (md_domain_init(si_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
		domain_exit(si_domain);
		return -EFAULT;
	}

	pr_debug("Identity mapping domain allocated\n");

	if (hw)
		return 0;

	for_each_online_node(nid) {
		unsigned long start_pfn, end_pfn;
		int i;

		for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, NULL) {
			ret = iommu_domain_identity_map(si_domain,
					PFN_PHYS(start_pfn), PFN_PHYS(end_pfn));
			if (ret)
				return ret;
		}
	}

	return 0;
}


static void iommu_flush_iova(struct iova_domain *iovad)
{
	struct dmar_domain *domain;
	int idx;

	domain = container_of(iovad, struct dmar_domain, iovad);

	for_each_domain_iommu(idx, domain) {
		struct intel_iommu *iommu = g_iommus[idx];
		u16 did = domain->iommu_did[iommu->seq_id];

		iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH);

		if (!cap_caching_mode(iommu->cap))
			iommu_flush_dev_iotlb(get_iommu_domain(iommu, did),
					      0, MAX_AGAW_PFN_WIDTH);
	}
}


static void quirk_iommu_rwbf(struct pci_dev *dev)
{
	/*
	 * Mobile 4 Series Chipset neglects to set RWBF capability,
	 * but needs it. Same seems to hold for the desktop versions.
	 */
	pci_info(dev, "Forcing write-buffer flush capability\n");
	rwbf_quirk = 1;
}


static inline unsigned long virt_to_dma_pfn(void *p)
{
	return page_to_dma_pfn(virt_to_page(p));
}


static int intel_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
	return !dma_addr;
}


static unsigned long intel_alloc_iova(struct device *dev,
				     struct dmar_domain *domain,
				     unsigned long nrpages, uint64_t dma_mask)
{
	unsigned long iova_pfn = 0;

	/* Restrict dma_mask to the width that the iommu can handle */
	dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask);
	/* Ensure we reserve the whole size-aligned region */
	nrpages = __roundup_pow_of_two(nrpages);

	if (!dmar_forcedac && dma_mask > DMA_BIT_MASK(32)) {
		/*
		 * First try to allocate an io virtual address in
		 * DMA_BIT_MASK(32) and if that fails then try allocating
		 * from higher range
		 */
		iova_pfn = alloc_iova_fast(&domain->iovad, nrpages,
					   IOVA_PFN(DMA_BIT_MASK(32)), false);
		if (iova_pfn)
			return iova_pfn;
	}
	iova_pfn = alloc_iova_fast(&domain->iovad, nrpages,
				   IOVA_PFN(dma_mask), true);
	if (unlikely(!iova_pfn)) {
		pr_err("Allocating %ld-page iova for %s failed",
		       nrpages, dev_name(dev));
		return 0;
	}

	return iova_pfn;
}


static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
				      unsigned long pfn, int *target_level)
{
	struct dma_pte *parent, *pte = NULL;
	int level = agaw_to_level(domain->agaw);
	int offset;

	BUG_ON(!domain->pgd);

	if (!domain_pfn_supported(domain, pfn))
		/* Address beyond IOMMU's addressing capabilities. */
		return NULL;

	parent = domain->pgd;

	while (1) {
		void *tmp_page;

		offset = pfn_level_offset(pfn, level);
		pte = &parent[offset];
		if (!*target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
			break;
		if (level == *target_level)
			break;

		if (!dma_pte_present(pte)) {
			uint64_t pteval;

			tmp_page = alloc_pgtable_page(domain->nid);

			if (!tmp_page)
				return NULL;

			domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE);
			pteval = ((uint64_t)virt_to_dma_pfn(tmp_page) << VTD_PAGE_SHIFT) | DMA_PTE_READ | DMA_PTE_WRITE;
			if (cmpxchg64(&pte->val, 0ULL, pteval))
				/* Someone else set it while we were thinking; use theirs. */
				free_pgtable_page(tmp_page);
			else
				domain_flush_cache(domain, pte, sizeof(*pte));
		}
		if (level == 1)
			break;

		parent = phys_to_virt(dma_pte_addr(pte));
		level--;
	}

	if (!*target_level)
		*target_level = level;

	return pte;
}


int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev)
{
	struct device_domain_info *info;
	struct context_entry *context;
	struct dmar_domain *domain;
	unsigned long flags;
	u64 ctx_lo;
	int ret;

	domain = get_valid_domain_for_dev(sdev->dev);
	if (!domain)
		return -EINVAL;

	spin_lock_irqsave(&device_domain_lock, flags);
	spin_lock(&iommu->lock);

	ret = -EINVAL;
	info = sdev->dev->archdata.iommu;
	if (!info || !info->pasid_supported)
		goto out;

	context = iommu_context_addr(iommu, info->bus, info->devfn, 0);
	if (WARN_ON(!context))
		goto out;

	ctx_lo = context[0].lo;

	sdev->did = domain->iommu_did[iommu->seq_id];
	sdev->sid = PCI_DEVID(info->bus, info->devfn);

	if (!(ctx_lo & CONTEXT_PASIDE)) {
		if (iommu->pasid_state_table)
			context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
		context[1].lo = (u64)virt_to_phys(info->pasid_table->table) |
			intel_iommu_get_pts(sdev->dev);

		wmb();
		/* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both
		 * extended to permit requests-with-PASID if the PASIDE bit
		 * is set. which makes sense. For CONTEXT_TT_PASS_THROUGH,
		 * however, the PASIDE bit is ignored and requests-with-PASID
		 * are unconditionally blocked. Which makes less sense.
		 * So convert from CONTEXT_TT_PASS_THROUGH to one of the new
		 * "guest mode" translation types depending on whether ATS
		 * is available or not. Annoyingly, we can't use the new
		 * modes *unless* PASIDE is set. */
		if ((ctx_lo & CONTEXT_TT_MASK) == (CONTEXT_TT_PASS_THROUGH << 2)) {
			ctx_lo &= ~CONTEXT_TT_MASK;
			if (info->ats_supported)
				ctx_lo |= CONTEXT_TT_PT_PASID_DEV_IOTLB << 2;
			else
				ctx_lo |= CONTEXT_TT_PT_PASID << 2;
		}
		ctx_lo |= CONTEXT_PASIDE;
		if (iommu->pasid_state_table)
			ctx_lo |= CONTEXT_DINVE;
		if (info->pri_supported)
			ctx_lo |= CONTEXT_PRS;
		context[0].lo = ctx_lo;
		wmb();
		iommu->flush.flush_context(iommu, sdev->did, sdev->sid,
					   DMA_CCMD_MASK_NOBIT,
					   DMA_CCMD_DEVICE_INVL);
	}

	/* Enable PASID support in the device, if it wasn't already */
	if (!info->pasid_enabled)
		iommu_enable_dev_iotlb(info);

	if (info->ats_enabled) {
		sdev->dev_iotlb = 1;
		sdev->qdep = info->ats_qdep;
		if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS)
			sdev->qdep = 0;
	}
	ret = 0;

 out:
	spin_unlock(&iommu->lock);
	spin_unlock_irqrestore(&device_domain_lock, flags);

	return ret;
}


static int __init si_domain_init(int hw)
{
	int nid, ret = 0;

	si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY);
	if (!si_domain)
		return -EFAULT;

	if (md_domain_init(si_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
		domain_exit(si_domain);
		return -EFAULT;
	}

	pr_debug("Identity mapping domain allocated\n");

	if (hw)
		return 0;

	for_each_online_node(nid) {
		unsigned long start_pfn, end_pfn;
		int i;

		for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, NULL) {
			ret = iommu_domain_identity_map(si_domain,
					PFN_PHYS(start_pfn), PFN_PHYS(end_pfn));
			if (ret)
				return ret;
		}
	}

	return 0;
}


static int domain_detach_iommu(struct dmar_domain *domain,
			       struct intel_iommu *iommu)
{
	int num, count = INT_MAX;

	assert_spin_locked(&device_domain_lock);
	assert_spin_locked(&iommu->lock);

	domain->iommu_refcnt[iommu->seq_id] -= 1;
	count = --domain->iommu_count;
	if (domain->iommu_refcnt[iommu->seq_id] == 0) {
		num = domain->iommu_did[iommu->seq_id];
		clear_bit(num, iommu->domain_ids);
		set_iommu_domain(iommu, num, NULL);

		domain_update_iommu_cap(domain);
		domain->iommu_did[iommu->seq_id] = 0;
	}

	return count;
}


int db_col_valid(struct db_filter_col *col)
{
	if (col != NULL && col->state == _DB_STA_VALID && col->filter_cnt > 0)
		return 0;
	return -EINVAL;
}


int db_col_reset(struct db_filter_col *col, uint32_t def_action)
{
	unsigned int iter;
	struct db_filter *db;
	struct db_filter_snap *snap;

	if (col == NULL)
		return -EINVAL;

	/* free any filters */
	for (iter = 0; iter < col->filter_cnt; iter++)
		_db_release(col->filters[iter]);
	col->filter_cnt = 0;
	if (col->filters)
		free(col->filters);
	col->filters = NULL;

	/* set the endianess to undefined */
	col->endian = 0;

	/* set the default attribute values */
	col->attr.act_default = def_action;
	col->attr.act_badarch = SCMP_ACT_KILL;
	col->attr.nnp_enable = 1;
	col->attr.tsync_enable = 0;
	col->attr.api_tskip = 0;

	/* set the state */
	col->state = _DB_STA_VALID;

	/* reset the initial db */
	db = _db_init(arch_def_native);
	if (db == NULL)
		return -ENOMEM;
	if (db_col_db_add(col, db) < 0) {
		_db_release(db);
		return -ENOMEM;
	}

	/* reset the transactions */
	while (col->snapshots) {
		snap = col->snapshots;
		col->snapshots = snap->next;
		for (iter = 0; iter < snap->filter_cnt; iter++)
			_db_release(snap->filters[iter]);
		free(snap->filters);
		free(snap);
	}

	return 0;
}


struct db_filter_col *db_col_init(uint32_t def_action)
{
	struct db_filter_col *col;

	col = zmalloc(sizeof(*col));
	if (col == NULL)
		return NULL;

	/* reset the DB to a known state */
	if (db_col_reset(col, def_action) < 0)
		goto init_failure;

	return col;

init_failure:
	db_col_release(col);
	return NULL;
}


static unsigned int _db_tree_remove(struct db_arg_chain_tree **tree,
				    struct db_arg_chain_tree *node)
{
	int cnt = 0;
	struct db_arg_chain_tree *c_iter;

	if (tree == NULL || *tree == NULL || node == NULL)
		return 0;

	c_iter = *tree;
	while (c_iter->lvl_prv != NULL)
		c_iter = c_iter->lvl_prv;

	do {
		/* current node? */
		if (c_iter == node)
			goto remove;

		/* check the sub-trees */
		cnt += _db_tree_remove(&(c_iter->nxt_t), node);
		cnt += _db_tree_remove(&(c_iter->nxt_f), node);

		/* check for empty/zombie nodes */
		if (_db_chain_zombie(c_iter))
			goto remove;

		/* next node on this level */
		c_iter = c_iter->lvl_nxt;
	} while (c_iter != NULL && cnt == 0);

	return cnt;

remove:
	/* reset the tree pointer if needed */
	if (c_iter == *tree) {
		if (c_iter->lvl_prv != NULL)
			*tree = c_iter->lvl_prv;
		else
			*tree = c_iter->lvl_nxt;
	}

	/* remove the node from the current level */
	if (c_iter->lvl_prv)
		c_iter->lvl_prv->lvl_nxt = c_iter->lvl_nxt;
	if (c_iter->lvl_nxt)
		c_iter->lvl_nxt->lvl_prv = c_iter->lvl_prv;
	c_iter->lvl_prv = NULL;
	c_iter->lvl_nxt = NULL;

	/* free the node and any sub-trees */
	cnt += _db_node_put(&c_iter);

	return cnt;
}


static int _gen_bpf_build_jmp_ret(struct bpf_state *state,
				  struct bpf_blk *blk, unsigned int offset,
				  struct bpf_blk *blk_ret)
{
	unsigned int j_len;
	uint64_t tgt_hash = blk_ret->hash;
	struct bpf_blk *b_jmp, *b_new;

	/* calculate the jump distance */
	j_len = blk->blk_cnt - (offset + 1);
	b_jmp = blk->next;
	while (b_jmp != NULL && b_jmp != blk_ret && j_len < _BPF_JMP_MAX_RET) {
		j_len += b_jmp->blk_cnt;
		b_jmp = b_jmp->next;
	}
	if (b_jmp == NULL)
		return -EFAULT;
	if (j_len <= _BPF_JMP_MAX_RET && b_jmp == blk_ret)
		return 0;

	/* we need a closer return instruction, see if one already exists */
	j_len = blk->blk_cnt - (offset + 1);
	b_jmp = blk->next;
	while (b_jmp != NULL && b_jmp->hash != tgt_hash &&
	       j_len < _BPF_JMP_MAX_RET) {
		j_len += b_jmp->blk_cnt;
		b_jmp = b_jmp->next;
	}
	if (b_jmp == NULL)
		return -EFAULT;
	if (j_len <= _BPF_JMP_MAX_RET && b_jmp->hash == tgt_hash)
		return 0;

	/* we need to insert a new return instruction - create one */
	b_new = _gen_bpf_action(state, NULL, blk_ret->blks[0].k.tgt.imm_k);
	if (b_new == NULL)
		return -EFAULT;

	/* NOTE - we need to be careful here, we're giving the block a hash
	 *	  value (this is a sneaky way to ensure we leverage the
	 *	  inserted long jumps as much as possible) but we never add the
	 *	  block to the hash table so it won't get cleaned up
	 *	  automatically */
	b_new->hash = tgt_hash;

	/* insert the jump after the current jumping block */
	b_new->prev = blk;
	b_new->next = blk->next;
	blk->next->prev = b_new;
	blk->next = b_new;

	return 1;
}


static struct bpf_blk *_blk_prepend(struct bpf_state *state,
				    struct bpf_blk *blk,
				    const struct bpf_instr *instr)
{
	/* empty - we can treat this like a normal append operation */
	if (blk == NULL || blk->blk_cnt == 0)
		return _blk_append(state, blk, instr);

	if (_blk_resize(state, blk, 1) == NULL)
		return NULL;
	memmove(&blk->blks[1], &blk->blks[0], blk->blk_cnt++ * sizeof(*instr));
	memcpy(&blk->blks[0], instr, sizeof(*instr));

	return blk;
}


static struct bpf_blk *_gen_bpf_find_nxt(const struct bpf_blk *blk,
					 unsigned int nxt)
{
	struct bpf_blk *iter = blk->next;

	for (; (iter != NULL) && (nxt > 0); nxt--)
		iter = iter->next;

	return iter;
}


struct bpf_program *gen_bpf_generate(const struct db_filter_col *col)
{
	int rc;
	struct bpf_state state;
	struct bpf_program *prgm;

	memset(&state, 0, sizeof(state));
	state.attr = &col->attr;

	prgm = zmalloc(sizeof(*(prgm)));
	if (prgm == NULL)
		return NULL;
	state.bpf = prgm;

	rc = _gen_bpf_build_bpf(&state, col);
	if (rc == 0)
		state.bpf = NULL;
	_state_release(&state);

	return prgm;
}


int am_validate_redirect_url(request_rec *r, const char *url)
{
    am_dir_cfg_rec *cfg = am_get_dir_cfg(r);
    apr_uri_t uri;
    int ret;

    ret = apr_uri_parse(r->pool, url, &uri);
    if (ret != APR_SUCCESS) {
        AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
                      "Invalid redirect URL: %s", url);
        return HTTP_BAD_REQUEST;
    }

    /* Sanity check of the scheme of the domain. We only allow http and https. */
    if (uri.scheme) {
        if (strcasecmp(uri.scheme, "http")
            && strcasecmp(uri.scheme, "https")) {
            AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
                          "Only http or https scheme allowed in redirect URL: %s (%s)",
                          url, uri.scheme);
            return HTTP_BAD_REQUEST;
        }
    }

    if (!uri.hostname) {
        return OK; /* No hostname to check. */
    }

    for (int i = 0; cfg->redirect_domains[i] != NULL; i++) {
        const char *redirect_domain = cfg->redirect_domains[i];
        if (!strcasecmp(redirect_domain, "[self]")) {
            if (!strcasecmp(uri.hostname, am_request_hostname(r))) {
                return OK;
            }
        } else if (apr_fnmatch(redirect_domain, uri.hostname,
                               APR_FNM_PERIOD | APR_FNM_CASE_BLIND) ==
                   APR_SUCCESS) {
            return OK;
        }
    }
    AM_LOG_RERROR(APLOG_MARK, APLOG_ERR, 0, r,
                  "Untrusted hostname (%s) in redirect URL: %s",
                  uri.hostname, url);
    return HTTP_BAD_REQUEST;
}


push_token(apr_array_header_t *tokens, TokenType type, const char *str,
           const char *start, const char *end)
{
    apr_size_t offset = start - str;
    Token *token = apr_array_push(tokens);

    if (type == TOKEN_DBL_QUOTE_STRING) {
        /* do not include quotes in token value */
        start++; end--;
    }

    token->type = type;
    token->len = end - start;
    token->offset = offset;
    token->str = apr_pstrmemdup(tokens->pool, start, token->len);

    if (type == TOKEN_DBL_QUOTE_STRING) {
        /*
         * The original HTTP 1.1 spec was ambiguous with respect to
         * backslash quoting inside double quoted strings. This has since
         * been resolved in this errata:
         *
         * http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-16.html#rfc.section.3.2.3
         *
         * Which states:
         *
         * Recipients that process the value of the quoted-string MUST
         * handle a quoted-pair as if it were replaced by the octet
         * following the backslash.
         *
         * Senders SHOULD NOT escape octets in quoted-strings that do not
         * require escaping (i.e., other than DQUOTE and the backslash
         * octet).
         */
        char *p, *t;

        for (p = token->str; *p; p++) {
            if (p[0] == '\\' && p[1]) {
                /*
                 * Found backslash with following character.
                 * Move rest of string down 1 character.
                 */
                for (t = p; *t; t++) {
                    t[0] = t[1];
                }
                token->len--;
            }
        }
    }
}


apr_status_t am_file_stat(am_file_data_t *file_data)
{
    char buffer[512];

    if (file_data == NULL) {
        return APR_EINVAL;
    }

    file_data->strerror = NULL;

    file_data->stat_time = apr_time_now();
    file_data->rv = apr_stat(&file_data->finfo, file_data->path,
                             APR_FINFO_SIZE, file_data->pool);
    if (file_data->rv != APR_SUCCESS) {
        file_data->strerror =
            apr_psprintf(file_data->pool,
                         "apr_stat: Error opening \"%s\" [%d] \"%s\"",
                         file_data->path, file_data->rv,
                         apr_strerror(file_data->rv, buffer, sizeof(buffer)));
    }

    return file_data->rv;
}


int PDFDoc::saveAs(const GooString *name, PDFWriteMode mode) {
  FILE *f;
  OutStream *outStr;
  int res;

  if (!(f = openFile(name->c_str(), "wb"))) {
    error(errIO, -1, "Couldn't open file '{0:t}'", name);
    return errOpenFile;
  }
  outStr = new FileOutStream(f,0);
  res = saveAs(outStr, mode);
  delete outStr;
  fclose(f);
  return res;
}


    void SetNetwork(const CString& sLine) {
        const CString sVar = sLine.Token(1).AsLower();
        const CString sUsername = sLine.Token(2);
        const CString sNetwork = sLine.Token(3);
        const CString sValue = sLine.Token(4, true);

        if (sValue.empty()) {
            PutModule(t_s(
                "Usage: SetNetwork <variable> <username> <network> <value>"));
            return;
        }

        CUser* pUser = FindUser(sUsername);
        if (!pUser) {
            return;
        }

        CIRCNetwork* pNetwork = FindNetwork(pUser, sNetwork);
        if (!pNetwork) {
            return;
        }

        if (sVar.Equals("nick")) {
            pNetwork->SetNick(sValue);
            PutModule("Nick = " + pNetwork->GetNick());
        } else if (sVar.Equals("altnick")) {
            pNetwork->SetAltNick(sValue);
            PutModule("AltNick = " + pNetwork->GetAltNick());
        } else if (sVar.Equals("ident")) {
            pNetwork->SetIdent(sValue);
            PutModule("Ident = " + pNetwork->GetIdent());
        } else if (sVar.Equals("realname")) {
            pNetwork->SetRealName(sValue);
            PutModule("RealName = " + pNetwork->GetRealName());
        } else if (sVar.Equals("bindhost")) {
            if (!pUser->DenySetBindHost() || GetUser()->IsAdmin()) {
                if (sValue.Equals(pNetwork->GetBindHost())) {
                    PutModule(t_s("This bind host is already set!"));
                    return;
                }

                pNetwork->SetBindHost(sValue);
                PutModule("BindHost = " + sValue);
            } else {
                PutModule(t_s("Access denied!"));
            }
        } else if (sVar.Equals("floodrate")) {
            pNetwork->SetFloodRate(sValue.ToDouble());
            PutModule("FloodRate = " + CString(pNetwork->GetFloodRate()));
        } else if (sVar.Equals("floodburst")) {
            pNetwork->SetFloodBurst(sValue.ToUShort());
            PutModule("FloodBurst = " + CString(pNetwork->GetFloodBurst()));
        } else if (sVar.Equals("joindelay")) {
            pNetwork->SetJoinDelay(sValue.ToUShort());
            PutModule("JoinDelay = " + CString(pNetwork->GetJoinDelay()));
#ifdef HAVE_ICU
        } else if (sVar.Equals("encoding")) {
            pNetwork->SetEncoding(sValue);
            PutModule("Encoding = " + pNetwork->GetEncoding());
#endif
        } else if (sVar.Equals("quitmsg")) {
            pNetwork->SetQuitMsg(sValue);
            PutModule("QuitMsg = " + pNetwork->GetQuitMsg());
        } else if (sVar.Equals("trustallcerts")) {
            bool b = sValue.ToBool();
            pNetwork->SetTrustAllCerts(b);
            PutModule("TrustAllCerts = " + CString(b));
        } else if (sVar.Equals("trustpki")) {
            bool b = sValue.ToBool();
            pNetwork->SetTrustPKI(b);
            PutModule("TrustPKI = " + CString(b));
        } else {
            PutModule(t_s("Error: Unknown variable"));
        }
    }


const CString& CZNC::GetCurPath() const {
    if (!CFile::Exists(m_sCurPath)) {
        CDir::MakeDir(m_sCurPath);
    }
    return m_sCurPath;
}


    void Delay(unsigned short int uDelay) {
        m_bDelayed = true;
        Start(uDelay);
    }


const vector<CServer*>& CIRCNetwork::GetServers() const { return m_vServers; }


void CZNC::AuthUser(std::shared_ptr<CAuthBase> AuthClass) {
    // TODO unless the auth module calls it, CUser::IsHostAllowed() is not
    // honoured
    bool bReturn = false;
    GLOBALMODULECALL(OnLoginAttempt(AuthClass), &bReturn);
    if (bReturn) return;

    CUser* pUser = FindUser(AuthClass->GetUsername());

    if (!pUser || !pUser->CheckPass(AuthClass->GetPassword())) {
        AuthClass->RefuseLogin("Invalid Password");
        return;
    }

    CString sHost = AuthClass->GetRemoteIP();

    if (!pUser->IsHostAllowed(sHost)) {
        AuthClass->RefuseLogin("Your host [" + sHost + "] is not allowed");
        return;
    }

    AuthClass->AcceptLogin(*pUser);
}


bool CIRCNetwork::PutIRC(const CString& sLine) {
    CIRCSock* pIRCSock = GetIRCSock();

    if (!pIRCSock) {
        return false;
    }

    pIRCSock->PutIRC(sLine);
    return true;
}


void CUser::CloneNetworks(const CUser& User) {
    const vector<CIRCNetwork*>& vNetworks = User.GetNetworks();
    for (CIRCNetwork* pUserNetwork : vNetworks) {
        CIRCNetwork* pNetwork = FindNetwork(pUserNetwork->GetName());

        if (pNetwork) {
            pNetwork->Clone(*pUserNetwork);
        } else {
            new CIRCNetwork(this, *pUserNetwork);
        }
    }

    set<CString> ssDeleteNetworks;
    for (CIRCNetwork* pNetwork : m_vIRCNetworks) {
        if (!(User.FindNetwork(pNetwork->GetName()))) {
            ssDeleteNetworks.insert(pNetwork->GetName());
        }
    }

    for (const CString& sNetwork : ssDeleteNetworks) {
        // The following will move all the clients to the user.
        // So the clients are not disconnected. The client could
        // have requested the rehash. Then when we do
        // client->PutStatus("Rehashing succeeded!") we would
        // crash if there was no client anymore.
        const vector<CClient*>& vClients = FindNetwork(sNetwork)->GetClients();

        while (vClients.begin() != vClients.end()) {
            CClient* pClient = vClients.front();
            // This line will remove pClient from vClients,
            // because it's a reference to the internal Network's vector.
            pClient->SetNetwork(nullptr);
        }

        DeleteNetwork(sNetwork);
    }
}


    void PrintHelp(const CString& sLine) {
        HandleHelpCommand(sLine);

        const CString str = t_s("String");
        const CString boolean = t_s("Boolean (true/false)");
        const CString integer = t_s("Integer");
        const CString number = t_s("Number");

        const CString sCmdFilter = sLine.Token(1, false);
        const CString sVarFilter = sLine.Token(2, true).AsLower();

        if (sCmdFilter.empty() || sCmdFilter.StartsWith("Set") ||
            sCmdFilter.StartsWith("Get")) {
            Setting vars[] = {
                {"Nick", str},
                {"Altnick", str},
                {"Ident", str},
                {"RealName", str},
                {"BindHost", str},
                {"MultiClients", boolean},
                {"DenyLoadMod", boolean},
                {"DenySetBindHost", boolean},
                {"DefaultChanModes", str},
                {"QuitMsg", str},
                {"ChanBufferSize", integer},
                {"QueryBufferSize", integer},
                {"AutoClearChanBuffer", boolean},
                {"AutoClearQueryBuffer", boolean},
                {"Password", str},
                {"JoinTries", integer},
                {"MaxJoins", integer},
                {"MaxNetworks", integer},
                {"MaxQueryBuffers", integer},
                {"Timezone", str},
                {"Admin", boolean},
                {"AppendTimestamp", boolean},
                {"PrependTimestamp", boolean},
                {"AuthOnlyViaModule", boolean},
                {"TimestampFormat", str},
                {"DCCBindHost", str},
                {"StatusPrefix", str},
#ifdef HAVE_I18N
                {"Language", str},
#endif
#ifdef HAVE_ICU
                {"ClientEncoding", str},
#endif
            };
            PrintVarsHelp(sVarFilter, vars, ARRAY_SIZE(vars),
                          t_s("The following variables are available when "
                              "using the Set/Get commands:"));
        }

        if (sCmdFilter.empty() || sCmdFilter.StartsWith("SetNetwork") ||
            sCmdFilter.StartsWith("GetNetwork")) {
            Setting nvars[] = {
                {"Nick", str},
                {"Altnick", str},
                {"Ident", str},
                {"RealName", str},
                {"BindHost", str},
                {"FloodRate", number},
                {"FloodBurst", integer},
                {"JoinDelay", integer},
#ifdef HAVE_ICU
                {"Encoding", str},
#endif
                {"QuitMsg", str},
                {"TrustAllCerts", boolean},
                {"TrustPKI", boolean},
            };
            PrintVarsHelp(sVarFilter, nvars, ARRAY_SIZE(nvars),
                          t_s("The following variables are available when "
                              "using the SetNetwork/GetNetwork commands:"));
        }

        if (sCmdFilter.empty() || sCmdFilter.StartsWith("SetChan") ||
            sCmdFilter.StartsWith("GetChan")) {
            Setting cvars[] = {{"DefModes", str},
                               {"Key", str},
                               {"BufferSize", integer},
                               {"InConfig", boolean},
                               {"AutoClearChanBuffer", boolean},
                               {"Detached", boolean}};
            PrintVarsHelp(sVarFilter, cvars, ARRAY_SIZE(cvars),
                          t_s("The following variables are available when "
                              "using the SetChan/GetChan commands:"));
        }

        if (sCmdFilter.empty())
            PutModule(
                t_s("You can use $user as the user name and $network as the "
                    "network name for modifying your own user and network."));
    }


CString CZNC::GetCompileOptionsString() {
    // Build system doesn't affect ABI
    return ZNC_COMPILE_OPTIONS_STRING + CString(
                                            ", build: "
#ifdef BUILD_WITH_CMAKE
                                            "cmake"
#else
                                            "autoconf"
#endif
                                            );
}


static array_size_helper<N> array_size(T (&)[N]) {
    return array_size_helper<N>();
}


std::vector<CQuery*> CIRCNetwork::FindQueries(const CString& sWild) const {
    std::vector<CQuery*> vQueries;
    vQueries.reserve(m_vQueries.size());
    const CString sLower = sWild.AsLower();
    for (CQuery* pQuery : m_vQueries) {
        if (pQuery->GetName().AsLower().WildCmp(sLower))
            vQueries.push_back(pQuery);
    }
    return vQueries;
}


const CString& CZNC::GetHomePath() const { return CFile::GetHomePath(); }


TEST_F(ZNCTest, Modperl) {
    if (QProcessEnvironment::systemEnvironment().value(
            "DISABLED_ZNC_PERL_PYTHON_TEST") == "1") {
        return;
    }
    auto znc = Run();
    znc->CanLeak();
    auto ircd = ConnectIRCd();
    auto client = LoginClient();
    client.Write("znc loadmod modperl");
    client.Write("znc loadmod perleval");
    client.Write("PRIVMSG *perleval :2+2");
    client.ReadUntil(":*perleval!znc@znc.in PRIVMSG nick :Result: 4");
    client.Write("PRIVMSG *perleval :$self->GetUser->GetUserName");
    client.ReadUntil("Result: user");
}


void CUser::SetMultiClients(bool b) { m_bMultiClients = b; }


CModule* CZNC::FindModule(const CString& sModName, CUser* pUser) {
    if (pUser) {
        return pUser->GetModules().FindModule(sModName);
    }

    return CZNC::Get().GetModules().FindModule(sModName);
}


bool CZNC::DeletePidFile() {
    CFile* File = InitPidFile();
    if (File == nullptr) return false;

    CUtils::PrintAction("Deleting pid file [" + File->GetLongName() + "]");

    bool bRet = File->Delete();

    delete File;
    CUtils::PrintStatus(bRet);
    return bRet;
}


CString CZNC::GetDHParamLocation() const {
    return CDir::ChangeDir(
        "", m_sSSLDHParamFile.empty() ? m_sSSLCertFile : m_sSSLDHParamFile);
}


    void AddChan(const CString& sLine) {
        const CString sUsername = sLine.Token(1);
        const CString sNetwork = sLine.Token(2);
        const CString sChan = sLine.Token(3);

        if (sChan.empty()) {
            PutModule(t_s("Usage: AddChan <username> <network> <channel>"));
            return;
        }

        CUser* pUser = FindUser(sUsername);
        if (!pUser) return;

        CIRCNetwork* pNetwork = FindNetwork(pUser, sNetwork);
        if (!pNetwork) {
            return;
        }

        if (pNetwork->FindChan(sChan)) {
            PutModule(t_f("Error: User {1} already has a channel named {2}.")(
                sUsername, sChan));
            return;
        }

        CChan* pChan = new CChan(sChan, pNetwork, true);
        if (pNetwork->AddChan(pChan))
            PutModule(t_f("Channel {1} for user {2} added to network {3}.")(
                pChan->GetName(), sUsername, pNetwork->GetName()));
        else
            PutModule(t_f(
                "Could not add channel {1} for user {2} to network {3}, does "
                "it already exist?")(sChan, sUsername, pNetwork->GetName()));
    }


bool CZNC::IsForcingEncoding() const { return m_uiForceEncoding; }


bool CUser::PutModNotice(const CString& sModule, const CString& sLine,
                         CClient* pClient, CClient* pSkipClient) {
    for (CClient* pEachClient : m_vClients) {
        if ((!pClient || pClient == pEachClient) &&
            pSkipClient != pEachClient) {
            pEachClient->PutModNotice(sModule, sLine);

            if (pClient) {
                return true;
            }
        }
    }

    return (pClient == nullptr);
}


bool CIRCNetwork::IsIRCConnected() const {
    const CIRCSock* pSock = GetIRCSock();
    return (pSock && pSock->IsAuthed());
}


void CZNC::PauseConnectQueue() {
    DEBUG("Connection queue paused");
    m_uiConnectPaused++;

    if (m_pConnectQueueTimer) {
        m_pConnectQueueTimer->Pause();
    }
}


CZNC::CZNC()
    : m_TimeStarted(time(nullptr)),
      m_eConfigState(ECONFIG_NOTHING),
      m_vpListeners(),
      m_msUsers(),
      m_msDelUsers(),
      m_Manager(),
      m_sCurPath(""),
      m_sZNCPath(""),
      m_sConfigFile(""),
      m_sSkinName(""),
      m_sStatusPrefix(""),
      m_sPidFile(""),
      m_sSSLCertFile(""),
      m_sSSLKeyFile(""),
      m_sSSLDHParamFile(""),
      m_sSSLCiphers(""),
      m_sSSLProtocols(""),
      m_vsBindHosts(),
      m_vsTrustedProxies(),
      m_vsMotd(),
      m_pLockFile(nullptr),
      m_uiConnectDelay(5),
      m_uiAnonIPLimit(10),
      m_uiMaxBufferSize(500),
      m_uDisabledSSLProtocols(Csock::EDP_SSL),
      m_pModules(new CModules),
      m_uBytesRead(0),
      m_uBytesWritten(0),
      m_lpConnectQueue(),
      m_pConnectQueueTimer(nullptr),
      m_uiConnectPaused(0),
      m_uiForceEncoding(0),
      m_sConnectThrottle(),
      m_bProtectWebSessions(true),
      m_bHideVersion(false),
      m_bAuthOnlyViaModule(false),
      m_Translation("znc"),
      m_uiConfigWriteDelay(0),
      m_pConfigTimer(nullptr) {
    if (!InitCsocket()) {
        CUtils::PrintError("Could not initialize Csocket!");
        exit(-1);
    }
    m_sConnectThrottle.SetTTL(30000);
}


bool CUser::PutStatus(const CString& sLine, CClient* pClient,
                      CClient* pSkipClient) {
    vector<CClient*> vClients = GetAllClients();
    for (CClient* pEachClient : vClients) {
        if ((!pClient || pClient == pEachClient) &&
            pSkipClient != pEachClient) {
            pEachClient->PutStatus(sLine);

            if (pClient) {
                return true;
            }
        }
    }

    return (pClient == nullptr);
}