xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/dsn_filter.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: dsn_filter.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dsn_filter 3
6 /* SUMMARY
7 /*	filter delivery status code or text
8 /* SYNOPSIS
9 /*	#include <dsn_filter.h>
10 /*
11 /*	DSN_FILTER *dsn_filter_create(
12 /*	const char *title,
13 /*	const char *map_names)
14 /*
15 /*	DSN	*dsn_filter_lookup(
16 /*	DSN_FILTER *fp,
17 /*	DSN	*dsn)
18 /*
19 /*	void	dsn_filter_free(
20 /*	DSN_FILTER *fp)
21 /* DESCRIPTION
22 /*	This module maps (bounce or defer non-delivery status code
23 /*	and text) into replacement (bounce or defer non-delivery
24 /*	status code and text), or maps (success status code and
25 /*	text) into replacement (success status code and text). Other
26 /*	DSN attributes are passed through without modification.
27 /*
28 /*	dsn_filter_create() instantiates a delivery status filter.
29 /*
30 /*	dsn_filter_lookup() queries the specified filter. The input
31 /*	DSN must be a success, bounce or defer DSN. If a match is
32 /*	found a non-delivery status must map to a non-delivery
33 /*	status, a success status must map to a success status, and
34 /*	the text must be non-empty. The result is a null pointer
35 /*	when no valid match is found. Otherwise, the result is
36 /*	overwritten upon each call.  This function must not be
37 /*	called with the result from a dsn_filter_lookup() call.
38 /*
39 /*	dsn_filter_free() destroys the specified delivery status
40 /*	filter.
41 /*
42 /*	Arguments:
43 /* .IP title
44 /*	Origin of the mapnames argument, typically a configuration
45 /*	parameter name. This is reported in diagnostics.
46 /* .IP mapnames
47 /*	List of lookup tables, separated by whitespace or comma.
48 /* .IP fp
49 /*	filter created with dsn_filter_create()
50 /* .IP dsn
51 /*	A success, bounce or defer DSN data structure. The
52 /*	dsn_filter_lookup() result value is in part a shallow copy
53 /*	of this argument.
54 /* SEE ALSO
55 /*	maps(3) multi-table search
56 /* DIAGNOSTICS
57 /*	Panic: invalid dsn argument; recursive call. Fatal error:
58 /*	memory allocation problem. Warning: invalid DSN lookup
59 /*	result.
60 /* LICENSE
61 /* .ad
62 /* .fi
63 /*	The Secure Mailer license must be distributed with this software.
64 /* AUTHOR(S)
65 /*	Wietse Venema
66 /*	IBM T.J. Watson Research
67 /*	P.O. Box 704
68 /*	Yorktown Heights, NY 10598, USA
69 /*--*/
70 
71  /*
72   * System libraries.
73   */
74 #include <sys_defs.h>
75 
76  /*
77   * Utility library.
78   */
79 #include <msg.h>
80 #include <mymalloc.h>
81 #include <vstring.h>
82 
83  /*
84   * Global library.
85   */
86 #include <maps.h>
87 #include <dsn.h>
88 #include <dsn_util.h>
89 #include <maps.h>
90 #include <dsn_filter.h>
91 
92  /*
93   * Private data structure.
94   */
95 struct DSN_FILTER {
96     MAPS   *maps;			/* Replacement (status, text) */
97     VSTRING *buffer;			/* Status code and text */
98     DSN_SPLIT dp;			/* Parsing aid */
99     DSN     dsn;			/* Shallow copy */
100 };
101 
102  /*
103   * SLMs.
104   */
105 #define STR(x) vstring_str(x)
106 
107 /* dsn_filter_create - create delivery status filter */
108 
dsn_filter_create(const char * title,const char * map_names)109 DSN_FILTER *dsn_filter_create(const char *title, const char *map_names)
110 {
111     static const char myname[] = "dsn_filter_create";
112     DSN_FILTER *fp;
113 
114     if (msg_verbose)
115 	msg_info("%s: %s %s", myname, title, map_names);
116 
117     fp = (DSN_FILTER *) mymalloc(sizeof(*fp));
118     fp->buffer = vstring_alloc(100);
119     fp->maps = maps_create(title, map_names, DICT_FLAG_LOCK);
120     return (fp);
121 }
122 
123 /* dsn_filter_lookup - apply delivery status filter */
124 
dsn_filter_lookup(DSN_FILTER * fp,DSN * dsn)125 DSN    *dsn_filter_lookup(DSN_FILTER *fp, DSN *dsn)
126 {
127     static const char myname[] = "dsn_filter_lookup";
128     const char *result;
129     int     ndr_dsn = 0;
130 
131     if (msg_verbose)
132 	msg_info("%s: %s %s", myname, dsn->status, dsn->reason);
133 
134     /*
135      * XXX Instead of hard-coded '4' etc., use some form of encapsulation
136      * when reading or updating the status class field.
137      */
138 #define IS_SUCCESS_DSN(s) (dsn_valid(s) && (s)[0] == '2')
139 #define IS_NDR_DSN(s) (dsn_valid(s) && ((s)[0] == '4' || (s)[0] == '5'))
140 
141     /*
142      * Sanity check. We filter only success/bounce/defer DSNs.
143      */
144     if (IS_SUCCESS_DSN(dsn->status))
145 	ndr_dsn = 0;
146     else if (IS_NDR_DSN(dsn->status))
147 	ndr_dsn = 1;
148     else
149 	msg_panic("%s: dsn argument with bad status code: %s",
150 		  myname, dsn->status);
151 
152     /*
153      * Sanity check. A delivery status filter must not be invoked with its
154      * own result.
155      */
156     if (dsn->reason == fp->dsn.reason)
157 	msg_panic("%s: recursive call is not allowed", myname);
158 
159     /*
160      * Look up replacement status and text.
161      */
162     vstring_sprintf(fp->buffer, "%s %s", dsn->status, dsn->reason);
163     if ((result = maps_find(fp->maps, STR(fp->buffer), 0)) != 0) {
164 	/* Sanity check. Do not allow success<=>error mappings. */
165 	if ((ndr_dsn == 0 && !IS_SUCCESS_DSN(result))
166 	    || (ndr_dsn != 0 && !IS_NDR_DSN(result))) {
167 	    msg_warn("%s: bad status code: %s", fp->maps->title, result);
168 	    return (0);
169 	} else {
170 	    vstring_strcpy(fp->buffer, result);
171 	    dsn_split(&fp->dp, "can't happen", STR(fp->buffer));
172 	    (void) DSN_ASSIGN(&fp->dsn, DSN_STATUS(fp->dp.dsn),
173 			      (result[0] == '4' ? "delayed" :
174 			       result[0] == '5' ? "failed" :
175 			       dsn->action),
176 			      fp->dp.text, dsn->dtype, dsn->dtext,
177 			      dsn->mtype, dsn->mname);
178 	    return (&fp->dsn);
179 	}
180     }
181     return (0);
182 }
183 
184 /* dsn_filter_free - destroy delivery status filter */
185 
dsn_filter_free(DSN_FILTER * fp)186 void    dsn_filter_free(DSN_FILTER *fp)
187 {
188     static const char myname[] = "dsn_filter_free";
189 
190     if (msg_verbose)
191 	msg_info("%s: %s", myname, fp->maps->title);
192 
193     maps_free(fp->maps);
194     vstring_free(fp->buffer);
195     myfree((void *) fp);
196 }
197