ctm_rmail.c revision 7147
192654Sjeff/*
2139318Sbmilekic * Accept one (or more) ASCII encoded chunks that together make a compressed
3139318Sbmilekic * CTM delta.  Decode them and reconstruct the deltas.  Any completed
4139318Sbmilekic * deltas may be passed to ctm for unpacking.
5139318Sbmilekic *
692654Sjeff * Author: Stephen McKay
792654Sjeff *
892654Sjeff * NOTICE: This is free software.  I hope you get some use from this program.
992654Sjeff * In return you should think about all the nice people who give away software.
1092654Sjeff * Maybe you should write some free software too.
1192654Sjeff */
1292654Sjeff
1392654Sjeff#include <stdio.h>
1492654Sjeff#include <stdlib.h>
1592654Sjeff#include <string.h>
1692654Sjeff#include <errno.h>
1792654Sjeff#include <unistd.h>
1892654Sjeff#include <sys/types.h>
1992654Sjeff#include <sys/stat.h>
2092654Sjeff#include <fcntl.h>
2192654Sjeff#include <limits.h>
2292654Sjeff#include "error.h"
2392654Sjeff#include "options.h"
2492654Sjeff
2592654Sjeff#define CTM_STATUS	".ctm_status"
2692654Sjeff
2792654Sjeffchar *piece_dir = NULL;		/* Where to store pieces of deltas. */
2892654Sjeffchar *delta_dir = NULL;		/* Where to store completed deltas. */
2992654Sjeffchar *base_dir = NULL;		/* The tree to apply deltas to. */
3092654Sjeffint delete_after = 0;		/* Delete deltas after ctm applies them. */
3192654Sjeff
3292654Sjeffvoid apply_complete(void);
3392654Sjeffint read_piece(char *input_file);
3492654Sjeffint combine_if_complete(char *delta, int pce, int npieces);
3592654Sjeffint combine(char *delta, int npieces, char *dname, char *pname, char *tname);
3692654Sjeffint decode_line(char *line, char *out_buf);
3792654Sjeffint lock_file(char *name);
3892654Sjeff
3992654Sjeff/*
4092654Sjeff * If given a '-p' flag, read encoded delta pieces from stdin or file
4192654Sjeff * arguments, decode them and assemble any completed deltas.  If given
4292654Sjeff * a '-b' flag, pass any completed deltas to 'ctm' for application to
4392654Sjeff * the source tree.  The '-d' flag is mandatory, but either of '-p' or
4492654Sjeff * '-b' can be omitted.  If given the '-l' flag, notes and errors will
4592654Sjeff * be timestamped and written to the given file.
4692654Sjeff *
4792654Sjeff * Exit status is 0 for success or 1 for indigestible input.  That is,
48129906Sbmilekic * 0 means the encode input pieces were decoded and stored, and 1 means
4992654Sjeff * some input was discarded.  If a delta fails to apply, this won't be
5092654Sjeff * reflected in the exit status.  In this case, the delta is left in
5192654Sjeff * 'deltadir'.
5292654Sjeff */
5392654Sjeffint
5492654Sjeffmain(int argc, char **argv)
5592654Sjeff    {
5692654Sjeff    char *log_file = NULL;
5792654Sjeff    int status = 0;
5892654Sjeff    int fork_ctm = 0;
59132987Sgreen
6092654Sjeff    err_prog_name(argv[0]);
6192654Sjeff
62132987Sgreen    OPTIONS("[-Df] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
63132987Sgreen	FLAG('D', delete_after)
6492654Sjeff	FLAG('f', fork_ctm)
6592654Sjeff	STRING('p', piece_dir)
6692654Sjeff	STRING('d', delta_dir)
67105689Ssheldonh	STRING('b', base_dir)
6892654Sjeff	STRING('l', log_file)
69132987Sgreen    ENDOPTS
7092654Sjeff
7192654Sjeff    if (delta_dir == NULL)
7292654Sjeff	usage();
7392654Sjeff
7492654Sjeff    if (piece_dir == NULL && (base_dir == NULL || argc > 1))
7592654Sjeff	usage();
7692654Sjeff
7792654Sjeff    if (log_file != NULL)
7892654Sjeff	err_set_log(log_file);
7992654Sjeff
8092654Sjeff    /*
8192654Sjeff     * Digest each file in turn, or just stdin if no files were given.
8292654Sjeff     */
8392654Sjeff    if (argc <= 1)
8492654Sjeff	{
8592654Sjeff	if (piece_dir != NULL)
8692654Sjeff	    status = read_piece(NULL);
8792654Sjeff	}
8892654Sjeff    else
8992654Sjeff	{
9092654Sjeff	while (*++argv != NULL)
9192654Sjeff	    status |= read_piece(*argv);
9292654Sjeff	}
9392654Sjeff
9492654Sjeff    /*
95132987Sgreen     * Maybe it's time to look for and apply completed deltas with ctm.
9692654Sjeff     *
9792654Sjeff     * Shall we report back to sendmail immediately, and let a child do
98132987Sgreen     * the work?  Sendmail will be waiting for us to complete, delaying
99132987Sgreen     * other mail, and possibly some intermediate process (like MH slocal)
10092654Sjeff     * will terminate us if we take too long!
10192654Sjeff     *
10292654Sjeff     * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
10392654Sjeff     * Also, the child exit status is unimportant.
10492654Sjeff     */
105132987Sgreen    if (base_dir != NULL)
10692654Sjeff	if (!fork_ctm || fork() == 0)
10792654Sjeff	    apply_complete();
10892654Sjeff
10992654Sjeff    return status;
11092654Sjeff    }
11192654Sjeff
11292654Sjeff
11392654Sjeff/*
11492654Sjeff * Construct the file name of a piece of a delta.
11592654Sjeff */
11692654Sjeff#define mk_piece_name(fn,d,p,n)	\
11792654Sjeff    sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
11892654Sjeff
11992654Sjeff/*
12092654Sjeff * Construct the file name of an assembled delta.
12192654Sjeff */
12292654Sjeff#define mk_delta_name(fn,d)	\
12392654Sjeff    sprintf((fn), "%s/%s", delta_dir, (d))
12492654Sjeff
12592654Sjeff/*
12692654Sjeff * If the next required delta is now present, let ctm lunch on it and any
12792654Sjeff * contiguous deltas.
12892654Sjeff */
12992654Sjeffvoid
13092654Sjeffapply_complete()
13192654Sjeff    {
13292654Sjeff    int i, dn;
13392654Sjeff    int lfd;
13492654Sjeff    FILE *fp, *ctm;
135105689Ssheldonh    struct stat sb;
13692654Sjeff    char class[20];
13792654Sjeff    char delta[30];
13892654Sjeff    char junk[2];
13992654Sjeff    char fname[PATH_MAX];
14092654Sjeff    char here[PATH_MAX];
14192654Sjeff    char buf[PATH_MAX*2];
14292654Sjeff
14392654Sjeff    /*
14492654Sjeff     * Grab a lock on the ctm mutex file so that we can be sure we are
14592654Sjeff     * working alone, not fighting another ctm_rmail!
14692654Sjeff     */
14792654Sjeff    strcpy(fname, delta_dir);
14892654Sjeff    strcat(fname, "/.mutex_apply");
14992654Sjeff    if ((lfd = lock_file(fname)) < 0)
15092654Sjeff	return;
15192654Sjeff
15292654Sjeff    /*
15392654Sjeff     * Find out which delta ctm needs next.
15492654Sjeff     */
15592654Sjeff    sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
15692654Sjeff    if ((fp = fopen(fname, "r")) == NULL)
15792654Sjeff	{
15892654Sjeff	close(lfd);
15992654Sjeff	return;
16092654Sjeff	}
16192654Sjeff
16292654Sjeff    i = fscanf(fp, "%s %d %c", class, &dn, junk);
16392654Sjeff    fclose(fp);
16492654Sjeff    if (i != 2)
16592654Sjeff	{
16695925Sarr	close(lfd);
16792654Sjeff	return;
16892654Sjeff	}
16992654Sjeff
170120223Sjeff    /*
171129906Sbmilekic     * We might need to convert the delta filename to an absolute pathname.
172129906Sbmilekic     */
173129906Sbmilekic    here[0] = '\0';
174129906Sbmilekic    if (delta_dir[0] != '/')
175129906Sbmilekic	{
176129906Sbmilekic	getcwd(here, sizeof(here)-1);
177129906Sbmilekic	i = strlen(here) - 1;
178129906Sbmilekic	if (i >= 0 && here[i] != '/')
179129906Sbmilekic	    {
180129906Sbmilekic	    here[++i] = '/';
181129906Sbmilekic	    here[++i] = '\0';
182129906Sbmilekic	    }
183129906Sbmilekic	}
184129906Sbmilekic
185129906Sbmilekic    /*
186129906Sbmilekic     * Keep applying deltas until we run out or something bad happens.
187129906Sbmilekic     */
188129906Sbmilekic    for (;;)
189129906Sbmilekic	{
190129906Sbmilekic	sprintf(delta, "%s.%04d.gz", class, ++dn);
191129906Sbmilekic	mk_delta_name(fname, delta);
192129906Sbmilekic
193129906Sbmilekic	if (stat(fname, &sb) < 0)
194129913Sbmilekic	    break;
195129913Sbmilekic
196129913Sbmilekic	sprintf(buf, "(cd %s && ctm %s%s) 2>&1", base_dir, here, fname);
197129906Sbmilekic	if ((ctm = popen(buf, "r")) == NULL)
198129906Sbmilekic	    {
199129906Sbmilekic	    err("ctm failed to apply %s", delta);
200129906Sbmilekic	    break;
201129906Sbmilekic	    }
202129906Sbmilekic
203129906Sbmilekic	while (fgets(buf, sizeof(buf), ctm) != NULL)
204129906Sbmilekic	    {
205129906Sbmilekic	    i = strlen(buf) - 1;
206120223Sjeff	    if (i >= 0 && buf[i] == '\n')
207120223Sjeff		buf[i] = '\0';
208120223Sjeff	    err("ctm: %s", buf);
209120223Sjeff	    }
210120223Sjeff
21192654Sjeff	if (pclose(ctm) != 0)
21292654Sjeff	    {
21392654Sjeff	    err("ctm failed to apply %s", delta);
21492654Sjeff	    break;
21592654Sjeff	    }
21692654Sjeff
21792654Sjeff	if (delete_after)
21892654Sjeff	    unlink(fname);
21995758Sjeff
220103531Sjeff	err("%s applied%s", delta, delete_after ? " and deleted" : "");
221103531Sjeff	}
222103531Sjeff
223103531Sjeff    /*
224103531Sjeff     * Closing the lock file clears the lock.
225103531Sjeff     */
226103531Sjeff    close(lfd);
227103531Sjeff    }
228129906Sbmilekic
229129906Sbmilekic
230129906Sbmilekic/*
23192654Sjeff * This cheap plastic checksum effectively rotates our checksum-so-far
23292654Sjeff * left one, then adds the character.  We only want 16 bits of it, and
23392654Sjeff * don't care what happens to the rest.  It ain't much, but it's small.
23492654Sjeff */
23592654Sjeff#define add_ck(sum,x)	\
23692654Sjeff    ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
23792654Sjeff
23892654Sjeff
23992654Sjeff/*
24092654Sjeff * Decode the data between BEGIN and END, and stash it in the staging area.
24194161Sjeff * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
24292654Sjeff * If we have all pieces of a delta, combine them.  Returns 0 on success,
24392654Sjeff * and 1 for any sort of failure.
24492654Sjeff */
24592654Sjeffint
24692654Sjeffread_piece(char *input_file)
24794161Sjeff    {
24892654Sjeff    int status = 0;
24992654Sjeff    FILE *ifp, *ofp = 0;
25092654Sjeff    int decoding = 0;
25192654Sjeff    int got_one = 0;
25292654Sjeff    int line_no = 0;
25392654Sjeff    int i, n;
25492654Sjeff    int pce, npieces;
25595766Sjeff    unsigned claimed_cksum;
25692654Sjeff    unsigned short cksum = 0;
25792654Sjeff    char out_buf[200];
25892654Sjeff    char line[200];
259111119Simp    char delta[30];
26092654Sjeff    char pname[PATH_MAX];
26192654Sjeff    char tname[PATH_MAX];
26292654Sjeff    char junk[2];
26395766Sjeff
26492654Sjeff    ifp = stdin;
26592654Sjeff    if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
26692654Sjeff	{
26792654Sjeff	err("cannot open '%s' for reading", input_file);
26892654Sjeff	return 1;
26992654Sjeff	}
27092654Sjeff
27195766Sjeff    while (fgets(line, sizeof(line), ifp) != NULL)
27292654Sjeff	{
27392654Sjeff	line_no++;
27495766Sjeff
27592654Sjeff	/*
27695766Sjeff	 * Look for the beginning of an encoded piece.
27792654Sjeff	 */
27892654Sjeff	if (!decoding)
27992654Sjeff	    {
28092654Sjeff	    char *s;
28192654Sjeff
28292654Sjeff	    if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
28392654Sjeff		    delta, &pce, &npieces, junk) != 3)
28492654Sjeff		continue;
28592654Sjeff
28692654Sjeff	    while ((s = strchr(delta, '/')) != NULL)
28792654Sjeff		*s = '_';
28892654Sjeff
28992654Sjeff	    got_one++;
29092654Sjeff	    strcpy(tname, piece_dir);
29192654Sjeff	    strcat(tname, "/p.XXXXXX");
29292654Sjeff	    if (mktemp(tname) == NULL)
29392654Sjeff		{
29492654Sjeff		err("*mktemp: '%s'", tname);
29592654Sjeff		status++;
29692654Sjeff		continue;
29792654Sjeff		}
29892654Sjeff	    if ((ofp = fopen(tname, "w")) == NULL)
29992654Sjeff		{
30092654Sjeff		err("cannot open '%s' for writing", tname);
30192654Sjeff		status++;
30292654Sjeff		continue;
30392654Sjeff		}
304100326Smarkm
30592654Sjeff	    cksum = 0xffff;
30692654Sjeff	    decoding++;
30792654Sjeff	    continue;
30892654Sjeff	    }
30992654Sjeff
31092654Sjeff	/*
31192654Sjeff	 * We are decoding.  Stop if we see the end flag.
31292654Sjeff	 */
31392654Sjeff	if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
31492654Sjeff	    {
31592654Sjeff	    int e;
31692654Sjeff
31792654Sjeff	    decoding = 0;
31892654Sjeff
31992654Sjeff	    fflush(ofp);
32092654Sjeff	    e = ferror(ofp);
32192654Sjeff	    fclose(ofp);
32292654Sjeff
32392654Sjeff	    if (e)
32492654Sjeff		err("error writing %s", tname);
32592654Sjeff
32692654Sjeff	    if (cksum != claimed_cksum)
32792654Sjeff		err("checksum: read %d, calculated %d", claimed_cksum, cksum);
32892654Sjeff
32992654Sjeff	    if (e || cksum != claimed_cksum)
33092654Sjeff		{
33192654Sjeff		err("%s %d/%d discarded", delta, pce, npieces);
33292654Sjeff		unlink(tname);
33392654Sjeff		status++;
33492654Sjeff		continue;
33592654Sjeff		}
33692654Sjeff
33792654Sjeff	    mk_piece_name(pname, delta, pce, npieces);
33892654Sjeff	    if (rename(tname, pname) < 0)
33992654Sjeff		{
34092654Sjeff		err("*rename: '%s' to '%s'", tname, pname);
34192654Sjeff		err("%s %d/%d lost!", delta, pce, npieces);
34292654Sjeff		unlink(tname);
34392654Sjeff		status++;
34492654Sjeff		continue;
34592654Sjeff		}
34692654Sjeff
34792654Sjeff	    err("%s %d/%d stored", delta, pce, npieces);
34892654Sjeff
34992654Sjeff	    if (!combine_if_complete(delta, pce, npieces))
35092654Sjeff		status++;
35192654Sjeff	    continue;
35292654Sjeff	    }
35392654Sjeff
35492654Sjeff	/*
35592654Sjeff	 * Must be a line of encoded data.  Decode it, sum it, and save it.
35692654Sjeff	 */
35792654Sjeff	n = decode_line(line, out_buf);
35892654Sjeff	if (n <= 0)
35992654Sjeff	    {
36092654Sjeff	    err("line %d: illegal character: '%c'", line_no, line[-n]);
36192654Sjeff	    err("%s %d/%d discarded", delta, pce, npieces);
36292654Sjeff
36392654Sjeff	    fclose(ofp);
36492654Sjeff	    unlink(tname);
365103531Sjeff
36692654Sjeff	    status++;
36792654Sjeff	    decoding = 0;
36892654Sjeff	    continue;
36992654Sjeff	    }
37092654Sjeff
371103531Sjeff	for (i = 0; i < n; i++)
37292654Sjeff	    add_ck(cksum, out_buf[i]);
37392654Sjeff
374103531Sjeff	fwrite(out_buf, sizeof(char), n, ofp);
37592654Sjeff	}
37692654Sjeff
37792654Sjeff    if (decoding)
37892654Sjeff	{
37992654Sjeff	err("truncated file");
38092654Sjeff	err("%s %d/%d discarded", delta, pce, npieces);
38192654Sjeff
38292654Sjeff	fclose(ofp);
38392654Sjeff	unlink(tname);
38492654Sjeff
38592654Sjeff	status++;
38692654Sjeff	}
38792654Sjeff
38892654Sjeff    if (ferror(ifp))
38992654Sjeff	{
39092654Sjeff	err("error reading %s", input_file == NULL ? "stdin" : input_file);
39192654Sjeff	status++;
39292654Sjeff	}
39392654Sjeff
39492654Sjeff    if (input_file != NULL)
39592654Sjeff	fclose(ifp);
39692654Sjeff
39792654Sjeff    if (!got_one)
39892654Sjeff	{
39992654Sjeff	err("message contains no delta");
40092654Sjeff	status++;
40192654Sjeff	}
40292654Sjeff
40392654Sjeff    return (status != 0);
40492654Sjeff    }
40592654Sjeff
40692654Sjeff
40792654Sjeff/*
40892654Sjeff * Put the pieces together to form a delta, if they are all present.
40992758Sjeff * Returns 1 on success (even if we didn't do anything), and 0 on failure.
41092758Sjeff */
41192758Sjeffint
41292758Sjeffcombine_if_complete(char *delta, int pce, int npieces)
41392758Sjeff    {
41492758Sjeff    int i, e;
41592758Sjeff    int lfd;
41692758Sjeff    struct stat sb;
41792758Sjeff    char pname[PATH_MAX];
41892758Sjeff    char dname[PATH_MAX];
41992654Sjeff    char tname[PATH_MAX];
42092654Sjeff
421129906Sbmilekic    /*
422129906Sbmilekic     * We can probably just rename() it into place if it is a small delta.
423129906Sbmilekic     */
424129906Sbmilekic    if (npieces == 1)
425129906Sbmilekic	{
426129906Sbmilekic	mk_delta_name(dname, delta);
427129906Sbmilekic	mk_piece_name(pname, delta, 1, 1);
428129906Sbmilekic	if (rename(pname, dname) == 0)
429129906Sbmilekic	    {
430129906Sbmilekic	    err("%s complete", delta);
431129906Sbmilekic	    return 1;
432129906Sbmilekic	    }
433129906Sbmilekic	}
434129906Sbmilekic
435129906Sbmilekic    /*
436129906Sbmilekic     * Grab a lock on the reassembly mutex file so that we can be sure we are
437129906Sbmilekic     * working alone, not fighting another ctm_rmail!
438129906Sbmilekic     */
439129906Sbmilekic    strcpy(tname, delta_dir);
440129906Sbmilekic    strcat(tname, "/.mutex_build");
441129906Sbmilekic    if ((lfd = lock_file(tname)) < 0)
442129906Sbmilekic	return 0;
44392654Sjeff
44492654Sjeff    /*
44592654Sjeff     * Are all of the pieces present?  Of course the current one is,
44692654Sjeff     * unless all pieces are missing because another ctm_rmail has
44792654Sjeff     * processed them already.
44892654Sjeff     */
44992654Sjeff    for (i = 1; i <= npieces; i++)
45092654Sjeff	{
45192654Sjeff	if (i == pce)
45292654Sjeff	    continue;
45392654Sjeff	mk_piece_name(pname, delta, i, npieces);
45492654Sjeff	if (stat(pname, &sb) < 0)
45592654Sjeff	    {
45692654Sjeff	    close(lfd);
45792654Sjeff	    return 1;
45892654Sjeff	    }
45992654Sjeff	}
46092654Sjeff
46192654Sjeff    /*
46292654Sjeff     * Stick them together.  Let combine() use our file name buffers, since
46392654Sjeff     * we're such good buddies. :-)
46492654Sjeff     */
46592654Sjeff    e = combine(delta, npieces, dname, pname, tname);
46692654Sjeff    close(lfd);
46792654Sjeff    return e;
46892654Sjeff    }
46992654Sjeff
47092654Sjeff
47192654Sjeff/*
47292654Sjeff * Put the pieces together to form a delta.
47392654Sjeff * Returns 1 on success, and 0 on failure.
47492654Sjeff * Note: dname, pname, and tname are room for some file names that just
47592654Sjeff * happened to by lying around in the calling routine.  Waste not, want not!
47692654Sjeff */
47792654Sjeffint
47892654Sjeffcombine(char *delta, int npieces, char *dname, char *pname, char *tname)
47994157Sjeff    {
48092654Sjeff    FILE *dfp, *pfp;
48192654Sjeff    int i, n, e;
48292654Sjeff    char buf[BUFSIZ];
48392654Sjeff
48492654Sjeff    strcpy(tname, delta_dir);
48592654Sjeff    strcat(tname, "/d.XXXXXX");
48692654Sjeff    if (mktemp(tname) == NULL)
48792654Sjeff	{
48892654Sjeff	err("*mktemp: '%s'", tname);
48992654Sjeff	return 0;
49092654Sjeff	}
49192654Sjeff    if ((dfp = fopen(tname, "w")) == NULL)
49292654Sjeff	{
49392654Sjeff	err("cannot open '%s' for writing", tname);
49492654Sjeff	return 0;
49592654Sjeff	}
49692654Sjeff
497129906Sbmilekic    /*
498129906Sbmilekic     * Reconstruct the delta by reading each piece in order.
499129906Sbmilekic     */
500129906Sbmilekic    for (i = 1; i <= npieces; i++)
501129906Sbmilekic	{
502129906Sbmilekic	mk_piece_name(pname, delta, i, npieces);
503129906Sbmilekic	if ((pfp = fopen(pname, "r")) == NULL)
504129906Sbmilekic	    {
505129906Sbmilekic	    err("cannot open '%s' for reading", pname);
506129906Sbmilekic	    fclose(dfp);
507129906Sbmilekic	    unlink(tname);
508129906Sbmilekic	    return 0;
509129906Sbmilekic	    }
510129906Sbmilekic	while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
51192654Sjeff	    fwrite(buf, sizeof(char), n, dfp);
51292654Sjeff	e = ferror(pfp);
513	fclose(pfp);
514	if (e)
515	    {
516	    err("error reading '%s'", pname);
517	    fclose(dfp);
518	    unlink(tname);
519	    return 0;
520	    }
521	}
522    fflush(dfp);
523    e = ferror(dfp);
524    fclose(dfp);
525    if (e)
526	{
527	err("error writing '%s'", tname);
528	unlink(tname);
529	return 0;
530	}
531
532    mk_delta_name(dname, delta);
533    if (rename(tname, dname) < 0)
534	{
535	err("*rename: '%s' to '%s'", tname, dname);
536	unlink(tname);
537	return 0;
538	}
539
540    /*
541     * Throw the pieces away.
542     */
543    for (i = 1; i <= npieces; i++)
544	{
545	mk_piece_name(pname, delta, i, npieces);
546	if (unlink(pname) < 0)
547	    err("*unlink: '%s'", pname);
548	}
549
550    err("%s complete", delta);
551    return 1;
552    }
553
554
555/*
556 * MIME BASE64 decode table.
557 */
558static unsigned char from_b64[0x80] =
559    {
560    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
561    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
562    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
563    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
564    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
565    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
566    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
567    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
568    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
569    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
570    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
571    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
572    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
573    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
574    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
575    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
576    };
577
578
579/*
580 * Decode a line of ASCII into binary.  Returns the number of bytes in
581 * the output buffer, or < 0 on indigestable input.  Error output is
582 * the negative of the index of the inedible character.
583 */
584int
585decode_line(char *line, char *out_buf)
586    {
587    unsigned char *ip = (unsigned char *)line;
588    unsigned char *op = (unsigned char *)out_buf;
589    unsigned long bits;
590    unsigned x;
591
592    for (;;)
593	{
594	if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
595	    break;
596	bits = x << 18;
597	ip++;
598	if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
599	    {
600	    bits |= x << 12;
601	    *op++ = bits >> 16;
602	    ip++;
603	    if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
604		{
605		bits |= x << 6;
606		*op++ = bits >> 8;
607		ip++;
608		if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
609		    {
610		    bits |= x;
611		    *op++ = bits;
612		    ip++;
613		    }
614		}
615	    }
616	}
617
618    if (*ip == '\0' || *ip == '\n')
619	return op - (unsigned char *)out_buf;
620    else
621	return -(ip - (unsigned char *)line);
622    }
623
624
625/*
626 * Create and lock the given file.
627 *
628 * Clearing the lock is as simple as closing the file descriptor we return.
629 */
630int
631lock_file(char *name)
632    {
633    int lfd;
634
635    if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
636	{
637	err("*open: '%s'", name);
638	return -1;
639	}
640    if (flock(lfd, LOCK_EX) < 0)
641	{
642	close(lfd);
643	err("*flock: '%s'", name);
644	return -1;
645	}
646    return lfd;
647    }
648