summaryrefslogtreecommitdiffstats
path: root/src/global/dsn_filter.c
blob: 3ffeb9fea61cd81787a11e85e7a9c05a48e9d141 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*++
/* NAME
/*	dsn_filter 3
/* SUMMARY
/*	filter delivery status code or text
/* SYNOPSIS
/*	#include <dsn_filter.h>
/*
/*	DSN_FILTER *dsn_filter_create(
/*	const char *title,
/*	const char *map_names)
/*
/*	DSN	*dsn_filter_lookup(
/*	DSN_FILTER *fp,
/*	DSN	*dsn)
/*
/*	void	dsn_filter_free(
/*	DSN_FILTER *fp)
/* DESCRIPTION
/*	This module maps (bounce or defer non-delivery status code
/*	and text) into replacement (bounce or defer non-delivery
/*	status code and text), or maps (success status code and
/*	text) into replacement (success status code and text). Other
/*	DSN attributes are passed through without modification.
/*
/*	dsn_filter_create() instantiates a delivery status filter.
/*
/*	dsn_filter_lookup() queries the specified filter. The input
/*	DSN must be a success, bounce or defer DSN. If a match is
/*	found a non-delivery status must map to a non-delivery
/*	status, a success status must map to a success status, and
/*	the text must be non-empty. The result is a null pointer
/*	when no valid match is found. Otherwise, the result is
/*	overwritten upon each call.  This function must not be
/*	called with the result from a dsn_filter_lookup() call.
/*
/*	dsn_filter_free() destroys the specified delivery status
/*	filter.
/*
/*	Arguments:
/* .IP title
/*	Origin of the mapnames argument, typically a configuration
/*	parameter name. This is reported in diagnostics.
/* .IP mapnames
/*	List of lookup tables, separated by whitespace or comma.
/* .IP fp
/*	filter created with dsn_filter_create()
/* .IP dsn
/*	A success, bounce or defer DSN data structure. The
/*	dsn_filter_lookup() result value is in part a shallow copy
/*	of this argument.
/* SEE ALSO
/*	maps(3) multi-table search
/* DIAGNOSTICS
/*	Panic: invalid dsn argument; recursive call. Fatal error:
/*	memory allocation problem. Warning: invalid DSN lookup
/*	result.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

 /*
  * System libraries.
  */
#include <sys_defs.h>

 /*
  * Utility library.
  */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>

 /*
  * Global library.
  */
#include <maps.h>
#include <dsn.h>
#include <dsn_util.h>
#include <maps.h>
#include <dsn_filter.h>

 /*
  * Private data structure.
  */
struct DSN_FILTER {
    MAPS   *maps;			/* Replacement (status, text) */
    VSTRING *buffer;			/* Status code and text */
    DSN_SPLIT dp;			/* Parsing aid */
    DSN     dsn;			/* Shallow copy */
};

 /*
  * SLMs.
  */
#define STR(x) vstring_str(x)

/* dsn_filter_create - create delivery status filter */

DSN_FILTER *dsn_filter_create(const char *title, const char *map_names)
{
    static const char myname[] = "dsn_filter_create";
    DSN_FILTER *fp;

    if (msg_verbose)
	msg_info("%s: %s %s", myname, title, map_names);

    fp = (DSN_FILTER *) mymalloc(sizeof(*fp));
    fp->buffer = vstring_alloc(100);
    fp->maps = maps_create(title, map_names, DICT_FLAG_LOCK);
    return (fp);
}

/* dsn_filter_lookup - apply delivery status filter */

DSN    *dsn_filter_lookup(DSN_FILTER *fp, DSN *dsn)
{
    static const char myname[] = "dsn_filter_lookup";
    const char *result;
    int     ndr_dsn = 0;

    if (msg_verbose)
	msg_info("%s: %s %s", myname, dsn->status, dsn->reason);

    /*
     * XXX Instead of hard-coded '4' etc., use some form of encapsulation
     * when reading or updating the status class field.
     */
#define IS_SUCCESS_DSN(s) (dsn_valid(s) && (s)[0] == '2')
#define IS_NDR_DSN(s) (dsn_valid(s) && ((s)[0] == '4' || (s)[0] == '5'))

    /*
     * Sanity check. We filter only success/bounce/defer DSNs.
     */
    if (IS_SUCCESS_DSN(dsn->status))
	ndr_dsn = 0;
    else if (IS_NDR_DSN(dsn->status))
	ndr_dsn = 1;
    else
	msg_panic("%s: dsn argument with bad status code: %s",
		  myname, dsn->status);

    /*
     * Sanity check. A delivery status filter must not be invoked with its
     * own result.
     */
    if (dsn->reason == fp->dsn.reason)
	msg_panic("%s: recursive call is not allowed", myname);

    /*
     * Look up replacement status and text.
     */
    vstring_sprintf(fp->buffer, "%s %s", dsn->status, dsn->reason);
    if ((result = maps_find(fp->maps, STR(fp->buffer), 0)) != 0) {
	/* Sanity check. Do not allow success<=>error mappings. */
	if ((ndr_dsn == 0 && !IS_SUCCESS_DSN(result))
	    || (ndr_dsn != 0 && !IS_NDR_DSN(result))) {
	    msg_warn("%s: bad status code: %s", fp->maps->title, result);
	    return (0);
	} else {
	    vstring_strcpy(fp->buffer, result);
	    dsn_split(&fp->dp, "can't happen", STR(fp->buffer));
	    (void) DSN_ASSIGN(&fp->dsn, DSN_STATUS(fp->dp.dsn),
			      (result[0] == '4' ? "delayed" :
			       result[0] == '5' ? "failed" :
			       dsn->action),
			      fp->dp.text, dsn->dtype, dsn->dtext,
			      dsn->mtype, dsn->mname);
	    return (&fp->dsn);
	}
    }
    return (0);
}

/* dsn_filter_free - destroy delivery status filter */

void    dsn_filter_free(DSN_FILTER *fp)
{
    static const char myname[] = "dsn_filter_free";

    if (msg_verbose)
	msg_info("%s: %s", myname, fp->maps->title);

    maps_free(fp->maps);
    vstring_free(fp->buffer);
    myfree((void *) fp);
}