/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2007 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author: Alexandre Kalendarev akalend@mail.ru Copyright (c) 2009-2010 |
  | Lead:                                                                |
  | - Pieter de Zwart                                                    |
  | Maintainers:                                                         |
  | - Brad Rodriguez                                                     |
  | - Jonathan Tansavatdi                                                |
  +----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
    #include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "zend_ini.h"
#include "zend_exceptions.h"

#ifdef PHP_WIN32
    #if PHP_VERSION_ID >= 80000
        #include <stdint.h>
    #else
        #include "win32/php_stdint.h"
    #endif
    #include "win32/signal.h"
#else
    #include <stdint.h>
    #include <signal.h>
#endif

#if HAVE_LIBRABBITMQ_NEW_LAYOUT
    #include <rabbitmq-c/amqp.h>
    #include <rabbitmq-c/framing.h>
#else
    #include <amqp.h>
    #include <amqp_framing.h>
#endif

#include "php_amqp.h"
#include "amqp_connection.h"
#include "amqp_basic_properties.h"
#include "amqp_connection_resource.h"
#include "amqp_channel.h"
#include "amqp_envelope.h"
#include "amqp_envelope_exception.h"
#include "amqp_exchange.h"
#include "amqp_queue.h"
#include "amqp_value.h"
#include "amqp_timestamp.h"
#include "amqp_decimal.h"

#ifdef PHP_WIN32
    #include "win32/unistd.h"
#else
    #include <unistd.h>
#endif

/* True global resources - no need for thread safety here */
zend_class_entry *amqp_exception_class_entry, *amqp_connection_exception_class_entry,
    *amqp_channel_exception_class_entry, *amqp_exchange_exception_class_entry, *amqp_queue_exception_class_entry,
    *amqp_value_exception_class_entry;

/* {{{ amqp_functions[]
*
*Every user visible function must have an entry in amqp_functions[].
*/
zend_function_entry amqp_functions[] = {
    {NULL, NULL, NULL}
};
/* }}} */

bool php_amqp_is_valid_identifier(zend_string *val)
{
    return ZSTR_LEN(val) > 0 && ZSTR_LEN(val) <= PHP_AMQP_MAX_IDENTIFIER_LENGTH;
}

bool php_amqp_is_valid_credential(zend_string *val)
{
    return ZSTR_LEN(val) > 0 && ZSTR_LEN(val) <= PHP_AMQP_MAX_CREDENTIALS_LENGTH;
}

bool php_amqp_is_valid_port(zend_long val) { return val >= PHP_AMQP_MIN_PORT && val <= PHP_AMQP_MAX_PORT; }

bool php_amqp_is_valid_timeout(double timeout) { return timeout >= 0; }

bool php_amqp_is_valid_channel_max(zend_long val) { return val > 0 && val <= PHP_AMQP_DEFAULT_CHANNEL_MAX; }

bool php_amqp_is_valid_frame_size_max(zend_long val) { return val > 0 && val <= PHP_AMQP_MAX_FRAME_SIZE; }

bool php_amqp_is_valid_heartbeat(zend_long val) { return val >= 0 && val <= PHP_AMQP_MAX_HEARTBEAT; }

bool php_amqp_is_valid_prefetch_size(zend_long val) { return val >= 0 && val <= PHP_AMQP_MAX_PREFETCH_SIZE; }

bool php_amqp_is_valid_prefetch_count(zend_long val) { return val >= 0 && val <= PHP_AMQP_MAX_PREFETCH_COUNT; }

