Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / oqmgr / qmgr_message.c
blob0e083628a90d90f8f2417cb3795141877a0378d1
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* qmgr_message 3
6 /* SUMMARY
7 /* in-core message structures
8 /* SYNOPSIS
9 /* #include "qmgr.h"
11 /* int qmgr_message_count;
12 /* int qmgr_recipient_count;
14 /* QMGR_MESSAGE *qmgr_message_alloc(class, name, qflags, mode)
15 /* const char *class;
16 /* const char *name;
17 /* int qflags;
18 /* mode_t mode;
20 /* QMGR_MESSAGE *qmgr_message_realloc(message)
21 /* QMGR_MESSAGE *message;
23 /* void qmgr_message_free(message)
24 /* QMGR_MESSAGE *message;
26 /* void qmgr_message_update_warn(message)
27 /* QMGR_MESSAGE *message;
29 /* void qmgr_message_kill_record(message, offset)
30 /* QMGR_MESSAGE *message;
31 /* long offset;
32 /* DESCRIPTION
33 /* This module performs en-gross operations on queue messages.
35 /* qmgr_message_count is a global counter for the total number
36 /* of in-core message structures (i.e. the total size of the
37 /* `active' message queue).
39 /* qmgr_recipient_count is a global counter for the total number
40 /* of in-core recipient structures (i.e. the sum of all recipients
41 /* in all in-core message structures).
43 /* qmgr_message_alloc() creates an in-core message structure
44 /* with sender and recipient information taken from the named queue
45 /* file. A null result means the queue file could not be read or
46 /* that the queue file contained incorrect information. A result
47 /* QMGR_MESSAGE_LOCKED means delivery must be deferred. The number
48 /* of recipients read from a queue file is limited by the global
49 /* var_qmgr_rcpt_limit configuration parameter. When the limit
50 /* is reached, the \fIrcpt_offset\fR structure member is set to
51 /* the position where the read was terminated. Recipients are
52 /* run through the resolver, and are assigned to destination
53 /* queues. Recipients that cannot be assigned are deferred or
54 /* bounced. Mail that has bounced twice is silently absorbed.
55 /* A non-zero mode means change the queue file permissions.
57 /* qmgr_message_realloc() resumes reading recipients from the queue
58 /* file, and updates the recipient list and \fIrcpt_offset\fR message
59 /* structure members. A null result means that the file could not be
60 /* read or that the file contained incorrect information.
62 /* qmgr_message_free() destroys an in-core message structure and makes
63 /* the resources available for reuse. It is an error to destroy
64 /* a message structure that is still referenced by queue entry structures.
66 /* qmgr_message_update_warn() takes a closed message, opens it, updates
67 /* the warning field, and closes it again.
69 /* qmgr_message_kill_record() takes a closed message, opens it, updates
70 /* the record type at the given offset to "killed", and closes the file.
71 /* A killed envelope record is ignored. Killed records are not allowed
72 /* inside the message content.
73 /* DIAGNOSTICS
74 /* Warnings: malformed message file. Fatal errors: out of memory.
75 /* SEE ALSO
76 /* envelope(3) message envelope parser
77 /* LICENSE
78 /* .ad
79 /* .fi
80 /* The Secure Mailer license must be distributed with this software.
81 /* AUTHOR(S)
82 /* Wietse Venema
83 /* IBM T.J. Watson Research
84 /* P.O. Box 704
85 /* Yorktown Heights, NY 10598, USA
86 /*--*/
88 /* System library. */
90 #include <sys_defs.h>
91 #include <sys/stat.h>
92 #include <stdlib.h>
93 #include <stdio.h> /* sscanf() */
94 #include <fcntl.h>
95 #include <errno.h>
96 #include <unistd.h>
97 #include <string.h>
98 #include <ctype.h>
100 #ifdef STRCASECMP_IN_STRINGS_H
101 #include <strings.h>
102 #endif
104 /* Utility library. */
106 #include <msg.h>
107 #include <mymalloc.h>
108 #include <vstring.h>
109 #include <vstream.h>
110 #include <split_at.h>
111 #include <valid_hostname.h>
112 #include <argv.h>
113 #include <stringops.h>
114 #include <myflock.h>
116 /* Global library. */
118 #include <dict.h>
119 #include <mail_queue.h>
120 #include <mail_params.h>
121 #include <canon_addr.h>
122 #include <record.h>
123 #include <rec_type.h>
124 #include <sent.h>
125 #include <deliver_completed.h>
126 #include <opened.h>
127 #include <verp_sender.h>
128 #include <mail_proto.h>
129 #include <qmgr_user.h>
130 #include <split_addr.h>
131 #include <dsn_mask.h>
132 #include <rec_attr_map.h>
134 /* Client stubs. */
136 #include <rewrite_clnt.h>
137 #include <resolve_clnt.h>
139 /* Application-specific. */
141 #include "qmgr.h"
143 int qmgr_message_count;
144 int qmgr_recipient_count;
146 /* qmgr_message_create - create in-core message structure */
148 static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
149 const char *queue_id, int qflags)
151 QMGR_MESSAGE *message;
153 message = (QMGR_MESSAGE *) mymalloc(sizeof(QMGR_MESSAGE));
154 qmgr_message_count++;
155 message->flags = 0;
156 message->qflags = qflags;
157 message->tflags = 0;
158 message->tflags_offset = 0;
159 message->rflags = QMGR_READ_FLAG_DEFAULT;
160 message->fp = 0;
161 message->refcount = 0;
162 message->single_rcpt = 0;
163 message->arrival_time.tv_sec = message->arrival_time.tv_usec = 0;
164 message->create_time = 0;
165 GETTIMEOFDAY(&message->active_time);
166 message->data_offset = 0;
167 message->queue_id = mystrdup(queue_id);
168 message->queue_name = mystrdup(queue_name);
169 message->encoding = 0;
170 message->sender = 0;
171 message->dsn_envid = 0;
172 message->dsn_ret = 0;
173 message->filter_xport = 0;
174 message->inspect_xport = 0;
175 message->redirect_addr = 0;
176 message->data_size = 0;
177 message->cont_length = 0;
178 message->warn_offset = 0;
179 message->warn_time = 0;
180 message->rcpt_offset = 0;
181 message->verp_delims = 0;
182 message->client_name = 0;
183 message->client_addr = 0;
184 message->client_port = 0;
185 message->client_proto = 0;
186 message->client_helo = 0;
187 message->sasl_method = 0;
188 message->sasl_username = 0;
189 message->sasl_sender = 0;
190 message->rewrite_context = 0;
191 recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
192 return (message);
195 /* qmgr_message_close - close queue file */
197 static void qmgr_message_close(QMGR_MESSAGE *message)
199 vstream_fclose(message->fp);
200 message->fp = 0;
203 /* qmgr_message_open - open queue file */
205 static int qmgr_message_open(QMGR_MESSAGE *message)
209 * Sanity check.
211 if (message->fp)
212 msg_panic("%s: queue file is open", message->queue_id);
215 * Open this queue file. Skip files that we cannot open. Back off when
216 * the system appears to be running out of resources.
218 if ((message->fp = mail_queue_open(message->queue_name,
219 message->queue_id,
220 O_RDWR, 0)) == 0) {
221 if (errno != ENOENT)
222 msg_fatal("open %s %s: %m", message->queue_name, message->queue_id);
223 msg_warn("open %s %s: %m", message->queue_name, message->queue_id);
224 return (-1);
226 return (0);
229 /* qmgr_message_oldstyle_scan - support for Postfix < 1.0 queue files */
231 static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message)
233 VSTRING *buf;
234 long orig_offset, extra_offset;
235 int rec_type;
236 char *start;
239 * Initialize. No early returns or we have a memory leak.
241 buf = vstring_alloc(100);
242 if ((orig_offset = vstream_ftell(message->fp)) < 0)
243 msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
246 * Rewind to the very beginning to make sure we see all records.
248 if (vstream_fseek(message->fp, 0, SEEK_SET) < 0)
249 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
252 * Scan through the old style queue file. Count the total number of
253 * recipients and find the data/extra sections offsets. Note that the new
254 * queue files require that data_size equals extra_offset - data_offset,
255 * so we set data_size to this as well and ignore the size record itself
256 * completely.
258 for (;;) {
259 rec_type = rec_get(message->fp, buf, 0);
260 if (rec_type <= 0)
261 /* Report missing end record later. */
262 break;
263 start = vstring_str(buf);
264 if (msg_verbose > 1)
265 msg_info("old-style scan record %c %s", rec_type, start);
266 if (rec_type == REC_TYPE_END)
267 break;
268 if (rec_type == REC_TYPE_MESG) {
269 if (message->data_offset == 0) {
270 if ((message->data_offset = vstream_ftell(message->fp)) < 0)
271 msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
272 if ((extra_offset = atol(start)) <= message->data_offset)
273 msg_fatal("bad extra offset %s file %s",
274 start, VSTREAM_PATH(message->fp));
275 if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0)
276 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
277 message->data_size = extra_offset - message->data_offset;
279 continue;
284 * Clean up.
286 if (vstream_fseek(message->fp, orig_offset, SEEK_SET) < 0)
287 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
288 vstring_free(buf);
291 * Sanity checks. Verify that all required information was found,
292 * including the queue file end marker.
294 if (message->data_offset == 0 || rec_type != REC_TYPE_END)
295 msg_fatal("%s: envelope records out of order", message->queue_id);
298 /* qmgr_message_read - read envelope records */
300 static int qmgr_message_read(QMGR_MESSAGE *message)
302 VSTRING *buf;
303 int rec_type;
304 long curr_offset;
305 long save_offset = message->rcpt_offset; /* save a flag */
306 char *start;
307 int nrcpt = 0;
308 const char *error_text;
309 char *name;
310 char *value;
311 char *orig_rcpt = 0;
312 int count;
313 int dsn_notify = 0;
314 char *dsn_orcpt = 0;
315 int n;
316 int have_log_client_attr = 0;
319 * Initialize. No early returns or we have a memory leak.
321 buf = vstring_alloc(100);
324 * If we re-open this file, skip over on-file recipient records that we
325 * already looked at, and refill the in-core recipient address list.
327 if (message->rcpt_offset) {
328 if (message->rcpt_list.len)
329 msg_panic("%s: recipient list not empty on recipient reload",
330 message->queue_id);
331 if (vstream_fseek(message->fp, message->rcpt_offset, SEEK_SET) < 0)
332 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
333 message->rcpt_offset = 0;
337 * Read envelope records. XXX Rely on the front-end programs to enforce
338 * record size limits. Read up to var_qmgr_rcpt_limit recipients from the
339 * queue file, to protect against memory exhaustion. Recipient records
340 * may appear before or after the message content, so we keep reading
341 * from the queue file until we have enough recipients (rcpt_offset != 0)
342 * and until we know all the non-recipient information.
344 * When reading recipients from queue file, stop reading when we reach a
345 * per-message in-core recipient limit rather than a global in-core
346 * recipient limit. Use the global recipient limit only in order to stop
347 * opening queue files. The purpose is to achieve equal delay for
348 * messages with recipient counts up to var_qmgr_rcpt_limit recipients.
350 * If we would read recipients up to a global recipient limit, the average
351 * number of in-core recipients per message would asymptotically approach
352 * (global recipient limit)/(active queue size limit), which gives equal
353 * delay per recipient rather than equal delay per message.
355 * On the first open, we must examine all non-recipient records.
357 * Optimization: when we know that recipient records are not mixed with
358 * non-recipient records, as is typical with mailing list mail, then we
359 * can avoid having to examine all the queue file records before we can
360 * start deliveries. This avoids some file system thrashing with huge
361 * mailing lists.
363 for (;;) {
364 if ((curr_offset = vstream_ftell(message->fp)) < 0)
365 msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp));
366 if (curr_offset == message->data_offset && curr_offset > 0) {
367 if (vstream_fseek(message->fp, message->data_size, SEEK_CUR) < 0)
368 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
369 curr_offset += message->data_size;
371 rec_type = rec_get_raw(message->fp, buf, 0, REC_FLAG_NONE);
372 start = vstring_str(buf);
373 if (msg_verbose > 1)
374 msg_info("record %c %s", rec_type, start);
375 if (rec_type == REC_TYPE_PTR) {
376 if ((rec_type = rec_goto(message->fp, start)) == REC_TYPE_ERROR)
377 break;
378 /* Need to update curr_offset after pointer jump. */
379 continue;
381 if (rec_type <= 0) {
382 msg_warn("%s: message rejected: missing end record",
383 message->queue_id);
384 break;
386 if (rec_type == REC_TYPE_END) {
387 message->rflags |= QMGR_READ_FLAG_SEEN_ALL_NON_RCPT;
388 break;
392 * Map named attributes to pseudo record types, so that we don't have
393 * to pollute the queue file with records that are incompatible with
394 * past Postfix versions. Preferably, people should be able to back
395 * out from an upgrade without losing mail.
397 if (rec_type == REC_TYPE_ATTR) {
398 if ((error_text = split_nameval(start, &name, &value)) != 0) {
399 msg_warn("%s: ignoring bad attribute: %s: %.200s",
400 message->queue_id, error_text, start);
401 rec_type = REC_TYPE_ERROR;
402 break;
404 if ((n = rec_attr_map(name)) != 0) {
405 start = value;
406 rec_type = n;
411 * Process recipient records.
413 if (rec_type == REC_TYPE_RCPT) {
414 /* See also below for code setting orig_rcpt etc. */
415 #define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
416 if (message->rcpt_offset == 0) {
417 recipient_list_add(&message->rcpt_list, curr_offset,
418 dsn_orcpt ? dsn_orcpt : "",
419 dsn_notify ? dsn_notify : 0,
420 orig_rcpt ? orig_rcpt : "", start);
421 if (dsn_orcpt) {
422 myfree(dsn_orcpt);
423 dsn_orcpt = 0;
425 if (orig_rcpt) {
426 myfree(orig_rcpt);
427 orig_rcpt = 0;
429 if (dsn_notify)
430 dsn_notify = 0;
431 if (message->rcpt_list.len >= FUDGE(var_qmgr_rcpt_limit)) {
432 if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
433 msg_fatal("vstream_ftell %s: %m",
434 VSTREAM_PATH(message->fp));
435 if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
436 /* We already examined all non-recipient records. */
437 break;
438 if (message->rflags & QMGR_READ_FLAG_MIXED_RCPT_OTHER)
439 /* Examine all remaining non-recipient records. */
440 continue;
441 /* Optimizations for "pure recipient" record sections. */
442 if (curr_offset > message->data_offset) {
443 /* We already examined all non-recipient records. */
444 message->rflags |= QMGR_READ_FLAG_SEEN_ALL_NON_RCPT;
445 break;
447 /* Examine non-recipient records in extracted segment. */
448 if (vstream_fseek(message->fp, message->data_offset
449 + message->data_size, SEEK_SET) < 0)
450 msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
451 continue;
454 continue;
456 if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_DRCP) {
457 if (message->rcpt_offset == 0) {
458 if (dsn_orcpt) {
459 myfree(dsn_orcpt);
460 dsn_orcpt = 0;
462 if (orig_rcpt) {
463 myfree(orig_rcpt);
464 orig_rcpt = 0;
466 if (dsn_notify)
467 dsn_notify = 0;
469 continue;
471 if (rec_type == REC_TYPE_DSN_ORCPT) {
472 /* See also above for code clearing dsn_orcpt. */
473 if (dsn_orcpt != 0) {
474 msg_warn("%s: ignoring out-of-order DSN original recipient address <%.200s>",
475 message->queue_id, dsn_orcpt);
476 myfree(dsn_orcpt);
477 dsn_orcpt = 0;
479 if (message->rcpt_offset == 0)
480 dsn_orcpt = mystrdup(start);
481 continue;
483 if (rec_type == REC_TYPE_DSN_NOTIFY) {
484 /* See also above for code clearing dsn_notify. */
485 if (dsn_notify != 0) {
486 msg_warn("%s: ignoring out-of-order DSN notify flags <%d>",
487 message->queue_id, dsn_notify);
488 dsn_notify = 0;
490 if (message->rcpt_offset == 0) {
491 if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_NOTIFY_OK(n))
492 msg_warn("%s: ignoring malformed DSN notify flags <%.200s>",
493 message->queue_id, start);
494 else
495 dsn_notify = n;
496 continue;
499 if (rec_type == REC_TYPE_ORCP) {
500 /* See also above for code clearing orig_rcpt. */
501 if (orig_rcpt != 0) {
502 msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
503 message->queue_id, orig_rcpt);
504 myfree(orig_rcpt);
505 orig_rcpt = 0;
507 if (message->rcpt_offset == 0)
508 orig_rcpt = mystrdup(start);
509 continue;
513 * Process non-recipient records.
515 if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
516 /* We already examined all non-recipient records. */
517 continue;
518 if (rec_type == REC_TYPE_SIZE) {
519 if (message->data_offset == 0) {
520 if ((count = sscanf(start, "%ld %ld %d %d %ld",
521 &message->data_size, &message->data_offset,
522 &nrcpt, &message->rflags,
523 &message->cont_length)) >= 3) {
524 /* Postfix >= 1.0 (a.k.a. 20010228). */
525 if (message->data_offset <= 0 || message->data_size <= 0) {
526 msg_warn("%s: invalid size record: %.100s",
527 message->queue_id, start);
528 rec_type = REC_TYPE_ERROR;
529 break;
531 if (message->rflags & ~QMGR_READ_FLAG_USER) {
532 msg_warn("%s: invalid flags in size record: %.100s",
533 message->queue_id, start);
534 rec_type = REC_TYPE_ERROR;
535 break;
537 } else if (count == 1) {
538 /* Postfix < 1.0 (a.k.a. 20010228). */
539 qmgr_message_oldstyle_scan(message);
540 } else {
541 /* Can't happen. */
542 msg_warn("%s: message rejected: weird size record",
543 message->queue_id);
544 rec_type = REC_TYPE_ERROR;
545 break;
548 /* Postfix < 2.4 compatibility. */
549 if (message->cont_length == 0) {
550 message->cont_length = message->data_size;
551 } else if (message->cont_length < 0) {
552 msg_warn("%s: invalid size record: %.100s",
553 message->queue_id, start);
554 rec_type = REC_TYPE_ERROR;
555 break;
557 continue;
559 if (rec_type == REC_TYPE_TIME) {
560 if (message->arrival_time.tv_sec == 0)
561 REC_TYPE_TIME_SCAN(start, message->arrival_time);
562 continue;
564 if (rec_type == REC_TYPE_CTIME) {
565 if (message->create_time == 0)
566 message->create_time = atol(start);
567 continue;
569 if (rec_type == REC_TYPE_FILT) {
570 if (message->filter_xport != 0)
571 myfree(message->filter_xport);
572 message->filter_xport = mystrdup(start);
573 continue;
575 if (rec_type == REC_TYPE_INSP) {
576 if (message->inspect_xport != 0)
577 myfree(message->inspect_xport);
578 message->inspect_xport = mystrdup(start);
579 continue;
581 if (rec_type == REC_TYPE_RDR) {
582 if (message->redirect_addr != 0)
583 myfree(message->redirect_addr);
584 message->redirect_addr = mystrdup(start);
585 continue;
587 if (rec_type == REC_TYPE_FROM) {
588 if (message->sender == 0) {
589 message->sender = mystrdup(start);
590 opened(message->queue_id, message->sender,
591 message->cont_length, nrcpt,
592 "queue %s", message->queue_name);
594 continue;
596 if (rec_type == REC_TYPE_DSN_ENVID) {
597 if (message->dsn_envid == 0)
598 message->dsn_envid = mystrdup(start);
600 if (rec_type == REC_TYPE_DSN_RET) {
601 if (message->dsn_ret == 0) {
602 if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_RET_OK(n))
603 msg_warn("%s: ignoring malformed DSN RET flags in queue file record:%.100s",
604 message->queue_id, start);
605 else
606 message->dsn_ret = n;
609 if (rec_type == REC_TYPE_ATTR) {
610 /* Allow extra segment to override envelope segment info. */
611 if (strcmp(name, MAIL_ATTR_ENCODING) == 0) {
612 if (message->encoding != 0)
613 myfree(message->encoding);
614 message->encoding = mystrdup(value);
618 * Backwards compatibility. Before Postfix 2.3, the logging
619 * attributes were called client_name, etc. Now they are called
620 * log_client_name. etc., and client_name is used for the actual
621 * client information. To support old queue files, we accept both
622 * names for the purpose of logging; the new name overrides the
623 * old one.
625 * XXX Do not use the "legacy" client_name etc. attribute values for
626 * initializing the logging attributes, when this file already
627 * contains the "modern" log_client_name etc. logging attributes.
628 * Otherwise, logging attributes that are not present in the
629 * queue file would be set with information from the real client.
631 else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_NAME) == 0) {
632 if (have_log_client_attr == 0 && message->client_name == 0)
633 message->client_name = mystrdup(value);
634 } else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_ADDR) == 0) {
635 if (have_log_client_attr == 0 && message->client_addr == 0)
636 message->client_addr = mystrdup(value);
637 } else if (strcmp(name, MAIL_ATTR_ACT_CLIENT_PORT) == 0) {
638 if (have_log_client_attr == 0 && message->client_port == 0)
639 message->client_port = mystrdup(value);
640 } else if (strcmp(name, MAIL_ATTR_ACT_PROTO_NAME) == 0) {
641 if (have_log_client_attr == 0 && message->client_proto == 0)
642 message->client_proto = mystrdup(value);
643 } else if (strcmp(name, MAIL_ATTR_ACT_HELO_NAME) == 0) {
644 if (have_log_client_attr == 0 && message->client_helo == 0)
645 message->client_helo = mystrdup(value);
647 /* Original client attributes. */
648 else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_NAME) == 0) {
649 if (message->client_name != 0)
650 myfree(message->client_name);
651 message->client_name = mystrdup(value);
652 have_log_client_attr = 1;
653 } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_ADDR) == 0) {
654 if (message->client_addr != 0)
655 myfree(message->client_addr);
656 message->client_addr = mystrdup(value);
657 have_log_client_attr = 1;
658 } else if (strcmp(name, MAIL_ATTR_LOG_CLIENT_PORT) == 0) {
659 if (message->client_port != 0)
660 myfree(message->client_port);
661 message->client_port = mystrdup(value);
662 have_log_client_attr = 1;
663 } else if (strcmp(name, MAIL_ATTR_LOG_PROTO_NAME) == 0) {
664 if (message->client_proto != 0)
665 myfree(message->client_proto);
666 message->client_proto = mystrdup(value);
667 have_log_client_attr = 1;
668 } else if (strcmp(name, MAIL_ATTR_LOG_HELO_NAME) == 0) {
669 if (message->client_helo != 0)
670 myfree(message->client_helo);
671 message->client_helo = mystrdup(value);
672 have_log_client_attr = 1;
673 } else if (strcmp(name, MAIL_ATTR_SASL_METHOD) == 0) {
674 if (message->sasl_method == 0)
675 message->sasl_method = mystrdup(value);
676 else
677 msg_warn("%s: ignoring multiple %s attribute: %s",
678 message->queue_id, MAIL_ATTR_SASL_METHOD, value);
679 } else if (strcmp(name, MAIL_ATTR_SASL_USERNAME) == 0) {
680 if (message->sasl_username == 0)
681 message->sasl_username = mystrdup(value);
682 else
683 msg_warn("%s: ignoring multiple %s attribute: %s",
684 message->queue_id, MAIL_ATTR_SASL_USERNAME, value);
685 } else if (strcmp(name, MAIL_ATTR_SASL_SENDER) == 0) {
686 if (message->sasl_sender == 0)
687 message->sasl_sender = mystrdup(value);
688 else
689 msg_warn("%s: ignoring multiple %s attribute: %s",
690 message->queue_id, MAIL_ATTR_SASL_SENDER, value);
691 } else if (strcmp(name, MAIL_ATTR_RWR_CONTEXT) == 0) {
692 if (message->rewrite_context == 0)
693 message->rewrite_context = mystrdup(value);
694 else
695 msg_warn("%s: ignoring multiple %s attribute: %s",
696 message->queue_id, MAIL_ATTR_RWR_CONTEXT, value);
700 * Optional tracing flags (verify, sendmail -v, sendmail -bv).
701 * This record is killed after a trace logfile report is sent and
702 * after the logfile is deleted.
704 else if (strcmp(name, MAIL_ATTR_TRACE_FLAGS) == 0) {
705 message->tflags = DEL_REQ_TRACE_FLAGS(atoi(value));
706 if (message->tflags == DEL_REQ_FLAG_RECORD)
707 message->tflags_offset = curr_offset;
708 else
709 message->tflags_offset = 0;
711 continue;
713 if (rec_type == REC_TYPE_WARN) {
714 if (message->warn_offset == 0) {
715 message->warn_offset = curr_offset;
716 REC_TYPE_WARN_SCAN(start, message->warn_time);
718 continue;
720 if (rec_type == REC_TYPE_VERP) {
721 if (message->verp_delims == 0) {
722 if (message->sender == 0 || message->sender[0] == 0) {
723 msg_warn("%s: ignoring VERP request for null sender",
724 message->queue_id);
725 } else if (verp_delims_verify(start) != 0) {
726 msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
727 message->queue_id, start);
728 } else {
729 message->single_rcpt = 1;
730 message->verp_delims = mystrdup(start);
733 continue;
738 * Grr.
740 if (dsn_orcpt != 0) {
741 if (rec_type > 0)
742 msg_warn("%s: ignoring out-of-order DSN original recipient <%.200s>",
743 message->queue_id, dsn_orcpt);
744 myfree(orig_rcpt);
746 if (orig_rcpt != 0) {
747 if (rec_type > 0)
748 msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
749 message->queue_id, orig_rcpt);
750 myfree(orig_rcpt);
754 * Avoid clumsiness elsewhere in the program. When sending data across an
755 * IPC channel, sending an empty string is more convenient than sending a
756 * null pointer.
758 if (message->dsn_envid == 0)
759 message->dsn_envid = mystrdup("");
760 if (message->encoding == 0)
761 message->encoding = mystrdup(MAIL_ATTR_ENC_NONE);
762 if (message->client_name == 0)
763 message->client_name = mystrdup("");
764 if (message->client_addr == 0)
765 message->client_addr = mystrdup("");
766 if (message->client_port == 0)
767 message->client_port = mystrdup("");
768 if (message->client_proto == 0)
769 message->client_proto = mystrdup("");
770 if (message->client_helo == 0)
771 message->client_helo = mystrdup("");
772 if (message->sasl_method == 0)
773 message->sasl_method = mystrdup("");
774 if (message->sasl_username == 0)
775 message->sasl_username = mystrdup("");
776 if (message->sasl_sender == 0)
777 message->sasl_sender = mystrdup("");
778 if (message->rewrite_context == 0)
779 message->rewrite_context = mystrdup(MAIL_ATTR_RWR_LOCAL);
780 /* Postfix < 2.3 compatibility. */
781 if (message->create_time == 0)
782 message->create_time = message->arrival_time.tv_sec;
785 * Clean up.
787 vstring_free(buf);
790 * Sanity checks. Verify that all required information was found,
791 * including the queue file end marker.
793 if (rec_type <= 0) {
794 /* Already logged warning. */
795 } else if (message->arrival_time.tv_sec == 0) {
796 msg_warn("%s: message rejected: missing arrival time record",
797 message->queue_id);
798 } else if (message->sender == 0) {
799 msg_warn("%s: message rejected: missing sender record",
800 message->queue_id);
801 } else if (message->data_offset == 0) {
802 msg_warn("%s: message rejected: missing size record",
803 message->queue_id);
804 } else {
805 return (0);
807 message->rcpt_offset = save_offset; /* restore flag */
808 return (-1);
811 /* qmgr_message_update_warn - update the time of next delay warning */
813 void qmgr_message_update_warn(QMGR_MESSAGE *message)
817 * XXX eventually this should let us schedule multiple warnings, right
818 * now it just allows for one.
820 if (qmgr_message_open(message)
821 || vstream_fseek(message->fp, message->warn_offset, SEEK_SET) < 0
822 || rec_fprintf(message->fp, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
823 REC_TYPE_WARN_ARG(0)) < 0
824 || vstream_fflush(message->fp))
825 msg_fatal("update queue file %s: %m", VSTREAM_PATH(message->fp));
826 qmgr_message_close(message);
829 /* qmgr_message_kill_record - mark one message record as killed */
831 void qmgr_message_kill_record(QMGR_MESSAGE *message, long offset)
833 if (offset <= 0)
834 msg_panic("qmgr_message_kill_record: bad offset 0x%lx", offset);
835 if (qmgr_message_open(message)
836 || rec_put_type(message->fp, REC_TYPE_KILL, offset) < 0
837 || vstream_fflush(message->fp))
838 msg_fatal("update queue file %s: %m", VSTREAM_PATH(message->fp));
839 qmgr_message_close(message);
842 /* qmgr_message_sort_compare - compare recipient information */
844 static int qmgr_message_sort_compare(const void *p1, const void *p2)
846 RECIPIENT *rcpt1 = (RECIPIENT *) p1;
847 RECIPIENT *rcpt2 = (RECIPIENT *) p2;
848 QMGR_QUEUE *queue1;
849 QMGR_QUEUE *queue2;
850 char *at1;
851 char *at2;
852 int result;
855 * Compare most significant to least significant recipient attributes.
856 * The comparison function must be transitive, so NULL values need to be
857 * assigned an ordinal (we set NULL last).
860 queue1 = rcpt1->u.queue;
861 queue2 = rcpt2->u.queue;
862 if (queue1 != 0 && queue2 == 0)
863 return (-1);
864 if (queue1 == 0 && queue2 != 0)
865 return (1);
866 if (queue1 != 0 && queue2 != 0) {
869 * Compare message transport.
871 if ((result = strcmp(queue1->transport->name,
872 queue2->transport->name)) != 0)
873 return (result);
876 * Compare queue name (nexthop or recipient@nexthop).
878 if ((result = strcmp(queue1->name, queue2->name)) != 0)
879 return (result);
883 * Compare recipient domain.
885 at1 = strrchr(rcpt1->address, '@');
886 at2 = strrchr(rcpt2->address, '@');
887 if (at1 == 0 && at2 != 0)
888 return (1);
889 if (at1 != 0 && at2 == 0)
890 return (-1);
891 if (at1 != 0 && at2 != 0
892 && (result = strcasecmp(at1, at2)) != 0)
893 return (result);
896 * Compare recipient address.
898 return (strcasecmp(rcpt1->address, rcpt2->address));
901 /* qmgr_message_sort - sort message recipient addresses by domain */
903 static void qmgr_message_sort(QMGR_MESSAGE *message)
905 qsort((char *) message->rcpt_list.info, message->rcpt_list.len,
906 sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare);
907 if (msg_verbose) {
908 RECIPIENT_LIST list = message->rcpt_list;
909 RECIPIENT *rcpt;
911 msg_info("start sorted recipient list");
912 for (rcpt = list.info; rcpt < list.info + list.len; rcpt++)
913 msg_info("qmgr_message_sort: %s", rcpt->address);
914 msg_info("end sorted recipient list");
918 /* qmgr_resolve_one - resolve or skip one recipient */
920 static int qmgr_resolve_one(QMGR_MESSAGE *message, RECIPIENT *recipient,
921 const char *addr, RESOLVE_REPLY *reply)
923 #define QMGR_REDIRECT(rp, tp, np) do { \
924 (rp)->flags = 0; \
925 vstring_strcpy((rp)->transport, (tp)); \
926 vstring_strcpy((rp)->nexthop, (np)); \
927 } while (0)
929 if ((message->tflags & DEL_REQ_FLAG_MTA_VRFY) == 0)
930 resolve_clnt_query_from(message->sender, addr, reply);
931 else
932 resolve_clnt_verify_from(message->sender, addr, reply);
933 if (reply->flags & RESOLVE_FLAG_FAIL) {
934 QMGR_REDIRECT(reply, MAIL_SERVICE_RETRY,
935 "4.3.0 address resolver failure");
936 return (0);
937 } else if (reply->flags & RESOLVE_FLAG_ERROR) {
938 QMGR_REDIRECT(reply, MAIL_SERVICE_ERROR,
939 "5.1.3 bad address syntax");
940 return (0);
941 } else {
942 return (0);
946 /* qmgr_message_resolve - resolve recipients */
948 static void qmgr_message_resolve(QMGR_MESSAGE *message)
950 static ARGV *defer_xport_argv;
951 RECIPIENT_LIST list = message->rcpt_list;
952 RECIPIENT *recipient;
953 QMGR_TRANSPORT *transport = 0;
954 QMGR_QUEUE *queue = 0;
955 RESOLVE_REPLY reply;
956 VSTRING *queue_name;
957 char *at;
958 char **cpp;
959 char *nexthop;
960 ssize_t len;
961 int status;
962 DSN dsn;
963 MSG_STATS stats;
964 DSN *saved_dsn;
966 #define STREQ(x,y) (strcmp(x,y) == 0)
967 #define STR vstring_str
968 #define LEN VSTRING_LEN
970 resolve_clnt_init(&reply);
971 queue_name = vstring_alloc(1);
972 for (recipient = list.info; recipient < list.info + list.len; recipient++) {
975 * Redirect overrides all else. But only once (per entire message).
976 * For consistency with the remainder of Postfix, rewrite the address
977 * to canonical form before resolving it.
979 if (message->redirect_addr) {
980 if (recipient > list.info) {
981 recipient->u.queue = 0;
982 continue;
984 message->rcpt_offset = 0;
985 rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
986 reply.recipient);
987 RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
988 if (qmgr_resolve_one(message, recipient,
989 recipient->address, &reply) < 0)
990 continue;
991 if (!STREQ(recipient->address, STR(reply.recipient)))
992 RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
996 * Content filtering overrides the address resolver.
998 * XXX Bypass content_filter inspection for user-generated probes
999 * (sendmail -bv). MTA-generated probes never have the "please filter
1000 * me" bits turned on, but we handle them here anyway for the sake of
1001 * future proofing.
1003 else if (message->filter_xport
1004 && (message->tflags & DEL_REQ_TRACE_ONLY_MASK) == 0) {
1005 reply.flags = 0;
1006 vstring_strcpy(reply.transport, message->filter_xport);
1007 if ((nexthop = split_at(STR(reply.transport), ':')) == 0
1008 || *nexthop == 0)
1009 nexthop = var_myhostname;
1010 vstring_strcpy(reply.nexthop, nexthop);
1011 vstring_strcpy(reply.recipient, recipient->address);
1015 * Resolve the destination to (transport, nexthop, address). The
1016 * result address may differ from the one specified by the sender.
1018 else {
1019 if (qmgr_resolve_one(message, recipient,
1020 recipient->address, &reply) < 0)
1021 continue;
1022 if (!STREQ(recipient->address, STR(reply.recipient)))
1023 RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
1027 * Bounce null recipients. This should never happen, but is most
1028 * likely the result of a fault in a different program, so aborting
1029 * the queue manager process does not help.
1031 if (recipient->address[0] == 0) {
1032 QMGR_REDIRECT(&reply, MAIL_SERVICE_ERROR,
1033 "5.1.3 null recipient address");
1037 * Discard mail to the local double bounce address here, so this
1038 * system can run without a local delivery agent. They'd still have
1039 * to configure something for mail directed to the local postmaster,
1040 * though, but that is an RFC requirement anyway.
1042 * XXX This lookup should be done in the resolver, and the mail should
1043 * be directed to a general-purpose null delivery agent.
1045 if (reply.flags & RESOLVE_CLASS_LOCAL) {
1046 at = strrchr(STR(reply.recipient), '@');
1047 len = (at ? (at - STR(reply.recipient))
1048 : strlen(STR(reply.recipient)));
1049 if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
1050 len) == 0
1051 && !var_double_bounce_sender[len]) {
1052 status = sent(message->tflags, message->queue_id,
1053 QMGR_MSG_STATS(&stats, message), recipient,
1054 "none", DSN_SIMPLE(&dsn, "2.0.0",
1055 "undeliverable postmaster notification discarded"));
1056 if (status == 0) {
1057 deliver_completed(message->fp, recipient->offset);
1058 #if 0
1059 /* It's the default verification probe sender address. */
1060 msg_warn("%s: undeliverable postmaster notification discarded",
1061 message->queue_id);
1062 #endif
1063 } else
1064 message->flags |= status;
1065 continue;
1070 * Optionally defer deliveries over specific transports, unless the
1071 * restriction is lifted temporarily.
1073 if (*var_defer_xports && (message->qflags & QMGR_FLUSH_DFXP) == 0) {
1074 if (defer_xport_argv == 0)
1075 defer_xport_argv = argv_split(var_defer_xports, " \t\r\n,");
1076 for (cpp = defer_xport_argv->argv; *cpp; cpp++)
1077 if (strcmp(*cpp, STR(reply.transport)) == 0)
1078 break;
1079 if (*cpp) {
1080 QMGR_REDIRECT(&reply, MAIL_SERVICE_RETRY,
1081 "4.3.2 deferred transport");
1086 * Look up or instantiate the proper transport.
1088 if (transport == 0 || !STREQ(transport->name, STR(reply.transport))) {
1089 if ((transport = qmgr_transport_find(STR(reply.transport))) == 0)
1090 transport = qmgr_transport_create(STR(reply.transport));
1091 queue = 0;
1095 * This message is being flushed. If need-be unthrottle the
1096 * transport.
1098 if ((message->qflags & QMGR_FLUSH_EACH) != 0
1099 && QMGR_TRANSPORT_THROTTLED(transport))
1100 qmgr_transport_unthrottle(transport);
1103 * This transport is dead. Defer delivery to this recipient.
1105 if (QMGR_TRANSPORT_THROTTLED(transport)) {
1106 saved_dsn = transport->dsn;
1107 if ((transport = qmgr_error_transport(MAIL_SERVICE_RETRY)) != 0) {
1108 nexthop = qmgr_error_nexthop(saved_dsn);
1109 vstring_strcpy(reply.nexthop, nexthop);
1110 myfree(nexthop);
1111 queue = 0;
1112 } else {
1113 qmgr_defer_recipient(message, recipient, saved_dsn);
1114 continue;
1119 * The nexthop destination provides the default name for the
1120 * per-destination queue. When the delivery agent accepts only one
1121 * recipient per delivery, give each recipient its own queue, so that
1122 * deliveries to different recipients of the same message can happen
1123 * in parallel, and so that we can enforce per-recipient concurrency
1124 * limits and prevent one recipient from tying up all the delivery
1125 * agent resources. We use recipient@nexthop as queue name rather
1126 * than the actual recipient domain name, so that one recipient in
1127 * multiple equivalent domains cannot evade the per-recipient
1128 * concurrency limit. Split the address on the recipient delimiter if
1129 * one is defined, so that extended addresses don't get extra
1130 * delivery slots.
1132 * Fold the result to lower case so that we don't have multiple queues
1133 * for the same name.
1135 * Important! All recipients in a queue must have the same nexthop
1136 * value. It is OK to have multiple queues with the same nexthop
1137 * value, but only when those queues are named after recipients.
1139 * The single-recipient code below was written for local(8) like
1140 * delivery agents, and assumes that all domains that deliver to the
1141 * same (transport + nexthop) are aliases for $nexthop. Delivery
1142 * concurrency is changed from per-domain into per-recipient, by
1143 * changing the queue name from nexthop into localpart@nexthop.
1145 * XXX This assumption is incorrect when different destinations share
1146 * the same (transport + nexthop). In reality, such transports are
1147 * rarely configured to use single-recipient deliveries. The fix is
1148 * to decouple the per-destination recipient limit from the
1149 * per-destination concurrency.
1151 vstring_strcpy(queue_name, STR(reply.nexthop));
1152 if (strcmp(transport->name, MAIL_SERVICE_ERROR) != 0
1153 && strcmp(transport->name, MAIL_SERVICE_RETRY) != 0
1154 && transport->recipient_limit == 1) {
1155 /* Copy the recipient localpart. */
1156 at = strrchr(STR(reply.recipient), '@');
1157 len = (at ? (at - STR(reply.recipient))
1158 : strlen(STR(reply.recipient)));
1159 vstring_strncpy(queue_name, STR(reply.recipient), len);
1160 /* Remove the address extension from the recipient localpart. */
1161 if (*var_rcpt_delim && split_addr(STR(queue_name), *var_rcpt_delim))
1162 vstring_truncate(queue_name, strlen(STR(queue_name)));
1163 /* Assume the recipient domain is equivalent to nexthop. */
1164 vstring_sprintf_append(queue_name, "@%s", STR(reply.nexthop));
1166 lowercase(STR(queue_name));
1169 * This transport is alive. Find or instantiate a queue for this
1170 * recipient.
1172 if (queue == 0 || !STREQ(queue->name, STR(queue_name))) {
1173 if ((queue = qmgr_queue_find(transport, STR(queue_name))) == 0)
1174 queue = qmgr_queue_create(transport, STR(queue_name),
1175 STR(reply.nexthop));
1179 * This message is being flushed. If need-be unthrottle the queue.
1181 if ((message->qflags & QMGR_FLUSH_EACH) != 0
1182 && QMGR_QUEUE_THROTTLED(queue))
1183 qmgr_queue_unthrottle(queue);
1186 * This queue is dead. Defer delivery to this recipient.
1188 if (QMGR_QUEUE_THROTTLED(queue)) {
1189 saved_dsn = queue->dsn;
1190 if ((queue = qmgr_error_queue(MAIL_SERVICE_RETRY, saved_dsn)) == 0) {
1191 qmgr_defer_recipient(message, recipient, saved_dsn);
1192 continue;
1197 * This queue is alive. Bind this recipient to this queue instance.
1199 recipient->u.queue = queue;
1201 resolve_clnt_free(&reply);
1202 vstring_free(queue_name);
1205 /* qmgr_message_assign - assign recipients to specific delivery requests */
1207 static void qmgr_message_assign(QMGR_MESSAGE *message)
1209 RECIPIENT_LIST list = message->rcpt_list;
1210 RECIPIENT *recipient;
1211 QMGR_ENTRY *entry = 0;
1212 QMGR_QUEUE *queue;
1215 * Try to bundle as many recipients in a delivery request as we can. When
1216 * the recipient resolves to the same site and transport as the previous
1217 * recipient, do not create a new queue entry, just move that recipient
1218 * to the recipient list of the existing queue entry. All this provided
1219 * that we do not exceed the transport-specific limit on the number of
1220 * recipients per transaction. Skip recipients with a dead transport or
1221 * destination.
1223 #define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit)))
1225 for (recipient = list.info; recipient < list.info + list.len; recipient++) {
1226 if ((queue = recipient->u.queue) != 0) {
1227 if (message->single_rcpt || entry == 0 || entry->queue != queue
1228 || !LIMIT_OK(entry->queue->transport->recipient_limit,
1229 entry->rcpt_list.len)) {
1230 entry = qmgr_entry_create(queue, message);
1232 recipient_list_add(&entry->rcpt_list, recipient->offset,
1233 recipient->dsn_orcpt, recipient->dsn_notify,
1234 recipient->orig_addr, recipient->address);
1235 qmgr_recipient_count++;
1238 recipient_list_free(&message->rcpt_list);
1239 recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
1242 /* qmgr_message_free - release memory for in-core message structure */
1244 void qmgr_message_free(QMGR_MESSAGE *message)
1246 if (message->refcount != 0)
1247 msg_panic("qmgr_message_free: reference len: %d", message->refcount);
1248 if (message->fp)
1249 msg_panic("qmgr_message_free: queue file is open");
1250 myfree(message->queue_id);
1251 myfree(message->queue_name);
1252 if (message->dsn_envid)
1253 myfree(message->dsn_envid);
1254 if (message->encoding)
1255 myfree(message->encoding);
1256 if (message->sender)
1257 myfree(message->sender);
1258 if (message->verp_delims)
1259 myfree(message->verp_delims);
1260 if (message->filter_xport)
1261 myfree(message->filter_xport);
1262 if (message->inspect_xport)
1263 myfree(message->inspect_xport);
1264 if (message->redirect_addr)
1265 myfree(message->redirect_addr);
1266 if (message->client_name)
1267 myfree(message->client_name);
1268 if (message->client_addr)
1269 myfree(message->client_addr);
1270 if (message->client_port)
1271 myfree(message->client_port);
1272 if (message->client_proto)
1273 myfree(message->client_proto);
1274 if (message->client_helo)
1275 myfree(message->client_helo);
1276 if (message->sasl_method)
1277 myfree(message->sasl_method);
1278 if (message->sasl_username)
1279 myfree(message->sasl_username);
1280 if (message->sasl_sender)
1281 myfree(message->sasl_sender);
1282 if (message->rewrite_context)
1283 myfree(message->rewrite_context);
1284 recipient_list_free(&message->rcpt_list);
1285 qmgr_message_count--;
1286 myfree((char *) message);
1289 /* qmgr_message_alloc - create in-core message structure */
1291 QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
1292 int qflags, mode_t mode)
1294 const char *myname = "qmgr_message_alloc";
1295 QMGR_MESSAGE *message;
1297 if (msg_verbose)
1298 msg_info("%s: %s %s", myname, queue_name, queue_id);
1301 * Create an in-core message structure.
1303 message = qmgr_message_create(queue_name, queue_id, qflags);
1306 * Extract message envelope information: time of arrival, sender address,
1307 * recipient addresses. Skip files with malformed envelope information.
1309 #define QMGR_LOCK_MODE (MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT)
1311 if (qmgr_message_open(message) < 0) {
1312 qmgr_message_free(message);
1313 return (0);
1315 if (myflock(vstream_fileno(message->fp), INTERNAL_LOCK, QMGR_LOCK_MODE) < 0) {
1316 msg_info("%s: skipped, still being delivered", queue_id);
1317 qmgr_message_close(message);
1318 qmgr_message_free(message);
1319 return (QMGR_MESSAGE_LOCKED);
1321 if (qmgr_message_read(message) < 0) {
1322 qmgr_message_close(message);
1323 qmgr_message_free(message);
1324 return (0);
1325 } else {
1328 * We have validated the queue file content, so it is safe to modify
1329 * the file properties now.
1331 if (mode != 0 && fchmod(vstream_fileno(message->fp), mode) < 0)
1332 msg_fatal("fchmod %s: %m", VSTREAM_PATH(message->fp));
1335 * Reset the defer log. This code should not be here, but we must
1336 * reset the defer log *after* acquiring the exclusive lock on the
1337 * queue file and *before* resolving new recipients. Since all those
1338 * operations are encapsulated so nicely by this routine, the defer
1339 * log reset has to be done here as well.
1341 * Note: it is safe to remove the defer logfile from a previous queue
1342 * run of this queue file, because the defer log contains information
1343 * about recipients that still exist in this queue file.
1345 if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT)
1346 msg_fatal("%s: %s: remove %s %s: %m", myname,
1347 queue_id, MAIL_QUEUE_DEFER, queue_id);
1348 qmgr_message_sort(message);
1349 qmgr_message_resolve(message);
1350 qmgr_message_sort(message);
1351 qmgr_message_assign(message);
1352 qmgr_message_close(message);
1353 return (message);
1357 /* qmgr_message_realloc - refresh in-core message structure */
1359 QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *message)
1361 const char *myname = "qmgr_message_realloc";
1364 * Sanity checks.
1366 if (message->rcpt_offset <= 0)
1367 msg_panic("%s: invalid offset: %ld", myname, message->rcpt_offset);
1368 if (msg_verbose)
1369 msg_info("%s: %s %s offset %ld", myname, message->queue_name,
1370 message->queue_id, message->rcpt_offset);
1373 * Extract recipient addresses. Skip files with malformed envelope
1374 * information.
1376 if (qmgr_message_open(message) < 0)
1377 return (0);
1378 if (qmgr_message_read(message) < 0) {
1379 qmgr_message_close(message);
1380 return (0);
1381 } else {
1382 qmgr_message_sort(message);
1383 qmgr_message_resolve(message);
1384 qmgr_message_sort(message);
1385 qmgr_message_assign(message);
1386 qmgr_message_close(message);
1387 return (message);