1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2018 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 | Authors: Andrey Hristov <andrey@php.net> |
16 | Ulf Wendel <uw@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "php.h"
21 #include "mysqlnd.h"
22 #include "mysqlnd_wireprotocol.h"
23 #include "mysqlnd_connection.h"
24 #include "mysqlnd_ps.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_debug.h"
27 #include "mysql_float_to_double.h"
28
29
30 enum mysqlnd_timestamp_type
31 {
32 MYSQLND_TIMESTAMP_NONE= -2,
33 MYSQLND_TIMESTAMP_ERROR= -1,
34 MYSQLND_TIMESTAMP_DATE= 0,
35 MYSQLND_TIMESTAMP_DATETIME= 1,
36 MYSQLND_TIMESTAMP_TIME= 2
37 };
38
39
40 struct st_mysqlnd_time
41 {
42 unsigned int year, month, day, hour, minute, second;
43 zend_ulong second_part;
44 zend_bool neg;
45 enum mysqlnd_timestamp_type time_type;
46 };
47
48
49 struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
50
51 #define MYSQLND_PS_SKIP_RESULT_W_LEN -1
52 #define MYSQLND_PS_SKIP_RESULT_STR -2
53
54 /* {{{ ps_fetch_from_1_to_8_bytes */
55 void
ps_fetch_from_1_to_8_bytes(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row,unsigned int byte_count)56 ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
57 const zend_uchar ** row, unsigned int byte_count)
58 {
59 char tmp[22];
60 size_t tmp_len = 0;
61 zend_bool is_bit = field->type == MYSQL_TYPE_BIT;
62 DBG_ENTER("ps_fetch_from_1_to_8_bytes");
63 DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count);
64 if (field->flags & UNSIGNED_FLAG) {
65 uint64_t uval = 0;
66
67 switch (byte_count) {
68 case 8:uval = is_bit? (uint64_t) bit_uint8korr(*row):(uint64_t) uint8korr(*row);break;
69 case 7:uval = bit_uint7korr(*row);break;
70 case 6:uval = bit_uint6korr(*row);break;
71 case 5:uval = bit_uint5korr(*row);break;
72 case 4:uval = is_bit? (uint64_t) bit_uint4korr(*row):(uint64_t) uint4korr(*row);break;
73 case 3:uval = is_bit? (uint64_t) bit_uint3korr(*row):(uint64_t) uint3korr(*row);break;
74 case 2:uval = is_bit? (uint64_t) bit_uint2korr(*row):(uint64_t) uint2korr(*row);break;
75 case 1:uval = (uint64_t) uint1korr(*row);break;
76 }
77
78 #if SIZEOF_ZEND_LONG==4
79 if (uval > INT_MAX) {
80 DBG_INF("stringify");
81 tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
82 } else
83 #endif /* #if SIZEOF_LONG==4 */
84 {
85 if (byte_count < 8 || uval <= L64(9223372036854775807)) {
86 ZVAL_LONG(zv, (zend_long) uval); /* the cast is safe, we are in the range */
87 } else {
88 DBG_INF("stringify");
89 tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
90 DBG_INF_FMT("value=%s", tmp);
91 }
92 }
93 } else {
94 /* SIGNED */
95 int64_t lval = 0;
96 switch (byte_count) {
97 case 8:lval = (int64_t) sint8korr(*row);break;
98 /*
99 7, 6 and 5 are not possible.
100 BIT is only unsigned, thus only uint5|6|7 macroses exist
101 */
102 case 4:lval = (int64_t) sint4korr(*row);break;
103 case 3:lval = (int64_t) sint3korr(*row);break;
104 case 2:lval = (int64_t) sint2korr(*row);break;
105 case 1:lval = (int64_t) *(int8_t*)*row;break;
106 }
107
108 #if SIZEOF_ZEND_LONG==4
109 if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) {
110 DBG_INF("stringify");
111 tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval);
112 } else
113 #endif /* SIZEOF */
114 {
115 ZVAL_LONG(zv, (zend_long) lval); /* the cast is safe, we are in the range */
116 }
117 }
118
119 if (tmp_len) {
120 ZVAL_STRINGL(zv, tmp, tmp_len);
121 }
122 (*row)+= byte_count;
123 DBG_VOID_RETURN;
124 }
125 /* }}} */
126
127
128 /* {{{ ps_fetch_null */
129 static void
ps_fetch_null(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)130 ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
131 {
132 ZVAL_NULL(zv);
133 }
134 /* }}} */
135
136
137 /* {{{ ps_fetch_int8 */
138 static void
ps_fetch_int8(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)139 ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
140 {
141 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 1);
142 }
143 /* }}} */
144
145
146 /* {{{ ps_fetch_int16 */
147 static void
ps_fetch_int16(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)148 ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
149 {
150 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 2);
151 }
152 /* }}} */
153
154
155 /* {{{ ps_fetch_int32 */
156 static void
ps_fetch_int32(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)157 ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
158 {
159 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 4);
160 }
161 /* }}} */
162
163
164 /* {{{ ps_fetch_int64 */
165 static void
ps_fetch_int64(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)166 ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
167 {
168 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 8);
169 }
170 /* }}} */
171
172
173 /* {{{ ps_fetch_float */
174 static void
ps_fetch_float(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)175 ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
176 {
177 float fval;
178 double dval;
179 DBG_ENTER("ps_fetch_float");
180 float4get(fval, *row);
181 (*row)+= 4;
182 DBG_INF_FMT("value=%f", fval);
183
184 #ifndef NOT_FIXED_DEC
185 # define NOT_FIXED_DEC 31
186 #endif
187
188 dval = mysql_float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : (int)field->decimals);
189
190 ZVAL_DOUBLE(zv, dval);
191 DBG_VOID_RETURN;
192 }
193 /* }}} */
194
195
196 /* {{{ ps_fetch_double */
197 static void
ps_fetch_double(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)198 ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
199 {
200 double value;
201 DBG_ENTER("ps_fetch_double");
202 float8get(value, *row);
203 ZVAL_DOUBLE(zv, value);
204 (*row)+= 8;
205 DBG_INF_FMT("value=%f", value);
206 DBG_VOID_RETURN;
207 }
208 /* }}} */
209
210
211 /* {{{ ps_fetch_time */
212 static void
ps_fetch_time(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)213 ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
214 {
215 struct st_mysqlnd_time t;
216 zend_ulong length; /* First byte encodes the length*/
217 char * value;
218 DBG_ENTER("ps_fetch_time");
219
220 if ((length = php_mysqlnd_net_field_length(row))) {
221 const zend_uchar * to = *row;
222
223 t.time_type = MYSQLND_TIMESTAMP_TIME;
224 t.neg = (zend_bool) to[0];
225
226 t.day = (zend_ulong) sint4korr(to+1);
227 t.hour = (unsigned int) to[5];
228 t.minute = (unsigned int) to[6];
229 t.second = (unsigned int) to[7];
230 t.second_part = (length > 8) ? (zend_ulong) sint4korr(to+8) : 0;
231 t.year = t.month= 0;
232 if (t.day) {
233 /* Convert days to hours at once */
234 t.hour += t.day*24;
235 t.day = 0;
236 }
237
238 (*row) += length;
239 } else {
240 memset(&t, 0, sizeof(t));
241 t.time_type = MYSQLND_TIMESTAMP_TIME;
242 }
243
244 length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
245
246 DBG_INF_FMT("%s", value);
247 ZVAL_STRINGL(zv, value, length);
248 mnd_sprintf_free(value);
249 DBG_VOID_RETURN;
250 }
251 /* }}} */
252
253
254 /* {{{ ps_fetch_date */
255 static void
ps_fetch_date(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)256 ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
257 {
258 struct st_mysqlnd_time t = {0};
259 zend_ulong length; /* First byte encodes the length*/
260 char * value;
261 DBG_ENTER("ps_fetch_date");
262
263 if ((length = php_mysqlnd_net_field_length(row))) {
264 const zend_uchar * to = *row;
265
266 t.time_type = MYSQLND_TIMESTAMP_DATE;
267 t.neg = 0;
268
269 t.second_part = t.hour = t.minute = t.second = 0;
270
271 t.year = (unsigned int) sint2korr(to);
272 t.month = (unsigned int) to[2];
273 t.day = (unsigned int) to[3];
274
275 (*row)+= length;
276 } else {
277 memset(&t, 0, sizeof(t));
278 t.time_type = MYSQLND_TIMESTAMP_DATE;
279 }
280
281 length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
282
283 DBG_INF_FMT("%s", value);
284 ZVAL_STRINGL(zv, value, length);
285 mnd_sprintf_free(value);
286 DBG_VOID_RETURN;
287 }
288 /* }}} */
289
290
291 /* {{{ ps_fetch_datetime */
292 static void
ps_fetch_datetime(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)293 ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
294 {
295 struct st_mysqlnd_time t;
296 zend_ulong length; /* First byte encodes the length*/
297 char * value;
298 DBG_ENTER("ps_fetch_datetime");
299
300 if ((length = php_mysqlnd_net_field_length(row))) {
301 const zend_uchar * to = *row;
302
303 t.time_type = MYSQLND_TIMESTAMP_DATETIME;
304 t.neg = 0;
305
306 t.year = (unsigned int) sint2korr(to);
307 t.month = (unsigned int) to[2];
308 t.day = (unsigned int) to[3];
309
310 if (length > 4) {
311 t.hour = (unsigned int) to[4];
312 t.minute = (unsigned int) to[5];
313 t.second = (unsigned int) to[6];
314 } else {
315 t.hour = t.minute = t.second= 0;
316 }
317 t.second_part = (length > 7) ? (zend_ulong) sint4korr(to+7) : 0;
318
319 (*row)+= length;
320 } else {
321 memset(&t, 0, sizeof(t));
322 t.time_type = MYSQLND_TIMESTAMP_DATETIME;
323 }
324
325 length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
326
327 DBG_INF_FMT("%s", value);
328 ZVAL_STRINGL(zv, value, length);
329 mnd_sprintf_free(value);
330 DBG_VOID_RETURN;
331 }
332 /* }}} */
333
334
335 /* {{{ ps_fetch_string */
336 static void
ps_fetch_string(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)337 ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
338 {
339 /*
340 For now just copy, before we make it possible
341 to write \0 to the row buffer
342 */
343 const zend_ulong length = php_mysqlnd_net_field_length(row);
344 DBG_ENTER("ps_fetch_string");
345 DBG_INF_FMT("len = %lu", length);
346 DBG_INF("copying from the row buffer");
347 ZVAL_STRINGL(zv, (char *)*row, length);
348
349 (*row) += length;
350 DBG_VOID_RETURN;
351 }
352 /* }}} */
353
354
355 /* {{{ ps_fetch_bit */
356 static void
ps_fetch_bit(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)357 ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
358 {
359 const zend_ulong length = php_mysqlnd_net_field_length(row);
360 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
361 }
362 /* }}} */
363
364
365 /* {{{ _mysqlnd_init_ps_fetch_subsystem */
_mysqlnd_init_ps_fetch_subsystem()366 void _mysqlnd_init_ps_fetch_subsystem()
367 {
368 memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions));
369 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func = ps_fetch_null;
370 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
371 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL;
372 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE;
373
374 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
375 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
376 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type = IS_LONG;
377 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE;
378
379 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
380 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
381 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type = IS_LONG;
382 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE;
383
384 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
385 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
386 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type = IS_LONG;
387 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE;
388
389 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
390 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
391 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type = IS_LONG;
392 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE;
393
394 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
395 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
396 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG;
397 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE;
398
399 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
400 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
401 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG;
402 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE;
403
404 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
405 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
406 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type = IS_DOUBLE;
407 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE;
408
409 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
410 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
411 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE;
412 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE;
413
414 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_time;
415 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
416 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type = IS_STRING;
417 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE;
418
419 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_date;
420 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
421 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type = IS_STRING;
422 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE;
423
424 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
425 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
426 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type = IS_STRING;
427 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE;
428
429 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
430 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
431 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING;
432 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE;
433
434 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
435 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
436 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING;
437 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE;
438
439 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].func = ps_fetch_string;
440 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
441 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].php_type = IS_STRING;
442 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].is_possibly_blob = TRUE;
443 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].can_ret_as_str_in_uni = TRUE;
444
445 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string;
446 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
447 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
448 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE;
449 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE;
450
451 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string;
452 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
453 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING;
454 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE;
455 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE;
456
457 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string;
458 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
459 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type = IS_STRING;
460 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE;
461 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE;
462
463 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string;
464 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
465 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type = IS_STRING;
466 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE;
467 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni = TRUE;
468
469 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bit;
470 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = 8;
471 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type = IS_LONG;
472 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE;
473
474 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
475 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
476 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
477 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE;
478
479 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
480 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
481 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING;
482 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE;
483
484 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
485 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
486 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING;
487 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE;
488
489 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
490 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
491 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING;
492 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE;
493
494 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
495 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
496 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING;
497 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE;
498
499 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
500 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
501 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type = IS_STRING;
502
503 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
504 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
505 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type = IS_STRING;
506
507 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
508 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
509 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
510 }
511 /* }}} */
512
513
514 /* {{{ mysqlnd_stmt_copy_it */
515 static enum_func_status
mysqlnd_stmt_copy_it(zval ** copies,zval * original,unsigned int param_count,unsigned int current)516 mysqlnd_stmt_copy_it(zval ** copies, zval * original, unsigned int param_count, unsigned int current)
517 {
518 if (!*copies) {
519 *copies = mnd_ecalloc(param_count, sizeof(zval));
520 }
521 if (*copies) {
522 ZVAL_COPY(&(*copies)[current], original);
523 return PASS;
524 }
525 return FAIL;
526 }
527 /* }}} */
528
529
530 /* {{{ mysqlnd_stmt_free_copies */
531 static void
mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt,zval * copies)532 mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval *copies)
533 {
534 if (copies) {
535 unsigned int i;
536 for (i = 0; i < stmt->param_count; i++) {
537 zval_ptr_dtor(&copies[i]);
538 }
539 mnd_efree(copies);
540 }
541 }
542 /* }}} */
543
544
545 /* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
546 static enum_func_status
mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar ** buf,zend_uchar ** p,size_t * buf_len,zend_uchar * const provided_buffer,size_t needed_bytes)547 mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes)
548 {
549 const size_t overalloc = 5;
550 size_t left = (*buf_len - (*p - *buf));
551
552 if (left < (needed_bytes + overalloc)) {
553 const size_t offset = *p - *buf;
554 zend_uchar *tmp_buf;
555 *buf_len = offset + needed_bytes + overalloc;
556 tmp_buf = mnd_emalloc(*buf_len);
557 if (!tmp_buf) {
558 return FAIL;
559 }
560 memcpy(tmp_buf, *buf, offset);
561 if (*buf != provided_buffer) {
562 mnd_efree(*buf);
563 }
564 *buf = tmp_buf;
565 /* Update our pos pointer */
566 *p = *buf + offset;
567 }
568 return PASS;
569 }
570 /* }}} */
571
572
573 /* {{{ mysqlnd_stmt_execute_prepare_param_types */
574 static enum_func_status
mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt,zval ** copies_param,int * resend_types_next_time)575 mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copies_param, int * resend_types_next_time)
576 {
577 unsigned int i;
578 DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
579 for (i = 0; i < stmt->param_count; i++) {
580 const short current_type = stmt->param_bind[i].type;
581 zval *parameter = &stmt->param_bind[i].zv;
582
583 ZVAL_DEREF(parameter);
584 if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG || current_type == MYSQL_TYPE_TINY)) {
585 /* always copy the var, because we do many conversions */
586 if (Z_TYPE_P(parameter) != IS_LONG &&
587 PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
588 {
589 SET_OOM_ERROR(stmt->error_info);
590 goto end;
591 }
592 /*
593 if it doesn't fit in a long send it as a string.
594 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
595 */
596 if (Z_TYPE_P(parameter) != IS_LONG) {
597 zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: parameter;
598 /*
599 Because converting to double and back to long can lead
600 to losing precision we need second variable. Conversion to double is to see if
601 value is too big for a long. As said, precision could be lost.
602 */
603 zval tmp_data_copy;
604 ZVAL_COPY(&tmp_data_copy, tmp_data);
605 convert_to_double_ex(&tmp_data_copy);
606
607 /*
608 if it doesn't fit in a long send it as a string.
609 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
610 We do transformation here, which will be used later when sending types. The code later relies on this.
611 */
612 if (Z_DVAL(tmp_data_copy) > ZEND_LONG_MAX || Z_DVAL(tmp_data_copy) < ZEND_LONG_MIN) {
613 stmt->send_types_to_server = *resend_types_next_time = 1;
614 convert_to_string_ex(tmp_data);
615 } else {
616 convert_to_long_ex(tmp_data);
617 }
618
619 zval_ptr_dtor(&tmp_data_copy);
620 }
621 }
622 }
623 DBG_RETURN(PASS);
624 end:
625 DBG_RETURN(FAIL);
626 }
627 /* }}} */
628
629
630 /* {{{ mysqlnd_stmt_execute_store_types */
631 static void
mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt,zval * copies,zend_uchar ** p)632 mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar ** p)
633 {
634 unsigned int i;
635 for (i = 0; i < stmt->param_count; i++) {
636 short current_type = stmt->param_bind[i].type;
637 zval *parameter = &stmt->param_bind[i].zv;
638 /* our types are not unsigned */
639 #if SIZEOF_ZEND_LONG==8
640 if (current_type == MYSQL_TYPE_LONG) {
641 current_type = MYSQL_TYPE_LONGLONG;
642 }
643 #endif
644 ZVAL_DEREF(parameter);
645 if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
646 /*
647 if it doesn't fit in a long send it as a string.
648 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
649 */
650 if (Z_TYPE_P(parameter) != IS_LONG) {
651 const zval *tmp_data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i] : parameter;
652 /*
653 In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
654 The actual transformation has been performed several dozens line above.
655 */
656 if (Z_TYPE_P(tmp_data) == IS_STRING) {
657 current_type = MYSQL_TYPE_VAR_STRING;
658 /*
659 don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
660 we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
661 if the type is however not long, then we will do a goto in the next switch.
662 We want to preserve the original bind type given by the user. Thus, we do these hacks.
663 */
664 }
665 }
666 }
667 int2store(*p, current_type);
668 *p+= 2;
669 }
670 }
671 /* }}} */
672
673
674 /* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
675 static enum_func_status
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt,zval ** copies_param,size_t * data_size)676 mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval ** copies_param, size_t * data_size)
677 {
678 unsigned int i;
679 DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
680 for (i = 0; i < stmt->param_count; i++) {
681 unsigned short is_longlong = 0;
682 unsigned int j;
683 zval *bind_var, *the_var = &stmt->param_bind[i].zv;
684
685 bind_var = the_var;
686 ZVAL_DEREF(the_var);
687 if ((stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
688 continue;
689 }
690
691 if (Z_ISREF_P(bind_var)) {
692 for (j = i + 1; j < stmt->param_count; j++) {
693 if (Z_ISREF(stmt->param_bind[j].zv) && Z_REFVAL(stmt->param_bind[j].zv) == the_var) {
694 /* Double binding of the same zval, make a copy */
695 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
696 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
697 SET_OOM_ERROR(stmt->error_info);
698 goto end;
699 }
700 }
701 break;
702 }
703 }
704 }
705
706 switch (stmt->param_bind[i].type) {
707 case MYSQL_TYPE_DOUBLE:
708 *data_size += 8;
709 if (Z_TYPE_P(the_var) != IS_DOUBLE) {
710 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
711 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
712 SET_OOM_ERROR(stmt->error_info);
713 goto end;
714 }
715 }
716 }
717 break;
718 case MYSQL_TYPE_LONGLONG:
719 is_longlong = 4;
720 /* fall-through */
721 case MYSQL_TYPE_LONG:
722 {
723 zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: the_var;
724 if (Z_TYPE_P(tmp_data) == IS_STRING) {
725 goto use_string;
726 }
727 convert_to_long_ex(tmp_data);
728 }
729 *data_size += 4 + is_longlong;
730 break;
731 case MYSQL_TYPE_LONG_BLOB:
732 if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
733 /*
734 User hasn't sent anything, we will send empty string.
735 Empty string has length of 0, encoded in 1 byte. No real
736 data will follows after it.
737 */
738 (*data_size)++;
739 }
740 break;
741 case MYSQL_TYPE_VAR_STRING:
742 use_string:
743 *data_size += 8; /* max 8 bytes for size */
744 if (Z_TYPE_P(the_var) != IS_STRING) {
745 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
746 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
747 SET_OOM_ERROR(stmt->error_info);
748 goto end;
749 }
750 }
751 the_var = &((*copies_param)[i]);
752 }
753 convert_to_string_ex(the_var);
754 *data_size += Z_STRLEN_P(the_var);
755 break;
756 }
757 }
758 DBG_RETURN(PASS);
759 end:
760 DBG_RETURN(FAIL);
761 }
762 /* }}} */
763
764
765 /* {{{ mysqlnd_stmt_execute_store_param_values */
766 static void
mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt,zval * copies,zend_uchar * buf,zend_uchar ** p,size_t null_byte_offset)767 mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar * buf, zend_uchar ** p, size_t null_byte_offset)
768 {
769 unsigned int i;
770 for (i = 0; i < stmt->param_count; i++) {
771 zval *data, *parameter = &stmt->param_bind[i].zv;
772
773 ZVAL_DEREF(parameter);
774 data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i]: parameter;
775 /* Handle long data */
776 if (!Z_ISUNDEF_P(parameter) && Z_TYPE_P(data) == IS_NULL) {
777 (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
778 } else {
779 switch (stmt->param_bind[i].type) {
780 case MYSQL_TYPE_DOUBLE:
781 convert_to_double_ex(data);
782 float8store(*p, Z_DVAL_P(data));
783 (*p) += 8;
784 break;
785 case MYSQL_TYPE_LONGLONG:
786 if (Z_TYPE_P(data) == IS_STRING) {
787 goto send_string;
788 }
789 /* data has alreade been converted to long */
790 int8store(*p, Z_LVAL_P(data));
791 (*p) += 8;
792 break;
793 case MYSQL_TYPE_LONG:
794 if (Z_TYPE_P(data) == IS_STRING) {
795 goto send_string;
796 }
797 /* data has alreade been converted to long */
798 int4store(*p, Z_LVAL_P(data));
799 (*p) += 4;
800 break;
801 case MYSQL_TYPE_TINY:
802 if (Z_TYPE_P(data) == IS_STRING) {
803 goto send_string;
804 }
805 int1store(*p, Z_LVAL_P(data));
806 (*p)++;
807 break;
808 case MYSQL_TYPE_LONG_BLOB:
809 if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
810 stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
811 } else {
812 /* send_long_data() not called, send empty string */
813 *p = php_mysqlnd_net_store_length(*p, 0);
814 }
815 break;
816 case MYSQL_TYPE_VAR_STRING:
817 send_string:
818 {
819 const size_t len = Z_STRLEN_P(data);
820 /* to is after p. The latter hasn't been moved */
821 *p = php_mysqlnd_net_store_length(*p, len);
822 memcpy(*p, Z_STRVAL_P(data), len);
823 (*p) += len;
824 }
825 break;
826 default:
827 /* Won't happen, but set to NULL */
828 (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
829 break;
830 }
831 }
832 }
833 }
834 /* }}} */
835
836
837 /* {{{ mysqlnd_stmt_execute_store_params */
838 static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s,zend_uchar ** buf,zend_uchar ** p,size_t * buf_len)839 mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len )
840 {
841 MYSQLND_STMT_DATA * stmt = s->data;
842 zend_uchar * provided_buffer = *buf;
843 size_t data_size = 0;
844 zval *copies = NULL;/* if there are different types */
845 enum_func_status ret = FAIL;
846 int resend_types_next_time = 0;
847 size_t null_byte_offset;
848
849 DBG_ENTER("mysqlnd_stmt_execute_store_params");
850
851 {
852 unsigned int null_count = (stmt->param_count + 7) / 8;
853 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
854 SET_OOM_ERROR(stmt->error_info);
855 goto end;
856 }
857 /* put `null` bytes */
858 null_byte_offset = *p - *buf;
859 memset(*p, 0, null_count);
860 *p += null_count;
861 }
862
863 /* 1. Store type information */
864 /*
865 check if need to send the types even if stmt->send_types_to_server is 0. This is because
866 if we send "i" (42) then the type will be int and the server will expect int. However, if next
867 time we try to send > LONG_MAX, the conversion to string will send a string and the server
868 won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
869 occur, and force resend for the next execution.
870 */
871 if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time)) {
872 goto end;
873 }
874
875 int1store(*p, stmt->send_types_to_server);
876 (*p)++;
877
878 if (stmt->send_types_to_server) {
879 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
880 SET_OOM_ERROR(stmt->error_info);
881 goto end;
882 }
883 mysqlnd_stmt_execute_store_types(stmt, copies, p);
884 }
885
886 stmt->send_types_to_server = resend_types_next_time;
887
888 /* 2. Store data */
889 /* 2.1 Calculate how much space we need */
890 if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size)) {
891 goto end;
892 }
893
894 /* 2.2 Enlarge the buffer, if needed */
895 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
896 SET_OOM_ERROR(stmt->error_info);
897 goto end;
898 }
899
900 /* 2.3 Store the actual data */
901 mysqlnd_stmt_execute_store_param_values(stmt, copies, *buf, p, null_byte_offset);
902
903 ret = PASS;
904 end:
905 mysqlnd_stmt_free_copies(stmt, copies);
906
907 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
908 DBG_RETURN(ret);
909 }
910 /* }}} */
911
912
913 /* {{{ mysqlnd_stmt_execute_generate_request */
914 enum_func_status
mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s,zend_uchar ** request,size_t * request_len,zend_bool * free_buffer)915 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer)
916 {
917 MYSQLND_STMT_DATA * stmt = s->data;
918 zend_uchar *p = stmt->execute_cmd_buffer.buffer,
919 *cmd_buffer = stmt->execute_cmd_buffer.buffer;
920 size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
921 enum_func_status ret;
922
923 DBG_ENTER("mysqlnd_stmt_execute_generate_request");
924
925 int4store(p, stmt->stmt_id);
926 p += 4;
927
928 /* flags is 4 bytes, we store just 1 */
929 int1store(p, (zend_uchar) stmt->flags);
930 p++;
931
932 /* Make it all zero */
933 int4store(p, 0);
934
935 int1store(p, 1); /* and send 1 for iteration count */
936 p+= 4;
937
938 ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length);
939
940 *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
941 *request_len = (p - cmd_buffer);
942 *request = cmd_buffer;
943 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
944 DBG_RETURN(ret);
945 }
946 /* }}} */
947
948 /*
949 * Local variables:
950 * tab-width: 4
951 * c-basic-offset: 4
952 * End:
953 * vim600: noet sw=4 ts=4 fdm=marker
954 * vim<600: noet sw=4 ts=4
955 */
956