7 /* mail queue file access
9 /* #include <mail_queue.h>
11 /* VSTREAM *mail_queue_enter(queue_name, mode, tp)
12 /* const char *queue_name;
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;
22 /* char *mail_queue_dir(buf, queue_name, queue_id)
24 /* const char *queue_name;
25 /* const char *queue_id;
27 /* char *mail_queue_path(buf, queue_name, queue_id)
29 /* const char *queue_name;
30 /* const char *queue_id;
32 /* int mail_queue_mkdirs(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;
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
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
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.
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.
98 /* The Secure Mailer license must be distributed with this software.
101 /* IBM T.J. Watson Research
103 /* Yorktown Heights, NY 10598, USA
106 /* System library. */
108 #include <sys_defs.h>
109 #include <stdio.h> /* rename() */
115 #include <sys/time.h> /* gettimeofday, not in POSIX */
119 #ifdef STRCASECMP_IN_STRINGS_H
123 /* Utility library. */
128 #include <mymalloc.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. */
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;
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
);
167 if (private_buf
== 0)
168 private_buf
= vstring_alloc(100);
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) {
188 dir_forest(hash_buf
, queue_id
, var_hash_queue_depth
));
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;
206 if (private_buf
== 0)
207 private_buf
= vstring_alloc(100);
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
);
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
);
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);
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);
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
));
259 vstring_free(old_buf
);
260 vstring_free(new_buf
);
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
)
278 if (*queue_name
== 0 || strlen(queue_name
) > 100)
281 for (cp
= queue_name
; *cp
; cp
++)
287 /* mail_queue_id_ok - validate mail queue id */
289 int mail_queue_id_ok(const char *queue_id
)
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
)
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
!= '_')
310 /* mail_queue_enter - make mail queue entry with locally-unique name */
312 VSTREAM
*mail_queue_enter(const char *queue_name
, mode_t mode
,
315 const char *myname
= "mail_queue_enter";
316 static VSTRING
*id_buf
;
318 static VSTRING
*path_buf
;
319 static VSTRING
*temp_path
;
331 id_buf
= vstring_alloc(10);
332 path_buf
= vstring_alloc(10);
333 temp_path
= vstring_alloc(100);
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.
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)
353 if (errno
== EEXIST
|| errno
== EISDIR
)
355 msg_warn("%s: create file %s: %m", myname
, STR(temp_path
));
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
++) {
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 */
386 if (errno
== EPERM
|| errno
== EISDIR
) /* collision. weird. */
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
);
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
);
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)
416 if ((flags
& O_CREAT
) == O_CREAT
&& mail_queue_mkdirs(path
) == 0)
417 fp
= vstream_fopen(path
, flags
, mode
);