static ZEND_INI_MH(onUpdateIdentifier)
{
    if (new_value != NULL && !php_amqp_is_valid_identifier(new_value)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdateCredentials)
{
    if (new_value != NULL && !php_amqp_is_valid_credential(new_value)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdatePort)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_port(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdateTimeout)
{
    if (!php_amqp_is_valid_timeout(zend_strtod(ZSTR_VAL(new_value), NULL))) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdateChannelMax)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_channel_max(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdateFrameSizeMax)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_frame_size_max(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdateHeartbeat)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_heartbeat(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdatePrefetchCount)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_prefetch_count(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

static ZEND_INI_MH(onUpdatePrefetchSize)
{
    zend_long val = zend_ini_parse_quantity_warn(new_value, entry->name);
    if (!php_amqp_is_valid_prefetch_size(val)) {
        return FAILURE;
    }

    return SUCCESS;
}

PHP_INI_BEGIN()
    PHP_INI_ENTRY("amqp.host", DEFAULT_HOST, PHP_INI_ALL, onUpdateIdentifier)
    PHP_INI_ENTRY("amqp.vhost", DEFAULT_VHOST, PHP_INI_ALL, onUpdateIdentifier)
    PHP_INI_ENTRY("amqp.port", DEFAULT_PORT, PHP_INI_ALL, onUpdatePort)
    PHP_INI_ENTRY("amqp.timeout", DEFAULT_TIMEOUT, PHP_INI_ALL, onUpdateTimeout)
    PHP_INI_ENTRY("amqp.read_timeout", DEFAULT_READ_TIMEOUT, PHP_INI_ALL, onUpdateTimeout)
    PHP_INI_ENTRY("amqp.write_timeout", DEFAULT_WRITE_TIMEOUT, PHP_INI_ALL, onUpdateTimeout)
    PHP_INI_ENTRY("amqp.connect_timeout", DEFAULT_CONNECT_TIMEOUT, PHP_INI_ALL, onUpdateTimeout)
    PHP_INI_ENTRY("amqp.rpc_timeout", DEFAULT_RPC_TIMEOUT, PHP_INI_ALL, onUpdateTimeout)
    PHP_INI_ENTRY("amqp.login", DEFAULT_LOGIN, PHP_INI_ALL, onUpdateCredentials)
    PHP_INI_ENTRY("amqp.password", DEFAULT_PASSWORD, PHP_INI_ALL, onUpdateCredentials)
    PHP_INI_ENTRY("amqp.auto_ack", DEFAULT_AUTOACK, PHP_INI_ALL, NULL)
    PHP_INI_ENTRY("amqp.prefetch_count", DEFAULT_PREFETCH_COUNT, PHP_INI_ALL, onUpdatePrefetchCount)
    PHP_INI_ENTRY("amqp.prefetch_size", DEFAULT_PREFETCH_SIZE, PHP_INI_ALL, onUpdatePrefetchSize)
    PHP_INI_ENTRY("amqp.global_prefetch_count", DEFAULT_GLOBAL_PREFETCH_COUNT, PHP_INI_ALL, onUpdatePrefetchCount)
    PHP_INI_ENTRY("amqp.global_prefetch_size", DEFAULT_GLOBAL_PREFETCH_SIZE, PHP_INI_ALL, onUpdatePrefetchSize)
    PHP_INI_ENTRY("amqp.channel_max", DEFAULT_CHANNEL_MAX, PHP_INI_ALL, onUpdateChannelMax)
    PHP_INI_ENTRY("amqp.frame_max", DEFAULT_FRAME_MAX, PHP_INI_ALL, onUpdateFrameSizeMax)
    PHP_INI_ENTRY("amqp.heartbeat", DEFAULT_HEARTBEAT, PHP_INI_ALL, onUpdateHeartbeat)
    PHP_INI_ENTRY("amqp.cacert", DEFAULT_CACERT, PHP_INI_ALL, NULL)
    PHP_INI_ENTRY("amqp.cert", DEFAULT_CERT, PHP_INI_ALL, NULL)
    PHP_INI_ENTRY("amqp.key", DEFAULT_KEY, PHP_INI_ALL, NULL)
    PHP_INI_ENTRY("amqp.verify", DEFAULT_VERIFY, PHP_INI_ALL, NULL)
    PHP_INI_ENTRY("amqp.sasl_method", PHP_AMQP_STRINGIFY(DEFAULT_SASL_METHOD), PHP_INI_ALL, NULL)
    STD_PHP_INI_ENTRY(
        "amqp.serialization_depth",
        DEFAULT_SERIALIZATION_DEPTH,
        PHP_INI_ALL,
        OnUpdateLongGEZero,
        serialization_depth,
        zend_amqp_globals,
        amqp_globals
    )
    STD_PHP_INI_ENTRY(
        "amqp.deserialization_depth",
        DEFAULT_SERIALIZATION_DEPTH,
        PHP_INI_ALL,
        OnUpdateLongGEZero,
        deserialization_depth,
        zend_amqp_globals,
        amqp_globals
    )
PHP_INI_END()

ZEND_DECLARE_MODULE_GLOBALS(amqp)

static PHP_GINIT_FUNCTION(amqp) /* {{{ */
{
#if defined(COMPILE_DL_AMQP) && defined(ZTS)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif

    memset(amqp_globals, 0, sizeof(*amqp_globals));

    amqp_globals->error_message = NULL;
    amqp_globals->error_code = 0;
} /* }}} */

static PHP_MINIT_FUNCTION(amqp) /* {{{ */
{
#if defined(COMPILE_DL_AMQP) && defined(ZTS)
    ZEND_TSRMLS_CACHE_UPDATE();
#endif

    zend_class_entry ce;

    /* Set up the connection resource */
    le_amqp_connection_resource = zend_register_list_destructors_ex(
        amqp_connection_resource_dtor,
        NULL,
        PHP_AMQP_CONNECTION_RES_NAME,
        module_number
    );
    le_amqp_connection_resource_persistent = zend_register_list_destructors_ex(
        NULL,
        amqp_connection_resource_dtor_persistent,
        PHP_AMQP_CONNECTION_RES_NAME,
        module_number
    );

    /* Exceptions */
    INIT_CLASS_ENTRY(ce, "AMQPException", NULL);
    amqp_exception_class_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default());

    INIT_CLASS_ENTRY(ce, "AMQPConnectionException", NULL);
    amqp_connection_exception_class_entry = zend_register_internal_class_ex(&ce, amqp_exception_class_entry);

    INIT_CLASS_ENTRY(ce, "AMQPChannelException", NULL);
    amqp_channel_exception_class_entry = zend_register_internal_class_ex(&ce, amqp_exception_class_entry);

    INIT_CLASS_ENTRY(ce, "AMQPQueueException", NULL);
    amqp_queue_exception_class_entry = zend_register_internal_class_ex(&ce, amqp_exception_class_entry);

    INIT_CLASS_ENTRY(ce, "AMQPExchangeException", NULL);
    amqp_exchange_exception_class_entry = zend_register_internal_class_ex(&ce, amqp_exception_class_entry);

    INIT_CLASS_ENTRY(ce, "AMQPValueException", NULL);
    amqp_value_exception_class_entry = zend_register_internal_class_ex(&ce, amqp_exception_class_entry);

    PHP_MINIT(amqp_connection)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_channel)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_queue)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_exchange)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_basic_properties)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_envelope)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_envelope_exception)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_value)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_timestamp)(INIT_FUNC_ARGS_PASSTHRU);
    PHP_MINIT(amqp_decimal)(INIT_FUNC_ARGS_PASSTHRU);

    REGISTER_INI_ENTRIES();

    REGISTER_STRING_CONSTANT("AMQP_EXTENSION_VERSION", PHP_AMQP_VERSION, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_EXTENSION_VERSION_MAJOR", PHP_AMQP_VERSION_MAJOR, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_EXTENSION_VERSION_MINOR", PHP_AMQP_VERSION_MINOR, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_EXTENSION_VERSION_PATCH", PHP_AMQP_VERSION_PATCH, CONST_CS | CONST_PERSISTENT);
    REGISTER_STRING_CONSTANT("AMQP_EXTENSION_VERSION_EXTRA", PHP_AMQP_VERSION_EXTRA, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_EXTENSION_VERSION_ID", PHP_AMQP_VERSION_ID, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("AMQP_NOPARAM", AMQP_NOPARAM, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_JUST_CONSUME", AMQP_JUST_CONSUME, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_DURABLE", AMQP_DURABLE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_PASSIVE", AMQP_PASSIVE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_EXCLUSIVE", AMQP_EXCLUSIVE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_AUTODELETE", AMQP_AUTODELETE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_INTERNAL", AMQP_INTERNAL, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_NOLOCAL", AMQP_NOLOCAL, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_AUTOACK", AMQP_AUTOACK, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_IFEMPTY", AMQP_IFEMPTY, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_IFUNUSED", AMQP_IFUNUSED, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_MANDATORY", AMQP_MANDATORY, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_IMMEDIATE", AMQP_IMMEDIATE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_MULTIPLE", AMQP_MULTIPLE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_NOWAIT", AMQP_NOWAIT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_REQUEUE", AMQP_REQUEUE, CONST_CS | CONST_PERSISTENT);

    REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_DIRECT", AMQP_EX_TYPE_DIRECT, CONST_CS | CONST_PERSISTENT);
    REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_FANOUT", AMQP_EX_TYPE_FANOUT, CONST_CS | CONST_PERSISTENT);
    REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_TOPIC", AMQP_EX_TYPE_TOPIC, CONST_CS | CONST_PERSISTENT);
    REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_HEADERS", AMQP_EX_TYPE_HEADERS, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("AMQP_OS_SOCKET_TIMEOUT_ERRNO", AMQP_OS_SOCKET_TIMEOUT_ERRNO, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("PHP_AMQP_MAX_CHANNELS", PHP_AMQP_MAX_CHANNELS, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("AMQP_SASL_METHOD_PLAIN", AMQP_SASL_METHOD_PLAIN, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_SASL_METHOD_EXTERNAL", AMQP_SASL_METHOD_EXTERNAL, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("AMQP_DELIVERY_MODE_TRANSIENT", AMQP_DELIVERY_NONPERSISTENT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("AMQP_DELIVERY_MODE_PERSISTENT", AMQP_DELIVERY_PERSISTENT, CONST_CS | CONST_PERSISTENT);

    return SUCCESS;
} /* }}} */

static PHP_MSHUTDOWN_FUNCTION(amqp) /* {{{ */
{
    UNREGISTER_INI_ENTRIES();

    return SUCCESS;
} /* }}} */

static PHP_RSHUTDOWN_FUNCTION(amqp) /* {{{ */
{
    if (NULL != PHP_AMQP_G(error_message)) {
        efree(PHP_AMQP_G(error_message));
        PHP_AMQP_G(error_message) = NULL;
    }

    PHP_AMQP_G(error_code) = 0;

    return SUCCESS;
} /* }}} */

static PHP_MINFO_FUNCTION(amqp) /* {{{ */
{
    php_info_print_table_start();
    php_info_print_table_header(2, "Version", PHP_AMQP_VERSION);
    php_info_print_table_header(2, "Compiled", __DATE__ " @ " __TIME__);
    php_info_print_table_header(2, "AMQP protocol version", "0-9-1");
    php_info_print_table_header(2, "librabbitmq version", amqp_version());
    php_info_print_table_header(2, "Default max channels per connection", DEFAULT_CHANNEL_MAX);
    php_info_print_table_header(2, "Default max frame size", DEFAULT_FRAME_MAX);
    php_info_print_table_header(2, "Default heartbeats interval", DEFAULT_HEARTBEAT);
    DISPLAY_INI_ENTRIES();
} /* }}} */

/* {{{ amqp_module_entry
*/
zend_module_entry amqp_module_entry = {
    STANDARD_MODULE_HEADER,
    "amqp",
    amqp_functions,
    PHP_MINIT(amqp),
    PHP_MSHUTDOWN(amqp),
    NULL,
    PHP_RSHUTDOWN(amqp),
    PHP_MINFO(amqp),
    PHP_AMQP_VERSION,
    PHP_MODULE_GLOBALS(amqp),
    PHP_GINIT(amqp),
    NULL,
    NULL,
    STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */

#ifdef COMPILE_DL_AMQP
ZEND_GET_MODULE(amqp)
#endif

int php_amqp_error(
    amqp_rpc_reply_t reply,
    char **message,
    amqp_connection_resource *connection_resource,
    amqp_channel_resource *channel_resource
)
{
    return php_amqp_error_advanced(reply, message, connection_resource, channel_resource, 1);
}

int php_amqp_error_advanced(
    amqp_rpc_reply_t reply,
    char **message,
    amqp_connection_resource *connection_resource,
    amqp_channel_resource *channel_resource,
    int fail_on_errors
)
{
    assert(connection_resource != NULL);

    PHP_AMQP_G(error_code) = 0;
    if (*message != NULL) {
        efree(*message);
    }

    int res = php_amqp_connection_resource_error(
        reply,
        message,
        connection_resource,
        (amqp_channel_t) (channel_resource ? channel_resource->channel_id : 0)
    );

    switch (res) {
        case PHP_AMQP_RESOURCE_RESPONSE_OK:
            break;
        case PHP_AMQP_RESOURCE_RESPONSE_ERROR:
            if (!fail_on_errors) {
                break;
            }
            /* Library or other non-protocol or even protocol related errors may be here. */
            /* In most cases it indicates some underlying hard errors. Fail fast. */
        case PHP_AMQP_RESOURCE_RESPONSE_ERROR_CONNECTION_CLOSED:
            /* Mark connection resource as closed to prevent sending any further requests */
            connection_resource->is_connected = '\0';

            /* Close connection with all its channels */
            php_amqp_disconnect_force(connection_resource);

            break;
        case PHP_AMQP_RESOURCE_RESPONSE_ERROR_CHANNEL_CLOSED:
            /* Mark channel as closed to prevent sending channel.close request */
            assert(channel_resource != NULL);
            if (channel_resource) {
                channel_resource->is_connected = '\0';

                /* Close channel */
                php_amqp_close_channel(channel_resource, 1);
            }
            /* No more error handling necessary, returning. */
            break;
        default:
            spprintf(message, 0, "Unknown server error, method id 0x%08X (not handled by extension)", reply.reply.id);
            break;
    }

    return res;
}

void php_amqp_zend_throw_exception_short(amqp_rpc_reply_t reply, zend_class_entry *exception_ce)
{
    php_amqp_zend_throw_exception(reply, exception_ce, PHP_AMQP_G(error_message), PHP_AMQP_G(error_code));
}

void php_amqp_zend_throw_exception(
    amqp_rpc_reply_t reply,
    zend_class_entry *exception_ce,
    const char *message,
    zend_long code
)
{
    switch (reply.reply_type) {
        case AMQP_RESPONSE_NORMAL:
            break;
        case AMQP_RESPONSE_NONE:
            exception_ce = amqp_exception_class_entry;
            break;
        case AMQP_RESPONSE_LIBRARY_EXCEPTION:
            switch (reply.library_error) {
                case AMQP_STATUS_CONNECTION_CLOSED:
                case AMQP_STATUS_SOCKET_ERROR:
                case AMQP_STATUS_SOCKET_CLOSED:
                case AMQP_STATUS_SOCKET_INUSE:
                case AMQP_STATUS_BROKER_UNSUPPORTED_SASL_METHOD:
                case AMQP_STATUS_HOSTNAME_RESOLUTION_FAILED:
                    exception_ce = amqp_connection_exception_class_entry;
                    break;
                default:
                    exception_ce = amqp_exception_class_entry;
            }
            break;
        case AMQP_RESPONSE_SERVER_EXCEPTION:
            switch (reply.reply.id) {
                case AMQP_CONNECTION_CLOSE_METHOD:
                    /* Fatal errors - pass them to connection level */
                    exception_ce = amqp_connection_exception_class_entry;
                    break;
                case AMQP_CHANNEL_CLOSE_METHOD:
                    /* Most channel-level errors occurs due to previously known action and thus their kind can be predicted. */
                    /* exception_ce = amqp_channel_exception_class_entry; */
                    break;
            }
            break;
        /* Default for the above switch should be handled by the below default. */
        default:
            exception_ce = amqp_exception_class_entry;
            break;
    }

    zend_throw_exception(exception_ce, message, code);
}


void php_amqp_maybe_release_buffers_on_channel(
    amqp_connection_resource *connection_resource,
    amqp_channel_resource *channel_resource
)
{
    assert(channel_resource != NULL);
    assert(channel_resource->channel_id > 0);

    if (connection_resource) {
        amqp_maybe_release_buffers_on_channel(connection_resource->connection_state, channel_resource->channel_id);
    }
}
