1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
|
/*++
/* NAME
/* mail_queue 3
/* SUMMARY
/* mail queue file access
/* SYNOPSIS
/* #include <mail_queue.h>
/*
/* VSTREAM *mail_queue_enter(queue_name, mode)
/* const char *queue_name;
/* int mode;
/*
/* VSTREAM *mail_queue_open(queue_name, queue_id, flags, mode)
/* const char *queue_name;
/* const char *queue_id;
/* int flags;
/* int mode;
/*
/* char *mail_queue_dir(buf, queue_name, queue_id)
/* VSTRING *buf;
/* const char *queue_name;
/* const char *queue_id;
/*
/* char *mail_queue_path(buf, queue_name, queue_id)
/* VSTRING *buf;
/* const char *queue_name;
/* const char *queue_id;
/*
/* int mail_queue_mkdirs(path)
/* const char *path;
/*
/* int mail_queue_rename(queue_id, old_queue, new_queue)
/* const char *queue_id;
/* const char *old_queue;
/* const char *new_queue;
/*
/* int mail_queue_remove(queue_name, queue_id)
/* const char *queue_name;
/* const char *queue_id;
/*
/* int mail_queue_name_ok(queue_name)
/* const char *queue_name;
/*
/* int mail_queue_id_ok(queue_id)
/* const char *queue_id;
/* DESCRIPTION
/* This module encapsulates access to the mail queue hierarchy.
/* Unlike most other modules, this one does not abort the program
/* in case of file access problems. But it does abort when the
/* application attempts to use a malformed queue name or queue id.
/*
/* mail_queue_enter() creates an entry in the named queue. The queue
/* id is the file base name, see VSTREAM_PATH(). Queue ids are
/* relatively short strings and are recycled in the course of time.
/* The only guarantee given is that on a given machine, no two queue
/* entries will have the same queue ID at the same time.
/*
/* mail_queue_open() opens the named queue file. The \fIflags\fR
/* and \fImode\fR arguments are as with open(2). The result is a
/* null pointer in case of problems.
/*
/* mail_queue_dir() returns the directory name of the specified queue
/* file. When a null result buffer pointer is provided, the result is
/* written to a private buffer that may be overwritten upon the next
/* call.
/*
/* mail_queue_path() returns the pathname of the specified queue
/* file. When a null result buffer pointer is provided, the result
/* is written to a private buffer that may be overwritten upon the
/* next call.
/*
/* mail_queue_mkdirs() creates missing parent directories
/* for the file named in \fBpath\fR. A non-zero result means
/* that the operation failed.
/*
/* mail_queue_rename() renames a queue file. A non-zero result
/* means the operation failed.
/*
/* mail_queue_remove() renames the named queue file. A non-zero result
/* means the operation failed.
/*
/* mail_queue_name_ok() validates a mail queue name and returns
/* non-zero (true) if the name contains no nasty characters.
/*
/* mail_queue_id_ok() does the same thing for mail queue ID names.
/* DIAGNOSTICS
/* Panic: invalid queue name or id given to mail_queue_path(),
/* mail_queue_rename(), or mail_queue_remove().
/* Fatal error: out of memory.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdio.h> /* rename() */
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h> /* gettimeofday, not in POSIX */
#include <string.h>
#include <errno.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
/* Utility library. */
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
#include <mymalloc.h>
#include <argv.h>
#include <dir_forest.h>
#include <make_dirs.h>
#include <split_at.h>
/* Global library. */
#include "file_id.h"
#include "mail_params.h"
#include "mail_queue.h"
#define STR vstring_str
/* mail_queue_dir - construct mail queue directory name */
const char *mail_queue_dir(VSTRING *buf, const char *queue_name,
const char *queue_id)
{
char *myname = "mail_queue_dir";
static VSTRING *private_buf = 0;
static VSTRING *hash_buf = 0;
static ARGV *hash_queue_names = 0;
char **cpp;
/*
* Sanity checks.
*/
if (mail_queue_name_ok(queue_name) == 0)
msg_panic("%s: bad queue name: %s", myname, queue_name);
if (mail_queue_id_ok(queue_id) == 0)
msg_panic("%s: bad queue id: %s", myname, queue_id);
/*
* Initialize.
*/
if (buf == 0) {
if (private_buf == 0)
private_buf = vstring_alloc(100);
buf = private_buf;
}
if (hash_buf == 0) {
hash_buf = vstring_alloc(100);
hash_queue_names = argv_split(var_hash_queue_names, " \t\r\n,");
}
/*
* First, put the basic queue directory name into place.
*/
vstring_strcpy(buf, queue_name);
vstring_strcat(buf, "/");
/*
* Then, see if we need to append a little directory forest.
*/
for (cpp = hash_queue_names->argv; *cpp; cpp++) {
if (strcasecmp(*cpp, queue_name) == 0) {
vstring_strcat(buf,
dir_forest(hash_buf, queue_id, var_hash_queue_depth));
break;
}
}
return (STR(buf));
}
/* mail_queue_path - map mail queue id to path name */
const char *mail_queue_path(VSTRING *buf, const char *queue_name,
const char *queue_id)
{
static VSTRING *private_buf = 0;
/*
* Initialize.
*/
if (buf == 0) {
if (private_buf == 0)
private_buf = vstring_alloc(100);
buf = private_buf;
}
/*
* Append the queue id to the possibly hashed queue directory.
*/
(void) mail_queue_dir(buf, queue_name, queue_id);
vstring_strcat(buf, queue_id);
return (STR(buf));
}
/* mail_queue_mkdirs - fill in missing directories */
int mail_queue_mkdirs(const char *path)
{
char *myname = "mail_queue_mkdirs";
char *saved_path = mystrdup(path);
int ret;
/*
* Truncate a copy of the pathname (for safety sake), and create the
* missing directories.
*/
if (split_at_right(saved_path, '/') == 0)
msg_panic("%s: no slash in: %s", myname, saved_path);
ret = make_dirs(saved_path, 0700);
myfree(saved_path);
return (ret);
}
/* mail_queue_rename - move message to another queue */
int mail_queue_rename(const char *queue_id, const char *old_queue,
const char *new_queue)
{
VSTRING *old_buf = vstring_alloc(100);
VSTRING *new_buf = vstring_alloc(100);
int error;
/*
* Try the operation. If it fails, see if it is because of missing
* intermediate directories.
*/
error = rename(mail_queue_path(old_buf, old_queue, queue_id),
mail_queue_path(new_buf, new_queue, queue_id));
if (error != 0 && mail_queue_mkdirs(STR(new_buf)) == 0)
error = rename(STR(old_buf), STR(new_buf));
/*
* Cleanup.
*/
vstring_free(old_buf);
vstring_free(new_buf);
return (error);
}
/* mail_queue_remove - remove mail queue file */
int mail_queue_remove(const char *queue_name, const char *queue_id)
{
return (REMOVE(mail_queue_path((VSTRING *) 0, queue_name, queue_id)));
}
/* mail_queue_name_ok - validate mail queue name */
int mail_queue_name_ok(const char *queue_name)
{
const char *cp;
for (cp = queue_name; *cp; cp++)
if (!ISALNUM(*cp))
return (0);
return (1);
}
/* mail_queue_id_ok - validate mail queue id */
int mail_queue_id_ok(const char *queue_id)
{
const char *cp;
for (cp = queue_id; *cp; cp++)
if (!ISALNUM(*cp))
return (0);
return (1);
}
/* mail_queue_enter - make mail queue entry with locally-unique name */
VSTREAM *mail_queue_enter(const char *queue_name, int mode)
{
char *myname = "mail_queue_enter";
static VSTRING *id_buf;
static int pid;
static VSTRING *path_buf;
static VSTRING *temp_path;
struct timeval tv;
int fd;
const char *file_id;
VSTREAM *stream;
int count;
/*
* Initialize.
*/
if (id_buf == 0) {
pid = getpid();
id_buf = vstring_alloc(10);
path_buf = vstring_alloc(10);
temp_path = vstring_alloc(100);
}
GETTIMEOFDAY(&tv);
/*
* Create a file with a temporary name that does not collide. The process
* ID alone is not sufficiently unique: maildrops can be shared via the
* network. Not that I recommend using a network-based queue, or having
* multiple hosts write to the same queue, but we should try to avoid
* losing mail if we can.
*
* If someone is racing against us, try to win.
*/
for (;;) {
vstring_sprintf(temp_path, "%s/%d.%d", queue_name,
(int) tv.tv_usec, pid);
if ((fd = open(STR(temp_path), O_RDWR | O_CREAT | O_EXCL, mode)) >= 0)
break;
if (errno == EEXIST || errno == EISDIR) {
if ((int) ++tv.tv_usec < 0)
tv.tv_usec = 0;
continue;
}
msg_warn("%s: create file %s: %m", myname, STR(temp_path));
sleep(10);
}
/*
* Rename the file to something that is derived from the file ID. I saw
* this idea first being used in Zmailer. On any reasonable file system
* the file ID is guaranteed to be unique. Better let the OS resolve
* collisions than doing a worse job in an application. Another
* attractive property of file IDs is that they can appear in messages
* without leaking a significant amount of system information (unlike
* process ids). Not so nice is that files need to be renamed when they
* are moved to another file system.
*
* If someone is racing against us, try to win.
*/
file_id = get_file_id(fd);
GETTIMEOFDAY(&tv);
for (count = 0;; count++) {
vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id);
mail_queue_path(path_buf, queue_name, STR(id_buf));
if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
break;
if (errno == EPERM || errno == EISDIR) {/* collision. weird. */
if ((int) ++tv.tv_usec < 0)
tv.tv_usec = 0;
continue;
}
if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) {
msg_warn("%s: rename %s to %s: %m", myname,
STR(temp_path), STR(path_buf));
}
if (count > 1000) /* XXX whatever */
msg_fatal("%s: rename %s to %s: giving up", myname,
STR(temp_path), STR(path_buf));
}
stream = vstream_fdopen(fd, O_RDWR);
vstream_control(stream, VSTREAM_CTL_PATH, STR(path_buf), VSTREAM_CTL_END);
return (stream);
}
/* mail_queue_open - open mail queue file */
VSTREAM *mail_queue_open(const char *queue_name, const char *queue_id,
int flags, int mode)
{
const char *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
VSTREAM *fp;
/*
* Try the operation. If file creation fails, see if it is because of a
* missing subdirectory.
*/
if ((fp = vstream_fopen(path, flags, mode)) == 0)
if ((flags & O_CREAT) == O_CREAT && mail_queue_mkdirs(path) == 0)
fp = vstream_fopen(path, flags, mode);
return (fp);
}
|