~ubuntu-branches/ubuntu/trusty/postfix/trusty-proposed

« back to all changes in this revision

Viewing changes to src/global/bounce_log.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-02-27 09:33:07 UTC
  • Revision ID: james.westby@ubuntu.com-20050227093307-cn789t27ibnlh6tf
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      bounce_log 3
 
4
/* SUMMARY
 
5
/*      bounce file API
 
6
/* SYNOPSIS
 
7
/*      #include <bounce_log.h>
 
8
/*
 
9
/*      typedef struct {
 
10
/* .in +4
 
11
/*          /* public members... */
 
12
/*          const char *recipient;
 
13
/*          const char *orig_rcpt;
 
14
/*          long        rcpt_offset;
 
15
/*          const char *dsn_status;
 
16
/*          const char *dsn_action;
 
17
/*          const char *text;
 
18
/* .in -4
 
19
/*      } BOUNCE_LOG;
 
20
/*
 
21
/*      BOUNCE_LOG *bounce_log_open(queue, id, flags, mode)
 
22
/*      const char *queue;
 
23
/*      const char *id;
 
24
/*      int     flags;
 
25
/*      int     mode;
 
26
/*
 
27
/*      BOUNCE_LOG *bounce_log_read(bp)
 
28
/*      BOUNCE_LOG *bp;
 
29
/*
 
30
/*      void    bounce_log_rewind(bp)
 
31
/*      BOUNCE_LOG *bp;
 
32
/*
 
33
/*      BOUNCE_LOG *bounce_log_forge(orig_rcpt, recipient, rcpt_offset,
 
34
/*                                      dsn_status, dsn_action, why)
 
35
/*      const char *orig_rcpt;
 
36
/*      const char *recipient;
 
37
/*      long    rcpt_offset;
 
38
/*      const char *dsn_status;
 
39
/*      const char *dsn_action;
 
40
/*      const char *why;
 
41
/*
 
42
/*      void    bounce_log_close(bp)
 
43
/*      BOUNCE_LOG *bp;
 
44
/* DESCRIPTION
 
45
/*      This module implements a bounce/defer logfile API. Information
 
46
/*      is sanitized for control and non-ASCII characters.
 
47
/*
 
48
/*      bounce_log_open() opens the named bounce or defer logfile
 
49
/*      and returns a handle that must be used for further access.
 
50
/*      The result is a null pointer if the file cannot be opened.
 
51
/*      The caller is expected to inspect the errno code and deal
 
52
/*      with the problem.
 
53
/*
 
54
/*      bounce_log_read() reads the next record from the bounce or defer
 
55
/*      logfile (skipping over and warning about malformed data)
 
56
/*      and breaks out the recipient address, the recipient status
 
57
/*      and the text that explains why the recipient was undeliverable.
 
58
/*      bounce_log_read() returns a null pointer when no recipient was read,
 
59
/*      otherwise it returns its argument.
 
60
/*
 
61
/*      bounce_log_rewind() is a helper that seeks to the first recipient
 
62
/*      in an open bounce or defer logfile (skipping over recipients that
 
63
/*      are marked as done). The result is 0 in case of success, -1 in case
 
64
/*      of problems.
 
65
/*
 
66
/*      bounce_log_forge() forges one recipient status record
 
67
/*      without actually accessing a logfile.
 
68
/*      The result cannot be used for any logfile access operation
 
69
/*      and must be disposed of by passing it to bounce_log_close().
 
70
/*
 
71
/*      bounce_log_close() closes an open bounce or defer logfile and
 
72
/*      releases memory for the specified handle. The result is non-zero
 
73
/*      in case of I/O errors.
 
74
/*
 
75
/*      Arguments:
 
76
/* .IP queue
 
77
/*      The bounce or defer queue name.
 
78
/* .IP id
 
79
/*      The message queue id of bounce or defer logfile. This
 
80
/*      file has the same name as the original message file.
 
81
/* .IP flags
 
82
/*      File open flags, as with open(2).
 
83
/* .IP more
 
84
/*      File permissions, as with open(2).
 
85
/* .PP
 
86
/*      Results:
 
87
/* .IP recipient
 
88
/*      The final recipient address in RFC 822 external form, or <>
 
89
/*      in case of the null recipient address.
 
90
/* .IP orig_rcpt
 
91
/*      Null pointer or the original recipient address in RFC 822
 
92
/*      external form.
 
93
/* .IP rcpt_offset
 
94
/*      Queue file offset of recipient record.
 
95
/* .IP text
 
96
/*      The text that explains why the recipient was undeliverable.
 
97
/* .IP dsn_status
 
98
/*      String with DSN compatible status code (digit.digit.digit).
 
99
/* .IP dsn_action
 
100
/*      "delivered", "failed", "delayed" and so on.
 
101
/* .PP
 
102
/*      Other fields will be added as the code evolves.
 
103
/* LICENSE
 
104
/* .ad
 
105
/* .fi
 
106
/*      The Secure Mailer license must be distributed with this software.
 
107
/* AUTHOR(S)
 
108
/*      Wietse Venema
 
109
/*      IBM T.J. Watson Research
 
110
/*      P.O. Box 704
 
111
/*      Yorktown Heights, NY 10598, USA
 
112
/*--*/
 
