16081Sphk/*
26081Sphk * Accept one (or more) ASCII encoded chunks that together make a compressed
36081Sphk * CTM delta.  Decode them and reconstruct the deltas.  Any completed
46081Sphk * deltas may be passed to ctm for unpacking.
56081Sphk *
66081Sphk * Author: Stephen McKay
76081Sphk *
86081Sphk * NOTICE: This is free software.  I hope you get some use from this program.
96081Sphk * In return you should think about all the nice people who give away software.
106081Sphk * Maybe you should write some free software too.
1155635Skris *
1255635Skris * $FreeBSD$
136081Sphk */
146081Sphk
156081Sphk#include <stdio.h>
166081Sphk#include <stdlib.h>
1755635Skris#include <string.h>
1813015Sphk#include <ctype.h>
196081Sphk#include <errno.h>
206081Sphk#include <unistd.h>
216081Sphk#include <sys/types.h>
226081Sphk#include <sys/stat.h>
236695Sphk#include <fcntl.h>
246695Sphk#include <limits.h>
256081Sphk#include "error.h"
266081Sphk#include "options.h"
276081Sphk
286081Sphk#define CTM_STATUS	".ctm_status"
296081Sphk
306081Sphkchar *piece_dir = NULL;		/* Where to store pieces of deltas. */
316081Sphkchar *delta_dir = NULL;		/* Where to store completed deltas. */
326081Sphkchar *base_dir = NULL;		/* The tree to apply deltas to. */
336081Sphkint delete_after = 0;		/* Delete deltas after ctm applies them. */
3413019Speterint apply_verbose = 0;		/* Run with '-v' */
3515456Sphkint set_time = 0;		/* Set the time of the files that is changed. */
3689675Siedowseint mask = 0;			/* The current umask */
376081Sphk
386081Sphkvoid apply_complete(void);
396081Sphkint read_piece(char *input_file);
406081Sphkint combine_if_complete(char *delta, int pce, int npieces);
416695Sphkint combine(char *delta, int npieces, char *dname, char *pname, char *tname);
426081Sphkint decode_line(char *line, char *out_buf);
436695Sphkint lock_file(char *name);
446081Sphk
456081Sphk/*
466081Sphk * If given a '-p' flag, read encoded delta pieces from stdin or file
476081Sphk * arguments, decode them and assemble any completed deltas.  If given
486081Sphk * a '-b' flag, pass any completed deltas to 'ctm' for application to
496081Sphk * the source tree.  The '-d' flag is mandatory, but either of '-p' or
506081Sphk * '-b' can be omitted.  If given the '-l' flag, notes and errors will
516081Sphk * be timestamped and written to the given file.
526081Sphk *
536081Sphk * Exit status is 0 for success or 1 for indigestible input.  That is,
546081Sphk * 0 means the encode input pieces were decoded and stored, and 1 means
556081Sphk * some input was discarded.  If a delta fails to apply, this won't be
566081Sphk * reflected in the exit status.  In this case, the delta is left in
576081Sphk * 'deltadir'.
586081Sphk */
596290Sphkint
606081Sphkmain(int argc, char **argv)
616081Sphk    {
626081Sphk    char *log_file = NULL;
636081Sphk    int status = 0;
646695Sphk    int fork_ctm = 0;
656081Sphk
6689675Siedowse    mask = umask(0);
6789675Siedowse    umask(mask);
6889675Siedowse
696081Sphk    err_prog_name(argv[0]);
706081Sphk
7115456Sphk    OPTIONS("[-Dfuv] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
726081Sphk	FLAG('D', delete_after)
736695Sphk	FLAG('f', fork_ctm)
7415456Sphk	FLAG('u', set_time)
7513019Speter	FLAG('v', apply_verbose)
766081Sphk	STRING('p', piece_dir)
776081Sphk	STRING('d', delta_dir)
786081Sphk	STRING('b', base_dir)
796081Sphk	STRING('l', log_file)
806081Sphk    ENDOPTS
816081Sphk
826290Sphk    if (delta_dir == NULL)
836081Sphk	usage();
846081Sphk
856457Sphk    if (piece_dir == NULL && (base_dir == NULL || argc > 1))
866290Sphk	usage();
876290Sphk
886081Sphk    if (log_file != NULL)
896081Sphk	err_set_log(log_file);
906081Sphk
916695Sphk    /*
926695Sphk     * Digest each file in turn, or just stdin if no files were given.
936695Sphk     */
946081Sphk    if (argc <= 1)
956081Sphk	{
966081Sphk	if (piece_dir != NULL)
976081Sphk	    status = read_piece(NULL);
986081Sphk	}
996081Sphk    else
1006081Sphk	{
1016081Sphk	while (*++argv != NULL)
1026081Sphk	    status |= read_piece(*argv);
1036081Sphk	}
1046081Sphk
1056695Sphk    /*
1066695Sphk     * Maybe it's time to look for and apply completed deltas with ctm.
1076695Sphk     *
1086695Sphk     * Shall we report back to sendmail immediately, and let a child do
1096695Sphk     * the work?  Sendmail will be waiting for us to complete, delaying
1106695Sphk     * other mail, and possibly some intermediate process (like MH slocal)
1116695Sphk     * will terminate us if we take too long!
1126695Sphk     *
1136695Sphk     * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
1146695Sphk     * Also, the child exit status is unimportant.
1156695Sphk     */
1166081Sphk    if (base_dir != NULL)
1176695Sphk	if (!fork_ctm || fork() == 0)
1186695Sphk	    apply_complete();
1196081Sphk
1206081Sphk    return status;
1216081Sphk    }
1226081Sphk
1236081Sphk
1246081Sphk/*
1256081Sphk * Construct the file name of a piece of a delta.
1266081Sphk */
1276081Sphk#define mk_piece_name(fn,d,p,n)	\
1287147Sphk    sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
1296081Sphk
1306081Sphk/*
1316081Sphk * Construct the file name of an assembled delta.
1326081Sphk */
1336081Sphk#define mk_delta_name(fn,d)	\
1346081Sphk    sprintf((fn), "%s/%s", delta_dir, (d))
1356081Sphk
1366081Sphk/*
1376081Sphk * If the next required delta is now present, let ctm lunch on it and any
1386081Sphk * contiguous deltas.
1396081Sphk */
1406081Sphkvoid
1416081Sphkapply_complete()
1426081Sphk    {
1436081Sphk    int i, dn;
1446695Sphk    int lfd;
1456081Sphk    FILE *fp, *ctm;
1466081Sphk    struct stat sb;
1476081Sphk    char class[20];
1486081Sphk    char delta[30];
1496081Sphk    char junk[2];
1506695Sphk    char fname[PATH_MAX];
1516695Sphk    char here[PATH_MAX];
1526695Sphk    char buf[PATH_MAX*2];
1536081Sphk
1546695Sphk    /*
1556695Sphk     * Grab a lock on the ctm mutex file so that we can be sure we are
1566695Sphk     * working alone, not fighting another ctm_rmail!
1576695Sphk     */
1586695Sphk    strcpy(fname, delta_dir);
1596695Sphk    strcat(fname, "/.mutex_apply");
1606695Sphk    if ((lfd = lock_file(fname)) < 0)
1616695Sphk	return;
1626695Sphk
1636695Sphk    /*
1646695Sphk     * Find out which delta ctm needs next.
1656695Sphk     */
1666081Sphk    sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
1676081Sphk    if ((fp = fopen(fname, "r")) == NULL)
1686695Sphk	{
1696695Sphk	close(lfd);
1706081Sphk	return;
1716695Sphk	}
1726081Sphk
17355635Skris    i = fscanf(fp, "%19s %d %c", class, &dn, junk);
1746081Sphk    fclose(fp);
1756081Sphk    if (i != 2)
1766695Sphk	{
1776695Sphk	close(lfd);
1786081Sphk	return;
1796695Sphk	}
1806081Sphk
1816081Sphk    /*
1826081Sphk     * We might need to convert the delta filename to an absolute pathname.
1836081Sphk     */
1846081Sphk    here[0] = '\0';
1856081Sphk    if (delta_dir[0] != '/')
1866081Sphk	{
1876081Sphk	getcwd(here, sizeof(here)-1);
1886081Sphk	i = strlen(here) - 1;
1896081Sphk	if (i >= 0 && here[i] != '/')
1906081Sphk	    {
1916081Sphk	    here[++i] = '/';
1926081Sphk	    here[++i] = '\0';
1936081Sphk	    }
1946081Sphk	}
1956081Sphk
1966081Sphk    /*
1976081Sphk     * Keep applying deltas until we run out or something bad happens.
1986081Sphk     */
1996081Sphk    for (;;)
2006081Sphk	{
2016081Sphk	sprintf(delta, "%s.%04d.gz", class, ++dn);
2026081Sphk	mk_delta_name(fname, delta);
2036081Sphk
2046081Sphk	if (stat(fname, &sb) < 0)
2056695Sphk	    break;
2066081Sphk
20715456Sphk	sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
20815456Sphk				set_time ? "-u " : "",
20913019Speter				apply_verbose ? "-v " : "", here, fname);
2106081Sphk	if ((ctm = popen(buf, "r")) == NULL)
2116081Sphk	    {
2126081Sphk	    err("ctm failed to apply %s", delta);
2136695Sphk	    break;
2146081Sphk	    }
2156081Sphk
2166081Sphk	while (fgets(buf, sizeof(buf), ctm) != NULL)
2176081Sphk	    {
2186081Sphk	    i = strlen(buf) - 1;
2196081Sphk	    if (i >= 0 && buf[i] == '\n')
2206081Sphk		buf[i] = '\0';
2216081Sphk	    err("ctm: %s", buf);
2226081Sphk	    }
2236081Sphk
2246081Sphk	if (pclose(ctm) != 0)
2256081Sphk	    {
2266081Sphk	    err("ctm failed to apply %s", delta);
2276695Sphk	    break;
2286081Sphk	    }
2296081Sphk
2306081Sphk	if (delete_after)
2316081Sphk	    unlink(fname);
2326081Sphk
2336081Sphk	err("%s applied%s", delta, delete_after ? " and deleted" : "");
2346081Sphk	}
2356695Sphk
2366695Sphk    /*
2376695Sphk     * Closing the lock file clears the lock.
2386695Sphk     */
2396695Sphk    close(lfd);
2406081Sphk    }
2416081Sphk
2426081Sphk
2436081Sphk/*
2446081Sphk * This cheap plastic checksum effectively rotates our checksum-so-far
2456081Sphk * left one, then adds the character.  We only want 16 bits of it, and
2466081Sphk * don't care what happens to the rest.  It ain't much, but it's small.
2476081Sphk */
2486081Sphk#define add_ck(sum,x)	\
2496081Sphk    ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
2506081Sphk
2516081Sphk
2526081Sphk/*
2536081Sphk * Decode the data between BEGIN and END, and stash it in the staging area.
2546081Sphk * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
2556081Sphk * If we have all pieces of a delta, combine them.  Returns 0 on success,
2566081Sphk * and 1 for any sort of failure.
2576081Sphk */
2586081Sphkint
2596081Sphkread_piece(char *input_file)
2606081Sphk    {
2616081Sphk    int status = 0;
2626290Sphk    FILE *ifp, *ofp = 0;
2636081Sphk    int decoding = 0;
2646695Sphk    int got_one = 0;
2656081Sphk    int line_no = 0;
2666081Sphk    int i, n;
2676081Sphk    int pce, npieces;
2686081Sphk    unsigned claimed_cksum;
2696290Sphk    unsigned short cksum = 0;
2706081Sphk    char out_buf[200];
2716081Sphk    char line[200];
2726081Sphk    char delta[30];
2736695Sphk    char pname[PATH_MAX];
2746695Sphk    char tname[PATH_MAX];
2756081Sphk    char junk[2];
2766081Sphk
2776081Sphk    ifp = stdin;
2786081Sphk    if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
2796081Sphk	{
2806081Sphk	err("cannot open '%s' for reading", input_file);
2816081Sphk	return 1;
2826081Sphk	}
2836081Sphk
2846081Sphk    while (fgets(line, sizeof(line), ifp) != NULL)
2856081Sphk	{
2866081Sphk	line_no++;
2876081Sphk
2886081Sphk	/*
28913015Sphk	 * Remove all trailing white space.
29013015Sphk	 */
29113015Sphk	i = strlen(line) - 1;
29213015Sphk	while (i > 0 && isspace(line[i]))
29313028Sphk		line[i--] = '\0';
29413015Sphk
29513015Sphk	/*
2966081Sphk	 * Look for the beginning of an encoded piece.
2976081Sphk	 */
2986081Sphk	if (!decoding)
2996081Sphk	    {
3006290Sphk	    char *s;
30155635Skris	    int fd = -1;
3026081Sphk
30355635Skris	    if (sscanf(line, "CTM_MAIL BEGIN %29s %d %d %c",
3046290Sphk		    delta, &pce, &npieces, junk) != 3)
3056290Sphk		continue;
3066081Sphk
3076290Sphk	    while ((s = strchr(delta, '/')) != NULL)
3086290Sphk		*s = '_';
3096081Sphk
3106695Sphk	    got_one++;
3116695Sphk	    strcpy(tname, piece_dir);
31255635Skris	    strcat(tname, "/p.XXXXXXXXXX");
31355635Skris	    if ((fd = mkstemp(tname)) == -1 ||
31455635Skris		(ofp = fdopen(fd, "w")) == NULL)
3156695Sphk		{
31655635Skris		if (fd != -1) {
31755635Skris		    err("cannot open '%s' for writing", tname);
31855635Skris		    close(fd);
31955635Skris		    }
32055635Skris		else
32155635Skris		    err("*mkstemp: '%s'", tname);
3226695Sphk		status++;
3236695Sphk		continue;
3246695Sphk		}
3256290Sphk
3266290Sphk	    cksum = 0xffff;
3276290Sphk	    decoding++;
3286081Sphk	    continue;
3296081Sphk	    }
3306081Sphk
3316081Sphk	/*
3326081Sphk	 * We are decoding.  Stop if we see the end flag.
3336081Sphk	 */
3346081Sphk	if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
3356081Sphk	    {
3366081Sphk	    int e;
3376081Sphk
3386081Sphk	    decoding = 0;
3396081Sphk
3406081Sphk	    fflush(ofp);
3416081Sphk	    e = ferror(ofp);
3426081Sphk	    fclose(ofp);
3436081Sphk
3446081Sphk	    if (e)
3456658Sphk		err("error writing %s", tname);
3466081Sphk
3476081Sphk	    if (cksum != claimed_cksum)
3486081Sphk		err("checksum: read %d, calculated %d", claimed_cksum, cksum);
3496081Sphk
3506081Sphk	    if (e || cksum != claimed_cksum)
3516081Sphk		{
3526081Sphk		err("%s %d/%d discarded", delta, pce, npieces);
3536658Sphk		unlink(tname);
3546081Sphk		status++;
3556081Sphk		continue;
3566081Sphk		}
3576081Sphk
3586695Sphk	    mk_piece_name(pname, delta, pce, npieces);
3596658Sphk	    if (rename(tname, pname) < 0)
3606658Sphk		{
3616695Sphk		err("*rename: '%s' to '%s'", tname, pname);
3626695Sphk		err("%s %d/%d lost!", delta, pce, npieces);
3636658Sphk		unlink(tname);
3646658Sphk		status++;
3656658Sphk		continue;
3666658Sphk		}
3676658Sphk
3686081Sphk	    err("%s %d/%d stored", delta, pce, npieces);
3696081Sphk
3706081Sphk	    if (!combine_if_complete(delta, pce, npieces))
3716081Sphk		status++;
3726081Sphk	    continue;
3736081Sphk	    }
3746081Sphk
3756081Sphk	/*
3766081Sphk	 * Must be a line of encoded data.  Decode it, sum it, and save it.
3776081Sphk	 */
3786081Sphk	n = decode_line(line, out_buf);
3796695Sphk	if (n <= 0)
3806081Sphk	    {
3816081Sphk	    err("line %d: illegal character: '%c'", line_no, line[-n]);
3826081Sphk	    err("%s %d/%d discarded", delta, pce, npieces);
3836081Sphk
3846081Sphk	    fclose(ofp);
3856695Sphk	    unlink(tname);
3866081Sphk
3876081Sphk	    status++;
3886081Sphk	    decoding = 0;
3896081Sphk	    continue;
3906081Sphk	    }
3916081Sphk
3926081Sphk	for (i = 0; i < n; i++)
3936081Sphk	    add_ck(cksum, out_buf[i]);
3946081Sphk
3956081Sphk	fwrite(out_buf, sizeof(char), n, ofp);
3966081Sphk	}
3976081Sphk
3986081Sphk    if (decoding)
3996081Sphk	{
4006081Sphk	err("truncated file");
4016081Sphk	err("%s %d/%d discarded", delta, pce, npieces);
4026081Sphk
4036081Sphk	fclose(ofp);
4046695Sphk	unlink(tname);
4056081Sphk
4066081Sphk	status++;
4076081Sphk	}
4086081Sphk
4096081Sphk    if (ferror(ifp))
4106081Sphk	{
4116081Sphk	err("error reading %s", input_file == NULL ? "stdin" : input_file);
4126081Sphk	status++;
4136081Sphk	}
4146081Sphk
4156081Sphk    if (input_file != NULL)
4166081Sphk	fclose(ifp);
4176081Sphk
4186695Sphk    if (!got_one)
4196695Sphk	{
4206695Sphk	err("message contains no delta");
4216695Sphk	status++;
4226695Sphk	}
4236695Sphk
4246081Sphk    return (status != 0);
4256081Sphk    }
4266081Sphk
4276081Sphk
4286081Sphk/*
4296081Sphk * Put the pieces together to form a delta, if they are all present.
4306081Sphk * Returns 1 on success (even if we didn't do anything), and 0 on failure.
4316081Sphk */
4326081Sphkint
4336081Sphkcombine_if_complete(char *delta, int pce, int npieces)
4346081Sphk    {
4356695Sphk    int i, e;
4366695Sphk    int lfd;
4376081Sphk    struct stat sb;
4386695Sphk    char pname[PATH_MAX];
4396695Sphk    char dname[PATH_MAX];
4406695Sphk    char tname[PATH_MAX];
4416081Sphk
4426081Sphk    /*
4436695Sphk     * We can probably just rename() it into place if it is a small delta.
4446081Sphk     */
4456695Sphk    if (npieces == 1)
4466081Sphk	{
4476695Sphk	mk_delta_name(dname, delta);
4486695Sphk	mk_piece_name(pname, delta, 1, 1);
4496695Sphk	if (rename(pname, dname) == 0)
4506695Sphk	    {
45189675Siedowse	    chmod(dname, 0666 & ~mask);
4526695Sphk	    err("%s complete", delta);
4536081Sphk	    return 1;
4546695Sphk	    }
4556081Sphk	}
4566081Sphk
4576695Sphk    /*
4586695Sphk     * Grab a lock on the reassembly mutex file so that we can be sure we are
4596695Sphk     * working alone, not fighting another ctm_rmail!
4606695Sphk     */
4616695Sphk    strcpy(tname, delta_dir);
4626695Sphk    strcat(tname, "/.mutex_build");
4636695Sphk    if ((lfd = lock_file(tname)) < 0)
4646695Sphk	return 0;
4656081Sphk
4666081Sphk    /*
4676695Sphk     * Are all of the pieces present?  Of course the current one is,
4686695Sphk     * unless all pieces are missing because another ctm_rmail has
4696695Sphk     * processed them already.
4706081Sphk     */
4716695Sphk    for (i = 1; i <= npieces; i++)
4726081Sphk	{
4736695Sphk	if (i == pce)
4746695Sphk	    continue;
4756695Sphk	mk_piece_name(pname, delta, i, npieces);
4766695Sphk	if (stat(pname, &sb) < 0)
4776081Sphk	    {
4786695Sphk	    close(lfd);
4796081Sphk	    return 1;
4806081Sphk	    }
4816081Sphk	}
4826081Sphk
4836695Sphk    /*
4846695Sphk     * Stick them together.  Let combine() use our file name buffers, since
4856695Sphk     * we're such good buddies. :-)
4866695Sphk     */
4876695Sphk    e = combine(delta, npieces, dname, pname, tname);
4886695Sphk    close(lfd);
4896695Sphk    return e;
4906695Sphk    }
4916695Sphk
4926695Sphk
4936695Sphk/*
4946695Sphk * Put the pieces together to form a delta.
4956695Sphk * Returns 1 on success, and 0 on failure.
4966695Sphk * Note: dname, pname, and tname are room for some file names that just
4976695Sphk * happened to by lying around in the calling routine.  Waste not, want not!
4986695Sphk */
4996695Sphkint
5006695Sphkcombine(char *delta, int npieces, char *dname, char *pname, char *tname)
5016695Sphk    {
5026695Sphk    FILE *dfp, *pfp;
5036695Sphk    int i, n, e;
5046695Sphk    char buf[BUFSIZ];
50555635Skris    int fd = -1;
5066695Sphk
5076695Sphk    strcpy(tname, delta_dir);
50855635Skris    strcat(tname, "/d.XXXXXXXXXX");
50955635Skris    if ((fd = mkstemp(tname)) == -1 ||
51055635Skris	(dfp = fdopen(fd, "w")) == NULL)
5116081Sphk	{
51255635Skris	if (fd != -1) {
51355635Skris	    close(fd);
51455635Skris	    err("cannot open '%s' for writing", tname);
51555635Skris	    }
51655635Skris	else
51776300Skris	    err("*mkstemp: '%s'", tname);
5186081Sphk	return 0;
5196081Sphk	}
5206081Sphk
5216081Sphk    /*
5226695Sphk     * Reconstruct the delta by reading each piece in order.
5236081Sphk     */
5246081Sphk    for (i = 1; i <= npieces; i++)
5256081Sphk	{
5266081Sphk	mk_piece_name(pname, delta, i, npieces);
5276081Sphk	if ((pfp = fopen(pname, "r")) == NULL)
5286081Sphk	    {
5296081Sphk	    err("cannot open '%s' for reading", pname);
5306081Sphk	    fclose(dfp);
5316695Sphk	    unlink(tname);
5326081Sphk	    return 0;
5336081Sphk	    }
5346695Sphk	while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
5356695Sphk	    fwrite(buf, sizeof(char), n, dfp);
5366695Sphk	e = ferror(pfp);
5376081Sphk	fclose(pfp);
5386695Sphk	if (e)
5396695Sphk	    {
5406695Sphk	    err("error reading '%s'", pname);
5416695Sphk	    fclose(dfp);
5426695Sphk	    unlink(tname);
5436695Sphk	    return 0;
5446695Sphk	    }
5456081Sphk	}
5466081Sphk    fflush(dfp);
5476695Sphk    e = ferror(dfp);
5486695Sphk    fclose(dfp);
5496695Sphk    if (e)
5506081Sphk	{
5516695Sphk	err("error writing '%s'", tname);
5526695Sphk	unlink(tname);
5536081Sphk	return 0;
5546081Sphk	}
5556081Sphk
5566695Sphk    mk_delta_name(dname, delta);
5576695Sphk    if (rename(tname, dname) < 0)
5586695Sphk	{
5596695Sphk	err("*rename: '%s' to '%s'", tname, dname);
5606695Sphk	unlink(tname);
5616695Sphk	return 0;
5626695Sphk	}
56389675Siedowse    chmod(dname, 0666 & ~mask);
5646695Sphk
5656081Sphk    /*
5666081Sphk     * Throw the pieces away.
5676081Sphk     */
5686081Sphk    for (i = 1; i <= npieces; i++)
5696081Sphk	{
5706081Sphk	mk_piece_name(pname, delta, i, npieces);
5716695Sphk	if (unlink(pname) < 0)
5726695Sphk	    err("*unlink: '%s'", pname);
5736081Sphk	}
5746081Sphk
5756081Sphk    err("%s complete", delta);
5766081Sphk    return 1;
5776081Sphk    }
5786081Sphk
5796081Sphk
5806081Sphk/*
5816081Sphk * MIME BASE64 decode table.
5826081Sphk */
5836081Sphkstatic unsigned char from_b64[0x80] =
5848857Srgrimes    {
5856081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5866081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5876081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5886081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5898857Srgrimes    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5906081Sphk    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
5916081Sphk    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
5926081Sphk    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5936081Sphk    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
5946081Sphk    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
5956081Sphk    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
5966081Sphk    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
5976081Sphk    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
5986081Sphk    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
5996081Sphk    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
6006081Sphk    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
6016081Sphk    };
6026081Sphk
6036081Sphk
6046081Sphk/*
6056081Sphk * Decode a line of ASCII into binary.  Returns the number of bytes in
6066081Sphk * the output buffer, or < 0 on indigestable input.  Error output is
6076081Sphk * the negative of the index of the inedible character.
6086081Sphk */
6096081Sphkint
6106081Sphkdecode_line(char *line, char *out_buf)
6116081Sphk    {
6126081Sphk    unsigned char *ip = (unsigned char *)line;
6136081Sphk    unsigned char *op = (unsigned char *)out_buf;
6146081Sphk    unsigned long bits;
6156081Sphk    unsigned x;
6166081Sphk
6176081Sphk    for (;;)
6186081Sphk	{
6196081Sphk	if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
6206081Sphk	    break;
6216081Sphk	bits = x << 18;
6226081Sphk	ip++;
6236081Sphk	if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6246081Sphk	    {
6256081Sphk	    bits |= x << 12;
6266081Sphk	    *op++ = bits >> 16;
6276081Sphk	    ip++;
6286081Sphk	    if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6296081Sphk		{
6306081Sphk		bits |= x << 6;
6316081Sphk		*op++ = bits >> 8;
6326081Sphk		ip++;
6336081Sphk		if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6346081Sphk		    {
6356081Sphk		    bits |= x;
6366081Sphk		    *op++ = bits;
6376081Sphk		    ip++;
6386081Sphk		    }
6396081Sphk		}
6406081Sphk	    }
6416081Sphk	}
6426081Sphk
6436081Sphk    if (*ip == '\0' || *ip == '\n')
6446081Sphk	return op - (unsigned char *)out_buf;
6456081Sphk    else
6466081Sphk	return -(ip - (unsigned char *)line);
6476081Sphk    }
6486695Sphk
6496695Sphk
6506695Sphk/*
6516695Sphk * Create and lock the given file.
6526695Sphk *
6536695Sphk * Clearing the lock is as simple as closing the file descriptor we return.
6546695Sphk */
6556695Sphkint
6566695Sphklock_file(char *name)
6576695Sphk    {
6586695Sphk    int lfd;
6596695Sphk
6606695Sphk    if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
6616695Sphk	{
6626695Sphk	err("*open: '%s'", name);
6636695Sphk	return -1;
6646695Sphk	}
6656695Sphk    if (flock(lfd, LOCK_EX) < 0)
6666695Sphk	{
6676695Sphk	close(lfd);
6686695Sphk	err("*flock: '%s'", name);
6696695Sphk	return -1;
6706695Sphk	}
6716695Sphk    return lfd;
6726695Sphk    }
673