ctm_rmail.c revision 7147
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.
116081Sphk */
126081Sphk
136081Sphk#include <stdio.h>
146081Sphk#include <stdlib.h>
156081Sphk#include <string.h>
166081Sphk#include <errno.h>
176081Sphk#include <unistd.h>
186081Sphk#include <sys/types.h>
196081Sphk#include <sys/stat.h>
206695Sphk#include <fcntl.h>
216695Sphk#include <limits.h>
226081Sphk#include "error.h"
236081Sphk#include "options.h"
246081Sphk
256081Sphk#define CTM_STATUS	".ctm_status"
266081Sphk
276081Sphkchar *piece_dir = NULL;		/* Where to store pieces of deltas. */
286081Sphkchar *delta_dir = NULL;		/* Where to store completed deltas. */
296081Sphkchar *base_dir = NULL;		/* The tree to apply deltas to. */
306081Sphkint delete_after = 0;		/* Delete deltas after ctm applies them. */
316081Sphk
326081Sphkvoid apply_complete(void);
336081Sphkint read_piece(char *input_file);
346081Sphkint combine_if_complete(char *delta, int pce, int npieces);
356695Sphkint combine(char *delta, int npieces, char *dname, char *pname, char *tname);
366081Sphkint decode_line(char *line, char *out_buf);
376695Sphkint lock_file(char *name);
386081Sphk
396081Sphk/*
406081Sphk * If given a '-p' flag, read encoded delta pieces from stdin or file
416081Sphk * arguments, decode them and assemble any completed deltas.  If given
426081Sphk * a '-b' flag, pass any completed deltas to 'ctm' for application to
436081Sphk * the source tree.  The '-d' flag is mandatory, but either of '-p' or
446081Sphk * '-b' can be omitted.  If given the '-l' flag, notes and errors will
456081Sphk * be timestamped and written to the given file.
466081Sphk *
476081Sphk * Exit status is 0 for success or 1 for indigestible input.  That is,
486081Sphk * 0 means the encode input pieces were decoded and stored, and 1 means
496081Sphk * some input was discarded.  If a delta fails to apply, this won't be
506081Sphk * reflected in the exit status.  In this case, the delta is left in
516081Sphk * 'deltadir'.
526081Sphk */
536290Sphkint
546081Sphkmain(int argc, char **argv)
556081Sphk    {
566081Sphk    char *log_file = NULL;
576081Sphk    int status = 0;
586695Sphk    int fork_ctm = 0;
596081Sphk
606081Sphk    err_prog_name(argv[0]);
616081Sphk
626695Sphk    OPTIONS("[-Df] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
636081Sphk	FLAG('D', delete_after)
646695Sphk	FLAG('f', fork_ctm)
656081Sphk	STRING('p', piece_dir)
666081Sphk	STRING('d', delta_dir)
676081Sphk	STRING('b', base_dir)
686081Sphk	STRING('l', log_file)
696081Sphk    ENDOPTS
706081Sphk
716290Sphk    if (delta_dir == NULL)
726081Sphk	usage();
736081Sphk
746457Sphk    if (piece_dir == NULL && (base_dir == NULL || argc > 1))
756290Sphk	usage();
766290Sphk
776081Sphk    if (log_file != NULL)
786081Sphk	err_set_log(log_file);
796081Sphk
806695Sphk    /*
816695Sphk     * Digest each file in turn, or just stdin if no files were given.
826695Sphk     */
836081Sphk    if (argc <= 1)
846081Sphk	{
856081Sphk	if (piece_dir != NULL)
866081Sphk	    status = read_piece(NULL);
876081Sphk	}
886081Sphk    else
896081Sphk	{
906081Sphk	while (*++argv != NULL)
916081Sphk	    status |= read_piece(*argv);
926081Sphk	}
936081Sphk
946695Sphk    /*
956695Sphk     * Maybe it's time to look for and apply completed deltas with ctm.
966695Sphk     *
976695Sphk     * Shall we report back to sendmail immediately, and let a child do
986695Sphk     * the work?  Sendmail will be waiting for us to complete, delaying
996695Sphk     * other mail, and possibly some intermediate process (like MH slocal)
1006695Sphk     * will terminate us if we take too long!
1016695Sphk     *
1026695Sphk     * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
1036695Sphk     * Also, the child exit status is unimportant.
1046695Sphk     */
1056081Sphk    if (base_dir != NULL)
1066695Sphk	if (!fork_ctm || fork() == 0)
1076695Sphk	    apply_complete();
1086081Sphk
1096081Sphk    return status;
1106081Sphk    }
1116081Sphk
1126081Sphk
1136081Sphk/*
1146081Sphk * Construct the file name of a piece of a delta.
1156081Sphk */
1166081Sphk#define mk_piece_name(fn,d,p,n)	\
1177147Sphk    sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
1186081Sphk
1196081Sphk/*
1206081Sphk * Construct the file name of an assembled delta.
1216081Sphk */
1226081Sphk#define mk_delta_name(fn,d)	\
1236081Sphk    sprintf((fn), "%s/%s", delta_dir, (d))
1246081Sphk
1256081Sphk/*
1266081Sphk * If the next required delta is now present, let ctm lunch on it and any
1276081Sphk * contiguous deltas.
1286081Sphk */
1296081Sphkvoid
1306081Sphkapply_complete()
1316081Sphk    {
1326081Sphk    int i, dn;
1336695Sphk    int lfd;
1346081Sphk    FILE *fp, *ctm;
1356081Sphk    struct stat sb;
1366081Sphk    char class[20];
1376081Sphk    char delta[30];
1386081Sphk    char junk[2];
1396695Sphk    char fname[PATH_MAX];
1406695Sphk    char here[PATH_MAX];
1416695Sphk    char buf[PATH_MAX*2];
1426081Sphk
1436695Sphk    /*
1446695Sphk     * Grab a lock on the ctm mutex file so that we can be sure we are
1456695Sphk     * working alone, not fighting another ctm_rmail!
1466695Sphk     */
1476695Sphk    strcpy(fname, delta_dir);
1486695Sphk    strcat(fname, "/.mutex_apply");
1496695Sphk    if ((lfd = lock_file(fname)) < 0)
1506695Sphk	return;
1516695Sphk
1526695Sphk    /*
1536695Sphk     * Find out which delta ctm needs next.
1546695Sphk     */
1556081Sphk    sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
1566081Sphk    if ((fp = fopen(fname, "r")) == NULL)
1576695Sphk	{
1586695Sphk	close(lfd);
1596081Sphk	return;
1606695Sphk	}
1616081Sphk
1626081Sphk    i = fscanf(fp, "%s %d %c", class, &dn, junk);
1636081Sphk    fclose(fp);
1646081Sphk    if (i != 2)
1656695Sphk	{
1666695Sphk	close(lfd);
1676081Sphk	return;
1686695Sphk	}
1696081Sphk
1706081Sphk    /*
1716081Sphk     * We might need to convert the delta filename to an absolute pathname.
1726081Sphk     */
1736081Sphk    here[0] = '\0';
1746081Sphk    if (delta_dir[0] != '/')
1756081Sphk	{
1766081Sphk	getcwd(here, sizeof(here)-1);
1776081Sphk	i = strlen(here) - 1;
1786081Sphk	if (i >= 0 && here[i] != '/')
1796081Sphk	    {
1806081Sphk	    here[++i] = '/';
1816081Sphk	    here[++i] = '\0';
1826081Sphk	    }
1836081Sphk	}
1846081Sphk
1856081Sphk    /*
1866081Sphk     * Keep applying deltas until we run out or something bad happens.
1876081Sphk     */
1886081Sphk    for (;;)
1896081Sphk	{
1906081Sphk	sprintf(delta, "%s.%04d.gz", class, ++dn);
1916081Sphk	mk_delta_name(fname, delta);
1926081Sphk
1936081Sphk	if (stat(fname, &sb) < 0)
1946695Sphk	    break;
1956081Sphk
1966695Sphk	sprintf(buf, "(cd %s && ctm %s%s) 2>&1", base_dir, here, fname);
1976081Sphk	if ((ctm = popen(buf, "r")) == NULL)
1986081Sphk	    {
1996081Sphk	    err("ctm failed to apply %s", delta);
2006695Sphk	    break;
2016081Sphk	    }
2026081Sphk
2036081Sphk	while (fgets(buf, sizeof(buf), ctm) != NULL)
2046081Sphk	    {
2056081Sphk	    i = strlen(buf) - 1;
2066081Sphk	    if (i >= 0 && buf[i] == '\n')
2076081Sphk		buf[i] = '\0';
2086081Sphk	    err("ctm: %s", buf);
2096081Sphk	    }
2106081Sphk
2116081Sphk	if (pclose(ctm) != 0)
2126081Sphk	    {
2136081Sphk	    err("ctm failed to apply %s", delta);
2146695Sphk	    break;
2156081Sphk	    }
2166081Sphk
2176081Sphk	if (delete_after)
2186081Sphk	    unlink(fname);
2196081Sphk
2206081Sphk	err("%s applied%s", delta, delete_after ? " and deleted" : "");
2216081Sphk	}
2226695Sphk
2236695Sphk    /*
2246695Sphk     * Closing the lock file clears the lock.
2256695Sphk     */
2266695Sphk    close(lfd);
2276081Sphk    }
2286081Sphk
2296081Sphk
2306081Sphk/*
2316081Sphk * This cheap plastic checksum effectively rotates our checksum-so-far
2326081Sphk * left one, then adds the character.  We only want 16 bits of it, and
2336081Sphk * don't care what happens to the rest.  It ain't much, but it's small.
2346081Sphk */
2356081Sphk#define add_ck(sum,x)	\
2366081Sphk    ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
2376081Sphk
2386081Sphk
2396081Sphk/*
2406081Sphk * Decode the data between BEGIN and END, and stash it in the staging area.
2416081Sphk * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
2426081Sphk * If we have all pieces of a delta, combine them.  Returns 0 on success,
2436081Sphk * and 1 for any sort of failure.
2446081Sphk */
2456081Sphkint
2466081Sphkread_piece(char *input_file)
2476081Sphk    {
2486081Sphk    int status = 0;
2496290Sphk    FILE *ifp, *ofp = 0;
2506081Sphk    int decoding = 0;
2516695Sphk    int got_one = 0;
2526081Sphk    int line_no = 0;
2536081Sphk    int i, n;
2546081Sphk    int pce, npieces;
2556081Sphk    unsigned claimed_cksum;
2566290Sphk    unsigned short cksum = 0;
2576081Sphk    char out_buf[200];
2586081Sphk    char line[200];
2596081Sphk    char delta[30];
2606695Sphk    char pname[PATH_MAX];
2616695Sphk    char tname[PATH_MAX];
2626081Sphk    char junk[2];
2636081Sphk
2646081Sphk    ifp = stdin;
2656081Sphk    if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
2666081Sphk	{
2676081Sphk	err("cannot open '%s' for reading", input_file);
2686081Sphk	return 1;
2696081Sphk	}
2706081Sphk
2716081Sphk    while (fgets(line, sizeof(line), ifp) != NULL)
2726081Sphk	{
2736081Sphk	line_no++;
2746081Sphk
2756081Sphk	/*
2766081Sphk	 * Look for the beginning of an encoded piece.
2776081Sphk	 */
2786081Sphk	if (!decoding)
2796081Sphk	    {
2806290Sphk	    char *s;
2816081Sphk
2826457Sphk	    if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
2836290Sphk		    delta, &pce, &npieces, junk) != 3)
2846290Sphk		continue;
2856081Sphk
2866290Sphk	    while ((s = strchr(delta, '/')) != NULL)
2876290Sphk		*s = '_';
2886081Sphk
2896695Sphk	    got_one++;
2906695Sphk	    strcpy(tname, piece_dir);
2916695Sphk	    strcat(tname, "/p.XXXXXX");
2926695Sphk	    if (mktemp(tname) == NULL)
2936695Sphk		{
2946695Sphk		err("*mktemp: '%s'", tname);
2956695Sphk		status++;
2966695Sphk		continue;
2976695Sphk		}
2986658Sphk	    if ((ofp = fopen(tname, "w")) == NULL)
2996290Sphk		{
3006658Sphk		err("cannot open '%s' for writing", tname);
3016290Sphk		status++;
3026290Sphk		continue;
3036081Sphk		}
3046290Sphk
3056290Sphk	    cksum = 0xffff;
3066290Sphk	    decoding++;
3076081Sphk	    continue;
3086081Sphk	    }
3096081Sphk
3106081Sphk	/*
3116081Sphk	 * We are decoding.  Stop if we see the end flag.
3126081Sphk	 */
3136081Sphk	if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
3146081Sphk	    {
3156081Sphk	    int e;
3166081Sphk
3176081Sphk	    decoding = 0;
3186081Sphk
3196081Sphk	    fflush(ofp);
3206081Sphk	    e = ferror(ofp);
3216081Sphk	    fclose(ofp);
3226081Sphk
3236081Sphk	    if (e)
3246658Sphk		err("error writing %s", tname);
3256081Sphk
3266081Sphk	    if (cksum != claimed_cksum)
3276081Sphk		err("checksum: read %d, calculated %d", claimed_cksum, cksum);
3286081Sphk
3296081Sphk	    if (e || cksum != claimed_cksum)
3306081Sphk		{
3316081Sphk		err("%s %d/%d discarded", delta, pce, npieces);
3326658Sphk		unlink(tname);
3336081Sphk		status++;
3346081Sphk		continue;
3356081Sphk		}
3366081Sphk
3376695Sphk	    mk_piece_name(pname, delta, pce, npieces);
3386658Sphk	    if (rename(tname, pname) < 0)
3396658Sphk		{
3406695Sphk		err("*rename: '%s' to '%s'", tname, pname);
3416695Sphk		err("%s %d/%d lost!", delta, pce, npieces);
3426658Sphk		unlink(tname);
3436658Sphk		status++;
3446658Sphk		continue;
3456658Sphk		}
3466658Sphk
3476081Sphk	    err("%s %d/%d stored", delta, pce, npieces);
3486081Sphk
3496081Sphk	    if (!combine_if_complete(delta, pce, npieces))
3506081Sphk		status++;
3516081Sphk	    continue;
3526081Sphk	    }
3536081Sphk
3546081Sphk	/*
3556081Sphk	 * Must be a line of encoded data.  Decode it, sum it, and save it.
3566081Sphk	 */
3576081Sphk	n = decode_line(line, out_buf);
3586695Sphk	if (n <= 0)
3596081Sphk	    {
3606081Sphk	    err("line %d: illegal character: '%c'", line_no, line[-n]);
3616081Sphk	    err("%s %d/%d discarded", delta, pce, npieces);
3626081Sphk
3636081Sphk	    fclose(ofp);
3646695Sphk	    unlink(tname);
3656081Sphk
3666081Sphk	    status++;
3676081Sphk	    decoding = 0;
3686081Sphk	    continue;
3696081Sphk	    }
3706081Sphk
3716081Sphk	for (i = 0; i < n; i++)
3726081Sphk	    add_ck(cksum, out_buf[i]);
3736081Sphk
3746081Sphk	fwrite(out_buf, sizeof(char), n, ofp);
3756081Sphk	}
3766081Sphk
3776081Sphk    if (decoding)
3786081Sphk	{
3796081Sphk	err("truncated file");
3806081Sphk	err("%s %d/%d discarded", delta, pce, npieces);
3816081Sphk
3826081Sphk	fclose(ofp);
3836695Sphk	unlink(tname);
3846081Sphk
3856081Sphk	status++;
3866081Sphk	}
3876081Sphk
3886081Sphk    if (ferror(ifp))
3896081Sphk	{
3906081Sphk	err("error reading %s", input_file == NULL ? "stdin" : input_file);
3916081Sphk	status++;
3926081Sphk	}
3936081Sphk
3946081Sphk    if (input_file != NULL)
3956081Sphk	fclose(ifp);
3966081Sphk
3976695Sphk    if (!got_one)
3986695Sphk	{
3996695Sphk	err("message contains no delta");
4006695Sphk	status++;
4016695Sphk	}
4026695Sphk
4036081Sphk    return (status != 0);
4046081Sphk    }
4056081Sphk
4066081Sphk
4076081Sphk/*
4086081Sphk * Put the pieces together to form a delta, if they are all present.
4096081Sphk * Returns 1 on success (even if we didn't do anything), and 0 on failure.
4106081Sphk */
4116081Sphkint
4126081Sphkcombine_if_complete(char *delta, int pce, int npieces)
4136081Sphk    {
4146695Sphk    int i, e;
4156695Sphk    int lfd;
4166081Sphk    struct stat sb;
4176695Sphk    char pname[PATH_MAX];
4186695Sphk    char dname[PATH_MAX];
4196695Sphk    char tname[PATH_MAX];
4206081Sphk
4216081Sphk    /*
4226695Sphk     * We can probably just rename() it into place if it is a small delta.
4236081Sphk     */
4246695Sphk    if (npieces == 1)
4256081Sphk	{
4266695Sphk	mk_delta_name(dname, delta);
4276695Sphk	mk_piece_name(pname, delta, 1, 1);
4286695Sphk	if (rename(pname, dname) == 0)
4296695Sphk	    {
4306695Sphk	    err("%s complete", delta);
4316081Sphk	    return 1;
4326695Sphk	    }
4336081Sphk	}
4346081Sphk
4356695Sphk    /*
4366695Sphk     * Grab a lock on the reassembly mutex file so that we can be sure we are
4376695Sphk     * working alone, not fighting another ctm_rmail!
4386695Sphk     */
4396695Sphk    strcpy(tname, delta_dir);
4406695Sphk    strcat(tname, "/.mutex_build");
4416695Sphk    if ((lfd = lock_file(tname)) < 0)
4426695Sphk	return 0;
4436081Sphk
4446081Sphk    /*
4456695Sphk     * Are all of the pieces present?  Of course the current one is,
4466695Sphk     * unless all pieces are missing because another ctm_rmail has
4476695Sphk     * processed them already.
4486081Sphk     */
4496695Sphk    for (i = 1; i <= npieces; i++)
4506081Sphk	{
4516695Sphk	if (i == pce)
4526695Sphk	    continue;
4536695Sphk	mk_piece_name(pname, delta, i, npieces);
4546695Sphk	if (stat(pname, &sb) < 0)
4556081Sphk	    {
4566695Sphk	    close(lfd);
4576081Sphk	    return 1;
4586081Sphk	    }
4596081Sphk	}
4606081Sphk
4616695Sphk    /*
4626695Sphk     * Stick them together.  Let combine() use our file name buffers, since
4636695Sphk     * we're such good buddies. :-)
4646695Sphk     */
4656695Sphk    e = combine(delta, npieces, dname, pname, tname);
4666695Sphk    close(lfd);
4676695Sphk    return e;
4686695Sphk    }
4696695Sphk
4706695Sphk
4716695Sphk/*
4726695Sphk * Put the pieces together to form a delta.
4736695Sphk * Returns 1 on success, and 0 on failure.
4746695Sphk * Note: dname, pname, and tname are room for some file names that just
4756695Sphk * happened to by lying around in the calling routine.  Waste not, want not!
4766695Sphk */
4776695Sphkint
4786695Sphkcombine(char *delta, int npieces, char *dname, char *pname, char *tname)
4796695Sphk    {
4806695Sphk    FILE *dfp, *pfp;
4816695Sphk    int i, n, e;
4826695Sphk    char buf[BUFSIZ];
4836695Sphk
4846695Sphk    strcpy(tname, delta_dir);
4856695Sphk    strcat(tname, "/d.XXXXXX");
4866695Sphk    if (mktemp(tname) == NULL)
4876081Sphk	{
4886695Sphk	err("*mktemp: '%s'", tname);
4896081Sphk	return 0;
4906081Sphk	}
4916695Sphk    if ((dfp = fopen(tname, "w")) == NULL)
4926695Sphk	{
4936695Sphk	err("cannot open '%s' for writing", tname);
4946695Sphk	return 0;
4956695Sphk	}
4966081Sphk
4976081Sphk    /*
4986695Sphk     * Reconstruct the delta by reading each piece in order.
4996081Sphk     */
5006081Sphk    for (i = 1; i <= npieces; i++)
5016081Sphk	{
5026081Sphk	mk_piece_name(pname, delta, i, npieces);
5036081Sphk	if ((pfp = fopen(pname, "r")) == NULL)
5046081Sphk	    {
5056081Sphk	    err("cannot open '%s' for reading", pname);
5066081Sphk	    fclose(dfp);
5076695Sphk	    unlink(tname);
5086081Sphk	    return 0;
5096081Sphk	    }
5106695Sphk	while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
5116695Sphk	    fwrite(buf, sizeof(char), n, dfp);
5126695Sphk	e = ferror(pfp);
5136081Sphk	fclose(pfp);
5146695Sphk	if (e)
5156695Sphk	    {
5166695Sphk	    err("error reading '%s'", pname);
5176695Sphk	    fclose(dfp);
5186695Sphk	    unlink(tname);
5196695Sphk	    return 0;
5206695Sphk	    }
5216081Sphk	}
5226081Sphk    fflush(dfp);
5236695Sphk    e = ferror(dfp);
5246695Sphk    fclose(dfp);
5256695Sphk    if (e)
5266081Sphk	{
5276695Sphk	err("error writing '%s'", tname);
5286695Sphk	unlink(tname);
5296081Sphk	return 0;
5306081Sphk	}
5316081Sphk
5326695Sphk    mk_delta_name(dname, delta);
5336695Sphk    if (rename(tname, dname) < 0)
5346695Sphk	{
5356695Sphk	err("*rename: '%s' to '%s'", tname, dname);
5366695Sphk	unlink(tname);
5376695Sphk	return 0;
5386695Sphk	}
5396695Sphk
5406081Sphk    /*
5416081Sphk     * Throw the pieces away.
5426081Sphk     */
5436081Sphk    for (i = 1; i <= npieces; i++)
5446081Sphk	{
5456081Sphk	mk_piece_name(pname, delta, i, npieces);
5466695Sphk	if (unlink(pname) < 0)
5476695Sphk	    err("*unlink: '%s'", pname);
5486081Sphk	}
5496081Sphk
5506081Sphk    err("%s complete", delta);
5516081Sphk    return 1;
5526081Sphk    }
5536081Sphk
5546081Sphk
5556081Sphk/*
5566081Sphk * MIME BASE64 decode table.
5576081Sphk */
5586081Sphkstatic unsigned char from_b64[0x80] =
5596081Sphk    {
5606081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5616081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5626081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5636081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5646081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5656081Sphk    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
5666081Sphk    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
5676081Sphk    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5686081Sphk    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
5696081Sphk    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
5706081Sphk    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
5716081Sphk    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
5726081Sphk    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
5736081Sphk    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
5746081Sphk    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
5756081Sphk    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
5766081Sphk    };
5776081Sphk
5786081Sphk
5796081Sphk/*
5806081Sphk * Decode a line of ASCII into binary.  Returns the number of bytes in
5816081Sphk * the output buffer, or < 0 on indigestable input.  Error output is
5826081Sphk * the negative of the index of the inedible character.
5836081Sphk */
5846081Sphkint
5856081Sphkdecode_line(char *line, char *out_buf)
5866081Sphk    {
5876081Sphk    unsigned char *ip = (unsigned char *)line;
5886081Sphk    unsigned char *op = (unsigned char *)out_buf;
5896081Sphk    unsigned long bits;
5906081Sphk    unsigned x;
5916081Sphk
5926081Sphk    for (;;)
5936081Sphk	{
5946081Sphk	if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
5956081Sphk	    break;
5966081Sphk	bits = x << 18;
5976081Sphk	ip++;
5986081Sphk	if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
5996081Sphk	    {
6006081Sphk	    bits |= x << 12;
6016081Sphk	    *op++ = bits >> 16;
6026081Sphk	    ip++;
6036081Sphk	    if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6046081Sphk		{
6056081Sphk		bits |= x << 6;
6066081Sphk		*op++ = bits >> 8;
6076081Sphk		ip++;
6086081Sphk		if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6096081Sphk		    {
6106081Sphk		    bits |= x;
6116081Sphk		    *op++ = bits;
6126081Sphk		    ip++;
6136081Sphk		    }
6146081Sphk		}
6156081Sphk	    }
6166081Sphk	}
6176081Sphk
6186081Sphk    if (*ip == '\0' || *ip == '\n')
6196081Sphk	return op - (unsigned char *)out_buf;
6206081Sphk    else
6216081Sphk	return -(ip - (unsigned char *)line);
6226081Sphk    }
6236695Sphk
6246695Sphk
6256695Sphk/*
6266695Sphk * Create and lock the given file.
6276695Sphk *
6286695Sphk * Clearing the lock is as simple as closing the file descriptor we return.
6296695Sphk */
6306695Sphkint
6316695Sphklock_file(char *name)
6326695Sphk    {
6336695Sphk    int lfd;
6346695Sphk
6356695Sphk    if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
6366695Sphk	{
6376695Sphk	err("*open: '%s'", name);
6386695Sphk	return -1;
6396695Sphk	}
6406695Sphk    if (flock(lfd, LOCK_EX) < 0)
6416695Sphk	{
6426695Sphk	close(lfd);
6436695Sphk	err("*flock: '%s'", name);
6446695Sphk	return -1;
6456695Sphk	}
6466695Sphk    return lfd;
6476695Sphk    }
648