1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656 9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 9675 9676 9677 9678 9679 9680 9681 9682 9683 9684 9685 9686 9687 9688 9689 9690 9691 9692 9693 9694 9695 9696 9697 9698 9699 9700 9701 9702 9703 9704 9705 9706 9707 9708 9709 9710 9711 9712 9713 9714 9715 9716 9717 9718 9719 9720 9721 9722 9723 9724 9725 9726 9727 9728 9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747 9748 9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762 9763 9764 9765 9766 9767 9768 9769 9770 9771 9772 9773 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 9789 9790 9791 9792 9793 9794 9795 9796 9797 9798 9799 9800 9801 9802 9803 9804 9805 9806 9807 9808 9809 9810 9811 9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832 9833 9834 9835 9836 9837 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 10725 10726 10727 10728 10729 10730 10731 10732 10733 10734 10735 10736 10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 10751 10752 10753 10754 10755 10756 10757 10758 10759 10760 10761 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 10840 10841 10842 10843 10844 10845 10846 10847 10848 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999 11000 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 11013 11014 11015 11016 11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 11048 11049 11050 11051 11052 11053 11054 11055 11056 11057 11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 11085 11086 11087 11088 11089 11090 11091 11092 11093 11094 11095 11096 11097 11098 11099 11100 11101 11102 11103 11104 11105 11106 11107 11108 11109 11110 11111 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124 11125 11126 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 11205 11206 11207 11208 11209 11210 11211 11212 11213 11214 11215 11216 11217 11218 11219 11220 11221 11222 11223 11224 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 11271 11272 11273 11274 11275 11276 11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 11291 11292 11293 11294 11295 11296 11297 11298 11299 11300 11301 11302 11303 11304 11305 11306 11307 11308 11309 11310 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 11325 11326 11327 11328 11329 11330 11331 11332 11333 11334 11335 11336 11337 11338 11339 11340 11341 11342 11343 11344 11345 11346 11347 11348 11349 11350 11351 11352 11353 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 11364 11365 11366 11367 11368 11369 11370 11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 11383 11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407 11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 11448 11449 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 11738 11739 11740 11741 11742 11743 11744 11745 11746 11747 11748 11749 11750 11751 11752 11753 11754 11755 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 11786 11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 11798 11799 11800 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 11891 11892 11893 11894 11895 11896 11897 11898 11899 11900 11901 11902 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998 11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 12021 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057 12058 12059 12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 12072 12073 12074 12075 12076 12077 12078 12079 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 12090 12091 12092 12093 12094 12095 12096 12097 12098 12099 12100 12101 12102 12103 12104 12105 12106 12107 12108 12109 12110 12111 12112 12113 12114 12115 12116 12117 12118 12119 12120 12121 12122 12123 12124 12125 12126 12127 12128 12129 12130 12131 12132 12133 12134 12135 12136 12137 12138 12139 12140 12141 12142 12143 12144 12145 12146 12147 12148 12149 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 12163 12164 12165 12166 12167 12168 12169 12170 12171 12172 12173 12174 12175 12176 12177 12178 12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 12199 12200 12201 12202 12203 12204 12205 12206 12207 12208 12209 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 12221 12222 12223 12224 12225 12226 12227 12228 12229 12230 12231 12232 12233 12234 12235 12236 12237 12238 12239 12240 12241 12242 12243 12244 12245 12246 12247 12248 12249 12250 12251 12252 12253 12254 12255 12256 12257 12258 12259 12260 12261 12262 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276 12277 12278 12279 12280 12281 12282 12283 12284 12285 12286 12287 12288 12289 12290 12291 12292 12293 12294 12295 12296 12297 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 12327 12328 12329 12330 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421 12422 12423 12424 12425 12426 12427 12428 12429 12430 12431 12432 12433 12434 12435 12436 12437 12438 12439 12440 12441 12442 12443 12444 12445 12446 12447 12448 12449 12450 12451 12452 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487
|
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename mailfromd.info
@settitle Mailfromd Manual
@c %**end of header
@setchapternewpage odd
@defcodeindex pr
@c mailfromd options
@defcodeindex op
@c mtasim options.
@defcodeindex mt
@defcodeindex kw
@defcodeindex fl
@syncodeindex fn cp
@syncodeindex vr cp
@syncodeindex ky cp
@syncodeindex mt cp
@syncodeindex pg cp
@syncodeindex tp cp
@syncodeindex op cp
@syncodeindex pr cp
@syncodeindex kw cp
@syncodeindex fl cp
@include version.texi
@include rendition.texi
@include macros.texi
@include global.texi
@ifinfo
@dircategory Email
@direntry
* Mailfromd: (mailfromd). General-purpose mail-filtering software.
* mailfromd: (mailfromd) Invocation. Mail Filtering and Real-time Modification daemon.
* calloutd: (mailfromd) calloutd. A Stand-Alone Callout Daemon.
* mfdbtool: (mailfromd) mfdbtool. Database Management Tool.
* mtasim: (mailfromd) mtasim. MTA simulator.
* pmult: (mailfromd) pmult. Pmilter multiplexer program.
@end direntry
@end ifinfo
@copying
Copyright @copyright{} 2005--2025 Sergey Poznyakoff
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license is included in the section entitled ``GNU
Free Documentation License''.
@end copying
@titlepage
@title Mailfromd mail filter
@subtitle version @value{VERSION}, @value{UPDATED}
@author Sergey Poznyakoff.
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@headings off
@ifnothtml
@page
@w{ }
@sp 9
@end ifnothtml
@quotation
@i{Dedico aquest treball a Lluis Llach, per obrir els nous horitzons.}
@end quotation
@ifnothtml
@w{ }
@page
@w{ }
@page
@end ifnothtml
@headings on
@ifnothtml
@page
@summarycontents
@page
@end ifnothtml
@contents
@ifnottex
@node Top
@top Mailfromd
This edition of the @cite{Mailfromd Manual}, last updated @value{UPDATED},
documents @command{mailfromd} Version @value{VERSION}.
@end ifnottex
@menu
* Preface:: Short description of this manual; brief
history and acknowledgments.
* Intro:: Introduction to Mailfromd.
* Building:: Building the Package.
* Tutorial:: Mailfromd Tutorial.
* MFL:: The Mail Filtering Language.
* Library:: The MFL Library Functions.
* Using MFL Mode:: Using the GNU Emacs MFL Mode.
* Mailfromd Configuration:: Configuring @command{mailfromd}.
* Invocation:: How to Start and Stop @command{mailfromd}.
* MTA Configuration:: Using @command{mailfromd} with Various @acronym{MTA}s
* calloutd:: A Stand-Alone Callout Daemon.
* mfdbtool:: A Database Management Tool.
* mtasim:: An @acronym{MTA} simulator.
* pmult:: Pmilter multiplexer program.
* Reporting Bugs:: How to Report a Bug.
Appendices
* Gacopyz::
* Time and Date Formats::
* Upgrading::
* Copying This Manual:: The GNU Free Documentation License.
* Concept Index:: Index of Concepts.
@ifset WEBDOC
* This Manual in Other Formats::
@end ifset
@detailmenu
--- The Detailed Node Listing ---
Preface
* History:: Short @command{mailfromd} history.
* Acknowledgments:: Acknowledgments.
Introduction to @command{mailfromd}
* Conventions:: Typographical conventions.
* Overview:: Mailfromd at a first glance
* SAV:: Principles of Sender Address Verification.
* Rate Limit:: Controlling Mail Sending Rate.
* SPF:: SPF, DKIM, and others.
Sender Address Verification.
* Limitations::
Tutorial
* Start Up::
* Simplest Configurations::
* Conditional Execution::
* Functions and Modules::
* Domain Name System::
* Checking Sender Address::
* SMTP Timeouts::
* Avoiding Verification Loops::
* HELO Domain::
* rset::
* Controlling Number of Recipients::
* Sending Rate::
* Greylisting::
* Local Account Verification::
* Databases::
* Testing Filter Scripts::
* Run Mode::
* Examining Defaults::
* Logging and Debugging::
* Runtime errors::
* Notes::
Databases
* Database Formats::
* Basic Database Operations::
* Database Maintenance::
Run Mode
* top-block:: The Top of a Script File.
* getopt:: Parsing Command Line Arguments.
Mail Filtering Language
* Comments:: Comments.
* include::
* line::
* Generated warnings and errors::
* Pragmas:: Pragmatic comments.
* Data Types::
* Numbers::
* Literals::
* Here Documents::
* Sendmail Macros::
* Constants::
* Variables::
* Back references::
* Handlers::
* Special handlers:: Initialization and cleanup handlers.
* Functions:: Functions.
* Expressions:: Expressions.
* Shadowing:: Variable and Constant Shadowing.
* Statements::
* Conditionals:: Conditional Statements.
* Loops:: Loop Statements.
* Exceptions:: Exceptional Conditions and their Handling.
* Polling:: Sender Verification Tests.
* Modules:: Modules are Collections of Useful Functions.
* mfmod:: Dynamically Loaded Modules.
* Preprocessor:: Input Text Is Preprocessed.
* Filter Script Example:: A Working Filter Script Explained.
* Reserved Words:: A Reference List of Reserved Words.
Pragmatic comments
* prereq:: Pragma prereq.
* stacksize:: Pragma stacksize.
* regex:: Pragma regex.
* dbprop:: Pragma dbprop.
* greylist:: Pragma greylist.
* miltermacros:: Pragma miltermacros.
* provide-callout:: Pragma provide-callout.
Constants
* Built-in constants::
Variables
* Predefined variables::
Functions
* Some Useful Functions::
Expressions
* Constant expressions:: String and Numeric Constants.
* Function calls:: A Function Call is an Expression.
* Concatenation:: String Concatenation.
* Arithmetic operations:: @samp{+}, @samp{-}, etc.
* Bitwise shifts:: @samp{<<} and @samp{>>}.
* Relational expressions:: @samp{=}, @samp{<}, etc.
* Special comparisons:: @code{matches}, @code{mx matches}, etc.
* Boolean expressions:: @code{and}, @code{or}, @code{not}.
* Precedence:: How various operators nest.
* Type casting::
Statements
* Actions:: Actions control the handling of the mail.
* Assignments::
* Pass::
* Echo::
@c Return statement::
@c Conditionals
@c Exception handlers
Exceptional Conditions
* Built-in Exceptions::
* User-defined Exceptions::
* Catch and Throw::
Modules
* module structure:: Declaring Modules
* scope of visibility::
* import:: Require and Import
Dynamically Loaded Modules
* Loadable Library::
* Interface Module::
* mfmodnew:: Creating a Mfmod Structure
Creating a Mfmod Structure
* mfmodnew invocation::
The MFL Library Functions
* Macro access::
* Character translation::
* String transformation::
* String manipulation::
* String formatting::
* Character Type::
* I/O functions::
* Filtering functions::
* Email processing functions::
* Envelope modification functions::
* Header modification functions::
* Body Modification Functions::
* Message modification queue::
* Mail header functions::
* Mail body functions::
* EOM Functions::
* Current Message Functions::
* Mailbox functions::
* Message functions::
* Quarantine functions::
* SMTP Callout functions::
* Compatibility Callout functions::
* Internet address manipulation functions::
* DNS functions::
* Geolocation functions::
* Database functions::
* Control database::
* System functions::
* Passwd functions::
* Sieve Interface::
* Interfaces to Third-Party Programs::
* Rate limiting functions::
* Greylisting functions::
* Special test functions::
* Mail Sending Functions::
* Blacklisting Functions::
* SPF Functions::
* DKIM::
* Sockmaps::
* NLS Functions::
* Syslog Interface::
* Debugging Functions::
* Informative Functions::
* Mfmod Interface::
Message Functions
* Header functions::
* Message body functions::
* MIME functions::
* Message digest functions::
Interfaces to Third-Party Programs
* SpamAssassin::
* ClamAV::
DKIM
* Setting up a DKIM record::
Configuring @command{mailfromd}
* conf-types:: Special Configuration Data Types
* conf-base:: Base Mailfromd Configuration
* conf-preprocessor:: Preprocessor Configuration
* conf-resolver:: DNS Resolver Configuration
* conf-server:: Server Configuration
* conf-milter:: Milter Connection Configuration
* conf-debug:: Logging and Debugging configuration
* conf-timeout:: Timeout Configuration
* conf-callout:: Call-out Configuration
* conf-priv:: Privilege Configuration
* conf-database:: Database Configuration
* conf-runtime:: Runtime Constants
* conf-mailutils:: Standard Mailutils Statements
@command{Mailfromd} Command Line Syntax
* options:: Command Line Options.
* Starting and Stopping:: How to Start and Shut Down the Daemon.
Command Line Options.
* Operation Modifiers::
* General Settings::
* Preprocessor Options::
* Timeout Control::
* Logging and Debugging Options::
* Informational Options::
Using @command{mailfromd} with Various @acronym{MTA}s
* Sendmail::
* MeTA1::
* Postfix::
@command{calloutd}
* config-calloutd:: Calloutd Configuration.
* invocation-calloutd:: Calloutd Command-Line Options.
* protocol-calloutd:: The Callout Protocol.
Calloutd Configuration
* conf-calloutd-setup:: @command{calloutd} General Setup.
* conf-calloutd-server:: The @code{server} Statement.
* conf-calloutd-log:: @command{calloutd} Logging.
@command{mfdbtool}
* Invoking mfdbtool::
* Configuring mfdbtool::
@command{mtasim} --- a testing tool
* interactive mode::
* expect commands::
* traces::
* daemon mode::
* command summary::
* option summary::
Pmilter multiplexer program.
* pmult configuration::
* pmult example::
* pmult invocation::
Pmult Configuration
* pmult-conf:: Multiplexer Configuration.
* pmult-macros:: Translating MeTA1 macros.
* pmult-client:: Pmult Client Configuration.
* pmult-debug:: Debugging Pmult.
Upgrading
* 8140-8150:: Upgrading from 8.14 to 8.15
* 8130-8140:: Upgrading from 8.13 to 8.14
* 870-880:: Upgrading from 8.7 to 8.8
* 850-860:: Upgrading from 8.5 to 8.6
* 820-830:: Upgrading from 8.2 to 8.3 (or 8.4)
* 700-800:: Upgrading from 7.0 to 8.0
* 600-700:: Upgrading from 6.0 to 7.0
* 5x0-600:: Upgrading from 5.x to 6.0
* 500-510:: Upgrading from 5.0 to 5.1
* 440-500:: Upgrading from 4.4 to 5.0
* 43x-440:: Upgrading from 4.3.x to 4.4
* 420-43x:: Upgrading from 4.2 to 4.3.x
* 410-420:: Upgrading from 4.1 to 4.2
* 400-410:: Upgrading from 4.0 to 4.1
* 31x-400:: Upgrading from 3.1.x to 4.0
* 30x-31x:: Upgrading from 3.0.x to 3.1
* 2x-30x:: Upgrading from 2.x to 3.0.x
* 1x-2x:: Upgrading from 1.x to 2.x
@end detailmenu
@end menu
@node Preface
@unnumbered Preface
Simple Mail Transfer Protocol (@acronym{SMTP}) which is the standard
for email transmissions across the Internet was designed in the
good old days when nobody could even think of the
possibility of e-mail being abused to send tons of unsolicited
messages of dubious contents. Therefore it lacks mechanisms that
could have prevented this abuse (@dfn{spamming}), or at least could
have made it difficult. Attempts to introduce such mechanisms (such
as @uref{http://tools.ietf.org/html/rfc2554, @acronym{SMTP-AUTH}
extension}) are being made, but they are not in wide use yet and,
probably, their introduction will not be enough to stop the e-mail
abuse. Spamming is today's grim reality and developers spend lots of
time and efforts designing new protection measures against it.
@command{Mailfromd} is one of such attempts.
The package is designed to work with any @acronym{MTA} supporting
@samp{Milter} or @samp{Pmilter} protocol, such as @samp{Sendmail},
@samp{MeTA1} or @samp{Postfix}. It allows you to:
@itemize @bullet
@item
Control whether messages come from trustworthy senders, using so
called @dfn{callout} or @dfn{Sender Address Verification} (@pxref{SAV})
mechanism.
@item
Prevent emails coming from forged addresses by use of @acronym{SPF}
mechanism (@pxref{SPF Functions}).
@item
Limit connection and/or sending rates (@pxref{Rate Limit}).
@item
Use @dfn{black-}, @dfn{white-} and @dfn{greylisting} techniques.
@item
Invoke external programs or other mail filters.
@end itemize
@menu
* History:: Short @command{mailfromd} history.
* Acknowledgments:: Acknowledgments.
@end menu
@node History
@unnumberedsec Short history of @command{mailfromd}.
The idea of the utility appeared in 2005, and its first version
appeared soon afterward. Back then it was a simple implementation of
Sender Address Verification (@pxref{SAV}) for @samp{Sendmail} (hence
its name -- @command{mailfromd}) with rudimentary tuning
possibilities.
After a short run on my mail servers, I discovered that the utility
was not flexible enough. It took less than a month to implement a
configuration file that allowed the user to control program and data flow
during the @samp{envfrom} @acronym{SMTP} state. The new version, 1.0,
appeared in June, 2005.
Next major release, 1.2 (1.1 contained mostly bugfixes),
appeared two months later, and introduced @dfn{mail sending rate}
control (@pxref{Rate Limit}).
The program evolved during the next year, and the version 2.0 was
released in September, 2006. This version was a major change in the
main idea of the program. Configuration file become a flexible filter
script allowing the operator to control almost all @acronym{SMTP} states. The
program supplied in the script file was compiled into a pseudo-code at
startup, this code being subsequently evaluated each time the filter
was invoked. This caused a considerable speed-up in comparison with
the previous versions, where the run-time evaluator was traversing the
parse tree. This version also introduced (implicitly, at the time),
two separate data types for the entities declared in the script, which
also played its role in the speed improvement (in the previous
versions all data were considered strings). Lots of improvements were
made in the filter language (@pxref{MFL}) itself, such
as user-defined functions, the @code{switch} statement, the @code{catch}
statement for handling run-time errors, etc. The set of
built-in functions extended considerably. A testsuite (using
@i{DejaGNU}) was introduced in this version.
During this initial development period the limitations
imposed by @command{libmilter} implementation became obvious. Finally,
I felt they were stopping further development, and decided
that @command{mailfromd} should use its own @samp{Milter}
implementation. This new library, @command{libgacopyz} was the main
new feature of the 3.0 release, which was released in November, 2006.
Another major feature was the @option{--dump-macros} option and
the @option{macros} subcommand to @command{rc.mailfromd} script, that
were intended to facilitate configuration on the @samp{Sendmail} side.
The development of 3.@i{x} (more properly, 3.1.@i{x}) series
concentrated mainly on bug-fixes, while the main development was done
on the next branch.
The version 4.0 appeared on May 12, 2007. A full
list of changes in this release is more than 500 lines long, so it is
impractical to list them here. In particular, this version introduced
lots of new features in @acronym{MFL} syntax and the
library of useful @acronym{MFL} functions. The runtime engine was
also improved, in particular, stack space become expandable which
eliminated many run-time errors. This version also provided a
foundation for @acronym{MFL} module system. The code generation
was re-implemented to facilitate introduction of object files in
future versions. Another new features in this release include
@acronym{SPF} support and @command{mtasim} utility --- an @acronym{MTA}
simulator designed for testing @command{mailfromd} scripts
(@pxref{mtasim}). The test suite in this version was made portable by
rewriting it in @i{Autotest}.
Another big leap forward was the 5.0 release, which appeared on
December 26, 2008. It largely enriched a set of available functions
(61 new functions were introduced, which amounts to 41% of all the
available functions in 5.0 release) and introduced several
improvements in the MFL itself. Among others, function aliases and
optional arguments in user-defined functions were introduced in this
release. The new ``run operation mode'' allowed to execute arbitrary
MFL functions from the command line. This release also raised the
Mailutils version requirements to at least 2.0.
Version 6.0, which was released in on 12 December, 2009, introduced
a full-fledged modular system, akin to that of Python, and quite a few
improvements to the language. such as explicit type casts,
concatenation operator, static variables, etc.
Starting from version 7.0, the focus of further development of
@command{mailfromd} has shifted. While previously it had been regarded
as a mail-filtering server, since then it was developed as a system for
extending @acronym{MTA} functionality in the broad sense, mail
filtering being only one of features it provides.
Version 7.0 makes the @acronym{MFL} syntax more consistent and the
language itself more powerful. For example, it is no longer necessary
to use prefixes before variables to dereference them. The new
@samp{try--catch} construct allows for elegant handling of exceptions
and errors. User-defined exceptions provide a way for programming
complex loops and recursions with non-local exits.
This version introduces a concept of dedicated callout server.
This allows @command{mailfromd} to defer verifications for a later
time if the remote server does not response within a reasonably short
period of time (@pxref{SMTP Timeouts}).
Six years later the version 8.0 was released. This version was a
major rewrite of the mailfromd codebase. It introduced a separate
callout daemon that made it possible to separate the mailfromd server
machine from machines performing callout checks. The MFL language
was extended by a number of built-in functions.
Since version 8.3 (2017-11-02) @command{mailfromd} uses
@samp{adns}@footnote{@uref{https://www.gnu.org/software/adns}} for
DNS queries.
The version 8.7 released in July, 2020 introduced DKIM support.
The version 8.15 (2022-12-11) introduced dynamically loaded MFL modules.
These modules use dynamically loaded libraries to extend the program
functionality without having to modify its code. Several such modules
were provided as separate projects. @xref{mfmod}.
The version 9.0 released in January, 2024 introduced full IPv6
support, separated module and search paths and added several new
MFL functions.
@node Acknowledgments
@unnumberedsec Acknowledgments
Many people need to be thanked for their assistance in developing
and debugging @command{mailfromd}. After S.@: C.@: Johnson, I can say
that this program ``@i{owes much to a most stimulating collection of
users, who have goaded me beyond my inclination, and frequently beyond
my ability in their endless search for "one more feature". Their
irritating unwillingness to learn how to do things my way has usually
led to my doing things their way; most of the time, they have been right.}''
A real test for a program like @command{mailfromd} cannot be done
but in conditions of production environment. A decision to try it
in these conditions is by no means an easy one, it requires courage
and good faith in the intentions and abilities of the author. To
begin with, I would like to thank my contributors for these virtues.
@cindex Jan Rafaj
Jan Rafaj has intrepidly been using @command{mailfromd} since its
early releases and invested lots of efforts in improving the program
and its documentation. He is the author of many of the @acronym{MFL}
library functions, shipped with the package. Some of his ideas are
still waiting in my implementation queue, while new ones are
consistently arriving.
@cindex Peter Markeloff
Peter Markeloff patiently tested every @command{mailfromd}
release and helped discover and fix many bugs.
@cindex Zeus Panchenko
Zeus Panchenko contributed many ideas and gave lots of helpful
comments. He offered invaluable help in debugging and testing
@command{mailfromd} on @acronym{FreeBSD} platform.
@cindex Sergey Afonin
Sergey Afonin proposed many improvements and new ideas. He also
invested a lot of his time in finding bugs and testing bugfixes.
@cindex John McEleney
@cindex Ben McKeegan
John McEleney and Ben McKeegan contributed the token bucket filter
implementation (@pxref{TBF}).
@cindex Con Tassios
Con Tassios helped to find and fix various bugs and contributed the
new implementation of the @code{greylist} function (@pxref{greylisting
types}).
The following people (in alphabetical order) provided bug reports
and helpful comments for various versions of the program:
@cindex Alan Dobkin
@cindex Brent Spencer
@cindex Jeff Ballard
@cindex Nacho Gonz@'alez L@'opez
@cindex Phil Miller
@cindex Simon Christian
@cindex Thomas Lynch
Alan Dobkin, Brent Spencer, Jeff Ballard, Nacho Gonz@'alez L@'opez,
Phil Miller, Simon Christian, Thomas Lynch.
@node Intro
@chapter Introduction to @command{mailfromd}
@command{Mailfromd} is a general-purpose mail filtering daemon and a
suite of accompanying utilities for @command{Sendmail}@footnote{See
@uref{http://www.sendmail.org}}, @command{MeTA1}@footnote{See
@uref{http://www.meta1.org}}, @command{Postfix}@footnote{See
@uref{http://www.postfix.org}} or any other @acronym{MTA} that
supports @command{Milter} (or @command{Pmilter}) protocol. It is able
to filter both incoming and outgoing messages using a filter program,
written in @dfn{mail filtering language} (@acronym{MFL}). The daemon
interfaces with the @acronym{MTA} using @command{Milter} protocol.
The name @command{mailfromd} can be thought of as an abbreviation for
@samp{@emph{Mail} @emph{F}iltering and @emph{R}untime
@emph{M}odification} @emph{D}aemon, with an @samp{o} for itself.
Historically, it stemmed from the fact that the original
implementation was a simple filter implementing the @dfn{sender
address verification} technique. Since then the program has changed
dramatically, and now it is actually a language translator and
run-time evaluator providing a set of built-in and library functions
for filtering electronic mail.
The first part of this manual is an overview, describing the features
@command{mailfromd} offers in general.
The second part is a tutorial, which provides an introduction for
those who have not used @command{mailfromd} previously. It moves from
topic to topic in a logical, progressive order, building on
information already explained. It offers only the principal information
needed to master basic practical usage of @command{mailfromd}, while
omitting many subtleties.
The other parts are meant to be used as a reference for those who
know @command{mailfromd} well enough, but need to look up some notions
from time to time. Each chapter presents everything that needs to be
said about a specific topic.
The manual assumes that the reader has a good knowledge of the
@acronym{SMTP} protocol and the mail transport system he uses
(@command{Sendmail} , @command{Postfix} or @command{MeTA1}).
@menu
* Conventions:: Typographical conventions.
* Overview:: Mailfromd at a first glance
* SAV:: Principles of Sender Address Verification.
* Rate Limit:: Controlling Mail Sending Rate.
* SPF:: SPF, DKIM, and others.
@end menu
@node Conventions
@section Typographical conventions
@cindex Texinfo
This manual is written using Texinfo, the GNU documentation
formatting language. The same set of Texinfo source files is used to
produce both the printed and online versions of the documentation.
@ifnotinfo
Because of this, the typographical conventions
may be slightly different than in other books you may have read.
@end ifnotinfo
@ifinfo
This section briefly documents the typographical conventions used in
this manual.
@end ifinfo
Examples you would type at the command line are preceded by the common
shell primary prompt, @samp{$}. The command itself is printed @kbd{in
this font}, and the output it produces @samp{in this font}, for
example:
@example
$ @kbd{mailfromd --version}
mailfromd (mailfromd @value{VERSION})
@end example
In the text, the command names are printed @command{like this},
command line options are displayed in @option{this font}. Some
notions are emphasized @emph{like this}, and if a point needs to be made
strongly, it is done @strong{this way}. The first occurrence of
a new term is usually its @dfn{definition} and appears in the same
font as the previous occurrence of ``definition'' in this sentence.
File names are indicated like this: @file{/path/to/ourfile}.
The variable names are represented @var{like this}, keywords and
fragments of program text are written in @code{this font}.
@node Overview
@section Overview of Mailfromd
In contrast to the most existing milter filters,
@command{mailfromd} does not implement any default filtering
policies. Instead, it depends entirely on a @dfn{filter script},
supplied to it by the administrator. The script, written in a
specialized and simple to use language, called @acronym{MFL}
(@pxref{MFL}), is supposed to run a set of tests and to decide
whether the message should be accepted by the @acronym{MTA} or not.
To perform the tests, the script can examine the values of
@command{Sendmail} macros, use an extensive set of built-in
and library functions, and invoke user-defined functions.
@node SAV
@section Sender Address Verification.
@cindex sender address verification, described
@cindex callout, described
@dfn{Sender address verification}, or @dfn{callout}, is one of the
basic mail verification techniques, implemented by
@command{mailfromd}. It consists in probing each @acronym{MX} server
for the given address, until one of them gives a definite (positive or
negative) reply. Using this technique you can block a sender address
if it is not deliverable, thereby cutting off a large amount of spam.
It can also be useful to block mail for undeliverable recipients, for
example on a mail relay host that does not have a list of all the
valid recipient addresses. This prevents undeliverable junk mail from
entering the queue, so that your @acronym{MTA} doesn't have to waste
resources trying to send @samp{MAILER-DAEMON} messages back.
Let's illustrate how it works on an example:
@anchor{standard verification}
@cindex standard address verification
@cindex probe message
@cindex account probing
Suppose that the user @samp{<jsmith@@somedomain.net>} is trying to
send mail to one of your local users. The remote machine connects to
your @acronym{MTA} and issues @code{MAIL FROM: <jsmith@@somedomain.net>}
command. However, your @acronym{MTA} does not have to take its word for it, so
it uses @command{mailfromd} to verify the sender address
validity. @command{Mailfromd} strips the domain name from the address
(@samp{somedomain.net}) and queries @acronym{DNS} about @samp{MX} records for that
domain. Suppose, it receives the following list
@multitable @columnfractions 0.2 0.7
@item 10 @tab relay1.somedomain.net
@item 20 @tab relay2.somedomain.net
@end multitable
It then connects to first @acronym{MX} server, using @acronym{SMTP}
protocol, as if it were going to send a message to
@samp{<jsmith@@somedomain.net>}. This is called sending a
@dfn{probe message}. If the server accepts the recipient address, the
@command{mailfromd} accepts the incoming mail. Otherwise, if the
server rejects the address, the mail is rejected as well. If the @acronym{MX}
server cannot be connected, @command{mailfromd} selects next server
from the list and continues this process until it finds the
answer or the list of servers is exhausted.
The @dfn{probe message} is like a normal mail except that no data
are ever being sent. The probe message transaction in our example
might look as follows (@samp{S:} meaning messages sent by remote
@acronym{MTA}, @samp{C:} meaning those sent by @command{mailfromd}):
@example
C: HELO mydomain.net
S: 220 OK, nice to meet you
C: MAIL FROM: <>
S: 220 <>: Sender OK
C: RCPT TO: <jsmith@@somedomain.net>
S: 220 <jsmith@@remote.net>: Recipient OK
C: QUIT
@end example
Probe messages are never delivered, deferred or bounced; they are
always discarded.
@cindex strict address verification
The described method of address verification is called
a @dfn{standard} method throughout this document. @command{Mailfromd}
also implements a method we call @dfn{strict}. When using strict
method, @command{mailfromd} first resolves @acronym{IP} address of sender
machine to a fully qualified domain name. Then it obtains @samp{MX} records
for this machine, and then proceeds with probing as described above.
So, the difference between the two methods is in the set of @samp{MX}
records that are being probed: standard method queries @samp{MX}s based on
the sender email domain, strict method works with @samp{MX}s for the sender
@acronym{IP} address.
Strict method allows to cut off much larger amount of spam,
although it does have many drawbacks. Returning to our example above,
consider the following situation: @samp{<jsmith@@somedomain.net>} is a
perfectly normal address, but it is being used by a spammer from some
other domain, say @samp{otherdomain.com}. The standard method is not able
to cope with such cases, whereas the strict one is.
An alert reader will ask: what happens if @command{mailfromd} is
not able to get a definite answer from any of @acronym{MX} servers? Actually,
it depends entirely on how you will instruct it to act in this case,
but the general practice is to return temporary failure, which will
urge the remote party to retry sending their message later.
@cindex caching @acronym{DNS} requests
After receiving a definite answer, @command{mailfromd} will
cache it in its database, so that next time your @acronym{MTA} receives a
message from that address (or from the sender @acronym{IP}/email address pair,
for strict method), it will not waste its time trying to reach @acronym{MX}
servers again. The records remain in the cache database for a certain
time, after which they are discarded.
@menu
* Limitations::
@end menu
@node Limitations
@subsection Limitations of Sender Address Verification
@cindex sender address verification, limitations
Before deciding whether and how to use sender address
verification, you should be aware of its limitations.
Both standard and strict methods suffer from the following
limitations:
@itemize @bullet
@item The sender verification methods will perform poorly on highly
loaded sites. The traffic and/or resource usage overhead may not be
feasible for you. However, you may experiment with various
@command{mailfromd} options to find an optimal configuration.
@item Some sites may blacklist your @acronym{MTA} if it probes them too
often. @command{Mailfromd} eliminates this drawback by using a
@dfn{cache database}, which keeps results of the recent callouts.
@item When verifying the remote address, no attempt to actually
deliver the message is made. If @acronym{MTA} accepts the address,
@command{mailfromd} assumes it is OK. However in reality, a mail for a
remote address can bounce @emph{after} the nearest @acronym{MTA} accepts the
recipient address.
This drawback can often be avoided by combining sender address
verification with greylisting (@pxref{Greylisting}).
@item If the remote server rejects the address, no attempt is being
made to discern between various reasons for rejection (client
rejected, @samp{HELO rejected}, @samp{MAIL FROM} rejected, etc.)
@item Some major sites such as @indicateurl{yahoo.com} do not
reject unknown addresses in reply to the @samp{RCPT TO} command, but report a
delivery failure in response to end of @samp{DATA} after a message is
transferred. Of course, sender address verification does not work with such
sites. However, a combination of address verification and greylisting
(@pxref{Greylisting}) may be a good choice in such cases.
@end itemize
In addition, strict verification breaks forward mail
delivery. This is obvious, since mail forwarding is based on
delivering unmodified message to another location, so the sender
address domain will most probably not be the same as that of the @acronym{MTA}
doing the forwarding.
@node Rate Limit
@section Controlling Mail Sending Rate.
@cindex mail sending rate, explained
@cindex sending rate, explained
@dfn{Mail Sending Rate} for a given identity is defined as the number of
messages with this identity received within a predefined interval of
time.
@acronym{MFL} offers a set of functions for limiting mail sending
rate (@pxref{Rate limiting functions}), and for controlling broader
rate aspects, such as data transfer rates (@pxref{TBF}).
@node SPF
@section SPF, DKIM, and others
@cindex @acronym{SPF}
@cindex Sender Policy Framework
@dfn{Sender Policy Framework}, or @acronym{SPF} for short, is an
extension to @acronym{SMTP} protocol that allows to identify forged
identities supplied with the @code{MAIL FROM} and @code{HELO}
commands. The framework is explained in detail in @acronym{RFC} 4408
(@uref{http://tools.ietf.org/html/rfc4408}) and on the
@uref{http://www.openspf.org/, SPF Project Site}.
Mailfromd provides a set of functions for using @acronym{SPF} to
control mail flow. These are described in @ref{SPF Functions}.
@cindex DKIM
@dfn{DomainKeys Identified Mail} (@acronym{DKIM}) is an email
authentication method designed to detect forged sender addresses in
emails. Mailfromd supports both @acronym{DKIM} signing and
verification. @xref{DKIM}, for a detailed description of these
features.
Mailfromd also provides support for some third-party
spam-abatement programs, namely @command{SpamAssassin},
and @command{ClamAV}. These are discussed in
@ref{Interfaces to Third-Party Programs}.
@node Building
@chapter Building the Package
@cindex building @command{mailfromd}
@cindex @command{mailfromd}, building
This chapter contains a detailed list of steps you need to
undertake in order to configure and build the package.
@enumerate 1
@item Make sure you have the necessary software installed.
To build @command{mailfromd} you will need to have following
packages on your machine:
@enumerate A
@item GNU mailutils version 3.3 or newer.
@cindex mailutils
GNU mailutils is a general-purpose library for handling electronic mail.
It is available from @uref{http://mailutils.org}.
@item GNU adns library, version 1.5.1 or newer.
@cindex adns
GNU adns is an advanced DNS client library. The recent version can be
downloaded from @uref{http://www.chiark.greenend.org.uk/~ian/adns/adns.tar.gz}.
Visit @uref{http://www.gnu.org/software/adns}, for more information.
@item A @acronym{DBM} library.
@anchor{DBM}
@cindex @acronym{DBM}
@cindex Berkeley DB
@cindex @acronym{GDBM}
@cindex @option{--with-dbm}, @command{configure} option
@command{Mailfromd} is able to link with any flavor of
@acronym{DBM} supported by GNU mailutils. As of version
@value{VERSION} it will refuse to build without @acronym{DBM}.
By default, @command{configure} will try to
find the best implementation installed on your machine (preference is
given to Berkeley DB) and will use it. You can, however, explicitly
specify which implementation you want to use. To do so, use the
@option{--with-dbm} configure option. Its argument specifies the
@dfn{type} of database to use. It must be one of the types supported
by GNU mailutils. At the time of this writing, these are:
@table @asis
@item bdb
Berkeley DB (versions 2 to 6).
@item gdbm
GNU DBM.
@item kc
Kyoto Cabinet
@item tc
Tokyo Cabinet
@item ndbm
NDBM
@end table
To check what database types are supported by your version of
mailutils, run the following command:
@example
$ @kbd{mailutils dbd gdbm kc tc ndbm}
@end example
For backward compatibility, @command{configure} accepts the following
two options:
@table @option
@item --with-gdbm
Same as @option{--with-dbm=gdbm}.
@item --with-berkeley-db
Same as @option{--with-dbm=bdb}.
@end table
@cindex confMAPDEF, @command{Sendmail} macro
For @command{Sendmail} users, it often makes sense to configure
@command{mailfromd} to use the same database flavor as @command{sendmail}.
The following table will help you do that. The column @samp{DB type} lists
types of @acronym{DBM} databases supported by @command{mailfromd}.
The column @samp{confMAPDEF} lists the value of @code{confMAPDEF} Sendmail
configuration macro corresponding to that database type. The column
@samp{configure option} contains the corresponding option to configure.
@multitable @columnfractions 0.25 .25 .50
@headitem DB type @tab confMAPDEF @tab configure option
@item NDBM @tab @option{-NNDBM} @tab @option{--with-dbm=ndbm}
@item Berkeley DB @tab @option{-NNEWDB} @tab @option{--with-dbm=bdb}
@item GDBM @tab N/A @tab @option{--with-dbm=gdbm}
@end multitable
@end enumerate
@anchor{default user privileges}
@cindex default user privileges
@cindex @code{DEFAULT_USER}, @command{configure} variable
@item Decide what user privileges will be used to run @command{mailfromd}
After startup, the program drops root privileges. By default,
it switches to the privileges of user @samp{mail}, group @samp{mail}.
If there is no such user on your system, or you wish to use another
user account for this purpose, override it using @var{DEFAULT_USER}
environment variable. For example for @command{mailfromd} to run as
user @samp{nobody}, use
@example
./configure DEFAULT_USER=nobody
@end example
@noindent
The user name can also be changed at run-time (@pxref{--user}).
@cindex @option{--prefix}, @command{configure} option
@item Decide where to install @command{mailfromd} and where its
filter script and data files will be located.
As usual, the default value for the installation prefix is
@file{/usr/local}. If it does not suit you, specify another location
using @option{--prefix} option, e.g.: @samp{--prefix=/usr}.
During installation phase, the build system will install several
files. These files are:
@table @file
@item @var{prefix}/sbin/mailfromd
Main daemon. @xref{Invocation,, mailfromd}.
@anchor{default script file}
@item @var{prefix}/etc/mailfromd.mfl
Default main filter script file. It is installed only if it is
not already there. Thus, if you are upgrading to a newer version of
@command{mailfromd}, your old script file will be preserved
with all your changes.
@xref{MFL}, for a description of the mail filtering language.
@item @var{prefix}/share/mailfromd/@value{VERSION}/*.mfl
@acronym{MFL} modules. @xref{Modules}.
@item @var{prefix}/share/mailfromd/@value{VERSION}/include/pp-setup
Default preprocessor setup file. @xref{Preprocessor}.
@item @var{prefix}/info/mailfromd.info*
Documentation files.
@item @var{prefix}/bin/mtasim
@acronym{MTA} simulator program for testing @command{mailfromd}
scripts. @xref{mtasim}.
@item @var{prefix}/sbin/pmult
Pmilter multiplexor for @command{MeTA1}. @xref{pmult}.
It is build only if @command{MeTA1} version @samp{PreAlpha29.0} or
newer is installed on the system. You may disable it by using the
@option{--disable-pmilter} command line option.
When testing for @command{MeTA1} presence, @command{configure} assumes
its default location. If it is not found there, inform
@command{configure} about its actual location by using the following
option:
@example
--enable-pmilter=@var{prefix}
@end example
@noindent
where @var{prefix} stands for the @command{MeTA1} installation prefix.
@end table
@cindex @option{--sysconfdir}, @command{configure} option
It is advisable to use the same settings for file name prefixes
as those you used when configuring @command{mailutils}. In particular,
try to use the same @option{--sysconfdir}, since it will facilitate
configuring the whole system.
@anchor{statedir}
@cindex local state directory
@cindex DEFAULT_STATE_DIR, @command{configure} variable
Another important point is location of @dfn{local state
directory}, i.e. a directory where @command{mailfromd} keeps its
data files (e.g. communication socket, @acronym{PID}-file and database
files). By default, its full name is
@file{@var{localstatedir}/mailfromd}. You can change it by setting
@code{DEFAULT_STATE_DIR} configuration variable. This value can be
changed at run-time using the @code{state-directory} configuration
statement (@pxref{conf-base, state-directory}). To inspect the
actual value of the local state directory, run
@example
mailfromd --no-config --show-defaults | grep '^statedir:'
@end example
@xref{Examining Defaults}, for information about the
@option{--show-defaults} option.
@item Select default communication socket.
@cindex default communication socket
@cindex default communication port
@cindex DEFAULT_SOCKET, @command{configure} variable
This is the socket used to communicate with @acronym{MTA}, in
the usual @command{Milter} port notation (@pxref{milter port specification}).
If the socket name does not begin with a protocol or directory
separator, it is assumed to be a @acronym{UNIX} socket, located in the
local state directory. The default value is @file{mailfrom}, which is
equivalent to @file{unix:@var{localstatedir}/mailfromd/mailfrom}.
To alter this, use @code{DEFAULT_SOCKET} environment variable, e.g.:
@example
./configure DEFAULT_SOCKET=inet:999@@localhost
@end example
@noindent
The communication socket can be changed at run time using
@option{--port} command line option (@pxref{--port}) or the
@code{listen} configuration statement (@pxref{conf-server, listen}).
@item Select default expiration interval.
@cindex default expiration interval
@cindex DEFAULT_EXPIRE_INTERVAL, @command{configure} variable
@dfn{Expiration interval} defines the period of time during which a
record in the @command{mailfromd} database is considered valid. It is
described in more detail in @ref{Databases}. The default value is
86400 seconds, i.e. 24 hours. It is @sc{ok} for most sites. If, however,
you wish to change it, use @var{DEFAULT_EXPIRE_INTERVAL} environment
variable.
@cindex DEFAULT_EXPIRE_RATES_INTERVAL, @command{configure} variable
The @code{DEFAULT_EXPIRE_RATES_INTERVAL} variable sets default expiration time for mail rate database (@pxref{Rate limiting functions}).
Expiration settings can be changed at run time using
@code{database} statement in the @command{mailfromd} configuration file
(@pxref{conf-database}).
@cindex enable-syslog-async, @option{--enable-syslog-async}, @command{configure} option
@cindex syslog, non-blocking
@item Select a @command{syslog} implementation to use.
@anchor{syslog-async}
@command{Mailfromd} uses @code{syslog} for diagnostics output. The
default @code{syslog} implementation on most systems (most notably, on
GNU/Linux) uses blocking @code{AF_UNIX SOCK_DGRAM} sockets. As a
result, when an application calls @code{syslog()}, and
@command{syslogd} is not responding and the socket buffers get full,
the application will hang.
@cindex Simon Kelley
For @command{mailfromd}, as for any daemon, it is more important
that it continue to run, than that it continue to log. For this
purpose, @command{mailfromd} is shipped with a non-blocking
@code{syslog} implementation by Simon Kelley. This implementation,
instead of blocking, buffers log lines in memory. When the buffer log
overflows, some lines are lost, but the daemon continues to run. When
lines are lost, this fact is logged with a message of the form:
@example
async_syslog overflow: 5 log entries lost
@end example
To enable this implementation, configure the package with
@option{--enable-syslog-async} option, e.g.:
@example
./configure --enable-syslog-async
@end example
@cindex @code{DEFAULT_SYSLOG_ASYNC}, @command{configure} variable
Additionally, you can instruct @command{mailfromd} to use
asynchronous syslog by default. To do so, set
@code{DEFAULT_SYSLOG_ASYNC} to 1, as shown in example below:
@example
./configure --enable-syslog-async DEFAULT_SYSLOG_ASYNC=1
@end example
You will be able to override these defaults at run-time by using
the @option{--logger} command line option
(@pxref{Logging and Debugging}).
@item Run @command{configure} with all the desired options.
For example, the following command:
@example
./configure DEFAULT_SOCKET=inet:999@@localhost --with-berkeley-db=3
@end example
@noindent
will configure the package to use Berkeley DB database, version 2,
and @samp{inet:999@@localhost} as the default communication socket.
At the end of its run @command{configure} will print a concise
summary of its configuration settings. It looks like that (with the
long lines being split for readability):
@example
@group
*******************************************************************
Mailfromd configured with the following settings:
Mailutils version......................... 3.15
External preprocessor..................... /usr/bin/m4 -s
DBM version............................... Berkeley DB v. 3
Default user.............................. mail
State directory...........................
$(localstatedir)/$(PACKAGE)
Socket.................................... mailfrom
Expiration interval....................... 86400
Compile asynchronous syslog............... no
Readline (for mtasim)..................... yes
Documentation rendition type.............. PROOF
Enable pmilter support.................... no
Enable GeoIP2 support..................... no
Enable DKIM support....................... yes
IPv6 support.............................. yes
*******************************************************************
@end group
@end example
Make sure these settings satisfy your needs. If they do not,
reconfigure the package with the right options.
@item Run @command{make}.
@item Run @command{make} install.
@item Make sure @file{@var{localstatedir}/mailfromd} has the right owner
and mode.
@item Examine filter script file
(@file{@var{sysconfdir}/mailfromd.mfl}) and edit it, if necessary.
@item If you are upgrading from an earlier release of Mailfromd, refer to
@ref{Upgrading}, for detailed instructions.
@end enumerate
@node Tutorial
@chapter Tutorial
This chapter contains a tutorial introduction, guiding you
through various @command{mailfromd} configurations, starting from the
simplest ones and proceeding up to more advanced forms. It omits
most complicated details, concentrating mainly on the
common practical tasks.
If you are familiar with @command{mailfromd}, you can skip this
chapter and go directly to the next one (@pxref{MFL}), which contains
detailed discussion of the mail filtering language and
@command{mailfromd} interaction with the Mail Transport Agent.
@menu
* Start Up::
* Simplest Configurations::
* Conditional Execution::
* Functions and Modules::
* Domain Name System::
* Checking Sender Address::
* SMTP Timeouts::
* Avoiding Verification Loops::
* HELO Domain::
* rset::
* Controlling Number of Recipients::
* Sending Rate::
* Greylisting::
* Local Account Verification::
* Databases::
* Testing Filter Scripts::
* Run Mode::
* Examining Defaults::
* Logging and Debugging::
* Runtime errors::
* Notes::
@end menu
@node Start Up
@section Start Up
@cindex @acronym{MTA}
@cindex Mail Transfer Agent (@acronym{MTA})
The @command{mailfromd} utility runs as a standalone @dfn{daemon}
program and listens on a predefined communication channel for requests
from the @dfn{Mail Transfer Agent} (@acronym{MTA}, for short). When
processing each message, the @acronym{MTA} installs communication with
@command{mailfromd}, and goes through several states, collecting the
necessary data from the sender. At each state it sends the relevant
information to @command{mailfromd}, and waits for it to reply. The
@command{mailfromd} filter receives the message data through
@dfn{Sendmail macros} and runs a @dfn{handler program}
defined for the given state. The result of this run is a @dfn{response
code}, that it returns to the @acronym{MTA}. The following response
codes are defined:
@table @code
@anchor{continue}
@item continue
@cindex continue action, introduced
Continue message processing from next milter state.
@anchor{accept}
@item accept
@cindex accept action, introduced
Accept this message for delivery. After receiving this code the
@acronym{MTA} continues processing this message without
further consulting @command{mailfromd} filter.
@anchor{reject}
@item reject
@cindex reject action, introduced
Reject this message. The message processing stops at this stage, and the
sender receives the reject reply (@samp{5@var{xx}} reply code). No
further @command{mailfromd} handlers are called for this message.
@anchor{discard}
@item discard
@cindex discard action, introduced
Silently discard the message. This means that @acronym{MTA} will
continue processing this message as if it were going to deliver it,
but will discard it after receiving. No further interaction with
@command{mailfromd} occurs.
@anchor{tempfail}
@item tempfail
@cindex tempfail action, introduced
Temporarily reject the message. The message processing stops at this
stage, and the sender receives the @samp{temporary failure} reply
(@samp{4@var{xx}} reply code). No further @command{mailfromd}
handlers are called for this message.
@end table
@cindex filter script, described
The instructions on how to process the message are supplied to
@command{mailfromd} in its @dfn{filter script file}. It is normally
called @file{/usr/local/etc/mailfromd.mfl} (but can be located elsewhere,
@pxref{Invocation}) and contains a set of @dfn{milter state handlers},
or subroutines to be executed in various @acronym{SMTP} states. Each
interaction state can be supplied its own handling procedure. A
missing procedure implies @code{continue} response code.
@anchor{milter state}
@cindex milter state handler, described
@cindex handler, described
@cindex connect, handler
@cindex helo, handler
@cindex envfrom, handler
@cindex envrcpt, handler
@cindex data, handler
@cindex header, handler
@cindex eoh, handler
@cindex body, handler
@cindex eom, handler
@cindex begin, special handler
@cindex end, special handler
@anchor{handler names}
The filter script can define up to nine @dfn{milter state handlers},
called after the names of milter states: @samp{connect}, @samp{helo},
@samp{envfrom}, @samp{envrcpt}, @samp{data}, @samp{header}, @samp{eoh},
@samp{body}, and @samp{eom}. The @samp{data} handler is invoked only
if @acronym{MTA} uses Milter protocol version 3 or later. Two special
handlers are available for initialization and clean-up purposes:
@samp{begin} is called before the processing starts, and @samp{end} is
called after it is finished. The diagram below shows the control flow
when processing an @acronym{SMTP} transaction. Lines marked with
@code{C:} show @acronym{SMTP} commands issued by the remote machine (the
@dfn{client}), those marked with @samp{@result{}} show called handlers
with their arguments. An @samp{[R]} appearing at the start of a line
indicates that this part of the transaction can be repeated any number
of times:
@float Figure, milter-control-flow
@caption{Mailfromd Control Flow}
@example
@group
@result{} begin()
@result{} connect(@var{hostname}, @var{family}, @var{port}, @samp{IP address})
C: HELO @var{domain}
helo(@var{domain})
for each message transaction
do
C: MAIL FROM @var{sender}
@result{} envfrom(@var{sender})
[R] C: RCPT TO @var{recipient}
@result{} envrcpt(@var{recipient})
C: DATA
@result{} data()
[R] C: @var{header}: @var{value}
@result{} header(@var{header}, @var{value})
C:
@result{} eoh()
[R] C: @var{body-line}
@result{} /* @r{Collect lines into blocks @var{blk} of}
@result{} * @r{at most @var{len} bytes and for each}
@result{} * @r{such block call:}
@result{} */
@result{} body(@var{blk}, @var{len})
C: .
@result{} eom()
done
@result{} end()
@end group
@end example
@end float
This control flow is maintained for as long as each called handler
returns @code{continue} (@pxref{Actions}). Otherwise, if
any handler returns @code{accept} or @code{discard}, the message
processing continues, but no other handler is called. In the case
of @code{accept}, the @acronym{MTA} will accept the message for
delivery, in the case of @code{discard} it will silently discard it.
If any of the handlers returns @code{reject} or @code{tempfail}, the
result depends on the handler. If this code is returned by
@code{envrcpt} handler, it causes this particular recipient address to
be rejected. When returned by any other handler,
it causes the whole message will be rejected.
The @code{reject} and @code{tempfail} actions executed by
@code{helo} handler do not take effect immediately. Instead, their
action is deferred until the next @acronym{SMTP} command from the
client, which is usually @code{MAIL FROM}.
@node Simplest Configurations
@section Simplest Configurations
@cindex handler declaration
@cindex milter state handler, declaring
@cindex state handler, declaring
@cindex declaring milter state handler
The @command{mailfromd} script file contains a
series of @dfn{declarations} of the handler procedures. Each
declaration has the form:
@example
@group
prog @var{name}
do
@dots{}
done
@end group
@end example
@noindent
where @code{prog}, @code{do} and @code{done} are the @dfn{keywords},
and @var{name} is the state name for this handler. The dots in the
above example represent the actual @dfn{code}, or a set of
commands, instructing @command{mailfromd} how to process the message.
For example, the declaration:
@example
@group
prog envfrom
do
accept
done
@end group
@end example
@noindent
installs a handler for @samp{envfrom} state, which always approves the
message for delivery, without any further interaction with
@command{mailfromd}.
@cindex actions, introduced
The word @code{accept} in the above example is an @dfn{action}.
@dfn{Action} is a special language statement that instructs the
run-time engine to stop execution of the program and to return a
response code to the @command{Sendmail}. There are five actions, one
for each response code: @code{continue}, @code{accept}, @code{reject},
@code{discard}, and @code{tempfail}. Among these, @code{reject} and
@code{discard} can optionally take one to three arguments. There are
two ways of supplying the arguments.
In the first form, called @dfn{literal} or @dfn{traditional} notation,
the arguments are supplied as additional words after the action name,
separated by whitespace. The first argument is a three-digit
@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
@code{reject} and with @samp{4} for @code{tempfail}. If two arguments
are supplied, the second argument must be either an @dfn{extended
reply code} (@acronym{RFC} 1893/2034) or a textual string to be
returned along with the @acronym{SMTP} reply. Finally, if all three
arguments are supplied, then the second one must be an extended reply
code and the third one must supply the textual string. The following
examples illustrate all possible ways of using the @code{reject}
statement in literal notation:
@example
@group
reject
reject 503
reject 503 5.0.0
reject 503 "Need HELO command"
reject 503 5.0.0 "Need HELO command"
@end group
@end example
@noindent
Please note the quotes around the textual string.
Another form for these action is called @dfn{functional} notation,
because it resembles the function syntax. When used in this form, the
action word is followed by a parenthesized group of exactly three
arguments, separated by commas. The meaning and ordering of the
argument is the same as in literal form. Any of three arguments may
be absent, in which case it will be replaced by the default value. To
illustrate this, here are the statements from the previous example,
written in functional notation:
@example
@group
reject(,,)
reject(503,,)
reject(503, 5.0.0)
reject(503,, "Need HELO command")
reject(503, 5.0.0, "Need HELO command")
@end group
@end example
@node Conditional Execution
@section Conditional Execution
Programs consisting of a single action are rarely useful. In most
cases you will want to do some checking and decide whether to process
the message depending on its result. For example, if you do not want
to accept messages from the address @samp{<badguy@@some.net>}, you
could write the following program:
@example
@group
prog envfrom
do
if $f = "badguy@@some.net"
reject
else
accept
fi
done
@end group
@end example
This example illustrates several important concepts. First or
all, @code{$f} in the third line is a @dfn{Sendmail macro
reference}. Sendmail macros are referenced the same way as in
@file{sendmail.cf}, with the only difference that curly braces around
macro names are optional, even if the name consists of several
letters. The value of a macro reference is always a string.
The equality operator (@samp{=}) compares its left and right
arguments and evaluates to true if the two strings are exactly the
same, or to false otherwise. Apart from equality, you can use the
regular relational operators: @samp{!=}, @samp{>}, @samp{>=}, @samp{<}
and @samp{<=}. Notice that string comparison in @command{mailfromd}
is always case sensitive. To do case-insensitive comparison,
translate both operands to upper or lower case (@xref{tolower}, and
@pxref{toupper}).
The @code{if} statement decides what actions to execute depending
on the value its condition evaluates to. Its usual form is:
@example
if @var{expression} @var{then-body} [else @var{else-body}] fi
@end example
The @var{then-body} is executed if the @var{expression} evaluates to
@code{true} (i.e. to any non-zero value). The optional
@var{else-body} is executed if the @var{expression} yields
@code{false} (i.e. zero). Both @var{then-body} and @var{else-body} can contain
other @code{if} statements, their nesting depth is not limited. To
facilitate writing complex conditional statements, the @code{elif}
keyword can be used to introduce alternative conditions, for example:
@example
@group
prog envfrom
do
if $f = "badguy@@some.net"
reject
elif $f = "other@@domain.com"
tempfail 470 "Please try again later"
else
accept
fi
done
@end group
@end example
@xref{switch}, for more elaborate forms of conditional branching.
@node Functions and Modules
@section Functions and Modules
@cindex function, defined
As any programming language, @acronym{MFL} supports a concept of
@dfn{function}, i.e. a body of code that is assigned a unique name and
can be invoked elsewhere as many times as needed.
All functions have a @dfn{definition} that introduces types and
names of the formal parameters and the result type, if the function is
to return a meaningful value (function definitions in @acronym{MFL}
are discussed in detail in @pxref{User-defined, User-Defined Functions}).
@anchor{funcall}
@cindex function calls
A function is invoked using a special construct, a @dfn{function
call}:
@example
@var{name} (@var{arg-list})
@end example
@noindent
where @var{name} is the function name, and @var{arg-list} is a
comma-separated list of expressions. Each expression in
@var{arg-list} is evaluated, and its type is compared with that of the
corresponding formal argument. If the types differ, the expression is
converted to the formal argument type. Finally, a copy of its value
is passed to the function as a corresponding argument. The order in
which the expressions are evaluated is not defined. The compiler
checks that the number of elements in @var{arg-list} match the
number of mandatory arguments for function @var{name}.
If the function does not deliver a result, it should only be called
as a statement.
Functions may be recursive, even mutually recursive.
@cindex built-in and library functions, introduced
@cindex library and built-in functions, introduced
@cindex module, defined
@kwindex require
@command{Mailfromd} comes with a rich set of predefined functions
for various purposes. There are two basic function classes:
@dfn{built-in} functions, that are implemented by the @acronym{MFL}
runtime environment in @command{mailfromd}, and @dfn{library}
functions, that are implemented in @acronym{MFL}. The built-in
functions are always available and no preparatory work is needed before
calling them. In contrast, the library functions are defined in
@dfn{modules}, special @acronym{MFL} source files that contain functions
designed for a particular task. In order to access a library
function, you must first @dfn{require} a module it is defined in.
This is done using @code{require} statement. For example, the
function @code{hostname} looks up in the @acronym{DNS} the name
corresponding to the @acronym{IP} address specified as its argument. This
function is defined in module @file{dns.mfl}, so before calling it you
must require this module:
@example
require dns
@end example
@noindent
The @code{require} statement takes a single argument: the name of the
requested module (without the @samp{.mfl} suffix). It looks up the
module on disk and loads it if it is available.
For more information about the module system @xref{Modules}.
@node Domain Name System
@section Domain Name System
Site administrators often do not wish to accept mail from hosts that
do not have a proper reverse delegation in the Domain Name System.
In the previous section we introduced the library function
@code{hostname}, that looks up in the @acronym{DNS} the name corresponding to
the @acronym{IP} address specified as its argument. If there is no
corresponding name, the function returns its argument unchanged. This
can be used to test if the @acronym{IP} was resolved, as illustrated in the
example below:
@example
@group
require 'dns'
prog envfrom
do
if hostname($client_addr) = $client_addr
reject
fi
done
@end group
@end example
The @code{#require dns} statement loads the module @file{dns.mfl},
after which the definition of @code{hostname} becomes available.
A similar function, @code{resolve}, which resolves the symbolic
name to the corresponding @acronym{IP} address is provided in the same
@file{dns.mfl} module.
@node Checking Sender Address
@section Checking Sender Address
A special language construct is provided for verification of
sender addresses (@dfn{callout}):
@example
@group
on poll $f do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
@end group
@end example
The @code{on poll} construct runs standard verification
(@pxref{standard verification}) for the email address specified as its
argument (in the example above it is the value of the Sendmail macro
@samp{$f}). The check can result in the following conditions:
@table @code
@item success
The address exists.
@item not_found
The address does not exist.
@item failure
Some error of permanent nature occurred during the check. The
existence of the address cannot be verified.
@item temp_failure
Some temporary failure occurred during the check. The
existence of the address cannot be verified at the moment.
@end table
The @code{when} branches of the @code{on poll} statement
introduce statements, that are executed depending on the actual
return condition. If any condition occurs that is not handled within
the @code{on} block, the run-time evaluator will signal an
@dfn{exception}@footnote{For more information about exceptions and
their handling, please refer to @ref{Exceptions}.} and return temporary
failure, therefore it is advisable to always handle all four
conditions. In fact, the condition handling shown in the above
example is preferable for most normal configurations: the mail is
accepted if the sender address is proved to exist and rejected
otherwise. If a temporary failure occurs, the remote party is urged
to retry the transaction some time later.
The @code{poll} statement itself has a number of options that
control the type of the verification. These are discussed in detail
in @ref{poll}.
It is worth noticing that there is one special email
address which is always available on any host, it is the @dfn{null
address} @samp{<>} used in error reporting. It is of no use verifying
its existence:
@example
@group
prog envfrom
do
if $f == ""
accept
else
on poll $f do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
fi
done
@end group
@end example
@node SMTP Timeouts
@section @acronym{SMTP} Timeouts
When using polling functions, it is important to take into account
possible delays, which can occur in @acronym{SMTP} transactions. Such
delays may be due to low network bandwidth or high load on the remote
server. Some sites impose them willingly, as a spam-fighting measure.
@cindex timeout escalation
Ideally the callout verification should use the timeout values
defined in the RFC 2822, but this is impossible in practice, because
it would cause a @dfn{timeout escalation}, which consists in
propagating delays encountered in a callout @acronym{SMTP} session
back to the remote client whose session initiated the callout.
Consider, for example, the following scenario. An @acronym{MFL}
script performs a callout on @samp{envfrom} stage. The remote server
is overloaded and delays heavily in responding, so that the initial response
arrives 3 minutes after establishing the connection, and processing
the @samp{EHLO} command takes another 3 minutes. These delays are
OK according to the RFC, which imposes a 5 minute limit for each stage,
but while waiting for the remote reply our @acronym{SMTP} server
remains in the @samp{envfrom} state with the client waiting
for a response to its @samp{MAIL} command more than 6 minutes, which is
intolerable, because of the same 5 minute limit. Thus, the client
will almost certainly break the session.
@anchor{callout server}
@cindex callout server
@cindex server, callout
@cindex soft SMTP timeout
@cindex hard STMP timeout
To avoid this, @command{mailfromd} uses a special instance, called
@dfn{callout server}, which is responsible for running callout
@acronym{SMTP} sessions asynchronously. The usual sender verification
is performed using so-called @dfn{soft} timeout values, which
are set to values short enough to not disturb the incoming session
(e.g. a timeout for @samp{HELO} response is 3 seconds, instead of 5
minutes). If this verification yields a definite answer, that answer
is stored in the cache database and returned to the calling procedure
immediately. If, however, the verification is aborted due to a timeout,
the caller procedure is returned an @samp{e_temp_failure} exception, and
the callout is scheduled for processing by a callout server. This
exception normally causes the milter session to return a temporary
error to the sender, urging it to retry the connection later.
In the meantime, the callout server runs the sender verification
again using another set of timeouts, called @dfn{hard} timeouts, which
are normally much longer than @samp{soft} ones (they default to the
values required by RFC 2822). If it gets a definitive result (e.g.
@samp{email found} or @samp{email not found}), the server stores it
in the cache database. If the callout ends due to a timeout, a
@samp{not_found} result is stored in the database.
Some time later, the remote server retries the delivery, and the
@command{mailfromd} script is run again. This time, the callout
function will immediately obtain the already cached result from the
database and proceed accordingly. If the callout server has not
finished the request by the time the sender retries the connection,
the latter is again returned a temporary error, and the process
continues until the callout is finished.
Usually, callout server is just another instance of
@command{mailfromd} itself, which is started automatically to
perform scheduled SMTP callouts. It is also possible to set up
a separate callout server on another machine. This is discussed in
@ref{calloutd}.
For a detailed information about callout timeouts and their
configuration, see @ref{conf-timeout}.
For a description of how to configure @command{mailfromd} to use callout
servers, see @ref{conf-server}.
@node Avoiding Verification Loops
@section Avoiding Verification Loops
An @code{envfrom} program consisting only of the @code{on poll}
statement will work smoothly for incoming mails, but will create
infinite loops for outgoing mails. This is because upon sending an outgoing
message @command{mailfromd} will start the verification procedure, which
will initiate an @acronym{SMTP} transaction with the same mail server
that runs it. This transaction will in turn trigger execution of
@code{on poll} statement, etc. @i{ad infinitum}. To avoid this, any
properly written filter script should not run the verification
procedure on the email addresses in those domains that are relayed by
the server it runs on. This can be achieved using @code{relayed}
function. The function returns @code{true} if its argument is
contained in one of the predefined @dfn{domain list} files. These
files correspond to @command{Sendmail} plain text files used in
@code{F} class definition forms (see @cite{Sendmail Installation and
Operation Guide}, chapter 5.3), i.e. they contain one domain name per
line, with empty lines and lines started with @samp{#} being ignored.
The domain files consulted by @code{relayed} function are defined
in the @code{relayed-domain-file} configuration file statement
(@pxref{conf-base, relayed-domain-file}):
@example
relayed-domain-file (/etc/mail/local-host-names,
/etc/mail/relay-domains);
@end example
@noindent
or:
@example
@group
relayed-domain-file /etc/mail/local-host-names;
relayed-domain-file /etc/mail/relay-domains;
@end group
@end example
The above example declares two domain list files, most commonly
used in @command{Sendmail} installations to keep hostnames of the server
@footnote{class @samp{w}, see @cite{Sendmail Installation and Operation
Guide}, chapter 5.2.} and names of the domains, relayed by this
server@footnote{class @samp{R}}.
Given all this, we can improve our filter program:
@example
@group
require 'dns'
prog envfrom
do
if $f == ""
accept
elif relayed(hostname($@{client_addr@}))
accept
else
on poll $f do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
fi
done
@end group
@end example
If you feel that your Sendmail's relayed domains are not restrictive
enough for @command{mailfromd} filters (for example you are relaying
mails from some third-party servers), you can use a database of
trusted mail server addresses. If the number of such servers is small
enough, a single @samp{or} statement can be used, e.g.:
@example
elif $@{client_addr@} = "10.10.10.1"
or $@{client_addr@} = "192.168.11.7"
accept
@dots{}
@end example
@noindent
otherwise, if the servers' @acronym{IP} addresses fall within one or
several @acronym{CIDR}s, you can use the @code{match_cidr} function
(@pxref{Internet address manipulation functions}), e.g.:
@example
elif match_cidr ($@{client_addr@}, "199.232.0.0/16")
accept
@dots{}
@end example
@noindent
or combine both methods. Finally, you can keep a @acronym{DBM}
database of relayed addresses and use @code{dbmap} or @code{dbget}
function for checking (@pxref{Database functions}).
@example
elif dbmap("%__statedir__/relay.db", $@{client_addr@})
accept
@dots{}
@end example
@node HELO Domain
@section HELO Domain
@cindex s, @command{Sendmail} macro
Some of the mail filtering conditions may depend on the value of
@dfn{helo domain} name, i.e. the argument to the @acronym{SMTP} @code{EHLO} (or
@code{HELO}) command. If you ever need such conditions, take into
account the following caveats. Firstly, although @command{Sendmail}
passes the helo domain in @code{$s} macro, it does not do this
consistently. In fact, the @code{$s} macro is available only to
the @code{helo} handler, all other handlers won't see it, no matter what
the value of the corresponding @code{Milter.macros.@var{handler}}
statement. So, if you wish to access its value from any
handler, other than @code{helo}, you will have to store it in a
@dfn{variable} in the @code{helo} handler and then use this variable
value in the other handler. This approach is also recommended for
another @acronym{MTA}s. This brings us to the concept of
variables in @command{mailfromd} scripts.
@cindex variables, introduced
@cindex variable declaration
A variable is declared using the following syntax:
@example
@var{type} @var{name}
@end example
@noindent
where @var{variable} is the variable name and @var{type} is
@samp{string}, if the variable is to hold a string value, and
@samp{number}, if it is supposed to have a numeric value.
@cindex variable assignment
@cindex assignment to variable
A variable is assigned a value using the @code{set} statement:
@example
set @var{name} @var{expr}
@end example
@noindent
where @var{expr} is any valid @acronym{MFL} expression.
The @code{set} statement can occur within handler or function
declarations as well as outside of them.
There are two kinds of @command{Mailfromd} variables: @dfn{global
variables}, that are visible to all handlers and functions, and
@dfn{automatic variables}, that are available only within the handler
or function where they are declared. For our purpose we need a global
variable (@xref{Variables, Variable classes}, for detailed descriptions
of both kinds of variables).
The following example illustrates an approach that allows to use
the @code{HELO} domain name in any handler:
@example
@group
# @r{Declare the helohost variable}
string helohost
prog helo
do
# @r{Save the host name for further use}
set helohost $s
done
prog envfrom
do
# @r{Reject hosts claiming to be @i{localhost}}
if helohost = "localhost"
reject 570 "Please specify real host name"
fi
done
@end group
@end example
Notice, that for this approach to work, your @acronym{MTA}
must export the @samp{s} macro (e.g., in case of Sendmail, the
@code{Milter.macros.helo} statement in the @file{sendmail.cf} file must contain
@samp{s}. @pxref{Sendmail}). This requirement can be removed by
using the @dfn{handler argument} of @code{helo}. Each
@command{mailfromd} handler is given one or several arguments. The
exact number of arguments and their meaning are handler-specific and are
described in @ref{Handlers}, and @ref{milter-control-flow}.
The arguments are referenced by their ordinal number, using the notation
@code{$@var{n}}. The @code{helo} handler takes one argument, whose
value is the helo domain. Using this information, the @code{helo}
handler from the example above can be rewritten as follows:
@example
@group
prog helo
do
# @r{Save the host name for further use}
set helohost $1
done
@end group
@end example
@node rset
@section SMTP RSET and Milter Abort Handling
@cindex RSET
@cindex milter abort
In previous section we have used a global variable to hold certain
information and share it between handlers. In the majority of cases,
such information is session specific, and becomes invalid if the
remote party issues the SMTP @code{RSET} command. Therefore,
@command{mailfromd} clears all global variables when it receives a
Milter @samp{abort} request, which is normally generated by this
command.
@kwindex precious
@cindex precious variables
@cindex variable, precious
However, you may need some variables that retain their values
even across SMTP session resets. In @command{mailfromd} terminology
such variables are called @dfn{precious}. Precious variables are
declared by prefixing their declaration with the keyword
@code{precious}. Consider, for example, this snippet of code:
@example
precious number rcpt_counter
prog envrcpt
do
set rcpt_counter rcpt_counter + 1
done
@end example
Here, the variable @samp{rcpt_counter} is declared as precious and
its value is incremented each time the @samp{envrcpt} handler is
called. This way, @samp{rcpt_counter} will keep the total number of
SMTP @code{RCPT} commands issued during the session, no matter how
many times it was restarted using the @code{RSET} command.
@node Controlling Number of Recipients
@section Controlling Number of Recipients
@cindex @code{MaxRecipientsPerMessage}, @command{sendmail} option
Any @acronym{MTA} provides a way to limit the number of recipients
per message. For example, in @command{Sendmail} you may use the
@code{MaxRecipientsPerMessage} option@footnote{@cite{Sendmail (tm)
Installation and Operation Guide}, chapter 5.6, @samp{O -- Set
Option}.}. However, such methods are not flexible, so
you are often better off using @command{mailfromd} for this purpose.
@command{Mailfromd} keeps the number of recipients collected so far
in variable @code{rcpt_count}, which can be controlled in
@code{envrcpt} handler as shown in the example below:
@example
@group
prog envrcpt
do
if rcpt_count > 10
reject 550 5.7.1 "Too many recipients"
fi
done
@end group
@end example
This filter will accept no more than 10 recipients per message.
You may achieve finer granularity by using additional conditions. For
example, the following code will allow any number of recipients if the
mail is coming from a domain relayed by the server, while limiting it
to 10 for incoming mail from other domains:
@example
@group
prog envrcpt
do
if not relayed(hostname($client_addr)) and rcpt_count > 10
reject 550 5.7.1 "Too many recipients"
fi
done
@end group
@end example
There are three important features to notice in the above code.
First of all, it introduces two @dfn{boolean} operators:
@code{and}, which evaluates to @code{true} only if both
left-side and right-side expressions are @code{true}, and @code{not},
which reverses the value of its argument.
Secondly, the scope of an operation is determined by its
@dfn{precedence}, or @dfn{binding strength}. @code{Not} binds more
tightly than @code{and}, so its scope is limited by the next
expression between it and @code{and}. Using parentheses to underline the
operator scoping, the above @code{if} condition can be rewritten as
follows:
@example
if (not (relayed(hostname($client_addr)))) and (%rcpt_count > 10)
@end example
Finally, it is important to notice that all boolean expressions
are computed using @dfn{shortcut evaluation}. To understand what it
is, let's consider the following expression: @code{@var{x} and
@var{y}}. Its value is @code{true} only if both @var{x} and @var{y}
are @code{true}. Now suppose that we evaluate the expression from
left to right and we find that @var{x} is false. This means that
no matter what the value of @var{y} is, the resulting expression will be
@code{false}, therefore there is no need to compute @var{y} at all.
So, the boolean shortcut evaluation works as follows:
@table @code
@item @var{x} and @var{y}
If @code{@var{x} @result{} @code{false}}, do not evaluate @var{y} and
return @code{false}.
@item @var{x} or @var{y}
If @code{@var{x} @result{} @code{true}}, do not evaluate @var{y} and
return @code{true}.
@end table
Thus, in the expression @code{not relayed(hostname($client_addr)) and
rcpt_count > 10}, the value of the @code{rcpt_count} variable will be
compared with @samp{10} only if the @code{relayed} function yielded
@code{false}.
To further enhance our sample filter, you may wish to make the
@code{reject} output more informative, to let the sender know what
the recipient limit is. To do so, you can use the @dfn{concatenation
operator} @samp{.} (a dot):
@example
@group
set max_rcpt 10
prog envrcpt
do
if not relayed(hostname($client_addr)) and rcpt_count > 10
reject 550 5.7.1 "Too many recipients, max=" . max_rcpt
fi
done
@end group
@end example
When evaluating the third argument to @code{reject},
@command{mailfromd} will first convert @code{max_rcpt} to string and
then concatenate both strings together, producing string @samp{Too many
recipients, max=10}.
@node Sending Rate
@section Sending Rate
We have introduced the notion of mail sending rate in @ref{Rate
Limit}. @command{Mailfromd} keeps the computed rates in the special
@code{rate} database (@pxref{Databases}). Each record in this
database consists of a @code{key}, for which the rate is computed, and
the rate value, in form of a double precision floating point number,
representing average number of messages per second sent by this
@code{key} within the last sampling interval. In the simplest case,
the sender email address can be used as a @code{key}, however we recommend
to use a conjunction @var{email}-@var{sender_ip} instead, so the
actual @var{email} owner won't be blocked by actions of some spammer
abusing his/her address.
Two functions are provided to control and update sending rates. The
@code{rateok} function takes three mandatory arguments:
@example
bool rateok(string @var{key}, number @var{interval}, number @var{threshold})
@end example
The @var{key} meaning is described above. The @var{interval} is the
sampling interval, or the number of seconds to which the actual
sending rate value is converted. Remember that it is stored
internally as a floating point number, and thus cannot be directly
used in @command{mailfromd} filters, which operate only on integer
numbers. To use the rate value, it is first converted to messages per
given interval, which is an integer number. For example, the rate
@code{0.138888} brought to 1-hour interval gives @code{500}
(messages per hour).
When the @code{rateok} function is called, it recomputes
rate record for the given @var{key}. If the new rate value converted
to messages per given @var{interval} is less than @var{threshold},
the function updates the database and returns @code{True}. Otherwise it
returns @code{False} and does not update the database.
This function must be @dfn{required} prior to use, by placing the
following statement somewhere at the beginning of your script:
@example
require rateok
@end example
For example, the following code limits the mail sending rate for each
@samp{email address}-@samp{@acronym{IP}} combination to 180 per hour.
If the actual rate value exceeds this limit, the sender is returned a
temporary failure response:
@example
@group
require rateok
prog envfrom
do
if not rateok($f . "-" . $@{client_addr@}, 3600, 180)
tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
fi
done
@end group
@end example
@noindent
Notice argument concatenation, used to produce the key.
It is often inconvenient to specify intervals in seconds,
therefore a special @code{interval} function is provided. It
converts its argument, which is a textual string representing time
interval in English, to the corresponding number of seconds. Using
this function, the function invocation would be:
@example
rateok($f . "-" . $@{client_addr@}, interval("1 hour"), 180)
@end example
The @code{interval} function is described in @ref{interval}, and time
intervals are discussed in @ref{time interval specification}.
The @code{rateok} function begins computing the rate
as soon as it has collected enough data. By default, it needs at least
four mails. Since this may lead to a big number of false positives
(i.e. overestimated rates) at the beginning of sampling interval,
there is a way to specify a minimum number of samples @code{rateok}
must collect before starting to actually compute rates. This number of
samples is given as the optional fourth argument to the function. For
example, the following call will always return @code{True} for the
first 10 mails, no matter what the actual rate:
@example
rateok($f . "-" . $@{client_addr@}, interval("1 hour"), 180, 10)
@end example
@anchor{TBF}
The @code{tbf_rate} function allows to exercise more control over
the mail rates. This function implements a @dfn{token bucket filter}
(@acronym{TBF}) algorithm.
The token bucket controls when the data can be transmitted based on
the presence of abstract entities called @dfn{tokens} in a container
called @dfn{bucket}. Each token represents some amount of data. The
algorithm works as follows:
@itemize @bullet
@item A token is added to the bucket at a constant rate of 1 token per
@var{t} microseconds.
@item A bucket can hold at most @var{m} tokens. If a token arrives
when the bucket is full, that token is discarded.
@item When @var{n} items of data arrive (e.g.@: @var{n} mails), @var{n}
tokens are removed from the bucket and the data are accepted.
@item If fewer than @var{n} tokens are available, no tokens are
removed from the bucket and the data are not accepted.
@end itemize
This algorithm allows to keep the data traffic at a constant rate
@var{t} with bursts of up to @var{m} data items. Such bursts occur
when no data was being arrived for @var{m}*@var{t} or more
microseconds.
@command{Mailfromd} keeps buckets in a database @samp{tbf}. Each
bucket is identified by a unique @dfn{key}. The @code{tbf_rate}
function is defined as follows:
@example
bool tbf_rate(string @var{key}, number @var{n}, number @var{t}, number @var{m})
@end example
The @var{key} identifies the bucket to operate upon. The rest of
arguments is described above. The @code{tbf_rate} function returns
@samp{True} if the algorithm allows to accept the data and
@samp{False} otherwise.
Depending on how the actual arguments are selected the @code{tbf_rate}
function can be used to control various types of flow rates. For
example, to control mail sending rate, assign the arguments as
follows: @var{n} to the number of mails and @var{t} to the control
interval in microseconds:
@example
@group
prog envfrom
do
if not tbf_rate($f . "-" . $client_addr, 1, 10000000, 20)
tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
fi
done
@end group
@end example
The example above permits to send at most one mail each 10 seconds.
The burst size is set to 20.
Another use for the @code{tbf_rate} function is to limit the total
delivered mail size per given interval of time. To do so, the
function must be used in @code{prog eom} handler, because it is the
only handler where the entire size of the message is known. The
@var{n} argument must contain the number of bytes in the email (or
email bytes * number of recipients), and the @var{t} must be set to
the number of bytes per microsecond a given user is allowed to send. The
@var{m} argument must be large enough to accommodate a couple of
large emails. E.g.:
@example
@group
prog eom
do
if not tbf_rate("$f-$client_addr",
message_size(current_message()),
10240*1000000, # At most 10 kb/sec
10*1024*1024)
tempfail 450 4.7.0 "Data sending rate exceeded. Try again later"
fi
done
@end group
@end example
@xref{Rate limiting functions}, for more information about
@code{rateok} and @code{tbf_rate} functions.
@node Greylisting
@section Greylisting
Greylisting is a simple method of defending against the spam
proposed by Evan Harris. In few words, it consists in recording the
@samp{sender @acronym{IP}}-@samp{sender email}-@samp{recipient email} triplet of
mail transactions. Each time the unknown triplet is seen, the
corresponding message is rejected with the @code{tempfail} code. If the
mail is legitimate, this will make the originating server retry
the delivery later, until the destination eventually accepts it. If,
however, the mail is a spam, it will probably never be retried, so
the users will not be bothered by it. Even if the spammer will retry
the delivery, the @dfn{greylisting period} will give spam-detection
systems, such as @acronym{DNSBL}s, enough time to detect and blacklist it,
so by the time the destination host starts accepting emails from this
triplet, it will already be blocked by other means.
You will find the detailed description of the method in
@uref{http://projects.puremagic.com/@/greylisting/@/whitepaper.html,
The Next Step in the Spam Control War: Greylisting}, the original
whitepaper by Evan Harris.
The @command{mailfromd} implementation of greylisting is based on
@code{greylist} function. The function takes two arguments:
the @code{key}, identifying the greylisting triplet, and the
@code{interval}. The function looks up the key in the @dfn{greylisting
database}. If such a key is not found, a new entry is created for it
and the function returns @code{true}. If the key is
found, @code{greylist} returns @code{false}, if it was inserted to the
database more than @code{interval} seconds ago, and @code{true} otherwise.
In other words, from the point of view of the greylisting algorithm, the
function returns @code{true} when the message delivery should be
blocked. Thus, the simplest implementation of the algorithm would be:
@example
@group
prog envrcpt
do
if greylist("$@{client_addr@}-$f-$@{rcpt_addr@}", interval("1 hour"))
tempfail 451 4.7.1 "You are greylisted"
fi
done
@end group
@end example
@cindex greylist_seconds_left, global variable, introduced
However, the message returned by this example, is not informative
enough. In particular, it does not tell when the message will be
accepted. To help you produce more informative messages, @code{greylist}
function stores the number of seconds left to the end of the
greylisting period in the global variable
@code{greylist_seconds_left}, so the above example could be enhanced
as follows:
@example
@group
prog envrcpt
do
set gltime interval("1 hour")
if greylist("$@{client_addr@}-$f-$@{rcpt_addr@}", gltime)
if greylist_seconds_left = gltime
tempfail 451 4.7.1
"You are greylisted for %gltime seconds"
else
tempfail 451 4.7.1
"Still greylisted for %greylist_seconds_left seconds"
fi
fi
done
@end group
@end example
In real life you will have to avoid greylisting some messages, in
particular those coming from the @samp{<>} address and from the @acronym{IP}
addresses in your relayed domain. It can easily be done using the
techniques described in previous sections and is left as an exercise
to the reader.
@anchor{greylisting types}
@cindex greylisting types
@cindex greylisting, traditional
@code{Mailfromd} provides two implementations of greylisting
primitives, which differ in the information stored in the database.
The one described above is called @dfn{traditional}. It keeps in the
database the time when the greylisting was activated for the given
key, so the @code{greylisting} function uses its second argument
(@code{interval}) and the current timestamp to decide whether the key
is still greylisted.
@cindex greylisting, Con Tassios type
@cindex Con Tassios greylisting type
The second implementation is called by the name of its inventor
@dfn{Con Tassios}. This implementation stores in the database the
time when the greylisting period is set to expire, computed by the
@code{greylist} when it is first called for the given key, using the
formula @samp{current_timestamp + interval}. Subsequent calls to
@code{greylist} compare the current timestamp with the one stored in
the database and ignore their second argument. This implementation is
enabled by one of the following pragmas:
@example
#pragma greylist con-tassios
@end example
@noindent
or
@example
#pragma greylist ct
@end example
When Con Tassios implementation is used, yet another function
becomes available. The function @code{is_greylisted}
(@pxref{Greylisting functions,,is_greylisted}) returns
@samp{True} if its argument is greylisted and @samp{False} otherwise.
It can be used to check for the greylisting status without actually
updating the database:
@example
if is_greylisted("$@{client_addr@}-$f-$@{rcpt_addr@}")
@dots{}
fi
@end example
@anchor{whitelisting}
@cindex whitelisting
One special case is @dfn{whitelisting}, which is often used
together with greylisting. To implement it, @command{mailfromd}
provides the function @code{dbmap}, which takes two mandatory arguments:
@code{dbmap(@var{file}, @var{key})} (it also allows an optional third
argument, see @ref{dbmap}, for more information on it). The first argument is
the name of the @acronym{DBM} file where to search for the key, the second one
is the key to be searched. Assuming you keep your whitelist database
in file @file{/var/run/whitelist.db}, a more practical example will be:
@example
@group
prog envrcpt
do
set gltime interval("1 hour")
if not ($f = "" or relayed(hostname($@{client_addr@}))
or dbmap("/var/run/whitelist.db", $@{client_addr@}))
if greylist("$@{client_addr@}-$f-$@{rcpt_addr@}", gltime)
if greylist_seconds_left = gltime
tempfail 451 4.7.1
"You are greylisted for %gltime seconds"
else
tempfail 451 4.7.1
"Still greylisted for %greylist_seconds_left seconds"
fi
fi
fi
done
@end group
@end example
@node Local Account Verification
@section Local Account Verification
In your filter script you may need to verify if the given
user name is served by your mail server, in other words, to verify if
it represents a @dfn{local account}. Notice that in this context, the word
@dfn{local} does not necessarily mean that the account is local for
the server running @command{mailfromd}, it simply means any account
whose mailbox is served by the mail servers using @command{mailfromd}.
The @code{validuser} function may be used for this purpose. It
takes one argument, the user name, and returns @code{true} if
this name corresponds to a local account. To verify this, the
function relies on @command{libmuauth}, a powerful authentication
library shipped with GNU @command{mailutils}. More precisely, it
invokes a list of @dfn{authorization} functions. Each function is
responsible for looking up the user name in a particular source of
information, such as system @file{passwd} database, an @acronym{SQL} database,
etc. The search is terminated when one of the functions finds
the name in question or the list is exhausted. In the former case, the
account is local, in the latter it is not. This concept is
discussed in detail in @pxref{authentication, Authentication,
Authorization and Authentication Principles, mailutils, GNU Mailutils
Manual}). Here we will give only some practical advices for
implementing it in @command{mailfromd} filters.
The actual list of available authorization modules depends on your
@command{mailutils} installation. Usually it includes, apart from
traditional @acronym{UNIX} @file{passwd} database, the functions for verifying
@acronym{PAM}, @acronym{RADIUS} and @acronym{SQL} database accounts.
Each of the authorization methods is configured using special
configuration file statements. For the description of the Mailutils
configuration files, @xref{configuration, Mailutils Configuration
File, Mailutils Configuration File, mailutils, GNU Mailutils Manual}.
You can obtain the template for @command{mailfromd} configuration by
running @command{mailfromd --config-help}.
For example, the following @file{mailfromd.conf} file:
@example
@group
auth @{
authorization pam:system;
@}
pam @{
service mailfromd;
@}
@end group
@end example
@noindent
sets up the authorization using @acronym{PAM} and system @file{passwd}
database. The name of @acronym{PAM} service to use is @samp{mailfromd}.
@cindex aliases, looking up
The function @code{validuser} is often used together with
@code{dbmap}, as in the example below:
@example
@group
#pragma dbprop /etc/mail/aliases.db null
if dbmap("/etc/mail/aliases.db", localpart($rcpt_addr))
and validuser(localpart($rcpt_addr))
@dots{}
fi
@end group
@end example
For more information about @code{dbmap} function, see @ref{dbmap}.
For a description of @code{dbprop} pragma, see @ref{Database functions}.
@node Databases
@section Databases
Some @command{mailfromd} functions use @acronym{DBM} databases to save their
persistent state data. Each database has a unique @dfn{identifier},
and is assigned several pieces of information for its maintenance: the
database @dfn{file name} and the @dfn{expiration period}, i.e. the
time after which a record is considered expired.
@xopindex{show-defaults, mailfromd, introduced}
To obtain the list of available databases along with their
preconfigured settings, run @kbd{mailfromd --show-defaults}
(@pxref{Examining Defaults}). You will see an output similar to
this:
@example
@group
version: @value{VERSION}
script file: /etc/mailfromd.mfl
preprocessor: /usr/bin/m4 -s
user: mail
statedir: /var/run/mailfromd
socket: unix:/var/run/mailfromd/mailfrom
pidfile: /var/run/mailfromd/mailfromd.pid
default syslog: blocking
supported databases: gdbm, bdb
default database type: bdb
optional features: DKIM GeoIP2 STARTTLS
greylist database: /var/run/mailfromd/greylist.db
greylist expiration: 86400
tbf database: /var/run/mailfromd/tbf.db
tbf expiration: 86400
rate database: /var/run/mailfromd/rates.db
rate expiration: 86400
cache database: /var/run/mailfromd/mailfromd.db
cache positive expiration: 86400
cache negative expiration: 43200
@end group
@end example
The text below @samp{optional features} line describes the available
built-in databases. Notice that the @samp{cache} database, in
contrast to the rest of databases, has two expiration periods
associated with it. This is explained in the next subsection.
@menu
* Database Formats::
* Basic Database Operations::
* Database Maintenance::
@end menu
@node Database Formats
@subsection Database Formats
@cindex database formats
The version @value{VERSION} runs the following database types (or
@dfn{formats}):
@cindex databases used by @command{mailfromd}
@table @samp
@cindex cache database
@item cache
@anchor{cache database}
@dfn{Cache database} keeps the information about external emails,
obtained using sender verification functions (@pxref{Checking Sender
Address}). The key entry to this database is an email address or
@var{email}:@var{sender-ip} string, for addresses checked using strict
verification. The data its stores for each key are:
@enumerate 1
@item
Address validity. This field can be either @code{success} or
@code{not_found}, meaning the address is confirmed to exists or it
is not.
@item
The time when the entry was entered into the database. It is used to
check for expired entries.
@end enumerate
@anchor{cache expiration}
@cindex positive expiration period, defined
@cindex negative expiration period, defined
The @samp{cache} database has two expiration periods: a
@dfn{positive expiration} period, that is applied to entries with
the first field set to @code{success}, and a @dfn{negative expiration}
period, applied to entries marked as @code{not_found}.
@cindex rate database
@item rate
@anchor{rate database}
The mail sending rate data, maintained by @code{rate} function
(@pxref{Rate limiting functions}). A record consists of the following fields:
@table @asis
@item timestamp
The time when the entry was entered into the database.
@item interval
Interval during which the rate was measured (seconds).
@item count
Number of mails sent during this interval.
@end table
@cindex tbf database
@item tbf
@anchor{tbf database}
This database is maintained by @code{tbf_rate} function (@pxref{TBF}).
Each record represents a single bucket and consists of the following
keys:
@table @asis
@item timestamp
Timestamp of most recent token, as a 64-bit unsigned integer
(microseconds resolution).
@item expirytime
Estimated time when this bucket expires (seconds since epoch).
@item tokens
Number of tokens in the bucket (@code{size_t}).
@end table
@cindex greylist database
@item greylist
@anchor{greylist database}
This database is maintained by @code{greylist} function
(@pxref{Greylisting}). Each record holds only the timestamp.
Its semantics depends on the greylisting implementation in
use (@pxref{greylisting types}). In traditional implementation, it
is the time when the entry was entered into the database. In Con
Tassios implementation, it is the time when the greylisting period
expires.
@end table
@node Basic Database Operations
@subsection Basic Database Operations
@pindex mfdbtool
The @command{mfdbtool} utility is provided for performing various
operations on the @command{mailfromd} database.
@cindex database, listing
@cindex listing a database contents
@xopindex{list, mfdbtool, described}
@anchor{--list option}
To list the contents of a database, use @option{--list} option.
When used without any arguments it will list the @samp{cache}
database:
@example
@group
$ @kbd{mfdbtool --list}
abrakat@@mail.com success Thu Aug 24 15:28:58 2006
baccl@@EDnet.NS.CA not_found Fri Aug 25 10:04:18 2006
bhzxhnyl@@chello.pl not_found Fri Aug 25 10:11:57 2006
brqp@@aaanet.ru:24.1.173.165 not_found Fri Aug 25 14:16:06 2006
@end group
@end example
You can also list data for any particular key or keys. To do so,
give the keys as arguments to @command{mfdbtool}:
@example
@group
$ @kbd{mfdbtool --list abrakat@@mail.com brqp@@aaanet.ru:24.1.173.165}
abrakat@@mail.com success Thu Aug 24 15:28:58 2006
brqp@@aaanet.ru:24.1.173.165 not_found Fri Aug 25 14:16:06 2006
@end group
@end example
@xopindex{format, mfdbtool, introduced}
@xopindex{format, mfdbtool, using with @option{--list}}
To list another database, give its format identifier with the
@option{--format} (@option{-H}) option. For example, to list the
@samp{rate} database:
@example
@group
$ @kbd{mfdbtool --list --format=rate}
sam@@mail.net-62.12.4.3 Wed Sep 6 19:41:42 2006 139 3 0.0216 6.82e-06
axw@@rame.com-59.39.165.172 Wed Sep 6 20:26:24 2006 0 1 N/A N/A
@end group
@end example
The @option{--format} option can be used with any database
management option, described below.
@anchor{estimated time of sending}
@cindex estimated time of sending, prediction of
@xopindex{predict, mfdbtool, introduced}
Another useful operation you can do while listing @samp{rate}
database is the prediction of @dfn{estimated time of sending},
i.e. the time when the user will be able to send mail if currently his
mail sending rate has exceeded the limit. This is done using
@option{--predict} option. The option takes an argument, specifying
the mail sending rate limit, e.g. (the second line is split for readability):
@example
@group
$ @kbd{mfdbtool --predict="180 per 1 minute"}
ed@@fae.net-21.10.1.2 Wed Sep 13 03:53:40 2006 0 1 N/A N/A; free to send
service@@19.netlay.com-69.44.129.19 Wed Sep 13 15:46:07 2006 7 2
0.286 0.0224; in 46 sec. on Wed Sep 13 15:49:00 2006
@end group
@end example
@noindent
Notice, that there is no need to use @option{--list --format=rate}
along with this option, although doing so is not an error.
@anchor{deleting from databases}
@xopindex{delete, mfdbtool, introduced}
To delete an entry from the database, use @option{--delete} option,
for example: @kbd{mfdbtool --delete abrakat@@mail.com}. You can give
any number of keys to delete in the command line.
@node Database Maintenance
@subsection Database Maintenance
@cindex database maintenance
@cindex maintenance, database
There are two principal operations of database management:
expiration and compaction. @dfn{Expiration} consists in removing
expired entries from the database. In fact, it is rarely needed,
since the expired entries are removed in the process of normal
@command{mailfromd} work. Nevertheless, a special option is provided
in case an explicit expiration is needed (for example, before dumping
the database to another format, to avoid transferring useless
information).
@xopindex{expire, mfdbtool, introduced}
The command line option @option{--expire} instructs
@command{mfdbtool} to delete expired entries from the specified database. As
usual, the database is specified using @option{--format} option. If
it is not given explicitly, @samp{cache} is assumed.
@cindex database compaction
@cindex compaction, database
While removing expired entries the space they occupied is marked as
free, so it can be used by subsequent inserts. The database does
not shrink after expiration is finished. To actually return the
unused space to the file system you should @dfn{compact} your
database.
@anchor{compaction}
@xopindex{compact, mfdbtool, introduced}
This is done by running @kbd{mfdbtool --compact} (and, optionally,
specifying the database to operate upon with @option{--format}
option). Notice, that compacting a database needs roughly as
much disk space on the partition where the database resides as is
currently used by the database. Database compaction runs in three phases.
First, the database is scanned and all non-expired records are stored
in the memory. Secondly, a temporary database is created in the state
directory and all the cached entries are flushed into it. This
database is named after the @acronym{PID} of the running
@command{mfdbtool} process. Finally, the temporary database is
renamed to the source database.
@anchor{compact cronjob}
@xopindex{all, mfdbtool, introduced}
Both @option{--compact} and @option{--expire} can be applied to all
databases by combining them with @option{--all}. It is useful, for
example, in @file{crontab} files. For example, I have the following
monthly job in my @file{crontab}:
@example
0 1 1 * * /usr/bin/mfdbtool --compact --all
@end example
@node Testing Filter Scripts
@section Testing Filter Scripts
It is important to check your filter script before actually starting
to use it. There are several ways to do so.
@cindex lint mode
@cindex syntax check
@cindex script file checking
@xopindex{lint, mailfromd, introduced}
@xopindex{syntax-check, mailfromd, introduced}
To test the syntax of your filter script, use the @option{--lint}
option. It will cause @command{mailfromd} to exit
immediately after attempting to compile the script file. If the
compilation succeeds, the program will exit with code 0. Otherwise,
it will exit with error code 78 (@samp{configuration error}). In the
latter case, @command{mailfromd} will also print a diagnostic message,
describing the error along with the exact location where the error was
diagnosed, for example:
@example
@group
mailfromd: /etc/mailfromd.mfl:39: syntax error, unexpected reject
@end group
@end example
@xopindex{location-column, mailfromd, described}
The error location is indicated by the name of the file and the
number of the line when the error occurred. By using the
@option{--location-column} option you instruct @command{mailfromd} to
also print the @dfn{column number}. E.g. with this option the above
error message may look like:
@example
@group
mailfromd: /etc/mailfromd.mfl:39.12 syntax error, unexpected reject
@end group
@end example
Here, @samp{39} is the line and @samp{12} is the column number.
@cindex cross-reference
For complex scripts you may wish to obtain a listing of variables
used in the script. This can be achieved using @option{--xref}
command line option:
The output it produces consists of four columns:
@table @asis
@item Variable name
@item Data type
Either @code{number} or @code{string}.
@item Offset in data segment
Measured in words.
@item References
A comma-separated list of locations where the variable was
referenced. Each location is represented as @var{file}:@var{line}.
If several locations pertain to the same @var{file}, the file name is
listed only once.
@end table
@noindent
Here is an example of the cross-reference output:
@example
@group
$ @kbd{mailfromd --xref}
Cross-references:
-----------------
cache_used number 5 /etc/mailfromd.mfl:48
clamav_virus_name string 9 /etc/mailfromd.mfl:240,240
db string 15 /etc/mailfromd.mfl:135,194,215
dns_record_ttl number 16 /etc/mailfromd.mfl:136,172,173
ehlo_domain string 11
gltime number 13 /etc/mailfromd.mfl:37,219,220,222,223
greylist_seconds_left number 1 /etc/mailfromd.mfl:220,226,227
last_poll_host string 2
@end group
@end example
@anchor{test mode}
@cindex filter script, debugging
@cindex debugging the filter script
@cindex filter script, running in test mode
@xopindex{test, mailfromd, introduced}
If the script passes syntax check, the next step is often to test if
it works as you expect it to. This is done with @option{--test}
(@option{-t}) command line option. This option runs the
@code{envfrom} handler (or another one, see below) and prints the
result of its execution.
@cindex sendmail macros, setting from the command line
When running your script in test mode, you will need to supply the
values of @command{Sendmail} macros it needs. You do this by placing
the necessary assignments in the command line. For example, this is
how to supply initial values for @code{f} and @code{client_addr}
macros:
@example
$ @kbd{mailfromd --test f=gray@@gnu.org client_addr=127.0.0.1}
@end example
@anchor{overriding initial values}
@cindex overriding initial variable values
@cindex variable values, setting from the command line
@xopindex{variable, mailfromd, introduced}
You may also need to alter initial values of some global variables
your script uses. To do so, use @option{-v} (@option{--variable})
command line option. This option takes a single argument consisting
of the variable name and its initial value, separated by an equals
sign. For example, here is how to change the value of
@code{ehlo_domain} global variable:
@example
$ @kbd{mailfromd -v ehlo_domain=mydomain.org}
@end example
The @option{--test} option is often useful in conjunction with options
@option{--debug}, @option{--trace} and @option{--transcript}
(@pxref{Logging and Debugging}. The following example shows what the
author got while debugging the filter script described in
@ref{Filter Script Example}:
@example
@group
$ @kbd{mailfromd --test --debug=50 f=gray@@gnu.org client_addr=127.0.0.1}
MX 20 mx20.gnu.org
MX 10 mx10.gnu.org
MX 10 mx10.gnu.org
MX 20 mx20.gnu.org
getting cache info for gray@@gnu.org
found status: success (0), time: Thu Sep 14 14:54:41 2006
getting rate info for gray@@gnu.org-127.0.0.1
found time: 1158245710, interval: 29, count: 5, rate: 0.172414
rate for gray@@gnu.org-127.0.0.1 is 0.162162
updating gray@@gnu.org-127.0.0.1 rates
SET REPLY 450 4.7.0 Mail sending rate exceeded. Try again later
State envfrom: tempfail
@end group
@end example
@xopindex{echo, mailfromd, echo to stdout or file}.
If your script uses @code{echo} statements (@pxref{Echo}), they will
print their output on standard error. To direct them to the standard
output, use the @option{--echo} option. You can also redirect the
@code{echo} output to arbitrary file, by supplying its name as
argument, as in: @option{--echo=@var{file}}. @pxref{echo option}.
@xopindex{test, mailfromd, specifying handler name}
To test any handler, other than @samp{envfrom}, give its name as the
argument to @option{--test} option. Since this argument is optional,
it is important that it be given immediately after the option, without
any intervening white space, for example @kbd{mailfromd --test=helo},
or @kbd{mailfromd -thelo}.
@cindex @command{mtasim}, introduced
This method allows to test one handler at a time. To test the
script as a whole, use @command{mtasim} utility. When
started it enters interactive mode, similar to that of
@command{sendmail -bs}, where it expects @acronym{SMTP} commands on
its standard input and sends answers to the standard output. The
@option{--port=auto} command line option instructs it to start
@command{mailfromd} and to create a unique socket for communication
with it. For the detailed description of the program and the ways to
use it, @xref{mtasim}.
@node Run Mode
@section Run Mode
@cindex run mode
Mailfromd provides a special option that allows to run arbitrary
@acronym{MFL} scripts.
@xopindex{run, mailfromd, described}
@cindex main, MFL function
When given the @option{--run} command line option,
@command{mailfromd} loads the script given in its command line, looks for
the function called @samp{main}, and runs it.
This function must be declared as:
@example
func main(...) returns number
@end example
Mailfromd passes all command line arguments that follow the script
name as arguments to that function. When the function returns, its
return value is used by @command{mailfromd} as exit code.
As an example, suppose the file @file{script.mfl} contains the
following:
@example
func main (...)
returns number
do
loop for number i 1,
while i <= $#,
set i i + 1
do
echo "arg %i=" . $(i)
done
done
@end example
This function prints all its arguments (@xref{variadic functions}, for
a detailed description of functions with variable
number of arguments). Now running:
@example
$ @kbd{mailfromd --run script.mfl 1 file dest}
@end example
@noindent
displays the following:
@example
@cartouche
arg 1=1
arg 2=file
arg 3=dest
@end cartouche
@end example
You can direct the script output to the standard output by using the
@option{--echo}, as described above, e.g.:
@example
$ @kbd{mailfromd --echo --run script.mfl 1 file dest}
@end example
Note, that @acronym{MFL} does not have a direct equivalent of
shell's @code{$0} argument. If your function needs to know the name
of the script that is being executed, use @code{__file__} built-in
constant instead (@pxref{Built-in constants, __file__}).
The name @code{main} is not hard-coded. You can use the
@option{--run} option to run any function, provided that its
definition is as discussed above. Just give the name of this
function as the argument to the option. This argument is optional,
therefore it must be separated from the option by an equals sign (with
no whitespace from either side). For example, given the command line below,
@command{mailfromd} will load the file @file{script.mfl} and execute the
function @samp{start}:
@example
$ @kbd{mailfromd --run=start script.mfl}
@end example
If you need to define sendmail macros (@pxref{Sendmail Macros}) for
use in the run mode, place the @var{macro}=@var{value} assignments
@emph{before} the script name, e.g.:
@example
$ @kbd{mailfromd --run=start i=feedbeef client_addr=::1 script.mfl}
@end example
To summarize, the command line when using the run mode is:
@example
mailfromd [@var{options}] --run [@var{macro}=@var{value}] @var{file} @var{args...}
@end example
Finally, notice that @var{file} together with @var{args...} can be
omitted. In this case the default script file will be used
(@pxref{default script file}).
@noindent
The @samp{@var{macro}=@var{value}} assignments define Sendmail macros,
@var{args...} are passed as arguments to the @code{main} function
defined in @var{file}, and @var{option} stands for any other
@command{mailfromd} options that might be needed.
@menu
* top-block:: The Top of a Script File.
* getopt:: Parsing Command Line Arguments.
@end menu
@node top-block
@subsection The Top of a Script File
@cindex @samp{#!} shell magic sequence
The @option{--run} option makes it possible to use @command{mailfromd}
scripts as standalone programs. The traditional way to do so was to
set the executable bit on the script file and to begin the script with
the @dfn{interpreter selector}, i.e. the characters @samp{#!} followed by
the name of the @command{mailfromd} executable, e.g.:
@example
#! /usr/sbin/mailfromd --run
@end example
This would cause the shell to invoke @command{mailfromd} with the
command line constructed from the @option{--run} option, the name
of the invoked script file itself, and any actual arguments from
the invocation. Once invoked, @command{mailfromd} would treat the
initial @samp{#!} line as a usual single-line comment
(@pxref{Comments}).
However, the interpretation of the @samp{#!} by shells has various
deficiencies, which depend on the actual shell being used. For
example, some shells pass any characters following the whitespace
after the interpreter name as a single argument, some others silently
truncate the command line after some number of characters, etc. This
often make it impossible to pass additional arguments to
@command{mailfromd}. For example, a script which begins with the
following line would most probably fail to be executed properly:
@example
#! /usr/sbin/mailfromd --echo --run
@end example
@cindex @samp{#! ... !#} initial comment
To compensate for these deficiencies and to allow for more complex
invocation sequences, @command{mailfromd} handles initial @samp{#}
in a special way. If the first line of a source file begins with
@samp{#!/} or @samp{#! /} (with a single space between @samp{!} and
@samp{/}), it is treated as a start of a multi-line comment, which is
closed by the two characters @samp{!#} on a line by themselves.
Thus, the correct way to begin a @command{mailfromd} script is:
@example
#! /usr/sbin/mailfromd --run
!#
@end example
Using this feature, you can start the @command{mailfromd} with
arbitrary shell code, provided it ends with an @code{exec} statement
invoking the interpreter itself. For example:
@example
#!/bin/sh
exec /usr/sbin/mailfromd --echo --run $0 $@@
!#
func main(...)
returns number
do
/* actual mfl code goes here */
done
@end example
Note the use of @samp{$0} and @samp{$@@} to pass the actual script file
name and command line arguments to @command{mailfromd}.
@node getopt
@subsection Parsing Command Line Arguments
@cindex command line arguments, parsing in MFL
@cindex scripting, parsing command line arguments
@cindex parsing command line arguments
A special function is provided to break (parse) the command
line into options, and to check them for validity. It uses the GNU
getopt routines (@pxref{Getopt, getopt, , libc, The GNU C Library
Reference Manual}).
@deftypefn {Built-in Function} string getopt (number @var{argc}, @
pointer @var{argv}, ...)
The @command{getopt} function parses the command line arguments, as
supplied by @var{argc} and @var{argv}. The @var{argc} argument is the
argument count, and @var{argv} is an opaque data structure,
representing the array of arguments@footnote{When @acronym{MFL} has
array data type, the second argument will change to array of
strings.}. The operator @code{vaptr} (@pxref{vaptr}) is
provided to initialize this argument.
An argument that starts with @samp{-} (and is not exactly @samp{-} or
@samp{--}), is an option element. An argument that starts with a
@samp{-} is called @dfn{short} or @dfn{traditional} option. The
characters of this element, except for the initial @samp{-} are option
characters. Each option character represents a separate option. An
argument that starts with @samp{--} is called @dfn{long} or @dfn{GNU}
option. The characters of this element, except for the initial
@samp{--} form the @dfn{option name}.
Options may have arguments. The argument to a short option is
supplied immediately after the option character, or as the next word
in command line. E.g., if option @option{-f} takes a mandatory
argument, then it may be given either as @option{-farg} or as
@option{-f arg}. The argument to a long option is either given
immediately after it and separated from the option name by an equals
sign (as @option{--file=arg}), or is given as the next word in the
command line (e.g.@: @option{--file arg}).
If the option argument is optional, i.e. it may not necessarily be
given, then only the first form is allowed (i.e. either @option{-farg} or
@option{--file=arg}.
The @samp{--} command line argument ends the option list. Any
arguments following it are not considered options, even if they begin
with a dash.
If @code{getopt} is called repeatedly, it returns successively each of
the option characters from each of the option elements (for short
options) and each option name (for long options). In this case, the
actual arguments are supplied only to the first invocation.
Subsequent calls must be given two nulls as arguments. Such
invocation instructs @code{getopt} to use the values saved on the
previous invocation.
When the function finds another option, it returns its character or name
updating the external variable @code{optind} (see below) so that the
next call to @code{getopt} can resume the scan with the following
option.
When there are no more options left, or a @samp{--} argument is
encountered, @code{getopt} returns an empty string. Then
@code{optind} gives the index in @var{argv} of the first element that
is not an option.
The legitimate options and their characteristics are supplied in
additional arguments to @code{getopt}. Each such argument is a string
consisting of two parts, separated by a vertical bar (@samp{|}). Any
one of these parts is optional, but at least one of them must be
present. The first part specifies short option character. If it is
followed by a colon, this character takes mandatory argument. If it
is followed by two colons, this character takes an optional argument.
If only the first part is present, the @samp{|} separator may be
omitted. Examples:
@table @asis
@item "c"
@itemx "c|"
Short option @option{-c}.
@item "f:"
@itemx "f:|"
Short option @option{-f}, taking a mandatory argument.
@item "f::"
@itemx "f::|"
Short option @option{-f}, taking an optional argument.
@end table
If the vertical bar is present and is followed by any characters, these
characters specify the name of a long option, synonymous to the short
one, specified by the first part. Any mandatory or optional arguments
to the short option remain mandatory or optional for the corresponding
long option. Examples:
@table @asis
@item "f:|file"
Short option @option{-f}, or long option @option{--file}, requiring an
argument.
@item "f::|file"
Short option @option{-f}, or long option @option{--file}, taking an
optional argument.
@end table
In any of the above cases, if this option appears in the command line,
@command{getopt} returns its short option character.
To define a long option without a short equivalent, begin it with a
bar, e.g.:
@table @asis
@item "|help"
@end table
If this option is to take an argument, this is specified using the
mechanism described above, except that the short option character
is replaced with a minus sign. For example:
@table @asis
@item "-:|output"
Long option @option{--output}, which takes a mandatory argument.
@item "-::|output"
Long option @option{--output}, which takes an optional argument.
@end table
@vrindex optarg
If an option is returned that has an argument in the command line,
@code{getopt} stores this argument in the variable @code{optarg}.
@vrindex optind
After each invocation, @code{getopt} sets the variable @code{optind}
to the index of the next @var{argv} element to be parsed. Thus,
when the list of options is exhausted and the function returned an
empty string, @code{optind} contains the index of the the first
element that is not an option.
@vrindex optopt
When @code{getopt} encounters an option that is not described in its
arguments or if it detects a missing option argument it prints an
error message using @command{mailfromd} logging facilities, stores
the offending option in the variable @code{optopt}, and returns @samp{?}.
@vrindex opterr
If printing error message is not desired (e.g.@: the application is going
to take care of error messaging), it can be disabled by setting the
variable @code{opterr} to @samp{0}.
@cindex controlling argument, getopt
The third argument to @code{getopt}, called @dfn{controlling argument},
may be used to control the behavior of the function. If it is a
colon, it disables printing the error message for unrecognized options
and missing option arguments (as setting @code{opterr} to @samp{0}
does). In this case @code{getopt} returns @samp{:}, instead of
@samp{?} to indicate missing option argument.
If the controlling argument is a plus sign, or the environment
variable @env{POSIXLY_CORRECT} is set, then option processing stops as
soon as a non-option argument is encountered. By default, if options
and non optional arguments are intermixed in @var{argv}, @code{getopt}
permutes them so that the options go first, followed by non-optional
arguments.
If the controlling argument is @samp{-}, then each non-option
element in @var{argv} is handled as if it were the argument of
an option with character code 1 (@samp{"\001"}, in @acronym{MFL}
notation. This can used by programs that are written to expect options and
other @var{argv}-elements in any order and that care about the
ordering of the two.
Any other value of the controlling argument is handled as an option
definition.
@end deftypefn
@anchor{vaptr}
@cindex vaptr
A special language construct is provided to supply the second
argument (@var{argv}) to @code{getopt} and similar functions:
@example
vaptr(@var{param})
@end example
@noindent
where @var{param} is a positional parameter, from which to start the
array of @var{argv}. For example:
@example
func main(...)
returns number
do
set rc getopt($#, vaptr($1), "|help")
...
@end example
Here, @code{vaptr($1)} constructs the @var{argv} array from all the
arguments, supplied to the function @code{main}.
To illustrate the use of @code{getopt} function, let's suppose you
write a script that takes the following options:
@table @option
@item -f @var{file}
@itemx --file=@var{file}
@item --output[=@var{dir}]
@item --help
@end table
Then, the corresponding @code{getopt} invocation will be:
@example
func main(...)
returns number
do
loop for string rc getopt($#, vaptr($1),
"f:|file", "-::|output", "h|help"),
while rc != "",
set rc getopt(0, 0)
do
switch rc
do
case "f":
set file optarg
case "output"
set output 1
set output_dir optarg
case "h"
help()
default:
return 1
done
...
@end example
@node Examining Defaults
@section Examining Default Values
Sometimes you may need to check what are the default settings of the
@command{mailfromd} binary and what values it uses actually. Both
tasks are accomplished using the @option{--show-defaults} option.
When used alone, it shows the settings actually in use (default
values, eventually modified by your configuration settings). When
used together with @command{--no-config}, it displays the compiled
defaults.
The output of @command{mailfromd --show-defaults} looks like this:
@example
@group
version: @value{VERSION}
script file: /etc/mailfromd.mfl
preprocessor: /usr/bin/m4 -s -DWITH_DKIM -DWITH_MFMOD
/var/mailfromd/@value{VERSION}/include/pp-setup
user: mail
statedir: /var/lib/mailfromd
socket: mailfrom
pidfile: mailfromd.pid
default syslog: blocking
include path: /etc/mailfromd:/usr/share/mailfromd/include:
/usr/share/mailfromd/8.14.94/include
module path: /usr/share/mailfromd:
/usr/share/mailfromd/@value{VERSION}
mfmod path: /usr/lib/mailfromd
optional features: DKIM, mfmod, STARTTLS
supported database types: gdbm, bdb
default database type: bdb
greylist database: /var/lib/mailfromd/greylist.db
greylist expiration: 86400
tbf database: /var/lib/mailfromd/tbf.db
tbf expiration: 86400
rate database: /var/lib/mailfromd/rates.db
rate expiration: 86400
cache database: /var/lib/mailfromd/mailfromd.db
cache positive expiration: 604800
cache negative expiration: 86400
@end group
@end example
The above format, called @dfn{human-readable}, with two-column output
and long lines split across several physical lines, is used if
@command{mailfromd} is linked with GNU @command{libmailutils}
library version 3.16 or later and its standard output is connected to
a terminal. Otherwise, @dfn{machine-readable} output format is used,
in which additional whitespace is elided, and long lines are retained
verbatim. This makes it possible to easily extract default values
using familiar text processing tools, e.g.:
@example
@group
$ @kbd{mailfromd --show-defaults --no-config | grep '^script file:'}
script file:/etc/mailfromd.mfl
$ @kbd{mailfromd --show-defaults --no-config | sed -ne '/^script file:/s///p'}
/etc/mailfromd.mfl
@end group
@end example
The following table describes each line of the output in detail:
@table @asis
@item version
Program version.
@item script file
The script file used by the program. It is empty if the script file
is not found.
@item preprocessor
Preprocessor command line. @xref{Preprocessor}. This value can be
changed in configuration: @xref{conf-preprocessor}.
@item user
System user @command{mailfromd} runs as. @xref{conf-priv}.
@item statedir
@command{mailfromd} local state directory. @xref{statedir}.
@item socket
The socket @command{mailfromd} listens on. If UNIX socket, the
filename is shown. Unless it begins with @samp{/}, it is relative to
the local state directory. TCP sockets are shown in @ref{milter port
specification}.
@xref{conf-server, listen}.
@item pidfile
PID file name (relative to local state directory, unless absolute).
@xref{conf-base, pidfile}.
@item default syslog
Syslog implementation used: either @samp{blocking}, or
@samp{non-blocking}.
@xref{syslog-async, Using non-blocking syslog}. See also @ref{Logging and Debugging}.
@item include path
Include search path. @xref{include search path}.
It can be changed from the command line, using the @option{-I} option
(@pxref{General Settings}), and in configuration file, using the
@code{include-path} statement (@pxref{conf-base, include-path}).
@item module path
Search path for MFL modules. @pxref{module search path}.
It can be changed from the command line, using the @option{-P}
(@option{--module-path}) option (@pxref{General Settings}), and in
configuration file, using the @code{module-path} statement
(@pxref{conf-base, module-path}).
@item mfmod path
Search path for dynamically loaded modules. @pxref{mfmod-path}.
@item optional features
Comma-delimited list of optional features, included to
@command{mailfromd} at compile time. It can contain the following
feature names:
@multitable @columnfractions 0.3 0.6
@headitem Feature @tab Reference
@item DKIM @tab @xref{DKIM}.
@item GeoIP2 @tab @xref{Geolocation functions}.
@item mfmod @tab @xref{mfmod, Dynamically Loaded Modules}.
@item STARTTLS @tab @xref{conf-callout, STARTTLS in call-out}.
@end multitable
@item supported database types
Comma-delimited list of supported database types. @xref{Databases}.
These types can be used as scheme prefixes in database names
(@pxref{DBM scheme}).
@item default database type
Type of the DBM used by default. @xref{Databases}.
@item greylist database
@itemx greylist expiration
File name and record expiration time of the greylisting database.
@xref{greylist database}.
@item tbf database
@itemx tbf expiration
File name and record expiration time of the token-bucket filter
rate-limiting database. @xref{tbf database}.
@item rate database
@itemx rate expiration
@xref{rate database}
File name and record expiration time of the legacy rate-limiting
database. @xref{Rate limiting functions}.
@item cache database
@itemx cache positive expiration
@itemx cache negative expiration
File name and record expiration times of the call-out cache database.
@xref{cache database}.
@end table
The database settings can be changed using @ref{conf-database}.
@node Logging and Debugging
@section Logging and Debugging
@cindex diagnostics channel
@cindex standard error, using for diagnostics output
@cindex syslog, using for diagnostics output
Depending on its operation mode, @command{mailfromd} tries to guess
whether it is appropriate to print its diagnostics and informational
messages on standard error or to send them to syslog. Standard error
is assumed if the program is run with one of the following command
line options:
@itemize @bullet
@item @option{--test} (@pxref{Testing Filter Scripts})
@item @option{--run} (@pxref{Run Mode})
@item @option{--lint} (@pxref{Testing Filter Scripts})
@item @option{--dump-code} (@pxref{Logging and Debugging Options})
@item @option{--dump-grammar-trace} (@pxref{Logging and Debugging Options})
@item @option{--dump-lex-trace} (@pxref{Logging and Debugging Options})
@item @option{--dump-macros} (@pxref{Logging and Debugging Options})
@item @option{--dump-tree} (@pxref{Logging and Debugging Options})
@item @option{--xref} (or @option{--dump-xref}) (@pxref{Testing Filter Scripts})
@end itemize
@cindex syslog, default implementation
@cindex syslog, non-blocking
@cindex syslog, asynchronous
@cindex asynchronous syslog
@cindex non-blocking syslog
@xopindex{logger, mailfromd, introduced}
If none of these are used, @command{mailfromd} switches to syslog as
soon as it finishes its startup. There are two ways to communicate
with the @command{syslogd} daemon: using the @code{syslog}
function from the system @file{libc} library, which is a @dfn{blocking}
implementation in most cases, or via internal, @dfn{asynchronous},
syslog implementation. Whether the latter is compiled in and which
implementation is used by default is determined when compiling
the package, as described in @ref{syslog-async, Using non-blocking syslog}.
The @option{--logger} command line option allows you to manually
select the diagnostic channel:
@table @option
@item --logger=stderr
Log everything to the standard error.
@item --logger=syslog
Log to syslog.
@item --logger=syslog:async
Log to syslog using the asynchronous syslog implementation.
@end table
Another way to select the diagnostic channel is by using the
@code{logger} statement in the configuration file. The statement
takes the same argument as its command line counterpart.
The rest of details regarding diagnostic output are controlled by
the @code{logging} configuration statement.
@cindex syslog facility, selecting
@cindex selecting syslog facility
@cindex syslog facility, default
@cindex default syslog facility
@xopindex{log-facility, mailfromd, introduced}
The default syslog facility is @samp{mail}; it can be changed
using the @option{--log-facility} command line option or
@code{facility} statement. Argument in both cases is a valid
facility name, i.e. one of: @samp{user}, @samp{daemon},
@samp{auth}, @samp{authpriv}, @samp{mail}, and @samp{local0}
through @samp{local7}. The argument can be given in upper, lower or
mixed cases, and it can be prefixed with @samp{log_}:
@anchor{syslog tag}
@cindex syslog tag
@xopindex{log-tag, mailfromd, introduced}
Another syslog-related parameter that can be configured is the
@dfn{tag}, which identifies @command{mailfromd} messages. The default tag
is the program name. It is changed by the @option{--log-tag}
(@option{-L} command line option and the @code{tag} logging statement.
The following example configures both the syslog facility and tag:
@example
logging @{
facility local7;
tag "mfd";
@}
@end example
As any other @acronym{UNIX} utility, @command{mailfromd} is very quiet unless it
has something important to communicate, such as, e.g.@: an error condition.
A set of command line options is provided for
controlling the verbosity of its output.
@xopindex{trace, mailfromd, introduced}
The @option{--trace} option enables tracing Sendmail actions
executed during message verifications. When this option is given,
any @code{accept}, @code{discard}, @code{continue}, etc. triggered
during execution of your filter program will leave their traces in
the log file. Here is an example of how it looks like (syslog time
stamp, tag and @acronym{PID} removed for readability):
@example
@group
k8DHxvO9030656: /etc/mailfromd.mfl:45: reject 550 5.1.1 Sender validity
not confirmed
@end group
@end example
@noindent
This shows that while verifying the message with @acronym{ID}
@samp{k8DHxvO9030656} the @code{reject} action was executed by filter
script @file{/etc/mailfromd.mfl} at line 45.
@anchor{Message-ID}
@cindex Message-ID, using in @command{mailfromd} logs
@cindex Message-ID, exporting
The use of message @acronym{ID} in the log deserves a special
notice. The program will always identify its log messages with
the @samp{Message-Id}, when it is available. Your responsibility as an
administrator is to make sure it is available by configuring
your @acronym{MTA} to export the macro @samp{i} to @command{mailfromd}.
The rule of thumb is: make @samp{i} available to the very first
handler @command{mailfromd} executes. It is not necessary to export
it to the rest of the handlers, since @command{mailfromd} will cache
it. For example, if your filter script contains @samp{envfrom} and
@samp{envrcpt} handlers, export @samp{i} for @samp{envfrom}.
The exact instructions on how to ensure it depend on the
@acronym{MTA} you use. For @samp{Sendmail}, refer to @ref{Sendmail}.
For MeTA1, see @ref{MeTA1}, and @ref{pmult-macros}. For
@samp{Postfix}, see @ref{Postfix}.
@xopindex{debug, mailfromd, introduced}
@cindex debugging level
@cindex verbosity level
To push log verbosity further, use the @code{debug}
configuration statement (@pxref{conf-debug}) or its command line
equivalent, @option{--debug} (@option{-d}, @pxref{--debug}). Its
argument is a @dfn{debugging level}, whose syntax is described
in @uref{http://mailutils.org/wiki/Debug_level}.
@anchor{debugging level specification}
The debugging output is controlled by a set of levels, each of which
can be set independently of others. Each debug level consists of a
category name, which identifies the part of package for which
additional debugging is desired, and a level number, which indicates
how verbose should its output be.
Valid debug levels are:
@table @asis
@item error
Displays error conditions which are normally not reported, but passed
to the caller layers for handling.
@item trace0 through trace9
Ten levels of verbosity, @code{trace0} producing less output,
@code{trace9} producing the maximum amount of output.
@item prot
Displays network protocol interaction, where applicable.
@end table
The overall debugging level is specified as a list of individual levels,
delimited with semicolons. Each individual level can be specified as
one of:
@table @asis
@item !@var{category}
Disables all levels for the specified category.
@item @var{category}
Enables all levels for the specified category.
@item @var{category}.@var{level}
For this category, enables all levels from @samp{error} to @var{level}, inclusive.
@item @var{category}.=@var{level}
Enables only the given @var{level} in this @var{category}.
@item @var{category}.!@var{level}
Disables all levels from @samp{error} to @var{level}, inclusive, in
this @var{category}.
@item @var{category}.!=@var{level}
Disables only the given @var{level} in this @var{category}.
@item @var{category}.@var{levelA}-@var{levelB}
Enables all levels in the range from @var{levelA} to @var{levelB}, inclusive.
@item @var{category}.!@var{levelA}-@var{levelB}
Disables all levels in the range from @var{levelA} to @var{levelB}, inclusive.
@end table
Additionally, a comma-separated list of level specifications is
allowed after the dot. For example, the following specification:
@example
acl.prot,!=trace9,!trace2
@end example
@noindent
enables in category acl all levels, except trace9, trace0, trace1, and trace2.
Implementation and applicability of each level of debugging differs
between various categories. Categories built-in to mailutils are
described in @uref{http://mailutils.org/wiki/Debug_level}. Mailfromd
introduces the following additional categories:
@table @asis
@item db
@table @asis
@item trace0
Detailed debugging info about expiration and compaction.
@item trace5
List records being removed.
@end table
@item dns
@table @asis
@item trace8
Verbose information about attempted DNS queries and their results.
@item trace9
Enables @samp{libadns} internal debugging.
@end table
@item srvman
@table @asis
@item trace0
Additional information about normal conditions, such as subprocess
exiting successfully or a remote party being allowed access by ACL.
@item trace1
Detailed transcript of server manager actions: startup, shutdown,
subprocess cleanups, etc.
@item trace3
Additional info about fd sets.
@item trace4
Individual subserver status information.
@item trace5
Subprocess registration.
@end table
@item pmult
@table @asis
@item trace1
Verbosely list incoming connections, functions being executed and
erroneous conditions: missing headers in SMFIR_CHGHEADER, undefined
macros, etc.
@item trace2
List milter requests being processed.
@item trace7
List SMTP body content in SMFIR_REPLBODY requests.
@item error
Verbosely list mild errors encountered: bad recipient addresses, etc.
@end table
@item callout
@table @asis
@item trace0
Verification session transcript.
@item trace1
MX servers checks.
@item trace5
List emails being checked.
@item trace9
Additional info.
@end table
@item main
@table @asis
@item trace5
Info about hostnames in relayed domain list
@end table
@item engine
Debugging of the virtual engine.
@table @asis
@item trace5
Message modification lists.
@item trace6
Debug message modification operations and Sendmail macros registered.
@item trace7
List SMTP stages (@samp{xxfi_*} calls).
@item trace9
Cleanup calls.
@end table
@item pp
Preprocessor.
@table @asis
@item trace1
Show command line of the preprocessor being run.
@end table
@item prog
@table @asis
@item trace8
Stack operations
@item trace9
Debug exception state save/restore operations.
@end table
@item spf
@table @asis
@item error
Mild errors.
@item trace0
List calls to @samp{spf_eval_record}, @samp{spf_test_record},
@samp{spf_check_host_internal}, etc.
@item trace1
General debug info.
@item trace6
Explicitly list A records obtained when processing the @samp{a} SPF mechanism.
@end table
@end table
Categories starting with @samp{bi_} debug built-in modules:
@table @asis
@item bi_db
Database functions.
@table @asis
@item trace5
List database look-ups.
@item trace6
Trace operations on the greylisting database.
@end table
@item bi_sa
SpamAssassin and ClamAV API.
@table @asis
@item trace1
Report the findings of the @samp{clamav} function.
@item trace9
Trace payload in interactions with @samp{spamd}.
@end table
@item bi_io
I/O functions.
@table @asis
@item trace1
Debug the following functions: @code{open}, @code{spawn}, @code{write}.
@item trace2
Report stderr redirection.
@item trace3
Report external commands being run.
@end table
@item bi_mbox
Mailbox functions.
@table @asis
@item trace1
Report opened mailboxes.
@end table
@item bi_other
Other built-ins.
@table @asis
@item trace1
Report results of checks for existence of usernames.
@end table
@end table
For example, the following invocation enables levels up to
@samp{trace2} in category @samp{engine}, all levels in category
@samp{savsrv} and levels up to @samp{trace0} in category
@samp{srvman}:
@example
$ @kbd{mailfromd --debug='engine.trace2;savsrv;srvman.trace0'}
@end example
You need to have sufficient knowledge about @command{mailfromd}
internal structure to use this form of the @option{--debug} option.
@anchor{SMTP transcript}
@xopindex{transcript, mailfromd, introduced}
To control the execution of the sender verification functions
(@pxref{SMTP Callout functions}), you may use
@option{--transcript} (@option{-X}) command line option which enables
transcripts of @acronym{SMTP} sessions in the logs. Here is an example
of the output produced running @kbd{mailfromd --transcript}:
@xopindex{transcript, mailfromd, output example}
@example
@group
k8DHxlCa001774: RECV: 220 spf-jail1.us4.outblaze.com ESMTP Postfix
k8DHxlCa001774: SEND: HELO mail.gnu.org.ua
k8DHxlCa001774: RECV: 250 spf-jail1.us4.outblaze.com
k8DHxlCa001774: SEND: MAIL FROM: <>
k8DHxlCa001774: RECV: 250 Ok
k8DHxlCa001774: SEND: RCPT TO: <t1Kmx17Q@@malaysia.net>
k8DHxlCa001774: RECV: 550 <>: No thank you rejected: Account
Unavailable: Possible Forgery
k8DHxlCa001774: poll exited with status: not_found; sent
"RCPT TO: <t1Kmx17Q@@malaysia.net>", got "550 <>: No thank you
rejected: Account Unavailable: Possible Forgery"
k8DHxlCa001774: SEND: QUIT
@end group
@end example
@node Runtime errors
@section Runtime Errors
@cindex runtime error
A @dfn{runtime error} is a special condition encountered during
execution of the filter program, that makes further execution of
the program impossible. There are two kinds of runtime errors: fatal
errors, and uncaught exceptions. Whenever a runtime error occurs,
@command{mailfromd} writes into the log file the following message:
@example
RUNTIME ERROR near @var{file}:@var{line}: @var{text}
@end example
@noindent
where @var{file}:@var{line} indicates approximate source file location
where the error occurred and @var{text} gives the textual description
of the error.
@subheading Fatal runtime errors
@cindex runtime errors, fatal
@cindex fatal runtime errors
Fatal runtime errors are caused by a condition that is impossible to
fix at run time. For version @value{VERSION} these are:
@table @asis
@cindex @samp{Not enough memory}, runtime error
@item Not enough memory
There is not enough memory for the execution of the program. Try to
make more memory available for @command{mailfromd} or to reduce
its memory requirements by rewriting your filter script.
@cindex @samp{Out of stack space; increase #pragma stacksize}, runtime error
@item Out of stack space; increase #pragma stacksize
@cindex @samp{Heap overrun; increase #pragma stacksize}, runtime error
@itemx Heap overrun; increase #pragma stacksize
@cindex @samp{memory chunk too big to fit into heap}, runtime error
@itemx memory chunk too big to fit into heap
These errors are reported when there is not enough space left on
stack to perform the requested operation, and the attempt to resize the stack
has failed. Usually @command{mailfromd} expands the stack when the need
arises (@pxref{automatic stack resizing}). This runtime error
indicates that there were no more memory available for stack
expansion. Try to make more memory available for @command{mailfromd}
or to reduce its memory requirements by rewriting your filter script.
@cindex @samp{Stack underflow}, runtime error
@item Stack underflow
Program attempted to pop a value off the stack but the stack was
already empty. This indicates an internal error in the
@acronym{MFL} compiler or @command{mailfromd} runtime engine. If you
ever encounter this error,
please report it to @email{bug-mailfromd@@gnu.org.ua}. Include
the log fragment (about 10-15 lines before and after this log message)
and your filter script. @xref{Reporting Bugs}, for more
information about bug reporting.
@cindex @samp{pc out of range}, runtime error
@item pc out of range
The @dfn{program counter} is out of allowed range. This is a severe
error, indicating an internal inconsistency in @command{mailfromd}
runtime engine. If you encounter it, please report it to
@email{bug-mailfromd@@gnu.org.ua}. Include the log fragment (about
10-15 lines before and after this log message) and your filter script.
@xref{Reporting Bugs}, for more information about how to report a
bug.
@end table
@subheading Programmatic runtime errors
These indicate a programmatic error in your filter script, which the
@acronym{MFL} compiler was unable to discover at compilation stage:
@table @asis
@cindex @samp{Invalid exception number}, runtime error
@item Invalid exception number: @var{n}
The @code{throw} statement used a not existent exception number @var{n}.
Fix the statement and restart @command{mailfromd}. @xref{throw}, for
the information about @code{throw} statement and see @ref{Exceptions},
for the list of available exception codes.
@cindex @samp{No previous regular expression}, runtime error
@item No previous regular expression
You have used a back-reference (@pxref{Back references}), where there
is no previous regular expression to refer to. Fix this line in
your code and restart the program.
@cindex @samp{Invalid back-reference number}, runtime error
@item Invalid back-reference number
You have used a back-reference (@pxref{Back references}), with a
number greater than the number of available groups in the previous
regular expression. For example:
@example
@group
if $f matches "(.*)@@gnu.org"
# @r{Wrong: there is only one group in the regexp above!}
set x \2
@dots{}
@end group
@end example
Fix your code and restart the daemon.
@end table
@anchor{uncaught exceptions}
@subheading Uncaught exceptions
Another kind of runtime errors are @dfn{uncaught exceptions},
i.e. exceptional conditions for which no handler was installed
(@xref{Exceptions}, for information on exceptions and on how to
handle them). These errors mean that the programmer (i.e. you), made
no provision for some specific condition. For example, consider the
following code:
@example
@group
prog envfrom
do
if $f mx matches "yahoo.com"
foo()
fi
done
@end group
@end example
@noindent
It is syntactically correct, but it overlooks the fact that @code{mx
matches} may generate @code{e_temp_failure} exception, if the underlying
@acronym{DNS} query has timed out (@pxref{Special comparisons}). If
this happens, @command{mailfromd} has no instructions on what to do
next and reports an error. This can easily be fixed using a
@code{try}/@code{catch} (@pxref{Catch and Throw}) statement, e.g.:
@example
prog envfrom
do
try
do
if $f mx matches "yahoo.com"
foo()
fi
done
# Catch @acronym{DNS} errors
catch e_temp_failure or e_failure
do
tempfail 451 4.1.1 "MX verification failed"
done
done
@end example
Another common case are undefined Sendmail macros. In this case the
@code{e_macroundef} exception is generated:
@example
RUNTIME ERROR near foo.c:34: Macro not defined: @{client_adr@}
@end example
@noindent
These can be caused either by misspelling the macro name (as in the
example message above) or by failing to export the required name in
Sendmail milter configuration (@pxref{exporting macros}). This error
should be fixed either in your source code or in @file{sendmail.cf}
file, but if you wish to provide a special handling for it, you can
use the following catch statement:
@example
@group
catch e_macroundef
do
@dots{}
done
@end group
@end example
@anchor{tracing runtime errors}
@cindex runtime errors, tracing
Sometimes the location indicated with the runtime error message is
not enough to trace the origin of the error. For example, an error
can be generated explicitly with @code{throw} statement
(@pxref{throw}):
@example
RUNTIME ERROR near match_cidr.mfl:30: invalid CIDR (text)
@end example
If you look in module @file{match_cidr.mfl}, you will see
the following code (line numbers added for reference):
@example
23 func match_cidr(string ipstr, string cidr)
24 returns number
25 do
26 number netmask
27
28 if cidr matches '^(([0-9]@{1,3@}\.)@{3@}[0-9]@{1,3@})/([0-9][0-9]?)'
29 return inet_aton(ipstr) & len_to_netmask(\3) = inet_aton(\1)
30 else
31 throw invcidr "invalid CIDR (%cidr)"
32 fi
33 return 0
34 done
@end example
@xopindex{stack-trace, mailfromd, explained}
Now, it is obvious that the value of @code{cidr} argument to
@code{match_cidr} was wrong, but how to find the caller that passed
the wrong value to it? The special command line option
@option{--stack-trace} is provided for this. This option enables
dumping @dfn{stack traces} when a fatal error occurs. Traces
contain information about function calls. Continuing our example,
using the @option{--stack-trace} option you will see the following diagnostics:
@example
RUNTIME ERROR near match_cidr.mfl:30: invalid CIDR (127%)
mailfromd: Stack trace:
mailfromd: 0077: match_cidr.mfl:31: match_cidr
mailfromd: 0096: test.mfl:13: bar
mailfromd: 0110: mailfromd.mfl:18: foo
mailfromd: Stack trace finishes
mailfromd: Execution of the configuration program was not finished
@end example
@cindex stack traces, reading
Each trace line describes one stack frame. The lines appear in the
order of most recently called to least recently called. Each frame
consists of:
@enumerate 1
@item Value of the program counter at the time of its execution;
@item Source code location, if available;
@item Name of the function called.
@end enumerate
Thus, the example above can be read as: ``the function
@code{match_cidr} was called by the function @code{bar} in file
@file{test.mfl} at line 13. This function was called from
the function @code{bar}, in file @file{test.mfl} at line 13. In its turn,
@code{bar} was called by the function @code{foo}, in file
@file{mailfromd.mfl} at line 18''.
Examining caller functions will help you localize the source of
the error and fix it.
@cindex @code{stack_trace} function, introduced
You can also request a stack trace any place in your code, by
calling the @code{stack_trace} function. This can be useful for
debugging.
@node Notes
@section Notes and Cautions
This section discusses some potential culprits in the
@acronym{MFL}.
It is important to execute special caution when writing format
strings for @code{sprintf} (@pxref{String formatting}) and @code{strftime}
(@pxref{strftime}) functions. They use @samp{%} as a character
introducing conversion specifiers, while the same character is used to
expand a @acronym{MFL} variable within a string. To prevent this
misinterpretation, always enclose format specification in @emph{single
quotes} (@pxref{singe-vs-double}). To illustrate this, let's consider
the following example:
@example
echo sprintf ("Mail from %s", $f)
@end example
If a variable @code{s} is not declared, this line will produce the
@samp{Variable s is not defined} error message, which will allow you
to identify and fix the bug. The situation is considerably worse if
@code{s} is declared. In that case you will see no warning message,
as the statement is perfectly valid, but at the run-time the variable
@code{s} will be interpreted within the format string, and its value
will replace @code{%s}. To prevent this from happening, single quotes
must be used:
@example
echo sprintf ('Mail from %s', $f)
@end example
This does not limit the functionality, since there is no need to fall
back to variable interpretation in format strings.
@anchor{variable--constant clashes}
Yet another dangerous feature of the language is the way to refer to
variable and constant names within literal strings. To expand a
variable or a constant the same notation is used
(@xref{Variables}, and @pxref{Constants}). Now, lets consider the
following code:
@example
const x 2
string x "X"
prog envfrom
do
echo "X is %x"
done
@end example
Does @code{%x} in @code{echo} refers to the variable or to the
constant? The correct answer is @samp{to the variable}. When executed, this
code will print @samp{X is X}.
As of version @value{VERSION}, @command{mailfromd} will always print a
diagnostic message whenever it stumbles upon a variable having the
same name as a previously defined constant or vice versa. The
resolution of such name clashes is described in detail in
@xref{variable--constant shadowing}.
Future versions of the program may provide a non-ambiguous way of
referring to variables and constants from literal strings.
@node MFL
@chapter Mail Filtering Language
@cindex MFL
@cindex mail filtering language
The @dfn{mail filtering language}, or @acronym{MFL}, is a special
language designed for writing filter scripts. It has a simple syntax,
similar to that of Bourne shell. In contrast to the most existing
programming languages, @acronym{MFL} does not have any special
terminating or separating characters (like, e.g. newlines and
semicolons in shell)@footnote{There are two noteworthy exceptions:
@code{module} and @code{from ... import} statements, which must be
terminated with a period. For details, refer to @ref{module structure}, and @ref{import}.}. All syntactical entities
are separated by any amount of white-space characters (i.e. spaces,
tabulations or newlines).
The following sections describe @acronym{MFL} syntax in detail.
@menu
* Comments:: Comments.
* include::
* line::
* Generated warnings and errors::
* Pragmas:: Pragmatic comments.
* Data Types::
* Numbers::
* Literals::
* Here Documents::
* Sendmail Macros::
* Constants::
* Variables::
* Back references::
* Handlers::
* Special handlers:: Initialization and cleanup handlers.
* Functions:: Functions.
* Expressions:: Expressions.
* Shadowing:: Variable and Constant Shadowing.
* Statements::
* Conditionals:: Conditional Statements.
* Loops:: Loop Statements.
* Exceptions:: Exceptional Conditions and their Handling.
* Polling:: Sender Verification Tests.
* Modules:: Modules are Collections of Useful Functions.
* mfmod:: Dynamically Loaded Modules.
* Preprocessor:: Input Text Is Preprocessed.
* Filter Script Example:: A Working Filter Script Explained.
* Reserved Words:: A Reference List of Reserved Words.
@end menu
@node Comments
@section Comments
@cindex comments
Two types of comments are allowed: @sc{c}-style, enclosed between
@samp{/*} and @samp{*/}, and shell-style, starting with @samp{#}
character and extending up to the end of line:
@example
@group
/* This is
a comment. */
# And this too.
@end group
@end example
There are, however, several special cases, where the characters
following @samp{#} are not ignored:
If the first line begins with @samp{#!/} or @samp{#! /}, this is
treated as a start of a multi-line comment, which is closed by the
characters @samp{!#} on a line by themselves. This feature allows for
writing sophisticated scripts. @xref{top-block}, for a detailed
description.
A @samp{#} is followed by @samp{include}, @samp{include_once},
@samp{line}, @samp{error}, or @samp{warning} is treated specially.
These cases are covered by the subsequent sections.
@node include
@section #include and #include_once
@cindex #include statement
@cindex including files
@kwindex #include
If @samp{#} is followed by word @samp{include} (with
optional whitespace between them), this statement requires inclusion
of the specified file, as in @sc{c}. There are two forms of the
@samp{#include} statement:
@enumerate 1
@item @code{#include <@var{file}>}
@item @code{#include "@var{file}"}
@end enumerate
The quotes around @var{file} in the second form quotes are optional.
@anchor{include search path}
@cindex include search path, introduced
Both forms are equivalent if @var{file} is an absolute file name.
Otherwise, the first form will look for @var{file} in the @dfn{include
search path}. The second one will look for it in the current working
directory first, and, if not found there, in the include search
path.
The default include search path is:
@enumerate 1
@item @file{@var{prefix}/share/mailfromd/include}
@item @file{@var{prefix}/share/mailfromd/@value{VERSION}/include}
@end enumerate
@noindent
where @var{prefix} is the installation prefix.
New directories can be appended in front of it using @option{-I}
(@option{--include-path}) command line option, or @code{include-path}
configuration statement (@pxref{conf-base, include-path}).
For example, invoking
@example
$ @kbd{mailfromd -I/var/mailfromd -I/com/mailfromd}
@end example
@noindent
creates the following include search path
@enumerate 1
@item @file{/var/mailfromd}
@item @file{/com/mailfromd}
@item @file{@var{prefix}/share/mailfromd/include}
@item @file{@var{prefix}/share/mailfromd/@value{VERSION}/include}
@end enumerate
@anchor{include_once}
@cindex include_once
Along with @code{#include}, there is also a special form
@code{#include_once}, that has the same syntax:
@example
#include_once <@var{file}>
#include_once "@var{file}"
@end example
This form works exactly as @code{#include}, except that, if the
@var{file} has already been included, it will not be included
again. As the name suggests, it will be included only once.
This form should be used to prevent re-inclusions of a code, which
can cause problems due to function redefinitions, variable
reassignments etc.
@node line
@section #line
@cindex line, @code{#line} statement
@kwindex #line
A line in the form
@example
#line @var{number} "@var{identifier}"
@end example
@noindent
causes the @acronym{MFL} compiler to believe, for purposes of error
diagnostics, that the line number of the next source line is given by
@var{number} and the current input file is named by @var{identifier}.
If the identifier is absent, the remembered file name does not change.
Line directives in @command{cpp} style are also understood:
@example
# @var{number} "@var{identifier}"
@end example
@node Generated warnings and errors
@section #warning and #error
@kwindex #warning
@cindex #warning
If @samp{#} is followed by the word @samp{warning}, any amount of
whitespace and a string in double quotes, the compiler will use that
string to generate a warning message at that point. As usual,
whitespace characters are allowed between @samp{#} and @samp{warning}:
@example
# warning "The code below is suspicious"
@end example
@kwindex #error
@cindex #error
Similarly, @samp{#} followed by the word @samp{error}, whitespace and
a doubly-quoted string causes the compiler to generate a compilation
error at that point.
To use backslash or double quote in the message text, precede them
with a single slash, e.g.:
@example
#error "the \"quoted\" text"
@end example
A backslash in front of any other character is retained.
@node Pragmas
@section Pragmatic comments
@cindex pragmatic comments
@cindex #pragma statement
@kwindex #pragma
If @samp{#} is immediately followed by word @samp{pragma} (with
optional whitespace between them), such a construct introduces a
@dfn{pragmatic comment}, i.e. an instruction that controls some
configuration setting.
The available pragma types are described in the following subsections.
@menu
* prereq:: Pragma prereq.
* stacksize:: Pragma stacksize.
* regex:: Pragma regex.
* dbprop:: Pragma dbprop.
* greylist:: Pragma greylist.
* miltermacros:: Pragma miltermacros.
* provide-callout:: Pragma provide-callout.
@end menu
@node prereq
@subsection Pragma prereq
The @code{#pragma prereq} statement ensures that the correct
@command{mailfromd} version is used to compile the source file it
appears in. It takes version number as its arguments and produces
a compilation error if the actual @command{mailfromd} version number
is earlier than that. For example, the following statement:
@example
#pragma prereq 7.0.94
@end example
@noindent
results in error if compiled with @command{mailfromd} version 7.0.93
or prior.
@node stacksize
@subsection Pragma stacksize
The @code{stacksize} pragma sets the initial size of the run-time
stack and may also define the policy of its growing, in case it
becomes full. The default stack size is @value{STACK_SIZE} words. You may
need to increase this number if your configuration program uses
recursive functions or does an excessive amount of string manipulations.
@deffn {pragma} stacksize size [incr [max]]
Sets stack size to @var{size} units. Optional @var{incr} and
@var{max} define stack growth policy (see below). The default
@dfn{units} are words. The following example sets the stack
size to 7168 words:
@example
#pragma stacksize 7168
@end example
The @var{size} may end with a @dfn{unit size} suffix:
@float Table, unit-size
@caption{Unit Size Suffix}
@multitable @columnfractions 0.3 0.6
@headitem Suffix @tab Meaning
@item k @tab Kiloword, i.e. 1024 words
@item m @tab Megawords, i.e. 1048576 words
@item g @tab Gigawords,
@item t @tab Terawords (ouch!)
@end multitable
@end float
File suffixes are case-insensitive, so the following two pragmas are
equivalent and set the stack size to @code{7*1048576 = 7340032} words:
@example
@group
#pragma stacksize 7m
#pragma stacksize 7M
@end group
@end example
@anchor{automatic stack resizing}
When the @acronym{MFL} engine notices that there is no more stack space
available, it attempts to expand the stack. If this attempt succeeds, the
operation continues. Otherwise, a runtime error is reported and the
execution of the filter stops.
@cindex stack growth policy
@cindex growth policy, stack
The optional @var{incr} argument to @code{#pragma stacksize} defines growth
policy for the stack. Two growth policies are implemented:
@dfn{fixed increment policy}, which expands stack in a fixed
number of @dfn{expansion chunks}, and @dfn{exponential growth policy}, which
duplicates the stack size until it is able to accommodate the needed
number of words. The fixed increment policy is the default. The default
chunk size is @value{STACK_INCR} words.
If @var{incr} is the word @samp{twice}, the duplicate policy is
selected. Otherwise @var{incr} must be a positive number optionally
suffixed with a size suffix (see above). This indicates the expansion
chunk size for the fixed increment policy.
The following example sets initial stack size to 10240, and
expansion chunk size to 2048 words:
@example
#pragma stacksize 10M 2K
@end example
The pragma below enables exponential stack growth policy:
@example
#pragma stacksize 10240 twice
@end example
In this case, when the run-time evaluator hits the stack size limit,
it expands the stack to twice the size it had before. So, in the
example above, the stack will be sequentially expanded to the
following sizes: 20480, 40960, 81920, 163840, etc.
The optional @var{max} argument defines the maximum size of
the stack. If stack grows beyond this limit, the execution of the
script will be aborted.
@end deffn
If you are concerned about the execution time of your script, you
may wish to avoid stack reallocations. To help you find out the
optimal stack size, each time the stack is expanded,
@command{mailfromd} issues a warning in its log file, which looks like
this:
@example
warning: stack segment expanded, new size=8192
@end example
You can use these messages to adjust your stack size configuration
settings.
@node regex
@subsection Pragma regex
@anchor{pragma regex}
The @samp{#pragma regex}, controls
compilation of regular expressions. You can use any number
of such pragma directives in your @file{mailfromd.mfl}. The scope of
@samp{#pragma regex} extends to the next occurrence of this directive
or to the end of the script file, whichever occurs first.
@deffn {pragma} regex [push|pop] flags
The optional @var{push|pop} parameter is one of the words @samp{push} or
@samp{pop} and is discussed in detail below. The @var{flags}
parameter is a whitespace-separated list of @dfn{regex flags}. Each
regex-flag is a word specifying some regex feature. It can be
preceded by @samp{+} to enable this feature (this is the default), by
@samp{-} to disable it or by @samp{=} to reset regex flags to its
value. Valid regex-flags are:
@table @samp
@item extended
Use @acronym{POSIX} Extended Regular Expression syntax when
interpreting regex. If not set, @acronym{POSIX} Basic Regular
Expression syntax is used.
@item icase
Do not differentiate case. Subsequent regex searches will be case
insensitive.
@item newline
@dfn{Match-any-character} operators don't match a newline.
A non-matching list (@samp{[^...]}) not containing a newline does not
match a newline.
@dfn{Match-beginning-of-line} operator (@samp{^}) matches the empty
string immediately after a newline.
@dfn{Match-end-of-line} operator (@samp{$}) matches the empty string
immediately before a newline.
@end table
For example, the following pragma enables @acronym{POSIX} extended,
case insensitive matching (a good thing to start your
@file{mailfromd.mfl} with):
@example
#pragma regex +extended +icase
@end example
@end deffn
Optional modifiers @samp{push} and @samp{pop} can be used to maintain
a stack of regex flags. The statement
@example
#pragma regex push [@var{flags}]
@end example
@noindent
saves current regex flags on stack and then optionally modifies them
as requested by @var{flags}.
The statement
@example
#pragma regex pop [@var{flags}]
@end example
@noindent
does the opposite: restores the current regex flags from the top of
stack and applies @var{flags} to it.
This statement is useful in module and include files to avoid disturbing user
regex settings. E.g.:
@example
@group
#pragma regex push +extended +icase
.
.
.
#pragma regex pop
@end group
@end example
@node dbprop
@subsection Pragma dbprop
@deffn {pragma} dbprop pattern prop @dots{}
This pragma configures properties for a @acronym{DBM} database.
@xref{Database functions}, for its detailed description.
@end deffn
@node greylist
@subsection Pragma greylist
@deffn {pragma} greylist type
Selects the greylisting implementation to use. Allowed
values for @var{type} are:
@table @asis
@item traditional
@itemx gray
Use the traditional greylisting implementation. This is the default.
@item con-tassios
@itemx ct
Use Con Tassios greylisting implementation.
@end table
@xref{greylisting types}, for a detailed description of these
greylisting implementations.
@end deffn
Notice, that this pragma can be used only once. A second use of this
pragma would constitute an error, because you cannot use both greylisting
implementations in the same program.
@node miltermacros
@subsection Pragma miltermacros
@deffn {pragma} miltermacros handler macro @dots{}
Declare that the Milter stage @var{handler} uses @acronym{MTA} macro
listed as the rest of arguments. The @var{handler} must be a valid
handler name (@pxref{Handlers}).
@end deffn
The @command{mailfromd} parser collects the names of the macros
referred to by a @samp{$@var{name}} construct within a handler
(@pxref{Sendmail Macros}) and declares them automatically for
corresponding handlers. It is, however, unable to track macros
used in functions called from handler as well as those referred to
via @code{getmacro} and @code{macro_defined} functions. Such
macros should be declared using @samp{#pragma miltermacros}.
During initial negotiation with the @acronym{MTA},
@command{mailfromd} will ask it to export the macro names declared
automatically or by using the @samp{#pragma miltermacros}. The
@acronym{MTA} is free to honor or to ignore this request. In
particular, Sendmail versions prior to 8.14.0 and Postfix versions
prior to 2.5 do not support this feature. If you use one of these,
you will need to export the needed macros explicitly in the
@acronym{MTA} configuration. For more details, refer to the section
in @ref{MTA Configuration} corresponding to your @acronym{MTA} type.
@node provide-callout
@subsection Pragma provide-callout
The @code{#pragma provide-callout} statement is used in the
@file{callout} module to inform @command{mailfromd} that the
module has been loaded.
Do not use this pragma.
@node Data Types
@section Data Types
The @command{mailfromd} filter script language operates on entities
of two types: numeric and string.
The @dfn{numeric} type is represented internally as a signed long
integer. Depending on the machine architecture, its size can vary.
For example, on machines with Intel-based @acronym{CPU}s it is 32 bits long.
A @dfn{string} is a string of characters of arbitrary length.
Strings can contain any characters except @acronym{ASCII} @sc{nul}.
There is also a @dfn{generic pointer}, which is designed to
facilitate certain operations. It appears only in the @code{body}
handler. @xref{body handler}, for more information about it.
@node Numbers
@section Numbers
A @dfn{decimal number} is any sequence of decimal digits, not
beginning with @samp{0}.
An @dfn{octal number} is @samp{0} followed by any number of octal
digits (@samp{0} through @samp{7}), for example: @code{0340}.
A @dfn{hex number} is @samp{0x} or @samp{0X} followed by any number
of hex digits (@samp{0} through @samp{9} and @samp{a} through @samp{f}
or @samp{A} through @samp{F}), for example: @code{0x3ef1}.
@node Literals
@section Literals
@cindex literals
A literal is any sequence of characters enclosed in single or
double quotes.
After @code{tempfail} and @code{reject} actions two special kinds of
literals are recognized: three-digit numeric values represent
@acronym{RFC} 2821 reply codes, and literals consisting of tree digit
groups separated by dots represent an extended reply code as per
@acronym{RFC} 1893/2034. For example:
@example
@group
510 # @r{A reply code}
5.7.1 # @r{An extended reply code}
@end group
@end example
@anchor{Double-quoted strings}
@subheading Double-quoted strings
String literals enclosed in double quotation marks
(@dfn{double-quoted strings}) are subject to @dfn{backslash interpretation},
@dfn{macro expansion}, @dfn{variable interpretation} and @dfn{back reference
interpretation}.
@cindex backslash interpretation
@dfn{Backslash interpretation} is performed at compilation time. It
consists in replacing the following @dfn{escape sequences} with the
corresponding single characters:
@float Table, backslash-interpretation
@caption{Backslash escapes}
@multitable @columnfractions 0.30 .5
@item Sequence @tab Replaced with
@item \a @tab Audible bell character (@acronym{ASCII} 7)
@item \b @tab Backspace character (@acronym{ASCII} 8)
@item \f @tab Form-feed character (@acronym{ASCII} 12)
@item \n @tab Newline character (@acronym{ASCII} 10)
@item \r @tab Carriage return character (@acronym{ASCII} 13)
@item \t @tab Horizontal tabulation character (@acronym{ASCII} 9)
@item \v @tab Vertical tabulation character (@acronym{ASCII} 11)
@end multitable
@end float
In addition, the sequence @samp{\@var{newline}} has the same
effect as @samp{\n}, for example:
@example
@group
"a string with\
embedded newline"
"a string with\n embedded newline"
@end group
@end example
Any escape sequence of the form @samp{\x@var{hh}}, where @var{h}
denotes any hex digit is replaced with the character whose @acronym{ASCII} value
is @var{hh}. For example:
@example
"\x61nother" @result{} "another"
@end example
Similarly, an escape sequence of the form @samp{\0@var{ooo}}, where
@var{o} is an octal digit, is replaced with the character whose @acronym{ASCII}
value is @var{ooo}.
@cindex variable interpretation
@cindex macro expansion
Macro expansion and variable interpretation occur at run-time. During
these phases all Sendmail macros (@pxref{Sendmail Macros}),
@command{mailfromd} variables (@pxref{Variables}), and constants
(@pxref{Constants}) referenced in the string are replaced by their
actual values. For example, if the Sendmail macro @code{f} has the
value @samp{postmaster@@gnu.org.ua} and the variable @code{last_ip}
has the value @samp{127.0.0.1}, then the
string@footnote{Implementation note: actually, the references
are not interpreted within the string, instead, each such string is
split at compilation time into a series of concatenated atoms. Thus,
our sample string will actually be compiled as:
@example
$f . " last connected from " . last_ip . ";"
@end example
@xref{Concatenation}, for a description of this construct. You can
easily see how various strings are interpreted by using
@option{--dump-tree} option (@pxref{--dump-tree}). In this case,
it will produce:
@example
CONCAT:
CONCAT:
CONCAT:
SYMBOL: f
CONSTANT: " last connected from "
VARIABLE last_ip (13)
CONSTANT: ";"
@end example
}
@example
"$f last connected from %last_ip;"
@end example
@noindent
will be expanded to
@example
"postmaster@@gnu.org.ua last connected from 127.0.0.1;"
@end example
@cindex back reference interpretation
A @dfn{back reference} is a sequence @samp{\@var{d}}, where @var{d}
is a decimal number. It refers to the @var{d}th parenthesized
subexpression in the last @command{matches} statement@footnote{The
subexpressions are numbered by the positions of their opening
parentheses, left to right.}. Any back reference occurring within a
double-quoted string is replaced by the value of the corresponding
subexpression. @xref{Special comparisons}, for a detailed
description of this process. Back reference interpretation is
performed at run time.
@anchor{singe-vs-double}
@subheading Single-quoted strings
Any characters enclosed in single quotation marks are read unmodified.
The following examples contain pairs of equivalent strings:
@example
@group
"a string"
'a string'
"\\(.*\\):"
'\(.*\):'
@end group
@end example
Notice the last example. Single quotes are particularly useful in writing
regular expressions (@pxref{Special comparisons}).
@node Here Documents
@section Here Documents
@cindex here document
@cindex multiline strings
@dfn{Here-document} is a special form of a string literal is, allowing to
specify multiline strings without having to use backslash
escapes. The format of here-documents is:
@example
@group
<<[@var{flags}]@var{word}
@dots{}
@var{word}
@end group
@end example
The @code{<<@var{word}} construct instructs the parser to read all
the following lines up to the line containing only @var{word}, with
possible trailing blanks. The lines thus read are concatenated
together into a single string. For example:
@example
@group
set str <<EOT
A multiline
string
EOT
@end group
@end example
The body of a here-document is interpreted the same way as
double-quoted strings (@pxref{Double-quoted strings}). For example,
if Sendmail macro @code{f} has the value @code{jsmith@@some.com} and
the variable @code{count} is set to @code{10}, then the following string:
@example
@group
set s <<EOT
<$f> has tried to send %count mails.
Please see docs for more info.
EOT
@end group
@end example
@noindent
will be expanded to:
@example
@group
<jsmith@@some.com> has tried to send 10 mails.
Please see docs for more info.
@end group
@end example
If the @var{word} is quoted, either by enclosing it in single quote
characters or by prepending it with a backslash, all interpretations
and expansions within the document body are suppressed. For
example:
@example
@group
set s <<'EOT'
The following line is read verbatim:
<$f> has tried to send %count mails.
Please see docs for more info.
EOT
@end group
@end example
Optional @var{flags} in the here-document construct control the way
leading white space is handled. If @var{flags} is @code{-} (a dash),
then all leading tab characters are stripped from input lines and the
line containing @var{word}. Furthermore, if @code{-} is followed by a
single space, all leading whitespace is stripped from them. This
allows here-documents within configuration scripts to be indented in a
natural fashion. Examples:
@example
@group
<<- TEXT
<$f> has tried to send %count mails.
Please see docs for more info.
TEXT
@end group
@end example
Here-documents are particularly useful with @code{reject} actions
(@pxref{reject and tempfail syntax}).
@node Sendmail Macros
@section Sendmail Macros
@cindex macros, referencing
@cindex Sendmail macros, referencing
Sendmail macros are referenced exactly the same way they are in
@file{sendmail.cf} configuration file, i.e.@: @samp{$@var{name}},
where @var{name} represents the macro name. Notice, that the notation
is the same for both single-character and multi-character macro names.
For consistency with the @command{Sendmail} configuration the
@samp{$@{@var{name}@}} notation is also accepted.
Another way to reference Sendmail macros is by using function
@code{getmacro} (@pxref{Macro access}).
Sendmail macros evaluate to string values.
Notice, that to reference a macro, you must properly export it in
your @acronym{MTA} configuration. Attempt to reference a not exported
macro will result in raising a @code{e_macroundef} exception at the run time
(@pxref{uncaught exceptions}).
@node Constants
@section Constants
@cindex constants, defining
@cindex const
A @dfn{constant} is a symbolic name for an @acronym{MFL} value.
Constants are defined using @code{const} statement:
@example
[@var{qualifier}] const @var{name} @var{expr}
@end example
@noindent
where @var{name} is an identifier, and @var{expr} is any valid
@acronym{MFL} expression evaluating immediately to a constant literal
or numeric value. Optional @var{qualifier} defines the scope of
visibility for that constant (@pxref{scope of visibility}): either
@code{public} or @code{static}.
@cindex constants, using in program text
Once defined, any appearance of @var{name} in the program text is
replaced by its value. For example:
@example
const x 10/5
const text "X is "
@end example
@noindent
defines the numeric constant @samp{x} with the value @samp{5}, and the
literal constant @samp{text} with the value @samp{X is }.
@cindex enumeration
A special construct is provided to define a series of numeric
constants (an @dfn{enumeration}):
@example
@group
[@var{qualifier}] const
do
@var{name0} [@var{expr0}]
@var{name1} [@var{expr1}]
...
@var{nameN} [@var{exprN}]
done
@end group
@end example
@noindent
Each @var{exprN}, if present, must evaluate to a constant numeric
expression. The resulting value will be assigned to constant
@var{nameN}. If @var{exprN} is not supplied, the constant will be
defined to the value of the previous constant plus one. If
@var{expr0} is not supplied, 0 is assumed.
For example, consider the following statement
@example
@group
const
do
A
B
C 10
D
done
@end group
@end example
@noindent
This defines @samp{A} to 0, @samp{B} to 1, @samp{C} to 10 and @samp{D}
to 11.
As a matter of fact, @var{exprN} may also evaluate to a constant
string expression, provided that all expressions in the enumeration
@samp{const} statement are provided. That is, the following is
correct:
@example
@group
const
do
A "one"
B "two"
C "three"
D "four"
done
@end group
@end example
@noindent
whereas the following is not:
@example
@group
const
do
A "one"
B
C "three"
D "four"
done
@end group
@end example
Trying to compile the latter example will produce:
@example
mailfromd: @var{filename}:5.3: initializer element is not numeric
@end example
@noindent
which means that @command{mailfromd} was trying to create constant
@samp{B} with the value of @samp{A} incremented by one, but was unable
to do so, because the value in question was not numeric.
@cindex constants, using in literals
Constants can be used in normal MFL expressions as well as in
literals. To expand a constant within a literal string, prepend a
percent sign to its name, e.g.:
@example
echo "New %text %x" @result{} "New X is 2"
@end example
This way of expanding constants creates an ambiguity if there happen
to be a variable of the same name as the constant.
@xref{variable--constant clashes}, for more information of this case
and ways to handle it.
@menu
* Built-in constants::
@end menu
@node Built-in constants
@subsection Built-in constants
@cindex built-in constants
@cindex constants, built-in
Several constants are built into the @acronym{MFL}
compiler. To discern them from user-defined ones,
their names start and end with two underscores (@samp{__}).
The following constants are defined in @command{mailfromd} version
@value{VERSION}:
@deftypevr {Built-in constant} string __file__
Expands to the name of the current source file.
@end deftypevr
@deftypevr {Built-in constant} string __function__
Expands to the name of the current lexical context,
i.e. the function or handler name.
@end deftypevr
@deftypevr {Built-in constant} string __git__
This built-in constant is defined for alpha versions only.
Its value is the Git tag of the recent commit corresponding to that
version of the package. If the release contains some uncommitted
changes, the value of the @samp{__git__} constant ends with
the suffix @samp{-dirty}.
@end deftypevr
@deftypevr {Built-in constant} number __line__
Expands to the current line number in the input source file.
@end deftypevr
@deftypevr {Built-in constant} number __major__
Expands to the major version number.
The following example uses @code{__major__} constant to determine
if some version-dependent feature can be used:
@example
@group
if __major__ > 2
# @r{Use some version-specific feature}
fi
@end group
@end example
@end deftypevr
@deftypevr {Built-in constant} number __minor__
Expands to the minor version number.
@end deftypevr
@deftypevr {Built-in constant} string __module__
Expands to the name of the current module (@pxref{Modules}).
@end deftypevr
@deftypevr {Built-in constant} string __package__
Expands to the package name (@samp{mailfromd})
@end deftypevr
@deftypevr {Built-in constant} number __patch__
For alpha versions and maintenance releases expands to the version
patch level. For stable versions, expands to @samp{0}.
@end deftypevr
@deftypevr {Built-in constant} string __defpreproc__
Expands to the default external preprocessor command line, if the
preprocessor is used, or to an empty string if it is not, e.g.:
@example
__defpreproc__ @result{} "/usr/bin/m4 -s"
@end example
@xref{Preprocessor}, for information on preprocessor and its
features.
@end deftypevr
@deftypevr {Built-in constant} string __preproc__
Expands to the current external preprocessor command line, if the
preprocessor is used, or to an empty string if it is not. Notice,
that it equals @code{__defpreproc__}, unless the preprocessor was
redefined using @option{--preprocessor} command line option
(@pxref{Preprocessor, --preprocessor}).
@end deftypevr
@deftypevr {Built-in constant} string __version__
Expands to the textual representation of the program version
(e.g.@: @samp{3.0.90})
@end deftypevr
@deftypevr {Built-in constant} string __defstatedir__
Expands to the default state directory (@pxref{statedir}).
@end deftypevr
@deftypevr {Built-in constant} string __statedir__
Expands to the current value of the program state directory
(@pxref{statedir}). Notice, that it is the same as
@code{__defstatedir__} unless the state directory was redefined at run
time.
@end deftypevr
Built-in constants can be used as variables, this allows to expand them
within strings or here-documents. The following example illustrates
the common practice used for debugging configuration scripts:
@example
@group
func foo(number x)
do
echo "%__file__:%__line__: foo called with arg %x"
@dots{}
done
@end group
@end example
If the function @code{foo} were called in line 28 of the
script file @code{/etc/mailfromd.mfl}, like this:
@code{foo(10)}, you will see the following string in your logs:
@example
/etc/mailfromd.mfl:28: foo called with arg 10
@end example
@node Variables
@section Variables
@cindex variables, defined
Variables represent regions of memory used to hold variable data.
These memory regions are identified by @dfn{variable names}. A
variable name must begin with a letter or underscore and must consist
of letters, digits and underscores.
@cindex scope of visibility, variables
@cindex variable lexical scope
@anchor{variable scope}
Each variable is associated with its @dfn{scope of visibility},
which defines the part of source code where it can be used
(@pxref{scope of visibility}). Depending on the scope, we discern
three main classes of variables: public, static and automatic (or local).
@dfn{Public variables} have indefinite lexical scope, so they may
be referred to anywhere in the program. @dfn{Static} are variables
visible only within their module (@pxref{Modules}). @dfn{Automatic}
or @dfn{local variables} are visible only within the given function or
handler.
Public and static variables are sometimes collectively called
@dfn{global}.
These variable classes occupy separate @dfn{namespaces}, so that an
automatic variable can have the same name as an existing public or
static one. In this case this variable is said to @dfn{shadow} its global
counterpart. All references to such a name will refer to the automatic
variable until the end of its scope is reached, where the global one
becomes visible again.
Likewise, a static variable may have the same name as a static
variable defined in another module. However, it may not have the
same name as a public variable.
@cindex variables, declaring
@cindex variable declarations
A variable is @dfn{declared} using the following syntax:
@example
[@var{qualifiers}] @var{type} @var{name}
@end example
@noindent
where @var{name} is the variable name, @var{type} is the type of
the data it is supposed to hold. It is @samp{string} for string
variables and @samp{number} for numeric ones.
For example, this is a declaration of a string variable @samp{var}:
@example
string var
@end example
If a variable declaration occurs within a function
(@pxref{Functions,User-defined}) or handler (@pxref{Handlers}), it
declares an automatic variable, local to this function or handler.
Otherwise, it declares a global variable.
@cindex qualifiers, variable declaration
@kwindex public
@kwindex static
Optional @var{qualifiers} are allowed only in global declarations, i.e.
in the variable declarations that appear outside of functions. They
specify the scope of the variable. The @code{public} qualifier
declares the variable as public and the @code{static} qualifier
declares it as static. The default scope is @samp{public},
unless specified otherwise in the module declaration (@pxref{module
structure}).
@kwindex precious
@cindex variables, precious
Additionally, @var{qualifiers} may contain the word @code{precious},
which instructs the compiler to mark this variable as @dfn{precious}.
(@pxref{rset,, precious variables}). The value of the precious variable
is not affected by the @acronym{SMTP} @samp{RSET} command. If both
scope qualifier and @code{precious} are used, they may appear in any
order, e.g.:
@example
static precious string rcpt_list
@end example
@noindent
or
@example
precious static string rcpt_list
@end example
Declaration can be followed by any valid @acronym{MFL}
expression, which supplies the initial value or @dfn{initializer} for the
variable, for example:
@example
string var "test"
@end example
A variable declared without initializer is implicitly
initialized to a null value, no matter what its scope: a numeric
variable assumes initial value 0, a string variables is initialized to
an empty string.
@cindex variable, assigning a value
@cindex variable assignment
@kwindex set
A variable is assigned a value using the @code{set} statement:
@example
set @var{name} @var{expr}
@end example
@noindent
where @var{name} is the variable name and @var{expr} is a
@command{mailfromd} expression (@pxref{Expressions}). The effect of
this statement is that the @var{expr} is evaluated and the value it
yields is assigned to the variable @var{name}.
If the @code{set} statement is located outside a function or handler
definition, the @var{expr} must be a constant expression, i.e. the
compiler should be able to evaluate it immediately. @FIXME-xref{optimizer}.
It is not an error to assign a value to a variable that is not
declared. In this case the assignment first declares a global or automatic
variable having the type of @var{expr} and then assigns a value to it.
Automatic variable is created if the assignment occurs within a
function or handler, global variable is declared if it occurs at
topmost lexical level. This is called @dfn{implicit variable
declaration}.
@cindex variables, referencing
In the @acronym{MFL} program, variables are referenced by their
name. When appearing inside a double-quoted string, variables are
referenced using the notation @samp{%@var{name}}. Any variable being
referenced must have been declared earlier (either explicitly or
implicitly).
@menu
* Predefined variables::
@end menu
@node Predefined variables
@subsection Predefined Variables
@cindex predefined variables
@cindex variables, predefined
Several variables are predefined. In @command{mailfromd} version
@value{VERSION} these are:
@deftypevr {Predefined Variable} number milter_state
Identifies the current @dfn{milter state} (@pxref{milter state}).
@flindex milter.mfl
The module @file{milter.mfl} defines the following symbolic names:
@table @code
@kwindex milter_state_none
@item milter_state_none
@kwindex milter_state_startup
@item milter_state_startup
@kwindex milter_state_shutdown
@item milter_state_shutdown
@kwindex milter_state_begin
@item milter_state_begin
@kwindex milter_state_end
@item milter_state_end
@kwindex milter_state_connect
@item milter_state_connect
@kwindex milter_state_helo
@item milter_state_helo
@kwindex milter_state_envfrom
@item milter_state_envfrom
@kwindex milter_state_envrcpt
@item milter_state_envrcpt
@kwindex milter_state_data
@item milter_state_data
@kwindex milter_state_header
@item milter_state_header
@kwindex milter_state_eoh
@item milter_state_eoh
@kwindex milter_state_body
@item milter_state_body
@kwindex milter_state_eom
@item milter_state_eom
@kwindex milter_action
@item milter_action
@end table
Use the @code{milter_state_name} function to obtain the corresponding
textual string (@pxref{Informative Functions, milter_state_name}).
@end deftypevr
@anchor{milter_server_id}
@deftypevr {Predefined Variable} string milter_server_id
Identifier of the milter server which executes the code. This is the
string passed to the @code{id} statement in the @code{server} section
of the configuration file (@pxref{conf-server}),
@end deftypevr
@deftypevr {Predefined Variable} string milter_server_address
Address of the socket the milter server is listening to. This is
defined by the @code{listen} statement in the @code{server} section
of the configuration file (@pxref{conf-server}),
@end deftypevr
@deftypevr {Predefined Variable} number milter_server_family
Address family of the milter server address, as defined by the
@code{listen} statement in the @code{server} section of the
configuration file (@pxref{conf-server}).
See the @code{FAMILY_} constants in @ref{socket-families}.
@end deftypevr
@deftypevr {Predefined Variable} string milter_client_address
Address of the milter client which initiated the connection.
@end deftypevr
@deftypevr {Predefined Variable} number milter_client_family
Address family of @code{milter_client_address}.
See the @code{FAMILY_} constants in @ref{socket-families}.
@end deftypevr
@deftypevr {Predefined Variable} number cache_used
This variable is set by @code{stdpoll} and @code{strictpoll} built-ins
(and, consequently, by the @code{on poll} statement). Its value is
@samp{1} if the function used the cached data instead of directly
polling the host, and @samp{0} if the polling took place.
@xref{SMTP Callout functions}.
@anchor{cache_used example}
@cindex @code{cache_used} variable, usage example
@cindex reject messages, marking cached rejects
You can use this variable to make your reject message more informative
for the remote party. The common paradigm is to define a function,
returning empty string if the result was obtained from polling, or
some notice if cached data were used, and to use the function in the
@code{reject} text, for example:
@example
@group
func cachestr() returns string
do
if cache_used
return "[CACHED] "
else
return ""
fi
done
@end group
@end example
@noindent
Then, in @code{prog envfrom} one can use:
@example
@group
on poll $f
do
when not_found or failure:
reject 550 5.1.0 cachestr() . "Sender validity not confirmed"
done
@end group
@end example
@end deftypevr
@deftypevr {Predefined Variable} number clamav_stream_max_length
@cindex @code{StreamMaxLength}, @file{clamd.conf} parameter.
@cindex clamd.conf
Maximum size of a message chunk, used when sending message to
@command{ClamAV} daemon. Must be less than or equal to the value of
@code{StreamMaxLength} parameter in @file{clamd.conf}.
Default value is 4096.
@xref{ClamAV}.
@end deftypevr
@deftypevr {Predefined Variable} string clamav_virus_name
Name of virus identified by @command{ClamAV}. Set by @code{clamav}
function (@pxref{ClamAV}).
@end deftypevr
@deftypevr {Predefined Variable} number greylist_seconds_left
Number of seconds left to the end of greylisting period. Set by
@code{greylist} and @code{is_greylisted} functions (@pxref{Special test
functions}).
@end deftypevr
@anchor{ehlo_domain}
@deftypevr {Predefined Variable} string ehlo_domain
Name of the domain used by polling functions in @acronym{SMTP}
@code{EHLO} or @code{HELO} command. Default value is the fully
qualified domain name of the host where @command{mailfromd} is run.
@xref{Polling}.
@end deftypevr
@deftypevr {Predefined Variable} string last_poll_greeting
Callout functions (@pxref{SMTP Callout functions}) set this variable before
returning. It contains the initial SMTP reply from the last polled
host.
@end deftypevr
@deftypevr {Predefined Variable} string last_poll_helo
Callout functions (@pxref{SMTP Callout functions}) set this variable before
returning. It contains the reply to the @code{HELO} (@code{EHLO})
command, received from the last polled host.
@end deftypevr
@deftypevr {Predefined Variable} string last_poll_host
Callout functions (@pxref{SMTP Callout functions}) set this variable before
returning. It contains the host name or @acronym{IP} address of the
last polled host.
@end deftypevr
@deftypevr {Predefined Variable} string last_poll_recv
Callout functions (@pxref{SMTP Callout functions}) set this variable before
returning. It contains the last @acronym{SMTP} reply received from
the remote host. In case of multi-line replies, only the first line is
stored. If nothing was received the variable contains the string
@samp{nothing}.
@end deftypevr
@deftypevr {Predefined Variable} string last_poll_sent
Callout functions (@pxref{SMTP Callout functions}) set this variable before
returning. It contains the last @acronym{SMTP} command sent to the
polled host. If nothing was sent, @code{last_poll_sent} contains the string
@samp{nothing}.
@end deftypevr
@anchor{mailfrom_address}
@deftypevr {Predefined Variable} string mailfrom_address
Email address used by polling functions in @acronym{SMTP} @code{MAIL
FROM} command (@pxref{Polling}.). Default is @samp{<>}. Here is an
example of how to change it:
@example
set mailfrom_address "postmaster@@my.domain.com"
@end example
@cindex multiple sender addresses
You can set this value to a comma-separated list of email addresses,
in which case the probing will try each address until either the
remote party accepts it or the list of addresses is exhausted,
whichever happens first.
It is not necessary to enclose emails in angle brackets, as they
will be added automatically where appropriate. The only exception is
null return address, when used in a list of addresses. In this case,
it should always be written as @samp{<>}. For example:
@example
set mailfrom_address "postmaster@@my.domain.com, <>"
@end example
@end deftypevr
@deftypevr {Predefined Variable} number sa_code
Spam score for the message, set by @code{sa} function (@pxref{sa}).
@end deftypevr
@deftypevr {Predefined Variable} number rcpt_count
The variable @code{rcpt_count} keeps the number of recipients given so
far by @code{RCPT TO} commands. It is defined only in @samp{envrcpt}
handlers.
@end deftypevr
@deftypevr {Predefined Variable} number sa_threshold
Spam threshold, set by @code{sa} function (@pxref{sa}).
@end deftypevr
@deftypevr {Predefined Variable} string sa_keywords
Spam keywords for the message, set by @code{sa} function (@pxref{sa}).
@end deftypevr
@deftypevr {Predefined Variable} number safedb_verbose
This variable controls the verbosity of the exception-safe database
functions. @xref{safedb_verbose}.
@end deftypevr
@node Back references
@section Back references
@cindex back references, in program text
A @dfn{back reference} is a sequence @samp{\@var{d}}, where @var{d}
is a decimal number. It refers to the @var{d}th parenthesized
subexpression in the last @command{matches} statement@footnote{The
subexpressions are numbered by the positions of their opening
parentheses, left to right.}. Any back reference occurring within a
double-quoted string is replaced with the value of the corresponding
subexpression. For example:
@example
@group
if $f matches '.*@@\(.*\)\.gnu\.org\.ua'
set host \1
fi
@end group
@end example
If the value of @code{f} macro is @samp{smith@@unza.gnu.org.ua}, the
above code will assign the string @samp{unza} to the variable
@code{host}.
Notice, that each occurrence of @code{matches} will reset the table
of back references, so try to use them as early as possible. The
following example illustrates a common error, when the back
reference is used after the reference table has been reused by another
matching:
@example
@group
# @r{Wrong!}
if $f matches '.*@@\(.*\)\.gnu\.org\.ua'
if $f matches 'some.*'
set host \1
fi
fi
@end group
@end example
This will produce the following run time error:
@example
mailfromd: RUNTIME ERROR near file.mfl:3: Invalid back-reference number
@end example
@noindent
because the inner match (@samp{some.*}) does not have any parenthesized
subexpressions.
@xref{Special comparisons}, for more information about @code{matches}
operator.
@node Handlers
@section Handlers
@cindex milter stage handler, defined
@cindex stage handler, defined
@cindex handler, defined
@kwindex prog
@dfn{Milter stage handler} (or @dfn{handler}, for short) is a
subroutine responsible for processing a particular milter state.
There are eight handlers available. Their order of invocation and
arguments are described in @ref{milter-control-flow}.
A handler is defined using the following construct:
@example
@group
prog @var{handler-name}
do
@var{handler-body}
done
@end group
@end example
@noindent
where @var{handler-name} is the name of the handler (@pxref{handler
names}), @var{handler-body} is the list of filter statements composing
the handler body. Some handlers take arguments, which can be accessed
within the @var{handler-body} using the notation @var{$@var{n}},
where @var{n} is the ordinal number of the argument. Here we describe
the available handlers and their arguments:
@anchor{connect handler}
@deffn {Handler} connect (string $1, number $2, number $3, string $4)
@table @b
@item Invocation:
This handler is called once at the beginning of each @acronym{SMTP} connection.
@item Arguments:
@enumerate 1
@item @code{string};
@anchor{hostname in connect handler}
The host name of the message sender, as reported by @acronym{MTA}. Usually it
is determined by a reverse lookup on the host address. If the reverse
lookup fails, @samp{$1} will contain the message sender's @acronym{IP} address
enclosed in square brackets (e.g.@: @samp{[127.0.0.1]}).
@item @code{number};
Socket address family. You need to require the @samp{status} module
to get symbolic definitions for the address families. Supported
families are:
@cindex FAMILY_STDIO
@cindex FAMILY_UNIX
@cindex FAMILY_INET
@cindex FAMILY_INET6
@float Table, socket-families
@caption{Supported socket families}
@multitable @columnfractions 0.25 .10 0.45
@headitem Constant @tab Value @tab Meaning
@item FAMILY_STDIO @tab 0 @tab Standard input/output (the @acronym{MTA} is
run with @option{-bs} option)
@item FAMILY_UNIX @tab 1 @tab @acronym{UNIX} socket
@item FAMILY_INET @tab 2 @tab @acronym{IP}v4 protocol
@item FAMILY_INET6 @tab 3 @tab @acronym{IP}v6 protocol
@end multitable
@end float
@item @code{number};
Port number if @samp{$2} is @samp{FAMILY_INET}.
@item @code{string};
Remote @acronym{IP} address if @samp{$2} is @samp{FAMILY_INET} or full file name
of the socket if @samp{$2} is @samp{FAMILY_UNIX}. If @samp{$2} is
@samp{FAMILY_STDIO}, @samp{$4} is an empty string.
@end enumerate
@end table
@cindex actions, using in @code{connect} handler
The actions (@pxref{Actions}) appearing in this handler
are handled by Sendmail in a special way. First of all, any textual
message is ignored. Secondly, the only action that immediately closes
the connection is @code{tempfail 421}. Any other reply codes result in
Sendmail switching to @dfn{nullserver} mode, where it accepts any
commands, but answers with a failure to any of them, except for the
following: @code{QUIT}, @code{HELO}, @code{NOOP}, which are processed
as usual.
The following table summarizes the Sendmail behavior depending on
the action used:
@table @code
@item tempfail 421 @var{excode} @var{message}
The caller is returned the following error message:
@example
421 4.7.0 @var{hostname} closing connection
@end example
@noindent
Both @var{excode} and @var{message} are ignored.
@item tempfail 4@var{xx} @var{excode} @var{message}
(where @var{xx} represents any digits, except @samp{21})
Both @var{excode} and @var{message} are ignored. Sendmail switches
to nullserver mode. Any subsequent command, excepting the ones listed above,
is answered with
@example
454 4.3.0 Please try again later
@end example
@item reject 5@var{xx} @var{excode} @var{message}
(where @var{xx} represents any digits). All arguments are
ignored. Sendmail switches to nullserver mode. Any subsequent
command, excepting ones listed above, is answered with
@example
550 5.0.0 Command rejected
@end example
@end table
Regarding reply codes, this behavior complies with @acronym{RFC}
2821 (section 3.9), which states:
@quotation
An @acronym{SMTP} server @emph{must not} intentionally close the connection except:@*
[@dots{}]@*
- After detecting the need to shut down the @acronym{SMTP} service and
returning a 421 response code. This response code can be issued
after the server receives any command or, if necessary,
asynchronously from command receipt (on the assumption that the
client will receive it after the next command is issued).
@end quotation
However, the @acronym{RFC} says nothing about textual messages and
extended error codes, therefore Sendmail's ignoring of these is,
in my opinion, absurd. My practice shows that it is often reasonable,
and even necessary, to return a meaningful textual message if the
initial connection is declined. The opinion of @command{mailfromd}
users seems to support this view. Bearing this in mind,
@command{mailfromd} is shipped with a patch for Sendmail,
which makes it honor both extended return code and textual message given
with the action. Two versions are provided:
@file{etc/@/sendmail-8.13.7.connect.diff}, for
Sendmail versions 8.13.x, and
@file{etc/@/sendmail-8.14.3.connect.diff}, for Sendmail versions 8.14.3.
@end deffn
@deffn {Handler} helo (string $1)
@table @b
@item Invocation:
This handler is called whenever the @acronym{SMTP} client sends @code{HELO} or
@code{EHLO} command. Depending on the actual @acronym{MTA} configuration, it
can be called several times or even not at all.
@item Arguments:
@enumerate 1
@item @code{string}; Argument to @code{HELO} (@code{EHLO}) commands.
@end enumerate
@item Notes:
According to @acronym{RFC} 28221, @code{$1} must be domain name of the
sending host, or, in case this is not available, its @acronym{IP} address
enclosed in square brackets. Be careful when taking decisions based
on this value, because in practice many hosts send arbitrary strings.
We recommend to use @code{heloarg_test} function
(@pxref{heloarg_test}) if you wish to analyze this value.
@end table
@end deffn
@deffn {Handler} envfrom (string $1, string $2)
@table @b
@item Invocation:
Called when the @acronym{SMTP} client sends @code{MAIL FROM} command, i.e. once
at the beginning of each message.
@item Arguments:
@enumerate 1
@item @code{string}; First argument to the @code{MAIL FROM} command,
i.e. the email address of the sender.
@item @code{string}; Rest of arguments to @code{MAIL FROM} separated
by space character. This argument can be @samp{""}.
@end enumerate
@item Notes
@enumerate 1
@item @code{$1} is not the same as @code{$f} Sendmail variable, because
the latter contains the sender email after address rewriting and
normalization, while @code{$1} contains exactly the value given by
sending party.
@item When the array type is implemented, @code{$2} will contain
an array of arguments.
@end enumerate
@end table
@end deffn
@deffn {Handler} envrcpt (string $1, string $2)
@table @b
@item Invocation:
Called once for each @code{RCPT TO} command, i.e. once for each
recipient, immediately after @code{envfrom}.
@item Arguments:
@enumerate 1
@item @code{string}; First argument to the @code{RCPT TO} command,
i.e. the email address of the recipient.
@item @code{string}; Rest of arguments to @code{RCPT TO} separated
by space character. This argument can be @samp{""}.
@end enumerate
@item Notes:
When the array type is implemented, @code{$2} will contain
an array of arguments.
@end table
@end deffn
@deffn {Handler} data ()
@table @b
@item Invocation:
Called after the @acronym{MTA} receives @acronym{SMTP} @samp{DATA}
command. Notice that this handler is not supported by Sendmail
versions prior to 8.14.0 and Postfix versions prior to 2.5.
@item Arguments:
None
@end table
@end deffn
@deffn {Handler} header (string $1, string $2)
@table @b
@item Invocation:
Called once for each header line received after @acronym{SMTP} @code{DATA} command.
@item Arguments:
@enumerate 1
@item @code{string}; Header field name.
@item @code{string}; Header field value. The content of the header may
include folded white space, i.e., multiple lines with following white
space where lines are separated by @sc{lf} (@acronym{ASCII} 10). The
trailing line terminator (@sc{cr/lf}) is removed.
@end enumerate
@end table
@end deffn
@deffn {Handler} eoh
@table @b
@item Invocation:
This handler is called once per message, after all headers have been
sent and processed.
@item Arguments:
None.
@end table
@end deffn
@anchor{body handler}
@deffn {Handler} body (pointer $1, number $2)
@table @b
@item Invocation:
This header is called zero or more times, for each piece of the
message body obtained from the remote host.
@item Arguments:
@enumerate 1
@item @code{pointer}; Piece of body text. See @samp{Notes} below.
@item @code{number}; Length of data pointed to by @code{$1}, in bytes.
@end enumerate
@item Notes:
The first argument points to the body chunk. Its size may be quite
considerable and passing it as a string may be costly both in terms of
memory and execution time. For this reason it is not passed as a
string, but rather as a @dfn{generic pointer}, i.e. an object having
the same size as @code{number}, which can be used to retrieve the
actual contents of the body chunk if the need arises.
A special function @code{body_string} is provided to convert this
object to a regular @acronym{MFL} string (@pxref{Mail body
functions}). Using it you can collect the entire body text into a
single global variable, as illustrated by the following example:
@example
string text
prog body
do
set text text . body_string($1,$2)
done
@end example
@end table
@end deffn
The text collected this way can then be used in the @code{eom} handler
(see below) to parse and analyze it.
If you wish to analyze both the headers and mail body, the following
code fragment will do that for you:
@example
string text
# @r{Collect all headers.}
prog header
do
set text text . $1 . ": " . $2 . "\n"
done
# @r{Append terminating newline to the headers.}
prog eoh
do
set text "%text\n"
done
# @r{Collect message body.}
prog body
do
set text text . body_string($1, $2)
done
@end example
@anchor{eom handler}
@deffn {Handler} eom
@table @b
@item Invocation:
This handler is called once per message, when the terminating dot
after @code{DATA} command has been received.
@item Arguments:
None
@item Notes:
This handler is useful for calling @dfn{message capturing} functions,
such as @code{sa} or @code{clamav}. For more information about these,
refer to @ref{Interfaces to Third-Party Programs}.
@end table
@end deffn
For your reference, the following table shows each handler with its arguments:
@cindex milter stage handler arguments
@cindex stage handler arguments
@cindex handler arguments
@float Table, handler-arguments
@caption{State Handler Arguments}
@multitable @columnfractions 0.20 0.20 0.20 0.20 0.20
@headitem Handler @tab $1 @tab $2 @tab $3 @tab $4
@item connect @tab Hostname @tab Socket Family @tab Port @tab Remote address
@item helo @tab @code{HELO} domain @tab @acronym{N/A} @tab @acronym{N/A} @tab @acronym{N/A}
@item envfrom @tab Sender email address @tab Rest of arguments @tab @acronym{N/A} @tab @acronym{N/A}
@item envrcpt @tab Recipient email address @tab Rest of arguments @tab @acronym{N/A} @tab @acronym{N/A}
@item header @tab Header name @tab Header value @tab @acronym{N/A} @tab @acronym{N/A}
@item eoh @tab @acronym{N/A} @tab @acronym{N/A} @tab @acronym{N/A} @tab @acronym{N/A}
@item body @tab Body segment (pointer) @tab Length of the segment
(numeric) @tab @acronym{N/A} @tab @acronym{N/A}
@item eom @tab @acronym{N/A} @tab @acronym{N/A} @tab @acronym{N/A} @tab @acronym{N/A}
@end multitable
@end float
@node Multiple handler definitions
@subsection Multiple Handler Definitions
Any handler may be declared multiple times. When compiling the filter
program, @command{mailfromd} combines the code from all @code{prog}
declarations having the same handler name into one code block and
compiles it. The resulting code is guaranteed to be executed in the
order in which it appears in the source files.
@node Special handlers
@section Initialization and Cleanup Handlers
Apart from the milter handlers described in the previous section,
@acronym{MFL} provides several @dfn{special handlers}, that serve
as @dfn{hooks}, allowing the programmer to insert code in certain
important points of the control flow.
Syntactically, special handlers are similar to milter state handlers,
i.e. they are defined as:
@example
@group
prog @var{handler}
do
...
done
@end group
@end example
@noindent
(@var{handler} being the handler name).
Special handlers can be subdivided into three groups.
The first group are @code{begin} and @code{end} handlers. These
are run at the beginning and before the end of each SMTP session and
are used to provide a session-specific initialization and cleanup
routines.
The second group are @code{startup} and @code{shutdown} handlers,
which provide global initialization and cleanup routines. These
handlers are invoked exactly once: @code{startup} when
@command{mailfromd} has started up, but hasn't yet begun to serve
milter requests, and @code{shutdown} when @command{mailfromd} is about
to terminate.
Finally, the @code{action} handler is run before executing each
reply action (@pxref{reply actions}).
@menu
* begin/end:: Session @samp{begin} and @samp{end} special handlers.
* startup/shutdown:: Global startup and shutdown handlers.
* action hook:: Action hook handler.
@end menu
@node begin/end
@subsection The @samp{begin} and @samp{end} special handlers
@cindex begin, special handler
@cindex end, special handler
@cindex session startup handler
@cindex handler, session startup
@cindex handler, session initialization
@cindex session cleanup handler
@cindex handler, session cleanup
These two special handlers are executed once for each session,
marking its beginning and end. Neither of them takes any arguments:
@deffn {Handler} begin
@example
# @r{Begin handler}
prog begin
do
@dots{}
done
@end example
The @code{begin} handler is run once for each @acronym{SMTP} session,
after the connection has been established but before the first milter
handler has been called.
@end deffn
@deffn {Handler} end
@example
# @r{End handler}
prog end
do
@dots{}
done
@end example
The @code{end} handler is run once for each @acronym{SMTP} session,
after all other handlers have finished their work and
@command{mailfromd} has already returned the resulting status to the
MTA and closed connection.
@end deffn
@anchor{multiple special handlers}
@FIXME{}
Multiple @samp{begin} and @samp{end} handlers are a useful feature
for writing modules (@pxref{Modules}), because each module can thus
have its own initialization and cleanup blocks. Notice, however, that
in this case the order in which subsequent @samp{begin} and @samp{end}
blocks are executed is not defined. It is only warranted that all
@samp{begin} blocks are executed at startup and all @samp{end} blocks
are executed at shutdown. It is also warranted that all @samp{begin}
and @samp{end} blocks defined within a compilation unit (i.e. a single
abstract source file, with all @code{#include} and
@code{#include_once} statements expanded in place) are executed in
order of their appearance in the unit.
@anchor{begin/end restrictions}
@cindex @samp{begin}, handler restrictions
@cindex @samp{end}, handler restrictions
Due to their special nature, the startup and cleanup blocks impose
certain restrictions on the statements that can be used within them:
@enumerate 1
@cindex @code{return} in @samp{begin}
@cindex @samp{begin} and @code{return}
@cindex @code{return} in @samp{end}
@cindex @samp{end} and @code{return}
@item @code{return} cannot be used in @samp{begin} and @samp{end}
handlers.
@cindex @code{accept} in @samp{begin}
@cindex @samp{begin} and @code{accept}
@cindex @code{accept} in @samp{end}
@cindex @samp{end} and @code{accept}
@cindex @code{continue} in @samp{begin}
@cindex @samp{begin} and @code{continue}
@cindex @code{continue} in @samp{end}
@cindex @samp{end} and @code{continue}
@cindex @code{discard} in @samp{begin}
@cindex @samp{begin} and @code{discard}
@cindex @code{discard} in @samp{end}
@cindex @samp{end} and @code{discard}
@cindex @code{reject} in @samp{begin}
@cindex @samp{begin} and @code{reject}
@cindex @code{reject} in @samp{end}
@cindex @samp{end} and @code{reject}
@cindex @code{tempfail} in @samp{begin}
@cindex @samp{begin} and @code{tempfail}
@cindex @code{tempfail} in @samp{end}
@cindex @samp{end} and @code{tempfail}
@item The following Sendmail actions cannot be used in them:
@code{accept}, @code{continue}, @code{discard}, @code{reject},
@code{tempfail}. They can, however, be used in @code{catch}
statements, declared in @samp{begin} blocks (see example below).
@cindex @code{add} in @samp{begin}
@cindex @samp{begin} and @code{add}
@cindex @code{add} in @samp{end}
@cindex @samp{end} and @code{add}
@cindex @code{replace} in @samp{begin}
@cindex @samp{begin} and @code{replace}
@cindex @code{replace} in @samp{end}
@cindex @samp{end} and @code{replace}
@cindex @code{delete} in @samp{begin}
@cindex @samp{begin} and @code{delete}
@cindex @code{delete} in @samp{end}
@cindex @samp{end} and @code{delete}
@item Header manipulation actions (@pxref{header manipulation}) cannot be
used in @samp{end} handler.
@end enumerate
The @samp{begin} handlers are the usual place to put global
initialization code to. For example, if you do not want to use
@acronym{DNS} caching, you can do it this way:
@example
@group
prog begin
do
db_set_active("dns", 0)
done
@end group
@end example
Additionally, you can set up global exception handling routines
there. For example, the following @samp{begin} statement installs a
handler for all exceptions not handled otherwise that logs the
exception along with the stack trace and continues processing the
message:
@example
@group
prog begin
do
catch *
do
echo "Caught exception $1: $2"
stack_trace()
continue
done
done
@end group
@end example
@node startup/shutdown
@subsection Global startup and shutdown handlers.
@cindex startup, special handler
@cindex shutdown, special handler
@cindex global startup handler
@cindex handler, global startup
@cindex global cleanup handler
@cindex handler, global cleanup
Yet another pair of special handlers, @code{startup} and
@code{shutdown}, can be used for global initialization and cleanup.
@deffn {Handler} startup
The @code{startup} handler is called exactly once, as a part of
@code{mailfromd} startup session.
This handler is normally used in @dfn{mfmod} interface modules to
load the shared library part (@pxref{mfmod}).
@end deffn
@deffn {Handler} shutdown
This handler is called during the normal program shutdown sequence,
before exiting.
@end deffn
Both handlers bear certain similarity to @code{begin} and @code{end}:
they take no arguments, and their use is subject to the same
restrictions (@pxref{begin/end restrictions}). Additionally,
the following features cannot be used in global handlers:
@enumerate 1
@item Sendmail macros.
@item Message modification functions.
@end enumerate
@node action hook
@subsection Action Hook
@cindex action hook
Action hook handler is run implicitly before executing each reply
action, such as @code{accept}, @code{reject}, etc. @xref{reply
actions}, for a discussion of reply action statements.
Upon invocation, the handler is passed six arguments:
@enumerate 1
@item Identifier of the action which is about to be performed.
Each action is assigned a numeric identifier. The @code{status}
module defines the following symbolic names for action identifiers:
@anchor{action identifiers}
@table @code
@kwindex ACCEPT_ACTION
@item ACCEPT_ACTION
@kwindex CONTINUE_ACTION
@item CONTINUE_ACTION
@kwindex DISCARD_ACTION
@item DISCARD_ACTION
@kwindex REJECT_ACTION
@item REJECT_ACTION
@kwindex TEMPFAIL_ACTION
@item TEMPFAIL_ACTION
@end table
To convert these to textual action names, use the
@code{milter_action_name} function (@pxref{Informative Functions,
milter_action_name}).
@item SMTP code of the reply
@item Extended reply code
@item Textual message passed to the action.
@item File name of the source location where the action was called.
@item Line number of source location where the action was called.
@end enumerate
Last three arguments are meaningful only for @code{reject} and
@code{tempfail} actions. For the remaining three actions
(@code{accept}, @code{discard}, and @code{continue}), empty strings
are passed.
Action hook handlers are useful mainly for logging and accounting
purposes. For example, the code fragment below assumes that the
@command{openmetrics} module is used (@pxref{Top,
mfmod_openmetrics,, mfmod_openmetrics, mfmod_openmetrics reference}).
It increases the corresponding metrics before taking the action.
Additionally, for @code{reject} and @code{tempfail} actions, the
metrics @samp{reject_@var{code}} and @samp{tempfail_@var{code}} are
increased, where @var{code} is the three-digit SMTP status code being
sent to the server.
@example
@group
prog action
do
openmetrics_incr(milter_action_name($1))
switch $1
do
case REJECT_ACTION:
openmetrics_incr("reject_" . $2)
case TEMPFAIL_ACTION:
openmetrics_incr("tempfail_" . $2)
done
done
@end group
@end example
@node Functions
@section Functions
A @dfn{function} is a named @command{mailfromd} subroutine, which
takes zero or more @dfn{parameters} and optionally returns a certain
value. Depending on the return value, functions can be
subdivided into @dfn{string functions} and @dfn{number functions}.
A function may have @dfn{mandatory} and @dfn{optional parameters}.
When invoked, the function must be supplied exactly as many
@dfn{actual arguments} as the number of its mandatory parameters.
Functions are invoked using the following syntax:
@example
@var{name} (@var{args})
@end example
@noindent
where @var{name} is the function name and @var{args} is a
comma-separated list of expressions. For example, the following are valid
function calls:
@example
@group
foo(10)
interval("1 hour")
greylist("/var/my.db", 180)
@end group
@end example
The number of parameters a function takes and their data types
compose the @dfn{function signature}. When actual arguments are
passed to the function, they are converted to types of the
corresponding formal parameters.
There are two major groups of functions: @dfn{built-in} functions,
that are implemented in the @command{mailfromd} binary, and
@dfn{user-defined} functions, that are written in @acronym{MFL}. The
invocation syntax is the same for both groups.
@command{Mailfromd} is shipped with a rich set of @dfn{library
functions}. These are described in @ref{Library}. In addition to
these you can define your own functions.
@anchor{User-defined}
@cindex function definition, syntax of
Function definitions can appear anywhere between the handler
declarations in a filter program, the only requirement being that the
function definition occur before the place where the function is
invoked.
The syntax of a function definition is:
@cindex @code{returns} statement, function definition
@cindex @code{func} statement, function definition
@example
@group
[@var{qualifier}] func @var{name} (@var{param-decl})
[returns @var{data-type}]
do
@var{function-body}
done
@end group
@end example
@noindent
where @var{name} is the name of the function to define, @var{param-decl} is
a comma-separated list of parameter declarations. The syntax of the
latter is the same as that of variable declarations (@pxref{Variables,
Variable declarations}), i.e.:
@example
@var{type} @var{name}
@end example
@noindent
declares the parameter @var{name} having the type @var{type}. The
@var{type} is @code{string} or @code{number}.
@cindex qualifier, function declaration
@kwindex public
@kwindex static
@cindex scope of visibility, functions
@anchor{function scope of visibility}
Optional @var{qualifier} declares the scope of visibility for that
function (@pxref{scope of visibility}). It is similar to that of
variables, except that functions cannot be local (i.e. you cannot
declare function within another function).
The @code{public} qualifier declares a function that may be referred
to from any module, whereas the @code{static} qualifier declares a
function that may be called only from the current module
(@pxref{Modules}). The default scope is @samp{public},
unless specified otherwise in the module declaration (@pxref{module
structure}).
For example, the following declares a function @samp{sum}, that takes
two numeric arguments and returns a numeric value:
@example
func sum(number x, number y) returns number
@end example
Similarly, the following is a declaration of a static function:
@example
static func sum(number x, number y) returns number
@end example
Parameters are referenced in the @var{function-body} by their name,
the same way as other variables. Similarly, the value of a parameter can be
altered using @code{set} statement.
@cindex optional arguments to a function
@cindex arguments, optional
A function can be declared to take a certain number of @dfn{optional
arguments}. In a function declaration, optional abstract arguments
must be placed after the mandatory ones, and must be separated from
them with a semicolon. The following example is a definition of
function @code{foo}, which takes two mandatory and two optional
arguments:
@example
func foo(string msg, string email; number x, string pfx)
@end example
@noindent
@kwindex $#
@cindex function arguments, getting the number of
@cindex function arguments, counting
@cindex number of actual arguments
@cindex @@@var{var}, special construct
@cindex argument number in the list of arguments
Mandatory parameters are: @code{msg} and @code{email}. Optional
parameters are: @code{x} and @code{pfx}. The actual number of
arguments supplied to the function is returned by a special construct
@code{$#}. In addition, the special construct @code{@@@var{arg}}
evaluates to the ordinal number of variable @var{arg} in the list of
formal parameters (the first argument has number @samp{0}). These two
constructs can be used to verify whether an argument is supplied to
the function.
@cindex optional arguments, checking if supplied
When an actual argument for parameter @code{n} is supplied, the number
of actual arguments (@code{$#}) is greater than the ordinal number
of that parameter in the declaration list (@code{@@@var{n}}). Thus,
the following construct can be used to check if an optional argument
@var{arg} is actually supplied:
@example
func foo(string msg, string email; number x, string arg)
do
if $# > @@arg
@dots{}
fi
@end example
The default @command{mailfromd} installation provides a special
macro for this purpose: @pxref{defined}. Using it, the example above
could be rewritten as:
@example
func foo(string msg, string email; number x, string arg)
do
if defined(arg)
@dots{}
fi
@end example
Within a function body, optional arguments are referenced
exactly the same way as the mandatory ones. Attempt to dereference an
optional argument for which no actual parameter was supplied, results
in an undefined value, so be sure to check whether a parameter is
passed before dereferencing it.
@anchor{variadic functions}
@cindex variadic function
@cindex variable number of arguments
A function can also take variable number of arguments (such
functions are called @dfn{variadic}). This is
indicated by ellipsis in place of the last abstract parameter name. The
statement below defines a function @code{foo} taking one mandatory, one
optional and any number of additional arguments:
@example
func foo (string a ; string b, string ...)
@end example
The data type before the ellipsis indicates the type to promote all
actual arguments to. If it is omitted, @code{string} is assumed, so
the above declaration can also be written as:
@example
func foo (string a ; string b, ...)
@end example
@cindex $(@var{n})
To refer to the actual arguments in the function body, the following
construct is used:
@example
$(@var{expr})
@end example
@noindent
where @var{expr} is any valid @acronym{MFL} expression, evaluating to
a number @var{n}. This construct refers to the value of @var{n}th
actual parameter from the variable argument list. Parameters are
numbered from @samp{1}, so the first variable parameter is @code{$(1)},
and the last one is @code{$($# - @var{Nm} - @var{No})}, where @var{Nm}
and @var{No} are numbers of mandatory and optional parameters to the
function.
@cindex $@var{n}
The construct @samp{$(@var{n})} where 1 <= @var{n} <= 9 can also be
written as @samp{$@var{n}}.
For example, the function below prints all its arguments:
@example
func pargs (string text, ...)
do
echo "text=%text"
loop for number i 1,
while i < $# - @@text,
set i i + 1
do
echo "arg %i=" . $(i)
done
done
@end example
@noindent
Note how the ordinal number operator is used to compute the upper
limit.
As another example, the function below computes the sum of its
arguments.
@example
@group
func sum(number ...)
do
number s 0
loop for number i 1,
while i <= $#,
set i i + 1
do
set s s + $(i)
done
return s
done
@end group
@end example
@cindex passing variable arguments on to another function
@kwindex $@@
Sometimes it is necessary to pass all variable arguments passed to a
variadic function on to another variadic function. To do so, use the
@code{$@@} operator. For example:
@example
@group
func y(string x, number ...)
do
echo "x is " . sum($@@)
done
@end group
@end example
Suppose @code{y} is called as @code{y("test", 1, 3, 5)}. Then, it
will call @code{sum} as: @code{sum(1, 3, 5)}.
The @code{$@@} can be used with a numeric argument, which indicates
number of arguments to remove from the resulted argument list. This
is similar to @code{shift} statement in other languages. Thus, if
@code{y} in the above example were written as:
@example
@group
func y(string x, ...)
do
x($@@(2))
done
@end group
@end example
then @code{y("test", "a", "b", "c")}, it will call @code{x} as
follows: @code{x("c")}.
Notice the following important points. First, @code{$@@} can be
used only as the last argument in the argument list. Secondly, it
cannot be used to pass mandatory and optional arguments to a function.
In other words, arguments passed via @code{$@@} must correspond to
ellipsis in the function declaration. Finally, passing shift count
greater than the actual number of variable arguments results in a
runtime error.
@cindex return statement, defined
The @var{function-body} is any list of valid @command{mailfromd}
statements. In addition to the statements discussed below
(@pxref{Statements}) it can also contain the @code{return} statement,
which is used to return a value from the function. The syntax of the
return statement is
@example
return @var{value}
@end example
@anchor{sum--example}
As an example of this, consider the following code snippet that
defines the function @samp{sum} to return a sum of its two arguments:
@example
@group
func sum(number x, number y) returns number
do
return x + y
done
@end group
@end example
@cindex procedures
@cindex function returning void
@cindex void functions
The @code{returns} part in the function declaration is optional. A
declaration lacking it defines a @dfn{procedure}, or @dfn{void
function}, i.e. a function that is not supposed to return any value.
Such functions cannot be used in expressions, instead they are
used as statements (@pxref{Statements}). The following example
shows a function that emits a customized temporary failure notice:
@example
@group
func stdtf()
do
tempfail 451 4.3.5 "Try again later"
done
@end group
@end example
@anchor{function alias}
@cindex aliases
@kwindex alias
A function may have several names. An alternative name (or
@dfn{alias}) can be assigned to a function by using @code{alias}
keyword, placed after @var{param-decl} part, for example:
@example
func foo()
alias bar
returns string
do
@dots{}
done
@end example
After this declaration, both @code{foo()} and @code{bar()} will refer
to the same function.
The number of function aliases is unlimited. The following fragment
declares a function having three names:
@example
func foo()
alias bar
alias baz
returns string
do
@dots{}
done
@end example
Although this feature is rarely needed, there are sometimes cases when
it may be necessary.
@cindex automatic variables
@cindex variables, local
@cindex local variables
@cindex variables, automatic
A variable declared within a function becomes a local variable to
this function. Its lexical scope ends with the terminating
@code{done} statement.
Parameters, local variables and global variables are using
separate namespaces, so a parameter name can coincide with the name of
a global, in which case a parameter is said to @dfn{shadow} the
global. All references to its name will refer to the parameter,
until the end of its scope is reached, where the global one
becomes visible again. Consider the following example:
@example
@group
number x
func foo(string x)
do
echo "foo: %x"
done
prog envfrom
do
set x "Global"
foo("Local")
echo x
done
@end group
@end example
@noindent
Running @command{mailfromd --test} with this configuration will
display:
@example
@cartouche
foo: Local
Global
@end cartouche
@end example
@menu
* Some Useful Functions::
@end menu
@node Some Useful Functions
@subsection Some Useful Functions
To illustrate the concept of user-defined functions, this subsection
shows the definitions of some of the library functions shipped with
@command{mailfromd}@footnote{Notice that these are intended for
educational purposes and do not necessarily coincide with the actual
definitions of these functions in Mailfromd version @value{VERSION}.}.
These functions are contained in modules installed along with the
@command{mailfromd} binary. To use any of them in your code, require
the appropriate module as described in @ref{import}, e.g. to use the
@code{revip} function, do @code{require 'revip'}.
Functions and their definitions:
@enumerate 1
@cindex revip, definition of
@item @code{revip}@*
The function @code{revip}, that was used in releases of
@command{mailfromd} up to 9.0 (@pxref{revip}) was implemented as follows:
@example
func revip(string ip) returns string
do
return inet_ntoa(ntohl(inet_aton(ip)))
done
@end example
Previously it was implemented using regular expressions. Below we include
this variant as well, as an illustration for the use of regular expressions:
@example
@group
#pragma regex push +extended
func revip(string ip) returns string
do
if ip matches '([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)'
return "\4.\3.\2.\1"
fi
return ip
done
#pragma regex pop
@end group
@end example
@cindex strip_domain_part, definition of
@item @code{strip_domain_part}@*
This function returns at most @var{n} last components of the domain
name @var{domain} (@pxref{strip_domain_part}).
@example
@group
#pragma regex push +extended
func strip_domain_part(string domain, number n) returns string
do
if n > 0 and
domain matches '.*((\.[^.]+)@{' . $2 . '@})'
return substring(\1, 1, -1)
else
return domain
fi
done
#pragma regex pop
@end group
@end example
@cindex valid_domain, definition
@item @code{valid_domain}@*
@xref{valid_domain}, for a description of this function. Its
definition follows:
@example
@group
require dns
func valid_domain(string domain) returns number
do
return not (resolve(domain) = "0" and not hasmx(domain))
done
@end group
@end example
@cindex match_dnsbl, definition
@item @code{match_dnsbl}@*
The function @code{match_dnsbl} (@pxref{match_dnsbl}) is defined as
follows:
@example
require dns
require match_cidr
#pragma regex push +extended
func match_dnsbl(string address, string zone, string range)
returns number
do
string rbl_ip
if range = 'ANY'
set rbl_ip '127.0.0.0/8'
else
set rbl_ip range
if not range matches '^([0-9]@{1,3@}\.)@{3@}[0-9]@{1,3@}$'
return 0
fi
fi
if not (address matches '^([0-9]@{1,3@}\.)@{3@}[0-9]@{1,3@}$'
and address != range)
return 0
fi
if address matches
'^([0-9]@{1,3@})\.([0-9]@{1,3@})\.([0-9]@{1,3@})\.([0-9]@{1,3@})$'
if match_cidr (resolve ("\4.\3.\2.\1", zone), rbl_ip)
return 1
else
return 0
fi
fi
# never reached
done
@end example
@end enumerate
@node Expressions
@section Expressions
@cindex expressions
Expressions are language constructs, that evaluate to a value, that
can subsequently be echoed, tested in a conditional statement,
assigned to a variable or passed to a function.
@menu
* Constant expressions:: String and Numeric Constants.
* Function calls:: A Function Call is an Expression.
* Concatenation:: String Concatenation.
* Arithmetic operations:: @samp{+}, @samp{-}, etc.
* Bitwise shifts:: @samp{<<} and @samp{>>}.
* Relational expressions:: @samp{=}, @samp{<}, etc.
* Special comparisons:: @code{matches}, @code{mx matches}, etc.
* Boolean expressions:: @code{and}, @code{or}, @code{not}.
* Precedence:: How various operators nest.
* Type casting::
@end menu
@node Constant expressions
@subsection Constant Expressions
Literals and numbers are @dfn{constant expressions}. They evaluate
to string and numeric types.
@node Function calls
@subsection Function Calls
A function call is an expression. Its type is the return type of
the function.
@node Concatenation
@subsection Concatenation
@cindex concatenation
Concatenation operator is @samp{.} (a dot). For example, if
@code{$f} is @samp{smith}, and @code{$client_addr} is
@samp{10.10.1.1}, then:
@example
$f . "-" . $client_addr @result{} "smith-10.10.1.1"
@end example
@cindex literal concatenation
Any two adjacent literal strings are concatenated, producing a new
string, e.g.
@example
"GNU's" " not " "UNIX" @result{} "GNU's not UNIX"
@end example
@node Arithmetic operations
@subsection Arithmetic Operations
The filter script language offers the common arithmetic operators:
@samp{+}, @samp{-}, @samp{*} and @samp{/}. In addition, the @samp{%}
is a @dfn{modulo} operator, i.e. it computes the remainder of division
of its operands.
All of them follow usual precedence rules and work as you would expect
them to.
@node Bitwise shifts
@subsection Bitwise shifts
The @samp{<<} represents a @dfn{bitwise shift left} operation, which
shifts the binary representation of the operand on its left by the
number of bits given by the operand on its right.
Similarly, the @samp{>>} represents a @dfn{bitwise shift right}.
@node Relational expressions
@subsection Relational Expressions
Relational expressions are:
@cindex @code{<} (left angle bracket), @code{<} operator
@cindex left angle bracket (@code{<}), @code{<} operator
@cindex @code{<} (left angle bracket), @code{<=} operator
@cindex left angle bracket (@code{<}), @code{<=} operator
@cindex @code{>} (right angle bracket), @code{>=} operator
@cindex right angle bracket (@code{>}), @code{>=} operator
@cindex @code{>} (right angle bracket), @code{>} operator
@cindex right angle bracket (@code{>}), @code{>} operator
@cindex @code{=} (equals sign), @code{=} operator
@cindex equals sign (@code{=}), @code{=} operator
@cindex @code{!} (exclamation point), @code{!=} operator
@cindex exclamation point (@code{!}), @code{!=} operator
@float Table,table-relational-expr
@caption{Relational Expressions}
@multitable @columnfractions .25 .75
@headitem Expression @tab Result
@item @var{x} @code{<} @var{y} @tab True if @var{x} is less than @var{y}.
@item @var{x} @code{<=} @var{y} @tab True if @var{x} is less than or equal to @var{y}.
@item @var{x} @code{>} @var{y} @tab True if @var{x} is greater than @var{y}.
@item @var{x} @code{>=} @var{y} @tab True if @var{x} is greater than or equal to @var{y}.
@item @var{x} @code{=} @var{y} @tab True if @var{x} is equal to @var{y}.
@item @var{x} @code{!=} @var{y} @tab True if @var{x} is not equal to @var{y}.
@end multitable
@end float
The relational expressions apply to string as well as to numbers.
When a relational operation applies to strings, case-sensitive
comparison is used, e.g.:
@example
@group
"String" = "string" @result{} False
"String" < "string" @result{} True
@end group
@end example
@node Special comparisons
@subsection Special Comparisons
In addition to the traditional relational operators, described
above, @command{mailfromd} provides two operators for regular
expression matching:
@kwindex matches
@kwindex fnmatches
@cindex regular expression matching
@cindex globbing patterns
@float Table,table-special-comp-expr
@caption{Regular Expression Matching}
@multitable @columnfractions .25 .75
@headitem Expression @tab Result
@item @var{x} @code{matches} @var{y} @tab True if the string @var{x} matches the
regexp denoted by @var{y}.
@item @var{x} @code{fnmatches} @var{y} @tab True if the string @var{x} matches the
globbing pattern denoted by @var{y}.
@end multitable
@end float
The type of the regular expression used by @code{matches} operator
is controlled by @code{#pragma regex} (@pxref{pragma regex}). For example:
@example
@group
$f @result{} "gray@@gnu.org.ua"
$f matches '.*@@gnu\.org\.ua' @result{} @code{true}
$f matches '.*@@GNU\.ORG\.UA' @result{} @code{false}
#pragma regex +icase
$f matches '.*@@GNU\.ORG\.UA' @result{} @code{true}
@end group
@end example
The @code{fnmatches} operator compares its left-hand operand with a
globbing pattern (see @cite{glob(7)}) given as its right-hand side
operand. For example:
@example
@group
$f @result{} "gray@@gnu.org.ua"
$f fnmatches "*ua" @result{} @code{true}
$f fnmatches "*org" @result{} @code{false}
$f fnmatches "*org*" @result{} @code{true}
@end group
@end example
@anchor{mx matches}
@cindex mx matches
Both operators have a special form, for @dfn{@samp{MX} pattern matching}.
The expression:
@example
@var{x} mx matches @var{y}
@end example
@noindent
is evaluated as follows: first, the expression @var{x} is analyzed and, if
it is an email address, its domain part is selected. If it is not,
its value is used verbatim. Then the list of @samp{MX}s for this domain is
looked up. Each of @samp{MX} names is then compared with the regular
expression @var{y}. If any of the names matches, the expression
returns true. Otherwise, its result is false.
@cindex mx fnmatches
Similarly, the expression:
@example
@var{x} mx fnmatches @var{y}
@end example
@noindent
returns true only if any of the @samp{MX}s for (domain or email) @var{x}
match the globbing pattern @var{y}.
Both @code{mx matches} and @code{mx fnmatches} can signal the
following exceptions: @code{e_temp_failure}, @code{e_failure}.
The value of any parenthesized subexpression occurring within the
right-hand side argument to @code{matches} or @code{mx matches} can be
referenced using the notation @samp{\@var{d}}, where @var{d} is the
ordinal number of the subexpression (subexpressions are numbered from
left to right, starting at 1). This notation is allowed in the
program text as well as within double-quoted strings and
here-documents, for example:
@example
@group
if $f matches '.*@@\(.*\)\.gnu\.org\.ua'
set message "Your host name is \1;"
fi
@end group
@end example
Remember that the grouping symbols are @samp{\(} and @samp{\)} for
basic regular expressions, and @samp{(} and @samp{)} for extended
regular expressions. Also make sure you properly escape all special
characters (backslashes in particular) in double-quoted strings, or
use single-quoted strings to avoid having to do so
(@pxref{singe-vs-double}, for a comparison of the two forms).
@node Boolean expressions
@subsection Boolean Expressions
@cindex and
@cindex or
@cindex not
A @dfn{boolean expression} is a combination of relational or
matching expressions using the boolean operators @code{and}, @code{or}
and @code{not}, and, eventually, parentheses to control nesting:
@float table, table boolean-expr
@caption{Boolean Operators}
@multitable @columnfractions .25 .75
@headitem Expression @tab Result
@item @var{x} @code{and} @var{y} @tab True only if both @var{x} and
@var{y} are true.
@item @var{x} @code{or} @var{y} @tab True if any of @var{x} or @var{y}
is true.
@item @code{not} @var{x} @tab True if @var{x} is false.
@end multitable
@end float
Binary boolean expressions are computed using @dfn{shortcut evaluation}:
@table @code
@item @var{x} and @var{y}
If @code{@var{x} @result{} @code{false}}, the result is @code{false}
and @var{y} is not evaluated.
@item @var{x} or @var{y}
If @code{@var{x} @result{} @code{true}}, the result is @code{true} and
@var{y} is not evaluated.
@end table
@node Precedence
@subsection Operator Precedence
@cindex operator precedence, defined
@cindex precedence, operators
Operator @dfn{precedence} is an abstract value associated with each
language operator, that determines the order in which operators are
executed when they appear together within a single expression.
Operators with higher precedence are executed first. For example,
@samp{*} has a higher precedence than @samp{+}, therefore the
expression @code{a + b * c} is evaluated in the following order: first
@code{b} is multiplied by @code{c}, then @code{a} is added to the
product.
@cindex operator associativity
@cindex associativity, operators
When operators of equal precedence are used together they are
evaluated from left to right (i.e., they are @dfn{left-associative}),
except for comparison operators, which are non-associative (these are
explicitly marked as such in the table below). This means that you
cannot write:
@example
if 5 <= x <= 10
@end example
@noindent
Instead, you should write:
@example
if 5 <= x and x <= 10
@end example
The precedence of the @command{mailfromd} operators where selected
so as to match that used in most programming languages.@footnote{The
only exception is @samp{not}, whose precedence in @acronym{MFL} is
much lower than usual (in most programming languages it has the same
precedence as unary @samp{-}). This allows to write conditional
expressions in more understandable manner. Consider the following
condition:
@example
if not x < 2 and y = 3
@end example
It is understood as ``if @code{x} is not less than 2 and @code{y} equals 3'',
whereas with the usual precedence for @samp{not} it would have meant
``if negated @code{x} is less than 2 and @code{y} equals 3''.}
The following table lists all operators in order of decreasing precedence:
@table @code
@item (...)
Grouping
@item $ %
@command{Sendmail} macros and @command{mailfromd} variables
@item * /
Multiplication, division
@item + -
Addition, subtraction
@item << >>
Bitwise shift left and right
@item < <= >= >
Relational operators (non-associative)
@item = != matches fnmatches
Equality and special comparison (non-associative)
@item &
Logical (bitwise) @sc{and}
@item ^
Logical (bitwise) @sc{xor}
@item |
Logical (bitwise) @sc{or}
@item not
Boolean negation
@item and
Logical @samp{and}.
@item or
Logical @samp{or}
@item .
String concatenation
@end table
@node Type casting
@subsection Type Casting
@cindex type casts, implicit
@cindex implicit type casts
When two operands on each side of a binary expression have
different type, @command{mailfromd} evaluator coerces them to a
common type. This is known as @dfn{implicit type casting}. The rules
for implicit type casting are:
@enumerate 1
@item
Both arguments to an arithmetical operation are cast to numeric
type.
@item
Both arguments to the concatenation operation are cast to string.
@item
Both arguments to `match' or `fnmatch' function are cast to string.
@item
The argument of the unary negation (arithmetical or boolean) is
cast to numeric.
@item
Otherwise the right-hand side argument is cast to the type of the
left-hand side argument.
@end enumerate
@cindex type casts, explicit
@cindex explicit type casts
@anchor{explicit type casts}
The construct for explicit type cast is:
@example
@var{type}(@var{expr})
@end example
@noindent
where @var{type} is the name of the type to coerce @var{expr} to. For
example:
@example
string(2 + 4*8) @result{} "34"
@end example
@anchor{void type cast}
@kwindex void
A special case of type casting is cast to @code{void}. It is used to
ignore return value of a function call between the braces, e.g.:
@example
void(dlcall(libh, "extlog", "s", text))
@end example
@node Shadowing
@section Variable and Constant Shadowing
@cindex shadowing, defined
@cindex name clashes
When any two named entities happen to have the same name we say that a
@dfn{name clash} occurs. The handling of name clashes depends on
types of the entities involved in it.
@subheading function -- any
A name of a constant or variable can coincide with that of a function,
it does not produce any warnings or errors because functions,
variables and constants use different namespaces. For example, the
following code is correct:
@example
const a 4
func a()
do
echo a
done
@end example
When executed, it prints @samp{4}.
@subheading function -- function, handler -- function, and function -- handler
Redefinition of a function or using a predefined handler name
(@pxref{Handlers}) as a function name results in a fatal error. For
example, compiling this code:
@example
func a()
do
echo "1"
done
func a()
do
echo "2"
done
@end example
@noindent
causes the following error message:
@example
mailfromd: sample.mfl:9: syntax error, unexpected
FUNCTION_PROC, expecting IDENTIFIER
@end example
@subheading handler -- variable
A variable name can coincide with a handler name. For example, the
following code is perfectly OK:
@example
string envfrom "M"
prog envfrom
do
echo envfrom
done
@end example
@subheading handler -- handler
If two handlers with the same name are defined, the definition that
appears further in the source text replaces the previous one. A
warning message is issued, indicating locations of both definitions,
e.g.:
@example
mailfromd: sample.mfl:116: Warning: Redefinition of handler
`envfrom'
mailfromd: sample.mfl:34: Warning: This is the location of the
previous definition
@end example
@subheading variable -- variable
@cindex variable shadowing
@cindex shadowing, variable
Defining a variable having the same name as an already defined one results
in a warning message being displayed. The compilation succeeds. The
second variable @dfn{shadows} the first, that is any subsequent
references to the variable name will refer to the second variable.
For example:
@example
string x "Text"
number x 1
prog envfrom
do
echo x
done
@end example
Compiling this code results in the following diagnostics:
@example
mailfromd: sample.mfl:4: Redeclaring `x' as different data type
mailfromd: sample.mfl:2: This is the location of the previous
definition
@end example
Executing it prints @samp{1}, i.e. the value of the last definition of
@code{x}.
The scope of the shadowing depends on storage classes of the two
variables. If both of them have external storage class (i.e. are
global ones), the shadowing remains in effect until the end of input.
In other words, the previous definition of the variable is effectively
forgotten.
If the previous definition is a global, and the shadowing definition
is an automatic variable or a function parameter, the scope of this
shadowing ends with the scope of the second variable, after which the
previous definition (global) becomes visible again. Consider the
following code:
@example
set x "initial"
func foo(string x) returns string
do
return x
done
prog envfrom
do
echo foo("param")
echo x
done
@end example
Its compilation produces the following warning:
@example
mailfromd: sample.mfl:3: Warning: Parameter `x' is shadowing a global
@end example
When executed, it produces the following output:
@example
param
initial
State envfrom: continue
@end example
@anchor{variable--constant shadowing}
@subheading variable -- constant
@cindex shadowing, variable--constant
If a constant is defined which has the same name as a previously
defined variable (the constant @dfn{shadows} the variable), the
compiler prints the following diagnostic message:
@example
@var{file}:@var{line}: Warning: Constant name `@var{name}' clashes with a variable name
@var{file}:@var{line}: Warning: This is the location of the previous definition
@end example
A similar diagnostics is issued if a variable is defined whose name
coincides with a previously defined constant (the variable shadows
the constant).
In any case, any subsequent notation %@var{name} refers to the last
defined symbol, be it variable or constant.
Notice, that shadowing occurs only when using %@var{name} notation.
Referring to the constant using its name without @samp{%} allows to
avoid shadowing effects.
If a variable shadows a constant, the scope of the shadowing depends
on the storage class of the variable. For automatic variables and
function parameters, it ends with the final @code{done} closing the
function. For global variables, it lasts up to the end of input.
For example, consider the following code:
@example
const a 4
func foo(string a)
do
echo a
done
prog envfrom
do
foo(10)
echo a
done
@end example
When run, it produces the following output:
@example
$ @kbd{mailfromd --test sample.mfl}
mailfromd: sample.mfl:3: Warning: Variable name `a' clashes with a
constant name
mailfromd: sample.mfl:1: Warning: This is the location of the previous
definition
10
4
State envfrom: continue
@end example
@subheading constant -- constant
@cindex shadowing, constant--constant
Redefining a constant produces a warning message. The latter
definition shadows the former. Shadowing remains in effect until
the end of input.
@node Statements
@section Statements
@cindex statements
Statements are language constructs, that, unlike expressions, do not
return any value. Statements execute some actions, such as assigning
a value to a variable, or serve to control the execution flow in the
program.
@menu
* Actions:: Actions control the handling of the mail.
* Assignments::
* Pass::
* Echo::
@c Return statement::
@c Conditionals
@c Exception handlers
@end menu
@node Actions
@subsection Action Statements
@cindex actions
An @dfn{action} statement instructs @command{mailfromd} to
perform a certain action over the message being processed. There are
two kinds of actions: return actions and header manipulation actions.
@subsubheading Reply Actions
@anchor{reply actions}
Reply actions tell @command{Sendmail} to return given response code
to the remote party. There are five such actions:
@table @code
@item accept
@cindex accept action, defined
@kwindex accept
Return an @code{accept} reply. The remote party will continue
transmitting its message.
@item reject @var{code} @var{excode} @var{message-expr}
@itemx reject (@var{code-expr}, @var{excode-expr}, @var{message-expr})
@cindex reject action, defined
@kwindex reject
Return a @code{reject} reply. The remote party will have to
cancel transmitting its message. The three arguments are optional,
their usage is described below.
@item tempfail @var{code} @var{excode} @var{message}
@itemx tempfail (@var{code-expr}, @var{excode-expr}, @var{message-expr})
@cindex tempfail action, defined
@kwindex tempfail
Return a @samp{temporary failure} reply. The remote party can retry
to send its message later. The three arguments are optional,
their usage is described below.
@item discard
@cindex discard action, defined
@kwindex discard
Instructs @command{Sendmail} to accept the message and silently discard
it without delivering it to any recipient.
@item continue
@cindex continue action, defined
@kwindex continue
Stops the current handler and instructs @command{Sendmail} to
continue processing of the message.
@end table
@anchor{reject and tempfail syntax}
Two actions, @code{reject} and @code{tempfail} can take up to three
optional parameters. There are two forms of supplying these
parameters.
In the first form, called @dfn{literal} or @dfn{traditional} notation,
the arguments are supplied as additional words after the action name,
and are separated by whitespace. The first argument is a three-digit
@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
@code{reject} and with @samp{4} for @code{tempfail}. If two arguments
are supplied, the second argument must be either an @dfn{extended
reply code} (@acronym{RFC} 1893/2034) or a textual string to be
returned along with the @acronym{SMTP} reply. Finally, if all three
arguments are supplied, then the second one must be an extended reply
code and the third one must give the textual string. The following
examples illustrate the possible ways of using the @code{reject}
statement:
@example
@group
reject
reject 503
reject 503 5.0.0
reject 503 "Need HELO command"
reject 503 5.0.0 "Need HELO command"
@end group
@end example
Used without arguments, @code{reject} is equivalent to
@example
reject @value{DEFAULT_REJECT_CODE}
@end example
@noindent
and @code{tempfail} to
@example
tempfail @value{DEFAULT_TEMPFAIL_CODE}
@end example
In literal notation, the values of code and extendended code (if
supplied) must be literal strings. The third argument (textual
message) can be either a literal string or @acronym{MFL} expression
that evaluates to string.
The second form of supplying arguments is called @dfn{functional}
notation, because it resembles the function syntax. When used in this
form, the action word is followed by a parenthesized group of exactly
three arguments, separated by commas. Each argument is a
@acronym{MFL} expression. The meaning and ordering of the arguments is
the same as in literal form. Any or all of these three arguments may
be absent, in which case the corresponding default value will be
used@footnote{The default value for code is
@value{DEFAULT_REJECT_CODE} for @code{reject} and
@value{DEFAULT_TEMPFAIL_CODE} for @code{tempfail}. The remaining two
arguments default to empty strings.}. To
illustrate this, here are the statements from the previous example,
written in functional notation:
@example
@group
reject(,,)
reject(503,,)
reject(503, 5.0.0)
reject(503, , "Need HELO command")
reject(503, 5.0.0, "Need HELO command")
@end group
@end example
Notice that there is an important difference between the two
notations. The functional notation allows to compute both reply codes
at run time, e.g.:
@example
reject(500 + dig2*10 + dig3, "5.%edig2.%edig2")
@end example
@subsubheading Header Actions
@anchor{header manipulation}
@cindex header manipulation actions
@cindex actions, header manipulation
Header manipulation actions provide basic means to add, delete or modify
the message @acronym{RFC} 2822 headers.
@table @command
@item add @var{name} @var{string}
@cindex add action, defined
@kwindex add
Add the header @var{name} with the value @var{string}. E.g.:
@example
add "X-Seen-By" "Mailfromd @value{VERSION}"
@end example
@noindent
(notice argument quoting)
@item replace @var{name} @var{string}
@cindex replace action, defined
@kwindex replace
The same as @code{add}, but if the header @var{name} already
exists, it will be removed first, for example:
@example
replace "X-Last-Processor" "Mailfromd @value{VERSION}"
@end example
@item delete @var{name}
@cindex delete action, defined
@kwindex delete
Delete the header named @var{name}:
@example
delete "X-Envelope-Date"
@end example
@end table
These actions impose some restrictions. First of all, their first
argument must be a literal string (not a variable or expression).
Secondly, there is no way to select a particular header instance
to delete or replace, which may be necessary to properly handle
multiple headers (e.g.@: @samp{Received}). For more elaborate ways of
header modifications, see @ref{Header modification functions}.
@node Assignments
@subsection Variable Assignments
@cindex assignment, defined
@cindex variable assignment
@kwindex set
An @dfn{assignment} is a special statement that assigns a value to
the variable. It has the following syntax:
@example
set @var{name} @var{value}
@end example
@noindent
where @var{name} is the variable name and @var{value} is the value to
be assigned to it.
Assignment statements can appear in any part of a filter program.
If an assignment occurs outside of function or handler definition,
the @var{value} must be a literal value (@pxref{Literals}). If it
occurs within a function or handler definition, @var{value} can be any
valid @command{mailfromd} expression (@pxref{Expressions}). In this
case, the expression will be evaluated and its value will be assigned
to the variable. For example:
@example
@group
set delay 150
prog envfrom
do
set delay delay * 2
@dots{}
done
@end group
@end example
@node Pass
@subsection The @code{pass} statement
@kwindex pass
The @code{pass} statement has no effect. It is used in places
where no statement is needed, but the language syntax requires one:
@example
@group
on poll $f do
when success:
pass
when not_found or failure:
reject 550
done
@end group
@end example
@node Echo
@subsection The @code{echo} statement
@kwindex echo
@cindex debugging
The @command{echo} statement concatenates all its arguments into a single
string and sends it to the @command{syslog} using the priority
@samp{info}. It is useful for debugging your script, in
conjunction with built-in constants (@pxref{Built-in constants}), for
example:
@example
@group
func foo(number x)
do
echo "%__file__:%__line__: foo called with arg %x"
@dots{}
done
@end group
@end example
@node Conditionals
@section Conditional Statements
@cindex conditional statements
@cindex statements, conditional
@dfn{Conditional expressions}, or conditionals for short, test
some conditions and alter the control flow depending on the
result. There are two kinds of conditional statements: @dfn{if-else}
branches and @dfn{switch} statements.
@kwindex if
@kwindex elif
@kwindex else
@kwindex fi
The syntax of an @dfn{if-else} branching construct is:
@example
if @var{condition} @var{then-body} [else @var{else-body}] fi
@end example
@noindent
Here, @var{condition} is an expression that governs control flow
within the statement. Both @var{then-body} and @var{else-body} are
lists of @command{mailfromd} statements. If @var{condition} is
true, @var{then-body} is executed, if it is false, @var{else-body} is
executed. The @samp{else} part of the statement is optional. The
condition is considered false if it evaluates to zero, otherwise it is
considered true. For example:
@example
@group
if $f = ""
accept
else
reject
fi
@end group
@end example
@noindent
This will accept the message if the value of the @command{Sendmail}
macro @code{$f} is an empty string, and reject it otherwise. Both
@var{then-body} and @var{else-body} can be compound statements
including other @code{if} statements. Nesting level of
conditional statements is not limited.
To facilitate writing complex conditional statements, the @code{elif}
keyword can be used to introduce alternative conditions, for example:
@example
@group
if $f = ""
accept
elif $f = "root"
echo "Mail from root!"
else
reject
fi
@end group
@end example
@anchor{switch}
@kwindex switch
@kwindex case
@cindex @code{switch} statement
@cindex @code{case}, @code{switch} statement
Another type of branching instruction is @code{switch} statement:
@example
@group
switch @var{condition}
do
case @var{x1} [or @var{x2} @dots{}]:
@var{stmt1}
case @var{y1} [or @var{y2} @dots{}]:
@var{stmt2}
.
.
.
[default:
@var{stmt}]
done
@end group
@end example
@noindent
Here, @var{x1}, @var{x2}, @var{y1}, @var{y2} are literal expressions;
@var{stmt1}, @var{stmt2} and @var{stmt} are arbitrary
@command{mailfromd} statements (possibly compound); @var{condition} is
the controlling expression. The vertical dotted row represent another
eventual @samp{case} branches.
This statement is executed as follows: the @var{condition}
expression is evaluated and if its value equals @var{x1} or @var{x2}
(or any other @var{x} from the first @code{case}), then
@var{stmt1} is executed. Otherwise, if @var{condition} evaluates
to @var{y1} or @var{y2} (or any other @var{y} from the second
@code{case}), then @var{stmt2} is executed. Other @code{case}
branches are tried in turn. If none of them matches, @var{stmt}
(called the @dfn{default branch}) is executed.
There can be as many @code{case} branches as you wish. The
@code{default} branch is optional. There can be at most one
@code{default} branch.
An example of @code{switch} statement follows:
@example
@group
switch x
do
case 1 or 3:
add "X-Branch" "1"
accept
case 2 or 4 or 6:
add "X-Branch" "2"
default:
reject
done
@end group
@end example
If the value of @command{mailfromd} variable @code{x} is 2 or 3,
it will accept the message immediately, and add a @samp{X-Branch: 1}
header to it. If @code{x} equals 2 or 4 or 6, this code will add
@samp{X-Branch: 2} header to the message and will continue processing
it. Otherwise, it will reject the message.
The controlling condition of a @code{switch} statement may evaluate
to numeric or string type. The type of the condition governs the
type of comparisons used in @code{case} branches: for numeric types,
numeric equality will be used, whereas for string types, string
equality is used.
@node Loops
@section Loop Statements
@kwindex loop
@kwindex while
@cindex loop statement
The loop statement allows for repeated execution of a block of code,
controlled by some conditional expression. It has the following form:
@example
@group
loop [@var{label}]
[for @var{stmt1}] [,while @var{expr1}] [,@var{stmt2}]
do
@var{stmt3}
done [while @var{expr2}]
@end group
@end example
@noindent
where @var{stmt1}, @var{stmt2}, and @var{stmt3} are statement lists,
@var{expr1} and @var{expr2} are expressions.
The control flow is as follows:
@enumerate 1
@item
If @var{stmt1} is specified, execute it.
@item
Evaluate @var{expr1}. If it is zero, go to 6. Otherwise, continue.
@item
Execute @var{stmt3}.
@item
If @var{stmt2} is supplied, execute it.
@item
If @var{expr2} is given, evaluate it. If it is zero, go to 6.
Otherwise, go to 2.
@item
End.
@end enumerate
Thus, @var{stmt3} is executed until either @var{expr1} or
@var{expr2} yield a zero value.
@cindex loop body
The @dfn{loop body} -- @var{stmt3} -- can contain special
statements:
@table @code
@kwindex break
@cindex break statement
@item break [@var{label}]
Terminates the loop immediately. Control passes to @samp{6} (End)
in the formal definition above. If @var{label} is supplied, the
statement terminates the loop statement marked with that label. This
allows to break from nested loops.
It is similar to @code{break} statement in @sc{c} or shell.
@kwindex next
@cindex next statement
@item next [@var{label}]
Initiates next iteration of the loop. Control passes to @samp{4} in
the formal definition above. If @var{label} is supplied, the
statement starts next iteration of the loop statement marked with that
label. This allows to request next iteration of an upper-level
loop from a nested loop statement.
@end table
The @code{loop} statement can be used to create iterative statements
of arbitrary complexity. Let's illustrate it in comparison with @sc{c}.
@cindex infinite loop
@cindex loop, infinite
The statement:
@example
@group
loop
do
@var{stmt-list}
done
@end group
@end example
@noindent
creates an infinite loop. The only way to exit from such a loop is to
call @code{break} (or @code{return}, if used within a function),
somewhere in @var{stmt-list}.
@cindex loop, while-style
@cindex while loop
The following statement is equivalent to @code{while (@var{expr1})
@var{stmt-list}} in @sc{c}:
@example
@group
loop while @var{expr}
do
@var{stmt-list}
done
@end group
@end example
@cindex loop, for-style
@cindex for loop
The @sc{c} construct @code{for (@var{expr1}; @var{expr2}; @var{expr3})}
is written in @acronym{MFL} as follows:
@example
@group
loop for @var{stmt1}, while @var{expr2}, @var{stmt2}
do
@var{stmt3}
done
@end group
@end example
For example, to repeat @var{stmt3} 10 times:
@example
@group
loop for set i 0, while i < 10, set i i + 1
do
@var{stmt3}
done
@end group
@end example
@cindex loop, do-style
@cindex do loop
Finally, the @sc{c} @samp{do} loop is implemented as follows:
@example
@group
loop
do
@var{stmt-list}
done while @var{expr}
@end group
@end example
As a real-life example of a loop statement, let's consider the
implementation of function @code{ptr_validate}, which takes a single
argument @var{ipstr}, and checks its validity using the following algorithm:
Perform a @acronym{DNS} reverse-mapping for @var{ipstr}, looking up the
corresponding @code{PTR} record in @samp{in-addr.arpa}. For each record
returned, look up its @acronym{IP} addresses (A records). If @var{ipstr} is
among the returned @acronym{IP} addresses, return 1 (@code{true}), otherwise
return 0 (@code{false}).
The implementation of this function in @acronym{MFL} is:
@example
#pragma regex push +extended
func ptr_validate(string ipstr) returns number
do
loop for string names dns_getname(ipstr) . " "
number i index(names, " "),
while i != -1,
set names substr(names, i + 1)
set i index(names, " ")
do
loop for string addrs dns_getaddr(substr(names, 0, i)) . " "
number j index(addrs, " "),
while j != -1,
set addrs substr(addrs, j + 1)
set j index(addrs, " ")
do
if ipstr == substr(addrs, 0, j)
return 1
fi
done
done
return 0
done
@end example
@node Exceptions
@section Exceptional Conditions
@cindex exceptions, defined
When the running program encounters a condition it is not able
to handle, it signals an @dfn{exception}. To illustrate the concept,
let's consider the execution of the following code fragment:
@example
@group
if primitive_hasmx(domainpart($f))
accept
fi
@end group
@end example
@noindent
The function @code{primitive_hasmx} (@pxref{primitive_hasmx}) tests whether the
domain name given as its argument has any @samp{MX} records. It should
return a boolean value. However, when querying the Domain Name
System, it may fail to get a definite result. For example, the @acronym{DNS}
server can be down or temporary unavailable. In other words,
@code{primitive_hasmx} can be in a situation when, instead of returning
@samp{yes} or @samp{no}, it has to return @samp{don't know}. It has
no way of doing so, therefore it signals an @dfn{exception}.
@anchor{status.mf}
@anchor{status.mfl}
@cindex exception types
@cindex @file{status.mfl}, module
@cindex exceptions, symbolic names
@flindex status.mfl
Each exception is identified by @dfn{exception type}, an integer
number associated with it.
@menu
* Built-in Exceptions::
* User-defined Exceptions::
* Catch and Throw::
@end menu
@node Built-in Exceptions
@subsection Built-in Exceptions
@flindex status.mfl
The first @value{MAX_E_EXCEPTIONS} exception numbers are reserved for
@dfn{built-in exceptions}. These are declared in module @file{status.mfl}.
The following table summarizes all built-in exception types implemented by
@command{mailfromd} version @value{VERSION}. Exceptions are listed in
lexicographic order.
@anchor{exception names}
@defvr {Exception} e_badmmq
The called function cannot finish its task because an incompatible
message modification function was called at some point before it.
For details, @ref{MMQ and dkim_sign}.
@end defvr
@defvr {Exception} e_dbfailure
General database failure. For example, the database cannot be
opened. This exception can be signaled by any function that queries
any @acronym{DBM} database.
@end defvr
@defvr {Exception} e_divzero
Division by zero.
@end defvr
@defvr {Exception} e_exists
This exception is emitted by @code{dbinsert} built-in if the
requested key is already present in the database (@pxref{Database
functions,dbinsert}).
@end defvr
@defvr {Exception} e_eof
Function reached end of file while reading. @xref{I/O functions},
for a description of functions that can signal this exception.
@end defvr
@defvr {Exception} e_failure
A general failure has occurred. In particular, this exception is
signaled by @acronym{DNS} lookup functions when any permanent failure occurs.
This exception can be signaled by any @acronym{DNS}-related function
(@code{hasmx}, @code{poll}, etc.) or operation (@code{mx matches}).
@end defvr
@defvr {Exception} e_format
Invalid input format. This exception is signaled if input data to a
function are improperly formatted. In version @value{VERSION} it is
signaled by @code{message_burst} function if its input message is not
formatted according to RFC 934. @xref{Message digest functions}.
@end defvr
@defvr {Exception} e_ilseq
Illegal byte sequence. Signaled when a string cannot be converted
between character sets because a sequence of bytes was encountered
that is not defined for the source character set or cannot be
represented in the destination character set.
@xref{MIME decoding}, for details.
@end defvr
@defvr {Exception} e_inval
Arguments supplied to a function are invalid.
@end defvr
@defvr {Exception} e_invcidr
Invalid @acronym{CIDR} notation. This is signaled by
@code{match_cidr} function when its second argument is not a valid
@acronym{CIDR}.
@end defvr
@defvr {Exception} e_invip
Invalid @acronym{IP} address. This is signaled by @code{match_cidr} function
when its first argument is not a valid @acronym{IP} address.
@end defvr
@defvr {Exception} e_invtime
Invalid time interval specification. It is signaled by
@code{interval} function if its argument is not a valid time interval
(@pxref{time interval specification}).
@end defvr
@defvr {Exception} e_io
An error occurred during the input-output operation. @xref{I/O
functions}, for a description of functions that can signal this
exception.
@end defvr
@defvr {Exception} e_macroundef
A Sendmail macro is undefined.
@end defvr
@defvr {Exception} e_not_found
Required entity is not found. It is raised, for example, by
@code{message_find_header}, when the requested header is not present
in the message and by @acronym{DNS} resolver functions when unable to
resolve host name or @acronym{IP} address.
@end defvr
@defvr {Exception} e_range
The supplied argument is outside the allowed range. This is
signalled, for example, by @code{substring} function (@pxref{substring}).
@end defvr
@defvr {Exception} e_regcomp
Regular expression cannot be compiled. This can happen when a
regular expression (a right-hand argument of a @code{matches}
operator) is built at the runtime and the produced string is an
invalid regex.
@end defvr
@defvr {Exception} e_ston_conv
String-to-number conversion failed. This can be signaled when a
string is used in numeric context which cannot be converted to the numeric
data type. For example:
@example
@group
set x "10a"
set y x / 2
@end group
@end example
@noindent
In this code fragment, line 2 will raise the @code{e_ston_conv}
exception, since @samp{10a} cannot be converted to a number.
@end defvr
@defvr {Exception} e_success
This is not an exception in the strict sense of the word, but a
constant indicating success.
@end defvr
@defvr {Exception} e_temp_failure
A temporary failure has occurred. This can be signaled by
@acronym{DNS}-related functions or operations.
@end defvr
@defvr {Exception} e_too_many
Raised by various @acronym{DNS} functions when they encounter a long chain
of CNAME records when trying to resolve a hostname. @xref{CNAME chains}.
@end defvr
@defvr {Exception} e_url
The supplied @acronym{URL} is invalid. @xref{Interfaces to
Third-Party Programs}.
@end defvr
@node User-defined Exceptions
@subsection User-defined Exceptions
@kwindex dclex
You can define your own exception types using the @code{dclex}
statement:
@example
dclex @var{type}
@end example
In this statement, @var{type} must be a valid @acronym{MFL}
identifier, not used for another constant (@pxref{Constants}).
The @code{dclex} statement defines a new exception identified by
the constant @var{type} and allocates a new exception number for it.
The @var{type} can subsequently be used in @code{throw} and
@code{catch} statements, for example:
@example
dclex myrange
number fact(number val)
returns number
do
if val < 0
throw myrange "fact argument is out of range"
fi
@dots{}
done
@end example
@node Catch and Throw
@subsection Exception Handling
@cindex exceptions, default handling
@cindex default exception handling
Normally when an exception is signalled, the program execution is
terminated and the @acronym{MTA} is returned a @code{tempfail}
status. Additional information regarding the exception is then output
to the logging channel (@pxref{Logging and Debugging}). However, the
user can intercept any exception by installing his own
exception-handling routines.
@cindex @code{try} statement
@cindex @code{catch} statement
@cindex try--catch construct
@cindex exception-handling routines
@cindex exception handlers
@kwindex catch
An exception-handling routine is introduced by a @dfn{try--catch}
statement, which has the following syntax:
@example
@group
try
do
@var{stmtlist}
done
catch @var{exception-list}
do
@var{handler-body}
done
@end group
@end example
@noindent
where @var{stmtlist} and @var{handler-body} are sequences of
@acronym{MFL} statements and @var{exception-list} is the list of
exception types, separated by the word @code{or}. A special
@var{exception-list} @samp{*} is allowed and means all exceptions.
This construct works as follows. First, the statements from
@var{stmtlist} are executed. If the execution finishes
successfully, control is passed to the first statement after the
@samp{catch} block. Otherwise, if an exception is signalled and this
exception is listed in @var{exception-list}, the execution is passed to the
@var{handler-body}. If the exception is not listed in
@var{exception-list}, it is handled as usual.
The following example shows a @samp{try--catch} construct used for
handling eventual exceptions, signalled by @code{primitive_hasmx}.
@example
@group
try
do
if primitive_hasmx(domainpart($f))
accept
else
reject
fi
done
catch e_failure or e_temp_failure
do
echo "primitive_hasmx failed"
continue
done
@end group
@end example
@cindex scope of exception handlers
@cindex scope of a @code{catch}
@cindex @code{catch} scope
@cindex exception handler scope
The @samp{try--catch} statement can appear anywhere inside a function or
a handler, but it cannot appear outside of them. It can also be nested
within another @samp{try--catch}, in either of its parts. Upon exit from a
function or milter handler, all exceptions are restored to the state
they had when it has been entered.
@cindex catch, standalone
@cindex standalone catch
A @code{catch} block can also be used alone, without preceding @code{try}
part. Such a construct is called a @dfn{standalone catch}. It is
mostly useful for setting global exception handlers in a @code{begin}
statement (@pxref{begin/end}). When used within a usual function or
handler, the exception handlers set by a standalone catch
remain in force until either another standalone catch appears further
in the same function or handler, or an end of the function is
encountered, whichever occurs first.
A standalone catch defined within a function must return from
it by executing @code{return} statement. If it does not do that
explicitly, the default value of 1 is returned. A standalone catch
defined within a milter handler must end execution with any of the
following actions: @code{accept}, @code{continue}, @code{discard},
@code{reject}, @code{tempfail}. By default, @code{continue} is
used.
It is not recommended to mix @samp{try--catch} constructs and
standalone catches. If a standalone catch appears within a
@samp{try--catch} statement, its scope of visibility is undefined.
@cindex returning from a catch
@cindex returning from an exception handler
@cindex @code{catch}, returning from
@cindex exception handler, returning from
@cindex @code{catch} arguments
@cindex arguments, @code{catch}
Upon entry to a @var{handler-body}, two implicit positional arguments
are defined, which can be referenced in @var{handler-body} as @code{$1}
and @code{$2}@footnote{As of @command{mailfromd} version
@value{VERSION}, there is also a third implicit argument, which holds
the value of program counter where the exception occurred. Currently
it is considered to be an implementation artifact. Filter writers are
discouraged from relying on it.}. The first argument gives the
numeric code of the exception that has occurred. The second argument
is a textual string containing a human-readable description of the exception.
The following is an improved version of the previous example, which
uses these parameters to supply more information about the failure:
@example
try
do
if primitive_hasmx(domainpart($f))
accept
else
reject
fi
done
catch e_failure or e_temp_failure
do
echo "Caught exception $1: $2"
continue
done
@end example
@cindex @code{hasmx}, definition of the function
The following example defines the function @code{hasmx} that
returns true if the domain part of its argument has any @samp{MX} records, and
false if it does not or if an exception occurs @footnote{This function is
part of the @code{mailfromd} library, @xref{hasmx}.}.
@example
func hasmx (string s)
returns number
do
try
do
return primitive_hasmx(domainpart(s))
done
catch *
do
return 0
done
done
@end example
The same function can written using standalone @code{catch}:
@example
func hasmx (string s)
returns number
do
catch *
do
return 0
done
return primitive_hasmx(domainpart(s))
done
@end example
@cindex @code{catch}, accessing variables from
@cindex variables, accessing from @code{catch}
@cindex accessing variables from @code{catch}
All variables remain visible within @code{catch} body, with the
exception of positional arguments of the enclosing handler. To access
positional arguments of a handler from the @code{catch} body, assign
them to local variables prior to the @samp{try--catch} construct, e.g.:
@example
@group
prog header
do
string hname $1
string hvalue $2
try
do
@dots{}
done
catch *
do
echo "Exception $1 while processing header %hname: %hvalue"
echo $2
tempfail
done
@end group
@end example
@anchor{throw}
@cindex exceptions, raising from code
@cindex raising exceptions
@kwindex throw
You can also generate (or @dfn{raise}) exceptions explicitly in the
code, using @code{throw} statement:
@example
throw @var{excode} @var{descr}
@end example
The arguments correspond exactly to the positional parameters of the
@code{catch} statement: @var{excode} gives the numeric code of the
exception, @var{descr} gives its textual description. This statement
can be used in complex scripts to create non-local exits from deeply
nested statements. @FIXME{Elaborate on that.}
Notice, that the the @var{excode} argument must be an immediate
value: an exception identifier (either a built-in one or one declared
previously using a @code{dclex} statement).
@node Polling
@section Sender Verification Tests
@cindex sender verification, writing tests
The filter script language provides a wide variety of functions for
sender address verification or @dfn{polling}, for short. These
functions, which were described in @ref{SMTP Callout functions}, can be
used to implement any sender verification method. The additional data
that can be needed is normally supplied by two global variables:
@code{ehlo_domain}, keeping the default domain for the @code{EHLO}
command, and @code{mailfrom_address}, which stores the sender address
for probe messages (@pxref{Predefined variables}).
For example, a simplest way to implement standard polling would be:
@example
prog envfrom
do
if stdpoll($1, ehlo_domain, mailfrom_address) == 0
accept
else
reject 550 5.1.0 "Sender validity not confirmed"
fi
done
@end example
However, this does not take into account exceptions that
@code{stdpoll} can signal. To handle them, one will have to use
@code{catch}, for example thus:
@example
require status
prog envfrom
do
try
do
if stdpoll($1, ehlo_domain, mailfrom_address) == 0
accept
else
reject 550 5.1.0 "Sender validity not confirmed"
fi
done
catch e_failure or e_temp_failure
do
switch $1
do
case failure:
reject 550 5.1.0 "Sender validity not confirmed"
case temp_failure:
tempfail 450 4.1.0 "Try again later"
done
done
done
@end example
If polls are used often, one can define a wrapper function, and use
it instead. The following example illustrates this approach:
@float Figure, figure-poll-wrapper
@caption{Building Poll Wrappers}
@example
func poll_wrapper(string email) returns number
do
catch e_failure or e_temp_failure
do
return email
done
return stdpoll(email, ehlo_domain, mailfrom_address)
done
prog envfrom
do
switch poll_wrapper($f)
do
case success:
accept
case not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
case temp_failure:
tempfail 450 4.1.0 "Try again later"
done
done
@end example
@end float
Notice the way @code{envfrom} handles @code{success} and
@code{not_found}, which are not exceptions in the strict sense of the
word.
@cindex @code{on} statement
@cindex @code{when} keyword
The above paradigm is so common that @command{mailfromd} provides a
special language construct to simplify it: the @code{on} statement.
Instead of manually writing the wrapper function and using it as a
@code{switch} condition, you can rewrite the above example as:
@float Figure, figure-stdpoll
@caption{Standard poll example}
@example
@group
prog envfrom
do
on stdpoll($1, ehlo_domain, mailfrom_address)
do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
done
@end group
@end example
@end float
@noindent
As you see the statement is pretty similar to @code{switch}. The
major syntactic difference is the use of the keyword @code{when} to
introduce conditional branches.
General syntax of the @code{on} statement is:
@example
@group
on @var{condition}
do
when @var{x1} [or @var{x2} @dots{}]:
@var{stmt1}
when @var{y1} [or @var{y2} @dots{}]:
@var{stmt2}
.
.
.
done
@end group
@end example
@noindent
The @var{condition} is either a function call or a special @code{poll}
statement (see below). The values used in @code{when} branches are
normally symbolic exception names (@pxref{exception names}).
When the compiler processes the @code{on} statement it does the
following:
@enumerate 1
@item
Builds a unique wrapper function, similar to that described in
@ref{figure-poll-wrapper}; The name of the function is constructed
from the @var{condition} function name and an unsigned number,
called @dfn{exception mask}, that is unique for each combination of
exceptions used in @code{when} branches; To avoid name clashes with
the user-defined functions, the wrapper name begins and ends with
@samp{$} which normally is not allowed in the identifiers;
@item
Translates the @code{on} body to the corresponding @code{switch}
statement;
@end enumerate
@anchor{poll}
@cindex @code{poll} keyword
@cindex @code{poll} statement, defined
A special form of the @var{condition} is @code{poll} keyword,
whose syntax is:
@example
@group
poll [for] @var{email}
[host @var{host}]
[from @var{domain}]
[as @var{email}]
@end group
@end example
The order of particular keywords in the @code{poll} statement is
arbitrary, for example @code{as @var{email}} can appear before
@var{email} as well as after it.
@cindex @code{poll} command, standard verification
@cindex standard verification with @code{poll}
The simplest form, @code{poll @var{email}}, performs the standard
sender verification of email address @var{email}. It is translated
to the following function call:
@example
stdpoll(@var{email}, ehlo_domain, mailfrom_address)
@end example
@cindex @code{poll} command, strict verification
@cindex strict verification with @code{poll}
The construct @code{poll @var{email} host @var{host}}, runs the
strict sender verification of address @var{email} on the given host.
It is translated to the following call:
@example
strictpoll(@var{host}, @var{email}, ehlo_domain, mailfrom_address)
@end example
@cindex as
@cindex from
Other keywords of the @code{poll} statement modify these two basic
forms. The @code{as} keyword introduces the email address to be used
in the @acronym{SMTP} @code{MAIL FROM} command, instead of
@code{mailfrom_address}. The @code{from} keyword sets the domain
name to be used in @code{EHLO} command. So, for example the following
construct:
@example
poll @var{email} host @var{host} from @var{domain} as @var{addr}
@end example
@noindent
is translated to
@example
strictpoll(@var{host}, @var{email}, @var{domain}, @var{addr})
@end example
To summarize the above, the code described in @ref{figure-stdpoll}
can be written as:
@example
@group
prog envfrom
do
on poll $f do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
done
@end group
@end example
@node Modules
@section Modules
@cindex module, defined
A @dfn{module} is a logically isolated part of code that implements a
separate concern or feature and contains a collection of conceptually
united functions and/or data. Each module occupies a separate
compilation unit (i.e. file). The functionality provided by
a module is incorporated into another module or the main program by
@dfn{requiring} this module or by @dfn{importing} the desired
components from it.
@menu
* module structure:: Declaring Modules
* scope of visibility::
* import:: Require and Import
@end menu
@node module structure
@subsection Declaring Modules
@cindex module declaration
@kwindex module
A module file must begin with a @dfn{module declaration}:
@example
module @var{modname} [@var{interface-type}].
@end example
Note the final dot.
The @var{modname} parameter declares the name of the module. It is
recommended that it be the same as the file name without the
@samp{.mfl} extension. The module name must be a valid @acronym{MFL}
literal. It also must not coincide with any defined @acronym{MFL}
symbol, therefore we recommend to always quote it (see example below).
The optional parameter @var{interface-type} defines the @dfn{default
scope of visibility} for the symbols declared in this module. If it is
@samp{public}, then all symbols declared in this module are made
public (importable) by default, unless explicitly declared otherwise
(@pxref{scope of visibility}). If it is @samp{static}, then all
symbols, not explicitly marked as public, become static. If the
@var{interface-type} is not given, @samp{public} is assumed.
The actual @acronym{MFL} code follows the @samp{module} line.
The module definition is terminated by the @dfn{logical end} of its
compilation unit, i.e. either by the end of file, or by the
keyword @code{bye}, whichever occurs first.
@kwindex bye
Special keyword @code{bye} may be used to prematurely end the current
compilation unit before the physical end of the containing file.
Any material between @code{bye} and the end of file is ignored by the
compiler.
Let's illustrate these concepts by writing a module @samp{revip}:
@example
@group
module 'revip' public.
func revip(string ip)
returns string
do
return inet_ntoa(ntohl(inet_aton(ip)))
done
bye
This text is ignored. You may put any additional
documentation here.
@end group
@end example
@node scope of visibility
@subsection Scope of Visibility
@cindex scope of visibility
@dfn{Scope of Visibility} of a symbol defines from where this symbol
may be referred to. Symbols in @acronym{MFL} may have either of the
following two scopes:
@table @dfn
@item Public
Public symbols are visible from the current module, as well as from
any external modules, including the main script file, provided that
they are properly imported (@pxref{import}).
@item Static
Static symbols are visible only from the current module. There is
no way to refer to them from outside.
@end table
The default scope of visibility for all symbols declared within
a module is defined in the module declaration (@pxref{module
structure}). It may be overridden for any individual symbol by
prefixing its declaration with an appropriate @dfn{qualifier}: either
@code{public} or @code{static}.
@node import
@subsection Require and Import
@cindex importing from modules
@cindex requiring modules
Functions or variables declared in another module must be @dfn{imported}
prior to their actual use. @acronym{MFL} provides two ways of doing
so: by @dfn{requiring} the entire module or by importing selected
symbols from it.
@anchor{module search path}
@cindex module search path
Modules are looked up in the @dfn{module search path}. The default
module search path consists of two directories:
@enumerate 1
@item @file{@var{prefix}/share/mailfromd}
@item @file{@var{prefix}/share/mailfromd/@value{VERSION}}
@end enumerate
@noindent
where @var{prefix} stands for the installation prefix (normally
@file{/usr} or @file{/usr/local}).
Module search path can be changed in the configuration file,
using the @code{module-path} statement (@pxref{conf-base,
module-path}), or from the command line, using the @option{-P}
(@option{--module-path}) option (@pxref{--module-path}).
@deffn {Module Import} require modname
The @code{require} statement instructs the compiler to locate the
module @var{modname} and to load all public interfaces from it.
@end deffn
The compiler looks for the file @file{@var{modname}.mfl} in the
module search path. If no such file is found, a compilation error is
reported.
For example, the following statement:
@example
require revip
@end example
@noindent
imports all interfaces from the module @file{revip.mfl}.
@kwindex from
@kwindex import
@cindex @code{from @dots{} import}
Another, more sophisticated way to import from a module is to use
the @samp{from ... import} construct:
@example
from @var{module} import @var{symbols}.
@end example
Note the final dot. The @samp{from} and @samp{module} statements are
the only two constructs in @acronym{MFL} that require the delimiter.
The @var{module} has the same semantics as in the @code{require}
construct. The @var{symbols} is a comma-separated list of symbol
names to import from @var{module}. A symbol name may be given in
several forms:
@enumerate 1
@item Literal
Literals specify exact symbol names to import. For example,
the following statement imports from module @file{A.mfl} symbols
@samp{foo} and @samp{bar}:
@example
from A import foo,bar.
@end example
@item Regular expression
Regular expressions must be surrounded by slashes. A regular
expression instructs the compiler to import all symbols whose
names match that expression. For example, the following statement
imports from @file{A.mfl} all symbols whose names begin with @samp{foo}
and contain at least one digit after it:
@example
from A import '/^foo.*[0-9]/'.
@end example
The type of regular expressions used in the @samp{from} statement is
controlled by @code{#pragma regex} (@pxref{regex}).
@item Regular expression with transformation
Regular expression may be followed by a @dfn{s-expression}, i.e. a
@command{sed}-like expression of the form:
@example
s/@var{regexp}/@var{replace}/[@var{flags}]
@end example
@noindent
where @var{regexp} is a @dfn{regular expression}, @var{replace} is a
replacement for each part of the input that matches @var{regexp}.
S-expressions and their parts are discussed in detail in
@ref{s-expression}.
The effect of such construct is to import all symbols that match the
regular expression and apply the s-expression to their names.
For example:
@example
from A import '/^foo.*[0-9]/s/.*/my_&/'.
@end example
This statement imports all symbols whose names begin with @samp{foo}
and contain at least one digit after it, and renames them, by prefixing
their names with the string @samp{my_}. Thus, if @file{A.mfl} declared a
function @samp{foo_1}, it becomes visible under the name of @samp{my_foo_1}.
@end enumerate
@node mfmod
@section Dynamically Loaded Modules
@cindex mfmod
Native mailfromd modules described above rely on the functions
provided by the @command{mailfromd} binary. For more sophisticated
tasks you might need to use C functions, either for efficiency reasons
or to make use of some third-party library. This is possible using
special kind of modules called @dfn{mfmod}.
An mfmod consists of two major parts: a dynamically loaded library
that provides its main functionality and a small @dfn{interface}
mailfromd module. The convention is that for the module @var{x}
the library is named @file{mfmod_@var{x}.so}@footnote{The actual
suffix depends on operating system. It is @samp{.so} on all POSIX
systems.}, and the interface module file is @file{@var{x}.mfl}.
At the time of this writing, three mfmods exist:
@table @asis
@item mfmod_pcre
Provides support for Perl-compatible regular expressions. It also
contains a special function for scanning an email message for a match
to a regular expression. @xref{Top, Mfmod_pcre,, mfmod_pcre,
Mfmod_pcre}.
@item mfmod_ldap
Functions for searching in LDAP directory. @xref{Top, Mfmod_ldap,,
mfmod_ldap, Mfmod_ldap}.
@item mfmod_openmetrics
Openmetrics support for @command{mailfromd}. @xref{Top,
mfmod_openmetrics,, mfmod_openmetrics, mfmod_openmetrics reference}.
@end table
The subsections below describe the internal structure of an mfmod in
detail.
@menu
* Loadable Library::
* Interface Module::
* mfmodnew:: Creating a Mfmod Structure
@end menu
@node Loadable Library
@subsection Loadable Library
@cindex mfmod, loadable library
External functions in the loadable library must be declared as
@example
int funcname(long count, MFMOD_PARAM *param, MFMOD_PARAM *retval);
@end example
@findex mfmod.h
The @code{MFMOD_PARAM} type is declared in the header file
@file{mailfromd/mfmod.h}, which must be included at the start of the
source code.
@deftp {mfmod type} MFMOD_PARAM type string number
This type is defined as follows:
@example
typedef struct mfmod_param @{
mfmod_data_type type;
union @{
char *string;
long number;
mu_message_t message;
@};
@} MFMOD_PARAM;
@end example
The @code{type} fields defines the type of the data represented by the
object. Its possible values are:
@defvr {mfmod constant} mfmod_string
String data.
@end defvr
@defvr {mfmod constant} mfmod_number
Numeric data.
@end defvr
@defvr {mfmod constant} mfmod_message
A @code{mailutils} message object (@code{mu_message_t}).
@end defvr
The actual data are accessed as @code{string}, @code{number}, or
@code{message}, depending on the value of @code{type}.
@end deftp
The first parameter in the external function declaration, @var{count},
is the number of arguments passed to that function. Actual arguments are
passed in the @code{MFMOD_PARAM} array @var{param}. The function should
never modify its elements. If the function returns a value to MFL, it
must pass it in the @var{retval} parameter. For example, the
following code returns the numeric value @samp{1}:
@example
retval->type = mfmod_number;
retval->number = 1;
@end example
To return a string value, allocate it using @code{malloc},
@code{calloc} or a similar function, like this:
@example
retval->type = mfmod_string;
retval->string = strdup("text");
@end example
If a message is returned, it should be created using mailutils message
creation primitives. @command{Mailutils} will call
@code{mu_message_destroy} on it, when it is no longer used.
The return value (in the C sense) of the function is used to determine
whether it succeeded or not. Zero means success. Returning -1 causes
a runtime exception @code{e_failure} with a generic error text
indicating the names of the module and function that caused the
exception. Any other non-zero value is treated as a
@command{mailfromd} exception code (@pxref{Exceptions}). In this case
an additional textual explanation of the error can be supplied in the
@code{retval} variable, whose type must then be set to @code{mfmod_string}.
This explanation string must be allocated using @code{malloc}.
To facilitate error handling, the following functions are provided
(declared in the @file{mailfromd/mfmod.h} header file):
@deftypefn {mfmod} int mfmod_error (MFMOD_PARAM *@var{retval}, int @var{ecode}, @
char const *@var{fmt}, ...)
Raises exception @var{ecode} with the error message formatted from the
variadic arguments using @code{printf}-style format string @var{fmt}.
Example use:
@example
@group
if (error_condition)
return mfmod_error(retval, "error %s occurred", error_text);
@end group
@end example
@end deftypefn
@deftypefn {mfmod} int mfmod_error_argtype (MFMOD_PARAM *@var{param}, MFMOD_PARAM *@var{retval}, @
int @var{n}, int @var{exptype})
Reports argument type mismatch error (@code{e_inval} with
appropriately formatted error text). Arguments are:
@table @var
@item param
@itemx retval
The two arguments passed to the interface function.
@item n
0-based index of the erroneous argument in @var{param}.
@item exptype
Expected data type of @code{@var{param}[@var{n}]}.
@end table
You will seldom need to use this function directly. Instead, use the
@code{ASSERT_ARGTYPE} macro described below.
@end deftypefn
@deftypefn {mfmod} {char const *} mfmod_data_type_str(mfmod_data_type @var{type})
Returns the MFL name of the mfmod data type @var{type}.
@end deftypefn
The following convenience macros are provided for checking the number
of argument and their types and returning error if necessary:
@deffn {mfmod macro} ASSERT_ARGCOUNT (MFMOD_PARAM *@var{retval}, long @var{count}, long @var{expcount})
Assert that the number of arguments (@var{count}) equals the expected
number (@var{expcount}). If it does not, return the @code{e_inval}
exception with a descriptive error text.
@var{retval} and @var{count} are corresponding arguments from the
calling function.
@end deffn
@deffn {mfmod macro} ASSERT_ARGTYPE (MFMOD_PARAM *@var{param}, MFMOD_PARAM *@var{retval}, @
int @var{n}, int @var{exptype})
Check if the data type of the @var{n}th parameter
(i.e. @code{@var{param}[@var{n}]}) is @var{exptype} and return the
@code{e_inval} exception if it does not.
@end deffn
As an example, suppose you want to write an interface to the system
@code{crypt} function. The loadable library source,
@file{mfmod_crypt.c}, will look as follows:
@example
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mailfromd/mfmod.h>
#include <mailfromd/exceptions.h>
/*
* Arguments:
* param[0] - key string to hash.
* param[1] - salt value.
*/
int
cryptval(long count, MFMOD_PARAM *param, MFMOD_PARAM *retval)
@{
char *hash;
/* Check if input arguments are correct: */
ASSERT_ARGCOUNT(retval, count, 2);
ASSERT_ARGTYPE(param, retval, 0, mfmod_string);
ASSERT_ARGTYPE(param, retval, 1, mfmod_string);
/* Hash the key string. */
hash = crypt(param[0].string, param[1].string);
/* Return string to MFL */
retval->type = mfmod_string;
retval->string = strdup(hash);
/* Throw exception if out of memory */
if (retval->string == NULL)
return -1;
return 0;
@}
@end example
The exact way of building a loadable library from this source file
depends on the operating system. For example, on GNU/Linux you
would do:
@example
cc -shared -fPIC -DPIC -omfmod_crypt.so -lcrypt mfmod_crypt.c
@end example
The preferred and portable way of doing so is via @command{libtool}
(@pxref{Top, Shared library support for GNU,,libtool,Libtool}).
@command{Mailfromd} provides a special command @command{mfmodnew} that
creates infrastructure necessary for building loadable modules.
@xref{mfmodnew}.
@node Interface Module
@subsection Interface Module
@cindex mfmod, interface module
The interface module is responsible for loading the library, and
providing MFL wrappers over external functions defined in it.
For the first task, the @code{dlopen} function is provided. It takes
a single argument, the file name of the library to load. This can
be an absolute pathname, in which case it is used as is, or a relative
file name, which will be searched in the @dfn{library search path}
(@pxref{mfmod-path}). On success, the function returns the
@dfn{library handle}, which will be used in subsequent calls to
identify that library. On error, a runtime exception is signalled.
It is common to call the @code{dlopen} function in the @code{startup}
section of the interface module (@pxref{startup/shutdown}), so that
the library gets loaded at the program startup. For example:
@example
static number libh
prog startup
do
set libh dlopen("mfmod_crypt.so")
done
@end example
The function @code{dlcall} is provided to call a function from the
already loaded library. It is a variadic function with three
mandatory parameters:
@enumerate 1
@item The handle of the loadable library as returned by @code{dlopen}.
@item Name of the external function to call.
@item Type string
@end enumerate
The @dfn{type string} argument declares data types of the variable
arguments. It contains a single letter for each additional argument
passed to @code{dlcall}. The valid letters are:
@table @asis
@item s
The argument is of string type.
@item n
@itemx d
The argument is of numeric type.
@item m
The argument is of message type.
@end table
For example, the following will call the @code{cryptval} function
defined in the previous section (supposing @code{key} and @code{salt}
are two string MFL variables):
@example
set x dlcall(libh, "cryptval", "ss", key, salt)
@end example
The last letter in type string can be @samp{+} or @samp{*}. Both mean
that any number of arguments are allowed (all of the type given by
the penultimate type letter). The difference between the two is that
@samp{+} allows for one or more arguments, while @samp{*} allows for
zero or more arguments. For example, @samp{n+} means one or more
numeric arguments, and @samp{n*} means zero or more such arguments.
Both are intended to be used in variadic functions, e.g.:
@example
@group
func pringstddev(number ...)
returns number
do
return dlcall(libh, "stddev", "n*", $@@)
done
@end group
@end example
@kwindex void
The @code{dlcall} function returns the value returned by the
library function it invoked. If the library function returns
no meaningful value, it is recommended to use the @code{void}
type cast around the @code{dlcall} invocation (@pxref{void type
cast}). E.g.:
@example
@group
func out(string text)
do
void(dlcall(libh, "output", "s", text))
done
@end group
@end example
Without @code{void} type cast, the definition above will produce the
following warning when compiled:
@example
return from dlcall is ignored
@end example
@node mfmodnew
@subsection Creating a Mfmod Structure
@cindex mfmodnew
The @command{mfmodnew} provides a convenient start for writing a new
@i{mfmod}. Given a name of the planned module, this command creates
directory @file{mfmod_@var{name}} and populates it with the files
necessary for building the new module using GNU autotools, as well as
boilerplate files for the loadable library and interface module.
Let's see how to use it to create the @code{crypt} module outlined in
previous subsections.
First, invoke the command:
@example
$ @kbd{mfmodnew crypt}
mfmodnew: setting up new module in mfmod_crypt
@end example
Let's change to the new directory and see the files in it:
@example
$ @kbd{cd mfmod_crypt}
$ @kbd{ls}
Makefile.am configure.ac crypt.mfl mfmod_crypt.c
@end example
Now, open the @file{mfmod_crypt.c} file and add to it the definition
of the @code{cryptval} function (@pxref{Loadable Library}). Then, add
the interface function definition from @ref{Interface Module} to file
@code{crypt.mfl}.
The last thing to do is to edit @file{configure.ac}. The
@code{crypt} function requires the @file{libcrypt} library, so the
following line should be added to the @samp{Checks for libraries.}
section.
@example
AC_CHECK_LIB([crypt], [crypt])
@end example
Now, run @command{autoreconf}, as follows:
@example
$ @kbd{autoreconf -f -i -s}
@end example
It will bootstrap the autotools infrastructure, importing additional
files as necessary. Once done, you can build the project:
@example
$ @kbd{./configure}
$ @kbd{make}
@end example
Notice, that if the @code{autoreconf} stage ends abnormally with a
diagnostics like:
@example
configure.ac:21: error: possibly undefined macro: AC_MFMOD
@end example
@noindent
@findex mfmod.m4
that means that @command{autoconf} was unable to find the file
@file{mfmod.m4}, which provides that macro. That's because the
directory where this file is installed is not searched by
@command{autoreconf}. To fix this, supply the name of that directory
using the @option{-I} option. E.g. assuming @file{mfmod.m4} is
installed in @file{/usr/local/share}:
@example
$ @kbd{autoreconf -fis -I /usr/local/share/aclocal}
@end example
@menu
* mfmodnew invocation::
@end menu
@node mfmodnew invocation
@subsubsection mfmodnew invocation
The @command{mfmodnew} is invoked as:
@example
mfmodnew [@var{options}] @var{modname} [@var{dir}]
@end example
@noindent
where @var{modname} is the name of the new module and @var{dir} is the
directory where to store the module infrastructure files. Normally
you would omit @var{dir} altogether: in this case the utility will use
@file{mfmod_@var{modname}} as the directory name.
Options are:
@table @option
@item -C @var{dir}
Search for template files in @var{dir}, instead of the default
location.
@item -e @var{email}
Supply the author's email. The email is passed as argument to
the @code{AC_INIT} macro in @file{configure.ac}. By default it
is constructed as @samp{@var{username}@@@var{hostname}}. If it is
incorrect, you can either edit @file{configure.ac} afterwards, or
just supply the correct one using this option.
@item -q
Suppress informative messages.
@item -h
Display a short command line usage help.
@end table
@node Preprocessor
@section @acronym{MFL} Preprocessor
@cindex preprocessor
@cindex m4
Before compiling the script file, @command{mailfromd} preprocesses
it. The built-in preprocessor handles only file inclusion
(@pxref{include}), while the rest of traditional facilities, such as
macro expansion, are supported via @command{m4}, which is used as an
external preprocessor.
The detailed description of @command{m4} facilities lies far beyond
the scope of this document. You will find a complete user manual in
@ref{Top, GNU M4 manual, GNU M4, m4, GNU M4 macro processor}. For the
rest of this section we assume the reader is sufficiently
acquainted with @command{m4} macro processor.
@flindex pp-setup
@cindex preprocessor setup file
The external preprocessor is invoked with @option{-s} flag, instructing
it to include line synchronization information in its output, which
is subsequently used by @acronym{MFL} compiler for purposes of error
reporting. The initial set of macro definitions is supplied in
@dfn{preprocessor setup} file @file{pp-setup}, located in the library
search path@footnote{It is usually located in
@file{/usr/local/share/mailfromd/@value{VERSION}/include/pp-setup}.},
which is fed to the preprocessor input before the script file itself.
The default @file{pp-setup} file renames all @command{m4} built-in
macro names so they all start with the prefix @samp{m4_}@footnote{This
is similar to GNU m4 @option{--prefix-builtin} options. This approach
was chosen to allow for using non-GNU @command{m4} implementations as
well.}. It changes comment characters to @samp{/*}, @samp{*/} pair,
and leaves the default quoting characters, grave (@samp{`}) and acute
(@samp{'}) accents without change. Finally, @file{pp-setup} defines
several useful macros (@pxref{m4 macros}).
@node Configuring Preprocessor
@subsection Preprocessor Configuration
@cindex preprocessor configuration
@cindex configuring the preprocessor
The preprocessor is configured in the mailfromd configuration file,
using the preprocessor statement (@pxref{conf-preprocessor}). The
default settings correspond to the following configuration:
@example
@group
preprocessor @{
# Enable preprocessor
enable yes;
# Preprocessor command line stub.
command "m4 -s";
# Pass current include path to the preprocessor via -I options.
pass-includes false;
# Pass to the preprocessor the feature definitions via -D options
# as well as any -D/-U options from the command line
pass-defines true;
# Name of the preprocessor setup file. Unless absolute, it is
# looked up in the include path.
setup-file "pp-setup";
@}
@end group
@end example
@kwindex command
@kwindex preprocessor.command
@kwindex pass-includes
@kwindex preprocessor.pass-includes
If @code{pass-includes} is true, the @code{command} value
is augmented by zero or more @option{-I} options supplying it the
mailfromd include search path (@pxref{include search path}).
@kwindex pass-defines
@kwindex preprocessor.pass-defines
Furthermore, if @code{pass-defines} is set, zero or more
@option{-D} options defining optional features are passed to it (e.g.
@option{-DWITH_DKIM}) as well as any @option{-D} and @option{-U}
options from the mailfromd command line.
@kwindex setup-file
@kwindex preprocessor.setup-file
Unless the value of @code{setup-file} begins with a slash,
the file with this name is looked up in the current include search
path. If found, its absolute name is passed to the preprocessor as
first argument.
If it begins with a slash, it is passed to the preprocessor as is.
@node Preprocessor Usage
@subsection Preprocessor Usage
@cindex E, -E @r{option, described}
You can obtain the preprocessed output, without starting actual
compilation, using @option{-E} command line option:
@example
$ @kbd{mailfromd -E file.mfl}
@end example
The output is in the form of preprocessed source code, which is sent
to the standard output. This can be useful, among others, to debug
your own macro definitions.
Macro definitions and deletions can be made on the command line, by
using the @option{-D} and @option{-U} options, provided that their use
is allowed by the @code{pass-defines} preprocessor configuration
setting (@pxref{Configuring Preprocessor}. They have the following format:
@table @option
@xopindex{define, mailfromd, described}
@cindex D, -D @r{option, described}
@item -D @var{name}[=@var{value}]
@itemx --define=@var{name}[=@var{value}]
Define a symbol @var{name} to have a value @var{value}. If
@var{value} is not supplied, the value is taken to be the empty
string. The @var{value} can be any string, and the macro can be
defined to take arguments, just as if it was defined from within the
input using the @code{m4_define} statement.
For example, the following invocation defines symbol @code{COMPAT} to
have a value @code{43}:
@example
$ @kbd{mailfromd -DCOMPAT=43}
@end example
@xopindex{undefine, mailfromd, described}
@cindex U, -U @r{option, described}
@item -U @var{name}
@item --undefine=@var{name}
A counterpart of the @option{-D} option is the option @option{-U}
(@option{--undefine}). It undefines a preprocessor symbol whose name
is given as its argument. The following example undefines the symbol
@code{COMPAT}:
@example
$ @kbd{mailfromd -UCOMPAT}
@end example
@end table
The following two options are supplied mainly for debugging purposes:
@table @option
@xopindex{no-preprocessor, mailfromd, usage}
@item --no-preprocessor
Disables the external preprocessor.
@xopindex{preprocessor, mailfromd, usage}
@item --preprocessor[=@var{command}]
Use @var{command} as external preprocessor. If @var{command} is not
supplied, use the default preprocessor, overriding the @code{enable}
preprocessor configuration setting.
Be especially careful with this option, because @command{mailfromd}
cannot verify whether @var{command} is actually some kind of a
preprocessor or not.
@end table
@node m4 macros
@subsection Preprocessor Macros
@anchor{defined}
@deftypefn {M4 Macro} boolean defined (@var{identifier})
The @var{identifier} must be the name of an optional abstract
argument to the function. This macro must be used only within a function
definition. It expands to the @acronym{MFL} expression that yields
@code{true} if the actual parameter is supplied for @var{identifier}.
For example:
@example
func rcut(string text; number num)
returns string
do
if (defined(num))
return substr(text, length(text) - num)
else
return text
fi
done
@end example
This function will return last @var{num} characters of @var{text} if
@var{num} is supplied, and entire @var{text} otherwise, e.g.:
@example
@group
rcut("text string") @result{} "text string"
rcut("text string", 3) @result{} "ing"
@end group
@end example
Invoking the @code{defined} macro with the name of a mandatory argument
yields @code{true}
@end deftypefn
@deffn {M4 Macro} printf (@var{format}, @dots{})
Provides a @code{printf} statement, that formats its optional
parameters in accordance with @var{format} and sends the resulting
string to the current log output (@pxref{Logging and Debugging}).
@xref{String formatting}, for a description of @var{format}.
Example usage:
@example
printf('Function %s returned %d', funcname, retcode)
@end example
@end deffn
@deftypefn {M4 Macro} string _ (@var{msgid})
A convenience macro. Expands to a call to @code{gettext} (@pxref{NLS
Functions}).
@end deftypefn
@deffn {M4 Macro} string_list_iterate (@var{list}, @var{delim}, @var{var}, @var{code})
This macro intends to compensate for the lack of array data type
in @acronym{MFL}. It splits the string @var{list} into segments
delimited by string @var{delim}. For each segment, the @acronym{MFL}
code @var{code} is executed. The code can use the variable @var{var}
to refer to the segment string.
For example, the following fragment prints names of all existing
directories listed in the @env{PATH} environment variable:
@example
string path getenv("PATH")
string seg
string_list_iterate(path, ":", seg, `
if access(seg, F_OK)
echo "%seg exists"
fi')
@end example
Care should be taken to properly quote its arguments. In the code
below the string @code{str} is treated as a comma-separated list of
values. To avoid interpreting the comma as argument delimiter the
second argument must be quoted:
@example
string_list_iterate(str, `","', seg, `
echo "next segment: " . seg')
@end example
@end deffn
@deffn {M4 Macro} N_ (@var{msgid})
A convenience macro, that expands to @var{msgid} verbatim. It is
intended to mark the literal strings that should appear in the
@file{.po} file, where actual call to @code{gettext} (@pxref{NLS
Functions}) cannot be used. For example:
@example
/* Mark the variable for translation: cannot use gettext here */
string message N_("Mail accepted")
prog envfrom
do
@dots{}
/* Translate and log the message */
echo gettext(message)
@end example
@end deffn
@node Filter Script Example
@section Example of a Filter Script File
In this section we will discuss a working example of the filter
script file. For the ease of illustration, it is divided in several
sections. Each section is prefaced with a comment explaining its
function.
This filter assumes that the @file{mailfromd.conf} file contains the
following:
@example
relayed-domain-file (/etc/mail/sendmail.cw,
/etc/mail/relay-domains);
io-timeout 33;
database cache @{
negative-expire-interval 1 day;
positive-expire-interval 2 weeks;
@};
@end example
Of course, the exact parameter settings may vary, what is important
is that they be declared. @xref{Mailfromd Configuration}, for a
description of @command{mailfromd} configuration file syntax.
Now, let's return to the script. Its first part defines the
configuration settings for this host:
@example
#pragma regex +extended +icase
set mailfrom_address "<>"
set ehlo_domain "gnu.org.ua"
@end example
The second part loads the necessary source modules:
@example
@group
require 'status'
require 'dns'
require 'rateok'
@end group
@end example
Next we define @code{envfrom} handler. In the first two rules, it
accepts all mails coming from the null address and from the machines
which we relay:
@example
prog envfrom
do
if $f = ""
accept
elif relayed hostname($client_addr)
accept
elif hostname($client_addr) = $client_addr
reject 550 5.7.7 "IP address does not resolve"
@end example
Next rule rejects all messages coming from hosts with dynamic @acronym{IP}
addresses. A regular expression used to catch such hosts is not 100%
fail-proof, but it tries to cover most existing host naming patterns:
@example
@group
elif hostname($client_addr) matches
".*(adsl|sdsl|hdsl|ldsl|xdsl|dialin|dialup|\
ppp|dhcp|dynamic|[-.]cpe[-.]).*"
reject 550 5.7.1 "Use your SMTP relay"
@end group
@end example
Messages coming from the machines whose host names contain
something similar to an @acronym{IP} are subject to strict checking:
@example
elif hostname($client_addr) matches
".*[0-9]@{1,3@}[-.][0-9]@{1,3@}[-.][0-9]@{1,3@}[-.][0-9]@{1,3@}.*"
on poll host $client_addr for $f do
when success:
pass
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail
done
@end example
If the sender domain is relayed by any of the @indicateurl{yahoo.com}
or @indicateurl{nameserver.com} @samp{MX}s, no checks are performed. We
will greylist this message in @code{envrcpt} handler:
@example
@group
elif $f mx fnmatches "*.yahoo.com"
or $f mx fnmatches "*.namaeserver.com"
pass
@end group
@end example
Finally, if the message does not meet any of the above conditions,
it is verified by the standard procedure:
@example
else
on poll $f do
when success:
pass
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail
done
fi
@end example
At the end of the handler we check if the sender-client pair does
not exceed allowed mail sending rate:
@example
@group
if not rateok("$f-$client_addr", interval("1 hour 30 minutes"), 100)
tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
fi
done
@end group
@end example
Next part defines the @code{envrcpt} handler. Its primary purpose
is to greylist messages from some domains that could not be checked
otherwise:
@example
prog envrcpt
do
set gltime 300
if $f mx fnmatches "*.yahoo.com"
or $f mx fnmatches "*.namaeserver.com"
and not dbmap("/var/run/whitelist.db", $client_addr)
if greylist("$client_addr-$f-$rcpt_addr", gltime)
if greylist_seconds_left = gltime
tempfail 450 4.7.0
"You are greylisted for %gltime seconds"
else
tempfail 450 4.7.0
"Still greylisted for " .
%greylist_seconds_left . " seconds"
fi
fi
fi
done
@end example
@node Reserved Words
@section Reserved Words
@cindex reserved words
@cindex keywords
For your reference, here is an alphabetical list of all reserved
words:
@itemize @bullet
@item __defpreproc__
@item __defstatedir__
@item __file__
@item __function__
@item __line__
@item __major__
@item __minor__
@item __module__
@item __package__
@item __patch__
@item __preproc__
@item __statedir__
@item __version__
@item accept
@item add
@item and
@item alias
@item begin
@item break
@item bye
@item case
@item catch
@item const
@item continue
@item default
@item delete
@item discard
@item do
@item done
@item echo
@item end
@item elif
@item else
@item fi
@item fnmatches
@item for
@item from
@item func
@item if
@item import
@item loop
@item matches
@item module
@item next
@item not
@item number
@item on
@item or
@item pass
@item precious
@item prog
@item public
@item reject
@item replace
@item return
@item returns
@item require
@item set
@item static
@item string
@item switch
@item tempfail
@item throw
@item try
@item vaptr
@item when
@item while
@end itemize
Several keywords are context-dependent: @code{mx} is a keyword if it
appears before @code{matches} or @code{fnmatches}. Following strings
are keywords in @code{on} context:
@itemize
@item as
@item host
@item poll
@end itemize
The following keywords are preprocessor macros:
@itemize
@item defined
@item _ (an underscore)
@item N_
@end itemize
Any keyword beginning with a @samp{m4_} prefix is a reserved
preprocessor symbol.
@node Library
@chapter The MFL Library Functions
@include functions.texi
@node Using MFL Mode
@chapter Using the GNU Emacs MFL Mode
@cindex Emacs, @acronym{MFL} mode
@cindex GNU Emacs, @acronym{MFL} mode
@cindex @acronym{MFL} mode, GNU Emacs
@acronym{MFL} sources are usual @acronym{ASCII} files and you may
edit them with any editor you like. However, the best choice for this
job (as well as for many others) is, without doubt, GNU Emacs. To ease
the work of editing script files, the @command{mailfromd} package
provides a special Emacs mode, called @dfn{@acronym{MFL} mode}.
@flindex mfl-mode.el
@flindex site-start.el
@flindex ~/.emacs
@cindex Enabling @acronym{MFL} mode
@cindex @acronym{MFL} mode, enabling
@cindex MFL mode,
The elisp source file providing this mode, @file{mfl-mode.el}, is
installed automatically, provided that GNU Emacs is present on your
machine. To enable the mode, add the following lines to your Emacs setup
file (either system-wide @file{site-start.el}, or your personal one,
@file{~/.emacs}):
@smalllisp
@group
(autoload 'mfl-mode "mfl-mode")
(setq auto-mode-alist (append auto-mode-alist
'(("\\.mfl$" . mfl-mode))))
@end group
@end smalllisp
The first directive loads the @acronym{MFL} mode, and the second one
tells Emacs to apply it to any file whose name ends in
@samp{.mfl} suffix.
@cindex indentation, @acronym{MFL}, default
@acronym{MFL} mode provides automatic indentation and syntax
highlighting for @acronym{MFL} sources. The default indentation setup
is the same as the one used throughout this book:
@itemize @bullet
@item Handler and function definitions start at column 1;
@item A block statement, i.e.@: @samp{do}, @samp{done}, @samp{if},
@samp{else}, @samp{elif} and @samp{fi}, occupies a line by itself,
with the only exception that @samp{do} after an @samp{on} statement is
located on the same line with it;
@item A @samp{do} statement that follows function or handler
definition is placed in column 1.
@item Each subsequent level of nesting is indented two columns to the
right (@pxref{mfl-basic-offset}).
@item A closing statement (@samp{done}, @samp{else}, @samp{elif},
@samp{fi}) is placed at the same column as the corresponding opening
statement;
@item Branch statements (@samp{case} and @samp{when}) are placed in
the same column as their controlling keyword (@samp{switch} and
@samp{on}, correspondingly (@pxref{mfl-case-line-offset}).
@item Loop substatements (@pxref{Loops}) are offset 5 columns to the right
from the controlling @code{loop} keyword.
(@pxref{mfl-loop-statement-offset}). Continuation statements within
loop header are offset 5 columns from the indentation of their
controlling keyword, either @code{for} or @code{while}
(@pxref{mfl-loop-continuation-offset}).
@end itemize
@cindex Finding function definition
@cindex Navigating through function definitions
The mode provides two special commands that help navigate through
the complex filter scripts:
@table @kbd
@item C-M-a
Move to the beginning of current function or handler definition.
@item C-M-e
Move to the end of current function or handler definition.
@end table
Here, @dfn{current function or handler} means the one within which
your cursor currently stays.
You can use @kbd{C-M-e} repeatedly to walk through all function
and handler definitions in your script files. Similarly, repeatedly
pressing @kbd{C-M-a} will visit all the definitions in the opposite
direction (from the last up to the very first one).
@cindex Verifying script syntax
Another special command, @kbd{C-c C-c}, allows to verify the
syntax of your script file. This command runs @command{mailfromd} in
syntax check mode (@pxref{Testing Filter Scripts}) and displays its
output in a secondary window, which allows to navigate through
eventual diagnostic messages and to jump to source locations
described by them.
@cindex customization, Emacs
@cindex customization, @acronym{MFL} mode
All @acronym{MFL} mode settings are customizable. To change any of
them, press @kbd{M-x customize} and visit @samp{Environment/Unix/Mfl}
customization group. This group offers two subgroups: @samp{Mfl Lint
group} and @samp{Mfl Indentation group}.
@samp{Mfl Lint group} controls invocation of mailfromd by @kbd{C-c
C-c}. This group contains two variables:
@defvr {MFL-mode setting} mfl-mailfromd-command
The @command{mailfromd} to be invoked. By default, it is
@samp{mailfromd}. You will have to change it, if @command{mailfromd}
cannot be found using @env{PATH} environment variable, or if you wish
to pass it some special options. However, do not include
@option{--lint} or @option{-I} options in this variable. The
@option{--lint} option is given automatically, and include paths are
controlled by @code{mfl-include-path} variable (see below).
@end defvr
@defvr {MFL-mode setting} mfl-include-path
A list of directories to be appended to @command{mailfromd} include
search path (@pxref{include search path}). By default it is empty.
@end defvr
@samp{Mfl Indentation group} controls automatic indentation of
@acronym{MFL} scripts. This group contains the following settings:
@anchor{mfl-basic-offset}
@defvr {MFL-mode setting} mfl-basic-offset
This variable sets the basic indentation increment. It is set to 2,
by default, which corresponds to the following indentation style:
@example
prog envfrom
do
if $f = ""
accept
else
@dots{}
fi
done
@end example
@end defvr
@anchor{mfl-case-line-offset}
@defvr {MFL-mode setting} mfl-case-line-offset
Indentation offset for @code{case} and @code{when} lines, relative to
the column of their controlling keyword. The default is 0, i.e.:
@example
switch x
do
case 0:
@dots{}
default:
@dots{}
done
@end example
@end defvr
@defvr {MFL-mode setting} mfl-returns-offset
Indentation offset of @code{returns} and @code{alias} statements,
relative to the controlling @code{func} keyword. The default value is
2, which corresponds to:
@example
@group
func foo()
alias bar
returns string
@end group
@end example
@end defvr
@defvr {MFL-mode setting} mfl-comment-offset
Indentation increment for multi-line comments. The default value is
1, which makes:
@example
@group
/* first comment line
second comment line */
@end group
@end example
@end defvr
@anchor{mfl-loop-statement-offset}
@defvr {MFL-mode setting} mfl-loop-statement-offset
Indentation increment for parts of a @code{loop} statement. The
default value is 5, which corresponds to the following style:
@example
@group
loop for @var{stmt},
while @var{cond},
@var{incr}
do
@end group
@end example
@end defvr
@anchor{mfl-loop-continuation-offset}
@defvr {MFL-mode setting} mfl-loop-continuation-offset
If any of the @code{loop} parts occupies several lines, the
indentation of continuation lines relative to the first line is
controlled by @code{mfl-loop-continuation-offset}, which defaults
to 5:
@example
@group
loop for set n 0
set z 1,
while n != 10
or z != 2,
set n n + 1
@end group
@end example
@end defvr
@node Mailfromd Configuration
@chapter Configuring @command{mailfromd}
Upon startup, @command{mailfromd} checks if the file
@file{/etc/mailfromd.conf} exists.@footnote{The exact location is
determined at compile time: the @file{/etc} directory is the system
configuration directory set when building @command{mailfromd}
(@pxref{Building}).} If it does, the program attempts to retrieve
its configuration settings from that file.
The @file{mailfromd.conf} file must be written in the @dfn{GNU mailutils
configuration format}, as described in @ref{conf-syntax, Configuration
File Syntax,, mailutils, GNU Mailutils Manual}. This format can be
summarized as follows:
@table @asis
@item Comments
Inline comments begin with @samp{//} or @samp{#} and end at the end of
the line. Multiline comments are delimited by @samp{/*} and
@samp{*/}. Multiline comments cannot be nested, but can contain
inline comment markers.
@item Empty lines and whitespace
Empty lines are ignored. Whitespace characters (i.e. horizontal, vertical
space, and newline) are ignored, except as they serve to separate tokens.
@item Statements
A statement consists of a keyword and a value, separated by
whitespace. Statements terminate with a semicolon. E.g.
@example
pidfile /var/run/mailfromd.pid;
@end example
@item Block statements
A block statement consists of a keyword and a list of statements
enclosed in @samp{@{} and @samp{@}} characters. Optional @dfn{label}
can appear between the keyword and opening curly brace. E.g.:
@example
logging @{
syslog on;
facility mail;
@}
@end example
Block statement is not required to terminate with a semicolon,
although it is allowed to.
@item File Inclusion
The @code{include} statement causes
inclusion of the file listed as its value:
@example
include /usr/share/mailfromd/config.inc;
@end example
@end table
The @file{mailfromd.conf} file is used by all programs that form the
@samp{mailfromd} package, i.e. @command{mailfromd},
@command{calloutd}, @command{mfdbtool}, and @command{pmult}. Since the sets
of statements understood by each of them differ, special syntactic means are
provided to separate program-specific configurations from each other.
First of all, if the argument to @code{include} is a directory, then
the program will search that directory for a file with the same name
as the base name of the program itself. If found, this file will be
loaded after finishing parsing the @file{mailfromd.conf} file.
Otherwise, this statement is ignored.
Secondly, the special block statement @code{program @var{tag}} is
processed only if @var{tag} matches the base name of the program being
run. Again, it is processed after the main @file{mailfromd.conf}
file.
Thus, if you need to provide configuration for the @command{calloutd}
component, there are two ways of doing so. First, you can place it to
a file named @file{calloutd} placed in a separate directory (say,
@file{/etc/mailfromd.d}), and use the name of that directory in a
@code{include} statement in the main configuration file:
@example
include /etc/mailfromd.d;
@end example
Secondly, you can use the @code{program} statement as follows:
@example
program calloutd @{
...
@}
@end example
@menu
* conf-types:: Special Configuration Data Types
* conf-base:: Base Mailfromd Configuration
* conf-preprocessor:: Preprocessor Configuration
* conf-resolver:: DNS Resolver Configuration
* conf-server:: Server Configuration
* conf-milter:: Milter Connection Configuration
* conf-debug:: Logging and Debugging configuration
* conf-timeout:: Timeout Configuration
* conf-callout:: Call-out Configuration
* conf-priv:: Privilege Configuration
* conf-database:: Database Configuration
* conf-runtime:: Runtime Constants
* conf-mailutils:: Standard Mailutils Statements
@end menu
@node conf-types
@section Special Configuration Data Types
In addition to the usual data types (@pxref{Statements,,,
mailutils, GNU Mailutils Manual}), @command{mailfromd} configuration
introduces the following two special ones:
@table @asis
@item time interval specification
@cindex Time Interval Specification
@anchor{time interval specification}
The @dfn{time interval specification} is a string that defines an
interval, much the same way we do this in English: it consists of one
or more pairs @samp{number}-@samp{time unit}. For example, the
following are valid interval specifications:
@example
@group
1 hour
2 hours 35 seconds
1 year 7 months 2 weeks 2 days 11 hours 12 seconds
@end group
@end example
@noindent
The pairs can occur in any order, however unusual it may sound to a
human ear, e.g.@: @samp{2 days 1 year}. If the @samp{time unit} is
omitted, seconds are supposed.
@item Connection URL
@anchor{milter port specification}
@table @asis
@item unix://@var{file}
@itemx unix:@var{file}
@itemx local://@var{file}
@itemx local:@var{file}
A named pipe (socket).
@item inet://@var{address}:@var{port}
@itemx inet:@var{port}@@@var{address}
An @acronym{IP}v4 connection to host @var{address} at port @var{port}. Port
must be specified either as a decimal number or as a string representing
the port name in @file{/etc/services}.
@item inet6:@var{port}@@@var{address}
An @acronym{IP}v6 connection to host @var{address} at port @var{port}. This
port type is not yet supported.
@end table
@end table
@node conf-base
@section Base Mailfromd Configuration
@deffn {Mailfromd Conf} script-file file
Read filter script from @var{file}. By default it is
@file{@var{sysconfdir}/mailfromd.mfl}.
@end deffn
@deffn {Mailfromd Conf} setvar name value
Initialize @acronym{MFL} variable @var{name} to @var{value}.
@end deffn
@deffn {Mailfromd Conf} include-path path
Add directories to the list of directories to be searched for include
files. @xref{include search path}.
Argument is a list of directory names separated by colons. These
names are added to the search path before the default search path
directories, but after any directories added by the previous
@code{include-path} statements, if any.
@xref{--include-path}
@end deffn
@deffn {Mailfromd Conf} module-path path
Add directories to the module search path @xref{module search path}.
Argument is a list of directory names separated by colons. These
names are added to the search path before the default search path
directories, but after any directories added by the previous
@code{module-path} statements, if any.
@xref{--module-path}
@end deffn
@deffn {Mailfromd Conf} state-directory dir
Set program state directory. @xref{statedir}.
@end deffn
@deffn {Mailfromd Conf} relayed-domain-file file
Append domain names from the named @var{file} to the list of relayed
domains. This list can be inspected from @acronym{MFL} script using
the @samp{relayed} function (@pxref{relayed}).
The @var{file} argument is either a single file name or a
list of file names, e.g.:
@example
relayed-domain-file /etc/mail/sendmail.cw;
relayed-domain-file (/etc/mail/sendmail.cw, /etc/mail/relay-domains);
@end example
@end deffn
@deffn {Mailfromd Conf} source-ip ipaddr
Set source IP address for outgoing TCP connections.
@end deffn
@deffn {Mailfromd Conf} pidfile file
Set the name of the file to store PID value in. The file must be
writable for the user or group @command{mailfromd} runs as
(@pxref{conf-priv}).
@end deffn
@node conf-preprocessor
@section Preprocessor Configuration
@kwindex preprocessor
MFL preprocessor is configured using the @code{preprocessor}
statement (default values shown):
@example
@group
preprocessor @{
# Enable preprocessor
enable yes;
# Preprocessor command line stub.
command "m4 -s";
# Pass current include path to the preprocessor via -I options.
pass-includes false;
# Pass to the preprocessor the feature definitions via -D options
# as well as any -D/-U options from the command line
pass-defines true;
# Name of the preprocessor setup file. Unless absolute, it is
# looked up in the include path.
setup-file "pp-setup";
@}
@end group
@end example
Its substatements are:
@deffn {preprocessor} enable @var{bool}
Enable or disable preprocessor. If disabled in configuration file,
preprocessor can be forcefully enabled in the command line by using
the @option{--preprocessor} option.
@end deffn
@deffn {preprocessor} command @var{string}
The stub for the preprocessor command line. Before use, it will be
eventually augmented by @option{-I} and @option{-D} options, depending
on the @code{pass-includes} and @code{pass-defines} settings in this
section. This is described in detail in @ref{Configuring Preprocessor}.
@end deffn
@deffn {preprocessor} pass-includes @var{bool}
If @code{true} the directories from include search path
(@pxref{include search path} will be passed to the preprocessor via
the @option{-I} options.
@end deffn
@deffn {preprocessor} pass-defines @var{bool}
If @code{true}, the -D options defining optional features will be
passed to the preprocessor (e.g. @option{-DWITH_DKIM}), as well as any
@option{-D} and @option{-U} options from the @command{mailfromd} command line.
@end deffn
@deffn {preprocessor} setup-file @var{string}
Name of the preprocessor setup file (@pxref{Preprocessor}). Unless
@var{string} begins with a slash, the file with this name is looked up
in the current include search path (@pxref{include search path}). If
found, its absolute name is passed to the preprocessor as its first
argument.
If @var{string} begins with a slash, it is passed to the preprocessor
as is.
@end deffn
@node conf-resolver
@section DNS Resolver Configuration
@kwindex resolver
DNS resolver settings are configured using the @code{resolver}
compound statement:
@example
@group
resolver @{
config @var{filename};
max-cname-chain @var{num};
@}
@end group
@end example
@deffn {resolver} config @var{filename}
@findex /etc/resolv.conf
Name of the resolver configuration file to use, instead of the default
@file{/etc/resolv.conf}.
@end deffn
@anchor{CNAME chains}
@deffn {resolver} max-cname-chain @var{num}
@cindex CNAME chains
Maximum allowed length of a DNS @dfn{CNAME chain} that will be
followed. A @dfn{CNAME chain} is a sequence of @code{CNAME} records
pointing to another @code{CNAME}s. Although CNAME chains are not
considered a good practice, many sites still use them. By default the
@command{mailfromd} resolver allows at most one CNAME record pointing
to a CNAME (this corresponds to @code{max-cname-chain 2}). If you
need to follow longer chains, raise this value. Note however, that
using values greater than 5 is not a good idea, anyway.
@end deffn
@node conf-server
@section Server Configuration
A single @command{mailfromd} daemon can run several @dfn{servers}.
These are configured in the following statement:
@example
@group
server @var{type} @{
id @var{name};
listen @var{url};
backlog @var{num};
max-instances @var{num};
single-process @var{bool};
reuseaddr @var{bool};
option @var{list};
default @var{bool};
callout @var{url};
acl @{ @dots{} @}
@}
@end group
@end example
@deffn {Mailfromd Conf} server @var{type}
Define a server. The @var{type} is either @samp{milter} or
@samp{callout}. @xref{SMTP Timeouts}, for a description of various
types of servers.
The substatements in the @code{server} block provide parameters for
configuring this server.
@end deffn
@deffn {server} id name
Assign an identifier to this server. This identifier is used as
a suffix to syslog tag (@pxref{syslog tag}) in messages related to
this server. For example, if a server block had the following
statement in it:
@example
id main;
@end example
@noindent
then all messages related to this server will be marked with tag
@samp{mailfromd#main}.
The part before the @samp{#} is set using the @code{tag} statement
in @code{logging} block (@pxref{Logging Statement, Mailutils
Configuration File,, mailutils, GNU Mailutils Manual}).
This identifier can be inspected from the MFL code using the
@code{milter_server_id} variable (@pxref{milter_server_id}).
@end deffn
@deffn {server} listen url
Listen for connections on the given @acronym{URL}.
@xref{milter port specification}, for a description of allowed
@var{url} formats.
Example:
@example
listen inet://10.10.10.1:3331;
@end example
The MFL code can access its address using the
@code{milter_server_address} and @code{milter_server_family} variables.
@end deffn
@deffn {server} backlog @var{num}
Configures the size of the queue of pending connections. Default
value is 8.
@end deffn
@deffn {server} max-instances number
Sets the maximum number of instances allowed for this server.
@end deffn
@deffn {server} single-process bool
When set to @samp{yes}, this server will run in @dfn{single-process}
mode, i.e. it will not fork sub-processes to serve requests. This
option is meant exclusively to assist in debugging
@command{mailfromd}. Don't use it for anything else but for
debugging!
@end deffn
@deffn {server} reuseaddr bool
When set to @samp{yes}, @command{mailfromd} will attempt to reuse
existing socket addresses. This is the default behavior.
@end deffn
If the server @var{type} is @samp{callout}, the following statement
is also allowed:
@deffn {server} option @var{list}
Configures server options. As of version @value{VERSION} only one
option is defined:
@table @asis
@item default
Mark this server as the default one. This means it will be used by
every milter server that doesn't define the @code{callout-url}
statement.
@end table
@end deffn
@deffn {server} default bool
When set to @samp{yes}, this server is marked as a default callout server
for all milter servers declared in the configuration. This is
equivalent to @code{option default}.
@end deffn
if the server @var{type} is @samp{milter}, you can use the following
statement to query a remote callout server:
@anchor{configuring default callout server}
@deffn {server} callout url
Use a callout server at @var{url} (@pxref{milter port specification}).
@end deffn
You can also set a @dfn{global callout server}, which will be used
by all milter servers that do not set the @code{callout} statement:
@deffn {Mailfromd Conf} callout-url url
Set global callout server. @xref{milter port specification}, for
allowed @var{url} formats.
@end deffn
@node conf-milter
@section Milter Connection Configuration
@deffn {Mailfromd Conf} milter-timeout time
Sets the timeout value for connection between the filter
and the @acronym{MTA}. Default value is @value{GACOPYZ_TIMEOUT} seconds. You
normally do not need to change this value.
@end deffn
@deffn {Mailfromd Conf} acl
This block statement configures @dfn{access control list} for
incoming Milter connections. @xref{ACL Statement, Mailutils
Configuration File,, mailutils, GNU Mailutils Manual}, for a
description of its syntax. E.g.:
@example
acl @{
allow from 10.10.10.0/24;
deny from any;
@}
@end example
@end deffn
@node conf-debug
@section Logging and Debugging configuration
@deffn {Mailfromd Conf} logger mech
Set default logger mechanism. Allowed values for
@var{mech} are:
@table @asis
@item stderr
Log everything to the standard error.
@item syslog
Log to syslog.
@item syslog:async
Log to syslog using the asynchronous syslog implementation.
@end table
@xref{Logging and Debugging}, for a detailed discussion.
See also @ref{syslog-async, Using non-blocking syslog}, for
information on how to set default syslog implementation at compile time.
@end deffn
@deffn {Mailfromd Conf} debug spec
Set @command{mailfromd} debug verbosity level. The @var{spec}
must be a valid debugging level specification (@pxref{debugging level specification}).
@end deffn
@deffn {Mailfromd Conf} stack-trace bool
Enables stack trace dumps on runtime errors. This feature is
useful for locating the source of an error, especially in complex
scripts. @xref{tracing runtime errors}, for a detailed description.
@end deffn
@deffn {Mailfromd Conf} trace-actions bool
Enable action tracing. If @var{bool} is @samp{true},
@command{mailfromd} will log all executed actions. @xref{Logging and
Debugging}, for a detailed description of action tracing.
@end deffn
@deffn {Mailfromd Conf} trace-program modlist
Enable program instruction tracing for modules in @var{modlist},
a comma-separated list of source code modules, e.g.:
@example
trace-program (bi_io,bi_db);
@end example
This statement enables tracing for functions from modules
@file{bi_io.c} and @file{bi_db.c} (notice, that you need not give file
suffixes).
This tracing is useful for debugging @command{mailfromd}, but is not
advised to use otherwise, since it is very time-costly.
@end deffn
@deffn {Mailfromd Conf} transcript bool
Enable transcripts of call-out @acronym{SMTP} sessions.
@xref{SMTP transcript}, for a detailed description of
@acronym{SMTP} transcripts.
@end deffn
@node conf-timeout
@section Timeout Configuration
The @acronym{SMTP} timeouts used in callout sessions are configured via
@code{smtp-timeout} statement:
@subheading Syntax
@kwindex smtp-timeout
@example
@group
smtp-timeout @var{type} @{
connection @var{interval};
initial-response @var{interval};
helo @var{interval};
mail @var{interval};
rcpt @var{interval};
rset @var{interval};
quit @var{interval};
@}
@end group
@end example
@deffn {Mailfromd Conf} smtp-timeout @var{type}
Declare @acronym{SMTP} timeouts of the given @var{type}, which may be
@samp{soft} or @samp{hard}.
Callout @acronym{SMTP} sessions initiated by polling functions, are
controlled by two sets of timeouts: @samp{soft} and @samp{hard}.
@dfn{Soft timeouts} are used by the mailfromd milter servers. @dfn{Hard
timeouts} are used by callout servers (@pxref{callout server}).
When a soft timeout is exceeded, the calling procedure is
delivered an @samp{e_temp_failure} exception and the session is
scheduled for processing by a callout server. The latter re-runs the
session using hard timeouts. If a hard timeout is exceeded, the
address is marked as @samp{not_found} and is stored in the cache
database with that status.
Normally, soft timeouts are set to shorter values, suitable for use
in @acronym{MFL} scripts without causing excessive delays. Hard
timeouts are set to large values, as requested by RFC 2822 and
guarantee obtaining a definite answer (see below for the default
values).
@end deffn
@subheading Statements
The @var{time} argument for all @code{smtp-timeout} sub-statements
is expressed in time interval units, as described in @ref{time
interval specification}.
@deffn {smtp-timeout} connection time
Sets initial connection timeout for callout tests. If the
connection is not established within this time, the corresponding
callout function returns temporary failure.
@end deffn
@deffn {smtp-timeout} initial-response time
Sets the time to wait for the initial @acronym{SMTP} response.
@end deffn
@deffn {smtp-timeout} helo time
Timeout for a response to @samp{HELO} (or @samp{EHLO}) command.
@end deffn
@deffn {smtp-timeout} mail time
Timeout for a response to @samp{MAIL} command.
@end deffn
@deffn {smtp-timeout} rcpt time
Timeout for a response to @samp{RCPT} command.
@end deffn
@deffn {smtp-timeout} rset time
Timeout for a response to @samp{RSET} command.
@end deffn
@deffn {smtp-timeout} quit time
Timeout for a response to @samp{QUIT} command.
@end deffn
@subheading Default Values
The default timeout settings are:
@float Table, smtp-timeouts
@caption{Default SMTP timeouts}
@multitable @columnfractions 0.5 0.25 0.25
@headitem Timeout @tab Soft @tab Hard
@item connection @tab 10s @tab 5m
@item initial-response @tab 30s @tab 5m
@item helo @tab I/O @tab 5m
@item mail @tab I/O @tab 10m
@item rcpt @tab I/O @tab 5m
@item rset @tab I/O @tab 5m
@item quit @tab I/O @tab 2m
@end multitable
@end float
@deffn {Mailfromd Conf} io-timeout time
Sets a general @acronym{SMTP I/O} operation timeout. This timeout
is used as the default for entries marked with @samp{I/O} in the above
table. The default is 3 seconds.
@end deffn
@node conf-callout
@section Call-out Configuration
@deffn {Mailfromd Conf} ehlo-domain string
Sets default domain used in @samp{EHLO} (or @samp{HELO})
@acronym{SMTP} command when probing the remote host. This value can be
overridden by @samp{from} parameter to @command{poll} command
(@pxref{poll}).
This statement assigns the value @var{string} to the
@samp{ehlo_domain} variable (@pxref{ehlo_domain}), and is therefore
equivalent to
@example
setvar ehlo_domain @var{string};
@end example
@end deffn
@deffn {Mailfromd Conf} mail-from-address string
Sets default email addresses used in @samp{MAIL FROM:}
@acronym{SMTP} command when probing the remote host. This value can be
overridden by @samp{as} parameter to @command{poll} command
(@pxref{poll}).
This statement assigns the value @var{string} to the
@samp{mailfrom_address} variable (@pxref{mailfrom_address}), and is therefore
equivalent to
@example
setvar mailfrom_address @var{string};
@end example
@end deffn
@deffn {Mailfromd Conf} enable-vrfy bool
@cindex VRFY, SMTP statement
Enables the use of SMTP VRFY statement prior to normal callout
sequence. If VRFY is supported by the remote server,
@command{mailfromd} relies on its reply and does not perform normal
callout.
The use of this statement is not recommended, because many existing
VRFY implementations always return affirmative result, no matter is
the requested email handled by the server or not.
The default is @code{enable-vrfy no}, i.e.@: VRFY is disabled.
@end deffn
@deffn {Mailfromd Conf} smtp-starttls string
@cindex STARTTLS in SMTP callout sessions
@cindex TLS support
Configures whether to issue the @code{STARTTLS} command if the mail
server offers such capability. Allowed values are:
@defvr {smtp-starttls value} never
Never use @code{STARTTLS}.
@end defvr
@defvr {smtp-starttls value} always
Always use @code{STARTTLS} if supported by the server.
@end defvr
@defvr {smtp-starttls value} ondemand
Use @code{STARTTLS} only if @code{MAIL FROM:} command failed with the code
530 (Authorization required).
@end defvr
Default is @samp{ondemand}.
Notice, that the @code{smtp-starttls} feature depends on whether
GnuTLS support is available in @code{libmailutils}. You can check
whether it is available by inspecting the output of @code{mailfromd
--show-defaults} (@pxref{Examining Defaults}): if so, the
@samp{optional features} line will contain the word @samp{STARTTLS}.
@end deffn
@deffn {Mailfromd Conf} tls @{ ... @}
Configures TLS settings for the callout. This is a compound
statement. The two most important statements in this compound are:
@deffn {Mailfromd TLS} ssl-priorities @var{string}
Configures the TLS session's handshake algorithms and options in a
compact, easy-to-use format. @xref{Priority strings,,,GnuTLS,GnuTLS},
for a detailed description of the priority string format.
Default value is @samp{NORMAL:%COMPAT}. You may need to adjust it in
order to work with older or misconfigured servers, e.g.:
@example
@group
tls @{
ssl-priorities "LEGACY:%COMPAT";
@}
@end group
@end example
@end deffn
@deffn {Mailfromd TLS} handshake-timeout @var{n}
Sets the timeout for TLS handshake to @var{n} seconds.
@end deffn
The remaining three statements are not of much importance for
callout. They are described here for completeness sake:
@deffn {Mailfromd TLS} ssl-ca-file file
Specifies the pathname of the certificate authority file.
@end deffn
@deffn {Mailfromd TLS} ssl-certificate-file file
Specifies the pathname of the certificate file.
@end deffn
@deffn {Mailfromd TLS} ssl-key-file file
Specifies the pathname of the certificate key file.
@end deffn
@end deffn
@node conf-priv
@section Privilege Configuration
@deffn {Mailfromd Conf} user name
Switch to this user's privileges after startup.
@xref{Starting and Stopping}, for a discussion of
the privileges @command{mailfromd} runs under and the options that
affect them. See also @code{group} below.
@end deffn
@deffn {Mailfromd Conf} group name
Retain the supplementary group @var{name} when switching to user
privileges. By default @command{mailfromd} clears the list of
supplementary groups when switching to user privileges, but this statement
allows to retain the given group. It can be specified multiple times
to retain several groups. This option may be necessary to
maintain proper access rights for various files. @xref{Starting and
Stopping}.
@end deffn
@node conf-database
@section Database Configuration
@subheading Syntax
@kwindex database
@example
database @var{dbname} @{
file @var{name};
enable @var{bool};
expire-interval @var{interval};
positive-expire-interval @var{interval};
negative-expire-interval @var{interval};
@}
@end example
@deffn {Mailfromd Conf} database dbname
The @code{database} statement controls run-time parameters of
the @acronym{DBM} database identified by @var{dbname}. Allowed
values for the latter are:
@table @code
@item cache
Callout cache database.
@item rate
Sending rate database. @xref{Sending Rate}.
@item tbf
Token-bucket filter database. @xref{TBF}.
@item greylist
Greylisting database. @xref{Greylisting}.
@end table
@end deffn
@subheading Statements
@deffn {database} file name
@cindex DBM scheme
@anchor{DBM scheme}
Set the database file name. The file name can be prefixed with the
@dfn{DBM scheme}, which indicates the desired DBM implementation to
use. The scheme consists of DBM type name followed by a colon and
two slashes. To obtain the list of available DBM types,
run @command{mailfromd --show-defaults} (@pxref{Examining Defaults})
and examine the @samp{supported database types:} line, e.g.:
@example
@group
$ @kbd{mailfromd --show-defaults | grep '^supported database types:'}
supported database types:gdbm, bdb
@end group
@end example
@cindex default database type
@cindex DBM implementation, default
If no DBM scheme is supplied, the default DBM implementation will be
used. The default implementation is set up on compile time and
can be inspected using the following command:
@example
mailfromd --show-defaults | grep '^default database type:'
@end example
The part that follows DBM scheme can be either absolute or relative
file name. Absolute file name (begining with @samp{/}) is used
verbatim. Relative file name (i.e. the name that begins with any
character except @samp{/}) is modified by prepending it with the
mailfromd local state directory (@pxref{statedir}).
To illustrate the above, suppose that the local state directory is
@file{/var/mailfromd} and default DBM implementation is @code{gdbm}. Then:
@multitable @columnfractions 0.3 0.3 0.2
@headitem Value of @code{file} @tab Resulting filename @tab DBM type
@item @code{tbf.db} @tab /var/mailfromd/tbf.db @tab GDBM
@item @code{bdb://tbf.db} @tab /var/mailfromd/tbf.db @tab Berkeley DB
@item @code{/run/tbf.db} @tab /run/tbf.db @tab GDBM
@item @code{bdb:///run/tbf.db} @tab /run/tbf.db @tab Berkeley DB
@end multitable
@end deffn
@deffn {database} enable bool
Enable or disable this database.
@end deffn
@deffn {database} expire-interval time
@anchor{expire-interval-conf}
Set the expiration interval for this database @var{dbname}.
@xref{time interval specification}, for a description of @var{time}
format.
@end deffn
@deffn {database} positive-expire-interval time
This statement is valid only for @samp{cache} database. It sets
the expiration interval for positive (@samp{success}) cache entries.
@end deffn
@deffn {database} negative-expire-interval time
This statement is valid only for @samp{cache} database, where
it sets expiration interval for negative (@samp{not_found}) cache entries.
@end deffn
@subheading Additional Statements
@deffn {Mailfromd Conf} database-type @var{type}
Set default database type. @var{type} is one of the database types
supported by mailutils (i.e., for Mailutils 3.0: @samp{gdbm},
@samp{ndbm}, @samp{bdb}, @samp{kc}, and @samp{tc}). Run
@example
mailfromd --show-defaults | grep 'supported databases:'
@end example
@noindent
to get a list of type names supported by your build of
@command{mailfromd} (@pxref{Examining Defaults}).
@end deffn
@deffn {Mailfromd Conf} database-mode @var{mode}
Defines file mode for newly created database files. @var{mode} must
be a valid file mode in octal.
@end deffn
@node conf-runtime
@section Runtime Constants Configuration
@deffn {Mailfromd Conf} runtime @{ statements @}
The statements in the @code{runtime} section configure various values
used by @acronym{MFL} builtin functions.
@end deffn
@deffn {runtime} max-streams number
Sets the maximum number of stream descriptors that can be opened
simultaneously. Default is @value{MAX_IOSTREAMS}. @xref{I/O functions}.
@end deffn
@deffn {runtime} max-open-mailboxes number
Sets the maximum number of available mailbox descriptors. This value
is used by @acronym{MFL} mailbox functions (@pxref{Mailbox functions}).
@end deffn
@deffn {runtime} max-open-messages number
Sets the maximum number of messages that can be opened simultaneously
using the @code{mailbox_get_message} function. @xref{Message
functions}, for details.
@end deffn
@deffn {runtime} max-mfmods number
Maximum number of dynamically loaded modules (@dfn{mfmod}s) that can
be used simultaneously. @xref{mfmod}.
@end deffn
@deffn {runtime} mfmod-path string
@anchor{mfmod-path}
Search path for dynamically loaded modules -- a colon-delimited list
of directories where to look for libraries loaded using the
@code{dlopen} function (@pxref{dlopen}). Default @code{mfmod-path}
value is @code{@var{prefix}/mailfromd/lib}, where @var{prefix} is the
installation prefix directory. @xref{mfmod}, for details.
@end deffn
@node conf-mailutils
@section Standard Mailutils Statements
The following standard Mailutils statements are understood:
@multitable @columnfractions 0.3 0.6
@headitem Statement @tab Reference
@item auth @tab @xref{auth statement, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@item debug @tab @xref{debug statement, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@item include @tab @xref{include, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@item logging @tab @xref{logging statement, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@item mailer @tab @xref{mailer statement, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@item locking @tab @xref{locking statement, Mailutils Configuration File,,
mailutils, GNU Mailutils Manual}.
@end multitable
@node Invocation
@chapter @command{Mailfromd} Command Line Syntax
@cindex invocation
@cindex command line, @command{mailfromd} invocation syntax
The @command{mailfromd} binary is started from the command line
using the following syntax (brackets indicate optional parts):
@example
$ @kbd{mailfromd [@var{options}] [@var{asgn}] [@file{@var{script}}]}
@end example
@noindent
The meaning of each invocation part is described in the table below:
@table @var
@item options
The command line options (@pxref{options}).
@item asgn
Sendmail macro assignments. These are currently meaningful only with
the @option{--test} option (@pxref{test mode}), but this
may change in the future. Each assignment has the form:
@example
@var{var}=@var{value}
@end example
@noindent
where @var{var} is the name of a Sendmail macro and @var{value} is the
value to be assigned to it.
@item script
The file name of the filter script, if other than the default one.
@end table
@menu
* options:: Command Line Options.
* Starting and Stopping:: How to Start and Shut Down the Daemon.
@end menu
@node options
@section Command Line Options.
@menu
* Operation Modifiers::
* General Settings::
* Preprocessor Options::
* Timeout Control::
* Logging and Debugging Options::
* Informational Options::
@end menu
@node Operation Modifiers
@subsection Operation Modifiers
@table @option
@opsummary{daemon, mailfromd}
@item --daemon
Run in daemon mode (default).
@opsummary{run, mailfromd}
@item --run[=@var{start}]
Load the script named in the command line and execute the function
named @var{start}, or @samp{main}, if @var{start} is not given.
The full invocation syntax for this mode is:
@example
mailfromd [@var{options}] --run[=@var{start} \
[@var{macro}=@var{value}] [@var{file} @var{args...}]
@end example
Its parts are:
@table @var
@item options
Any mailfromd options needed to tune its behavior.
@item start
Start symbol. Defaults to @code{main}.
@item @var{macro}=@var{value}
Any number of Sendmail macro definitions. @var{macro} is the macro
name and @var{value} is the value to initialize it to.
@item file
Script file name. If omitted, default script file will be used
(@pxref{default script file}).
@item args...
Command line arguments that will be pased to the @var{start} MFL
function as parameters.
@end table
The @var{start} function must be defined as:
@example
func @var{start} (...) returns number
@end example
@xref{Run Mode}, for a detailed discussion of this feature.
@opsummary{test, mailfromd}
@item -t[@var{state}]
@itemx --test[=@var{state}]
Run in test mode. @xref{Testing Filter Scripts}. Default @var{state} is
@samp{envfrom}. This option implies @option{--stderr} (@pxref{--stderr}).
@end table
@ignore
------------------------------------------------------------------------
-- This is to pacify make check-docs.
-- These options are reserved for future use
@opindex load, --load, mailfromd option
@opindex load-dir, --load-dir, mailfromd option
@opindex compile, --compile, mailfromd option
------------------------------------------------------------------------
@end ignore
@node General Settings
@subsection General Settings
@table @option
@opsummary{callout-socket, mailfromd}
@item --callout-socket=@var{string}
Set socket for the default callout server.
This is mainly useful together with the @option{--mtasim} option.
@opsummary{echo, mailfromd}
@anchor{echo option}
@item --echo
@itemx --echo=@var{file}
This option controls where the output of the @code{echo} statement
(@pxref{Echo}) goes when @command{mailfromd} is run in test mode,
i.e. using the @option{--test} or @option{--run} options
(@pxref{Testing Filter Scripts}).
When used without argument it directs the @code{echo} output to the
standard output stream. If an argument is supplied (second form
above), the output goes to the named file. The file will be created
if it doesn't exist. Notice, that the use of @samp{=} is compulsory
when specifying an argument. A single dash as a filename means
standard output, so that @option{--echo=-} and @option{--echo} are
equivalent.
@opsummary{foreground, mailfromd}
@item --foreground
Stay in foreground. When given this option, @command{mailfromd} will
not disconnect itself from the controlling terminal and will run in
the foreground.
@opsummary{group, mailfromd}
@item -g @var{name}
@itemx --group=@var{name}
Retain the group @var{name} when switching to user
privileges. @xref{Starting and Stopping}.
This option complements the @code{group} configuration statement
(@pxref{conf-priv, group}).
@opsummary{include-path, mailfromd}
@item --include-path=@var{dir}
@itemx -I @var{dir}
Add the directory @var{dir} to the list of directories to be searched
for include files (@pxref{include search path}). This setting affects
the @code{#include} statement (@pxref{include}) and is used to locate
preprocessor setup file (@pxref{Preprocessor}). If enabled in the
configuration (@pxref{conf-preprocessor, pass-includes}), directories
from the include search path will also be added to the preprocessor
command line as additional @option{-I} options.
@opsummary{module-path, mailfromd}
@item --module-path=@var{dir}
@itemx -P @var{dir}
Add the directory @var{dir} to the list of directories searched for
MFL module files (@pxref{module search path}). This setting affects
the @code{require} and @code{import} MFL statements. @xref{Modules}.
@opsummary{mailer, mailfromd}
@item --mailer=@var{url}
@itemx -M @var{url}
Set the @acronym{URL} of the mailer to use. @xref{Mail Sending
Functions}.
@opsummary{mtasim, mailfromd}
@item --mtasim
This option is reserved for use by @command{mtasim} (@pxref{mtasim}).
@opsummary{optimize, mailfromd}
@item -O[@var{level}]
@itemx --optimize[=@var{level}]
Set optimization level for code generator. Two levels are
implemented: @samp{0}, meaning no optimization, and @samp{1}, meaning
full optimization.
@opsummary{port, mailfromd}
@opsummary{milter-socket, mailfromd}
@item -p @var{string}
@itemx --port=@var{string}
@itemx --milter-socket=@var{string}
Set communication socket. Overrides the @code{listen} configuration
statement, which you are advised to use instead (@pxref{conf-server, listen}).
@opsummary{pidfile, mailfromd}
@item --pidfile=@var{file}
Set pidfile name. Overrides the @code{pidfile} configuration
statement, which you are advised to use instead (@pxref{conf-base, pidfile}).
@opsummary{relayed-domain-file, mailfromd}
@item --relayed-domain-file=@var{file}
@anchor{option relay}
Read relayed domains from @var{file}. Overrides the
@code{relayed-domain-file} configuration statement (@pxref{conf-base,
relayed-domain-file}), which you are advised to use instead.
@xref{Avoiding Verification Loops}, and the description of
@code{relayed} function (@pxref{relayed}) for more information.
@opsummary{resolv-conf-file, mailfromd}
@item --resolv-conf-file=@var{file}
Read resolver settings from @var{file}, instead of the default
@file{/etc/resolv.conf}.
@opsummary{state-directory, mailfromd}
@item --state-directory=@var{dir}
Set new program state directory. @xref{statedir, Local state directory}, for
the description of this directory and its purposes. This option
overrides the settings of @code{state-directory} configuration statement,
described in @ref{conf-base, state-directory}.
@opsummary{source-ip, mailfromd}
@item -S @var{ip}
@itemx --source-ip=@var{ip}
Set source address for @acronym{TCP} connections. Overrides the
@samp{source-ip} configuration statement, which you are advised to use
instead (@pxref{conf-base, source-ip}).
@opsummary{user, mailfromd}
@item -u @var{name}
@itemx --user @var{name}
Switch to this user privileges after startup. Overrides the @code{user}
configuration file statement, which you are advised to use instead
(@pxref{conf-priv, user}). Default user is @samp{@value{DEFAULT_USER}}.
@opsummary{variable, mailfromd}
@item -v @var{var}=@var{value}
@itemx --variable @var{var}=@var{value}
Assign @var{value} to the global variable @var{var}. The variable
must be declared in your startup script. @xref{overriding initial
values}, for a detailed discussion of this option.
@ignore
-- Reserved for future use
@opindex compile
@end ignore
@end table
@node Preprocessor Options
@subsection Preprocessor Options
Following command line options control the preprocessor
feature. @xref{Preprocessor}, for a detailed discussion of these.
@table @option
@opsummary{no-preprocessor, mailfromd}
@item --no-preprocessor
Do not run the preprocessor.
@opsummary{preprocessor, mailfromd}
@item --preprocessor[=@var{command}]
If @var{command} is supplied, use it as the external preprocessor
instead of the default. This overrides the
@code{preprocessor.command} setting (@pxref{conf-preprocessor}).
If used without arguments, forces the use of the configured
preprocessor. This form is intended as a way to manually enable the
use of the preprocessor if disabled in the configuration.
@opsummary{define, mailfromd}
@cindex D, -D @r{option, summary}
@item -D @var{name}[=@var{value}]
@itemx --define=@var{name}[=@var{value}]
Define a preprocessor symbol @var{name} to have a value @var{value}.
This option is ignored if the @code{preprocessor.pass-defines}
configuration setting is @code{false}.
@opsummary{undefine, mailfromd}
@cindex U, -U @r{option, summary}
@item -U @var{name}
@itemx --undefine=@var{name}
Undefine the preprocessor symbol @var{name}.
This option is ignored if the @code{preprocessor.pass-defines}
configuration setting is @code{false}.
@cindex E, -E @r{option, summary}
@item -E
Stop after the preprocessing stage; do not run the compiler proper.
The output is in the form of preprocessed source code, which is sent
to the standard output.
@end table
@node Timeout Control
@subsection Timeout Control
@xref{time interval specification}, for information on @var{interval} format.
@table @option
@opsummary{milter-timeout, mailfromd}
@item --milter-timeout=@var{interval}
Set @acronym{MTA} connection timeout. Overrides @code{milter-timeout}
statement in the @command{mailfromd} configuration file, which you are
advised to use instead (@pxref{conf-milter, milter-timeout}).
@end table
@node Logging and Debugging Options
@subsection Logging and Debugging Options
@table @option
@opsummary{location-column, mailfromd}
@item --location-column
@itemx --no-location-column
Mention column number in error messages. @xref{Testing Filter
Scripts,, location-column}. Use @option{--no-location-column} to
disable
@opsummary{debug, mailfromd}
@item -d @var{string}
@itemx --debug=@var{string}
Set debugging level. @xref{Logging and Debugging}.
@opsummary{dump-code, mailfromd}
@item --dump-code
Parse and compile the script file and dump the disassembled
listing of the produced code to the terminal. @xref{Logging and Debugging}.
@opsummary{dump-grammar-trace, mailfromd}
@item --dump-grammar-trace
Enable debugging the script file parser. While parsing the
file, the detailed dump of the parser states and tokens seen will be
output.
@opsummary{dump-lex-trace, mailfromd}
@item --dump-lex-trace
Enable debugging the lexical analyzer. While parsing the
script file, the detailed dump of the lexer states and matched
rules will be output.
@opsummary{dump-macros, mailfromd}
@item --dump-macros
Show Sendmail macros used in the script file. The macro names
are displayed as comma-separated lists, grouped by handler names.
@xref{Sendmail}, for a detailed description of this
option and its usage.
@opsummary{dump-tree, mailfromd}
@item --dump-tree
Parse and compile the script file and dump the parse tree in a
printable form to the terminal.
@opsummary{dump-xref, mailfromd}
@item --dump-xref
Print a cross-reference of variables used in the filter script.
@xref{Testing Filter Scripts}.
@cindex E, -E @r{option, summary}
@item -E
Stop after the preprocessing stage; do not run the compiler proper.
The output is in the form of preprocessed source code, which is sent
to the standard output. @xref{Preprocessor}.
@opsummary{lint, mailfromd}
@item --lint
Check script file syntax and exit. If the file is @sc{ok}, return 0
to the shell, otherwise print appropriate messages to stderr and exit
with code 78 (@samp{configuration error}).
@opsummary{single-process, mailfromd}
@item --single-process
Do not fork sub-processes to serve requests. This option is meant to
assist in debugging @command{mailfromd}. Don't use it for anything
else but for debugging, as it terribly degrades performance!
@opsummary{stack-trace, mailfromd}
@item --stack-trace
@itemx --no-stack-trace
Add @acronym{MFL} stack trace information to runtime error output.
Overrides @code{stack-trace} configuration statement. Use the
@option{--no-stack-trace} to disable trace information.
@xref{tracing runtime errors}, for more information on this feature.
@anchor{gacopyz-log option}
@opsummary{gacopyz-log, mailfromd}
@item --gacopyz-log=@var{level}
Set desired logging level for @command{gacopyz} library
(@pxref{Gacopyz}). There are five logging levels. The following
table lists them in order of decreasing priority:
@table @asis
@item fatal
Log fatal errors.
@item err
Log error messages.
@item warn
Log warning messages.
@item info
Log informational messages. In particular, this enables printing
messages on each subprocess startup and termination, which look like
that:
@example
Apr 28 09:00:11 host mailfromd[9411]: connect
from 192.168.10.1:50398
Apr 28 09:00:11 host mailfromd[9411]: finishing
connection
@end example
This level can be useful for debugging your scripts.
@item debug
Log debugging information.
@item proto
Log Milter protocol interactions. This level prints huge amounts of
information, in particular it displays dumps of each Milter packet
sent and received.
@end table
Although it is possible to set these levels independently of each
other, it is seldom practical. Therefore, the option
@option{--gacopyz-log=@var{level}} enables all logging levels from
@var{level} up. For example, @option{--gacopyz-log=warn} enables
log levels @samp{warn}, @samp{err} and @samp{fatal}. It is the
default. If you need to trace each subprocess startup and shutdown,
set @option{--gacopyz-log=info}. Setting the logging level to
@samp{proto} can be needed only for @command{Gacopyz} developers, to
debug the protocol.
@xref{Testing Filter Scripts}.
@opsummary{logger, mailfromd}
@item --logger=@var{mech}
Set logger mechanism (@var{mech} is one of @samp{stderr},
@samp{syslog}, @samp{syslog:async}). @xref{Logging and Debugging}.
@opsummary{log-facility, mailfromd}
@item --log-facility=@var{facility}
Output logs to syslog @var{facility}.
@opsummary{log-tag, mailfromd}
@item --log-tag=@var{string}
Tag syslog entries with the given @var{string}, instead of the program name.
@opsummary{source-info, mailfromd}
@item --source-info
@itemx --no-source-info
Include @sc{c} source information in debugging messages. This is
similar to setting @code{line-info yes} in the @code{debug}
configuration block (@pxref{debug statement, line-info,,
mailutils, GNU Mailutils Manual}).
The @option{--no-source-info} can be used to cancel the effect of the
@code{line-info yes} configuration statement.
You do not need this option, unless you are developing or debugging
@command{mailfromd}.
@opsummary{syntax-check, mailfromd}
@item --syntax-check
Synonym for @option{--lint}.
@opsummary{trace, mailfromd}
@item --trace
@item --no-trace
Enable or disable action tracing. If @option{--trace} is given,
@command{mailfromd} will log all executed actions. @xref{Logging and
Debugging}.
@opsummary{trace-program, mailfromd}
@item --trace-program[=@var{string}]
Enable program instruction tracing. With this option
@command{mailfromd} will log execution of every instruction in the
compiled filter program. The optional arguments allows to specify a
comma-separated list of source code modules for which the tracing is
to be enabled, for example @code{--trace-program=bi_io,bi_db} enables
tracing for functions from modules @file{bi_io.c} and @file{bi_db.c}
(notice, that you need not give file suffixes in @var{string}).
This option is useful for debugging @command{mailfromd}, but is not
advised to use otherwise, since it is very time-costly.
@opsummary{transcript, mailfromd}
@item -X
@itemx --transcript
@itemx --no-transcript
Enable or disable transcript of the @acronym{SMTP} sessions to the log
channel. @xref{Logging and Debugging}.
@opsummary{syslog, mailfromd}
@item --syslog
Selects default syslog mechanism for diagnostic output.
@opsummary{stderr, mailfromd}
@item --stderr
Directs all logging to standard output. Similar to @option{--logger=stderr}.
@opsummary{xref, mailfromd}
@item --xref
Same as @option{--dump-xref}. @xref{Logging and Debugging}.
@end table
@node Informational Options
@subsection Informational Options
@table @option
@item -?
@itemx --help
Give a short help summary.
@item --usage
Give a short usage message.
@item -V
@itemx --version
Print program version.
@opsummary{show-defaults, mailfromd}
@item --show-defaults
Show compilation defaults. @xref{Examining Defaults}.
@end table
@node Starting and Stopping
@section Starting and Stopping
@cindex startup
@cindex user privileges
@cindex groups
@cindex supplementary groups
Right after startup, when @command{mailfromd} has done the
operations that require root privileges, it switches to the privileges
of the user it is configured to run as (@pxref{default user privileges}) or
the one given in its configuration file (@pxref{conf-priv, user}). During
this process it will drop all supplementary groups and switch to the
principal group of that user.
Such limited privileges of the daemon can cause difficulties if your
filter script needs to access some files (e.g.@: @command{Sendmail}
databases) that are not accessible to that user and group. For
example, the following fragment using @code{dbmap} function:
@example
@group
if dbmap("/etc/mail/aliases.db", $f, 1)
@dots{}
fi
@end group
@end example
@noindent
will normally fail, because @file{/etc/mail/aliases.db} is readable only
to the root and members of the group @samp{smmsp}.
In such situations you need to instruct @command{mailfromd} to
retain the privileges of one or several supplementary groups when
switching to the user privileges. This is done using @code{group}
statement in the @command{mailfromd} configuration file
(@pxref{conf-priv, group}). In example above, you need to use the following
settings:
@example
group smmsp;
@end example
@noindent
(The same effect can be achieved with @option{--group} command line
option: @kbd{mailfromd --group=smmsp}).
@cindex signals
@cindex SIGQUIT
@cindex SIGTERM
@cindex SIGINT
To stop a running instance of @command{mailfromd} use one of the
following signals: @code{SIGQUIT}, @code{SIGTERM}, @code{SIGINT}.
All three signals have the same effect: the program cancels handling any
pending requests, deinitializes the communication socket (if it is a
@acronym{UNIX} socket, the program unlinks it) and exits.
@cindex SIGHUP
To restart the running @command{mailfromd} instance, send it
@code{SIGHUP}. For restart to be possible, two conditions must be
met: @command{mailfromd} must be invoked with the full file name, and the
configuration file name must be full as well. If either of them is not
met, @command{mailfromd} displays a similar warning message:
@example
@group
warning: script file is given without full file name
warning: restart (SIGHUP) will not work
@end group
@end example
@noindent
or:
@example
@group
warning: mailfromd started without full file name
warning: restart (SIGHUP) will not work
@end group
@end example
The reaction of @command{mailfromd} on @code{SIGHUP} in this case is
the same as on the three signals described previously, i.e. cleanup
and exit immediately.
@cindex pidfile
The @acronym{PID} of the master instance of @command{mailfromd} is
kept on the @dfn{pidfile}, which is named @file{mailfromd.pid} and is
located in the program @dfn{state directory}. Assuming the default
location of the latter, the following command will stop the running
instance of the daemon:
@example
kill -TERM `head -n1 /usr/local/var/mailfromd/mailfromd.pid`
@end example
The default pidfile location is shown in the output of @command{mailfromd
--show-defaults} (@pxref{Examining Defaults}), and can be changed at
run time using @code{pidfile} statement (@pxref{conf-base, pidfile}).
@cindex @file{rc.mailfromd}
@cindex system-wide startup script
To facilitate the use of @command{mailfromd}, it is shipped with a shell
script that can be used to launch it on system startup and shut it
down when the system goes down. The script, called
@file{rc.mailfromd}, is located in the directory @file{/etc} of the
distribution. It takes a single argument, specifying the action that
should be taken:
@table @asis
@item start
Start the program.
@item stop
Shut down the program
@item reload
Reload the program, by sending it @code{SIGHUP} signal.
@item restart
Shut down the program and start it again.
@item status
Display program status. It displays the @acronym{PID} of the master
process and its command line, for example:
@example
@group
$ @kbd{/etc/rc.d/rc.mailfromd status}
mailformd appears to be running at 26030
26030 /usr/local/sbin/mailfromd @/--group smmsp
@end group
@end example
@noindent
If the second line is not displayed, this most probably mean that
there is a @samp{stale} pidfile, i.e. the one left though the program
is not running.
An empty @kbd{rc.mailfromd status} output means that
@command{mailfromd} is not running.
@item configtest [@var{file}]
Check the script file syntax, report any errors found and exit. If
@var{file} is given it is checked instead of the default one.
@item macros [-c] [@var{file}]
Parse the script file (or @var{file}, if it is given, extract
the names of Sendmail macros it uses and generate corresponding export
statements usable in the Sendmail configuration file. By default,
@file{mc} statements are generated. If @option{-c} (@option{--cf}) is
given, the statements for @file{sendmail.cf} are output. See the next
chapter for the detailed description of this mode.
@end table
You can pass any additional arguments to @command{mailfromd} by
editing @code{ARGS} variable near line 22.
The script is not installed by default. You will have to copy it to
the directory where your system start-up scripts reside and ensure it
is called during the system startup and shut down. The exact
instructions on how to do so depend on the operating system you use
and are beyond the scope of this manual.
@node MTA Configuration
@chapter Using @command{mailfromd} with Various @acronym{MTA}s
The following sections describe how to configure various Milter-capable
@acronym{MTA}s to work with @command{mailfromd}.
@menu
* Sendmail::
* MeTA1::
* Postfix::
@end menu
@node Sendmail
@section Using @command{mailfromd} with Sendmail.
This chapter assumes you are familiar with Sendmail configuration in
general and with Milter configuration directives in particular. It
concentrates only on issues, specific for @command{mailfromd}.
@cindex INPUT_MAIL_FILTER, mc file directive
To prepare @command{Sendmail} to communicate with
@command{mailfromd} you need first to set up the @dfn{milter port}.
This is done with @code{INPUT_MAIL_FILTER} statement in your
@command{Sendmail} file:
@example
INPUT_MAIL_FILTER(`mailfrom', `S=unix:/usr/local/var/mailfromd/mailfrom')
@end example
Make sure that the value of @samp{S} matches the value of
@code{listen} statement in your @file{mailfromd.conf} file
(@pxref{conf-milter, listen}). Notice, however, that they
may not be literally the same, because @code{listen} allows to
specify socket address in various formats, whereas Sendmail's @samp{S}
accepts only milter format.
If you prefer to fiddle directly with @file{sendmail.cf} file, use
this statement instead:
@example
Xmailfrom, S=unix:/usr/local/var/mailfromd/mailfrom
@end example
@anchor{exporting macros}
@cindex f, @command{Sendmail} macro
@cindex i, @command{Sendmail} macro
@cindex client_addr, @command{Sendmail} macro
@cindex confMILTER_MACROS_ENVFROM, mc file directive
@cindex @command{Sendmail} macros, exporting
@cindex Message-ID, exporting in @file{mc} file
If you are using Sendmail version 8.14.0 or newer, you may skip to
the end of this section. These versions implement newer Milter
protocol that enables @command{mailfromd} to negotiate with the
@acronym{MTA} the macros it needs for each state.
Older versions of Sendmail do not offer this feature. For Sendmail
versions prior to 8.14.0, you need to manually configure
@command{Sendmail} to export macros you need in your
@command{mailfromd.mfl} file. The simplest way to do so is using
@command{rc.mailfromd} script, introduced in the previous chapter.
Run it with @code{macros} command line argument and copy its output to
your @file{sendmail.mc} configuration file:
@example
$ @kbd{rc.mailfromd macros}
@end example
If you prefer to work with @file{sendmail.cf} directly, use
@option{-c} (@option{--cf}) command line option:
@example
$ @kbd{rc.mailfromd macros -c}
@end example
Finally, if you use other mailfromd script file than that
already installed (for example, you are preparing a new configuration
while the old one is still being used in production environment), give
its name in the command line:
@example
$ @kbd{rc.mailfromd macros newscript.mfl}
# @r{or:}
$ @kbd{rc.mailfromd macros -c newscript.mfl}
@end example
If you use this method, you can skip the rest of this chapter.
However, if you are a daring sort of person and prefer to do everything
manually, follow the instructions below.
@xopindex{dump-macros, mailfromd, described}
First of all you need to build a list of macros used by handlers in
your @file{mailfromd.mfl} file. You can obtain it running
@command{mailfromd --dump-macros}. This will display all macros used
in your handlers, grouped by handler name, for example:
@example
@group
envfrom i, f, @{client_addr@}
envrcpt f, @{client_addr@}, @{rcpt_addr@}
@end group
@end example
Now, modify @code{confMILTER_MACROS_@var{handler}} macros in your
@file{mc} file. Here, @var{handler} means the uppercase name of the
@command{mailfromd} handler you want to export macros to, i.e. the
first word on each line of the above @command{mailfromd --dump-macros}
output. @emph{Notice,} that in addition to these macros, you should
also export the macro @code{i} for the very first handler
(@command{rc.mailfromd macros} takes care of it automatically, but you
preferred to do everything yourself...) It is necessary in
order for @command{mailfromd} to include @samp{Message-ID} in its log
messages (@pxref{Message-ID}).
For example, given the above macros listing, which corresponds to our
sample configuration (@pxref{Filter Script Example}), the
@file{sendmail.mc} snippet will contain:
@example
@group
define(`confMILTER_MACROS_ENVFROM',dnl
confMILTER_MACROS_ENVFROM `, i, f, @{client_addr@}')
define(`confMILTER_MACROS_ENVRCPT',dnl
confMILTER_MACROS_ENVRCPT `, f, @{client_addr@}, @{rcpt_addr@}')
@end group
@end example
@cindex s, @command{Sendmail} macro
Special attention should be paid to @code{s} macro
(@samp{HELO} domain name). In @command{Sendmail} versions up to
8.13.7 (at least) it is available only to @code{helo} handler. If you
wish to make it available elsewhere you will need to use the method
described in @ref{HELO Domain}
Now, if you are a @emph{really} daring person and prefer to do everything
manually @emph{and} to hack your @file{sendmail.cf} file directly, you
certainly don't need any advices. Nonetheless, here's how the two
statements above @emph{could} look in this case:
@example
@group
O Milter.macros.envfrom=i, @{auth_type@}, @{auth_authen@}, \
@{auth_ssf@}, @{auth_author@}, @{mail_mailer@}, @{mail_host@}, \
@{mail_addr@} ,@{mail_addr@}, @{client_addr@}, f
O Milter.macros.envrcpt=@{rcpt_mailer@}, @{rcpt_host@}, \
@{rcpt_addr@} ,i, f, @{client_addr@}
@end group
@end example
@node MeTA1
@section Using @command{mailfromd} with MeTA1.
@cindex meta1
MeTA1 (@uref{http://www.meta1.org}) is an @acronym{MTA} of next
generation which is designed to provide the following main features:
@itemize
@item Security
@item Reliability
@item Efficiency
@item Configurability
@item Extendibility
@end itemize
Instead of using Sendmail-compatible Milter protocol,
it implements a new protocol, called @dfn{policy milter}, therefore an
additional program is required to communicate with
@command{mailfromd}. This program is a @dfn{Pmilter--Milter
multiplexer} @command{pmult}, which is part of the @samp{Mailfromd}
distribution. @xref{pmult}, for a detailed description of its configuration.
The configuration of @samp{Meta1--Mailfromf} interaction can be
subdivided into three tasks.
@enumerate 1
@item Configure @command{mailfromd}
This was already covered in previous chapters. No special
@samp{MeTA1}-dependent configuration is needed.
@item Configure @command{pmult} to communicate with @command{mailfromd}
This is described in detail in @ref{pmult}.
@item Set up MeTA1 to communicate with @command{pmult}
The MeTA1 configuration file is located in @file{/etc/meta1/meta1.conf}.
Configure the @command{smtps} component, by adding the following
section:
@example
policy_milter @{
socket @{
type = @var{type};
address = @var{addr};
[path = @var{path};]
[port = @var{port-no};]
@};
[timeout = @var{interval};]
[flags = @{ @var{flag} @};]
@};
@end example
Statements in square brackets are optional. The meaning of each
instruction is as follows:
@table @code
@item type = @var{type}
Set the type of the socket to communicate with @command{pmult}.
Allowed values for @var{type} are:
@table @asis
@item inet
Use @acronym{INET} socket. The socket address and port number are
set using the @code{address} and @code{port} statements (see below).
@item unix
Use @acronym{UNIX} socket. The socket path is given by the
@code{path} statement (see below).
@end table
Notice, that depending on the @code{type} setting you have to set up
either @code{address}/@code{port} or @code{path}, but not both.
@item address = @var{addr}
Configure the socket address for @code{type = inet}. @var{Addr} is
the IP address on which @command{pmult} is listening
(@pxref{pmult-conf, listen statement}).
@item port = @var{port-no}
Port number @command{pmult} is listening on
(@pxref{pmult-conf, listen statement}).
@item path = @var{socket-file}
Full pathname of the socket file, if @code{type = unix}.
@item timeout = @var{interval}
Sets the maximum amount of time to wait for a reply from
@command{pmult}.
The behavior of @command{smtps} in case of time out depends
on the @code{flags} settings:
@item flags = @{ @var{flag} @}
@var{Flag} is one of the following:
@table @asis
@item abort
If @command{pmult} does not respond, abort the current @acronym{SMTP}
session with a @samp{421} error.
@item accept_but_reconnect
If @command{pmult} does not respond, continue the current session
but try to reconnect for the next session.
@end table
@end table
@end enumerate
For example, if the @command{pmult} configuration has:
@example
listen inet://127.0.0.1:3333;
@end example
@noindent
then the corresponding part in @file{/etc/meta1/meta1.conf} will be
@example
smtps @{
policy_milter @{
socket @{
type = inet;
address = 127.0.0.1;
port = 3333;
@};
@dots{}
@};
@dots{}
@};
@end example
Similarly, if the @command{pmult} configuration has:
@example
listen unix:///var/spool/meta1/pmult/socket;
@end example
@noindent
then the @file{/etc/meta1/meta1.conf} should have:
@example
smtps @{
policy_milter @{
socket @{
type = unix;
path = /var/spool/meta1/pmult/socket;
@};
@dots{}
@};
@dots{}
@};
@end example
@node Postfix
@section Using @command{mailfromd} with Postfix
@cindex Postfix
@cindex smtpd_milters, postfix configuration
@cindex non_smtpd_milters, postfix configuration
@findex /etc/postfix/main.cf
To configure @command{postfix} to work with your filter, you
need to inform it about the socket your filter is listening on.
The @code{smtpd_milters} (or @code{non_smtpd_milters}) statement in
@file{/etc/postfix/main.cf} serves this purpose. If the filter is
to handle mail that arrives via @acronym{SMTP}, use
@code{smtpd_milters}. If it is to handle mail submitted locally to the
queue, use @code{non_smtpd_milters}. In both cases, the value
is a whitespace-separated list of socket addresses. Note, that Postfix
syntax for socket addresses differs from that used by Sendmail and
mailfromd. The differences are summarized in the following table:
@float Table, postfix-socket
@caption{Socket addresses in various formats}
@multitable @columnfractions .33 .33 .33
@headitem Sendmail @tab Mailfromd @tab Postfix
@item inet:@var{port}@@@var{host}
@tab inet://@var{host}:@var{port}
@tab inet:@var{host}:@var{port}
@item unix:@var{file}
@tab unix://@var{file}
@tab unix:@var{file}
@end multitable
@end float
For example, if your @command{mailfromd} listens on
@samp{inet://127.0.0.1:4111}, add the following to
@file{/etc/postfix/main.cf}:
@example
smtpd_milters = inet:127.0.0.1:4111
@end example
@command{Mailfromd} uses Milter protocol version 6. Postfix, starting from
version 2.6 uses the same version. Older versions of Postfix use
Milter protocol 2 by default. Normally, it should not be a problem,
as @command{mailfromd} tries to detect what version the server is
speaking. If, however, it fails to select the proper version, you
will have to instruct Postfix what version to use. To do so, add the
following statement to @file{/etc/postfix/main.cf}:
@example
milter_protocol = 6
@end example
@cindex i, @command{Sendmail} macro in @command{Postfix}
The way Postfix handles macros differs from that of Sendmail.
Postfix emulates a limited subset of Sendmail macros, and not
all of them are are available when you would expect them to. In
particular, the @samp{i} macro is not available before the @samp{DATA}
stage, which brings two consequences. First, @command{mailfromd} log
messages will not include message ID until the @samp{DATA} stage is
reached. Secondly, you cannot use @samp{i} in handlers @samp{connect},
@samp{helo}, @samp{envfrom} and @samp{envrcpt},
@findex postfix-macros.sed
If you wish to tailor Postfix defaults to export the actual macros
used by your filter, run @command{mailfromd --dump-macros} and filter
its output through the @file{postfix-macros.sed} filter, which is
installed to the @file{@var{prefix}/share/mailfromd} directory,
e.g.:
@example
@group
$ @kbd{mailfromd --dump-macros | \
sed -f /usr/share/mailfromd/postfix-macros.sed}
milter_helo_macros = @{s@}
milter_mail_macros = @{client_addr@} @{s@} @{f@}
milter_rcpt_macros = @{rcpt_addr@} @{f@} @{client_addr@}
milter_end_of_data_macros = @{i@}
@end group
@end example
Cut and paste its output to your @file{/etc/postfix/main.cf}.
For more details regarding Postfix interaction with Milter and
available Postfix configuration options, see
@uref{http://www.postfix.org/MILTER_README.html, Postfix before-queue
Milter support}.
@node calloutd
@chapter @command{calloutd}
@include calloutd.texi
@node mfdbtool
@chapter @command{mfdbtool}
@include mfdbtool.texi
@node mtasim
@chapter @command{mtasim} --- a testing tool
@include mtasim.texi
@node pmult
@chapter Pmilter multiplexer program.
@include pmult.texi
@node Reporting Bugs
@chapter How to Report a Bug
@quotation
@i{Documentation is like sex: when it is good, it is very, very
good; and when it is bad, it is better than nothing.}@*
Dick Brandon
@end quotation
Although the author has tried to make this documentation as
detailed as is possible and practical, he is well aware that the
result is rather ``better than nothing'', than ``very good''. So, if
you find that some piece of explanation is lousy or if you find
anything that should have been mentioned here, but is not, please
report it to @email{bug-mailfromd@@gnu.org.ua}.
Similarly, if the program itself fails to meet your expectations, or
does not do what is described in this document; if you have found a
bug or happen to have any suggestion... or have written a useful
function you wish to share with the rest of @command{mailfromd} users,
or wish to express your thanks, email it to the same address,
@email{bug-mailfromd@@gnu.org.ua}.
If you think you've found a bug, please be sure to include maximum
information needed to reliably reproduce it, or at least to analyze
it. The information needed is:
@itemize
@item Version of the package you are using.
@item Compilation options used when configuring the package.
@item Run-time configuration (@file{mailfromd.mfl} file and the command
line options used).
@item Conditions under which the bug appears.
@end itemize
@node Gacopyz
@appendix Gacopyz
@include gacopyz.texi
@node Time and Date Formats
@appendix Time and Date Formats
@include strftime.texi
@node Upgrading
@appendix Upgrading
@include upgrade.texi
@node Copying This Manual
@appendix GNU Free Documentation License
@include fdl.texi
@node Concept Index
@unnumbered Concept Index
This is a general index of all issues discussed in this manual
@printindex cp
@ifset WEBDOC
@ifhtml
@node This Manual in Other Formats
@unnumbered This Manual in Other Formats
@include otherdoc.texi
@end ifhtml
@end ifset
@bye
|