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