/* -*- buffer-read-only: t -*- vi: set ro: THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */ #line 1 "header_rename.mf4" /* Header rename functions -*- mfl -*- Copyright (C) 2010-2020 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ module 'header_rename'. require status func header_rename(string name, string newname; number idx) do if not $#>@idx set idx 1 fi number msg current_message() try do string value message_find_header(msg, name, idx) header_delete(name, idx) header_add(newname, value) done catch e_range do pass done done /* * Prefixes all headers with the given NAME with PREFIX. * If PREFIX is not supplied, removes the matching headers. */ func header_prefix_all(string name; string prefix) do number msg current_message() loop for number i message_header_count(msg, name), while i >= 1, set i i - 1 do if $#>@prefix header_rename(name, prefix . name, i) else header_delete(name, i) fi done done /* * header_prefix_pattern(string pattern; string prefix) * ---------------------------------------------------- * Prefixes all headers that match PATTERN with PREFIX * * Implementing such functionality faces considerable difficulties because * the milter API doesn't provide calls for iterating over all message * headers. * The basic algorithm is: * 1. get the list of available messages from the captured message * 2. for each header that matches pattern * 2.1. save its value * 2.2. delete that header * 2.3. insert new header with the name constructed as concatenation * or the prefix and original header name and value saved in 2.1 * * The 2.3 should ideally preserve locations of the renamed headers. * However, that is impossible using the Milter API. It was consented then * that it should preserve relative ordering of the renamed headers. * * The initial approach was to use header_add function. Unfortunately, it * follows whimsical rules when selecting the position where to insert the * header. Normally, headers are added to the end of the list. However, if * a header with such name already exists, the new one is added before it. * The flags assigned to the header (H_USER and H_TRACE, in Sendmail) alter * that decision. Consequently, using header_add produces unjustifiably * complex code. * * Then, there is header_insert. Formally, it inserts the header at the given * index in the header list. However, the header list it operates on is not * the list of headers as present in the message, but an opaque internal list * maintained in MTA, whose contents is unknown to the filter and is updated * after the filter finishes its job. The only more or less reliable way of * using this call is with index 0, which is supposed to add the header at * the beginning of that internal list. At least, this ensures that the * relative ordering of the renamed headers is preserved. * Obviously the renamed headers must be inserted in the reverse order. * * So, the modified algorithm is: * 1. Build a temporary list of headers that match the pattern. Each * element of the list contains the header name and its instance number. * The list is sorted in reverse order: the last header in the message * appears at the start of the list. * Here, the difficulty is the lack of dictionary data type in MFL, so * the list is built as colon-delimited list of header specifications. * Each specification is HDR/N, where HDR is the header name and N is * its instance number. * 2. Iterate over that list using the `string_list_iterate' macro. For * each element in the list * 2.1. save the header value * 2.2. delete that header * 2.3. insert the renamed header in position 0. */ #pragma regex push +extended func header_prefix_pattern(string pattern; string prefix) do number msg current_message() number count message_header_count(msg) string h_list ':' loop for number i 1, while i <= count, set i i + 1 do string name message_nth_header_name(msg, i) if name fnmatches pattern if h_list matches ".*:%name/([[:digit:]]+):.*" set h_list ":%name/" . (\1 + 1) . h_list else set h_list ":%name/1" . h_list fi fi done if h_list matches '^:(.+):$' set h_list \1 fi #line 135 if h_list != "" #line 135 #line 135 #line 135 loop for string __h_list_itr h_list . ":" #line 135 number __h_list_pos index(__h_list_itr, ":"), #line 135 while __h_list_pos != -1, #line 135 set __h_list_itr substr(__h_list_itr, __h_list_pos + 1) #line 135 set __h_list_pos index(__h_list_itr, ":") #line 135 do #line 135 set h_spec substr(__h_list_itr, 0, __h_list_pos) #line 135 #line 135 if h_spec matches "^(.+)/([[:digit:]]+)$" #line 135 string value message_find_header(msg, \1, \2) #line 135 header_delete(\1, \2) #line 135 if $#>@prefix #line 135 header_add(prefix . \1, value, 0) #line 135 fi #line 135 fi #line 135 done #line 135 #line 135 #line 135 fi #line 143 done #pragma regex pop