Copyright updates:
[exim.git] / src / src / header.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* Copyright (c) The Exim Maintainers 2020 - 2021 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9
10 #include "exim.h"
11
12
13 /*************************************************
14 *         Test a header for matching name        *
15 *************************************************/
16
17 /* This function tests the name of a header. It is made into a function because
18 it isn't just a string comparison: spaces and tabs are permitted between the
19 name and the colon. The h->text field should nowadays never be NULL, but check
20 it just in case.
21
22 Arguments:
23   h         points to the header
24   name      the name to test
25   len       the length of the name
26   notdel    if TRUE, force FALSE for deleted headers
27
28 Returns:    TRUE or FALSE
29 */
30
31 BOOL
32 header_testname(header_line *h, const uschar *name, int len, BOOL notdel)
33 {
34 uschar *tt;
35 if (h->type == '*' && notdel) return FALSE;
36 if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE;
37 tt = h->text + len;
38 while (*tt == ' ' || *tt == '\t') tt++;
39 return *tt == ':';
40 }
41
42 /* This is a copy of the function above, only that it is possible to pass
43    only the beginning of a header name. It simply does a front-anchored
44    substring match. Arguments and Return codes are the same as for
45    header_testname() above. */
46
47 BOOL
48 header_testname_incomplete(header_line *h, const uschar *name,
49     int len, BOOL notdel)
50 {
51 if (h->type == '*' && notdel) return FALSE;
52 if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE;
53 return TRUE;
54 }
55
56
57 /*************************************************
58 *         Add new header backend function        *
59 *************************************************/
60
61 /* The header_last variable points to the last header during message reception
62 and delivery; otherwise it is NULL. We add new headers only when header_last is
63 not NULL. The function may get called sometimes when it is NULL (e.g. during
64 address verification where rewriting options exist). When called from a filter,
65 there may be multiple header lines in a single string.
66
67 This is an internal static function that is the common back end to the external
68 functions defined below. The general interface allows the header to be inserted
69 before or after a given occurrence of a given header.
70
71 (a) if "name" is NULL, the header is added at the end of all the existing
72     headers if "after" is true, or at the start if it is false. The "topnot"
73     flag is not used.
74
75 (b) If "name" is not NULL, the first existing header with that name is sought.
76     If "after" is false, the new header is added before it. If "after" is true,
77     a check is made for adjacent headers with the same name, and the new header
78     is added after the last of them. If a header of the given name is not
79     found, the new header is added first if "topnot" is true, and at the bottom
80     otherwise.
81
82 Arguments:
83   after     TRUE for "after", FALSE for "before"
84   name      name if adding at a specific header, else NULL
85   topnot    TRUE to add at top if no header found
86   type      Exim header type character (htype_something)
87   format    sprintf format
88   ap        va_list value for format arguments
89
90 Returns:    pointer to header struct (last one, if multiple added)
91 */
92
93 static header_line *
94 header_add_backend(BOOL after, uschar *name, BOOL topnot, int type,
95   const char *format, va_list ap)
96 {
97 header_line *h, *new = NULL;
98 header_line **hptr;
99
100 uschar * p, * q, * buf;
101 gstring gs;
102
103 if (!header_last) return NULL;
104
105 gs.s = buf = store_get(HEADER_ADD_BUFFER_SIZE, FALSE);
106 gs.size = HEADER_ADD_BUFFER_SIZE;
107 gs.ptr = 0;
108
109 if (!string_vformat(&gs, SVFMT_REBUFFER, format, ap))
110   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "string too long in header_add: "
111     "%.100s ...", string_from_gstring(&gs));
112
113 if (gs.s != buf) store_release_above(buf);
114 gstring_release_unused(&gs);
115 string_from_gstring(&gs);
116
117 /* Find where to insert this header */
118
119 if (!name)
120   if (after)
121     {
122     hptr = &header_last->next;
123     h = NULL;
124     }
125   else
126     {
127     hptr = &header_list;
128
129     /* header_list->text can be NULL if we get here between when the new
130     received header is allocated and when it is actually filled in. We want
131     that header to be first, so skip it for now. */
132
133     if (!header_list->text)
134       hptr = &header_list->next;
135     h = *hptr;
136     }
137
138 else
139   {
140   int len = Ustrlen(name);
141
142   /* Find the first non-deleted header with the correct name. */
143
144   for (hptr = &header_list; (h = *hptr); hptr = &h->next)
145     if (header_testname(h, name, len, TRUE))
146       break;
147
148   /* Handle the case where no header is found. To insert at the bottom, nothing
149   needs to be done. */
150
151   if (!h)
152     {
153     if (topnot)
154       {
155       hptr = &header_list;
156       h = header_list;
157       }
158     }
159
160   /* Handle the case where a header is found. Check for more if "after" is
161   true. In this case, we want to include deleted headers in the block. */
162
163   else if (after)
164     for (;;)
165       {
166       if (!h->next || !header_testname(h, name, len, FALSE)) break;
167       hptr = &h->next;
168       h = h->next;
169       }
170   }
171
172 /* Loop for multiple header lines, taking care about continuations. At this
173 point, we have hptr pointing to the link field that will point to the new
174 header, and h containing the following header, or NULL. */
175
176 for (p = q = gs.s; *p; p = q)
177   {
178   for (;;)
179     {
180     q = Ustrchr(q, '\n');
181     if (!q) q = p + Ustrlen(p);
182     if (*(++q) != ' ' && *q != '\t') break;
183     }
184
185   new = store_get(sizeof(header_line), FALSE);
186   new->text = string_copyn(p, q - p);
187   new->slen = q - p;
188   new->type = type;
189   new->next = h;
190
191   *hptr = new;
192   hptr = &new->next;
193
194   if (!h) header_last = new;
195   }
196 return new;
197 }
198
199
200 /*************************************************
201 *      Add new header anywhere in the chain      *
202 *************************************************/
203
204 /* This is an external interface to header_add_backend().
205
206 Arguments:
207   after     TRUE for "after", FALSE for "before"
208   name      name if adding at a specific header, else NULL
209   topnot    TRUE to add at top if no header found
210   type      Exim header type character (htype_something)
211   format    sprintf format
212   ...       format arguments
213
214 Returns:    pointer to header struct added
215 */
216
217 header_line *
218 header_add_at_position_internal(BOOL after, uschar *name, BOOL topnot, int type,
219   const char *format, ...)
220 {
221 header_line * h;
222 va_list ap;
223 va_start(ap, format);
224 h = header_add_backend(after, name, topnot, type, format, ap);
225 va_end(ap);
226 return h;
227 }
228
229
230 /* Documented external i/f for local_scan */
231 void
232 header_add_at_position(BOOL after, uschar *name, BOOL topnot, int type,
233   const char *format, ...)
234 {
235 va_list ap;
236 va_start(ap, format);
237 (void) header_add_backend(after, name, topnot, type, format, ap);
238 va_end(ap);
239 }
240
241 /*************************************************
242 *            Add new header on end of chain      *
243 *************************************************/
244
245 /* This is now a convenience interface to header_add_backend().
246
247 Arguments:
248   type      Exim header type character
249   format    sprintf format
250   ...       arguments for the format
251
252 Returns:    nothing
253 */
254
255 void
256 header_add(int type, const char *format, ...)
257 {
258 va_list ap;
259 va_start(ap, format);
260 (void) header_add_backend(TRUE, NULL, FALSE, type, format, ap);
261 va_end(ap);
262 }
263
264
265
266 /*************************************************
267 *        Remove (mark as old) a header           *
268 *************************************************/
269
270 /* This function is used by the filter code; it is also exported in the
271 local_scan() API. If no header is found, the function does nothing.
272
273 Arguments:
274   occ           the occurrence number for multiply-defined headers
275                   <= 0 means "all"; deleted headers are not counted
276   name          the header name
277
278 Returns:        nothing
279 */
280
281 void
282 header_remove(int occ, const uschar *name)
283 {
284 int hcount = 0;
285 int len = Ustrlen(name);
286 for (header_line * h = header_list; h; h = h->next)
287   if (header_testname(h, name, len, TRUE) && (occ <= 0 || ++hcount == occ))
288     {
289     h->type = htype_old;
290     if (occ > 0) return;
291     }
292 }
293
294
295
296 /*************************************************
297 *          Check the name of a header            *
298 *************************************************/
299
300 /* This function scans a table of header field names that Exim recognizes, and
301 returns the identification of a match. If "resent" is true, the header is known
302 to start with "resent-". In that case, the function matches only those fields
303 that are allowed to appear with resent- in front of them.
304
305 Arguments:
306   h             points to the header line
307   is_resent     TRUE if the name starts "Resent-"
308
309 Returns:        One of the htype_ enum values, identifying the header
310 */
311
312 int
313 header_checkname(header_line *h, BOOL is_resent)
314 {
315 uschar *text = h->text;
316 header_name *bot = header_names;
317 header_name *top = header_names + header_names_size;
318
319 if (is_resent) text += 7;
320
321 while (bot < top)
322   {
323   header_name *mid = bot + (top - bot)/2;
324   int c = strncmpic(text, mid->name, mid->len);
325
326   if (c == 0)
327     {
328     uschar * s = text + mid->len;
329     if (Uskip_whitespace(&s) == ':')
330       return (!is_resent || mid->allow_resent)? mid->htype : htype_other;
331     c = 1;
332     }
333
334   if (c > 0) bot = mid + 1; else top = mid;
335   }
336
337 return htype_other;
338 }
339
340
341 /*************************************************
342 *       Scan a header for certain strings        *
343 *************************************************/
344
345 /* This function is used for the "personal" test. It scans a particular header
346 line for any one of a number of strings, matched caselessly either as plain
347 strings, or as regular expressions. If the header line contains a list of
348 addresses, each match is applied only to the operative part of each address in
349 the header, and non-regular expressions must be exact matches.
350
351 The patterns can be provided either as a chain of string_item structures, or
352 inline in the argument list, or both. If there is more than one header of the
353 same name, they are all searched.
354
355 Arguments:
356   name           header name, including the trailing colon
357   has_addresses  TRUE if the header contains a list of addresses
358   cond           value to return if the header contains any of the strings
359   strings        points to a chain of string_item blocks
360   count          number of inline strings
361   ...            the inline strings
362
363 Returns:         cond if the header exists and contains one of the strings;
364                    otherwise !cond
365 */
366
367
368 /* First we have a local subroutine to handle a single pattern */
369
370 static BOOL
371 one_pattern_match(uschar *name, int slen, BOOL has_addresses, uschar *pattern)
372 {
373 BOOL yield = FALSE;
374 const pcre2_code *re = NULL;
375
376 /* If the pattern is a regex, compile it. Bomb out if compiling fails; these
377 patterns are all constructed internally and should be valid. */
378
379 if (*pattern == '^') re = regex_must_compile(pattern, TRUE, FALSE);
380
381 /* Scan for the required header(s) and scan each one */
382
383 for (header_line * h = header_list; !yield && h; h = h->next)
384   {
385   if (h->type == htype_old || slen > h->slen ||
386       strncmpic(name, h->text, slen) != 0)
387     continue;
388
389   /* If the header is a list of addresses, extract each one in turn, and scan
390   it. A non-regex scan must be an exact match for the address. */
391
392   if (has_addresses)
393     {
394     uschar *s = h->text + slen;
395
396     while (!yield && *s)
397       {
398       uschar *error, *next;
399       uschar *e = parse_find_address_end(s, FALSE);
400       int terminator = *e;
401       int start, end, domain;
402
403       /* Temporarily terminate the string at the address end while extracting
404       the operative address within. */
405
406       *e = 0;
407       next = parse_extract_address(s, &error, &start, &end, &domain, FALSE);
408       *e = terminator;
409
410       /* Move on, ready for the next address */
411
412       s = e;
413       if (*s == ',') s++;
414
415       /* If there is some kind of syntax error, just give up on this header
416       line. */
417
418       if (!next) break;
419
420       /* Otherwise, test for the pattern; a non-regex must be an exact match */
421
422       yield = re
423         ? regex_match(re, next, -1, NULL)
424         : (strcmpic(next, pattern) == 0);
425       }
426     }
427
428   /* For headers that are not lists of addresses, scan the entire header line,
429   and just require "contains" for non-regex patterns. */
430
431   else
432     {
433     yield = re
434       ? regex_match(re, h->text, h->slen, NULL)
435       : (strstric(h->text, pattern, FALSE) != NULL);
436     }
437   }
438
439 return yield;
440 }
441
442
443 /* The externally visible interface */
444
445 BOOL
446 header_match(uschar *name, BOOL has_addresses, BOOL cond, string_item *strings,
447   int count, ...)
448 {
449 va_list ap;
450 int slen = Ustrlen(name);
451
452 for (string_item * s = strings; s; s = s->next)
453   if (one_pattern_match(name, slen, has_addresses, s->text))
454     return cond;
455
456 va_start(ap, count);
457 for (int i = 0; i < count; i++)
458   if (one_pattern_match(name, slen, has_addresses, va_arg(ap, uschar *)))
459     {
460     va_end(ap);
461     return cond;
462     }
463 va_end(ap);
464
465 return !cond;
466 }
467
468 /* End of header.c */