Return to php_milter.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / sapi / milter |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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: php_milter.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 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: ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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: */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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. */ ! 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: */ ! 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: */ ! 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: */ ! 858: static int sapi_milter_ub_write(const char *str, uint str_length TSRMLS_DC) ! 859: { ! 860: return str_length; ! 861: } ! 862: ! 863: static void sapi_milter_flush(void *server_context) ! 864: { ! 865: } ! 866: ! 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: ! 872: static int sapi_milter_post_read(char *buf, uint count_bytes TSRMLS_DC) ! 873: { ! 874: return 0; ! 875: } ! 876: ! 877: static char* sapi_milter_read_cookies(TSRMLS_D) ! 878: { ! 879: return NULL; ! 880: } ! 881: ! 882: static int sapi_milter_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) ! 883: { ! 884: return SAPI_HEADER_SENT_SUCCESSFULLY; ! 885: } ! 886: ! 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: */ ! 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: ! 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: */ ! 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: #if SUHOSIN_PATCH ! 1115: php_printf("PHP %s with Suhosin-Patch (%s) (built: %s %s)\nCopyright (c) 1997-2012 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); ! 1116: #else ! 1117: php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2012 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); ! 1118: #endif ! 1119: php_end_ob_buffers(1 TSRMLS_CC); ! 1120: exit(1); ! 1121: break; ! 1122: ! 1123: case 'V': /* verbose */ ! 1124: flag_debug = atoi(ap_php_optarg); ! 1125: break; ! 1126: ! 1127: case 'z': /* load extension file */ ! 1128: zend_load_extension(ap_php_optarg); ! 1129: break; ! 1130: ! 1131: default: ! 1132: break; ! 1133: } ! 1134: } ! 1135: ! 1136: if (param_error) { ! 1137: SG(headers_sent) = 1; ! 1138: SG(request_info).no_headers = 1; ! 1139: PUTS(param_error); ! 1140: exit(1); ! 1141: } ! 1142: ! 1143: CG(interactive) = interactive; ! 1144: ! 1145: /* only set script_file if not set already and not in direct mode and not at end of parameter list */ ! 1146: if (argc > ap_php_optind && !filename) { ! 1147: filename=argv[ap_php_optind]; ! 1148: ap_php_optind++; ! 1149: } ! 1150: ! 1151: /* check if file exists, exit else */ ! 1152: ! 1153: if (dofork) { ! 1154: switch(fork()) { ! 1155: case -1: /* Uh-oh, we have a problem forking. */ ! 1156: fprintf(stderr, "Uh-oh, couldn't fork!\n"); ! 1157: exit(errno); ! 1158: break; ! 1159: case 0: /* Child */ ! 1160: break; ! 1161: default: /* Parent */ ! 1162: exit(0); ! 1163: } ! 1164: } ! 1165: ! 1166: if (sock) { ! 1167: struct stat junk; ! 1168: if (stat(sock,&junk) == 0) unlink(sock); ! 1169: } ! 1170: ! 1171: openlog("php-milter", LOG_PID, LOG_MAIL); ! 1172: ! 1173: if ((exit_status = mlfi_init())) { ! 1174: syslog(1, "mlfi_init failed."); ! 1175: closelog(); ! 1176: goto err; ! 1177: } ! 1178: ! 1179: smfi_setconn(sock); ! 1180: if (smfi_register(smfilter) == MI_FAILURE) { ! 1181: syslog(1, "smfi_register failed."); ! 1182: fprintf(stderr, "smfi_register failed\n"); ! 1183: closelog(); ! 1184: goto err; ! 1185: } else { ! 1186: exit_status = smfi_main(); ! 1187: } ! 1188: ! 1189: closelog(); ! 1190: ! 1191: if (milter_sapi_module.php_ini_path_override) { ! 1192: free(milter_sapi_module.php_ini_path_override); ! 1193: } ! 1194: ! 1195: } zend_catch { ! 1196: exit_status = EG(exit_status); ! 1197: } zend_end_try(); ! 1198: ! 1199: err: ! 1200: php_module_shutdown(TSRMLS_C); ! 1201: sapi_shutdown(); ! 1202: tsrm_shutdown(); ! 1203: ! 1204: exit(exit_status); ! 1205: } ! 1206: /* }}} */ ! 1207: ! 1208: /* ! 1209: * Local variables: ! 1210: * tab-width: 4 ! 1211: * c-basic-offset: 4 ! 1212: * End: ! 1213: * vim600: sw=4 ts=4 fdm=marker ! 1214: * vim<600: sw=4 ts=4 ! 1215: */