xref: /PHP-5.5/sapi/milter/php_milter.c (revision 73c1be26)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 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: Harald Radi <phanto@php.net>                                 |
16    |         Parts based on CGI SAPI Module by                            |
17    |         Rasmus Lerdorf, Stig Bakken and Zeev Suraski                 |
18    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 #include "php.h"
24 #include "php_globals.h"
25 #include "php_variables.h"
26 #include "zend_modules.h"
27 
28 #ifndef ZTS
29 #error SRM sapi module is only useable in thread-safe mode
30 #endif
31 
32 #include "SAPI.h"
33 
34 #include <stdio.h>
35 #include "php.h"
36 #if HAVE_SYS_TIME_H
37 #include <sys/time.h>
38 #endif
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #if HAVE_SIGNAL_H
43 #include <signal.h>
44 #endif
45 #if HAVE_SETLOCALE
46 #include <locale.h>
47 #endif
48 #include "zend.h"
49 #include "zend_extensions.h"
50 #include "php_ini.h"
51 #include "php_globals.h"
52 #include "php_main.h"
53 #include "fopen_wrappers.h"
54 #include "ext/standard/php_standard.h"
55 
56 #ifdef __riscos__
57 #include <unixlib/local.h>
58 #endif
59 
60 #include "zend_compile.h"
61 #include "zend_execute.h"
62 #include "zend_highlight.h"
63 #include "zend_indent.h"
64 
65 #include "libmilter/mfapi.h"
66 
67 #include "php_getopt.h"
68 
69 #define OPTSTRING "ac:d:Def:hnp:vVz:?"
70 #define MG(v)  TSRMG(milter_globals_id, zend_milter_globals *, v)
71 
72 #define IS_NONE "%s(): This function must not be called outside of a milter callback function's scope"
73 #define NOT_EOM "%s(): This function can only be used inside the milter_eom callback's scope"
74 #define NOT_INIT "%s(): This function can only be used inside the milter_init callback's scope"
75 
76 #define MLFI_NONE		0
77 #define MLFI_CONNECT	1
78 #define MLFI_HELO		2
79 #define MLFI_ENVFROM	3
80 #define MLFI_ENVRCPT	4
81 #define MLFI_HEADER		5
82 #define MLFI_EOH		6
83 #define MLFI_BODY		7
84 #define MLFI_EOM		8
85 #define MLFI_ABORT		9
86 #define MLFI_CLOSE		10
87 #define MLFI_INIT		11
88 
89 /* {{{ globals
90  */
91 extern char *ap_php_optarg;
92 extern int ap_php_optind;
93 
94 static int flag_debug=0;
95 static char *filename = NULL;
96 
97 /* per thread */
98 ZEND_BEGIN_MODULE_GLOBALS(milter)
99 	SMFICTX *ctx;
100 	int state;
101 	int initialized;
102 ZEND_END_MODULE_GLOBALS(milter)
103 
ZEND_DECLARE_MODULE_GLOBALS(milter)104 ZEND_DECLARE_MODULE_GLOBALS(milter)
105 /* }}} */
106 
107 /* this method is called only once when the milter starts */
108 /* {{{ Init Milter
109 */
110 static int mlfi_init()
111 {
112 	int ret = 0;
113 	zend_file_handle file_handle;
114 	zval function_name, retval;
115 	int status;
116 	TSRMLS_FETCH();
117 
118 	/* request startup */
119 	if (php_request_startup(TSRMLS_C)==FAILURE) {
120 		SG(headers_sent) = 1;
121 		SG(request_info).no_headers = 1;
122 		php_request_shutdown((void *) 0);
123 
124 		return -1;
125 	}
126 
127 	/* disable headers */
128 	SG(headers_sent) = 1;
129 	SG(request_info).no_headers = 1;
130 
131 	if (filename == NULL) {
132 		php_printf("No input file specified");
133 		return SMFIS_TEMPFAIL;
134 	}
135 
136 	if (!(file_handle.handle.fp = VCWD_FOPEN(filename, "rb"))) {
137 		php_printf("Could not open input file: %s\n", filename);
138 		return SMFIS_TEMPFAIL;
139 	}
140 
141 	file_handle.type = ZEND_HANDLE_FP;
142 	file_handle.filename = filename;
143 	file_handle.free_filename = 0;
144 	file_handle.opened_path = NULL;
145 
146 	php_execute_script(&file_handle TSRMLS_CC);
147 
148 	/* call userland */
149 	INIT_ZVAL(function_name);
150 
151 	ZVAL_STRING(&function_name, "milter_init", 0);
152 
153 	/* set the milter context for possible use in API functions */
154 	MG(state) = MLFI_INIT;
155 
156 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
157 
158 	MG(state) = MLFI_NONE;
159 	MG(initialized) = 1;
160 
161 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
162 		ret = Z_LVAL(retval);
163 	}
164 
165 	php_request_shutdown((void *) 0);
166 
167 	return ret;
168 }
169 /* }}} */
170 
171 /* {{{ Milter callback functions
172  */
173 
174 /* connection info filter, is called whenever sendmail connects to the milter */
175 /* {{{ mlfi_connect()
176 */
mlfi_connect(SMFICTX * ctx,char * hostname,_SOCK_ADDR * hostaddr)177 static sfsistat	mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
178 {
179 	zend_file_handle file_handle;
180 	zval function_name, retval, *param[1];
181 	int status;
182 	TSRMLS_FETCH();
183 
184 	/* request startup */
185 	if (php_request_startup(TSRMLS_C)==FAILURE) {
186 		SG(headers_sent) = 1;
187 		SG(request_info).no_headers = 1;
188 		php_request_shutdown((void *) 0);
189 
190 		return SMFIS_TEMPFAIL;
191 	}
192 
193 	/* disable headers */
194 	SG(headers_sent) = 1;
195 	SG(request_info).no_headers = 1;
196 
197 	if (filename == NULL) {
198 		php_printf("No input file specified");
199 		return SMFIS_TEMPFAIL;
200 	}
201 
202 	if (!(file_handle.handle.fp = VCWD_FOPEN(filename, "rb"))) {
203 		php_printf("Could not open input file: %s\n", filename);
204 		return SMFIS_TEMPFAIL;
205 	}
206 
207 	file_handle.type = ZEND_HANDLE_FP;
208 	file_handle.filename = filename;
209 	file_handle.free_filename = 0;
210 	file_handle.opened_path = NULL;
211 
212 	php_execute_script(&file_handle TSRMLS_CC);
213 
214 	/* call userland */
215 	INIT_ZVAL(function_name);
216 
217 	ALLOC_ZVAL(param[0]);
218 	INIT_PZVAL(param[0]);
219 
220 	ZVAL_STRING(&function_name, "milter_connect", 0);
221 	ZVAL_STRING(param[0], hostname, 1);
222 
223 	/* set the milter context for possible use in API functions */
224 	MG(ctx) = ctx;
225 	MG(state) = MLFI_CONNECT;
226 
227 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
228 
229 	MG(state) = MLFI_NONE;
230 	zval_ptr_dtor(param);
231 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
232 		return Z_LVAL(retval);
233 	}
234 
235 	return SMFIS_CONTINUE;
236 }
237 /* }}} */
238 
239 /* SMTP HELO command filter */
240 /* {{{ mlfi_helo()
241 */
mlfi_helo(SMFICTX * ctx,char * helohost)242 static sfsistat mlfi_helo(SMFICTX *ctx, char *helohost)
243 {
244 	zval function_name, retval, *param[1];
245 	int status;
246 	TSRMLS_FETCH();
247 
248 	/* call userland */
249 	INIT_ZVAL(function_name);
250 
251 	ALLOC_ZVAL(param[0]);
252 	INIT_PZVAL(param[0]);
253 
254 	ZVAL_STRING(&function_name, "milter_helo", 0);
255 	ZVAL_STRING(param[0], helohost, 1);
256 
257 	/* set the milter context for possible use in API functions */
258 	MG(ctx) = ctx;
259 	MG(state) = MLFI_HELO;
260 
261 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
262 
263 	MG(state) = MLFI_NONE;
264 	zval_ptr_dtor(param);
265 
266 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
267 		return Z_LVAL(retval);
268 	}
269 
270 	return SMFIS_CONTINUE;
271 }
272 /* }}} */
273 
274 /* envelope sender filter */
275 /* {{{ mlfi_envform()
276 */
mlfi_envfrom(SMFICTX * ctx,char ** argv)277 static sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv)
278 {
279 	zval function_name, retval, *param[1];
280 	int status;
281 	TSRMLS_FETCH();
282 
283 	/* call userland */
284 	INIT_ZVAL(function_name);
285 
286 	ALLOC_ZVAL(param[0]);
287 	INIT_PZVAL(param[0]);
288 
289 	ZVAL_STRING(&function_name, "milter_envfrom", 0);
290 	array_init(param[0]);
291 
292 	while (*argv) {
293 		add_next_index_string(param[0], *argv, 1);
294 		argv++;
295 	}
296 
297 	/* set the milter context for possible use in API functions */
298 	MG(ctx) = ctx;
299 	MG(state) = MLFI_ENVFROM;
300 
301 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
302 
303 	MG(state) = MLFI_NONE;
304 	zval_ptr_dtor(param);
305 
306 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
307 		return Z_LVAL(retval);
308 	}
309 
310 	return SMFIS_CONTINUE;
311 }
312 /* }}} */
313 
314 /* envelope recipient filter */
315 /* {{{ mlfi_envrcpt()
316 */
mlfi_envrcpt(SMFICTX * ctx,char ** argv)317 static sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv)
318 {
319 	zval function_name, retval, *param[1];
320 	int status;
321 	TSRMLS_FETCH();
322 
323 	/* call userland */
324 	INIT_ZVAL(function_name);
325 
326 	ALLOC_ZVAL(param[0]);
327 	INIT_PZVAL(param[0]);
328 
329 	ZVAL_STRING(&function_name, "milter_envrcpt", 0);
330 	array_init(param[0]);
331 
332 	while (*argv) {
333 		add_next_index_string(param[0], *argv, 1);
334 		argv++;
335 	}
336 
337 	/* set the milter context for possible use in API functions */
338 	MG(ctx) = ctx;
339 	MG(state) = MLFI_ENVRCPT;
340 
341 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
342 
343 	MG(state) = MLFI_NONE;
344 
345 	zval_ptr_dtor(param);
346 
347 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
348 		return Z_LVAL(retval);
349 	}
350 
351 	return SMFIS_CONTINUE;
352 }
353 /* }}} */
354 
355 /* header filter */
356 /* {{{ mlfi_header()
357 */
mlfi_header(SMFICTX * ctx,char * headerf,char * headerv)358 static sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
359 {
360 	zval function_name, retval, *param[2];
361 	int status;
362 	TSRMLS_FETCH();
363 
364 	/* call userland */
365 	INIT_ZVAL(function_name);
366 
367 	ALLOC_ZVAL(param[0]);
368 	ALLOC_ZVAL(param[1]);
369 	INIT_PZVAL(param[0]);
370 	INIT_PZVAL(param[1]);
371 
372 	ZVAL_STRING(&function_name, "milter_header", 0);
373 	ZVAL_STRING(param[0], headerf, 1);
374 	ZVAL_STRING(param[1], headerv, 1);
375 
376 	/* set the milter context for possible use in API functions */
377 	MG(ctx) = ctx;
378 	MG(state) = MLFI_HEADER;
379 
380 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 2, param TSRMLS_CC);
381 
382 	MG(state) = MLFI_NONE;
383 
384 	zval_ptr_dtor(&param[0]);
385 	zval_ptr_dtor(&param[1]);
386 
387 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
388 		return Z_LVAL(retval);
389 	}
390 
391 	return SMFIS_CONTINUE;
392 }
393 /* }}} */
394 
395 /* end of header */
396 /* {{{ mlfi_eoh()
397 */
mlfi_eoh(SMFICTX * ctx)398 static sfsistat mlfi_eoh(SMFICTX *ctx)
399 {
400 	zval function_name, retval;
401 	int status;
402 	TSRMLS_FETCH();
403 
404 	/* call userland */
405 	INIT_ZVAL(function_name);
406 	ZVAL_STRING(&function_name, "milter_eoh", 0);
407 
408 	/* set the milter context for possible use in API functions */
409 	MG(ctx) = ctx;
410 	MG(state) = MLFI_EOH;
411 
412 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
413 
414 	MG(state) = MLFI_NONE;
415 
416 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
417 		return Z_LVAL(retval);
418 	}
419 
420 	return SMFIS_CONTINUE;
421 }
422 /* }}} */
423 
424 /* body block */
425 /* {{{ mlfi_body()
426 */
mlfi_body(SMFICTX * ctx,u_char * bodyp,size_t len)427 static sfsistat mlfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
428 {
429 	zval function_name, retval, *param[1];
430 	int status;
431 	TSRMLS_FETCH();
432 
433 	/* call userland */
434 	INIT_ZVAL(function_name);
435 
436 	ALLOC_ZVAL(param[0]);
437 	INIT_PZVAL(param[0]);
438 
439 	ZVAL_STRING(&function_name, "milter_body", 0);
440 	ZVAL_STRINGL(param[0], (char*)bodyp, len, 1); /*alex*/
441 
442 	/* set the milter context for possible use in API functions */
443 	MG(ctx) = ctx;
444 	MG(state) = MLFI_BODY;
445 
446 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
447 
448 	MG(state) = MLFI_NONE;
449 
450 	zval_ptr_dtor(param);
451 
452 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
453 		return Z_LVAL(retval);
454 	}
455 
456 	return SMFIS_CONTINUE;
457 }
458 /* }}} */
459 
460 /* end of message */
461 /* {{{ mlfi_eom()
462 */
mlfi_eom(SMFICTX * ctx)463 static sfsistat mlfi_eom(SMFICTX *ctx)
464 {
465 	zval function_name, retval;
466 	int status;
467 	TSRMLS_FETCH();
468 
469 	/* call userland */
470 	INIT_ZVAL(function_name);
471 	ZVAL_STRING(&function_name, "milter_eom", 0);
472 
473 	/* set the milter context for possible use in API functions */
474 	MG(ctx) = ctx;
475 	MG(state) = MLFI_EOM;
476 
477 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
478 
479 	MG(state) = MLFI_NONE;
480 
481 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
482 		return Z_LVAL(retval);
483 	}
484 
485 	return SMFIS_CONTINUE;
486 }
487 /* }}} */
488 
489 /* message aborted */
490 /* {{{ mlfi_abort()
491 */
mlfi_abort(SMFICTX * ctx)492 static sfsistat mlfi_abort(SMFICTX *ctx)
493 {
494 	zval function_name, retval;
495 	int status;
496 	TSRMLS_FETCH();
497 
498 	/* call userland */
499 	INIT_ZVAL(function_name);
500 	ZVAL_STRING(&function_name, "milter_abort", 0);
501 
502 	/* set the milter context for possible use in API functions */
503 	MG(ctx) = ctx;
504 	MG(state) = MLFI_ABORT;
505 
506 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
507 
508 	MG(state) = MLFI_NONE;
509 
510 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
511 		return Z_LVAL(retval);
512 	}
513 
514 	return SMFIS_CONTINUE;
515 }
516 /* }}} */
517 
518 /* connection cleanup */
519 /* {{{ mlfi_close()
520 */
mlfi_close(SMFICTX * ctx)521 static sfsistat mlfi_close(SMFICTX *ctx)
522 {
523 	int ret = SMFIS_CONTINUE;
524 	zval function_name, retval;
525 	int status;
526 	TSRMLS_FETCH();
527 
528 	if (!SG(sapi_started) && SUCCESS != php_request_startup(TSRMLS_C)) {
529 		return ret;
530 	}
531 
532 	/* call userland */
533 	INIT_ZVAL(function_name);
534 	ZVAL_STRING(&function_name, "milter_close", 0);
535 
536 	/* set the milter context for possible use in API functions */
537 	MG(ctx) = ctx;
538 	MG(state) = MLFI_CLOSE;
539 
540 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
541 
542 	MG(state) = MLFI_NONE;
543 
544 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
545 		ret = Z_LVAL(retval);
546 	}
547 
548 	php_request_shutdown((void *) 0);
549 
550 	return ret;
551 }
552 /* }}} */
553 /* }}} */
554 
555 /* {{{ Milter entry struct
556  */
557 static struct smfiDesc smfilter = {
558     "php-milter",	/* filter name */
559     SMFI_VERSION,   /* version code -- leave untouched */
560     0,				/* flags */
561     mlfi_connect,	/* info filter callback */
562     mlfi_helo,		/* HELO filter callback */
563     mlfi_envfrom,	/* envelope filter callback */
564     mlfi_envrcpt,	/* envelope recipient filter callback */
565     mlfi_header,	/* header filter callback */
566     mlfi_eoh,		/* end of header callback */
567     mlfi_body,		/* body filter callback */
568     mlfi_eom,		/* end of message callback */
569     mlfi_abort,		/* message aborted callback */
570     mlfi_close,		/* connection cleanup callback */
571 };
572 /* }}} */
573 
574 /* {{{ PHP Milter API
575  */
576 
577 /* {{{ proto void smfi_setflags(long flags)
578    Sets the flags describing the actions the filter may take. */
PHP_FUNCTION(smfi_setflags)579 PHP_FUNCTION(smfi_setflags)
580 {
581 	long flags;
582 
583 	/* valid only in the init callback */
584 	if (MG(state) != MLFI_INIT) {
585 		php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
586 	} else if (zend_parse_parameters(1 TSRMLS_CC, "l", &flags) == SUCCESS) {
587 		flags = flags & (SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT);
588 		smfilter.xxfi_flags = flags;
589 	}
590 }
591 /* }}} */
592 
593 /* {{{ proto void smfi_settimeout(long timeout)
594    Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket. */
PHP_FUNCTION(smfi_settimeout)595 PHP_FUNCTION(smfi_settimeout)
596 {
597 	long timeout;
598 
599 	/* valid only in the init callback */
600 	if (MG(state) != MLFI_INIT) {
601 		php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
602 	} else if (zend_parse_parameters(1 TSRMLS_CC, "l", &timeout) == SUCCESS) {
603 		smfi_settimeout(timeout);
604 	}
605 }
606 /* }}} */
607 
608 /* {{{ proto string smfi_getsymval(string macro)
609    Returns the value of the given macro or NULL if the macro is not defined. */
PHP_FUNCTION(smfi_getsymval)610 PHP_FUNCTION(smfi_getsymval)
611 {
612 	char *symname, *ret;
613 	int len;
614 
615 	/* valid in any callback */
616 	if (MG(state) == MLFI_NONE) {
617 		php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
618 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &symname, &len) == SUCCESS) {
619 		if ((ret = smfi_getsymval(MG(ctx), symname)) != NULL) {
620 			RETURN_STRING(ret, 1);
621 		}
622 	}
623 
624 	RETURN_NULL();
625 }
626 /* }}} */
627 
628 /* {{{ proto bool smfi_setreply(string rcode, string xcode, string message)
629    Directly set the SMTP error reply code for this connection.
630    This code will be used on subsequent error replies resulting from actions taken by this filter. */
PHP_FUNCTION(smfi_setreply)631 PHP_FUNCTION(smfi_setreply)
632 {
633 	char *rcode, *xcode, *message;
634 	int len;
635 
636 	/* valid in any callback */
637 	if (MG(state) == MLFI_NONE) {
638 		php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
639 	} else if (zend_parse_parameters(3 TSRMLS_CC, "sss", &rcode, &len, &xcode, &len, &message, &len) == SUCCESS) {
640 		if (smfi_setreply(MG(ctx), rcode, xcode, message) == MI_SUCCESS) {
641 			RETURN_TRUE;
642 		}
643 	}
644 
645 	RETURN_FALSE;
646 }
647 /* }}} */
648 
649 /* {{{ proto bool smfi_addheader(string headerf, string headerv)
650    Adds a header to the current message. */
PHP_FUNCTION(smfi_addheader)651 PHP_FUNCTION(smfi_addheader)
652 {
653 	char *f, *v;
654 	int len;
655 
656 	/* valid only in milter_eom */
657 	if (MG(state) != MLFI_EOM) {
658 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
659 	} else if (zend_parse_parameters(2 TSRMLS_CC, "ss", &f, &len, &v, &len) == SUCCESS) {
660 		if (smfi_addheader(MG(ctx), f, v) == MI_SUCCESS) {
661 			RETURN_TRUE;
662 		}
663 	}
664 
665 	RETURN_FALSE;
666 }
667 /* }}} */
668 
669 /* {{{ proto bool smfi_chgheader(string headerf, string headerv)
670    Changes a header's value for the current message. */
PHP_FUNCTION(smfi_chgheader)671 PHP_FUNCTION(smfi_chgheader)
672 {
673 	char *f, *v;
674 	long idx;
675 	int len;
676 
677 	/* valid only in milter_eom */
678 	if (MG(state) != MLFI_EOM) {
679 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
680 	} else if (zend_parse_parameters(3 TSRMLS_CC, "sls", &f, &len, &idx, &v, &len) == SUCCESS) {
681 		if (smfi_chgheader(MG(ctx), f, idx, v) == MI_SUCCESS) {
682 			RETURN_TRUE;
683 		}
684 	}
685 
686 	RETURN_FALSE;
687 }
688 /* }}} */
689 
690 /* {{{ proto bool smfi_addrcpt(string rcpt)
691    Add a recipient to the message envelope. */
PHP_FUNCTION(smfi_addrcpt)692 PHP_FUNCTION(smfi_addrcpt)
693 {
694 	char *rcpt;
695 	int len;
696 
697 	/* valid only in milter_eom */
698 	if (MG(state) != MLFI_EOM) {
699 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
700 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
701 		if (smfi_addrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
702 			RETURN_TRUE;
703 		}
704 	}
705 
706 	RETURN_FALSE;
707 }
708 /* }}} */
709 
710 /* {{{ proto bool smfi_delrcpt(string rcpt)
711    Removes the named recipient from the current message's envelope. */
PHP_FUNCTION(smfi_delrcpt)712 PHP_FUNCTION(smfi_delrcpt)
713 {
714 	char *rcpt;
715 	int len;
716 
717 	/* valid only in milter_eom */
718 	if (MG(state) != MLFI_EOM) {
719 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
720 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
721 		if (smfi_delrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
722 			RETURN_TRUE;
723 		}
724 	}
725 
726 	RETURN_FALSE;
727 }
728 /* }}} */
729 
730 /* {{{ proto bool smfi_replacebody(string body)
731    Replaces the body of the current message. If called more than once,
732    subsequent calls result in data being appended to the new body. */
PHP_FUNCTION(smfi_replacebody)733 PHP_FUNCTION(smfi_replacebody)
734 {
735 	char *body;
736 	int len;
737 
738 	/* valid only in milter_eom */
739 	if (MG(state) != MLFI_EOM) {
740 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
741 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &body, &len) == SUCCESS) {
742 		if (smfi_replacebody(MG(ctx), (u_char*)body, len) == MI_SUCCESS) {
743 			RETURN_TRUE;
744 		}
745 	}
746 
747 	RETURN_FALSE;
748 }
749 /* }}} */
750 
751 /* {{{ PHP_MINIT_FUNCTION
752  */
PHP_MINIT_FUNCTION(milter)753 PHP_MINIT_FUNCTION(milter)
754 {
755 	REGISTER_LONG_CONSTANT("SMFIS_CONTINUE",	SMFIS_CONTINUE,	CONST_CS | CONST_PERSISTENT);
756 	REGISTER_LONG_CONSTANT("SMFIS_REJECT",		SMFIS_REJECT,	CONST_CS | CONST_PERSISTENT);
757 	REGISTER_LONG_CONSTANT("SMFIS_DISCARD",		SMFIS_DISCARD,	CONST_CS | CONST_PERSISTENT);
758 	REGISTER_LONG_CONSTANT("SMFIS_ACCEPT",		SMFIS_ACCEPT,	CONST_CS | CONST_PERSISTENT);
759 	REGISTER_LONG_CONSTANT("SMFIS_TEMPFAIL",	SMFIS_TEMPFAIL,	CONST_CS | CONST_PERSISTENT);
760 
761 	REGISTER_LONG_CONSTANT("SMFIF_ADDHDRS",		SMFIF_ADDHDRS,	CONST_CS | CONST_PERSISTENT);
762 	REGISTER_LONG_CONSTANT("SMFIF_CHGHDRS",		SMFIF_CHGHDRS,	CONST_CS | CONST_PERSISTENT);
763 	REGISTER_LONG_CONSTANT("SMFIF_CHGBODY",		SMFIF_CHGBODY,	CONST_CS | CONST_PERSISTENT);
764 	REGISTER_LONG_CONSTANT("SMFIF_ADDRCPT",		SMFIF_ADDRCPT,	CONST_CS | CONST_PERSISTENT);
765 	REGISTER_LONG_CONSTANT("SMFIF_DELRCPT",		SMFIF_DELRCPT,	CONST_CS | CONST_PERSISTENT);
766 
767 	ZEND_INIT_MODULE_GLOBALS(milter, NULL, NULL);
768 
769 	MG(state) = MLFI_NONE;
770 	MG(initialized) = 0;
771 	return SUCCESS;
772 }
773 /* }}} */
774 
775 /* {{{ PHP_MINFO_FUNCTION
776  */
PHP_MINFO_FUNCTION(milter)777 PHP_MINFO_FUNCTION(milter)
778 {
779 	php_info_print_table_start();
780 	php_info_print_table_header(2, "Milter support", "enabled");
781 	php_info_print_table_end();
782 }
783 /* }}} */
784 /* }}} */
785 
786 /* {{{ arginfo */
787 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setflags, 0, 0, 1)
788 	ZEND_ARG_INFO(0, flags)
789 ZEND_END_ARG_INFO()
790 
791 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_settimeout, 0, 0, 1)
792 	ZEND_ARG_INFO(0, timeout)
793 ZEND_END_ARG_INFO()
794 
795 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_getsymval, 0, 0, 1)
796 	ZEND_ARG_INFO(0, macro)
797 ZEND_END_ARG_INFO()
798 
799 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setreply, 0, 0, 3)
800 	ZEND_ARG_INFO(0, rcode)
801 	ZEND_ARG_INFO(0, xcode)
802 	ZEND_ARG_INFO(0, message)
803 ZEND_END_ARG_INFO()
804 
805 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addheader, 0, 0, 2)
806 	ZEND_ARG_INFO(0, headerf)
807 	ZEND_ARG_INFO(0, headerv)
808 ZEND_END_ARG_INFO()
809 
810 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_chgheader, 0, 0, 2)
811 	ZEND_ARG_INFO(0, headerf)
812 	ZEND_ARG_INFO(0, headerv)
813 ZEND_END_ARG_INFO()
814 
815 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addrcpt, 0, 0, 1)
816 	ZEND_ARG_INFO(0, rcpt)
817 ZEND_END_ARG_INFO()
818 
819 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_delrcpt, 0, 0, 1)
820 	ZEND_ARG_INFO(0, rcpt)
821 ZEND_END_ARG_INFO()
822 
823 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_replacebody, 0, 0, 1)
824 	ZEND_ARG_INFO(0, body)
825 ZEND_END_ARG_INFO()
826 /* }}} */
827 
828 /* {{{ milter_functions[]
829 */
830 const static zend_function_entry milter_functions[] = {
831 	PHP_FE(smfi_setflags, 		arginfo_smfi_setflags)
832 	PHP_FE(smfi_settimeout, 	arginfo_smfi_settimeout)
833 	PHP_FE(smfi_getsymval, 		arginfo_smfi_getsymval)
834 	PHP_FE(smfi_setreply, 		arginfo_smfi_setreply)
835 	PHP_FE(smfi_addheader, 		arginfo_smfi_addheader)
836 	PHP_FE(smfi_chgheader, 		arginfo_smfi_chgheader)
837 	PHP_FE(smfi_addrcpt, 		arginfo_smfi_addrcpt)
838 	PHP_FE(smfi_delrcpt, 		arginfo_smfi_delrcpt)
839 	PHP_FE(smfi_replacebody, 	arginfo_smfi_replacebody)
840 	PHP_FE_END
841 };
842 /* }}} */
843 
844 /* {{{ Zend module entry
845 */
846 static zend_module_entry php_milter_module = {
847 	STANDARD_MODULE_HEADER,
848 	"Milter",
849 	milter_functions,
850 	PHP_MINIT(milter),
851 	NULL,
852 	NULL,
853 	NULL,
854 	PHP_MINFO(milter),
855 	"0.1.0",
856 	STANDARD_MODULE_PROPERTIES
857 };
858 /* }}} */
859 
860 /* {{{ Milter SAPI
861 */
sapi_milter_ub_write(const char * str,uint str_length TSRMLS_DC)862 static int sapi_milter_ub_write(const char *str, uint str_length TSRMLS_DC)
863 {
864 	return str_length;
865 }
866 
sapi_milter_flush(void * server_context)867 static void sapi_milter_flush(void *server_context)
868 {
869 }
870 
sapi_milter_register_variables(zval * track_vars_array TSRMLS_DC)871 static void sapi_milter_register_variables(zval *track_vars_array TSRMLS_DC)
872 {
873 	php_register_variable ("SERVER_SOFTWARE", "Sendmail Milter", track_vars_array TSRMLS_CC);
874 }
875 
sapi_milter_post_read(char * buf,uint count_bytes TSRMLS_DC)876 static int sapi_milter_post_read(char *buf, uint count_bytes TSRMLS_DC)
877 {
878 	return 0;
879 }
880 
sapi_milter_read_cookies(TSRMLS_D)881 static char* sapi_milter_read_cookies(TSRMLS_D)
882 {
883 	return NULL;
884 }
885 
sapi_milter_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)886 static int sapi_milter_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
887 {
888 	return SAPI_HEADER_SENT_SUCCESSFULLY;
889 }
890 
php_milter_startup(sapi_module_struct * sapi_module)891 static int php_milter_startup(sapi_module_struct *sapi_module)
892 {
893 	if (php_module_startup(sapi_module, &php_milter_module, 1) == FAILURE) {
894 		return FAILURE;
895 	}
896 	return SUCCESS;
897 }
898 /* }}} */
899 
900 /* {{{ sapi_module_struct milter_sapi_module
901 */
902 static sapi_module_struct milter_sapi_module = {
903 	"milter",						/* name */
904 	"Sendmail Milter SAPI",			/* pretty name */
905 
906 	php_milter_startup,				/* startup */
907 	php_module_shutdown_wrapper,	/* shutdown */
908 
909 	NULL,							/* activate */
910 	NULL,							/* deactivate */
911 
912 	sapi_milter_ub_write,			/* unbuffered write */
913 	sapi_milter_flush,				/* flush */
914 	NULL,							/* get uid */
915 	NULL,							/* getenv */
916 
917 	php_error,						/* error handler */
918 
919 	NULL,							/* header handler */
920 	sapi_milter_send_headers,		/* send headers handler */
921 	NULL,							/* send header handler */
922 
923 	sapi_milter_post_read,			/* read POST data */
924 	sapi_milter_read_cookies,		/* read Cookies */
925 
926 	sapi_milter_register_variables,	/* register server variables */
927 	NULL,							/* Log message */
928 	NULL,							/* Get request time */
929 	NULL,							/* Child terminate */
930 
931 	NULL,							/* Block interruptions */
932 	NULL,							/* Unblock interruptions */
933 
934 	STANDARD_SAPI_MODULE_PROPERTIES
935 };
936 /* }}} */
937 
938 /****
939 * ripped from cli, has to be cleaned up !
940 */
941 
942 /* {{{ php_milter_usage
943 */
php_milter_usage(char * argv0)944 static void php_milter_usage(char *argv0)
945 {
946 	char *prog;
947 
948 	prog = strrchr(argv0, '/');
949 	if (prog) {
950 		prog++;
951 	} else {
952 		prog = "php-milter";
953 	}
954 
955 	printf(     "Usage: %s [options] [-f] <file> [args...]\n"
956 	            "       %s [options] [-- args...]\n"
957 				"  -a               Run interactively\n"
958 				"  -c <path>|<file> Look for php.ini file in this directory\n"
959 				"  -n               No php.ini file will be used\n"
960 				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
961 				"  -D               run as daemon\n"
962 				"  -e               Generate extended information for debugger/profiler\n"
963 				"  -f <file>        Parse <file>.\n"
964 				"  -h               This help\n"
965 				"  -p <socket>      path to create socket\n"
966 				"  -v               Version number\n"
967 				"  -V <n>           set debug level to n (1 or 2).\n"
968 				"  -z <file>        Load Zend extension <file>.\n"
969 				"  args...          Arguments passed to script. Use -- args when first argument \n"
970 				"                   starts with - or script is read from stdin\n"
971 				, prog, prog);
972 }
973 /* }}} */
974 
define_command_line_ini_entry(char * arg)975 static void define_command_line_ini_entry(char *arg) /* {{{ */
976 {
977 	char *name, *value;
978 
979 	name = arg;
980 	value = strchr(arg, '=');
981 	if (value) {
982 		*value = 0;
983 		value++;
984 	} else {
985 		value = "1";
986 	}
987 	zend_alter_ini_entry(name, strlen(name)+1, value, strlen(value), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
988 }
989 /* }}} */
990 
991 /* {{{ main
992 */
main(int argc,char * argv[])993 int main(int argc, char *argv[])
994 {
995     char *sock = NULL;
996 	int dofork = 0;
997 
998 	int exit_status = SUCCESS;
999 	int c;
1000 /* temporary locals */
1001 	int orig_optind=ap_php_optind;
1002 	char *orig_optarg=ap_php_optarg;
1003 	int interactive=0;
1004 	char *param_error=NULL;
1005 /* end of temporary locals */
1006 
1007 	void ***tsrm_ls;
1008 
1009 #ifdef HAVE_SIGNAL_H
1010 #if defined(SIGPIPE) && defined(SIG_IGN)
1011 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
1012 								that sockets created via fsockopen()
1013 								don't kill PHP if the remote site
1014 								closes it.  in apache|apxs mode apache
1015 								does that for us!  thies@thieso.net
1016 								20000419 */
1017 #endif
1018 #endif
1019 
1020 
1021 	tsrm_startup(1, 1, 0, NULL);
1022 	tsrm_ls = ts_resource(0);
1023 	sapi_startup(&milter_sapi_module);
1024 
1025 	while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
1026 		switch (c) {
1027 		case 'c':
1028 			milter_sapi_module.php_ini_path_override = strdup(ap_php_optarg);
1029 			break;
1030 		case 'n':
1031 			milter_sapi_module.php_ini_ignore = 1;
1032 			break;
1033 		}
1034 	}
1035 	ap_php_optind = orig_optind;
1036 	ap_php_optarg = orig_optarg;
1037 
1038 	milter_sapi_module.executable_location = argv[0];
1039 
1040 
1041 	sapi_module.startup(&milter_sapi_module);
1042 
1043 	zend_first_try {
1044 		while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
1045 			switch (c) {
1046 			case '?':
1047 				php_output_tearup();
1048 				SG(headers_sent) = 1;
1049 				php_milter_usage(argv[0]);
1050 				php_output_teardown();
1051 				exit(1);
1052 				break;
1053 			}
1054 		}
1055 		ap_php_optind = orig_optind;
1056 		ap_php_optarg = orig_optarg;
1057 
1058         /* Set some CLI defaults */
1059 		SG(options) |= SAPI_OPTION_NO_CHDIR;
1060 		zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
1061 		zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
1062 
1063 		zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */
1064 
1065 		while ((c = ap_php_getopt(argc, argv, OPTSTRING)) != -1) {
1066 			switch (c) {
1067 
1068 			case 'a':	/* interactive mode */
1069 				printf("Interactive mode enabled\n\n");
1070 				interactive=1;
1071 				break;
1072 
1073 			case 'C': /* don't chdir to the script directory */
1074 				/* This is default so NOP */
1075 				break;
1076 			case 'd': /* define ini entries on command line */
1077 				define_command_line_ini_entry(ap_php_optarg);
1078 				break;
1079 
1080 			case 'D': /* daemon */
1081 				dofork = 1;
1082 				break;
1083 
1084 			case 'e': /* enable extended info output */
1085 				CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1086 				break;
1087 
1088 			case 'f': /* parse file */
1089 				filename = ap_php_optarg;
1090 				break;
1091 
1092 			case 'h': /* help & quit */
1093 			case '?':
1094 				php_output_tearup();
1095 				SG(headers_sent) = 1;
1096 				php_milter_usage(argv[0]);
1097 				php_output_teardown();
1098 				exit(1);
1099 				break;
1100 
1101 			case 'p': /* socket */
1102 				sock = strdup(ap_php_optarg);
1103 				break;
1104 
1105 			case 'v': /* show php version & quit */
1106 				if (php_request_startup(TSRMLS_C)==FAILURE) {
1107 					zend_ini_deactivate(TSRMLS_C);
1108 					php_module_shutdown(TSRMLS_C);
1109 					sapi_shutdown();
1110 					tsrm_shutdown();
1111 
1112 					exit(1);
1113 				}
1114 				SG(headers_sent) = 1;
1115 				SG(request_info).no_headers = 1;
1116 				php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2015 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
1117 				php_output_teardown();
1118 				exit(1);
1119 				break;
1120 
1121 			case 'V': /* verbose */
1122 				flag_debug = atoi(ap_php_optarg);
1123 				break;
1124 
1125 			case 'z': /* load extension file */
1126 				zend_load_extension(ap_php_optarg);
1127 				break;
1128 
1129 			default:
1130 				break;
1131 			}
1132 		}
1133 
1134 		if (param_error) {
1135 			SG(headers_sent) = 1;
1136 			SG(request_info).no_headers = 1;
1137 			PUTS(param_error);
1138 			exit(1);
1139 		}
1140 
1141 		CG(interactive) = interactive;
1142 
1143 		/* only set script_file if not set already and not in direct mode and not at end of parameter list */
1144 		if (argc > ap_php_optind && !filename) {
1145 			filename=argv[ap_php_optind];
1146 			ap_php_optind++;
1147 		}
1148 
1149 		/* check if file exists, exit else */
1150 
1151 		if (dofork) {
1152 			switch(fork()) {
1153 				case -1: /* Uh-oh, we have a problem forking. */
1154 					fprintf(stderr, "Uh-oh, couldn't fork!\n");
1155 					exit(errno);
1156 					break;
1157 				case 0: /* Child */
1158 					break;
1159 				default: /* Parent */
1160 					exit(0);
1161 			}
1162 		}
1163 
1164 		if (sock) {
1165 			struct stat junk;
1166 			if (stat(sock,&junk) == 0) unlink(sock);
1167 		}
1168 
1169 		openlog("php-milter", LOG_PID, LOG_MAIL);
1170 
1171 		if ((exit_status = mlfi_init())) {
1172 			syslog(1, "mlfi_init failed.");
1173 			closelog();
1174 			goto err;
1175 		}
1176 
1177 		smfi_setconn(sock);
1178 		if (smfi_register(smfilter) == MI_FAILURE) {
1179 			syslog(1, "smfi_register failed.");
1180 			fprintf(stderr, "smfi_register failed\n");
1181 			closelog();
1182 			goto err;
1183 		} else {
1184 			exit_status = smfi_main();
1185 		}
1186 
1187 		closelog();
1188 
1189 		if (milter_sapi_module.php_ini_path_override) {
1190 			free(milter_sapi_module.php_ini_path_override);
1191 		}
1192 
1193 	} zend_catch {
1194 		exit_status = EG(exit_status);
1195 	} zend_end_try();
1196 
1197 err:
1198 	php_module_shutdown(TSRMLS_C);
1199 	sapi_shutdown();
1200 	tsrm_shutdown();
1201 
1202 	exit(exit_status);
1203 }
1204 /* }}} */
1205 
1206 /*
1207  * Local variables:
1208  * tab-width: 4
1209  * c-basic-offset: 4
1210  * End:
1211  * vim600: sw=4 ts=4 fdm=marker
1212  * vim<600: sw=4 ts=4
1213  */
1214