2 * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
5 * This code is derived from software contributed to The DragonFly Project
6 * by Simon Schubert <2@0x2c.org>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific, prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * 'Q'id files (queue):
56 * Organized like an RFC822 header, field: value. Ignores unknown fields.
58 * Sender: envelope-from
59 * Recipient: envelope-to
64 * Each queue file needs to have a corresponding data file.
65 * One data file might be shared by linking it several times.
67 * Queue ids are unique, formed from the inode of the data file
68 * and a unique identifier.
72 newspoolf(struct queue
*queue
)
79 if (snprintf(fn
, sizeof(fn
), "%s/%s", config
.spooldir
, "tmp_XXXXXXXXXX") <= 0)
85 /* XXX group rights */
86 if (fchmod(fd
, 0660) < 0)
88 if (flock(fd
, LOCK_EX
) == -1)
90 queue
->tmpf
= strdup(fn
);
91 if (queue
->tmpf
== NULL
)
97 if (fstat(fd
, &st
) != 0)
99 if (asprintf(&queue
->id
, "%"PRIxMAX
, (uintmax_t)st
.st_ino
) < 0)
102 queue
->mailf
= fdopen(fd
, "r+");
103 if (queue
->mailf
== NULL
)
106 t
= malloc(sizeof(*t
));
108 t
->str
= queue
->tmpf
;
109 SLIST_INSERT_HEAD(&tmpfs
, t
, next
);
114 if (queue
->mailf
!= NULL
)
115 fclose(queue
->mailf
);
122 writequeuef(struct qitem
*it
)
127 queuefd
= open_locked(it
->queuefn
, O_CREAT
|O_EXCL
|O_RDWR
, 0660);
130 if (fchmod(queuefd
, 0660) < 0)
132 it
->queuef
= fdopen(queuefd
, "w+");
133 if (it
->queuef
== NULL
)
136 error
= fprintf(it
->queuef
,
147 if (fflush(it
->queuef
) != 0 || fsync(fileno(it
->queuef
)) != 0)
153 static struct qitem
*
154 readqueuef(struct queue
*queue
, char *queuefn
)
157 struct queue itmqueue
;
160 char *queueid
= NULL
, *sender
= NULL
, *addr
= NULL
;
161 struct qitem
*it
= NULL
;
163 bzero(&itmqueue
, sizeof(itmqueue
));
164 LIST_INIT(&itmqueue
.queue
);
166 queuef
= fopen(queuefn
, "r");
170 while (!feof(queuef
)) {
171 if (fgets(line
, sizeof(line
), queuef
) == NULL
|| line
[0] == 0)
173 line
[strlen(line
) - 1] = 0; /* chop newline */
175 s
= strchr(line
, ':');
188 if (strcmp(line
, "ID") == 0) {
190 } else if (strcmp(line
, "Sender") == 0) {
192 } else if (strcmp(line
, "Recipient") == 0) {
195 syslog(LOG_DEBUG
, "ignoring unknown queue info `%s' in `%s'",
201 if (queueid
== NULL
|| sender
== NULL
|| addr
== NULL
||
202 *queueid
== 0 || *addr
== 0) {
205 syslog(LOG_ERR
, "malformed queue file `%s'", queuefn
);
209 if (add_recp(&itmqueue
, addr
, 0) != 0)
212 it
= LIST_FIRST(&itmqueue
.queue
);
213 it
->sender
= sender
; sender
= NULL
;
214 it
->queueid
= queueid
; queueid
= NULL
;
215 it
->queuefn
= queuefn
; queuefn
= NULL
;
216 LIST_INSERT_HEAD(&queue
->queue
, it
, next
);
232 linkspool(struct queue
*queue
)
237 if (fflush(queue
->mailf
) != 0 || fsync(fileno(queue
->mailf
)) != 0)
240 syslog(LOG_INFO
, "new mail from user=%s uid=%d envelope_from=<%s>",
241 username
, getuid(), queue
->sender
);
243 LIST_FOREACH(it
, &queue
->queue
, next
) {
244 if (asprintf(&it
->queueid
, "%s.%"PRIxPTR
, queue
->id
, (uintptr_t)it
) <= 0)
246 if (asprintf(&it
->queuefn
, "%s/Q%s", config
.spooldir
, it
->queueid
) <= 0)
248 if (asprintf(&it
->mailfn
, "%s/M%s", config
.spooldir
, it
->queueid
) <= 0)
251 /* Neither file may not exist yet */
252 if (stat(it
->queuefn
, &st
) == 0 || stat(it
->mailfn
, &st
) == 0)
255 if (writequeuef(it
) != 0)
258 if (link(queue
->tmpf
, it
->mailfn
) != 0)
262 LIST_FOREACH(it
, &queue
->queue
, next
) {
263 syslog(LOG_INFO
, "mail to=<%s> queued as %s",
264 it
->addr
, it
->queueid
);
271 LIST_FOREACH(it
, &queue
->queue
, next
) {
279 load_queue(struct queue
*queue
)
288 bzero(queue
, sizeof(*queue
));
289 LIST_INIT(&queue
->queue
);
291 spooldir
= opendir(config
.spooldir
);
292 if (spooldir
== NULL
)
293 err(1, "reading queue");
295 while ((de
= readdir(spooldir
)) != NULL
) {
299 /* ignore non-queue files */
300 if (de
->d_name
[0] != 'Q')
302 if (asprintf(&queuefn
, "%s/Q%s", config
.spooldir
, de
->d_name
+ 1) < 0)
304 if (asprintf(&mailfn
, "%s/M%s", config
.spooldir
, de
->d_name
+ 1) < 0)
308 * Some file systems don't provide a de->d_type, so we have to
309 * do an explicit stat on the queue file.
310 * Move on if it turns out to be something else than a file.
312 if (stat(queuefn
, &sb
) != 0)
314 if (!S_ISREG(sb
.st_mode
)) {
319 if (stat(mailfn
, &sb
) != 0)
322 it
= readqueuef(queue
, queuefn
);
330 syslog(LOG_INFO
, "could not pick up queue file: `%s'/`%s': %m", queuefn
, mailfn
);
344 delqueue(struct qitem
*it
)
348 if (it
->queuef
!= NULL
)
350 if (it
->mailf
!= NULL
)
356 acquirespool(struct qitem
*it
)
360 if (it
->queuef
== NULL
) {
361 queuefd
= open_locked(it
->queuefn
, O_RDWR
|O_NONBLOCK
);
364 it
->queuef
= fdopen(queuefd
, "r+");
365 if (it
->queuef
== NULL
)
369 if (it
->mailf
== NULL
) {
370 it
->mailf
= fopen(it
->mailfn
, "r");
371 if (it
->mailf
== NULL
)
378 if (errno
== EWOULDBLOCK
)
380 syslog(LOG_INFO
, "could not acquire queue file: %m");
385 dropspool(struct queue
*queue
, struct qitem
*keep
)
389 LIST_FOREACH(it
, &queue
->queue
, next
) {
393 if (it
->queuef
!= NULL
)
395 if (it
->mailf
!= NULL
)
401 flushqueue_since(unsigned int period
)
405 char *flushfn
= NULL
;
407 if (asprintf(&flushfn
, "%s/%s", config
.spooldir
, SPOOL_FLUSHFILE
) < 0)
409 if (stat(flushfn
, &st
) < 0) {
415 if (gettimeofday(&now
, 0) != 0)
418 /* Did the flush file get touched within the last period seconds? */
419 if (st
.st_mtim
.tv_sec
+ period
>= now
.tv_sec
)
426 flushqueue_signal(void)
428 char *flushfn
= NULL
;
431 if (asprintf(&flushfn
, "%s/%s", config
.spooldir
, SPOOL_FLUSHFILE
) < 0)
433 fd
= open(flushfn
, O_CREAT
|O_WRONLY
|O_TRUNC
, 0660);
436 syslog(LOG_ERR
, "could not open flush file: %m");