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(¶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 /* 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