summaryrefslogtreecommitdiffabout
path: root/NEWS
blob: ba959685645cd92d5d9cfb6ab0743a4248b914a7 (plain)
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
Mailfromd NEWS -- history of user-visible changes. 2009-05-04
Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff
See the end of file for copying conditions.

Please send Mailfromd bug reports to <bug-mailfromd@gnu.org.ua>


Version 5.0.93, GIT

* New pragma `dbprop'

This pragma defines user database properties.  It takes two or three
arguments:

 #pragma dbprop <pattern> <null> <mode>

where <pattern> is the name of the database or a shell globbing
pattern, <null> is the word "null" if the terminating null byte
is included in the key length, and <mode> is the database file
mode, either in octal or in usual `ls' notation (e.g. rw-r-----).
Either of <null> or <mode> may be omitted.  If both are given, they
may appear in any order.

* Token Bucket Filter

The new function is provided:

  bool tbf_rate(string key, number cost, number interval, number burst_size)

It implements a classical token bucket filter algorithm.  Tokens are
added to the bucket identified by the `key' at constant rate of 1
token per `interval' microseconds, to a maximum of `burst_size' tokens.
If no bucket is found for the specified key, a new bucket is created
and initialized to contain `burst_size' tokens.

For example:

  if not tbf_rate($f "-" ${client_addr}, 1, 10000000, 20)
    tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
  fi

This adds a token every 10 seconds with a burst size of 20 and a
cost of 1.  In other words, it allows to sent up to 20 emails within
the first 10 seconds after sending the very first email from the given
email/host address pair.  After that, that pair is allowed to send
at most 1 message per 10 seconds.

One of possible implementations for this function is to limit
the total size of messages tranferred per given amount of time.
To do so, the tbf_rate must be used in `prog eom'.  The `cost'
value must contain the number of bytes in an email (or email bytes
* number of recipients), the `interval' must be set to the number of
bytes per microsecond a given user is allowed to send, and the
`burst_size' must be large enough to accommodate a couple of large
emails.  E.g.: 

  prog eom
  do
    if not tbf_rate($f "-" ${client_addr},
                    message_size(current_message()),
                    10240,   # At most 10 Kb/ms
		    2000000) 
      tempfail 450 4.7.0 "Data sending rate exceeded. Try again later"
    fi
  done

The `tbf_rate' implementation is contributed by John McEleney and
Ben McKeegan.

* Greylisting

A new implementation of the `greylist' function is provided.  In the
contrast to the traditional implementation, which keeps in the
database the time when the greylisting was activated for the given
key, the new one stores the time when the greylisting period is set to
expire.  This implementation allowed to implement the `is_greylisted'
function:

  bool is_greylisted(string key)

which returns True if the `key' is currently greylisted, and False
otherwise.  This implementation is based on the patch by Con
Tassios.

By default, the traditional implementation is used, which ensures
backward compatibility with the previous versions.  To switch to
the new implementation, use the following pragmatic comment at the
beginning of your script:

  #pragma greylist con-tassios
or
  #pragma greylist ct

* The rate builtin

The rate builtin function now takes an optional `threshold' argument:

  number rate(string key, number interval, [number mincnt, number threshold])

If the observed rate (per interval seconds) is higher than the
threshold, the rate function does not increment the hit counters for
that key.  That way messages that were not accepted do not affect the
calculated rate.

Normally, the threshold argument should be equal to the value used in
the right side of comparison operator, e.g.:

  if rate($f "-" ${client_addr}, %rate_interval, 4, %maxrate) > %maxrate
    tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
  fi

The threshold argument is made optional in order to provide backward
compatibility with the prior releases of mailfromd.  Nevertheless, its
use is strongly encouraged.  To simplify the task, the new function
`rateok' is provided (see below).
  
* The rateok function

A new library function is provided:

  bool rateok(string key, number sample_span, number threshold; number mincnt)

This is a higher-level interface to the rate function.  This function
returns True if the mail sending rate for `key', computed for the
interval of `sample_span' seconds is less than the `threshold'.
Optional `mincnt' parameter supplies the minimal number of mails
needed to obtain the statistics.  It defaults to 4.

An example of rateok usage follows:

#require rateok

prog envfrom
do
  if not rateok($f "-" ${client_addr}, interval("1 minute"), 40)
    tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
  fi
done

This example limits the rate to 40 mails per minute.

* Rate expiration

In addition to the usual expiration algorithm, the rate records are
also expired if no mails were received during a time span greater than
the value of the 2nd argument to the rate (or rateok) function.

* Bugfixes
** Second argument to envfrom and envrcpt
** write without third argument
** sa_format_report_header: fix formatting
** Limit use of file descriptors by message capturing eom functions
** fix implementation of `restex' instruction.
** fix inconsistencies in message capturing code.


Version 5.0, 2008-12-26

* Requires Mailutils 2.0 or newer.

* Incompatible changes

** body handler

The type of first parameter ($1) is now generic pointer, not a string.
It can be converted to a usual string using the `body_string' function
(see below).

* Changes to MFL

** Function aliases

Functions can have several names. Alternative function names, or
aliases, are introduced by `alias' statement, placed between the
function declaration and return type declaration, e.g.:

func foo()
alias bar
alias baz
do
  ...
done

Any number of aliases is allowed.

** Functions with variable number of arguments

Ellipsis as the last argument in a list of formal arguments to a
function indicates that this function takes a variable number of
arguments. For example:

  func foo (string a ; string b, ...)

Actual arguments passed in a list of variable arguments have string
data type.  A special construct is provided to access these arguments:

  $(expr)

where expr is any valid MFL expression, evaluating to a number.  This
construct returns exprth argument from the variable argument list.

** getopt and vaptr 

New function `getopt' is provided.

New operator `vaptr' is available for converting function argument
list to the second argument of `getopt'.

* New MFL functions

** rcpt_add(string rcpt)

Adds a recipient to the message envelope.

** rcpt_delete(string rcpt)

Removes the given recipient from the envelope.

** header_add(string hdr, string value [, number index])

Adds a header "hdr: value" to the message.  This function differs from the
`add' action in two regards:

  1. It allows to construct the header name, whereas `add' requires it to
     be a literal string;
  2. Optional `index' argument specifies the location in the header
     list where this header should be inserted.
     
** header_insert(string hdr, string value, number index)

Equivalent to header_add(hdr, value, index).

** header_delete(string hdr [, number index])

Delete header `hdr' from the envelope.  This function differs from the
`delete' action in two regards:

  1. It allows to construct the header name, whereas `delete' requires it to
     be a literal string;
  2. Optional `index' argument allows to select a particular header
     instance to delete.

** header_replace(string hdr, string value [, number index])

Replace the value of the header `hdr' with `value'.  Optional argument
`index' specifies the number of `hdr' instance to replace (1-based).
     
This function differs from the `replace' action in two regards:

  1. It allows to construct the header name, whereas `replace' requires it
     to be a literal string;
  2. Optional `index' argument allows to select a particular header
     instance to replace.

** quarantine(string reason)

Quarantines the message using the given reason.

** replbody(string text)

Replaces the body of the message with the given `text'

** body_string

  string body_string(pointer text, number length)

This function converts a generic pointer to an MFL string.  It can
be used only in `body' handler, e.g. the following fragment collects
the entire message body in a variable and then uses it in the `eom'
handler: 

string text

prog body
do
  set text %text body_string($1,$2)
done

prog eom
do
  echo %text
done

** Macro Access

Two functions are provided for accessing Sendmail macros.

- string getmacro(string name)

