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 #include <stdint.h>
24 #include <amqp.h>
25 #include "Zend/zend_interfaces.h"
26 #include "amqp_type.h"
27 #include "amqp_timestamp.h"
28 #include "amqp_decimal.h"
29 
30 #ifdef PHP_WIN32
31 # define strtoimax _strtoi64
32 #endif
33 
34 static void php_amqp_type_internal_free_amqp_array(amqp_array_t *array);
35 static void php_amqp_type_internal_free_amqp_table(amqp_table_t *object, zend_bool clear_root);
36 
php_amqp_type_char_to_amqp_long(char const * cstr,PHP5to7_param_str_len_type_t len)37 amqp_bytes_t php_amqp_type_char_to_amqp_long(char const *cstr, PHP5to7_param_str_len_type_t len)
38 {
39 	amqp_bytes_t result;
40 
41 	if (len < 1) {
42 		return amqp_empty_bytes;
43 	}
44 
45 	result.len   = (size_t)len;
46 	result.bytes = (void *) cstr;
47 
48 	return result;
49 }
50 
php_amqp_type_amqp_bytes_to_char(amqp_bytes_t bytes)51 char *php_amqp_type_amqp_bytes_to_char(amqp_bytes_t bytes)
52 {
53 /* We will need up to 4 chars per byte, plus the terminating 0 */
54 	char *res = emalloc(bytes.len * 4 + 1);
55 	uint8_t *data = bytes.bytes;
56 	char *p = res;
57 	size_t i;
58 
59 	for (i = 0; i < bytes.len; i++) {
60 		if (data[i] >= 32 && data[i] != 127) {
61 			*p++ = data[i];
62 		} else {
63 			*p++ = '\\';
64 			*p++ = '0' + (data[i] >> 6);
65 			*p++ = '0' + (data[i] >> 3 & 0x7);
66 			*p++ = '0' + (data[i] & 0x7);
67 		}
68 	}
69 
70 	*p = 0;
71 	return res;
72 }
73 
php_amqp_type_internal_convert_zval_array(zval * php_array,amqp_field_value_t ** field,zend_bool allow_int_keys TSRMLS_DC)74 void php_amqp_type_internal_convert_zval_array(zval *php_array, amqp_field_value_t **field, zend_bool allow_int_keys TSRMLS_DC)
75 {
76 	HashTable *ht;
77 	HashPosition pos;
78 
79 	zval *value;
80 	zval **data;
81 
82 	PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
83 
84 	char *key;
85 	unsigned key_len;
86 
87 	zend_ulong index;
88 	ht = Z_ARRVAL_P(php_array);
89 
90 	zend_bool is_amqp_array = 1;
91 
92 	PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
93 		if (PHP5to7_ZEND_HASH_KEY_IS_STRING(ht, real_key, key, key_len, index, pos)) {
94 			is_amqp_array = 0;
95 			break;
96 		}
97 
98 	} PHP5to7_ZEND_HASH_FOREACH_END();
99 
100 	if (is_amqp_array) {
101 		(*field)->kind = AMQP_FIELD_KIND_ARRAY;
102 		php_amqp_type_internal_convert_zval_to_amqp_array(php_array, &(*field)->value.array TSRMLS_CC);
103 	} else {
104 		(*field)->kind = AMQP_FIELD_KIND_TABLE;
105 		php_amqp_type_internal_convert_zval_to_amqp_table(php_array, &(*field)->value.table, allow_int_keys TSRMLS_CC);
106 	}
107 }
108 
php_amqp_type_internal_convert_zval_to_amqp_table(zval * php_array,amqp_table_t * amqp_table,zend_bool allow_int_keys TSRMLS_DC)109 void php_amqp_type_internal_convert_zval_to_amqp_table(zval *php_array, amqp_table_t *amqp_table, zend_bool allow_int_keys TSRMLS_DC)
110 {
111 	HashTable *ht;
112 	HashPosition pos;
113 
114 	zval *value;
115 	zval **data;
116 
117 	PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
118 
119 	char *key;
120 	unsigned key_len;
121 
122 	zend_ulong index;
123 
124 
125 	ht = Z_ARRVAL_P(php_array);
126 
127 	amqp_table->entries = (amqp_table_entry_t *)ecalloc((size_t)zend_hash_num_elements(ht), sizeof(amqp_table_entry_t));
128 	amqp_table->num_entries = 0;
129 
130 	PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
131 		char *string_key;
132 		amqp_table_entry_t *table_entry;
133 		amqp_field_value_t *field;
134 
135 
136 		/* Now pull the key */
137 
138 		if (!PHP5to7_ZEND_HASH_KEY_IS_STRING(ht, real_key, key, key_len, index, pos)) {
139 			if (allow_int_keys) {
140 				/* Convert to strings non-string keys */
141 				char str[32];
142 
143 				key_len = sprintf(str, "%lu", index);
144 				key     = str;
145 			} else {
146 				/* Skip things that are not strings */
147 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ignoring non-string header field '%lu'", index);
148 
149 				continue;
150 			}
151 		} else {
152 			PHP5to7_ZEND_HASH_KEY_MAYBE_UNPACK(real_key, key, key_len);
153 		}
154 
155 		/* Build the value */
156 		table_entry = &amqp_table->entries[amqp_table->num_entries++];
157 		field = &table_entry->value;
158 
159 		if (!php_amqp_type_internal_convert_php_to_amqp_field_value(value, &field, key TSRMLS_CC)) {
160 			/* Reset entries counter back */
161 			amqp_table->num_entries --;
162 
163 			continue;
164 		}
165 
166 		string_key = estrndup(key, key_len);
167 		table_entry->key = amqp_cstring_bytes(string_key);
168 
169 	} PHP5to7_ZEND_HASH_FOREACH_END();
170 }
171 
php_amqp_type_internal_convert_zval_to_amqp_array(zval * zvalArguments,amqp_array_t * arguments TSRMLS_DC)172 void php_amqp_type_internal_convert_zval_to_amqp_array(zval *zvalArguments, amqp_array_t *arguments TSRMLS_DC)
173 {
174 	HashTable *ht;
175 	HashPosition pos;
176 
177 	zval *value;
178 	zval **data;
179 
180 	PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
181 
182 	char *key;
183 	unsigned key_len;
184 
185 	zend_ulong index;
186 
187 	char type[16];
188 
189 	ht = Z_ARRVAL_P(zvalArguments);
190 
191 	/* Allocate all the memory necessary for storing the arguments */
192 	arguments->entries = (amqp_field_value_t *)ecalloc((size_t)zend_hash_num_elements(ht), sizeof(amqp_field_value_t));
193 	arguments->num_entries = 0;
194 
195 	PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
196 		amqp_field_value_t *field = &arguments->entries[arguments->num_entries++];
197 
198 		if (!php_amqp_type_internal_convert_php_to_amqp_field_value(value, &field, key TSRMLS_CC)) {
199 			/* Reset entries counter back */
200 			arguments->num_entries --;
201 
202 			continue;
203 		}
204 	} PHP5to7_ZEND_HASH_FOREACH_END();
205 }
206 
php_amqp_type_internal_convert_php_to_amqp_field_value(zval * value,amqp_field_value_t ** fieldPtr,char * key TSRMLS_DC)207 zend_bool php_amqp_type_internal_convert_php_to_amqp_field_value(zval *value, amqp_field_value_t **fieldPtr, char *key TSRMLS_DC)
208 {
209 	zend_bool result;
210 	char type[16];
211 	amqp_field_value_t *field;
212 
213 	result = 1;
214 	field = *fieldPtr;
215 
216 	switch (Z_TYPE_P(value)) {
217 		PHP5to7_CASE_IS_BOOL:
218 			field->kind = AMQP_FIELD_KIND_BOOLEAN;
219 			field->value.boolean = (amqp_boolean_t) !PHP5to7_IS_FALSE_P(value);
220 			break;
221 		case IS_DOUBLE:
222 			field->kind = AMQP_FIELD_KIND_F64;
223 			field->value.f64 = Z_DVAL_P(value);
224 			break;
225 		case IS_LONG:
226 			field->kind = AMQP_FIELD_KIND_I64;
227 			field->value.i64 = Z_LVAL_P(value);
228 			break;
229 		case IS_STRING:
230 			field->kind = AMQP_FIELD_KIND_UTF8;
231 
232 			if (Z_STRLEN_P(value)) {
233 				amqp_bytes_t bytes;
234 				bytes.len = (size_t) Z_STRLEN_P(value);
235 				bytes.bytes = estrndup(Z_STRVAL_P(value), (unsigned) Z_STRLEN_P(value));
236 
237 				field->value.bytes = bytes;
238 			} else {
239 				field->value.bytes = amqp_empty_bytes;
240 			}
241 
242 			break;
243 		case IS_ARRAY:
244 			php_amqp_type_internal_convert_zval_array(value, &field, 1 TSRMLS_CC);
245 			break;
246 		case IS_NULL:
247 			field->kind = AMQP_FIELD_KIND_VOID;
248 			break;
249 		case IS_OBJECT:
250 			if (instanceof_function(Z_OBJCE_P(value), amqp_timestamp_class_entry TSRMLS_CC)) {
251                 PHP5to7_zval_t result_zv PHP5to7_MAYBE_SET_TO_NULL;
252 
253                 zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_timestamp_class_entry, NULL, "gettimestamp", &result_zv);
254 
255                 field->kind = AMQP_FIELD_KIND_TIMESTAMP;
256                 field->value.u64 = strtoimax(Z_STRVAL(PHP5to7_MAYBE_DEREF(result_zv)), NULL, 10);
257 
258                 PHP5to7_MAYBE_DESTROY(result_zv);
259 
260 				break;
261 			} else if (instanceof_function(Z_OBJCE_P(value), amqp_decimal_class_entry TSRMLS_CC)) {
262 				field->kind = AMQP_FIELD_KIND_DECIMAL;
263 				PHP5to7_zval_t result_zv PHP5to7_MAYBE_SET_TO_NULL;
264 
265 				zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_decimal_class_entry, NULL, "getexponent", &result_zv);
266 				field->value.decimal.decimals = (uint8_t)Z_LVAL(PHP5to7_MAYBE_DEREF(result_zv));
267 				PHP5to7_MAYBE_DESTROY(result_zv);
268 
269 				zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_decimal_class_entry, NULL, "getsignificand", &result_zv);
270 				field->value.decimal.value = (uint32_t)Z_LVAL(PHP5to7_MAYBE_DEREF(result_zv));
271 
272 				PHP5to7_MAYBE_DESTROY(result_zv);
273 
274 				break;
275 			}
276 		default:
277 			switch(Z_TYPE_P(value)) {
278 				case IS_OBJECT:
279 					strcpy(type, "object");
280 					break;
281 				case IS_RESOURCE:
282 					strcpy(type, "resource");
283 					break;
284 				default:
285 					strcpy(type, "unknown");
286 					break;
287 			}
288 
289 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ignoring field '%s' due to unsupported value type (%s)", key, type);
290 			result = 0;
291 			break;
292 	}
293 
294 	return result;
295 }
296 
php_amqp_type_convert_zval_to_amqp_table(zval * php_array TSRMLS_DC)297 amqp_table_t *php_amqp_type_convert_zval_to_amqp_table(zval *php_array TSRMLS_DC)
298 {
299 	amqp_table_t *amqp_table;
300 	/* In setArguments, we are overwriting all the existing values */
301 	amqp_table = (amqp_table_t *)emalloc(sizeof(amqp_table_t));
302 
303 	php_amqp_type_internal_convert_zval_to_amqp_table(php_array, amqp_table, 0 TSRMLS_CC);
304 
305 	return amqp_table;
306 }
307 
308 
php_amqp_type_internal_free_amqp_array(amqp_array_t * array)309 static void php_amqp_type_internal_free_amqp_array(amqp_array_t *array) {
310 	if (!array) {
311 		return;
312 	}
313 
314 	int macroEntryCounter;
315 	for (macroEntryCounter = 0; macroEntryCounter < array->num_entries; macroEntryCounter++) {
316 
317 		amqp_field_value_t *entry = &array->entries[macroEntryCounter];
318 
319 		switch (entry->kind) {
320 			case AMQP_FIELD_KIND_TABLE:
321 				php_amqp_type_internal_free_amqp_table(&entry->value.table, 0);
322 				break;
323 			case AMQP_FIELD_KIND_ARRAY:
324 				php_amqp_type_internal_free_amqp_array(&entry->value.array);
325 				break;
326 			case AMQP_FIELD_KIND_UTF8:
327 				if (entry->value.bytes.bytes) {
328 					efree(entry->value.bytes.bytes);
329 				}
330 				break;
331 				//
332 			default:
333 				break;
334 		}
335 	}
336 
337 	if (array->entries) {
338 		efree(array->entries);
339 	}
340 }
341 
php_amqp_type_internal_free_amqp_table(amqp_table_t * object,zend_bool clear_root)342 static void php_amqp_type_internal_free_amqp_table(amqp_table_t *object, zend_bool clear_root)
343 {
344 	if (!object) {
345 		return;
346 	}
347 
348 	if (object->entries) {
349 		int macroEntryCounter;
350 		for (macroEntryCounter = 0; macroEntryCounter < object->num_entries; macroEntryCounter++) {
351 
352 			amqp_table_entry_t *entry = &object->entries[macroEntryCounter];
353 			efree(entry->key.bytes);
354 
355 			switch (entry->value.kind) {
356 				case AMQP_FIELD_KIND_TABLE:
357 					php_amqp_type_internal_free_amqp_table(&entry->value.value.table, 0);
358 					break;
359 				case AMQP_FIELD_KIND_ARRAY:
360 					php_amqp_type_internal_free_amqp_array(&entry->value.value.array);
361 					break;
362 				case AMQP_FIELD_KIND_UTF8:
363 					if (entry->value.value.bytes.bytes) {
364 						efree(entry->value.value.bytes.bytes);
365 					}
366 					break;
367 				default:
368 					break;
369 			}
370 		}
371 		efree(object->entries);
372 	}
373 
374 	if (clear_root) {
375 		efree(object);
376 	}
377 }
378 
php_amqp_type_free_amqp_table(amqp_table_t * object)379 void php_amqp_type_free_amqp_table(amqp_table_t *object)
380 {
381 	php_amqp_type_internal_free_amqp_table(object, 1);
382 }
383