Postfix3.3.1
cleanup_bounce.c
[詳解]
1 /*++
2 /* NAME
3 /* cleanup_bounce 3
4 /* SUMMARY
5 /* bounce all recipients
6 /* SYNOPSIS
7 /* #include "cleanup.h"
8 /*
9 /* void cleanup_bounce(state)
10 /* CLEANUP_STATE *state;
11 /* DESCRIPTION
12 /* cleanup_bounce() updates the bounce log on request by client
13 /* programs that cannot handle such problems themselves.
14 /*
15 /* Upon successful completion, all error flags are reset,
16 /* and the message is scheduled for deletion.
17 /* Otherwise, the CLEANUP_STAT_WRITE error flag is raised.
18 /*
19 /* Arguments:
20 /* .IP state
21 /* Queue file and message processing state. This state is
22 /* updated as records are processed and as errors happen.
23 /* LICENSE
24 /* .ad
25 /* .fi
26 /* The Secure Mailer license must be distributed with this software.
27 /* AUTHOR(S)
28 /* Wietse Venema
29 /* IBM T.J. Watson Research
30 /* P.O. Box 704
31 /* Yorktown Heights, NY 10598, USA
32 /*--*/
33 
34 /* System library. */
35 
36 #include <sys_defs.h>
37 
38 /* Utility library. */
39 
40 #include <msg.h>
41 #include <stringops.h>
42 #include <stdlib.h>
43 
44 /* Global library. */
45 
46 #include <cleanup_user.h>
47 #include <mail_params.h>
48 #include <mail_proto.h>
49 #include <bounce.h>
50 #include <dsn_util.h>
51 #include <record.h>
52 #include <rec_type.h>
53 #include <dsn_mask.h>
54 #include <mail_queue.h>
55 #include <rec_attr_map.h>
56 
57 /* Application-specific. */
58 
59 #include "cleanup.h"
60 
61 #define STR(x) vstring_str(x)
62 
63 /* cleanup_bounce_append - update bounce logfile */
64 
65 static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
66  DSN *dsn)
67 {
68  MSG_STATS stats;
69 
70  /*
71  * Don't log a spurious warning (for example, when soft_bounce is turned
72  * on). bounce_append() already logs a record when the logfile can't be
73  * updated. Set the write error flag, so that a maildrop queue file won't
74  * be destroyed.
75  */
77  CLEANUP_MSG_STATS(&stats, state),
78  rcpt, "none", dsn) != 0) {
79  state->errs |= CLEANUP_STAT_WRITE;
80  }
81 }
82 
83 /* cleanup_bounce - bounce all recipients */
84 
86 {
87  const char *myname = "cleanup_bounce";
88  VSTRING *buf = vstring_alloc(100);
89  const CLEANUP_STAT_DETAIL *detail;
90  DSN_SPLIT dp;
91  const char *dsn_status;
92  const char *dsn_text;
93  char *rcpt = 0;
94  RECIPIENT recipient;
95  DSN dsn;
96  char *attr_name;
97  char *attr_value;
98  char *dsn_orcpt = 0;
99  int dsn_notify = 0;
100  char *orig_rcpt = 0;
101  char *start;
102  int rec_type;
103  int junk;
104  long curr_offset;
105  const char *encoding;
106  const char *dsn_envid;
107  int dsn_ret;
108  int bounce_err;
109 
110  /*
111  * Parse the failure reason if one was given, otherwise use a generic
112  * mapping from cleanup-internal error code to (DSN + text).
113  */
114  if (state->reason) {
115  dsn_split(&dp, "5.0.0", state->reason);
116  dsn_status = DSN_STATUS(dp.dsn);
117  dsn_text = dp.text;
118  } else {
119  detail = cleanup_stat_detail(state->errs);
120  dsn_status = detail->dsn;
121  dsn_text = detail->text;
122  }
123 
124  /*
125  * Create a bounce logfile with one entry for each final recipient.
126  * Degrade gracefully in case of no recipients or no queue file.
127  *
128  * Victor Duchovni observes that the number of recipients in the queue file
129  * can potentially be very large due to virtual alias expansion. This can
130  * expand the recipient count by virtual_alias_expansion_limit (default:
131  * 1000) times.
132  *
133  * After a queue file write error, purge any unwritten data (so that
134  * vstream_fseek() won't fail while trying to flush it) and reset the
135  * stream error flags to avoid false alarms.
136  */
137  if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) {
138  (void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH);
139  vstream_clearerr(state->dst);
140  }
141  if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
142  msg_fatal("%s: seek %s: %m", myname, cleanup_path);
143 
144  while ((state->errs & CLEANUP_STAT_WRITE) == 0) {
145  if ((curr_offset = vstream_ftell(state->dst)) < 0)
146  msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
147  if ((rec_type = rec_get(state->dst, buf, 0)) <= 0
148  || rec_type == REC_TYPE_END)
149  break;
150  start = STR(buf);
151  if (rec_type == REC_TYPE_ATTR) {
152  if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
153  || *attr_value == 0)
154  continue;
155  /* Map attribute names to pseudo record type. */
156  if ((junk = rec_attr_map(attr_name)) != 0) {
157  start = attr_value;
158  rec_type = junk;
159  }
160  }
161  switch (rec_type) {
162  case REC_TYPE_DSN_ORCPT: /* RCPT TO ORCPT parameter */
163  if (dsn_orcpt != 0) /* can't happen */
164  myfree(dsn_orcpt);
165  dsn_orcpt = mystrdup(start);
166  break;
167  case REC_TYPE_DSN_NOTIFY: /* RCPT TO NOTIFY parameter */
168  if (alldig(start) && (junk = atoi(start)) > 0
169  && DSN_NOTIFY_OK(junk))
170  dsn_notify = junk;
171  else
172  dsn_notify = 0;
173  break;
174  case REC_TYPE_ORCP: /* unmodified RCPT TO address */
175  if (orig_rcpt != 0) /* can't happen */
176  myfree(orig_rcpt);
177  orig_rcpt = mystrdup(start);
178  break;
179  case REC_TYPE_RCPT: /* rewritten RCPT TO address */
180  rcpt = start;
181  RECIPIENT_ASSIGN(&recipient, curr_offset,
182  dsn_orcpt ? dsn_orcpt : "", dsn_notify,
183  orig_rcpt ? orig_rcpt : rcpt, rcpt);
184  (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
185  cleanup_bounce_append(state, &recipient, &dsn);
186  /* FALLTHROUGH */
187  case REC_TYPE_DRCP: /* canceled recipient */
188  case REC_TYPE_DONE: /* can't happen */
189  if (orig_rcpt != 0) {
190  myfree(orig_rcpt);
191  orig_rcpt = 0;
192  }
193  if (dsn_orcpt != 0) {
194  myfree(dsn_orcpt);
195  dsn_orcpt = 0;
196  }
197  dsn_notify = 0;
198  break;
199  }
200  }
201  if (orig_rcpt != 0) /* can't happen */
202  myfree(orig_rcpt);
203  if (dsn_orcpt != 0) /* can't happen */
204  myfree(dsn_orcpt);
205 
206  /*
207  * No recipients. Yes, this can happen.
208  */
209  if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) {
210  RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown");
211  (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
212  cleanup_bounce_append(state, &recipient, &dsn);
213  }
214  vstring_free(buf);
215 
216  /*
217  * Flush the bounce logfile to the sender. See also qmgr_active.c.
218  */
219  if ((state->errs & CLEANUP_STAT_WRITE) == 0) {
220  if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0)
221  encoding = MAIL_ATTR_ENC_NONE;
222  dsn_envid = state->dsn_envid ?
223  state->dsn_envid : "";
224  /* Do not send unfiltered (body) content. */
225  dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ?
226  DSN_RET_HDRS : state->dsn_ret;
227 
228  if (state->verp_delims == 0 || var_verp_bounce_off) {
229  bounce_err =
231  state->queue_name, state->queue_id,
232  encoding, state->smtputf8, state->sender,
233  dsn_envid, dsn_ret);
234  } else {
235  bounce_err =
237  state->queue_name, state->queue_id,
238  encoding, state->smtputf8, state->sender,
239  dsn_envid, dsn_ret, state->verp_delims);
240  }
241  if (bounce_err != 0) {
242  msg_warn("%s: bounce message failure", state->queue_id);
243  state->errs |= CLEANUP_STAT_WRITE;
244  }
245  }
246 
247  /*
248  * Schedule this message (and trace logfile) for deletion when all is
249  * well. When all is not well these files would be deleted too, but the
250  * client would get a different completion status so we have to carefully
251  * maintain the bits anyway.
252  */
253  if ((state->errs &= CLEANUP_STAT_WRITE) == 0)
254  state->flags |= CLEANUP_FLAG_DISCARD;
255 
256  return (state->errs);
257 }
const CLEANUP_STAT_DETAIL * cleanup_stat_detail(unsigned status)
void myfree(void *ptr)
Definition: mymalloc.c:207
int smtputf8
Definition: cleanup.h:134
DSN_SPLIT * dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
Definition: dsn_util.c:138
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * queue_name
Definition: cleanup.h:55
#define MAIL_ATTR_ENCODING
Definition: mail_proto.h:202
#define DSN_SIMPLE(dsn, _status, _reason)
Definition: dsn.h:41
#define CLEANUP_STAT_SIZE
Definition: cleanup_user.h:59
off_t vstream_ftell(VSTREAM *stream)
Definition: vstream.c:1157
off_t start
Definition: cleanup.h:335
#define DSN_RET_HDRS
Definition: dsn_mask.h:18
char * reason
Definition: cleanup.h:89
const char * text
Definition: cleanup_user.h:87
#define vstream_clearerr(vp)
Definition: vstream.h:125
#define CLEANUP_FLAG_DISCARD
Definition: cleanup_user.h:21
#define DSN_STATUS(dsn_buf)
Definition: dsn_util.h:46
#define REC_TYPE_END
Definition: rec_type.h:77
int alldig(const char *string)
Definition: alldig.c:38
#define STR(x)
char * cleanup_path
Definition: cleanup_init.c:111
#define RECIPIENT_ASSIGN(rcpt, offs, orcpt, notify, orig, addr)
const char * split_nameval(char *buf, char **name, char **value)
Definition: split_nameval.c:61
#define REC_TYPE_DSN_NOTIFY
Definition: rec_type.h:73
#define BOUNCE_FLAG_CLEAN
Definition: bounce.h:61
char * dsn_envid
Definition: cleanup.h:97
NVTABLE * attr
Definition: cleanup.h:91
int bounce_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn)
Definition: bounce.c:222
#define REC_TYPE_DRCP
Definition: rec_type.h:47
#define MAIL_ATTR_ENC_NONE
Definition: mail_proto.h:205
#define REC_TYPE_DSN_ORCPT
Definition: rec_type.h:72
const char * text
Definition: dsn_util.h:55
char * sender
Definition: cleanup.h:59
#define REC_TYPE_RCPT
Definition: rec_type.h:45
int bounce_flush(int flags, const char *queue, const char *id, const char *encoding, int smtputf8, const char *sender, const char *dsn_envid, int dsn_ret)
Definition: bounce.c:343
#define VSTREAM_PURGE_BOTH
Definition: vstream.h:90
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
DSN_STAT dsn
Definition: dsn_util.h:54
char * queue_id
Definition: cleanup.h:56
int rec_attr_map(const char *attr_name)
Definition: rec_attr_map.c:39
#define CLEANUP_MSG_STATS(stats, state)
Definition: cleanup.h:315
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
VSTREAM * dst
Definition: cleanup.h:53
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
#define CLEANUP_STAT_CONT
Definition: cleanup_user.h:60
int bounce_flush_verp(int flags, const char *queue, const char *id, const char *encoding, int smtputf8, const char *sender, const char *dsn_envid, int dsn_ret, const char *verp_delims)
Definition: bounce.c:377
int cleanup_bounce(CLEANUP_STATE *state)
int dsn_ret
Definition: cleanup.h:98
Definition: dsn.h:17
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define DSN_NOTIFY_OK(v)
Definition: dsn_mask.h:63
char * verp_delims
Definition: cleanup.h:101
#define REC_TYPE_ATTR
Definition: rec_type.h:49
#define CLEANUP_STAT_WRITE
Definition: cleanup_user.h:58
#define nvtable_find(table, key)
Definition: nvtable.h:27
#define REC_TYPE_DONE
Definition: rec_type.h:44
#define rec_get(fp, buf, limit)
Definition: record.h:56
int vstream_fpurge(VSTREAM *stream, int direction)
Definition: vstream.c:1041
#define vstream_ferror(vp)
Definition: vstream.h:120
int var_verp_bounce_off
Definition: cleanup_init.c:152
#define REC_TYPE_ORCP
Definition: rec_type.h:46
const char * dsn
Definition: cleanup_user.h:86