1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2007 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Author: Alexandre Kalendarev akalend@mail.ru Copyright (c) 2009-2010 |
16   | Lead:                                                                |
17   | - Pieter de Zwart                                                    |
18   | Maintainers:                                                         |
19   | - Brad Rodriguez                                                     |
20   | - Jonathan Tansavatdi                                                |
21   +----------------------------------------------------------------------+
22 */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 #include "php_ini.h"
29 #include "ext/standard/info.h"
30 #include "zend_ini.h"
31 #include "zend_exceptions.h"
32 
33 #ifdef PHP_WIN32
34 # include "win32/php_stdint.h"
35 # include "win32/signal.h"
36 #else
37 # include <stdint.h>
38 # include <signal.h>
39 #endif
40 #include <amqp.h>
41 #include <amqp_framing.h>
42 
43 #include "php_amqp.h"
44 #include "amqp_connection.h"
45 #include "amqp_basic_properties.h"
46 #include "amqp_connection_resource.h"
47 #include "amqp_channel.h"
48 #include "amqp_envelope.h"
49 #include "amqp_exchange.h"
50 #include "amqp_queue.h"
51 #include "amqp_timestamp.h"
52 #include "amqp_decimal.h"
53 
54 #ifdef PHP_WIN32
55 # include "win32/unistd.h"
56 #else
57 # include <unistd.h>
58 #endif
59 
60 /* True global resources - no need for thread safety here */
61 
62 zend_class_entry *amqp_exception_class_entry,
63 				 *amqp_connection_exception_class_entry,
64 				 *amqp_channel_exception_class_entry,
65 				 *amqp_queue_exception_class_entry,
66 				 *amqp_exchange_exception_class_entry,
67 				 *amqp_envelope_exception_class_entry,
68 				 *amqp_value_exception_class_entry;
69 
70 /* {{{ amqp_functions[]
71 *
72 *Every user visible function must have an entry in amqp_functions[].
73 */
74 zend_function_entry amqp_functions[] = {
75 	{NULL, NULL, NULL}
76 };
77 /* }}} */
78 
79 PHP_INI_BEGIN()
80 	PHP_INI_ENTRY("amqp.host",					DEFAULT_HOST,					PHP_INI_ALL, NULL)
81 	PHP_INI_ENTRY("amqp.vhost",					DEFAULT_VHOST,					PHP_INI_ALL, NULL)
82 	PHP_INI_ENTRY("amqp.port",					DEFAULT_PORT,					PHP_INI_ALL, NULL)
83 	PHP_INI_ENTRY("amqp.timeout",				DEFAULT_TIMEOUT,				PHP_INI_ALL, NULL)
84 	PHP_INI_ENTRY("amqp.read_timeout",			DEFAULT_READ_TIMEOUT,			PHP_INI_ALL, NULL)
85 	PHP_INI_ENTRY("amqp.write_timeout",			DEFAULT_WRITE_TIMEOUT,			PHP_INI_ALL, NULL)
86 	PHP_INI_ENTRY("amqp.connect_timeout",		DEFAULT_CONNECT_TIMEOUT,		PHP_INI_ALL, NULL)
87 	PHP_INI_ENTRY("amqp.rpc_timeout",			DEFAULT_RPC_TIMEOUT,			PHP_INI_ALL, NULL)
88 	PHP_INI_ENTRY("amqp.login",					DEFAULT_LOGIN,					PHP_INI_ALL, NULL)
89 	PHP_INI_ENTRY("amqp.password",				DEFAULT_PASSWORD,				PHP_INI_ALL, NULL)
90 	PHP_INI_ENTRY("amqp.auto_ack",				DEFAULT_AUTOACK,				PHP_INI_ALL, NULL)
91 	PHP_INI_ENTRY("amqp.prefetch_count",		DEFAULT_PREFETCH_COUNT,			PHP_INI_ALL, NULL)
92 	PHP_INI_ENTRY("amqp.prefetch_size",			DEFAULT_PREFETCH_SIZE,			PHP_INI_ALL, NULL)
93 	PHP_INI_ENTRY("amqp.global_prefetch_count",	DEFAULT_GLOBAL_PREFETCH_COUNT,	PHP_INI_ALL, NULL)
94 	PHP_INI_ENTRY("amqp.global_prefetch_size",	DEFAULT_GLOBAL_PREFETCH_SIZE,	PHP_INI_ALL, NULL)
95 	PHP_INI_ENTRY("amqp.channel_max",			DEFAULT_CHANNEL_MAX,			PHP_INI_ALL, NULL)
96 	PHP_INI_ENTRY("amqp.frame_max",				DEFAULT_FRAME_MAX,				PHP_INI_ALL, NULL)
97 	PHP_INI_ENTRY("amqp.heartbeat",				DEFAULT_HEARTBEAT,				PHP_INI_ALL, NULL)
98 	PHP_INI_ENTRY("amqp.cacert",				DEFAULT_CACERT,					PHP_INI_ALL, NULL)
99 	PHP_INI_ENTRY("amqp.cert",					DEFAULT_CERT,					PHP_INI_ALL, NULL)
100 	PHP_INI_ENTRY("amqp.key",					DEFAULT_KEY,					PHP_INI_ALL, NULL)
101 	PHP_INI_ENTRY("amqp.verify",				DEFAULT_VERIFY,					PHP_INI_ALL, NULL)
102 	PHP_INI_ENTRY("amqp.sasl_method",			DEFAULT_SASL_METHOD,			PHP_INI_ALL, NULL)
103 PHP_INI_END()
104 
105 ZEND_DECLARE_MODULE_GLOBALS(amqp);
106 
PHP_GINIT_FUNCTION(amqp)107 static PHP_GINIT_FUNCTION(amqp) /* {{{ */
108 {
109 	amqp_globals->error_message = NULL;
110 	amqp_globals->error_code = 0;
111 } /* }}} */
112 
PHP_MINIT_FUNCTION(amqp)113 static PHP_MINIT_FUNCTION(amqp) /* {{{ */
114 {
115 	zend_class_entry ce;
116 
117 	/* Set up the connection resource */
118 	le_amqp_connection_resource = zend_register_list_destructors_ex(amqp_connection_resource_dtor, NULL, PHP_AMQP_CONNECTION_RES_NAME, module_number);
119 	le_amqp_connection_resource_persistent = zend_register_list_destructors_ex(NULL, amqp_connection_resource_dtor_persistent, PHP_AMQP_CONNECTION_RES_NAME, module_number);
120 
121 	PHP_MINIT(amqp_connection)(INIT_FUNC_ARGS_PASSTHRU);
122 	PHP_MINIT(amqp_channel)(INIT_FUNC_ARGS_PASSTHRU);
123 	PHP_MINIT(amqp_queue)(INIT_FUNC_ARGS_PASSTHRU);
124 	PHP_MINIT(amqp_exchange)(INIT_FUNC_ARGS_PASSTHRU);
125 	PHP_MINIT(amqp_basic_properties)(INIT_FUNC_ARGS_PASSTHRU);
126 	PHP_MINIT(amqp_envelope)(INIT_FUNC_ARGS_PASSTHRU);
127 	PHP_MINIT(amqp_timestamp)(INIT_FUNC_ARGS_PASSTHRU);
128 	PHP_MINIT(amqp_decimal)(INIT_FUNC_ARGS_PASSTHRU);
129 
130 	/* Class Exceptions */
131 	INIT_CLASS_ENTRY(ce, "AMQPException", NULL);
132 	amqp_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C));
133 
134 	INIT_CLASS_ENTRY(ce, "AMQPConnectionException", NULL);
135 	amqp_connection_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
136 
137 	INIT_CLASS_ENTRY(ce, "AMQPChannelException", NULL);
138 	amqp_channel_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
139 
140 	INIT_CLASS_ENTRY(ce, "AMQPQueueException", NULL);
141 	amqp_queue_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
142 
143 	INIT_CLASS_ENTRY(ce, "AMQPEnvelopeException", NULL);
144 	amqp_envelope_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
145 	zend_declare_property_null(amqp_envelope_exception_class_entry, ZEND_STRL("envelope"), ZEND_ACC_PUBLIC TSRMLS_CC);
146 
147 	INIT_CLASS_ENTRY(ce, "AMQPExchangeException", NULL);
148 	amqp_exchange_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
149 
150 	INIT_CLASS_ENTRY(ce, "AMQPValueException", NULL);
151 	amqp_value_exception_class_entry = PHP5to7_zend_register_internal_class_ex(&ce, amqp_exception_class_entry);
152 
153 	REGISTER_INI_ENTRIES();
154 
155 	REGISTER_LONG_CONSTANT("AMQP_NOPARAM",			AMQP_NOPARAM,		CONST_CS | CONST_PERSISTENT);
156 	REGISTER_LONG_CONSTANT("AMQP_JUST_CONSUME",		AMQP_JUST_CONSUME,	CONST_CS | CONST_PERSISTENT);
157 	REGISTER_LONG_CONSTANT("AMQP_DURABLE",			AMQP_DURABLE,		CONST_CS | CONST_PERSISTENT);
158 	REGISTER_LONG_CONSTANT("AMQP_PASSIVE",			AMQP_PASSIVE,		CONST_CS | CONST_PERSISTENT);
159 	REGISTER_LONG_CONSTANT("AMQP_EXCLUSIVE",		AMQP_EXCLUSIVE,		CONST_CS | CONST_PERSISTENT);
160 	REGISTER_LONG_CONSTANT("AMQP_AUTODELETE",		AMQP_AUTODELETE,	CONST_CS | CONST_PERSISTENT);
161 	REGISTER_LONG_CONSTANT("AMQP_INTERNAL",			AMQP_INTERNAL,		CONST_CS | CONST_PERSISTENT);
162 	REGISTER_LONG_CONSTANT("AMQP_NOLOCAL",			AMQP_NOLOCAL,		CONST_CS | CONST_PERSISTENT);
163 	REGISTER_LONG_CONSTANT("AMQP_AUTOACK",			AMQP_AUTOACK,		CONST_CS | CONST_PERSISTENT);
164 	REGISTER_LONG_CONSTANT("AMQP_IFEMPTY",			AMQP_IFEMPTY,		CONST_CS | CONST_PERSISTENT);
165 	REGISTER_LONG_CONSTANT("AMQP_IFUNUSED",			AMQP_IFUNUSED,		CONST_CS | CONST_PERSISTENT);
166 	REGISTER_LONG_CONSTANT("AMQP_MANDATORY",		AMQP_MANDATORY,		CONST_CS | CONST_PERSISTENT);
167 	REGISTER_LONG_CONSTANT("AMQP_IMMEDIATE",		AMQP_IMMEDIATE,		CONST_CS | CONST_PERSISTENT);
168 	REGISTER_LONG_CONSTANT("AMQP_MULTIPLE",			AMQP_MULTIPLE,		CONST_CS | CONST_PERSISTENT);
169 	REGISTER_LONG_CONSTANT("AMQP_NOWAIT",			AMQP_NOWAIT,		CONST_CS | CONST_PERSISTENT);
170 	REGISTER_LONG_CONSTANT("AMQP_REQUEUE",			AMQP_REQUEUE,		CONST_CS | CONST_PERSISTENT);
171 
172 	REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_DIRECT",	AMQP_EX_TYPE_DIRECT,	CONST_CS | CONST_PERSISTENT);
173 	REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_FANOUT",	AMQP_EX_TYPE_FANOUT,	CONST_CS | CONST_PERSISTENT);
174 	REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_TOPIC",	AMQP_EX_TYPE_TOPIC,		CONST_CS | CONST_PERSISTENT);
175 	REGISTER_STRING_CONSTANT("AMQP_EX_TYPE_HEADERS",AMQP_EX_TYPE_HEADERS,	CONST_CS | CONST_PERSISTENT);
176 
177 	REGISTER_LONG_CONSTANT("AMQP_OS_SOCKET_TIMEOUT_ERRNO",	AMQP_OS_SOCKET_TIMEOUT_ERRNO,	CONST_CS | CONST_PERSISTENT);
178 	REGISTER_LONG_CONSTANT("PHP_AMQP_MAX_CHANNELS",			PHP_AMQP_MAX_CHANNELS,			CONST_CS | CONST_PERSISTENT);
179 
180 	REGISTER_LONG_CONSTANT("AMQP_SASL_METHOD_PLAIN",		AMQP_SASL_METHOD_PLAIN,		CONST_CS | CONST_PERSISTENT);
181 	REGISTER_LONG_CONSTANT("AMQP_SASL_METHOD_EXTERNAL",		AMQP_SASL_METHOD_EXTERNAL,	CONST_CS | CONST_PERSISTENT);
182 	return SUCCESS;
183 } /* }}} */
184 
PHP_MSHUTDOWN_FUNCTION(amqp)185 static PHP_MSHUTDOWN_FUNCTION(amqp) /* {{{ */
186 {
187 	UNREGISTER_INI_ENTRIES();
188 
189 	return SUCCESS;
190 } /* }}} */
191 
PHP_RSHUTDOWN_FUNCTION(amqp)192 static PHP_RSHUTDOWN_FUNCTION(amqp) /* {{{ */
193 {
194 	if (NULL != PHP_AMQP_G(error_message)) {
195 		efree(PHP_AMQP_G(error_message));
196 		PHP_AMQP_G(error_message) = NULL;
197 	}
198 
199 	PHP_AMQP_G(error_code) = 0;
200 
201 	return SUCCESS;
202 } /* }}} */
203 
PHP_MINFO_FUNCTION(amqp)204 static PHP_MINFO_FUNCTION(amqp) /* {{{ */
205 {
206 	php_info_print_table_start();
207 	php_info_print_table_header(2, "Version",					PHP_AMQP_VERSION);
208 	php_info_print_table_header(2, "Revision",					PHP_AMQP_REVISION);
209 	php_info_print_table_header(2, "Compiled",					__DATE__ " @ "  __TIME__);
210 	php_info_print_table_header(2, "AMQP protocol version", 	"0-9-1");
211 	php_info_print_table_header(2, "librabbitmq version", amqp_version());
212 	php_info_print_table_header(2, "Default max channels per connection",	DEFAULT_CHANNEL_MAX);
213 	php_info_print_table_header(2, "Default max frame size",	DEFAULT_FRAME_MAX);
214 	php_info_print_table_header(2, "Default heartbeats interval",	DEFAULT_HEARTBEAT);
215 	DISPLAY_INI_ENTRIES();
216 } /* }}} */
217 
218 /* {{{ amqp_module_entry
219 */
220 zend_module_entry amqp_module_entry = {
221 	STANDARD_MODULE_HEADER,
222 	"amqp",
223 	amqp_functions,
224 	PHP_MINIT(amqp),
225 	PHP_MSHUTDOWN(amqp),
226 	NULL,
227 	PHP_RSHUTDOWN(amqp),
228 	PHP_MINFO(amqp),
229 	PHP_AMQP_VERSION,
230 	PHP_MODULE_GLOBALS(amqp),
231 	PHP_GINIT(amqp),
232 	NULL,
233 	NULL,
234 	STANDARD_MODULE_PROPERTIES_EX
235 };
236 /* }}} */
237 
238 #ifdef COMPILE_DL_AMQP
ZEND_GET_MODULE(amqp)239 	ZEND_GET_MODULE(amqp)
240 #endif
241 
242 int php_amqp_error(amqp_rpc_reply_t reply, char **message, amqp_connection_resource *connection_resource, amqp_channel_resource *channel_resource TSRMLS_DC)
243 {
244 	return php_amqp_error_advanced(reply, message, connection_resource, channel_resource, 1 TSRMLS_CC);
245 }
246 
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 TSRMLS_DC)247 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 TSRMLS_DC)
248 {
249 	assert(connection_resource != NULL);
250 
251 	PHP_AMQP_G(error_code) = 0;
252 	if (*message != NULL) {
253 		efree(*message);
254 	}
255 
256 	int res = php_amqp_connection_resource_error(reply, message, connection_resource, (amqp_channel_t)(channel_resource ? channel_resource->channel_id : 0) TSRMLS_CC);
257 
258 	switch (res) {
259 		case PHP_AMQP_RESOURCE_RESPONSE_OK:
260 			break;
261 		case PHP_AMQP_RESOURCE_RESPONSE_ERROR:
262 			if (!fail_on_errors) {
263 				break;
264 			}
265 			/* Library or other non-protocol or even protocol related errors may be here. */
266 			/* In most cases it designate some underlying hard errors. Fail fast. */
267 		case PHP_AMQP_RESOURCE_RESPONSE_ERROR_CONNECTION_CLOSED:
268 			/* Mark connection resource as closed to prevent sending any further requests */
269 			connection_resource->is_connected = '\0';
270 
271 			/* Close connection with all its channels */
272 			php_amqp_disconnect_force(connection_resource TSRMLS_CC);
273 
274 			break;
275 		case PHP_AMQP_RESOURCE_RESPONSE_ERROR_CHANNEL_CLOSED:
276 			/* Mark channel as closed to prevent sending channel.close request */
277 			assert(channel_resource != NULL);
278 			if (channel_resource) {
279 				channel_resource->is_connected = '\0';
280 
281 				/* Close channel */
282 				php_amqp_close_channel(channel_resource, 1 TSRMLS_CC);
283 			}
284 			/* No more error handling necessary, returning. */
285 			break;
286 		default:
287 			spprintf(message, 0, "Unknown server error, method id 0x%08X (not handled by extension)", reply.reply.id);
288 			break;
289 	}
290 
291 	return res;
292 }
293 
php_amqp_zend_throw_exception_short(amqp_rpc_reply_t reply,zend_class_entry * exception_ce TSRMLS_DC)294 void php_amqp_zend_throw_exception_short(amqp_rpc_reply_t reply, zend_class_entry *exception_ce TSRMLS_DC) {
295 	php_amqp_zend_throw_exception(reply, exception_ce, PHP_AMQP_G(error_message), PHP_AMQP_G(error_code) TSRMLS_CC);
296 }
297 
php_amqp_zend_throw_exception(amqp_rpc_reply_t reply,zend_class_entry * exception_ce,const char * message,PHP5to7_param_long_type_t code TSRMLS_DC)298 void php_amqp_zend_throw_exception(amqp_rpc_reply_t reply, zend_class_entry *exception_ce, const char *message, PHP5to7_param_long_type_t code TSRMLS_DC)
299 {
300 	switch (reply.reply_type) {
301 		case AMQP_RESPONSE_NORMAL:
302 			break;
303 		case AMQP_RESPONSE_NONE:
304 			exception_ce = amqp_exception_class_entry;
305 			break;
306 		case AMQP_RESPONSE_LIBRARY_EXCEPTION:
307 			exception_ce = amqp_exception_class_entry;
308 			break;
309 		case AMQP_RESPONSE_SERVER_EXCEPTION:
310 			switch (reply.reply.id) {
311 				case AMQP_CONNECTION_CLOSE_METHOD:
312 					/* Fatal errors - pass them to connection level */
313 					exception_ce = amqp_connection_exception_class_entry;
314 					break;
315 				case AMQP_CHANNEL_CLOSE_METHOD:
316 					/* Most channel-level errors occurs due to previously known action and thus their kind can be predicted. */
317 					/* exception_ce = amqp_channel_exception_class_entry; */
318 					break;
319 			}
320 			break;
321 		/* Default for the above switch should be handled by the below default. */
322 		default:
323 			exception_ce = amqp_exception_class_entry;
324 			break;
325 	}
326 
327 	zend_throw_exception(exception_ce, message, code TSRMLS_CC);
328 }
329 
330 
php_amqp_maybe_release_buffers_on_channel(amqp_connection_resource * connection_resource,amqp_channel_resource * channel_resource)331 void php_amqp_maybe_release_buffers_on_channel(amqp_connection_resource *connection_resource, amqp_channel_resource *channel_resource)
332 {
333 	assert(channel_resource != NULL);
334 	assert(channel_resource->channel_id > 0);
335 
336 	if (connection_resource) {
337 		amqp_maybe_release_buffers_on_channel(connection_resource->connection_state, channel_resource->channel_id);
338 	}
339 }
340 
341 /*
342 *Local variables:
343 *tab-width: 4
344 *c-basic-offset: 4
345 *End:
346 *vim600: noet sw=4 ts=4 fdm=marker
347 *vim<600: noet sw=4 ts=4
348 */
349