Postfix3.3.1
bounce_log.c
[詳解]
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 /* /* No public members. */
12 /* .in -4
13 /* } BOUNCE_LOG;
14 /*
15 /* BOUNCE_LOG *bounce_log_open(queue, id, flags, mode)
16 /* const char *queue;
17 /* const char *id;
18 /* int flags;
19 /* mode_t mode;
20 /*
21 /* BOUNCE_LOG *bounce_log_read(bp, rcpt, dsn)
22 /* BOUNCE_LOG *bp;
23 /* RCPT_BUF *rcpt;
24 /* DSN_BUF *dsn;
25 /*
26 /* void bounce_log_rewind(bp)
27 /* BOUNCE_LOG *bp;
28 /*
29 /* void bounce_log_close(bp)
30 /* BOUNCE_LOG *bp;
31 /* DESCRIPTION
32 /* This module implements a bounce/defer logfile API. Information
33 /* is sanitized for control and non-ASCII characters. Fields not
34 /* present in input are represented by empty strings.
35 /*
36 /* bounce_log_open() opens the named bounce or defer logfile
37 /* and returns a handle that must be used for further access.
38 /* The result is a null pointer if the file cannot be opened.
39 /* The caller is expected to inspect the errno code and deal
40 /* with the problem.
41 /*
42 /* bounce_log_read() reads the next record from the bounce or defer
43 /* logfile (skipping over and warning about malformed data)
44 /* and breaks out the recipient address, the recipient status
45 /* and the text that explains why the recipient was undeliverable.
46 /* bounce_log_read() returns a null pointer when no recipient was read,
47 /* otherwise it returns its argument.
48 /*
49 /* bounce_log_rewind() is a helper that seeks to the first recipient
50 /* in an open bounce or defer logfile (skipping over recipients that
51 /* are marked as done). The result is 0 in case of success, -1 in case
52 /* of problems.
53 /*
54 /* bounce_log_close() closes an open bounce or defer logfile and
55 /* releases memory for the specified handle. The result is non-zero
56 /* in case of I/O errors.
57 /*
58 /* Arguments:
59 /* .IP queue
60 /* The bounce or defer queue name.
61 /* .IP id
62 /* The message queue id of bounce or defer logfile. This
63 /* file has the same name as the original message file.
64 /* .IP flags
65 /* File open flags, as with open(2).
66 /* .IP mode
67 /* File permissions, as with open(2).
68 /* .IP rcpt
69 /* Recipient buffer. The RECIPIENT member is updated.
70 /* .IP dsn
71 /* Delivery status information. The DSN member is updated.
72 /* LICENSE
73 /* .ad
74 /* .fi
75 /* The Secure Mailer license must be distributed with this software.
76 /* AUTHOR(S)
77 /* Wietse Venema
78 /* IBM T.J. Watson Research
79 /* P.O. Box 704
80 /* Yorktown Heights, NY 10598, USA
81 /*--*/
82 
83 /* System library. */
84 
85 #include <sys_defs.h>
86 #include <string.h>
87 #include <ctype.h>
88 #include <unistd.h>
89 #include <stdlib.h>
90 
91 /* Utility library. */
92 
93 #include <msg.h>
94 #include <mymalloc.h>
95 #include <vstream.h>
96 #include <vstring.h>
97 #include <vstring_vstream.h>
98 #include <stringops.h>
99 
100 /* Global library. */
101 
102 #include <mail_params.h>
103 #include <mail_proto.h>
104 #include <mail_queue.h>
105 #include <dsn_mask.h>
106 #include <bounce_log.h>
107 
108 /* Application-specific. */
109 
110 #define STR(x) vstring_str(x)
111 
112 /* bounce_log_open - open bounce read stream */
113 
114 BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
115  int flags, mode_t mode)
116 {
117  BOUNCE_LOG *bp;
118  VSTREAM *fp;
119 
120 #define STREQ(x,y) (strcmp((x),(y)) == 0)
121 
122  /*
123  * Logfiles may contain a mixture of old-style (<recipient>: text) and
124  * new-style entries with multiple attributes per recipient.
125  *
126  * Kluge up default DSN status and action for old-style logfiles.
127  */
128  if ((fp = mail_queue_open(queue_name, queue_id, flags, mode)) == 0) {
129  return (0);
130  } else {
131  bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
132  bp->fp = fp;
133  bp->buf = vstring_alloc(100);
134  if (STREQ(queue_name, MAIL_QUEUE_DEFER)) {
135  bp->compat_status = mystrdup("4.0.0");
136  bp->compat_action = mystrdup("delayed");
137  } else {
138  bp->compat_status = mystrdup("5.0.0");
139  bp->compat_action = mystrdup("failed");
140  }
141  return (bp);
142  }
143 }
144 
145 /* bounce_log_read - read one record from bounce log file */
146 
148  DSN_BUF *dsn_buf)
149 {
150  char *recipient;
151  char *text;
152  char *cp;
153  int state;
154 
155  /*
156  * Our trivial logfile parser state machine.
157  */
158 #define START 0 /* still searching */
159 #define FOUND 1 /* in logfile entry */
160 
161  /*
162  * Initialize.
163  */
164  state = START;
165  rcpb_reset(rcpt_buf);
166  dsb_reset(dsn_buf);
167 
168  /*
169  * Support mixed logfile formats to make migration easier. The same file
170  * can start with old-style records and end with new-style records. With
171  * backwards compatibility, we even have old format followed by new
172  * format within the same logfile entry!
173  */
174  for (;;) {
175  if ((vstring_get_nonl(bp->buf, bp->fp) == VSTREAM_EOF))
176  return (0);
177 
178  /*
179  * Logfile entries are separated by blank lines. Even the old ad-hoc
180  * logfile format has a blank line after the last record. This means
181  * we can safely use blank lines to detect the start and end of
182  * logfile entries.
183  */
184  if (STR(bp->buf)[0] == 0) {
185  if (state == FOUND)
186  break;
187  state = START;
188  continue;
189  }
190 
191  /*
192  * Sanitize. XXX This needs to be done more carefully with new-style
193  * logfile entries.
194  */
195  cp = printable(STR(bp->buf), '?');
196 
197  if (state == START)
198  state = FOUND;
199 
200  /*
201  * New style logfile entries are in "name = value" format.
202  */
203  if (ISALNUM(*cp)) {
204  const char *err;
205  char *name;
206  char *value;
207  long offset;
208  int notify;
209 
210  /*
211  * Split into name and value.
212  */
213  if ((err = split_nameval(cp, &name, &value)) != 0) {
214  msg_warn("%s: malformed record: %s", VSTREAM_PATH(bp->fp), err);
215  continue;
216  }
217 
218  /*
219  * Save attribute value.
220  */
221  if (STREQ(name, MAIL_ATTR_RECIP)) {
222  vstring_strcpy(rcpt_buf->address, *value ?
223  value : "(MAILER-DAEMON)");
224  } else if (STREQ(name, MAIL_ATTR_ORCPT)) {
225  vstring_strcpy(rcpt_buf->orig_addr, *value ?
226  value : "(MAILER-DAEMON)");
227  } else if (STREQ(name, MAIL_ATTR_DSN_ORCPT)) {
228  vstring_strcpy(rcpt_buf->dsn_orcpt, value);
229  } else if (STREQ(name, MAIL_ATTR_DSN_NOTIFY)) {
230  if ((notify = atoi(value)) > 0 && DSN_NOTIFY_OK(notify))
231  rcpt_buf->dsn_notify = notify;
232  } else if (STREQ(name, MAIL_ATTR_OFFSET)) {
233  if ((offset = atol(value)) > 0)
234  rcpt_buf->offset = offset;
235  } else if (STREQ(name, MAIL_ATTR_DSN_STATUS)) {
236  vstring_strcpy(dsn_buf->status, value);
237  } else if (STREQ(name, MAIL_ATTR_DSN_ACTION)) {
238  vstring_strcpy(dsn_buf->action, value);
239  } else if (STREQ(name, MAIL_ATTR_DSN_DTYPE)) {
240  vstring_strcpy(dsn_buf->dtype, value);
241  } else if (STREQ(name, MAIL_ATTR_DSN_DTEXT)) {
242  vstring_strcpy(dsn_buf->dtext, value);
243  } else if (STREQ(name, MAIL_ATTR_DSN_MTYPE)) {
244  vstring_strcpy(dsn_buf->mtype, value);
245  } else if (STREQ(name, MAIL_ATTR_DSN_MNAME)) {
246  vstring_strcpy(dsn_buf->mname, value);
247  } else if (STREQ(name, MAIL_ATTR_WHY)) {
248  vstring_strcpy(dsn_buf->reason, value);
249  } else {
250  msg_warn("%s: unknown attribute name: %s, ignored",
251  VSTREAM_PATH(bp->fp), name);
252  }
253  continue;
254  }
255 
256  /*
257  * Old-style logfile record. Find the recipient address.
258  */
259  if (*cp != '<') {
260  msg_warn("%s: malformed record: %.30s...",
261  VSTREAM_PATH(bp->fp), cp);
262  continue;
263  }
264  recipient = cp + 1;
265  if ((cp = strstr(recipient, ">: ")) == 0) {
266  msg_warn("%s: malformed record: %.30s...",
267  VSTREAM_PATH(bp->fp), cp);
268  continue;
269  }
270  *cp = 0;
271  vstring_strcpy(rcpt_buf->address, *recipient ?
272  recipient : "(MAILER-DAEMON)");
273 
274  /*
275  * Find the text that explains why mail was not deliverable.
276  */
277  text = cp + 2;
278  while (*text && ISSPACE(*text))
279  text++;
280  vstring_strcpy(dsn_buf->reason, text);
281  }
282 
283  /*
284  * Specify place holders for missing fields. See also DSN_FROM_DSN_BUF()
285  * and RECIPIENT_FROM_RCPT_BUF() for null and non-null fields.
286  */
287 #define BUF_NODATA(buf) (STR(buf)[0] == 0)
288 #define BUF_ASSIGN(buf, text) vstring_strcpy((buf), (text))
289 
290  if (BUF_NODATA(rcpt_buf->address))
291  BUF_ASSIGN(rcpt_buf->address, "(recipient address unavailable)");
292  if (BUF_NODATA(dsn_buf->status))
293  BUF_ASSIGN(dsn_buf->status, bp->compat_status);
294  if (BUF_NODATA(dsn_buf->action))
295  BUF_ASSIGN(dsn_buf->action, bp->compat_action);
296  if (BUF_NODATA(dsn_buf->reason))
297  BUF_ASSIGN(dsn_buf->reason, "(description unavailable)");
298  (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
299  (void) DSN_FROM_DSN_BUF(dsn_buf);
300  return (bp);
301 }
302 
303 /* bounce_log_close - close bounce reader stream */
304 
306 {
307  int ret;
308 
309  ret = vstream_fclose(bp->fp);
310  vstring_free(bp->buf);
311  myfree(bp->compat_status);
312  myfree(bp->compat_action);
313  myfree((void *) bp);
314 
315  return (ret);
316 }
#define VSTREAM_EOF
Definition: vstream.h:110
#define MAIL_ATTR_DSN_NOTIFY
Definition: mail_proto.h:275
void myfree(void *ptr)
Definition: mymalloc.c:207
#define MAIL_ATTR_ORCPT
Definition: mail_proto.h:133
char * mystrdup(const char *str)
Definition: mymalloc.c:225
VSTREAM * fp
Definition: bounce_log.h:31
int vstring_get_nonl(VSTRING *vp, VSTREAM *fp)
void rcpb_reset(RCPT_BUF *rcpt)
Definition: rcpt_buf.c:95
void dsb_reset(DSN_BUF *dsb)
Definition: dsn_buf.c:333
VSTRING * mname
Definition: dsn_buf.h:33
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
VSTRING * dsn_orcpt
Definition: rcpt_buf.h:33
#define BUF_NODATA(buf)
#define MAIL_ATTR_DSN_ACTION
Definition: mail_proto.h:272
char * compat_status
Definition: bounce_log.h:33
const char * split_nameval(char *buf, char **name, char **value)
Definition: split_nameval.c:61
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define ISALNUM(c)
Definition: sys_defs.h:1745
#define MAIL_ATTR_DSN_STATUS
Definition: mail_proto.h:267
#define MAIL_ATTR_DSN_ORCPT
Definition: mail_proto.h:276
#define DSN_FROM_DSN_BUF(dsb)
Definition: dsn_buf.h:68
int dsn_notify
Definition: rcpt_buf.h:34
VSTRING * address
Definition: rcpt_buf.h:31
VSTRING * dtype
Definition: dsn_buf.h:34
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
VSTREAM * mail_queue_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: mail_queue.c:424
#define MAIL_ATTR_DSN_MNAME
Definition: mail_proto.h:271
VSTRING * action
Definition: dsn_buf.h:31
int bounce_log_close(BOUNCE_LOG *bp)
Definition: bounce_log.c:305
#define MAIL_ATTR_WHY
Definition: mail_proto.h:135
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define STR(x)
Definition: bounce_log.c:110
#define STREQ(x, y)
VSTRING * buf
Definition: bounce_log.h:32
VSTRING * orig_addr
Definition: rcpt_buf.h:32
#define RECIPIENT_FROM_RCPT_BUF(buf)
Definition: rcpt_buf.h:43
VSTRING * mtype
Definition: dsn_buf.h:32
#define MAIL_QUEUE_DEFER
Definition: mail_queue.h:34
VSTRING * reason
Definition: dsn_buf.h:37
#define MAIL_ATTR_RECIP
Definition: mail_proto.h:134
#define FOUND
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define DSN_NOTIFY_OK(v)
Definition: dsn_mask.h:63
char * compat_action
Definition: bounce_log.h:34
VSTRING * dtext
Definition: dsn_buf.h:35
BOUNCE_LOG * bounce_log_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: bounce_log.c:114
#define ISSPACE(c)
Definition: sys_defs.h:1753
long offset
Definition: rcpt_buf.h:35
#define START
#define MAIL_ATTR_DSN_MTYPE
Definition: mail_proto.h:270
char * printable(char *string, int replacement)
Definition: printable.c:49
#define BUF_ASSIGN(buf, text)
#define MAIL_ATTR_DSN_DTEXT
Definition: mail_proto.h:269
VSTRING * status
Definition: dsn_buf.h:30
BOUNCE_LOG * bounce_log_read(BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf)
Definition: bounce_log.c:147
#define MAIL_ATTR_OFFSET
Definition: mail_proto.h:138
#define MAIL_ATTR_DSN_DTYPE
Definition: mail_proto.h:268
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150