xref: /PHP-5.3/sapi/milter/php_milter.c (revision babeca35)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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 	/* call userland */
529 	INIT_ZVAL(function_name);
530 	ZVAL_STRING(&function_name, "milter_close", 0);
531 
532 	/* set the milter context for possible use in API functions */
533 	MG(ctx) = ctx;
534 	MG(state) = MLFI_CLOSE;
535 
536 	status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
537 
538 	MG(state) = MLFI_NONE;
539 
540 	if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
541 		ret = Z_LVAL(retval);
542 	}
543 
544 	php_request_shutdown((void *) 0);
545 
546 	return ret;
547 }
548 /* }}} */
549 /* }}} */
550 
551 /* {{{ Milter entry struct
552  */
553 struct smfiDesc smfilter = {
554     "php-milter",	/* filter name */
555     SMFI_VERSION,   /* version code -- leave untouched */
556     0,				/* flags */
557     mlfi_connect,	/* info filter callback */
558     mlfi_helo,		/* HELO filter callback */
559     mlfi_envfrom,	/* envelope filter callback */
560     mlfi_envrcpt,	/* envelope recipient filter callback */
561     mlfi_header,	/* header filter callback */
562     mlfi_eoh,		/* end of header callback */
563     mlfi_body,		/* body filter callback */
564     mlfi_eom,		/* end of message callback */
565     mlfi_abort,		/* message aborted callback */
566     mlfi_close,		/* connection cleanup callback */
567 };
568 /* }}} */
569 
570 /* {{{ PHP Milter API
571  */
572 
573 /* {{{ proto void smfi_setflags(long flags)
574    Sets the flags describing the actions the filter may take. */
PHP_FUNCTION(smfi_setflags)575 PHP_FUNCTION(smfi_setflags)
576 {
577 	long flags;
578 
579 	/* valid only in the init callback */
580 	if (MG(state) != MLFI_INIT) {
581 		php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
582 	} else if (zend_parse_parameters(1 TSRMLS_CC, "l", &flags) == SUCCESS) {
583 		flags = flags & (SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT);
584 		smfilter.xxfi_flags = flags;
585 	}
586 }
587 /* }}} */
588 
589 /* {{{ proto void smfi_settimeout(long timeout)
590    Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket. */
PHP_FUNCTION(smfi_settimeout)591 PHP_FUNCTION(smfi_settimeout)
592 {
593 	long timeout;
594 
595 	/* valid only in the init callback */
596 	if (MG(state) != MLFI_INIT) {
597 		php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
598 	} else if (zend_parse_parameters(1 TSRMLS_CC, "l", &timeout) == SUCCESS) {
599 		smfi_settimeout(timeout);
600 	}
601 }
602 /* }}} */
603 
604 /* {{{ proto string smfi_getsymval(string macro)
605    Returns the value of the given macro or NULL if the macro is not defined. */
PHP_FUNCTION(smfi_getsymval)606 PHP_FUNCTION(smfi_getsymval)
607 {
608 	char *symname, *ret;
609 	int len;
610 
611 	/* valid in any callback */
612 	if (MG(state) == MLFI_NONE) {
613 		php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
614 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &symname, &len) == SUCCESS) {
615 		if ((ret = smfi_getsymval(MG(ctx), symname)) != NULL) {
616 			RETURN_STRING(ret, 1);
617 		}
618 	}
619 
620 	RETURN_NULL();
621 }
622 /* }}} */
623 
624 /* {{{ proto bool smfi_setreply(string rcode, string xcode, string message)
625    Directly set the SMTP error reply code for this connection.
626    This code will be used on subsequent error replies resulting from actions taken by this filter. */
PHP_FUNCTION(smfi_setreply)627 PHP_FUNCTION(smfi_setreply)
628 {
629 	char *rcode, *xcode, *message;
630 	int len;
631 
632 	/* valid in any callback */
633 	if (MG(state) == MLFI_NONE) {
634 		php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
635 	} else if (zend_parse_parameters(3 TSRMLS_CC, "sss", &rcode, &len, &xcode, &len, &message, &len) == SUCCESS) {
636 		if (smfi_setreply(MG(ctx), rcode, xcode, message) == MI_SUCCESS) {
637 			RETURN_TRUE;
638 		}
639 	}
640 
641 	RETURN_FALSE;
642 }
643 /* }}} */
644 
645 /* {{{ proto bool smfi_addheader(string headerf, string headerv)
646    Adds a header to the current message. */
PHP_FUNCTION(smfi_addheader)647 PHP_FUNCTION(smfi_addheader)
648 {
649 	char *f, *v;
650 	int len;
651 
652 	/* valid only in milter_eom */
653 	if (MG(state) != MLFI_EOM) {
654 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
655 	} else if (zend_parse_parameters(2 TSRMLS_CC, "ss", &f, &len, &v, &len) == SUCCESS) {
656 		if (smfi_addheader(MG(ctx), f, v) == MI_SUCCESS) {
657 			RETURN_TRUE;
658 		}
659 	}
660 
661 	RETURN_FALSE;
662 }
663 /* }}} */
664 
665 /* {{{ proto bool smfi_chgheader(string headerf, string headerv)
666    Changes a header's value for the current message. */
PHP_FUNCTION(smfi_chgheader)667 PHP_FUNCTION(smfi_chgheader)
668 {
669 	char *f, *v;
670 	long idx;
671 	int len;
672 
673 	/* valid only in milter_eom */
674 	if (MG(state) != MLFI_EOM) {
675 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
676 	} else if (zend_parse_parameters(3 TSRMLS_CC, "sls", &f, &len, &idx, &v, &len) == SUCCESS) {
677 		if (smfi_chgheader(MG(ctx), f, idx, v) == MI_SUCCESS) {
678 			RETURN_TRUE;
679 		}
680 	}
681 
682 	RETURN_FALSE;
683 }
684 /* }}} */
685 
686 /* {{{ proto bool smfi_addrcpt(string rcpt)
687    Add a recipient to the message envelope. */
PHP_FUNCTION(smfi_addrcpt)688 PHP_FUNCTION(smfi_addrcpt)
689 {
690 	char *rcpt;
691 	int len;
692 
693 	/* valid only in milter_eom */
694 	if (MG(state) != MLFI_EOM) {
695 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
696 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
697 		if (smfi_addrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
698 			RETURN_TRUE;
699 		}
700 	}
701 
702 	RETURN_FALSE;
703 }
704 /* }}} */
705 
706 /* {{{ proto bool smfi_delrcpt(string rcpt)
707    Removes the named recipient from the current message's envelope. */
PHP_FUNCTION(smfi_delrcpt)708 PHP_FUNCTION(smfi_delrcpt)
709 {
710 	char *rcpt;
711 	int len;
712 
713 	/* valid only in milter_eom */
714 	if (MG(state) != MLFI_EOM) {
715 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
716 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
717 		if (smfi_delrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
718 			RETURN_TRUE;
719 		}
720 	}
721 
722 	RETURN_FALSE;
723 }
724 /* }}} */
725 
726 /* {{{ proto bool smfi_replacebody(string body)
727    Replaces the body of the current message. If called more than once,
728    subsequent calls result in data being appended to the new body. */
PHP_FUNCTION(smfi_replacebody)729 PHP_FUNCTION(smfi_replacebody)
730 {
731 	char *body;
732 	int len;
733 
734 	/* valid only in milter_eom */
735 	if (MG(state) != MLFI_EOM) {
736 		php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
737 	} else if (zend_parse_parameters(1 TSRMLS_CC, "s", &body, &len) == SUCCESS) {
738 		if (smfi_replacebody(MG(ctx), (u_char*)body, len) == MI_SUCCESS) {
739 			RETURN_TRUE;
740 		}
741 	}
742 
743 	RETURN_FALSE;
744 }
745 /* }}} */
746 
747 /* {{{ PHP_MINIT_FUNCTION
748  */
PHP_MINIT_FUNCTION(milter)749 PHP_MINIT_FUNCTION(milter)
750 {
751 	REGISTER_LONG_CONSTANT("SMFIS_CONTINUE",	SMFIS_CONTINUE,	CONST_CS | CONST_PERSISTENT);
752 	REGISTER_LONG_CONSTANT("SMFIS_REJECT",		SMFIS_REJECT,	CONST_CS | CONST_PERSISTENT);
753 	REGISTER_LONG_CONSTANT("SMFIS_DISCARD",		SMFIS_DISCARD,	CONST_CS | CONST_PERSISTENT);
754 	REGISTER_LONG_CONSTANT("SMFIS_ACCEPT",		SMFIS_ACCEPT,	CONST_CS | CONST_PERSISTENT);
755 	REGISTER_LONG_CONSTANT("SMFIS_TEMPFAIL",	SMFIS_TEMPFAIL,	CONST_CS | CONST_PERSISTENT);
756 
757 	REGISTER_LONG_CONSTANT("SMFIF_ADDHDRS",		SMFIF_ADDHDRS,	CONST_CS | CONST_PERSISTENT);
758 	REGISTER_LONG_CONSTANT("SMFIF_CHGHDRS",		SMFIF_CHGHDRS,	CONST_CS | CONST_PERSISTENT);
759 	REGISTER_LONG_CONSTANT("SMFIF_CHGBODY",		SMFIF_CHGBODY,	CONST_CS | CONST_PERSISTENT);
760 	REGISTER_LONG_CONSTANT("SMFIF_ADDRCPT",		SMFIF_ADDRCPT,	CONST_CS | CONST_PERSISTENT);
761 	REGISTER_LONG_CONSTANT("SMFIF_DELRCPT",		SMFIF_DELRCPT,	CONST_CS | CONST_PERSISTENT);
762 
763 	ZEND_INIT_MODULE_GLOBALS(milter, NULL, NULL);
764 
765 	MG(state) = MLFI_NONE;
766 	MG(initialized) = 0;
767 	return SUCCESS;
768 }
769 /* }}} */
770 
771 /* {{{ PHP_MINFO_FUNCTION
772  */
PHP_MINFO_FUNCTION(milter)773 PHP_MINFO_FUNCTION(milter)
774 {
775 	php_info_print_table_start();
776 	php_info_print_table_header(2, "Milter support", "enabled");
777 	php_info_print_table_end();
778 }
779 /* }}} */
780 /* }}} */
781 
782 /* {{{ arginfo */
783 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setflags, 0, 0, 1)
784 	ZEND_ARG_INFO(0, flags)
785 ZEND_END_ARG_INFO()
786 
787 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_settimeout, 0, 0, 1)
788 	ZEND_ARG_INFO(0, timeout)
789 ZEND_END_ARG_INFO()
790 
791 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_getsymval, 0, 0, 1)
792 	ZEND_ARG_INFO(0, macro)
793 ZEND_END_ARG_INFO()
794 
795 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setreply, 0, 0, 3)
796 	ZEND_ARG_INFO(0, rcode)
797 	ZEND_ARG_INFO(0, xcode)
798 	ZEND_ARG_INFO(0, message)
799 ZEND_END_ARG_INFO()
800 
801 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addheader, 0, 0, 2)
802 	ZEND_ARG_INFO(0, headerf)
803 	ZEND_ARG_INFO(0, headerv)
804 ZEND_END_ARG_INFO()
805 
806 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_chgheader, 0, 0, 2)
807 	ZEND_ARG_INFO(0, headerf)
808 	ZEND_ARG_INFO(0, headerv)
809 ZEND_END_ARG_INFO()
810 
811 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addrcpt, 0, 0, 1)
812 	ZEND_ARG_INFO(0, rcpt)
813 ZEND_END_ARG_INFO()
814 
815 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_delrcpt, 0, 0, 1)
816 	ZEND_ARG_INFO(0, rcpt)
817 ZEND_END_ARG_INFO()
818 
819 ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_replacebody, 0, 0, 1)
820 	ZEND_ARG_INFO(0, body)
821 ZEND_END_ARG_INFO()
822 /* }}} */
823 
824 /* {{{ milter_functions[]
825 */
826 const static zend_function_entry milter_functions[] = {
827 	PHP_FE(smfi_setflags, 		arginfo_smfi_setflags)
828 	PHP_FE(smfi_settimeout, 	arginfo_smfi_settimeout)
829 	PHP_FE(smfi_getsymval, 		arginfo_smfi_getsymval)
830 	PHP_FE(smfi_setreply, 		arginfo_smfi_setreply)
831 	PHP_FE(smfi_addheader, 		arginfo_smfi_addheader)
832 	PHP_FE(smfi_chgheader, 		arginfo_smfi_chgheader)
833 	PHP_FE(smfi_addrcpt, 		arginfo_smfi_addrcpt)
834 	PHP_FE(smfi_delrcpt, 		arginfo_smfi_delrcpt)
835 	PHP_FE(smfi_replacebody, 	arginfo_smfi_replacebody)
836 	PHP_FE_END
837 };
838 /* }}} */
839 
840 /* {{{ Zend module entry
841 */
842 static zend_module_entry php_milter_module = {
843 	STANDARD_MODULE_HEADER,
844 	"Milter",
845 	milter_functions,
846 	PHP_MINIT(milter),
847 	NULL,
848 	NULL,
849 	NULL,
850 	PHP_MINFO(milter),
851 	"0.1.0",
852 	STANDARD_MODULE_PROPERTIES
853 };
854 /* }}} */
855 
856 /* {{{ Milter SAPI
857 */
sapi_milter_ub_write(const char * str,uint str_length TSRMLS_DC)858 static int sapi_milter_ub_write(const char *str, uint str_length TSRMLS_DC)
859 {
860 	return str_length;
861 }
862 
sapi_milter_flush(void * server_context)863 static void sapi_milter_flush(void *server_context)
864 {
865 }
866 
sapi_milter_register_variables(zval * track_vars_array TSRMLS_DC)867 static void sapi_milter_register_variables(zval *track_vars_array TSRMLS_DC)
868 {
869 	php_register_variable ("SERVER_SOFTWARE", "Sendmail Milter", track_vars_array TSRMLS_CC);
870 }
871 
sapi_milter_post_read(char * buf,uint count_bytes TSRMLS_DC)872 static int sapi_milter_post_read(char *buf, uint count_bytes TSRMLS_DC)
873 {
874 	return 0;
875 }
876 
sapi_milter_read_cookies(TSRMLS_D)877 static char* sapi_milter_read_cookies(TSRMLS_D)
878 {
879 	return NULL;
880 }
881 
sapi_milter_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)882 static int sapi_milter_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
883 {
884 	return SAPI_HEADER_SENT_SUCCESSFULLY;
885 }
886 
php_milter_startup(sapi_module_struct * sapi_module)887 static int php_milter_startup(sapi_module_struct *sapi_module)
888 {
889 	if (php_module_startup(sapi_module, &php_milter_module, 1) == FAILURE) {
890 		return FAILURE;
891 	}
892 	return SUCCESS;
893 }
894 /* }}} */
895 
896 /* {{{ sapi_module_struct milter_sapi_module
897 */
898 static sapi_module_struct milter_sapi_module = {
899 	"milter",						/* name */
900 	"Sendmail Milter SAPI",			/* pretty name */
901 
902 	php_milter_startup,				/* startup */
903 	php_module_shutdown_wrapper,	/* shutdown */
904 
905 	NULL,							/* activate */
906 	NULL,							/* deactivate */
907 
908 	sapi_milter_ub_write,			/* unbuffered write */
909 	sapi_milter_flush,				/* flush */
910 	NULL,							/* get uid */
911 	NULL,							/* getenv */
912 
913 	php_error,						/* error handler */
914 
915 	NULL,							/* header handler */
916 	sapi_milter_send_headers,		/* send headers handler */
917 	NULL,							/* send header handler */
918 
919 	sapi_milter_post_read,			/* read POST data */
920 	sapi_milter_read_cookies,		/* read Cookies */
921 
922 	sapi_milter_register_variables,	/* register server variables */
923 	NULL,							/* Log message */
924 	NULL,							/* Get request time */
925 	NULL,							/* Child terminate */
926 
927 	NULL,							/* Block interruptions */
928 	NULL,							/* Unblock interruptions */
929 
930 	STANDARD_SAPI_MODULE_PROPERTIES
931 };
932 /* }}} */
933 
934 /****
935 * ripped from cli, has to be cleaned up !
936 */
937 
938 /* {{{ php_milter_usage
939 */
php_milter_usage(char * argv0)940 static void php_milter_usage(char *argv0)
941 {
942 	char *prog;
943 
944 	prog = strrchr(argv0, '/');
945 	if (prog) {
946 		prog++;
947 	} else {
948 		prog = "php-milter";
949 	}
950 
951 	printf(     "Usage: %s [options] [-f] <file> [args...]\n"
952 	            "       %s [options] [-- args...]\n"
953 				"  -a               Run interactively\n"
954 				"  -c <path>|<file> Look for php.ini file in this directory\n"
955 				"  -n               No php.ini file will be used\n"
956 				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
957 				"  -D               run as daemon\n"
958 				"  -e               Generate extended information for debugger/profiler\n"
959 				"  -f <file>        Parse <file>.\n"
960 				"  -h               This help\n"
961 				"  -p <socket>      path to create socket\n"
962 				"  -v               Version number\n"
963 				"  -V <n>           set debug level to n (1 or 2).\n"
964 				"  -z <file>        Load Zend extension <file>.\n"
965 				"  args...          Arguments passed to script. Use -- args when first argument \n"
966 				"                   starts with - or script is read from stdin\n"
967 				, prog, prog);
968 }
969 /* }}} */
970 
define_command_line_ini_entry(char * arg)971 static void define_command_line_ini_entry(char *arg) /* {{{ */
972 {
973 	char *name, *value;
974 
975 	name = arg;
976 	value = strchr(arg, '=');
977 	if (value) {
978 		*value = 0;
979 		value++;
980 	} else {
981 		value = "1";
982 	}
983 	zend_alter_ini_entry(name, strlen(name)+1, value, strlen(value), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
984 }
985 /* }}} */
986 
987 /* {{{ main
988 */
main(int argc,char * argv[])989 int main(int argc, char *argv[])
990 {
991     char *sock = NULL;
992 	int dofork = 0;
993 
994 	int exit_status = SUCCESS;
995 	int c;
996 /* temporary locals */
997 	int orig_optind=ap_php_optind;
998 	char *orig_optarg=ap_php_optarg;
999 	int interactive=0;
1000 	char *param_error=NULL;
1001 /* end of temporary locals */
1002 
1003 	void ***tsrm_ls;
1004 
1005 #ifdef HAVE_SIGNAL_H
1006 #if defined(SIGPIPE) && defined(SIG_IGN)
1007 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
1008 								that sockets created via fsockopen()
1009 								don't kill PHP if the remote site
1010 								closes it.  in apache|apxs mode apache
1011 								does that for us!  thies@thieso.net
1012 								20000419 */
1013 #endif
1014 #endif
1015 
1016 
1017 	tsrm_startup(1, 1, 0, NULL);
1018 	sapi_startup(&milter_sapi_module);
1019 
1020 	while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
1021 		switch (c) {
1022 		case 'c':
1023 			milter_sapi_module.php_ini_path_override = strdup(ap_php_optarg);
1024 			break;
1025 		case 'n':
1026 			milter_sapi_module.php_ini_ignore = 1;
1027 			break;
1028 		}
1029 	}
1030 	ap_php_optind = orig_optind;
1031 	ap_php_optarg = orig_optarg;
1032 
1033 	milter_sapi_module.executable_location = argv[0];
1034 
1035 	tsrm_ls = ts_resource(0);
1036 
1037 	sapi_module.startup(&milter_sapi_module);
1038 
1039 	zend_first_try {
1040 		while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
1041 			switch (c) {
1042 			case '?':
1043 				php_output_startup();
1044 				php_output_activate(TSRMLS_C);
1045 				SG(headers_sent) = 1;
1046 				php_milter_usage(argv[0]);
1047 				php_end_ob_buffers(1 TSRMLS_CC);
1048 				exit(1);
1049 				break;
1050 			}
1051 		}
1052 		ap_php_optind = orig_optind;
1053 		ap_php_optarg = orig_optarg;
1054 
1055         /* Set some CLI defaults */
1056 		SG(options) |= SAPI_OPTION_NO_CHDIR;
1057 		zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
1058 		zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
1059 
1060 		zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */
1061 
1062 		while ((c = ap_php_getopt(argc, argv, OPTSTRING)) != -1) {
1063 			switch (c) {
1064 
1065 			case 'a':	/* interactive mode */
1066 				printf("Interactive mode enabled\n\n");
1067 				interactive=1;
1068 				break;
1069 
1070 			case 'C': /* don't chdir to the script directory */
1071 				/* This is default so NOP */
1072 				break;
1073 			case 'd': /* define ini entries on command line */
1074 				define_command_line_ini_entry(ap_php_optarg);
1075 				break;
1076 
1077 			case 'D': /* daemon */
1078 				dofork = 1;
1079 				break;
1080 
1081 			case 'e': /* enable extended info output */
1082 				CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
1083 				break;
1084 
1085 			case 'f': /* parse file */
1086 				filename = ap_php_optarg;
1087 				break;
1088 
1089 			case 'h': /* help & quit */
1090 			case '?':
1091 				php_output_startup();
1092 				php_output_activate(TSRMLS_C);
1093 				SG(headers_sent) = 1;
1094 				php_milter_usage(argv[0]);
1095 				php_end_ob_buffers(1 TSRMLS_CC);
1096 				exit(1);
1097 				break;
1098 
1099 			case 'p': /* socket */
1100 				sock = strdup(ap_php_optarg);
1101 				break;
1102 
1103 			case 'v': /* show php version & quit */
1104 				if (php_request_startup(TSRMLS_C)==FAILURE) {
1105 					zend_ini_deactivate(TSRMLS_C);
1106 					php_module_shutdown(TSRMLS_C);
1107 					sapi_shutdown();
1108 					tsrm_shutdown();
1109 
1110 					exit(1);
1111 				}
1112 				SG(headers_sent) = 1;
1113 				SG(request_info).no_headers = 1;
1114 				php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2014 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
1115 				php_end_ob_buffers(1 TSRMLS_CC);
1116 				exit(1);
1117 				break;
1118 
1119 			case 'V': /* verbose */
1120 				flag_debug = atoi(ap_php_optarg);
1121 				break;
1122 
1123 			case 'z': /* load extension file */
1124 				zend_load_extension(ap_php_optarg);
1125 				break;
1126 
1127 			default:
1128 				break;
1129 			}
1130 		}
1131 
1132 		if (param_error) {
1133 			SG(headers_sent) = 1;
1134 			SG(request_info).no_headers = 1;
1135 			PUTS(param_error);
1136 			exit(1);
1137 		}
1138 
1139 		CG(interactive) = interactive;
1140 
1141 		/* only set script_file if not set already and not in direct mode and not at end of parameter list */
1142 		if (argc > ap_php_optind && !filename) {
1143 			filename=argv[ap_php_optind];
1144 			ap_php_optind++;
1145 		}
1146 
1147 		/* check if file exists, exit else */
1148 
1149 		if (dofork) {
1150 			switch(fork()) {
1151 				case -1: /* Uh-oh, we have a problem forking. */
1152 					fprintf(stderr, "Uh-oh, couldn't fork!\n");
1153 					exit(errno);
1154 					break;
1155 				case 0: /* Child */
1156 					break;
1157 				default: /* Parent */
1158 					exit(0);
1159 			}
1160 		}
1161 
1162 		if (sock) {
1163 			struct stat junk;
1164 			if (stat(sock,&junk) == 0) unlink(sock);
1165 		}
1166 
1167 		openlog("php-milter", LOG_PID, LOG_MAIL);
1168 
1169 		if ((exit_status = mlfi_init())) {
1170 			syslog(1, "mlfi_init failed.");
1171 			closelog();
1172 			goto err;
1173 		}
1174 
1175 		smfi_setconn(sock);
1176 		if (smfi_register(smfilter) == MI_FAILURE) {
1177 			syslog(1, "smfi_register failed.");
1178 			fprintf(stderr, "smfi_register failed\n");
1179 			closelog();
1180 			goto err;
1181 		} else {
1182 			exit_status = smfi_main();
1183 		}
1184 
1185 		closelog();
1186 
1187 		if (milter_sapi_module.php_ini_path_override) {
1188 			free(milter_sapi_module.php_ini_path_override);
1189 		}
1190 
1191 	} zend_catch {
1192 		exit_status = EG(exit_status);
1193 	} zend_end_try();
1194 
1195 err:
1196 	php_module_shutdown(TSRMLS_C);
1197 	sapi_shutdown();
1198 	tsrm_shutdown();
1199 
1200 	exit(exit_status);
1201 }
1202 /* }}} */
1203 
1204 /*
1205  * Local variables:
1206  * tab-width: 4
1207  * c-basic-offset: 4
1208  * End:
1209  * vim600: sw=4 ts=4 fdm=marker
1210  * vim<600: sw=4 ts=4
1211  */
1212