No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / mail_queue.c
bloba457c2939b69d04da3d054f11af86f0be3f98baa
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* mail_queue 3
6 /* SUMMARY
7 /* mail queue file access
8 /* SYNOPSIS
9 /* #include <mail_queue.h>
11 /* VSTREAM *mail_queue_enter(queue_name, mode, tp)
12 /* const char *queue_name;
13 /* mode_t mode;
14 /* struct timeval *tp;
16 /* VSTREAM *mail_queue_open(queue_name, queue_id, flags, mode)
17 /* const char *queue_name;
18 /* const char *queue_id;
19 /* int flags;
20 /* mode_t mode;
22 /* char *mail_queue_dir(buf, queue_name, queue_id)
23 /* VSTRING *buf;
24 /* const char *queue_name;
25 /* const char *queue_id;
27 /* char *mail_queue_path(buf, queue_name, queue_id)
28 /* VSTRING *buf;
29 /* const char *queue_name;
30 /* const char *queue_id;
32 /* int mail_queue_mkdirs(path)
33 /* const char *path;
35 /* int mail_queue_rename(queue_id, old_queue, new_queue)
36 /* const char *queue_id;
37 /* const char *old_queue;
38 /* const char *new_queue;
40 /* int mail_queue_remove(queue_name, queue_id)
41 /* const char *queue_name;
42 /* const char *queue_id;
44 /* int mail_queue_name_ok(queue_name)
45 /* const char *queue_name;
47 /* int mail_queue_id_ok(queue_id)
48 /* const char *queue_id;
49 /* DESCRIPTION
50 /* This module encapsulates access to the mail queue hierarchy.
51 /* Unlike most other modules, this one does not abort the program
52 /* in case of file access problems. But it does abort when the
53 /* application attempts to use a malformed queue name or queue id.
55 /* mail_queue_enter() creates an entry in the named queue. The queue
56 /* id is the file base name, see VSTREAM_PATH(). Queue ids are
57 /* relatively short strings and are recycled in the course of time.
58 /* The only guarantee given is that on a given machine, no two queue
59 /* entries will have the same queue ID at the same time. The tp
60 /* argument, if not a null pointer, receives the time stamp that
61 /* corresponds with the queue ID.
63 /* mail_queue_open() opens the named queue file. The \fIflags\fR
64 /* and \fImode\fR arguments are as with open(2). The result is a
65 /* null pointer in case of problems.
67 /* mail_queue_dir() returns the directory name of the specified queue
68 /* file. When a null result buffer pointer is provided, the result is
69 /* written to a private buffer that may be overwritten upon the next
70 /* call.
72 /* mail_queue_path() returns the pathname of the specified queue
73 /* file. When a null result buffer pointer is provided, the result
74 /* is written to a private buffer that may be overwritten upon the
75 /* next call.
77 /* mail_queue_mkdirs() creates missing parent directories
78 /* for the file named in \fBpath\fR. A non-zero result means
79 /* that the operation failed.
81 /* mail_queue_rename() renames a queue file. A non-zero result
82 /* means the operation failed.
84 /* mail_queue_remove() removes the named queue file. A non-zero result
85 /* means the operation failed.
87 /* mail_queue_name_ok() validates a mail queue name and returns
88 /* non-zero (true) if the name contains no nasty characters.
90 /* mail_queue_id_ok() does the same thing for mail queue ID names.
91 /* DIAGNOSTICS
92 /* Panic: invalid queue name or id given to mail_queue_path(),
93 /* mail_queue_rename(), or mail_queue_remove().
94 /* Fatal error: out of memory.
95 /* LICENSE
96 /* .ad
97 /* .fi
98 /* The Secure Mailer license must be distributed with this software.
99 /* AUTHOR(S)
100 /* Wietse Venema
101 /* IBM T.J. Watson Research
102 /* P.O. Box 704
103 /* Yorktown Heights, NY 10598, USA
104 /*--*/
106 /* System library. */
108 #include <sys_defs.h>
109 #include <stdio.h> /* rename() */
110 #include <stdlib.h>
111 #include <ctype.h>
112 #include <stdlib.h>
113 #include <unistd.h>
114 #include <fcntl.h>
115 #include <sys/time.h> /* gettimeofday, not in POSIX */
116 #include <string.h>
117 #include <errno.h>
119 #ifdef STRCASECMP_IN_STRINGS_H
120 #include <strings.h>
121 #endif
123 /* Utility library. */
125 #include <msg.h>
126 #include <vstring.h>
127 #include <vstream.h>
128 #include <mymalloc.h>
129 #include <argv.h>
130 #include <dir_forest.h>
131 #include <make_dirs.h>
132 #include <split_at.h>
133 #include <sane_fsops.h>
134 #include <valid_hostname.h>
136 /* Global library. */
138 #include "file_id.h"
139 #include "mail_params.h"
140 #include "mail_queue.h"
142 #define STR vstring_str
144 /* mail_queue_dir - construct mail queue directory name */
146 const char *mail_queue_dir(VSTRING *buf, const char *queue_name,
147 const char *queue_id)
149 const char *myname = "mail_queue_dir";
150 static VSTRING *private_buf = 0;
151 static VSTRING *hash_buf = 0;
152 static ARGV *hash_queue_names = 0;
153 char **cpp;
156 * Sanity checks.
158 if (mail_queue_name_ok(queue_name) == 0)
159 msg_panic("%s: bad queue name: %s", myname, queue_name);
160 if (mail_queue_id_ok(queue_id) == 0)
161 msg_panic("%s: bad queue id: %s", myname, queue_id);
164 * Initialize.
166 if (buf == 0) {
167 if (private_buf == 0)
168 private_buf = vstring_alloc(100);
169 buf = private_buf;
171 if (hash_buf == 0) {
172 hash_buf = vstring_alloc(100);
173 hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,");
177 * First, put the basic queue directory name into place.
179 vstring_strcpy(buf, queue_name);
180 vstring_strcat(buf, "/");
183 * Then, see if we need to append a little directory forest.
185 for (cpp = hash_queue_names->argv; *cpp; cpp++) {
186 if (strcasecmp(*cpp, queue_name) == 0) {
187 vstring_strcat(buf,
188 dir_forest(hash_buf, queue_id, var_hash_queue_depth));
189 break;
192 return (STR(buf));
195 /* mail_queue_path - map mail queue id to path name */
197 const char *mail_queue_path(VSTRING *buf, const char *queue_name,
198 const char *queue_id)
200 static VSTRING *private_buf = 0;
203 * Initialize.
205 if (buf == 0) {
206 if (private_buf == 0)
207 private_buf = vstring_alloc(100);
208 buf = private_buf;
212 * Append the queue id to the possibly hashed queue directory.
214 (void) mail_queue_dir(buf, queue_name, queue_id);
215 vstring_strcat(buf, queue_id);
216 return (STR(buf));
219 /* mail_queue_mkdirs - fill in missing directories */
221 int mail_queue_mkdirs(const char *path)
223 const char *myname = "mail_queue_mkdirs";
224 char *saved_path = mystrdup(path);
225 int ret;
228 * Truncate a copy of the pathname (for safety sake), and create the
229 * missing directories.
231 if (split_at_right(saved_path, '/') == 0)
232 msg_panic("%s: no slash in: %s", myname, saved_path);
233 ret = make_dirs(saved_path, 0700);
234 myfree(saved_path);
235 return (ret);
238 /* mail_queue_rename - move message to another queue */
240 int mail_queue_rename(const char *queue_id, const char *old_queue,
241 const char *new_queue)
243 VSTRING *old_buf = vstring_alloc(100);
244 VSTRING *new_buf = vstring_alloc(100);
245 int error;
248 * Try the operation. If it fails, see if it is because of missing
249 * intermediate directories.
251 error = sane_rename(mail_queue_path(old_buf, old_queue, queue_id),
252 mail_queue_path(new_buf, new_queue, queue_id));
253 if (error != 0 && mail_queue_mkdirs(STR(new_buf)) == 0)
254 error = sane_rename(STR(old_buf), STR(new_buf));
257 * Cleanup.
259 vstring_free(old_buf);
260 vstring_free(new_buf);
262 return (error);
265 /* mail_queue_remove - remove mail queue file */
267 int mail_queue_remove(const char *queue_name, const char *queue_id)
269 return (REMOVE(mail_queue_path((VSTRING *) 0, queue_name, queue_id)));
272 /* mail_queue_name_ok - validate mail queue name */
274 int mail_queue_name_ok(const char *queue_name)
276 const char *cp;
278 if (*queue_name == 0 || strlen(queue_name) > 100)
279 return (0);
281 for (cp = queue_name; *cp; cp++)
282 if (!ISALNUM(*cp))
283 return (0);
284 return (1);
287 /* mail_queue_id_ok - validate mail queue id */
289 int mail_queue_id_ok(const char *queue_id)
291 const char *cp;
294 * A file name is either a queue ID (short alphanumeric string in
295 * time+inum form) or a fast flush service logfile name (destination
296 * domain name with non-alphanumeric characters replaced by "_").
298 if (*queue_id == 0 || strlen(queue_id) > VALID_HOSTNAME_LEN)
299 return (0);
302 * OK if in time+inum form or in host_domain_tld form.
304 for (cp = queue_id; *cp; cp++)
305 if (!ISALNUM(*cp) && *cp != '_')
306 return (0);
307 return (1);
310 /* mail_queue_enter - make mail queue entry with locally-unique name */
312 VSTREAM *mail_queue_enter(const char *queue_name, mode_t mode,
313 struct timeval * tp)
315 const char *myname = "mail_queue_enter";
316 static VSTRING *id_buf;
317 static int pid;
318 static VSTRING *path_buf;
319 static VSTRING *temp_path;
320 struct timeval tv;
321 int fd;
322 const char *file_id;
323 VSTREAM *stream;
324 int count;
327 * Initialize.
329 if (id_buf == 0) {
330 pid = getpid();
331 id_buf = vstring_alloc(10);
332 path_buf = vstring_alloc(10);
333 temp_path = vstring_alloc(100);
335 if (tp == 0)
336 tp = &tv;
339 * Create a file with a temporary name that does not collide. The process
340 * ID alone is not sufficiently unique: maildrops can be shared via the
341 * network. Not that I recommend using a network-based queue, or having
342 * multiple hosts write to the same queue, but we should try to avoid
343 * losing mail if we can.
345 * If someone is racing against us, try to win.
347 for (;;) {
348 GETTIMEOFDAY(tp);
349 vstring_sprintf(temp_path, "%s/%d.%d", queue_name,
350 (int) tp->tv_usec, pid);
351 if ((fd = open(STR(temp_path), O_RDWR | O_CREAT | O_EXCL, mode)) >= 0)
352 break;
353 if (errno == EEXIST || errno == EISDIR)
354 continue;
355 msg_warn("%s: create file %s: %m", myname, STR(temp_path));
356 sleep(10);
360 * Rename the file to something that is derived from the file ID. I saw
361 * this idea first being used in Zmailer. On any reasonable file system
362 * the file ID is guaranteed to be unique. Better let the OS resolve
363 * collisions than doing a worse job in an application. Another
364 * attractive property of file IDs is that they can appear in messages
365 * without leaking a significant amount of system information (unlike
366 * process ids). Not so nice is that files need to be renamed when they
367 * are moved to another file system.
369 * If someone is racing against us, try to win.
371 file_id = get_file_id(fd);
374 * XXX Some systems seem to have clocks that correlate with process
375 * scheduling or something. Unfortunately, we cannot add random
376 * quantities to the time, because the non-inode part of a queue ID must
377 * not repeat within the same second. The queue ID is the sole thing that
378 * prevents multiple messages from getting the same Message-ID value.
380 for (count = 0;; count++) {
381 GETTIMEOFDAY(tp);
382 vstring_sprintf(id_buf, "%05X%s", (int) tp->tv_usec, file_id);
383 mail_queue_path(path_buf, queue_name, STR(id_buf));
384 if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
385 break;
386 if (errno == EPERM || errno == EISDIR) /* collision. weird. */
387 continue;
388 if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) {
389 msg_warn("%s: rename %s to %s: %m", myname,
390 STR(temp_path), STR(path_buf));
392 if (count > 1000) /* XXX whatever */
393 msg_fatal("%s: rename %s to %s: giving up", myname,
394 STR(temp_path), STR(path_buf));
397 stream = vstream_fdopen(fd, O_RDWR);
398 vstream_control(stream, VSTREAM_CTL_PATH, STR(path_buf), VSTREAM_CTL_END);
399 return (stream);
402 /* mail_queue_open - open mail queue file */
404 VSTREAM *mail_queue_open(const char *queue_name, const char *queue_id,
405 int flags, mode_t mode)
407 const char *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
408 VSTREAM *fp;
411 * Try the operation. If file creation fails, see if it is because of a
412 * missing subdirectory.
414 if ((fp = vstream_fopen(path, flags, mode)) == 0)
415 if (errno == ENOENT)
416 if ((flags & O_CREAT) == O_CREAT && mail_queue_mkdirs(path) == 0)
417 fp = vstream_fopen(path, flags, mode);
418 return (fp);