1 /* Generated by re2c 0.16 */
2 /*
3   +----------------------------------------------------------------------+
4   | Copyright (c) The PHP Group                                          |
5   +----------------------------------------------------------------------+
6   | This source file is subject to version 3.01 of the PHP license,      |
7   | that is bundled with this package in the file LICENSE, and is        |
8   | available through the world-wide-web at the following url:           |
9   | http://www.php.net/license/3_01.txt                                  |
10   | If you did not receive a copy of the PHP license and are unable to   |
11   | obtain it through the world-wide-web, please send a note to          |
12   | license@php.net so we can mail you a copy immediately.               |
13   +----------------------------------------------------------------------+
14   | Author: George Schlossnagle <george@omniti.com>                      |
15   +----------------------------------------------------------------------+
16 */
17 
18 #include "php.h"
19 #include "php_pdo_driver.h"
20 #include "php_pdo_int.h"
21 
22 #define PDO_PARSER_TEXT 1
23 #define PDO_PARSER_BIND 2
24 #define PDO_PARSER_BIND_POS 3
25 #define PDO_PARSER_ESCAPED_QUESTION 4
26 #define PDO_PARSER_EOI 5
27 
28 #define PDO_PARSER_BINDNO_ESCAPED_CHAR -1
29 
30 #define RET(i) {s->cur = cursor; return i; }
31 #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
32 
33 #define YYCTYPE         unsigned char
34 #define YYCURSOR        cursor
35 #define YYLIMIT         s->end
36 #define YYMARKER        s->ptr
37 #define YYFILL(n)		{ RET(PDO_PARSER_EOI); }
38 
39 typedef struct Scanner {
40 	const char *ptr, *cur, *tok, *end;
41 } Scanner;
42 
scan(Scanner * s)43 static int scan(Scanner *s)
44 {
45 	const char *cursor = s->cur;
46 
47 	s->tok = cursor;
48 
49 
50 
51 {
52 	YYCTYPE yych;
53 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
54 	yych = *YYCURSOR;
55 	switch (yych) {
56 	case 0x00:	goto yy2;
57 	case '"':	goto yy6;
58 	case '\'':	goto yy8;
59 	case '(':
60 	case ')':
61 	case '*':
62 	case '+':
63 	case ',':
64 	case '.':	goto yy9;
65 	case '-':	goto yy10;
66 	case '/':	goto yy11;
67 	case ':':	goto yy12;
68 	case '?':	goto yy13;
69 	default:	goto yy3;
70 	}
71 yy2:
72 	YYCURSOR = YYMARKER;
73 	goto yy7;
74 yy3:
75 	++YYCURSOR;
76 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
77 	yych = *YYCURSOR;
78 	switch (yych) {
79 	case 0x00:
80 	case '"':
81 	case '\'':
82 	case '(':
83 	case ')':
84 	case '*':
85 	case '+':
86 	case ',':
87 	case '-':
88 	case '.':
89 	case '/':
90 	case ':':
91 	case '?':	goto yy5;
92 	default:	goto yy3;
93 	}
94 yy5:
95 	{ RET(PDO_PARSER_TEXT); }
96 yy6:
97 	yych = *(YYMARKER = ++YYCURSOR);
98 	if (yych >= 0x01) goto yy16;
99 yy7:
100 	{ SKIP_ONE(PDO_PARSER_TEXT); }
101 yy8:
102 	yych = *(YYMARKER = ++YYCURSOR);
103 	if (yych <= 0x00) goto yy7;
104 	goto yy21;
105 yy9:
106 	yych = *++YYCURSOR;
107 	goto yy7;
108 yy10:
109 	yych = *++YYCURSOR;
110 	switch (yych) {
111 	case '-':	goto yy25;
112 	default:	goto yy7;
113 	}
114 yy11:
115 	yych = *(YYMARKER = ++YYCURSOR);
116 	switch (yych) {
117 	case '*':	goto yy28;
118 	default:	goto yy7;
119 	}
120 yy12:
121 	yych = *++YYCURSOR;
122 	switch (yych) {
123 	case '0':
124 	case '1':
125 	case '2':
126 	case '3':
127 	case '4':
128 	case '5':
129 	case '6':
130 	case '7':
131 	case '8':
132 	case '9':
133 	case 'A':
134 	case 'B':
135 	case 'C':
136 	case 'D':
137 	case 'E':
138 	case 'F':
139 	case 'G':
140 	case 'H':
141 	case 'I':
142 	case 'J':
143 	case 'K':
144 	case 'L':
145 	case 'M':
146 	case 'N':
147 	case 'O':
148 	case 'P':
149 	case 'Q':
150 	case 'R':
151 	case 'S':
152 	case 'T':
153 	case 'U':
154 	case 'V':
155 	case 'W':
156 	case 'X':
157 	case 'Y':
158 	case 'Z':
159 	case '_':
160 	case 'a':
161 	case 'b':
162 	case 'c':
163 	case 'd':
164 	case 'e':
165 	case 'f':
166 	case 'g':
167 	case 'h':
168 	case 'i':
169 	case 'j':
170 	case 'k':
171 	case 'l':
172 	case 'm':
173 	case 'n':
174 	case 'o':
175 	case 'p':
176 	case 'q':
177 	case 'r':
178 	case 's':
179 	case 't':
180 	case 'u':
181 	case 'v':
182 	case 'w':
183 	case 'x':
184 	case 'y':
185 	case 'z':	goto yy30;
186 	case ':':	goto yy33;
187 	default:	goto yy7;
188 	}
189 yy13:
190 	++YYCURSOR;
191 	switch ((yych = *YYCURSOR)) {
192 	case '?':	goto yy36;
193 	default:	goto yy14;
194 	}
195 yy14:
196 	{ RET(PDO_PARSER_BIND_POS); }
197 yy15:
198 	++YYCURSOR;
199 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
200 	yych = *YYCURSOR;
201 yy16:
202 	switch (yych) {
203 	case 0x00:	goto yy2;
204 	case '"':	goto yy17;
205 	case '\\':	goto yy19;
206 	default:	goto yy15;
207 	}
208 yy17:
209 	++YYCURSOR;
210 	{ RET(PDO_PARSER_TEXT); }
211 yy19:
212 	++YYCURSOR;
213 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
214 	yych = *YYCURSOR;
215 	if (yych <= 0x00) goto yy2;
216 	goto yy15;
217 yy20:
218 	++YYCURSOR;
219 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
220 	yych = *YYCURSOR;
221 yy21:
222 	switch (yych) {
223 	case 0x00:	goto yy2;
224 	case '\'':	goto yy22;
225 	case '\\':	goto yy24;
226 	default:	goto yy20;
227 	}
228 yy22:
229 	++YYCURSOR;
230 	{ RET(PDO_PARSER_TEXT); }
231 yy24:
232 	++YYCURSOR;
233 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
234 	yych = *YYCURSOR;
235 	if (yych <= 0x00) goto yy2;
236 	goto yy20;
237 yy25:
238 	++YYCURSOR;
239 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
240 	yych = *YYCURSOR;
241 	switch (yych) {
242 	case '\n':
243 	case '\r':	goto yy27;
244 	default:	goto yy25;
245 	}
246 yy27:
247 	{ RET(PDO_PARSER_TEXT); }
248 yy28:
249 	++YYCURSOR;
250 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
251 	yych = *YYCURSOR;
252 	switch (yych) {
253 	case '*':	goto yy38;
254 	default:	goto yy28;
255 	}
256 yy30:
257 	++YYCURSOR;
258 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
259 	yych = *YYCURSOR;
260 	switch (yych) {
261 	case '0':
262 	case '1':
263 	case '2':
264 	case '3':
265 	case '4':
266 	case '5':
267 	case '6':
268 	case '7':
269 	case '8':
270 	case '9':
271 	case 'A':
272 	case 'B':
273 	case 'C':
274 	case 'D':
275 	case 'E':
276 	case 'F':
277 	case 'G':
278 	case 'H':
279 	case 'I':
280 	case 'J':
281 	case 'K':
282 	case 'L':
283 	case 'M':
284 	case 'N':
285 	case 'O':
286 	case 'P':
287 	case 'Q':
288 	case 'R':
289 	case 'S':
290 	case 'T':
291 	case 'U':
292 	case 'V':
293 	case 'W':
294 	case 'X':
295 	case 'Y':
296 	case 'Z':
297 	case '_':
298 	case 'a':
299 	case 'b':
300 	case 'c':
301 	case 'd':
302 	case 'e':
303 	case 'f':
304 	case 'g':
305 	case 'h':
306 	case 'i':
307 	case 'j':
308 	case 'k':
309 	case 'l':
310 	case 'm':
311 	case 'n':
312 	case 'o':
313 	case 'p':
314 	case 'q':
315 	case 'r':
316 	case 's':
317 	case 't':
318 	case 'u':
319 	case 'v':
320 	case 'w':
321 	case 'x':
322 	case 'y':
323 	case 'z':	goto yy30;
324 	default:	goto yy32;
325 	}
326 yy32:
327 	{ RET(PDO_PARSER_BIND); }
328 yy33:
329 	++YYCURSOR;
330 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
331 	yych = *YYCURSOR;
332 	switch (yych) {
333 	case ':':	goto yy33;
334 	default:	goto yy35;
335 	}
336 yy35:
337 	{ RET(PDO_PARSER_TEXT); }
338 yy36:
339 	++YYCURSOR;
340 	{ RET(PDO_PARSER_ESCAPED_QUESTION); }
341 yy38:
342 	++YYCURSOR;
343 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
344 	yych = *YYCURSOR;
345 	switch (yych) {
346 	case '*':	goto yy38;
347 	case '/':	goto yy40;
348 	default:	goto yy28;
349 	}
350 yy40:
351 	++YYCURSOR;
352 	yych = *YYCURSOR;
353 	goto yy27;
354 }
355 
356 }
357 
358 struct placeholder {
359 	const char *pos;
360 	size_t len;
361 	size_t qlen;	/* quoted length of value */
362 	char *quoted;	/* quoted value */
363 	int freeq;
364 	int bindno;
365 	struct placeholder *next;
366 };
367 
free_param_name(zval * el)368 static void free_param_name(zval *el) {
369 	efree(Z_PTR_P(el));
370 }
371 
pdo_parse_params(pdo_stmt_t * stmt,const char * inquery,size_t inquery_len,char ** outquery,size_t * outquery_len)372 PDO_API int pdo_parse_params(pdo_stmt_t *stmt, const char *inquery, size_t inquery_len,
373 	char **outquery, size_t *outquery_len)
374 {
375 	Scanner s;
376 	const char *ptr;
377 	char *newbuffer;
378 	ptrdiff_t t;
379 	uint32_t bindno = 0;
380 	int ret = 0, escapes = 0;
381 	size_t newbuffer_len;
382 	HashTable *params;
383 	struct pdo_bound_param_data *param;
384 	int query_type = PDO_PLACEHOLDER_NONE;
385 	struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
386 
387 	ptr = *outquery;
388 	s.cur = inquery;
389 	s.end = inquery + inquery_len + 1;
390 
391 	/* phase 1: look for args */
392 	while((t = scan(&s)) != PDO_PARSER_EOI) {
393 		if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS || t == PDO_PARSER_ESCAPED_QUESTION) {
394 			if (t == PDO_PARSER_ESCAPED_QUESTION && stmt->supports_placeholders == PDO_PLACEHOLDER_POSITIONAL) {
395 				/* escaped question marks unsupported, treat as text */
396 				continue;
397 			}
398 
399 			if (t == PDO_PARSER_BIND) {
400 				ptrdiff_t len = s.cur - s.tok;
401 				if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
402 					continue;
403 				}
404 				query_type |= PDO_PLACEHOLDER_NAMED;
405 			} else if (t == PDO_PARSER_BIND_POS) {
406 				query_type |= PDO_PLACEHOLDER_POSITIONAL;
407 			}
408 
409 			plc = emalloc(sizeof(*plc));
410 			memset(plc, 0, sizeof(*plc));
411 			plc->next = NULL;
412 			plc->pos = s.tok;
413 			plc->len = s.cur - s.tok;
414 
415 			if (t == PDO_PARSER_ESCAPED_QUESTION) {
416 				plc->bindno = PDO_PARSER_BINDNO_ESCAPED_CHAR;
417 				plc->quoted = "?";
418 				plc->qlen = 1;
419 				plc->freeq = 0;
420 				escapes++;
421 			} else {
422 				plc->bindno = bindno++;
423 			}
424 
425 			if (placetail) {
426 				placetail->next = plc;
427 			} else {
428 				placeholders = plc;
429 			}
430 			placetail = plc;
431 		}
432 	}
433 
434 	/* did the query make sense to me? */
435 	if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
436 		/* they mixed both types; punt */
437 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters");
438 		ret = -1;
439 		goto clean_up;
440 	}
441 
442 	params = stmt->bound_params;
443 	if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE && params && bindno != zend_hash_num_elements(params)) {
444 		/* extra bit of validation for instances when same params are bound more than once */
445 		if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
446 			int ok = 1;
447 			for (plc = placeholders; plc; plc = plc->next) {
448 				if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
449 					ok = 0;
450 					break;
451 				}
452 			}
453 			if (ok) {
454 				goto safe;
455 			}
456 		}
457 		pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
458 		ret = -1;
459 		goto clean_up;
460 	}
461 
462 	if (!placeholders) {
463 		/* nothing to do; good! */
464 		return 0;
465 	}
466 
467 	if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
468 		/* query matches native syntax */
469 		if (escapes) {
470 			newbuffer_len = inquery_len;
471 			goto rewrite;
472 		}
473 
474 		ret = 0;
475 		goto clean_up;
476 	}
477 
478 	if (query_type == PDO_PLACEHOLDER_NAMED && stmt->named_rewrite_template) {
479 		/* magic/hack.
480 		 * We we pretend that the query was positional even if
481 		 * it was named so that we fall into the
482 		 * named rewrite case below.  Not too pretty,
483 		 * but it works. */
484 		query_type = PDO_PLACEHOLDER_POSITIONAL;
485 	}
486 
487 safe:
488 	/* what are we going to do ? */
489 	if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
490 		/* query generation */
491 
492 		newbuffer_len = inquery_len;
493 
494 		/* let's quote all the values */
495 		for (plc = placeholders; plc && params; plc = plc->next) {
496 			if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
497 				/* escaped character */
498 				continue;
499 			}
500 
501 			if (query_type == PDO_PLACEHOLDER_NONE) {
502 				continue;
503 			}
504 
505 			if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
506 				param = zend_hash_index_find_ptr(params, plc->bindno);
507 			} else {
508 				param = zend_hash_str_find_ptr(params, plc->pos, plc->len);
509 			}
510 			if (param == NULL) {
511 				/* parameter was not defined */
512 				ret = -1;
513 				pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
514 				goto clean_up;
515 			}
516 			if (stmt->dbh->methods->quoter) {
517 				zval *parameter;
518 				if (Z_ISREF(param->parameter)) {
519 					parameter = Z_REFVAL(param->parameter);
520 				} else {
521 					parameter = &param->parameter;
522 				}
523 				if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {
524 					php_stream *stm;
525 
526 					php_stream_from_zval_no_verify(stm, parameter);
527 					if (stm) {
528 						zend_string *buf;
529 
530 						buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
531 						if (!buf) {
532 							buf = ZSTR_EMPTY_ALLOC();
533 						}
534 						if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
535 								param->param_type)) {
536 							/* bork */
537 							ret = -1;
538 							strncpy(stmt->error_code, stmt->dbh->error_code, 6);
539 							if (buf) {
540 								zend_string_release_ex(buf, 0);
541 							}
542 							goto clean_up;
543 						}
544 						if (buf) {
545 							zend_string_release_ex(buf, 0);
546 						}
547 					} else {
548 						pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
549 						ret = -1;
550 						goto clean_up;
551 					}
552 					plc->freeq = 1;
553 				} else {
554 					enum pdo_param_type param_type = param->param_type;
555 					zend_string *buf = NULL;
556 
557 					/* assume all types are nullable */
558 					if (Z_TYPE_P(parameter) == IS_NULL) {
559 						param_type = PDO_PARAM_NULL;
560 					}
561 
562 					switch (param_type) {
563 						case PDO_PARAM_BOOL:
564 							plc->quoted = zend_is_true(parameter) ? "1" : "0";
565 							plc->qlen = sizeof("1")-1;
566 							plc->freeq = 0;
567 							break;
568 
569 						case PDO_PARAM_INT:
570 							buf = zend_long_to_str(zval_get_long(parameter));
571 
572 							plc->qlen = ZSTR_LEN(buf);
573 							plc->quoted = estrdup(ZSTR_VAL(buf));
574 							plc->freeq = 1;
575 							break;
576 
577 						case PDO_PARAM_NULL:
578 							plc->quoted = "NULL";
579 							plc->qlen = sizeof("NULL")-1;
580 							plc->freeq = 0;
581 							break;
582 
583 						default:
584 							buf = zval_get_string(parameter);
585 							if (EG(exception) ||
586 								!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf),
587 									ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
588 									param_type)) {
589 								/* bork */
590 								ret = -1;
591 								strncpy(stmt->error_code, stmt->dbh->error_code, 6);
592 								if (buf) {
593 									zend_string_release_ex(buf, 0);
594 								}
595 								goto clean_up;
596 							}
597 							plc->freeq = 1;
598 					}
599 
600 					if (buf) {
601 						zend_string_release_ex(buf, 0);
602 					}
603 				}
604 			} else {
605 				zval *parameter;
606 				if (Z_ISREF(param->parameter)) {
607 					parameter = Z_REFVAL(param->parameter);
608 				} else {
609 					parameter = &param->parameter;
610 				}
611 				plc->quoted = Z_STRVAL_P(parameter);
612 				plc->qlen = Z_STRLEN_P(parameter);
613 			}
614 			newbuffer_len += plc->qlen;
615 		}
616 
617 rewrite:
618 		/* allocate output buffer */
619 		newbuffer = emalloc(newbuffer_len + 1);
620 		*outquery = newbuffer;
621 
622 		/* and build the query */
623 		plc = placeholders;
624 		ptr = inquery;
625 
626 		do {
627 			t = plc->pos - ptr;
628 			if (t) {
629 				memcpy(newbuffer, ptr, t);
630 				newbuffer += t;
631 			}
632 			if (plc->quoted) {
633 				memcpy(newbuffer, plc->quoted, plc->qlen);
634 				newbuffer += plc->qlen;
635 			} else {
636 				memcpy(newbuffer, plc->pos, plc->len);
637 				newbuffer += plc->len;
638 			}
639 			ptr = plc->pos + plc->len;
640 
641 			plc = plc->next;
642 		} while (plc);
643 
644 		t = (inquery + inquery_len) - ptr;
645 		if (t) {
646 			memcpy(newbuffer, ptr, t);
647 			newbuffer += t;
648 		}
649 		*newbuffer = '\0';
650 		*outquery_len = newbuffer - *outquery;
651 
652 		ret = 1;
653 		goto clean_up;
654 
655 	} else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
656 		/* rewrite ? to :pdoX */
657 		char *name, *idxbuf;
658 		const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
659 		int bind_no = 1;
660 
661 		newbuffer_len = inquery_len;
662 
663 		if (stmt->bound_param_map == NULL) {
664 			ALLOC_HASHTABLE(stmt->bound_param_map);
665 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
666 		}
667 
668 		for (plc = placeholders; plc; plc = plc->next) {
669 			int skip_map = 0;
670 			char *p;
671 
672 			if (plc->bindno == PDO_PARSER_BINDNO_ESCAPED_CHAR) {
673 				continue;
674 			}
675 
676 			name = estrndup(plc->pos, plc->len);
677 
678 			/* check if bound parameter is already available */
679 			if (!strcmp(name, "?") || (p = zend_hash_str_find_ptr(stmt->bound_param_map, name, plc->len)) == NULL) {
680 				spprintf(&idxbuf, 0, tmpl, bind_no++);
681 			} else {
682 				idxbuf = estrdup(p);
683 				skip_map = 1;
684 			}
685 
686 			plc->quoted = idxbuf;
687 			plc->qlen = strlen(plc->quoted);
688 			plc->freeq = 1;
689 			newbuffer_len += plc->qlen;
690 
691 			if (!skip_map && stmt->named_rewrite_template) {
692 				/* create a mapping */
693 				zend_hash_str_update_mem(stmt->bound_param_map, name, plc->len, idxbuf, plc->qlen + 1);
694 			}
695 
696 			/* map number to name */
697 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1);
698 
699 			efree(name);
700 		}
701 
702 		goto rewrite;
703 
704 	} else {
705 		/* rewrite :name to ? */
706 
707 		newbuffer_len = inquery_len;
708 
709 		if (stmt->bound_param_map == NULL) {
710 			ALLOC_HASHTABLE(stmt->bound_param_map);
711 			zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
712 		}
713 
714 		for (plc = placeholders; plc; plc = plc->next) {
715 			char *name;
716 			name = estrndup(plc->pos, plc->len);
717 			zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, name, plc->len + 1);
718 			efree(name);
719 			plc->quoted = "?";
720 			plc->qlen = 1;
721 			newbuffer_len -= plc->len - 1;
722 		}
723 
724 		goto rewrite;
725 	}
726 
727 clean_up:
728 
729 	while (placeholders) {
730 		plc = placeholders;
731 		placeholders = plc->next;
732 
733 		if (plc->freeq) {
734 			efree(plc->quoted);
735 		}
736 
737 		efree(plc);
738 	}
739 
740 	return ret;
741 }
742