113
 
 
114
/* System library. */
 
115
 
 
116
#include <sys_defs.h>
 
117
#include <string.h>
 
118
#include <ctype.h>
 
119
#include <unistd.h>
 
120
#include <stdlib.h>
 
121
 
 
122
/* Utility library. */
 
123
 
 
124
#include <msg.h>
 
125
#include <mymalloc.h>
 
126
#include <vstream.h>
 
127
#include <vstring.h>
 
128
#include <vstring_vstream.h>
 
129
#include <stringops.h>
 
130
 
 
131
/* Global library. */
 
132
 
 
133
#include <mail_params.h>
 
134
#include <mail_proto.h>
 
135
#include <mail_queue.h>
 
136
#include <bounce_log.h>
 
137
 
 
138
/* Application-specific. */
 
139
 
 
140
#define STR(x)          vstring_str(x)
 
141
 
 
142
/* bounce_log_init - initialize structure */
 
143
 
 
144
static BOUNCE_LOG *bounce_log_init(VSTREAM *fp,
 
145
                                           VSTRING *buf,
 
146
                                           VSTRING *orcp_buf,
 
147
                                           VSTRING *rcpt_buf,
 
148
                                           long rcpt_offset,
 
149
                                           VSTRING *status_buf,
 
150
                                           const char *compat_status,
 
151
                                           VSTRING *action_buf,
 
152
                                           const char *compat_action,
 
153
                                           VSTRING *text_buf)
 
154
{
 
155
    BOUNCE_LOG *bp;
 
156
 
 
157
#define SET_BUFFER(bp, buf, str) { \
 
158
        bp->buf = buf; \
 
159
        bp->str = (buf && STR(buf)[0] ? STR(buf) : 0); \
 
160
    }
 
161
 
 
162
    bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
 
163
    bp->fp = fp;
 
164
    bp->buf = buf;
 
165
    SET_BUFFER(bp, orcp_buf, orig_rcpt);
 
166
    SET_BUFFER(bp, rcpt_buf, recipient);
 
167
    bp->rcpt_offset = rcpt_offset;
 
168
    SET_BUFFER(bp, status_buf, dsn_status);
 
169
    bp->compat_status = compat_status;
 
170
    SET_BUFFER(bp, action_buf, dsn_action);
 
171
    bp->compat_action = compat_action;
 
172
    SET_BUFFER(bp, text_buf, text);
 
173
    return (bp);
 
174
}
 
175
 
 
176
/* bounce_log_open - open bounce read stream */
 
177
 
 
178
BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
 
179
                                    int flags, int mode)
 