Return the value of Sendmail macro `name'.  It is entirely equivalent
to `${name}' construct, except that allows to construct the name
programmatically, while the `${name}' construct requires it to be a
literal string.

- number macro_defined(string name)

Return true if Sendmail macro `name' is defined.

** String functions

- string replstr(string s, number n)
- string sa_format_score(number score, number prec)
- string sa_format_report_header(string report)

** Character type functions

- number isalnum(string s)
- number isalpha(string s)
- number isascii(string s)
- number isblank(string s)
- number iscntrl(string s)
- number isdigit(string s)
- number isgraph(string s)
- number islower(string s)
- number isprint(string s)
- number ispunct(string s)
- number isspace(string s)
- number isupper(string s)
- number isxdigit(string s)

** Mailbox and message manipulation

-  number current_message()
-  void mailbox_append_message(number mbx, number msg)
-  void mailbox_close(number mbx)
-  number mailbox_get_message(number mbx, number nmsg)
-  number mailbox_messages_count(number nmbx)
-  number mailbox_open(string url[, string mode, string perms])
-  number message_body_lines(number nmsg)
-  void message_body_rewind(number nmsg)
-  number message_body_size(number nmsg)
-  void message_close(number nmsg)
-  number message_count_parts(number nmsg)
-  string message_find_header(number nmsg, string header[, number idx])
-  number message_get_part(number nmsg, number idx)
-  bool message_has_header(number msg, string header[, number idx])
-  number message_header_count(number nmsg)
-  number message_header_lines(number nmsg)
-  number message_header_size(number nmsg)
-  bool message_is_multipart(number nmsg)
-  number message_lines(number nmsg)
-  string message_read_body_line(number nmsg)
-  string message_read_line(number nmsg)
-  void message_rewind(number nmsg)
-  number message_size(number nmsg)

** System functions: umask

** string verp_extract_user(string email, string domain)

If `email' is a valid VERP-style email address for `domain', this
function returns the user name, corresponding to that email.
Otherwise, it returns an empty string.

** string sa_format_report_header(string text)

Format a SpamAssassin report `text' in order to include it in a RFC 822
header.  This function selects the score listing from `text', and
prefixes each line with "* ".

** string sa_format_score(number score, number prec)

Format `score' as a floating-point number with `prec' decimal
digits.  This function is convenient for formatting SpamAssassin
scores for use in message headers and textual reports.  It is defined
in module sa.mf.

@smallexample
sa_format_score(5000, 3) @result{} "5.000"
@end smallexample

** Sequential access to DBM

- number dbfirst (string name)
- number dbnext (number dn)
- string dbkey (number dn)
- string dbvalue (number dn)

* Changes to MFL functions

** sa

This function takes an optional third argument, which controls what kind of
data is returned in the sa_keywords variable.  If the third argument
is not supplied or is zero, the sa_keywords variable contains a string
of comma-separated SpamAssassin keywords identifying this message.
This is compatible with previous versions of Mailfromd.

Otherwise, if the third argument is not 0, the value of sa_keywords is
a @dfn{spam report} message.  It is a multi-line textual message,
containing detailed description of spam scores in a tabular form.
The function `sa_format_report_header' can be used to format it for
use in a message header.

** substring

Third argument (`end') can be a negative number, meaning offset from
the end of the string.  Thus:

  substring("mailfrom",4,-1) => "from"
  substring("mailfrom",4,-2) => "fro"

** index and rindex

Both functions take an optional third argument indicating where to
start searching, e.g.:

  index("string of rings", "ring")    => 2
  index("string of rings", "ring", 3) => 10
  
* New global variables.

** last_poll_greeting

Keeps the initial SMTP reply from the last poll.

** last_poll_helo

Keeps the reply to HELO (EHLO) command from the last poll.

* New operation mode.

When given `--run' command line option, mailfromd looks for a function
named `main' and invokes it, passing the rest of command line as its
arguments.  The function `main' must be declared as:

  func main(...) returns number

The return value from this function is used as the exit code.

Command line arguments may be processed using `getopt' builtin function.

* Two stack growth policies.

The stack can be grown either by fixed size blocks, or exponentially.
In first case, the size of the block is divisible by expansion chunk,
which is 4096 words by default.  The expansion chunk size can be
specified as a second argument to pragma stacksize, e.g.:

  #pragma stacksize 10240 8192

Exponential stack growth means that each time the evaluator needs to
have more stack space, it expands the stack to twice the size it had
before.  This growth policy is selected if the word `twice' appears as
the second argument to pragma stacksize:

  #pragma stacksize 10240 twice

All numbers may be suffixed with usual size suffixes (k, m, g, t,
meaning kilowords, megawords, etc).
  
Additionally, maximum stack size may be given as third argument:

  #pragma stacksize 10m twice 200m

* Milter `ports' can be specified using URL notation, e.g.:

  inet://127.0.0.1:1234  instead of inet:1234@127.0.0.1
  unix:///var/run/socket instead of unix:/var/run/socket

* ACLs

Access to Milter can be controlled using access control lists.
  
* Removed depecated features:

 1. Command line options --ehlo, --postmaster-email, and --mailfrom

* mtasim

New command line options `--user' and `--group' allow to specify user
name and a list of additional groups when the program is run with
`root' privileges.

* New programs:

** smap.

Smap is a general-purpose remote map for MeTA1.

** pmult

Pmult is a pmilter to milter multiplexer.  Pmilter is a new filter
protocol implemented in MeTA1, an MTA which should in future replace
Sendmail.  Pmult listens for Pmilter commands from the server,
translates them into equivalent Milter commands and passes the
translated requests to a preconfigured set of Milter filters.  When
the filters reply, the reverse operation is performed: Milter
responses are translated into their Pmilter equivalents and 
are sent back to the server.

Pmult allows to use existing milters with MeTA1 without any
modifications. 

** pies

Pies is a process invocation and execution supervisor.

* GNU Emacs MFL Mode is improved

* Bugfixes
** Fix program evaluator bug that manifested itself on machines where
   sizeof(unsigned long) > sizeof(usnigned).
** Fix stack reallocation.
** Header modification functions affected subsequent messages in the
same SMTP session.  This is fixed.


Version 4.4, 2008-03-10

* The --domain option is withdrawn.

This option was declared as deprecated in version 4.0.  Now it is
withdrawn and its short version (-D) is reused for another purpose
(see -D option, below).

* New command line options -D and -U

Both options work like their m4 counterparts: the `-D' command line
option defines a preprocessor symbol, the `-U' option undefines it.

* Constant vs. variable shadowing

In previous versions of mailfromd name clashes between constants and
variables went unnoticed, which sometimes lead to hard-to-diagnose
errors.  This bug is fixed in this version.  If a constant is defined
which has the same name as a previously defined variable (the constant
"shadows" the variable), the compiler prints the following diagnostic
message: 

<file>:<line>: Warning: Constant name `name' clashes with a variable name
<file>:<line>: Warning: This is the location of the previous definition

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, the %NAME notation refers to the last defined symbol, be
it variable or constant.

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 `done' closing the
function.  For global variables, it lasts up to the end of input.

* Exception names.

To minimize chances of name clashes, all symbolic exception codes has
been renamed by prefixing them with the `e_', thus, e.g. `divzero'
became `e_divzero', etc.  The `ioerr' exception code is renamed to
`e_io'.

For consistency, the following most often used codes are available without
the `e_' previx: success, not_found, failure, temp_failure.

The use of old exception codes is still possible by defining a
preprocessor symbol OLD_EXCEPTION_CODES, for example:

  mailfromd -DOLD_EXCEPTION_CODES

