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