180
{
 
181
    VSTREAM *fp;
 
182
 
 
183
#define STREQ(x,y)      (strcmp((x),(y)) == 0)
 
184
#define SAVE_TO_VSTRING(s)      vstring_strcpy(vstring_alloc(10), (s))
 
185
 
 
186
    /*
 
187
     * TODO: peek at the first byte to see if this is an old-style log
 
188
     * (<recipient>: text) or a new-style extensible log with multiple
 
189
     * attributes per recipient. That would not help during a transition from
 
190
     * old to new style, where one can expect to find mixed format files.
 
191
     * 
 
192
     * Kluge up default DSN status and action for old-style logfiles.
 
193
     */
 
194
    if ((fp = mail_queue_open(queue_name, queue_id, flags, mode)) == 0) {
 
195
        return (0);
 
196
    } else {
 
197
        return (bounce_log_init(fp,             /* stream */
 
198
                                vstring_alloc(100),     /* buffer */
 
199
                                vstring_alloc(10),      /* orig_rcpt */
 
200
                                vstring_alloc(10),      /* recipient */
 
201
                                (long) 0,       /* offset */
 
202
                                vstring_alloc(10),      /* dsn_status */
 
203
                                STREQ(queue_name, MAIL_QUEUE_DEFER) ?
 
204
                                "4.0.0" : "5.0.0",      /* compatibility */
 
205
                                vstring_alloc(10),      /* dsn_action */
 
206
                                STREQ(queue_name, MAIL_QUEUE_DEFER) ?
 
207
                                "delayed" : "failed",   /* compatibility */
 
208
                                vstring_alloc(10)));    /* text */
 
209
    }
 
210
}
 
211
 
 
212
/* bounce_log_read - read one record from bounce log file */
 
213
 
 
214
BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
 
