1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 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(¶m[0]);
385 zval_ptr_dtor(¶m[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-2014 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