* match_dnsbl and match_rhsbl

Both functions malfunctioned in versions from 4.0 up to 4.3.1 due to a
name clash between the exception code `range' and their third
argument.  This is fixed.

Additionally, previous versions of match_dnsbl silently ignored
invalid first argument.  Now, the e_invip exception is signalled in
this case.
  

Version 4.3.1, 2008-03-01

* Fix program evaluator bug that manifested itself on machines where
  sizeof(unsigned long) > sizeof(usnigned).


Version 4.3, 2008-02-10

* write built-in

The `write' built-in function takes an optional third argument,
specifying the number of bytes to write. This form is particualry
useful in `body' handler for writing $1, because it is not null-
terminated, e.g.:

prog body
do
  write(fd, $1, $2)
  ...
done  

* sieve

New built-in function `sieve' provides an interface to Sieve
interpreter.

* Restore previous meaning of --enable-syslog-async.

Unless this option is given to ./configure, asynchronous syslog
implementation will not be compiled.

* Bugfixes:

** Fix compilation on Sun.
** Fix header deletion (delete action).
** Variable shadowing was broken if a rehash happened between vardcl and
   forget_autos.


Version 4.2, 2007-10-23

* Licensed under the GPLv3

* New command line options --syslog-async and --no-syslog-async

These options allow to select the implementation of syslog to use
at run time. 

* RFC 2821 compatibility

The sender verification engine used to send whitespace character
between `MAIL FROM:' and `RCPT TO:' commands and their argument.
This violated RFC 2821, which requires the argument to follow the
command without any intermediate whitespace.  It is fixed in this
release.

* Sample threshold for `rate' function.

The `rate' function takes an optional third argument allowing to
specify the minimum number of received mails needed to obtain the
sending rate value.  The default is 2, which is probably too
conservative. The following example raises it to 10:

  if rate($f "-" ${client_addr}, interval("1 hour"), 10) > %maxrate
    ...
  fi   

* mtasim is improved

In particular, it accepts MAIL FROM: and RCPT TO: without extra space
after the colon, as required by RFC. The milter interface is also
improved. 

* The `resolve' function ignores TXT records.

In other words, resolve and primitive_resolve are guaranteed to return
either an A or a PTR record.
  

Version 4.1, 2007-06-11

* National Language Support.

The program includes National Language Support.  Polish and Ukrainian
translations are available.

* NLS Functions

NLS functions allow to localize your filter scripts for a particular
language.  The following functions are implemented: bindtextdomain,
dgettext, dngettext, textdomain, gettext, ngettext.  In addition, 
macros _() and N_() are also provided.

* GNU Emacs MFL Mode

This release comes with the file `mfl-mode.el', providing MFL mode for
GNU Emacs.  This mode facilitates editing MFL source files.  By
default, the new mode is installed whenever configure determines the
presense of GNU Emacs on your machine.  See the documentation, node
`Using MFL Mode' for the detailed discussion of this mode including
customization information.  

* Input files are preprocessed before compilation.  
  The default preprocessor is M4, but this can be changed (or disabled) at 
configuration time (see `DEFAULT_PREPROCESSOR' variable and 
`--with-preprocessor' command line option).

* New atom $#

  Returns the number of the arguments passed to the function.

* New atom @parm

  Returns the position of parameter `parm' in the function argument
list.  It can be used, for example, to check whether an optional
argument value is passed to the function, e.g.:

func foo(string x; number n)
do
  if $# > @n
    /* `n' is passed */
    ...

  The default preprocessor setup script provides a macro `define'
designed to be used for this purpose:
  
func foo(string x; number n)
do
  if defined(n)
    /* `n' is passed */
    ...

* sprintf

The built-in function `sprintf' is available with the same semantics
as its C counterpart.

* Discontinued support for deprecated features:

** `&code' form to specify an exception code is discontinued.
** pragma options retry, io-retry, and connect-retry
        
* Bugfixes:
** Built-in listen ignored optional second argument.
** Debug specification incorrectly gave preference to the global level
over the source level.  This is fixed, so that `--debug=40,dns=10'
means level 10 for calls from `dns.c', and level 40 for all the rest.


Version 4.0, 2007-05-12

Note for users of 3.1.x: see also the notes for previous alpha
(3.1.9x) versions.

* SIGHUP handling

  SIGHUP instructs `mailfromd' to restart itself.  

* rc.mailfromd reload

  The `reload' option given to `rc.mailfromd' instructs it to send
SIGHUP to the running instance of the program.

* mtasim

  The `mtasim' utility is an MTA simulator for testing and debugging
mailfromd filter scripts.  It supports stdio (-bs) and daemon (-bd)
modes, has GNU readline support and `expect' facility, which makes it
useful in automated test cases.

  See the documentation, chapter `mtasim'.

* `begin'/`end' handlers

  The `begin' and `end' special handlers may be used to
supply startup and cleanup code for the filter program.

  The `begin' special handler is executed once for each
SMTP session, after the connection has been established but
before the first milter handler has been called.  Similarly, an
`end' handler is executed exactly once, after the connection has
been closed.  Neither of handlers takes any arguments.

  See the documentation, section `begin/end'.

* Cache control

  Use function `db_set_active' to enable or disable given cache
database.  E.g.

  # Disable DNS cache:
  db_set_active("dns", 0)
  # Enable it back again: 
  db_set_active("dns", 1)

