1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 and 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2004 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.0 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_0.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: Michael Spector <michael@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $ Id: $ */
20
21 #include "php_expect.h"
22 #include <string.h>
23 #include <errno.h>
24
25 #if PHP_MAJOR_VERSION >= 8
26 #define TSRMLS_CC
27 #endif
28
29 ZEND_BEGIN_ARG_INFO_EX(arginfo_expect_popen, 0, 0, 1)
30 ZEND_ARG_INFO(0, command)
31 ZEND_END_ARG_INFO()
32
33 ZEND_BEGIN_ARG_INFO_EX(arginfo_expect_expectl, 0, 0, 2)
34 ZEND_ARG_INFO(0, stream)
35 ZEND_ARG_INFO(0, expect_cases)
36 ZEND_ARG_INFO(1, match)
37 ZEND_END_ARG_INFO()
38
39 /* {{{ expect_functions[] */
40 zend_function_entry expect_functions[] = {
41 PHP_FE(expect_popen, arginfo_expect_popen)
42 PHP_FE(expect_expectl, arginfo_expect_expectl)
43 { NULL, NULL, NULL }
44 };
45 /* }}} */
46
47
48 ZEND_DECLARE_MODULE_GLOBALS(expect)
49 static PHP_GINIT_FUNCTION(expect);
50 static PHP_GSHUTDOWN_FUNCTION(expect);
51
52 /* {{{ expect_module_entry
53 */
54 zend_module_entry expect_module_entry = {
55 STANDARD_MODULE_HEADER,
56 "expect",
57 expect_functions,
58 PHP_MINIT(expect),
59 PHP_MSHUTDOWN(expect),
60 NULL,
61 NULL,
62 PHP_MINFO(expect),
63 PHP_EXPECT_VERSION,
64 PHP_MODULE_GLOBALS(expect),
65 PHP_GINIT(expect),
66 PHP_GSHUTDOWN(expect),
67 NULL,
68 STANDARD_MODULE_PROPERTIES_EX
69 };
70 /* }}} */
71
72 #ifdef COMPILE_DL_EXPECT
73 ZEND_GET_MODULE(expect)
74 #endif
75
76 /* {{{ php_expect_init_globals
77 */
PHP_GINIT_FUNCTION(expect)78 static PHP_GINIT_FUNCTION(expect)
79 {
80 expect_globals->logfile_stream = NULL;
81 }
82 /* }}} */
83
84 /* {{{ php_expect_destroy_globals
85 */
PHP_GSHUTDOWN_FUNCTION(expect)86 static PHP_GSHUTDOWN_FUNCTION(expect)
87 {
88 if (expect_globals->logfile_stream) {
89 php_stream_close(expect_globals->logfile_stream);
90 }
91 }
92 /* }}} */
93
94 /* {{{ PHP_INI_MH
95 * */
PHP_INI_MH(OnSetExpectTimeout)96 static PHP_INI_MH(OnSetExpectTimeout)
97 {
98 if (new_value) {
99 #if PHP_MAJOR_VERSION >= 7
100 exp_timeout = atoi(ZSTR_VAL(new_value));
101 #else
102 exp_timeout = atoi(new_value);
103 #endif
104 return SUCCESS;
105 }
106 return FAILURE;
107 }
108 /* }}} */
109
110 /* {{{ PHP_INI_MH
111 * */
PHP_INI_MH(OnSetExpectMatchMax)112 static PHP_INI_MH(OnSetExpectMatchMax)
113 {
114 if (new_value) {
115 #if PHP_MAJOR_VERSION >= 7
116 exp_match_max = atoi(ZSTR_VAL(new_value));
117 #else
118 exp_match_max = atoi(new_value);
119 #endif
120 return SUCCESS;
121 }
122 return FAILURE;
123 }
124 /* }}} */
125
126
127 /* {{{ PHP_INI_MH
128 * */
PHP_INI_MH(OnSetExpectLogUser)129 static PHP_INI_MH(OnSetExpectLogUser)
130 {
131 if (new_value) {
132 #if PHP_MAJOR_VERSION >= 7
133 if (strncasecmp("on", ZSTR_VAL(new_value), sizeof("on")) == 0
134 || strncasecmp("true", ZSTR_VAL(new_value), sizeof("true")) == 0
135 || strncasecmp("yes", ZSTR_VAL(new_value), sizeof("yes")) == 0
136 || strncasecmp("1", ZSTR_VAL(new_value), sizeof("1")) == 0) {
137 #else
138 if (strncasecmp("on", new_value, sizeof("on")) == 0
139 || strncasecmp("true", new_value, sizeof("true")) == 0
140 || strncasecmp("yes", new_value, sizeof("yes")) == 0
141 || strncasecmp("1", new_value, sizeof("1")) == 0) {
142 #endif
143 exp_loguser = 1;
144 } else {
145 exp_loguser = 0;
146 }
147 return SUCCESS;
148 }
149 return FAILURE;
150 }
151 /* }}} */
152
153
154 /* {{{ PHP_INI_MH
155 * */
156 static PHP_INI_MH(OnSetExpectLogFile)
157 {
158 if (EXPECT_G(logfile_stream)) {
159 php_stream_close(EXPECT_G(logfile_stream));
160 }
161 #if PHP_MAJOR_VERSION >= 7
162 if (ZSTR_LEN(new_value) > 0) {
163 php_stream* stream = php_stream_open_wrapper (ZSTR_VAL(new_value), "a", 0, NULL);
164 #else
165 if (new_value_length > 0) {
166 php_stream* stream = php_stream_open_wrapper (new_value, "a", 0, NULL);
167 #endif
168 if (!stream) {
169 php_error_docref (NULL TSRMLS_CC, E_ERROR, "could not open log file for writing");
170 return FAILURE;
171 }
172 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
173 if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void **) &exp_logfile, REPORT_ERRORS) != SUCCESS) {
174 return FAILURE;
175 }
176 EXPECT_G(logfile_stream) = stream;
177 exp_logfile_all = 1;
178 } else {
179 EXPECT_G(logfile_stream) = NULL;
180 exp_logfile = NULL;
181 exp_logfile_all = 0;
182 }
183 return SUCCESS;
184 }
185 /* }}} */
186
187
188 PHP_INI_BEGIN()
189 PHP_INI_ENTRY("expect.timeout", "10", PHP_INI_ALL, OnSetExpectTimeout)
190 PHP_INI_ENTRY_EX("expect.loguser", "1", PHP_INI_ALL, OnSetExpectLogUser, php_ini_boolean_displayer_cb)
191 PHP_INI_ENTRY("expect.logfile", "", PHP_INI_ALL, OnSetExpectLogFile)
192 PHP_INI_ENTRY("expect.match_max", "5000", PHP_INI_ALL, OnSetExpectMatchMax)
193 PHP_INI_END()
194
195
196 /* {{{ PHP_MINIT_FUNCTION */
197 PHP_MINIT_FUNCTION(expect)
198 {
199 php_register_url_stream_wrapper("expect", &php_expect_wrapper TSRMLS_CC);
200
201 REGISTER_LONG_CONSTANT("EXP_GLOB", exp_glob, CONST_CS | CONST_PERSISTENT);
202 REGISTER_LONG_CONSTANT("EXP_EXACT", exp_exact, CONST_CS | CONST_PERSISTENT);
203 REGISTER_LONG_CONSTANT("EXP_REGEXP", exp_regexp, CONST_CS | CONST_PERSISTENT);
204 REGISTER_LONG_CONSTANT("EXP_EOF", EXP_EOF, CONST_CS | CONST_PERSISTENT);
205 REGISTER_LONG_CONSTANT("EXP_TIMEOUT", EXP_TIMEOUT, CONST_CS | CONST_PERSISTENT);
206 REGISTER_LONG_CONSTANT("EXP_FULLBUFFER", EXP_FULLBUFFER, CONST_CS | CONST_PERSISTENT);
207
208 REGISTER_INI_ENTRIES();
209
210 Tcl_Interp *interp = Tcl_CreateInterp();
211 if (Tcl_Init(interp) == TCL_ERROR) {
212 php_error_docref (NULL TSRMLS_CC, E_ERROR,
213 "Unable to initialize TCL interpreter: %s", Tcl_GetStringResult (interp));
214 return FAILURE;
215 }
216 if (Expect_Init(interp) == TCL_ERROR) {
217 php_error_docref (NULL TSRMLS_CC, E_ERROR,
218 "Unable to initialize Expect: %s", Tcl_GetStringResult (interp));
219 return FAILURE;
220 }
221
222 return SUCCESS;
223 }
224 /* }}} */
225
226
227 /* {{{ PHP_MSHUTDOWN_FUNCTION */
228 PHP_MSHUTDOWN_FUNCTION(expect)
229 {
230 php_unregister_url_stream_wrapper("expect" TSRMLS_CC);
231
232 UNREGISTER_INI_ENTRIES();
233
234 return SUCCESS;
235 }
236 /* }}} */
237
238
239 /* {{{ PHP_MINFO_FUNCTION */
240 PHP_MINFO_FUNCTION(expect)
241 {
242 php_info_print_table_start();
243 php_info_print_table_header(2, "Expect support", "enabled");
244 php_info_print_table_row(2, "Version", PHP_EXPECT_VERSION);
245 php_info_print_table_row(2, "Stream wrapper support", "expect://");
246 php_info_print_table_end();
247
248 DISPLAY_INI_ENTRIES();
249 }
250 /* }}} */
251
252
253 /* {{{
254 * proto resource expect_popen (string command)
255 */
256 PHP_FUNCTION(expect_popen)
257 {
258 #if PHP_MAJOR_VERSION >= 7
259 zend_string *command = NULL;
260 #else
261 char *command = NULL;
262 #endif
263 int command_len;
264 FILE *fp;
265 php_stream *stream = NULL;
266 #if PHP_MAJOR_VERSION >= 7
267 zval z_pid;
268 #else
269 zval *z_pid;
270 #endif
271
272 if (ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
273
274 #if PHP_MAJOR_VERSION >= 7
275 if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "S", &command) == FAILURE) {
276 #else
277 if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
278 #endif
279 return;
280 }
281
282 #if PHP_MAJOR_VERSION >= 7
283 if ((fp = exp_popen (ZSTR_VAL(command))) != NULL) {
284 #else
285 if ((fp = exp_popen (command)) != NULL) {
286 #endif
287 stream = php_stream_fopen_from_pipe (fp, "");
288 }
289 if (!stream) {
290 RETURN_FALSE;
291 }
292
293 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
294
295 #if PHP_MAJOR_VERSION >= 7
296 ZVAL_LONG (&z_pid, exp_pid);
297 #else
298 MAKE_STD_ZVAL (z_pid);
299 ZVAL_LONG (z_pid, exp_pid);
300 #endif
301 stream->wrapperdata = z_pid;
302
303 php_stream_to_zval(stream, return_value);
304 }
305 /* }}} */
306
307
308 /* {{{
309 * proto mixed expect_expectl (resource stream, array expect_cases [, array match])
310 */
311 PHP_FUNCTION(expect_expectl)
312 {
313 struct exp_case *ecases, *ecases_ptr, matchedcase;
314 #if PHP_MAJOR_VERSION >= 7
315 zval *z_stream, *z_cases, *z_match=NULL, *z_case, *z_value;
316 #else
317 zval *z_stream, *z_cases, *z_match=NULL, **z_case, **z_value;
318 #endif
319 php_stream *stream;
320 int fd, argc;
321 zend_ulong key;
322
323 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3) { WRONG_PARAM_COUNT; }
324
325 if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "ra|z", &z_stream, &z_cases, &z_match) == FAILURE) {
326 return;
327 }
328
329 #if PHP_MAJOR_VERSION >= 7
330 php_stream_from_zval (stream, z_stream);
331 #else
332 php_stream_from_zval (stream, &z_stream);
333 #endif
334
335 #if PHP_MAJOR_VERSION >= 7
336 if (!&(stream->wrapperdata)) {
337 #else
338 if (!stream->wrapperdata) {
339 #endif
340 php_error_docref (NULL TSRMLS_CC, E_ERROR, "supplied argument is not a valid stream resource");
341 return;
342 }
343
344 if (php_stream_cast (stream, PHP_STREAM_AS_FD, (void*)&fd, REPORT_ERRORS) != SUCCESS || fd < 0) {
345 return;
346 }
347
348 argc = zend_hash_num_elements (Z_ARRVAL_P(z_cases));
349 ecases = (struct exp_case*) safe_emalloc (argc + 1, sizeof(struct exp_case), 0);
350 ecases_ptr = ecases;
351
352 zend_hash_internal_pointer_reset (Z_ARRVAL_P(z_cases));
353
354 #if PHP_MAJOR_VERSION >= 7
355 while ((z_case = zend_hash_get_current_data (Z_ARRVAL_P(z_cases))) != NULL)
356 {
357 zval *z_pattern, *z_exp_type;
358 zend_hash_get_current_key(Z_ARRVAL_P(z_cases), NULL, &key);
359
360 if (Z_TYPE_P(z_case) != IS_ARRAY) {
361 #else
362 while (zend_hash_get_current_data (Z_ARRVAL_P(z_cases), (void **)&z_case) == SUCCESS)
363 {
364 zval **z_pattern, **z_exp_type;
365 zend_hash_get_current_key(Z_ARRVAL_P(z_cases), NULL, &key, 0);
366
367 if (Z_TYPE_PP(z_case) != IS_ARRAY) {
368 #endif
369 efree (ecases);
370 php_error_docref (NULL TSRMLS_CC, E_ERROR, "expect case must be an array");
371 return;
372 }
373
374 ecases_ptr->re = NULL;
375 ecases_ptr->type = exp_glob;
376
377 /* Gather pattern */
378 #if PHP_MAJOR_VERSION >= 7
379 if ((z_pattern = zend_hash_index_find(Z_ARRVAL_P(z_case), 0)) == NULL) {
380 #else
381 if (zend_hash_index_find(Z_ARRVAL_PP(z_case), 0, (void **)&z_pattern) != SUCCESS) {
382 #endif
383 efree (ecases);
384 php_error_docref (NULL TSRMLS_CC, E_ERROR, "missing parameter for pattern at index: 0");
385 return;
386 }
387 #if PHP_MAJOR_VERSION >= 7
388 if (Z_TYPE_P(z_pattern) != IS_STRING) {
389 #else
390 if (Z_TYPE_PP(z_pattern) != IS_STRING) {
391 #endif
392 efree (ecases);
393 php_error_docref (NULL TSRMLS_CC, E_ERROR, "pattern must be of string type");
394 return;
395 }
396 #if PHP_MAJOR_VERSION >= 7
397 ecases_ptr->pattern = Z_STRVAL_P(z_pattern);
398 #else
399 ecases_ptr->pattern = Z_STRVAL_PP(z_pattern);
400 #endif
401
402 /* Gather value */
403 #if PHP_MAJOR_VERSION >= 7
404 if (zend_hash_index_find(Z_ARRVAL_P(z_case), 1) == NULL) {
405 #else
406 if (zend_hash_index_find(Z_ARRVAL_PP(z_case), 1, (void **)&z_value) != SUCCESS) {
407 #endif
408 efree (ecases);
409 php_error_docref (NULL TSRMLS_CC, E_ERROR, "missing parameter for value at index: 1");
410 return;
411 }
412 ecases_ptr->value = key;
413
414 /* Gather expression type (optional, default: EXPECT_GLOB) */
415 #if PHP_MAJOR_VERSION >= 7
416 if ((z_exp_type = zend_hash_index_find(Z_ARRVAL_P(z_case), 2)) != NULL) {
417 if (Z_TYPE_P(z_exp_type) != IS_LONG) {
418 #else
419 if (zend_hash_index_find(Z_ARRVAL_PP(z_case), 2, (void **)&z_exp_type) == SUCCESS) {
420 if (Z_TYPE_PP(z_exp_type) != IS_LONG) {
421 #endif
422 efree (ecases);
423 php_error_docref (NULL TSRMLS_CC, E_ERROR, "expression type must be an integer constant");
424 return;
425 }
426 #if PHP_MAJOR_VERSION >= 7
427 if (Z_LVAL_P(z_exp_type) != exp_glob && Z_LVAL_P(z_exp_type) != exp_exact && Z_LVAL_P(z_exp_type) != exp_regexp) {
428 #else
429 if (Z_LVAL_PP(z_exp_type) != exp_glob && Z_LVAL_PP(z_exp_type) != exp_exact && Z_LVAL_PP(z_exp_type) != exp_regexp) {
430 #endif
431 efree (ecases);
432 php_error_docref (NULL TSRMLS_CC, E_ERROR, "expression type must be either EXPECT_GLOB, EXPECT_EXACT or EXPECT_REGEXP");
433 return;
434 }
435 #if PHP_MAJOR_VERSION >= 7
436 ecases_ptr->type = Z_LVAL_P(z_exp_type);
437 #else
438 ecases_ptr->type = Z_LVAL_PP(z_exp_type);
439 #endif
440 }
441
442 ecases_ptr++;
443 zend_hash_move_forward(Z_ARRVAL_P(z_cases));
444 }
445 ecases_ptr->pattern = NULL;
446 ecases_ptr->re = NULL;
447 ecases_ptr->value = 0;
448 ecases_ptr->type = exp_end;
449
450 int exp_retval = exp_expectv (fd, ecases);
451 int case_found = 0;
452 if (exp_retval >= 0) {
453 key = exp_retval;
454
455 int exp_match_len = exp_match_end - exp_match;
456 if (z_match && exp_match && exp_match_len > 0) {
457 char *tmp = (char *)emalloc (sizeof(char) * (exp_match_len + 1));
458 strlcpy (tmp, exp_match, exp_match_len + 1);
459 zval_dtor (z_match);
460 array_init(z_match);
461 #if PHP_MAJOR_VERSION >= 7
462 add_index_string(z_match, 0, tmp);
463 #else
464 add_index_string(z_match, 0, tmp, 1);
465 #endif
466 /* Get case that was matched */
467 matchedcase = ecases[key];
468 /* If there are subpattern matches ... */
469 if (matchedcase.re != NULL && matchedcase.re->startp != NULL) {
470 int i;
471 /* iterate across all possible 9 subpatterns (a limitation of libexpect)
472 and add matching substring to matches array */
473 for (i = 1; i <= 9; i++) {
474 if (matchedcase.re->startp[i] != NULL) {
475 int sub_match_len = matchedcase.re->endp[i] - matchedcase.re->startp[i];
476 char *sub_match = (char *)emalloc (sizeof(char) * (sub_match_len + 1));
477 strlcpy (sub_match, matchedcase.re->startp[i], sub_match_len + 1);
478 #if PHP_MAJOR_VERSION >= 7
479 add_next_index_string(z_match, sub_match);
480 #else
481 add_next_index_string(z_match, sub_match, 1);
482 #endif
483 efree (sub_match);
484 }
485 }
486 }
487 efree (tmp);
488 }
489
490 #if PHP_MAJOR_VERSION >= 7
491 if ((z_case = zend_hash_index_find (Z_ARRVAL_P(z_cases), key)) != NULL) {
492 if ((z_value = zend_hash_index_find(Z_ARRVAL_P(z_case), 1)) != NULL) {
493 *return_value = *z_value;
494 #else
495 if (zend_hash_index_find (Z_ARRVAL_P(z_cases), key, (void **)&z_case) == SUCCESS) {
496 if (zend_hash_index_find(Z_ARRVAL_PP(z_case), 1, (void **)&z_value) == SUCCESS) {
497 *return_value = **z_value;
498 #endif
499 zval_copy_ctor (return_value);
500 case_found = 1;
501 }
502 }
503 }
504
505 // Free compiled patterns:
506 ecases_ptr = ecases;
507 while (ecases_ptr != NULL && ecases_ptr->type != exp_end) {
508 if (ecases_ptr->re != NULL) {
509 free(ecases_ptr->re);
510 }
511 ecases_ptr++;
512 }
513 efree (ecases);
514
515 if (!case_found) {
516 RETURN_LONG(exp_retval);
517 }
518 }
519 /* }}} */
520
521
522 /*
523 * Local variables:
524 * tab-width: 4
525 * c-basic-offset: 4
526 * End:
527 * vim600: noet sw=4 ts=4 fdm=marker
528 * vim<600: noet sw=4 ts=4
529 */
530