ctm_smail.c revision 16880
16081Sphk/*
28857Srgrimes * Send a compressed CTM delta to a recipient mailing list by encoding it
36081Sphk * in safe ASCII characters, in mailer-friendly chunks, and passing it
46081Sphk * to sendmail.  The encoding is almost the same as MIME BASE64, and is
56081Sphk * protected by a simple checksum.
66081Sphk *
76081Sphk * Author: Stephen McKay
86081Sphk *
98857Srgrimes * NOTICE: This is free software.  I hope you get some use from this program.
108857Srgrimes * In return you should think about all the nice people who give away software.
118857Srgrimes * Maybe you should write some free software too.
1216880Sgpalmer *
1316880Sgpalmer * $Id$
146081Sphk */
156081Sphk
166081Sphk#include <stdio.h>
1716880Sgpalmer#include <stdlib.h>
186081Sphk#include <string.h>
196290Sphk#include <unistd.h>
2016880Sgpalmer#include <fcntl.h>
216081Sphk#include <sys/types.h>
226081Sphk#include <sys/stat.h>
236081Sphk#include <errno.h>
246081Sphk#include <paths.h>
256081Sphk#include "error.h"
266081Sphk#include "options.h"
276081Sphk
286081Sphk#define DEF_MAX_MSG	64000	/* Default maximum mail msg minus headers. */
296081Sphk
306081Sphk#define LINE_LENGTH	76	/* Chars per encode line. Divisible by 4. */
316081Sphk
326081Sphkvoid chop_and_send(char *delta, off_t ctm_size, long max_msg_size,
336081Sphk	char *mail_alias);
3416880Sgpalmervoid chop_and_queue(char *delta, off_t ctm_size, long max_msg_size,
3516880Sgpalmer	char *queue_dir, char *mail_alias);
366081Sphkunsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size);
376081Sphkvoid write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
386081Sphk	int npieces);
396081Sphkvoid write_trailer(FILE *sfp, unsigned sum);
406081Sphkvoid apologise(char *delta, off_t ctm_size, long max_ctm_size,
416081Sphk	char *mail_alias);
426081SphkFILE *open_sendmail(void);
436081Sphkint close_sendmail(FILE *fp);
4416880Sgpalmerint lock_queuedir(char *queue_dir);
4516880Sgpalmervoid free_lock(int lockf, char *queue_dir);
4616880Sgpalmervoid add_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames);
476081Sphk
486290Sphkint
496081Sphkmain(int argc, char **argv)
506081Sphk    {
516081Sphk    char *delta_file;
526081Sphk    char *mail_alias;
536081Sphk    long max_msg_size = DEF_MAX_MSG;
546081Sphk    long max_ctm_size = 0;
556081Sphk    char *log_file = NULL;
5616880Sgpalmer    char *queue_dir = NULL;
576081Sphk    struct stat sb;
586081Sphk
596081Sphk    err_prog_name(argv[0]);
606081Sphk
6116880Sgpalmer    OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
626081Sphk	NUMBER('m', max_msg_size)
636081Sphk	NUMBER('c', max_ctm_size)
646081Sphk	STRING('l', log_file)
6516880Sgpalmer	STRING('q', queue_dir)
666081Sphk    ENDOPTS
676081Sphk
686081Sphk    if (argc != 3)
696081Sphk	usage();
706081Sphk
716081Sphk    if (log_file != NULL)
726081Sphk	err_set_log(log_file);
736081Sphk
746081Sphk    delta_file = argv[1];
756081Sphk    mail_alias = argv[2];
766081Sphk
776081Sphk    if (stat(delta_file, &sb) < 0)
786081Sphk	{
796081Sphk	err("%s: %s", delta_file, strerror(errno));
806081Sphk	exit(1);
816081Sphk	}
828857Srgrimes
836081Sphk    if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
846081Sphk	apologise(delta_file, sb.st_size, max_ctm_size, mail_alias);
8516880Sgpalmer    else if (queue_dir == NULL)
8616880Sgpalmer	chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias);
876081Sphk    else
8816880Sgpalmer	chop_and_queue(delta_file, sb.st_size, max_msg_size, queue_dir, mail_alias);
896081Sphk
906081Sphk    return 0;
916081Sphk    }
926081Sphk
936081Sphk
946081Sphk/*
956081Sphk * Carve our CTM delta into pieces, encode them, and send them.
966081Sphk */
976081Sphkvoid
986081Sphkchop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias)
996081Sphk    {
1006081Sphk    int npieces;
1016081Sphk    long msg_size;
1026081Sphk    long exp_size;
1036081Sphk    int pce;
1046081Sphk    FILE *sfp;
1056081Sphk    FILE *dfp;
1066081Sphk    unsigned sum;
1076081Sphk
10816880Sgpalmer#ifdef howmany
10916880Sgpalmer#undef howmany
11016880Sgpalmer#endif
11116880Sgpalmer
11214707Sbde#define	howmany(x, y)	(((x) + ((y) - 1)) / (y))
1136081Sphk
1146081Sphk    /*
1156081Sphk     * Work out how many pieces we need, bearing in mind that each piece
1166081Sphk     * grows by 4/3 when encoded.  We count the newlines too, but ignore
1176081Sphk     * all mail headers and piece headers.  They are a "small" (almost
1186081Sphk     * constant) per message overhead that we make the user worry about. :-)
1196081Sphk     */
1206081Sphk    exp_size = ctm_size * 4 / 3;
1216081Sphk    exp_size += howmany(exp_size, LINE_LENGTH);
1226081Sphk    npieces = howmany(exp_size, max_msg_size);
1236081Sphk    msg_size = howmany(ctm_size, npieces);
1246081Sphk
1256081Sphk#undef howmany
1266081Sphk
1276081Sphk    if ((dfp = fopen(delta, "r")) == NULL)
1286081Sphk	{
1296081Sphk	err("cannot open '%s' for reading.", delta);
1306081Sphk	exit(1);
1316081Sphk	}
1326081Sphk
1336081Sphk    for (pce = 1; pce <= npieces; pce++)
1346081Sphk	{
1356081Sphk	sfp = open_sendmail();
1366081Sphk	if (sfp == NULL)
1376081Sphk	    exit(1);
1386081Sphk	write_header(sfp, mail_alias, delta, pce, npieces);
1396081Sphk	sum = encode_body(sfp, dfp, msg_size);
1406081Sphk	write_trailer(sfp, sum);
1416081Sphk	if (!close_sendmail(sfp))
1426081Sphk	    exit(1);
1436081Sphk	err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
1446081Sphk	}
1456081Sphk
1466081Sphk    fclose(dfp);
1476081Sphk    }
1486081Sphk
14916880Sgpalmer/*
15016880Sgpalmer * Carve our CTM delta into pieces, encode them, and drop them in the
15116880Sgpalmer * queue dir.
15216880Sgpalmer *
15316880Sgpalmer * Basic algorythm:
15416880Sgpalmer *
15516880Sgpalmer * - for (each piece)
15616880Sgpalmer * -   gen. temp. file name (one which the de-queuer will ignore)
15716880Sgpalmer * -   record in array
15816880Sgpalmer * -   open temp. file
15916880Sgpalmer * -   encode delta (including headers) into the temp file
16016880Sgpalmer * -   close temp. file
16116880Sgpalmer * - end
16216880Sgpalmer * - lock queue directory
16316880Sgpalmer * - foreach (temp. file)
16416880Sgpalmer * -   rename to the proper filename
16516880Sgpalmer * - end
16616880Sgpalmer * - unlock queue directory
16716880Sgpalmer *
16816880Sgpalmer * This is probably overkill, but it means that incomplete deltas
16916880Sgpalmer * don't get mailed, and also reduces the window for lock races
17016880Sgpalmer * between ctm_smail and the de-queueing process.
17116880Sgpalmer */
1726081Sphk
17316880Sgpalmervoid
17416880Sgpalmerchop_and_queue(char *delta, off_t ctm_size, long max_msg_size, char *queue_dir, char *mail_alias)
17516880Sgpalmer{
17616880Sgpalmer    int npieces, pce, len;
17716880Sgpalmer    long msg_size, exp_size;
17816880Sgpalmer    FILE *sfp, *dfp;
17916880Sgpalmer    unsigned sum;
18016880Sgpalmer    char **tempnames, *tempnam, *sn;
18116880Sgpalmer
18216880Sgpalmer#define	howmany(x, y)	(((x) + ((y) - 1)) / (y))
18316880Sgpalmer
18416880Sgpalmer    /*
18516880Sgpalmer     * Work out how many pieces we need, bearing in mind that each piece
18616880Sgpalmer     * grows by 4/3 when encoded.  We count the newlines too, but ignore
18716880Sgpalmer     * all mail headers and piece headers.  They are a "small" (almost
18816880Sgpalmer     * constant) per message overhead that we make the user worry about. :-)
18916880Sgpalmer     */
19016880Sgpalmer    exp_size = ctm_size * 4 / 3;
19116880Sgpalmer    exp_size += howmany(exp_size, LINE_LENGTH);
19216880Sgpalmer    npieces = howmany(exp_size, max_msg_size);
19316880Sgpalmer    msg_size = howmany(ctm_size, npieces);
19416880Sgpalmer
19516880Sgpalmer#undef howmany
19616880Sgpalmer
19716880Sgpalmer    /*
19816880Sgpalmer     * allocate space for the array of filenames. Try to be portable
19916880Sgpalmer     * by not assuming anything to do with sizeof(char *)
20016880Sgpalmer     */
20116880Sgpalmer    tempnames = malloc(npieces * sizeof(char *));
20216880Sgpalmer    if (tempnames == NULL)
20316880Sgpalmer    {
20416880Sgpalmer	err("malloc for tempnames failed");
20516880Sgpalmer	exit(1);
20616880Sgpalmer    }
20716880Sgpalmer
20816880Sgpalmer    len = strlen(queue_dir) + 16;
20916880Sgpalmer    tempnam = malloc(len);
21016880Sgpalmer    if (tempnam == NULL)
21116880Sgpalmer    {
21216880Sgpalmer	err("malloc for tempnames failed");
21316880Sgpalmer	exit(1);
21416880Sgpalmer    }
21516880Sgpalmer
21616880Sgpalmer    if ((dfp = fopen(delta, "r")) == NULL)
21716880Sgpalmer    {
21816880Sgpalmer	err("cannot open '%s' for reading.", delta);
21916880Sgpalmer	exit(1);
22016880Sgpalmer    }
22116880Sgpalmer
22216880Sgpalmer    if ((sn = strrchr(delta, '/')) == NULL)
22316880Sgpalmer	sn = delta;
22416880Sgpalmer    else
22516880Sgpalmer	sn++;
22616880Sgpalmer
22716880Sgpalmer    for (pce = 1; pce <= npieces; pce++)
22816880Sgpalmer    {
22916880Sgpalmer	if (snprintf(tempnam, len, "%s/.%08d-%03d", queue_dir, getpid(), pce) >= len)
23016880Sgpalmer	    err("Whoops! tempnam isn't long enough");
23116880Sgpalmer
23216880Sgpalmer	tempnames[pce - 1] = strdup(tempnam);
23316880Sgpalmer	if (tempnames[pce - 1] == NULL)
23416880Sgpalmer	{
23516880Sgpalmer	    err("strdup failed for temp. filename");
23616880Sgpalmer	    exit(1);
23716880Sgpalmer	}
23816880Sgpalmer
23916880Sgpalmer	sfp = fopen(tempnam, "w");
24016880Sgpalmer	if (sfp == NULL)
24116880Sgpalmer	    exit(1);
24216880Sgpalmer
24316880Sgpalmer	write_header(sfp, mail_alias, delta, pce, npieces);
24416880Sgpalmer	sum = encode_body(sfp, dfp, msg_size);
24516880Sgpalmer	write_trailer(sfp, sum);
24616880Sgpalmer
24716880Sgpalmer	if (fclose(sfp) != 0)
24816880Sgpalmer	    exit(1);
24916880Sgpalmer
25016880Sgpalmer	err("%s %d/%d created succesfully", sn, pce, npieces);
25116880Sgpalmer    }
25216880Sgpalmer
25316880Sgpalmer    add_to_queue(queue_dir, mail_alias, delta, npieces, tempnames);
25416880Sgpalmer
25516880Sgpalmer    fclose(dfp);
25616880Sgpalmer
25716880Sgpalmer}
25816880Sgpalmer
25916880Sgpalmer
2606081Sphk/*
2616081Sphk * MIME BASE64 encode table.
2626081Sphk */
2636081Sphkstatic char to_b64[0x40] =
2646081Sphk    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2656081Sphk
2666081Sphk/*
2676081Sphk * This cheap plastic checksum effectively rotates our checksum-so-far
2686081Sphk * left one, then adds the character.  We only want 16 bits of it, and
2696081Sphk * don't care what happens to the rest.  It ain't much, but it's small.
2706081Sphk */
2716081Sphk#define add_ck(sum,x)	\
2726081Sphk    ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
2736081Sphk
2746081Sphk/*
2756081Sphk * Encode the body.  Use an encoding almost the same as MIME BASE64.
2766081Sphk *
2776081Sphk * Characters are read from delta_fp and encoded characters are written
2786081Sphk * to sm_fp.  At most 'msg_size' characters should be read from delta_fp.
2796081Sphk *
2806081Sphk * The body consists of lines of up to LINE_LENGTH characters.  Each group
2816081Sphk * of 4 characters encodes 3 input characters.  Each output character encodes
2826081Sphk * 6 bits.  Thus 64 different characters are needed in this representation.
2836081Sphk */
2846081Sphkunsigned
2856081Sphkencode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
2866081Sphk    {
2876081Sphk    unsigned short cksum = 0xffff;
2886081Sphk    unsigned char *ip;
2896081Sphk    char *op;
2906081Sphk    int want, n, i;
2916081Sphk    unsigned char inbuf[LINE_LENGTH*3/4];
2926081Sphk    char outbuf[LINE_LENGTH+1];
2936081Sphk
2946081Sphk    /*
2956081Sphk     * Round up to the nearest line boundary, for the tiniest of gains,
2966081Sphk     * and lots of neatness. :-)
2976081Sphk     */
2986081Sphk    msg_size += (LINE_LENGTH*3/4) - 1;
2996081Sphk    msg_size -= msg_size % (LINE_LENGTH*3/4);
3006081Sphk
3016081Sphk    while (msg_size > 0)
3026081Sphk	{
3036081Sphk	want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf);
3046081Sphk	if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0)
3056081Sphk	    break;
3066081Sphk	msg_size -= n;
3076081Sphk
3086081Sphk	for (i = 0; i < n; i++)
3096081Sphk	    add_ck(cksum, inbuf[i]);
3106081Sphk
3116081Sphk	/*
3126081Sphk	 * Produce a line of encoded data.  Every line length will be a
3136081Sphk	 * multiple of 4, except for, perhaps, the last line.
3146081Sphk	 */
3156081Sphk	ip = inbuf;
3166081Sphk	op = outbuf;
3176081Sphk	while (n >= 3)
3186081Sphk	    {
3196081Sphk	    *op++ = to_b64[ip[0] >> 2];
3206081Sphk	    *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
3216081Sphk	    *op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6];
3226081Sphk	    *op++ = to_b64[ip[2] & 0x3f];
3236081Sphk	    ip += 3;
3246081Sphk	    n -= 3;
3256081Sphk	    }
3266081Sphk	if (n > 0)
3276081Sphk	    {
3286081Sphk	    *op++ = to_b64[ip[0] >> 2];
3296081Sphk	    *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
3306081Sphk	    if (n >= 2)
3316081Sphk		*op++ = to_b64[ip[1] << 2 & 0x3f];
3326081Sphk	    }
3336081Sphk	*op++ = '\n';
3346081Sphk	fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
3356081Sphk	}
3366081Sphk
3376081Sphk    if (ferror(delta_fp))
3386081Sphk	{
3396081Sphk	err("error reading input file.");
3406081Sphk	exit(1);
3416081Sphk	}
3426081Sphk
3436081Sphk    if (ferror(sm_fp))
3446081Sphk	{
34516880Sgpalmer	err("error writing encoded file");
3466081Sphk	exit(1);
3476081Sphk	}
3486081Sphk
3496081Sphk    return cksum;
3506081Sphk    }
3516081Sphk
3526081Sphk
3536081Sphk/*
3546081Sphk * Write the mail header and data header.
3556081Sphk */
3566081Sphkvoid
3576081Sphkwrite_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
3586081Sphk    {
3596081Sphk    char *sn;
3606081Sphk
3616081Sphk    if ((sn = strrchr(delta, '/')) == NULL)
3626081Sphk	sn = delta;
3636081Sphk    else
3646081Sphk	sn++;
3656081Sphk
3666457Sphk    fprintf(sfp, "From: owner-%s\n", mail_alias);
3676081Sphk    fprintf(sfp, "To: %s\n", mail_alias);
3686081Sphk    fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces);
3696081Sphk
3706081Sphk    fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces);
3716081Sphk    }
3726081Sphk
3736081Sphk
3746081Sphk/*
3756081Sphk * Write the data trailer.
3766081Sphk */
3776081Sphkvoid
3786081Sphkwrite_trailer(FILE *sfp, unsigned sum)
3796081Sphk    {
3806081Sphk    fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
3816081Sphk    }
3826081Sphk
3836081Sphk
3846081Sphk/*
3856081Sphk * We're terribly sorry, but the delta is too big to send.
3866081Sphk */
3876081Sphkvoid
3886081Sphkapologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
3896081Sphk    {
3906081Sphk    FILE *sfp;
3916081Sphk    char *sn;
3926081Sphk
3936081Sphk    sfp = open_sendmail();
3946081Sphk    if (sfp == NULL)
3956081Sphk	exit(1);
3966081Sphk
3976081Sphk    if ((sn = strrchr(delta, '/')) == NULL)
3986081Sphk	sn = delta;
3996081Sphk    else
4006081Sphk	sn++;
4016081Sphk
4026081Sphk    fprintf(sfp, "From: %s-owner\n", mail_alias);
4036081Sphk    fprintf(sfp, "To: %s\n", mail_alias);
4046081Sphk    fprintf(sfp, "Subject: ctm-notice %s\n\n", sn);
4056081Sphk
4066081Sphk    fprintf(sfp, "%s is %ld bytes.  The limit is %ld bytes.\n\n", sn,
4076081Sphk	(long)ctm_size, max_ctm_size);
4086081Sphk    fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n");
4096081Sphk
4106081Sphk    if (!close_sendmail(sfp))
4116081Sphk	exit(1);
4126081Sphk    }
4136081Sphk
4146081Sphk
4156081Sphk/*
4166081Sphk * Start a pipe to sendmail.  Sendmail will decode the destination
4176081Sphk * from the message contents.
4186081Sphk */
4196081SphkFILE *
4206081Sphkopen_sendmail()
4216081Sphk    {
4226081Sphk    FILE *fp;
4236081Sphk    char buf[100];
4246081Sphk
4256081Sphk    sprintf(buf, "%s -t", _PATH_SENDMAIL);
4266081Sphk    if ((fp = popen(buf, "w")) == NULL)
4276081Sphk	err("cannot start sendmail");
4286081Sphk    return fp;
4296081Sphk    }
4306081Sphk
4316081Sphk
4326081Sphk/*
4336081Sphk * Close a pipe to sendmail.  Sendmail will then do its bit.
4346081Sphk * Return 1 on success, 0 on failure.
4356081Sphk */
4366081Sphkint
4376081Sphkclose_sendmail(FILE *fp)
4386081Sphk    {
4396081Sphk    int status;
4406081Sphk
4416081Sphk    fflush(fp);
4426081Sphk    if (ferror(fp))
4436081Sphk	{
4446081Sphk	err("error writing to sendmail");
4456081Sphk	return 0;
4466081Sphk	}
4476081Sphk
4486081Sphk    if ((status = pclose(fp)) != 0)
4496081Sphk	err("sendmail failed with status %d", status);
4506081Sphk
4516081Sphk    return (status == 0);
4526081Sphk    }
45316880Sgpalmer
45416880Sgpalmer/*
45516880Sgpalmer * Lock the queuedir so we're the only guy messing about in there.
45616880Sgpalmer */
45716880Sgpalmerint
45816880Sgpalmerlock_queuedir(char *queue_dir)
45916880Sgpalmer{
46016880Sgpalmer    int fp, len;
46116880Sgpalmer    char *buffer;
46216880Sgpalmer    struct stat sb;
46316880Sgpalmer
46416880Sgpalmer    len = strlen(queue_dir) + 8;
46516880Sgpalmer
46616880Sgpalmer    buffer = malloc(len);
46716880Sgpalmer    if (buffer == NULL)
46816880Sgpalmer    {
46916880Sgpalmer	err("malloc failed in lock_queuedir");
47016880Sgpalmer	exit(1);
47116880Sgpalmer    }
47216880Sgpalmer
47316880Sgpalmer    if (snprintf(buffer, len, "%s/.lock", queue_dir) >= len)
47416880Sgpalmer	err("Whoops. lock buffer too small in lock_queuedir");
47516880Sgpalmer
47616880Sgpalmer    /*
47716880Sgpalmer     * We do our own lockfile scanning to avoid unlink races. 60
47816880Sgpalmer     * seconds should be enough to ensure that we won't get more races
47916880Sgpalmer     * happening between the stat and the open/flock.
48016880Sgpalmer     */
48116880Sgpalmer
48216880Sgpalmer    while (stat(buffer, &sb) == 0)
48316880Sgpalmer	sleep(60);
48416880Sgpalmer
48516880Sgpalmer    if ((fp = open(buffer, O_WRONLY | O_CREAT | O_EXLOCK, 0600)) < 0)
48616880Sgpalmer    {
48716880Sgpalmer	err("can't open `%s' in lock_queuedir", buffer);
48816880Sgpalmer	exit(1);
48916880Sgpalmer    }
49016880Sgpalmer
49116880Sgpalmer    snprintf(buffer, len, "%8ld", getpid());
49216880Sgpalmer    write(fp, buffer, 8);
49316880Sgpalmer
49416880Sgpalmer    free(buffer);
49516880Sgpalmer
49616880Sgpalmer    return(fp);
49716880Sgpalmer}
49816880Sgpalmer
49916880Sgpalmer/*
50016880Sgpalmer * Lock the queuedir so we're the only guy messing about in there.
50116880Sgpalmer */
50216880Sgpalmervoid
50316880Sgpalmerfree_lock(int lockf, char *queue_dir)
50416880Sgpalmer{
50516880Sgpalmer    int len;
50616880Sgpalmer    char *path;
50716880Sgpalmer
50816880Sgpalmer    /*
50916880Sgpalmer     * Most important: free the lock before we do anything else!
51016880Sgpalmer     */
51116880Sgpalmer
51216880Sgpalmer    close(lockf);
51316880Sgpalmer
51416880Sgpalmer    len = strlen(queue_dir) + 7;
51516880Sgpalmer
51616880Sgpalmer    path = malloc(len);
51716880Sgpalmer    if (path == NULL)
51816880Sgpalmer    {
51916880Sgpalmer	err("malloc failed in free_lock");
52016880Sgpalmer	exit(1);
52116880Sgpalmer    }
52216880Sgpalmer
52316880Sgpalmer    if (snprintf(path, len, "%s/.lock", queue_dir) >= len)
52416880Sgpalmer	err("lock path buffer too small in free_lock");
52516880Sgpalmer
52616880Sgpalmer    if (unlink(path) != 0)
52716880Sgpalmer    {
52816880Sgpalmer	err("can't unlink lockfile `%s'", path);
52916880Sgpalmer	exit(1);
53016880Sgpalmer    }
53116880Sgpalmer
53216880Sgpalmer    free(path);
53316880Sgpalmer}
53416880Sgpalmer
53516880Sgpalmer/* move everything into the queue directory. */
53616880Sgpalmer
53716880Sgpalmervoid
53816880Sgpalmeradd_to_queue(char *queue_dir, char *mail_alias, char *delta, int npieces, char **tempnames)
53916880Sgpalmer{
54016880Sgpalmer    char *queuefile, *sn;
54116880Sgpalmer    int pce, len, lockf;
54216880Sgpalmer
54316880Sgpalmer    if ((sn = strrchr(delta, '/')) == NULL)
54416880Sgpalmer	sn = delta;
54516880Sgpalmer    else
54616880Sgpalmer	sn++;
54716880Sgpalmer
54816880Sgpalmer    /* try to malloc all we need BEFORE entering the lock loop */
54916880Sgpalmer
55016880Sgpalmer    len = strlen(queue_dir) + strlen(sn) + 7;
55116880Sgpalmer    queuefile = malloc(len);
55216880Sgpalmer    if (queuefile == NULL)
55316880Sgpalmer    {
55416880Sgpalmer	err("can't malloc for queuefile");
55516880Sgpalmer	exit(1);
55616880Sgpalmer    }
55716880Sgpalmer
55816880Sgpalmer    /*
55916880Sgpalmer     * We should be the only process mucking around in the queue
56016880Sgpalmer     * directory while we add the new queue files ... it could be
56116880Sgpalmer     * awkward if the de-queue process starts it's job while we're
56216880Sgpalmer     * adding files ...
56316880Sgpalmer     */
56416880Sgpalmer
56516880Sgpalmer    lockf = lock_queuedir(queue_dir);
56616880Sgpalmer    for (pce = 0; pce < npieces; pce++)
56716880Sgpalmer    {
56816880Sgpalmer	struct stat sb;
56916880Sgpalmer
57016880Sgpalmer	if (snprintf(queuefile, len, "%s/%s+%03d", queue_dir, sn, pce + 1) >= len)
57116880Sgpalmer	    err("whoops, queuefile buffer is too small");
57216880Sgpalmer
57316880Sgpalmer	if (stat(queuefile, &sb) == 0)
57416880Sgpalmer	{
57516880Sgpalmer	    err("WOAH! Queue file `%s' already exists! Bailing out.", queuefile);
57616880Sgpalmer	    free_lock(lockf, queue_dir);
57716880Sgpalmer	    exit(1);
57816880Sgpalmer	}
57916880Sgpalmer
58016880Sgpalmer	rename(tempnames[pce], queuefile);
58116880Sgpalmer	err("Queue file %s now exists", queuefile);
58216880Sgpalmer    }
58316880Sgpalmer
58416880Sgpalmer    free_lock(lockf, queue_dir);
58516880Sgpalmer
58616880Sgpalmer    free(queuefile);
58716880Sgpalmer}
588