215
{
 
216
    char   *recipient;
 
217
    char   *text;
 
218
    char   *cp;
 
219
    int     state;
 
220
 
 
221
    /*
 
222
     * Our trivial logfile parser state machine.
 
223
     */
 
224
#define START   0                               /* still searching */
 
225
#define FOUND   1                               /* in logfile entry */
 
226
 
 
227
    /*
 
228
     * Initialize.
 
229
     */
 
230
    state = START;
 
231
    bp->recipient = "(unavailable)";
 
232
    bp->orig_rcpt = 0;
 
233
    bp->rcpt_offset = 0;
 
234
    bp->dsn_status = "(unavailable)";
 
235
    bp->dsn_action = "(unavailable)";
 
236
    bp->text = "(unavailable)";
 
237
 
 
238
    /*
 
239
     * Support mixed logfile formats to make transitions easier. The same
 
240
     * file can start with old-style records and end with new-style records.
 
241
     * With backwards compatibility, we even have old format followed by new
 
242
     * format within the same logfile entry!
 
243
     */
 
244
    while ((vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) {
 
245
 
 
246
        /*
 
247
         * Logfile entries are separated by blank lines. Even the old ad-hoc
 
248
         * logfile format has a blank line after the last record. This means
 
249
         * we can safely use blank lines to detect the start and end of
 
250
         * logfile entries.
 
251
         */
 
252
        if (STR(bp->buf)[0] == 0) {
 
253
            if (state == FOUND)
 
254
                return (bp);
 
255
            state = START;
 
256
            continue;
 
257
        }
 
258
 
 
259
        /*
 
260
         * Sanitize. XXX This needs to be done more carefully with new-style
 
261
         * logfile entries.
 
262
         */
 
263
        cp = printable(STR(bp->buf), '?');
 
264
 
 
265
        if (state == START)
 
266
            state = FOUND;
 
267
 
 
268
        /*
 
269
         * New style logfile entries are in "name = value" format.
 
270
         */
 
271
        if (ISALNUM(*cp)) {
 
272
            const char *err;
 
273
            char   *name;
 
274
            char   *value;
 
275
 
 
276
            /*
 
277
             * Split into name and value.
 
278
             */
 
279
            if ((err = split_nameval(cp, &name, &value)) != 0) {
 
280
                msg_warn("%s: malformed record: %s", VSTREAM_PATH(bp->fp), err);
 
281
                continue;
 
282
            }
 
283
 
 
284
            /*
 
285
             * Save attribute value.
 
286
             */
 
287
            if (STREQ(name, MAIL_ATTR_RECIP)) {
 
288
                bp->recipient = STR(vstring_strcpy(bp->rcpt_buf, *value ?
 
289
                                                value : "(MAILER-DAEMON)"));
 
290
            } else if (STREQ(name, MAIL_ATTR_ORCPT)) {
 
291
                bp->orig_rcpt = STR(vstring_strcpy(bp->orcp_buf, *value ?
 
292
                                                value : "(MAILER-DAEMON)"));
 
293
            } else if (STREQ(name, MAIL_ATTR_OFFSET)) {
 
294
                bp->rcpt_offset = atol(value);
 
295
            } else if (STREQ(name, MAIL_ATTR_STATUS)) {
 
296
                bp->dsn_status = STR(vstring_strcpy(bp->status_buf, value));
 
297
            } else if (STREQ(name, MAIL_ATTR_ACTION)) {
 
298
                bp->dsn_action = STR(vstring_strcpy(bp->action_buf, value));
 
299
            } else if (STREQ(name, MAIL_ATTR_WHY)) {
 
300
                bp->text = STR(vstring_strcpy(bp->text_buf, value));
 
301
            } else {
 
302
                msg_warn("%s: unknown attribute name: %s, ignored",
 
303
                         VSTREAM_PATH(bp->fp), name);
 
304
            }
 
305
            continue;
 
306
        }
 
307
 
 
308
        /*
 
309
         * Old-style logfile record. Find the recipient address.
 
310
         */
 
311
        if (*cp != '<') {
 
312
            msg_warn("%s: malformed record: %.30s...",
 
313
                     VSTREAM_PATH(bp->fp), cp);
 
314
            continue;
 
315
        }
 
316
        recipient = cp + 1;
 
317
        if ((cp = strstr(recipient, ">: ")) == 0) {
 
318
            msg_warn("%s: malformed record: %.30s...",
 
319
                     VSTREAM_PATH(bp->fp), cp);
 
320
            continue;
 
321
        }
 
322
        *cp = 0;
 
323
        vstring_strcpy(bp->rcpt_buf, *recipient ?
 
324
                       recipient : "(MAILER-DAEMON)");
 
325
        bp->recipient = STR(bp->rcpt_buf);
 
326
 
 
327
        /*
 
328
         * Find the text that explains why mail was not deliverable.
 
329
         */
 
330
        text = cp + 2;
 
331
        while (*text && ISSPACE(*text))
 
332
            text++;
 
333
        vstring_strcpy(bp->text_buf, text);
 
334
        bp->text = STR(bp->text_buf);
 
335
 
 
336
        /*
 
337
         * Add compatibility status and action info, to make up for data that
 
338
         * was not stored in old-style bounce logfiles.
 
339
         */
 
340
        bp->dsn_status = bp->compat_status;
 
341
        bp->dsn_action = bp->compat_action;
 
342
    }
 
343
    return (0);
 
344
}
 
345
 
 
346
/* bounce_log_forge - forge one recipient status record */
 
347
 
 
348
BOUNCE_LOG *bounce_log_forge(const char *orig_rcpt, const char *recipient,
 
349
                                   long rcpt_offset, const char *dsn_status,
 
350
                                   const char *dsn_action, const char *text)
 
351
{
 
352
    return (bounce_log_init((VSTREAM *) 0,
 
353
                            (VSTRING *) 0,
 
354
                            SAVE_TO_VSTRING(orig_rcpt),
 
355
                            SAVE_TO_VSTRING(recipient),
 
356
                            rcpt_offset,
 
357
                            SAVE_TO_VSTRING(dsn_status),
 
358
                            "(unavailable)",
 
359
                            SAVE_TO_VSTRING(dsn_action),
 
360
                            "(unavailable)",
 
361
                            SAVE_TO_VSTRING(text)));
 
362
}
 
363
 
 
364
/* bounce_log_close - close bounce reader stream */
 
365
 
 
366
int     bounce_log_close(BOUNCE_LOG *bp)
 
367
{
 
368
    int     ret;
 
369
 
 
370
    if (bp->fp)
 
371
        ret = vstream_fclose(bp->fp);
 
372
    else
 
373
        ret = 0;
 
374
    if (bp->buf)
 
375
        vstring_free(bp->buf);
 
376
    if (bp->rcpt_buf)
 
377
        vstring_free(bp->rcpt_buf);
 
378
    if (bp->orcp_buf)
 
379
        vstring_free(bp->orcp_buf);
 
380
    if (bp->status_buf)
 
381
        vstring_free(bp->status_buf);
 
382
    if (bp->action_buf)
 
383
        vstring_free(bp->action_buf);
 
384
    if (bp->text_buf)
 
385
        vstring_free(bp->text_buf);
 
386
    myfree((char *) bp);
 
387
    return (ret);
 
388
}