Similarly, the function `db_get_active' returns a number indicating
whether the given cache database is used or not.   

** Bugfixes
* Fix a long-standing bug in parsing the --predict option argument.  Is there
anybody using this option at all?
  

Version 3.1.91, 2007-04-23

* Non-blocking syslog

This version is shipped with non-blocking syslog implementation by
Simon Kelley.  You may wish to enable it if you noticed that the
number of mailfromd processes grows uncontrollably and the processes
are hung for prolonged amounts of time.  Usually this indicates that
the daemon blocks in syslog() calls.  Read the description of
`--enable-syslog-async' option in chapter `Building' for the detailed
discussion of this (try `info -f doc/mailfromd.info --index-search
syslog-async').

* SPF support

The function check_host() tests the SPF record for the given
identity/host name.  The syntax is:

 number check_host(string ip, string domain, string sender, string helo)

See the documentation, node `SPF functions' for the detailed
description of this function and the related functions and variables.

* next and pass

Use `pass' instead of `next'.

The `next' keyword has changed its semantics: it is now used to
resume the next iteration of the enclosing loop statement (see
below).

For compatibility with the previous versions, its use outside of a
loop statement is still allowed, but a warning is issued.  You are
encouraged to replace all occurrences of `next' in your configuration
scripts with `pass'.

* Loop

The loop statement is implemented.  Its syntax is:

loop [name]
     [for <stmt>,] [while <stmt>,] [<stmt>]
do
     ...
done [while <stmt>]

See the documentation, section `Loop Statements'.

* break and next

The `break' statement exits from the enclosing loop.

The `next' statement resumes the next iteration of the enclosing loop
statement.

Both statements take an optional argument specifying the identifier
(name) of the loop to break from (or continue), this allows to build
complex iterations consisting of nested loops.  For example, in this
code (line numbers added for clarity):

 1   loop outer for set i 1, while %i < %N
 2   do
 3	...
 4	loop for set j 1, while %j < %i
 5	do
 6	   if foo(%j)
 7	     break outer
 8         fi
 9	done
10  done		   		   
11 accept

if the call to `foo' in line 6 returns true, the control is immediately passed
to `accept' in line 11.

* Resizable stack

The runtime stack of the MFL grows automatically as the need arises.
Thus, `#pragma stacksize' sets the initial size of the stack, and
the `Out of stack space' error, which was common in the previous
versions, now can occur only if there is no more virtual memory left.

Whenever the stack gets expanded, mailfromd issues a warning message
to the logs, notifying of the new stack size, e.g.:

    warning: stack segment expanded, new size=8192

You can use these messages to adjust your stack size configuration
settings. 
    
* Runtime stack traces

New command line option --stack-trace enables dumping stack traces
on runtime errors.  This might help localize the source of the error.
The trace looks like:

mailfromd: RUNTIME ERROR near ../mflib/match_cidr.mf:30: invalid CIDR (boo%)
mailfromd: Stack trace:
mailfromd: 0077: test.mf:7: match_cidr
mailfromd: 0096: test.mf:13: bar
mailfromd: 0110: test.mf:18: foo
mailfromd: Stack trace finishes
mailfromd: Execution of the configuration program was not finished

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:

1. Value of program counter at the time of its execution
2. Source code location, if available
3. Name of the function called

The same output can be obtained by calling function stack_trace()
in your filter program.

See the documentation, section `Runtime Errors', for the detailed
description and examples.

* connect handler

Connect handler is implemented.

* envfrom and envrcpt

Both handlers take an additional second argument, containing the rest
of the SMTP command line.

* New functions

- string db_name(string fmt)

Return full file name of the database file corresponding to format
`fmt'

- number db_expire_interval(string fmt)

Return the expiration period for db format `fmt'

- string getmx(string domain [, number resolve])

Returns a whitespace-separated list of MX names (if `resolve' is not
given or is 0) or MX IP addresses (if `resolve'==1) for `domain'.  If
`domain' has no MX records, empty string is returned.  If the DNS
query fails, `getmx' raises an appropriate exception.

This interface differs from that of version 3.1.4 in that the calls to
getmx(domain) and getmx(domain,1) can return different number of
entries (see the docs for an example).

* #pragma regex stack

The `#pragma regex' statement can keep a stack of regex flags.  The
stack is maintained using `push' and `pop' commands.  The statement

   #pragma regex push [options]

saves current regex flags on stack and then optionally modifies them
as requested by options.

The statement

   #pragma regex pop [options]

does the opposite: restores the current regex flags from the top of
stack and applies [options] to it.

This statement is useful in include files to avoid disturbing user
regex settings.  E.g.:

   #pragma regex push +extended +icase
    .
    .
    .
   #pragma regex pop

* Optional arguments in user-defined functions

User-defined functions can take optional arguments.  In a declaration,
optional arguments are separated from the mandatory ones by a
semicolon.  For example:

    func foo(number a, number b; string c)

This function is declared with two mandatory arguments (a and b), and
an optional one (c).  Subsequently it can be invoked either as

   foo(x, y, z)      

or

   foo(x, y)

When invoking such functions, any missing arguments are replaced with
default values:

 - 0   for numeric arguments
 - ""  for string arguments

Thus, continuing our previous example, the invocation `foo(x, y)' is
equivalent to `foo(x, y, "")'.

* New statement #include_once

This statement works exactly like `#include' except that it keeps
track of the included files. If the requested file has already been
included, `#include_once' returns silently, while `#include' issues
an error message.

* New statement #require

Requires use of the named module, e.g.:

#require dns

See the documentation, section `Modules', for the description of MFL
module system.

* Internet address manipulation functions

- number ntohl (number N)
- number htonl (number N)
- number ntohs (number N)
- number htons (number N)
- number inet_aton (string S)
- string inet_ntoa (number N)
- number len_to_netmask (number N)
- number netmask_to_len (number N)

* DNS functions

DNS functions are reimplemented in two layers:

1. Primitive calls:

- string primitive_hostname (string IP)
- string primitive_resolve (string S [, string DOMAIN])
- number primitive_hasmx (string DOMAIN)
- number primitive_ismx (string DOMAIN, string IP)

These functions throw an exception if the requested lookup operation
fails.

2. Traditional calls:

- string hostname (string IP)
- string resolve (string S [, string DOMAIN])
- number hasmx (string DOMAIN)
- number ismx (string DOMAIN, string IP)

These are implemented in MFL and work exactly as their predecessors in
3.1.x branch.

To use the traditional calls, add the following statement at the
beginning of your script file:

  #require dns

(see the documentatio, section `Modules' for the description of
#require statement)

* Function `match_cidr'

This function has been reimplemented in MFL.  To use it, add
`#require match_cidr' at the top of your script source (see the
documentatio, section `Modules' for the description of
#require statement)

* Catch arguments

Catch takes two positional arguments: $1 gives the exception code,
$2 is the diagnostic string, explaining what happened in detail.

* New statement `throw'

The `throw' statement throws the given exception.  For example:

    throw invcidr "invalid CIDR (%cidr)"

* New command line option -v (--variable) allows to alter initial value of a
global variable, e.g:

  mailfromd -v ehlo_domain=mydomain.org

The old way of assigning values to the globals from the command line
(by prefixing an assignment with a percent sign) is discontinued.

* Pragmatic options `mailfrom' and `ehlo'

Both options create ambiguities in the language and are therefore
deprecated.  They are still understood but a deprecation warning is
issued when the parser sees them.  To update your scripts:

Change
  #pragma option mailfrom @var{value}
to   
  set mailfrom_address @var{value}

Change  
  #pragma option ehlo @var{value}
to  
  set ehlo_domain @var{value}

* Code generation redone to decrease memory requirements and make compiled
code self-sufficient.

* New statement `const' defines a named constant, e.g.:

  const n 10
  const s "String"

  if $f != s
     ...
  fi

In program text, constants are referred to by their name. In strings,
they are referred to using variable syntax (e.g. "%s").

* It is allowed to initialize variables in declarations.

For example, instead of

    number x
    set x 1

you can write

    number x 1

* Built-in macro __statedir__

New built-in macro `__statedir__' expands to the name of the default
program state directory.

* Changing state directory at run-time

It is possible using `--state-directory' command line option or
`#pragma option state-directory' statement.

* Bugfixes
** Fix incorrect packet length calculation in connect Milter handler.
The bug manifested itself with the following log messages:

- In mailfromd log:

   MailfromFilter: shan_connect: wrong length

- In Sendmail log:

   milter_read(mailfrom): cmd read returned 0, expecting 5
   Milter(mailfrom): to error state
   
** Fix coredumps on printing void returns with --dump-tree
** Fix coredumps on optimizing conditionals like

       if 0
	  do_something
       fi    

** Fix function context checks.

Previous versions bailed out on using context-limited built-ins (like `sa' and
`clamav') in functions. It is fixed. The context limit of the built-in
propagates to the function it is used in, that is defining

func sa_wrapper(string url)
do
	if sa(%url, 3)
	   discard
	fi
done

makes function `sa_wrapper' limited for use in `prog eom' only.	

** Fix passing of string handler arguments between handlers, as in
 
        prog helo
       do
 	  set x $1
        done
 
        prog envfrom
        do
 	  if %x = "dom.ain"
 	  ...	
 
(from 3.1.3)

** If, during sender verification, the remote server replies with
4xx to MAIL FROM command, do not try next sender address, but tempfail
immediately.
           

Version 3.1.90, 2006-12-13

* `==' can be used as well as `=' to test for equality

This is a bit of syntactic sugar for seasoned C programmers, who seem
to type == instinctively (like the author does).

* resolve takes an optional second argument

The argument specifies the domain.  For example, the following calls
are equivalent:

  resolve("puszcza", "gnu.org.ua")        = resolve("puszcza.gnu.org.ua")
  resolve("22.0.120.213", "in-addr.arpa") = hostname("213.130.0.22")  

* listens takes an optional second argument

The second argument specifies the port to use instead of the default 25.

* New functions

- message_header_decode(string TEXT, [string CS])

The TEXT must be encoded as per RFC 2047.  The function decodes it and
returns the resulting string.  Optional argument CS specifies the
character set for the output string.  Default is UTF-8.

- message_header_encode(string TEXT, [string ENC, string CS])

Encodes TEXT according to RFC 2047.  Optional arguments:

 ARG       Meaning          Default value
------+------------------+------------------- 
 ENC       Encoding         quoted-printable
 CS        Character set    UTF-8

Valid values for ENC are quoted-printable, Q, base64, B 

- unfold (string TEXT)

Unfold TEXT as defined in RFC 2822, section 2.2.3.  Return unfolded
string.

* Default logging facility is LOG_MAIL

* While checking sender validity, issue RSET if the previous MAIL
FROM returned 4xx.

Bugfixes:

* `Make install' creates state directory if it does not exist
* `clamav' function could cause coredumps when using socket ports.
* `resolve' function altered its argument if it was a CNAME.
* A typo in gram.y prevented some correct `matches' conditions from
being compiled. In particular, strip_domain_part.mf triggered this
error.


Version 3.1, 2006-12-07 

* Incompatible changes.

For detailed instructions on how to upgrade from version 3.0, please
see http://mailfromd.software.gnu.org.ua/upgrade.html

1. The package refuses to compile without DBM

2. The command line option --config-file (-c) is no longer supported.
To use an alternative filter script, give its name as an argument in the
command line, e.g.

   mailfromd my-script.rc

For backward compatibility, the invocation
`mailfromd --config-file my-script.rc' still works but produces a
warning message.

The semantics of `-c' will change in the next release.
   
3. The function `dbmap' takes an optional third argument.  If it is 1,
then the length of the lookup key will include the terminating null character.

In previous versions dbmap always counted the terminating null
character in the key length.  So, you should add the non-zero third argument
to the calls to dbmap to preserve their functionality.

4. Variables, implicitly declared within a function, are automatic.
Previous versions created them as global.

* Language changes

** Hex and octal constants

Usual C notation (0xNNN for hex and 0NNN for octal) is accepted.

** Bitwise operators: &, |, ^ (logical and, or, xor) and ~ (twos-complement)

** Search path for include files

The `#include' statement handles its argument the same way C
preprocessor does:  the argument is searched in the include file path,
if enclosed in angle brackets (<>), and in the current working
directory first and then in the include file path, if it is enclosed
in double quotes.  The default include file path is

/usr/share/mailfromd/include:/usr/local/share/mailfromd/include

plus $prefix/share/mailfromd/include if $prefix is not `/usr' or
`/usr/local'.

The command line option -I (--include) adds the named directory in
front of the default include path.

** Code optimization

Parse tree is optimized before code generation.  This can be
controlled using -Olevel option, where `level' is the optimization
level.  Currently implemented levels are 0 (no optimization) and 1
(full optimization), which is the default. 

** All variables are now strongly typed.

The declaration of the variable has the form: `TYPE NAME', where TYPE
is one of `string' or `number', and NAME is the variable name.  For
compatibility with the previous versions, the declaration is
optional.  If it is absent, the first assignment to the variable
defines its type.  Subsequent assignments will implicitly cast the
value being assigned to the type of the variable.

** New style of function declarations.  Named parameters.

Functions should be defined as:

func NAME (PARAM-LIST) returns TYPE

where TYPE is as described in the previous paragraph and PARAM-LIST is
a comma-separated list of parameter declarations in the form TYPE
NAME.  Consequently, instead of the positional notation, parameters
can be referenced by their names:

func sum(number a, number b) returns number
do
	return %a + %b
done

Within the function body, the named parameters can be handled the same
way as other variables, in particular they can be assigned new values
using `set' instruction.

For compatibility with the previous version, old type of function
declarations is supported as well.

** Automatic variables

Automatic variables are defined within a function or handler.  Their
scope of visibility ends with the terminating `done' statement.
Automatic variables are declared and referenced the same way as global
ones.  To declare an automatic variable, use `TYPE NAME' notation.
Variable declarations can be intermixed with executable statements.

The following example defines two automatic variables for
the function `foo':

func foo()
do
	number a
	string s
	...

If a variable is declared implicitly within a function or handler, it
is declared automatic.
	
See the documentation for the detailed description and examples.

** New functions:

*** I/O functions: open, close, write, getline
    See http://mailfromd.software.gnu.org.ua/manual/bi/io.html
    
*** Time functions: time, strftime
    See http://mailfromd.software.gnu.org.ua/manual/bi/system.html
    
*** System functions: system
    See http://mailfromd.software.gnu.org.ua/manual/bi/system.html
    
*** DBM functions: dbput, dbdel
    See http://mailfromd.software.gnu.org.ua/manual/bi/db.html

*** String functions: substr, index, rindex
    See http://mailfromd.software.gnu.org.ua/manual/bi/string.html

*** Debugging functions: debug, cancel_debug, program_trace,
    cancel_program_trace
    See http://mailfromd.software.gnu.org.ua/manual/bi/debug.html

*** Mail sending functions: send_mail, send_text, send_dsn
    See http://mailfromd.software.gnu.org.ua/manual/bi/mail.html

** The legacy function numrcpt() has been withdrawn

Use %rcpt_count instead.

** Built-in macros

Built-in macros have names beginning and ending with double underscore.
As their name implies, the macros are expanded to constant values.
The following built-in macros are defined:

1. __file__ expands to the name of the current source file
2. __line__ expands to the number of line in the current source file
3. __function__ expands to the name of the current lexical context,
i.e. the function or handler name.
4. __package__ expands to the string containing package name ("mailfromd")
5. __version__ expands to the textual representation of the program
version (e.g. "3.0.90")
6. __major__ expands to the major version number
7. __minor__ expands to the minor version number
8. __patch__ expands to the version patch level, or 0 if it is not defined.

Built-in macros can be used in variable context.  For example, to use
them within a string or here-document, prepend them with % as if they
were regular variables, e.g.:

   echo "%__file__:%__line__: Checkpoint"

* The envfrom and envrcpt handlers print entire argument array in the
debugging output. 

* New DNS caching scheme.

All DNS lookups are cached on global basis, as opposed to the per-session
basis in previous versions.  The cache is stored in the DBM database
`dns'.  It can be listed and otherwise operated upon using usual
mailfromd commands.

If a lookup gives a positive result, the TTL from the DNS record is
used as the record expiration interval.  For negative lookups, the
default interval of 3600 seconds is used.  It can be altered by the
following pragmatic comment:

  #pragma database dns negative-expire-interval N

* New command line option --xref

Produces a cross-reference listing of global variables.

* Fuller SMTP timeout control

In order to more fully control SMTP transactions, new timeout value
is introduced: initial-response-timeout.  This is the maximum time
to wait for the remote to issue the initial SMTP response.  This value
is especially useful for dealing with the servers that impose a
delay before the initial reply (most notably "CommuniGate Pro"
ones").  The default value is 30 seconds which should be enough for
most normal servers.  See the documentation, node "SMTP Timeouts" for
the detailed discussion.

* No more `retry' options.

The `retry' options and pragmas have been removed.  The new timeout
control scheme warrants that the polling will take at most the given
interval of time.  In particular, that affects:

** Command line option `--retry' 
** Pragma options io-retry and connect-retry

* Bugfixes
** Switch statements without the default branch produced incorrect code
(the very first branch was used as the default one).  This is fixed.
** Fix handling of escape sequences at the beginning of a string and before
the beginning of an interpreted sequence within the string.
** Fix the declarations of the built-in functions `toupper' and `tolower'.
** Fix storing the macro values obtained from Sendmail
** Collect zombie subprocesses as soon as possible
** Fix arithmetical expression syntax in rc.mailfromd
** Fix multiple from address handling
** Fix race condition when using GDBM
  

Version 3.0, 2006-11-05

* The mailfromd binary is now installed in ${prefix}/sbin.  Please,
update your scripts.  You are encouraged to update the startup script
(run `cp etc/rc.mailfromd /wherever-your-startup-lives'), since the new
version contains lots of enhancements (see below).

* The package no longer uses libmilter.

* Several `from' addresses can be specified both with polling functions
and in `#pragma option mailfrom' statement.  In this case the probing
will try each address until either the remote party accepts it or the
list of addresses is exhausted, whichever happens first.  This can
help if a remote host is picky about sender addresses. 

* After discussions with Jan, the final part of the standard poll method
has been redone.  Now the last-resort poll (i.e. querying the domain part
of the sender email, treated as an MX) is done only if the domain has
no MX records.

* New option --dump-macros shows all Sendmail macros used in the
configuration file, by milter states.  It is useful to create Sendmail
`Milter.macros.*' (confMILTER_MACROS_*) statements.

* rc.mailfromd stript two new options:

- rc.mailfromd configtest [FILE]

Checks configuration file syntax.  If FILE is not given, the default
one is assumed.

- rc.mailfromd macros [-c] [FILE]

Generate milter export statements for Sendmail configuration files.
Optional FILE specifies alternative mailfromd configuration file.
By default, `.mc' statements are generated.  Specifying `-c' option
instructs the script to create `.cf' statements instead.

* New pragmatic options `connect-retry' and `connect-timeout' set retry
count and timeout values for initial connections.  The corresponding values 
for I/O operations are set using `io-retry' and `io-timeout' options.
The pragmatic options `retry' and `timeout' are retained for backward
compatibility.  They are synonymous to their `io-' counterparts.  The
default values are:

#pragma option connect-retry 1
#pragma option connect-timeout 30
#pragma option io-retry 3
#pragma option io-timeout 3

* New function `sa' checks the message for spam via SpamAssasin spamd
interface.  It supplies additional data via the global variables
sa_score, sa_threshold, and sa_keywords.

* New function `clamav' checks the message for viruses via ClamAV daemon
interface.  Additional data (the virus name, if found) is stored in
the global variable clamav_virus_name.

* New function `ismx' returns true if the IP address or hostname given by its
second argument is one of the MX records of the domain name given by
the first argument.
 
* New variables `last_poll_host', `last_poll_send', `last_poll_recv' contain
the host name of the lastly polled host, the command sent and the
first line of the reply received.  The variables are set by polling
functions. 

* New variable `cache_used' is set to true if cache data were used instead of
polling, and to false otherwise.  The variable is set by stdpoll and
strictpoll built-in functions (and by `on poll' statement, accordingly).

* New string functions: `length' and `substring'

* Here document syntax expanded.

** Removing leading whitespace

Inserting a single space between the dash and the terminator word in
the beginning of the here-document construct, as in:

<<- WORD
...
WORD

instructs parser to remove leading white space characters
from each line of the document body, including terminating WORD.

** Expansion of macros and variables

Variables and sendmail macros are expanded when used within a
double-quoted string or a here-document body.  The variable expansion
and backslash interpretation is suppressed by quoting the WORD, e.g.:

<<\WORD
...
WORD

or

<<'WORD'
...
WORD

** Numeric escape sequences

Two new kinds of escape sequences are supported:

 \0ooo, where o is any octal digit
 \xhh, where h is any hex digit

** Back-references.

References to the parenthesized subexpressions of the previous regular
expression are expanded both in the code and in double-quoted string
literals.  For example:

 if domainpart $f matches '\(.*\).com'
   set d \1
 fi  

* Bugfixes
** Fix berkeley 4.x support
** Fix expiration of the greylist and rate databases.
** Fix returning multiline replies.  The last line of the reply was not taken
into account unless it ended with a newline. 
** Fix type casting of arguments to user-defined functions
** Fix argument passing in function calls generated for `on poll' statements.


Version 2.0

* Program requires Mailutils version 1.0 or newer
* Support for old DBM and NDBM has been withdrawn.
* Added support for Berkeley DB versions 3.x and 4.x

* INCOMPATIBLE CHANGES

** To use version 1.x configuration files, the following changes should
be applied to them:

 1. The entire code section should be enclosed in the following
    statement:

    prog envfrom
    do
	...
    done

    See the section `Handler declarations' below for the detailed
    description.
 2. Convert any `rate' statements to function calls, e.g.:

        if rate $f 180 / minute

    should be rewritten as

        if rate($f, interval(minute)) > 180

    See the section `rate(key, interval)' below for the detailed description.
    See also section "Special test functions" in the mailfromd documentation.

** Format of cache and rates database has changed: the key field now
includes the trailing nul character, which is also reflected in its
length.  This allows for empty (zero-length) keys.  To convert existing
databases to the new format, run 

   mailfromd --compact --all

after compiling the package.

** The database management options (--list,--delete,--expire) do not take
any argument.  To specify that the option refers to the rate database,
use --format=rate option.

* Language features

** Compiled code

Configuration file handling completely rewritten.  The file is parsed
into a pseudo-code program, which is executed considerably faster than
the parse tree in 1.x branch.

** Sendmail macros

Multiletter Sendmail macros can be used with and without
surrounding curly braces, i.e. ${rcpt_count} and $rcpt_count are both
valid. 

** File inclusion

Configuration file can include other files.  The syntax is:

#include "FILENAME"

Inclusion depth is not limited.

** Adjacent expressions concatenate:

  $f => "gray@gnu.org.ua"
  ${client_addr} => "127.0.0.1"

  $f "-" $client_addr => "gray@gnu.org.ua-127.0.0.1"

** Arithmetical expressions

The four usual arithmetical expressions are now supported.

** Comparisons

In addition to equal (=) and not equal (!=) the following comparison
operators are supported: <, <=, >, >=.  Their precedence and
associativity is the same as in C.

** Type casting

The rules for implicit type casts are:

  1. Both arguments to an arithmetical operation are cast to numeric
     types.
  2. All arguments of the concatenation operation are cast to string
     type. 
  3. Both arguments to `match' or `fnmatch' function are cast to
     string type.
  4. The argument of the unary negation (arithmetical or boolean) is
     cast to numeric type.
  5. Otherwise, if the arguments to a binary operation have different
     types, the right-hand side argument is cast to the type of the
     left-hand side argument.

There is no special syntactic sugar for explicit type casting.  To
cast an expression to string, concatenate an empty string to it:

  %var ""

To cast an expression to numeric, add it to zero:

  %var + 0

** Single-quoted strings

In addition to double-quoted strings, single-quoted ones are also
supported.  The single-quoted strings are not subject to backslash
expansion, thus they are particularly useful for writing regular
expressions: 

  if $f matches '.*\.com'

** Splitting strings between several lines.

Double-quoted strings can be split over several lines, by placing
a backslash immediately before the newline.  For example:

"A very\
 long string"

produces "A very long string". 
  
** Here document syntax and multiline sendmail replies

Mailfromd supports "here document" syntax:

<<[-]WORD
...
WORD

Optional '-' instructs parser to remove leading tabulation characters
from each line of the document body, including terminating WORD.  This
allows for here documents to follow normal program indentation. The
backslash expansion is performed on the contents of the here document,
unless WORD is quoted (i.e. either 'WORD' or \WORD).

This is particularly useful in providing multiline Sendmail replies:

  reject 550 5.0.0 <<-EOT
	 This service is not available now.
	 Please, refer to http://some.site for more information
	 on the subject.
	 EOT	 

** Handler declarations

The program consists of a set of handler declarations.  Each handler
is defined as

    prog STATE
    do
       ...
    done

where `...' represents the actual code, and STATE is the milter state
this handler is defined for. Recognized milter states are: helo,
envfrom, envrcpt, header, eoh, body, eom.

** Positional arguments ($N notation)

Handlers can take several positional arguments, which can be
dereferenced in the program using $N notation, where N is a decimal
number.  The semantics of the positional arguments depends on the state
for which the handler is designed.  For example:

    prog helo
    do
	/* For helo, $1 means helo domain */
	if $1 matches "gnu.org.ua"
	   ...
	fi
    done   

** Internal variables (% notation)

Mailfromd variables can be defined using `set' statement:

  set VAR expr

where VAR is the variable name.  The variables are dereferenced using
%VAR notation.  Their values are retained between handlers, for
example:

    prog helo
    do
	set helo_domain $1
    done

    prog envfrom
    do
	if $f = "gray" and %helo_domain matches "gnu.org.ua"
	   ...
	fi
    done 

The `set' statement can be used both inside and outside of program
handlers or functions.  When used outside them, it declares a global
variable and assigns it an initial value.  The variable will be
initialized to that value at the beginning of each message.
    
** Built-in function syntax

Built-in functions of more than 1 argument are invoked using the usual
C-like syntax, e.g.:

    name(arg1, arg2, arg3)

Built-in functions of one argument can be invoked with or without
parentheses:

    name arg
    name(arg)

** User defined functions

User defined functions are declared using the following syntax:

func NAME ( TYPELIST ) returns TYPE
do
	...
done

where NAME is the name of the function, TYPELIST is a comma-separated
list of parameter types, and TYPE is the return type of the
function. Types are designated by a single letter: 'n' denotes the numeric
type, 's' denotes the string type.

Within the function body, the arguments are denoted using positional
argument notation (see `Positional arguments' above).  The `return'
statement is used to return a value from the function.  For example:

func sum(n,n) returns n
do
	return $1 + $2;
done

** Switch statement

The mailfromd language now has switch statement.  Its syntax is
similar to that of C:

switch EXPR
do
   case VAL [or VAL...] :
	STMT
	.
	.
	.
   default:
	STMT		
done   

where EXPR is any valid mailfromd expression, VAL is a literal value
(numeric or string), STMT is any mailfromd statement or a list of
statements.  For example:

switch %x
do
	case 1 or 2:
	     echo "Accepting mail"
	     accept

	default:
	     reject
done

** Catch statement

The new `catch' statement can be used to handle exceptional conditions
occurring within a function.  An exceptional condition is signalled
when the filter script program encounters a condition it is not able
to handle.  See the documentation, section "Exceptions" for the
details.

The syntax of the `catch' statement is:

catch EXCEPTION-LIST
do
   HANDLER-BODY
done

where EXCEPTION-LIST is the list of exception types, separated by the
word `or'.  Special form `catch *' catches all exceptions.

The HANDLER-BODY is the list of statements comprising the handler body.

** On statement

The support for `on' statement has been entirely rewritten.  The
statement is implemented as a wrapper around `catch'.  Instead of
`poll' you can specify any function call as its selector value.

For example, this statement:

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

is actually a shortcut for:

function poll_wrapper(s) returns n
do
        catch &success or &not_found or &failure or &temp_failure
        do
              return $1
        done
        return stdpoll($1, %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

(See also the description of %ehlo_domain and %mailfrom_address variables)

** MX matching

The `matches' and `fnmatches' operations has been extended to allow
operations on MX lists.  The test

   if ${client_addr} mx matches '.*\.gnu\.org'

yields true if the list of MXs for the IP address ${client_addr}
contains a host name that matches the regular expression
'.*\.gnu\.org'.  The left side argument can be either a hostname or IP
address or an RFC 2822 email address.  In the latter case, its domain
part is used to query for the MX records.

The similar 'mx fnmatches' construction is also available.

Both `mx' tests can throw one of the following exceptions: not_found,
failure, temp_failure.

** #pragma database

New pragma `database' controls various aspects of databases used by
the program.  The pragma has four forms:

1. Store the database DBNAME in the given FILENAME

   #pragma database DBNAME file FILENAME

2. Set the expiration interval for DBNAME:
   
   #pragma database DBNAME expire-interval INTERVAL

   INTERVAL can be any valid interval specification (see the next 
   section).

If DBNAME is "cache", two additional forms are understood:
   
3. Set the expiration period for positive cache records:

   #pragma database cache positive-expire-interval INTERVAL

4. Set the expiration period for negative cache records:

   #pragma database cache negative-expire-interval INTERVAL

The forms 3. and 4. are equivalent to

   #pragma option positive-expire-interval INTERVAL
and
   #pragma option negative-expire-interval INTERVAL

correspondingly.           

** Time interval specification

  Time intervals can be specified as an English text, e.g. "1 hour 35
minutes".  The following pragmas take a time interval specification as their
argument:

#pragma option timeout
#pragma option milter-timeout
#pragma option expire-interval
#pragma option negative-expire-interval
#pragma option positive-expire-interval
#pragma option rates-expire-interval
#pragma option lock-retry-timeout

* New built-in functions:

** dbmap(dbname, key)

Returns true if `key' is found in the database file `dbname', E.g.:

   dbmap("/etc/mail/aliases.db", $f)
   
** domainpart(str)

Returns the domain part of `str' if it is a valid email address,
otherwise returns `str' itself. 

** greylist(key, interval)

Returns true if the `key' is found in the greylist database
(controlled by `#pragma database greylist' pragma).  The argument
`interval' gives the greylisting interval in seconds.  The function
sets internal variable %greylist_seconds_left to the number of seconds
left to the end of greylisting period.  Sample usage:

 set gltime 500
 if greylist(${client_addr} "-" $f "-" ${rcpt_addr}, %gltime)
   if %greylist_seconds_left = %gltime
      tempfail 470 "You are greylisted for " %gltime " seconds"
   else   
      tempfail 470 "Still greylisted for " %greylist_seconds_left "seconds"
   fi
 fi		     

** hasmx(host)

Returns true if `host' has any MX records.  The function can throw one
of the following exceptions: not_found, failure, temp_failure.

** interval(string)

Converts its argument, which should be a valid time interval
specification, to seconds

** localpart(str)

Returns the local part of `str' if it is a valid email address,
otherwise returns unchanged `str'. 

** match_cidr(ip, cidr)

Returns true if the IP address `ip' matches the network block `cidr'.
For example:

  match_cidr(${client_addr}, "213.130.0.0/19")

Possible exceptions: invip, if the first parameter is not a valid IP,
and invcidr if the second parameter is not a valid CIDR.
  
** stdpoll(email, domain, mailfrom)

Performs standard poll for `email', using `domain' as EHLO domain and
`mailfrom' as MAIL FROM: address.  Returns 0 or 1 depending on the
result of the test.  Can raise one of the following exceptions:
failure, temp_failure.

In `on' statement context, it is synonymous to `poll' 
without explicit `host'. 

** strictpoll(email, domain, mailfrom, host)

Performs strict poll for `email' on host `host'.  See the description
of stdpoll for the detailed information.

In `on' statement context, it is synonymous to `poll host'. 

** _pollhost(ip, email, domain, mailfrom)

Poll SMTP host `ip' for email address `email', using `domain' as EHLO
domain and `mailfrom' as MAIL FROM: address.  Returns 0 or 1 depending
on the result of the test.  In contrast to strictpoll function, this
function does not use cache database and does not fall back to MX poll
if the poll tempfails.  The function can throw one of the following
exceptions: failure, temp_failure.

** _pollmx(domain, email, domain, mailfrom)

Poll MX-s of the `domain' for email address `email', using `domain' as EHLO
domain and `mailfrom' as MAIL FROM: address.  Returns 0 or 1 depending
on the result of the test.  In contrast to stdpoll function, _pollmx
function does not use cache database and does not fall back to host poll
if the poll fails.  The function can throw one of the following
exceptions: failure, temp_failure.

** tolower(string)

Returns a copy of the string str, with all the upper-case characters
in string translated to their corresponding lower-case counterparts.
Non-alphabetic characters are left unchanged.

** toupper(string)

Returns a copy of the string str, with all the lower-case characters
in string translated to their corresponding upper-case counterparts.
Non-alphabetic characters are left unchanged.

** rate(key, interval)

Returns the mail sending rate for `key' per `interval'.  This function
replaces `rate' statement from 1.x branch.  To convert old rate
statements use the following algorithm:

Old statement: if rate KEY LIMIT '/' EXPR
New statement: if rate(KEY, interval("EXPR")) > LIMIT

For example:

Old statement: rate $f 180 / 1 hour 25 minutes
New statement: if rate($f, interval("1 hour 25 minutes")) > 180

** resolve(host)

Returns the IP address corresponding to `host' or "0" if it cannot be
resolved.

** validuser(user)

Returns true if `user' is a valid local account.  It uses mailutils
authentication mechanisms.

* Global variables

** %ehlo_domain

Name of the domain used by polling functions in EHLO or HELO command.
It is set by `#pragma option ehlo' directive, or via --ehlo command
line option.  

** %mailfrom_address

Email address used by polling functions in 'MAIL FROM' command.  Set
by `#pragma option mailfrom' directive or via --mailfrom command line
option. 

** %rcpt_count

The variable %rcpt_count keeps the number of recipients given so far.
The variable is defined in the envrcpt state. 

* Database expiration

The operation of `mailfromd --expire' has been completely redesigned
to avoid skipping some keys when using GDBM.

* Database compaction

New option --compact starts "database compaction" process,
which removes all expired entries and empty blocks from the database. 
It also converts any obsolete (not nul-terminated) keys to
nul-terminated ones.  During this process the original database file is
locked for writing, so the running mailfromd instance is able to read
entries from it, but cannot write or update it.

The existed database will be replaced with the compacted version only
if there were no errors during the process.  If you wish to ignore any
failed reads (keys that were not retrieved), use the 
--ignore-failed-reads option. 

* Database locking

Before accessing, any database file is locked using kernel locking.
By default, if the first attempt to lock the file fails, two more
attempts are undertaken in 1 second intervals.  If the lock cannot be
acquired after the last attempt, the database file is opened in read-
only mode.  The number of locking attempt and the timeout value are
controlled by command line options --lock-retry-count and
--lock-retry-timeout, or the corresponding pragmas:

#pragma option lock-retry-count
#pragma option lock-retry-timeout

* Selecting the database format and file

** New option -H (--format) specifies which format is the database being
operated upon by any of the database management options.  Recognized
formats are:

  cache        Poll cache database
  rate         Sending rate database
  greylist     Greylisting database (see below)

** New option --all can be used with one of the options --expire or
--compact to apply the operation to all configured databases.  This is
useful to invoke mailfromd as a crontab job.
  
** New option --file allows to explicitely specify the database file name.
Notice, that in contrast to the previous version, the name should
include the suffix.

* Debugging

** New option --lint (-l, --syntax-check): check the configuration file
syntax.  

** The argument to --debug option should be the numeric debug level.  The use 
of characters 'c', 'd', 'l', 'y' is discouraged (although still
supported for a while).  See below for the alternatives.

** New option --dump-code dumps the listing of the assembled code on screen
(similar to the earlier --debug=c)

** New option --dump-tree dumps the parse tree in human-readable form
(earlier --debug=d option)

** New option --dump-grammar-trace prints grammar parser traces while parsing
the configuration file (earlier --debug=g option).

** New option --dump-lex-trace dumps lexical analizer traces
(earlier --debug=l option).

** New option -L (--log-tag) sets the identifier used in syslog messages.

** New option --source-info includes source line information into the
debugging messages.  Previously this information was included by default.

* New option --group (-g) allows to retain the given supplementary group when
switching to user privileges.  By default mailfromd does not retain
any supplementary groups.  The use of this option may be necessary if
your mailfromd script needs to access some databases that have
restrictive access privileges.  For example, if mailfromd runs with
the privileges of user 'mail' (the default) and needs to access
/etc/mail/aliases.db, which is usually owned by root.smmsp and has
access rights 0640, you should run

         mailfromd --group smmsp

* New option --source (-S) sets the source address mailfromd will use for
any TCP connections.  The configuration file equivalent is

         #pragma option source

* To set up the local account validation (see the description of `validuser'
function above) mailfromd uses authentication options from mailutils.
See the mailutils documentation, chapter `Authorization and Authentication
Principles' for the detailed description of these. 	 

* Code generation optimized to avoid unnecessary instructions and to reduce
code size.

* MX lookups no longer recurse to parent domains.  Previously, if
the domain "some.domain.com" had no MX records, mailfromd would lookup for
MXs of "domain.com" and use these instead.  This is no longer the
case.  

* Added testsuite


Version 1.4

* Configuration

Added possibility to link against the forked version of libmilter
(--with-forks). The patch for sendmail-8.13.1 is included
(etc/sendmail-8.13.1.diff).

* Configuration file

** New unary expression `listens' checks if the host listens on port 25.
** Several `#pragma option relay' statements accumulate

* Bugfixes

** Fixed coredump on incorrect libmilter socket specification.
** Fixed `poll for EMAIL as EMAIL'.


Version 1.3

* Rewritten DNS resolver functions in order to take into account CNAMEs.
* Updated Makefiles to allow for compilation with the CVS Mailutils
* Improved documentation.


Version 1.2

* Implemented sending rate control.

This feature allows to impose a limit on the number of messages a
user can send within a given interval. If this number is exceeded, the
connection is refused until enough time passes to keep the rate within
the given limit.


Version 1.1

Mostly bugfixes.

Version 1.0

Lots of major improvements. Implemented two methods of sender address
verification, controlled by a sophisticated configuration file. Sender
domains and emails can also be distinguished basing on POSIX regex or
shell-style globbing patterns.


Version 0.2

First release.


=========================================================================
Copyright information:

Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff

   Permission is granted to anyone to make or distribute verbatim copies
   of this document as received, in any medium, provided that the
   copyright notice and this permission notice are preserved,
   thus giving the recipient permission to redistribute in turn.

   Permission is granted to distribute modified versions
   of this document, or of portions of it,
   under the above conditions, provided also that they
   carry prominent notices stating who last changed them.

Local variables:
mode: outline
paragraph-separate: "[	]*$"
eval: (add-hook 'write-file-hooks 'time-stamp)
time-stamp-start: "changes. "
time-stamp-format: "%:y-%02m-%02d"
time-stamp-end: "\n"
end:

Return to:

Send suggestions and report system problems to the System administrator.