ctm_rmail.c revision 13028
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>
1513015Sphk#include <strings.h>
1613015Sphk#include <ctype.h>
176081Sphk#include <errno.h>
186081Sphk#include <unistd.h>
196081Sphk#include <sys/types.h>
206081Sphk#include <sys/stat.h>
216695Sphk#include <fcntl.h>
226695Sphk#include <limits.h>
236081Sphk#include "error.h"
246081Sphk#include "options.h"
256081Sphk
266081Sphk#define CTM_STATUS	".ctm_status"
276081Sphk
286081Sphkchar *piece_dir = NULL;		/* Where to store pieces of deltas. */
296081Sphkchar *delta_dir = NULL;		/* Where to store completed deltas. */
306081Sphkchar *base_dir = NULL;		/* The tree to apply deltas to. */
316081Sphkint delete_after = 0;		/* Delete deltas after ctm applies them. */
3213019Speterint apply_verbose = 0;		/* Run with '-v' */
336081Sphk
346081Sphkvoid apply_complete(void);
356081Sphkint read_piece(char *input_file);
366081Sphkint combine_if_complete(char *delta, int pce, int npieces);
376695Sphkint combine(char *delta, int npieces, char *dname, char *pname, char *tname);
386081Sphkint decode_line(char *line, char *out_buf);
396695Sphkint lock_file(char *name);
406081Sphk
416081Sphk/*
426081Sphk * If given a '-p' flag, read encoded delta pieces from stdin or file
436081Sphk * arguments, decode them and assemble any completed deltas.  If given
446081Sphk * a '-b' flag, pass any completed deltas to 'ctm' for application to
456081Sphk * the source tree.  The '-d' flag is mandatory, but either of '-p' or
466081Sphk * '-b' can be omitted.  If given the '-l' flag, notes and errors will
476081Sphk * be timestamped and written to the given file.
486081Sphk *
496081Sphk * Exit status is 0 for success or 1 for indigestible input.  That is,
506081Sphk * 0 means the encode input pieces were decoded and stored, and 1 means
516081Sphk * some input was discarded.  If a delta fails to apply, this won't be
526081Sphk * reflected in the exit status.  In this case, the delta is left in
536081Sphk * 'deltadir'.
546081Sphk */
556290Sphkint
566081Sphkmain(int argc, char **argv)
576081Sphk    {
586081Sphk    char *log_file = NULL;
596081Sphk    int status = 0;
606695Sphk    int fork_ctm = 0;
616081Sphk
626081Sphk    err_prog_name(argv[0]);
636081Sphk
646695Sphk    OPTIONS("[-Df] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
656081Sphk	FLAG('D', delete_after)
666695Sphk	FLAG('f', fork_ctm)
6713019Speter	FLAG('v', apply_verbose)
686081Sphk	STRING('p', piece_dir)
696081Sphk	STRING('d', delta_dir)
706081Sphk	STRING('b', base_dir)
716081Sphk	STRING('l', log_file)
726081Sphk    ENDOPTS
736081Sphk
746290Sphk    if (delta_dir == NULL)
756081Sphk	usage();
766081Sphk
776457Sphk    if (piece_dir == NULL && (base_dir == NULL || argc > 1))
786290Sphk	usage();
796290Sphk
806081Sphk    if (log_file != NULL)
816081Sphk	err_set_log(log_file);
826081Sphk
836695Sphk    /*
846695Sphk     * Digest each file in turn, or just stdin if no files were given.
856695Sphk     */
866081Sphk    if (argc <= 1)
876081Sphk	{
886081Sphk	if (piece_dir != NULL)
896081Sphk	    status = read_piece(NULL);
906081Sphk	}
916081Sphk    else
926081Sphk	{
936081Sphk	while (*++argv != NULL)
946081Sphk	    status |= read_piece(*argv);
956081Sphk	}
966081Sphk
976695Sphk    /*
986695Sphk     * Maybe it's time to look for and apply completed deltas with ctm.
996695Sphk     *
1006695Sphk     * Shall we report back to sendmail immediately, and let a child do
1016695Sphk     * the work?  Sendmail will be waiting for us to complete, delaying
1026695Sphk     * other mail, and possibly some intermediate process (like MH slocal)
1036695Sphk     * will terminate us if we take too long!
1046695Sphk     *
1056695Sphk     * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
1066695Sphk     * Also, the child exit status is unimportant.
1076695Sphk     */
1086081Sphk    if (base_dir != NULL)
1096695Sphk	if (!fork_ctm || fork() == 0)
1106695Sphk	    apply_complete();
1116081Sphk
1126081Sphk    return status;
1136081Sphk    }
1146081Sphk
1156081Sphk
1166081Sphk/*
1176081Sphk * Construct the file name of a piece of a delta.
1186081Sphk */
1196081Sphk#define mk_piece_name(fn,d,p,n)	\
1207147Sphk    sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
1216081Sphk
1226081Sphk/*
1236081Sphk * Construct the file name of an assembled delta.
1246081Sphk */
1256081Sphk#define mk_delta_name(fn,d)	\
1266081Sphk    sprintf((fn), "%s/%s", delta_dir, (d))
1276081Sphk
1286081Sphk/*
1296081Sphk * If the next required delta is now present, let ctm lunch on it and any
1306081Sphk * contiguous deltas.
1316081Sphk */
1326081Sphkvoid
1336081Sphkapply_complete()
1346081Sphk    {
1356081Sphk    int i, dn;
1366695Sphk    int lfd;
1376081Sphk    FILE *fp, *ctm;
1386081Sphk    struct stat sb;
1396081Sphk    char class[20];
1406081Sphk    char delta[30];
1416081Sphk    char junk[2];
1426695Sphk    char fname[PATH_MAX];
1436695Sphk    char here[PATH_MAX];
1446695Sphk    char buf[PATH_MAX*2];
1456081Sphk
1466695Sphk    /*
1476695Sphk     * Grab a lock on the ctm mutex file so that we can be sure we are
1486695Sphk     * working alone, not fighting another ctm_rmail!
1496695Sphk     */
1506695Sphk    strcpy(fname, delta_dir);
1516695Sphk    strcat(fname, "/.mutex_apply");
1526695Sphk    if ((lfd = lock_file(fname)) < 0)
1536695Sphk	return;
1546695Sphk
1556695Sphk    /*
1566695Sphk     * Find out which delta ctm needs next.
1576695Sphk     */
1586081Sphk    sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
1596081Sphk    if ((fp = fopen(fname, "r")) == NULL)
1606695Sphk	{
1616695Sphk	close(lfd);
1626081Sphk	return;
1636695Sphk	}
1646081Sphk
1656081Sphk    i = fscanf(fp, "%s %d %c", class, &dn, junk);
1666081Sphk    fclose(fp);
1676081Sphk    if (i != 2)
1686695Sphk	{
1696695Sphk	close(lfd);
1706081Sphk	return;
1716695Sphk	}
1726081Sphk
1736081Sphk    /*
1746081Sphk     * We might need to convert the delta filename to an absolute pathname.
1756081Sphk     */
1766081Sphk    here[0] = '\0';
1776081Sphk    if (delta_dir[0] != '/')
1786081Sphk	{
1796081Sphk	getcwd(here, sizeof(here)-1);
1806081Sphk	i = strlen(here) - 1;
1816081Sphk	if (i >= 0 && here[i] != '/')
1826081Sphk	    {
1836081Sphk	    here[++i] = '/';
1846081Sphk	    here[++i] = '\0';
1856081Sphk	    }
1866081Sphk	}
1876081Sphk
1886081Sphk    /*
1896081Sphk     * Keep applying deltas until we run out or something bad happens.
1906081Sphk     */
1916081Sphk    for (;;)
1926081Sphk	{
1936081Sphk	sprintf(delta, "%s.%04d.gz", class, ++dn);
1946081Sphk	mk_delta_name(fname, delta);
1956081Sphk
1966081Sphk	if (stat(fname, &sb) < 0)
1976695Sphk	    break;
1986081Sphk
19913019Speter	sprintf(buf, "(cd %s && ctm %s%s%s) 2>&1", base_dir,
20013019Speter				apply_verbose ? "-v " : "", here, fname);
2016081Sphk	if ((ctm = popen(buf, "r")) == NULL)
2026081Sphk	    {
2036081Sphk	    err("ctm failed to apply %s", delta);
2046695Sphk	    break;
2056081Sphk	    }
2066081Sphk
2076081Sphk	while (fgets(buf, sizeof(buf), ctm) != NULL)
2086081Sphk	    {
2096081Sphk	    i = strlen(buf) - 1;
2106081Sphk	    if (i >= 0 && buf[i] == '\n')
2116081Sphk		buf[i] = '\0';
2126081Sphk	    err("ctm: %s", buf);
2136081Sphk	    }
2146081Sphk
2156081Sphk	if (pclose(ctm) != 0)
2166081Sphk	    {
2176081Sphk	    err("ctm failed to apply %s", delta);
2186695Sphk	    break;
2196081Sphk	    }
2206081Sphk
2216081Sphk	if (delete_after)
2226081Sphk	    unlink(fname);
2236081Sphk
2246081Sphk	err("%s applied%s", delta, delete_after ? " and deleted" : "");
2256081Sphk	}
2266695Sphk
2276695Sphk    /*
2286695Sphk     * Closing the lock file clears the lock.
2296695Sphk     */
2306695Sphk    close(lfd);
2316081Sphk    }
2326081Sphk
2336081Sphk
2346081Sphk/*
2356081Sphk * This cheap plastic checksum effectively rotates our checksum-so-far
2366081Sphk * left one, then adds the character.  We only want 16 bits of it, and
2376081Sphk * don't care what happens to the rest.  It ain't much, but it's small.
2386081Sphk */
2396081Sphk#define add_ck(sum,x)	\
2406081Sphk    ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
2416081Sphk
2426081Sphk
2436081Sphk/*
2446081Sphk * Decode the data between BEGIN and END, and stash it in the staging area.
2456081Sphk * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
2466081Sphk * If we have all pieces of a delta, combine them.  Returns 0 on success,
2476081Sphk * and 1 for any sort of failure.
2486081Sphk */
2496081Sphkint
2506081Sphkread_piece(char *input_file)
2516081Sphk    {
2526081Sphk    int status = 0;
2536290Sphk    FILE *ifp, *ofp = 0;
2546081Sphk    int decoding = 0;
2556695Sphk    int got_one = 0;
2566081Sphk    int line_no = 0;
2576081Sphk    int i, n;
2586081Sphk    int pce, npieces;
2596081Sphk    unsigned claimed_cksum;
2606290Sphk    unsigned short cksum = 0;
2616081Sphk    char out_buf[200];
2626081Sphk    char line[200];
2636081Sphk    char delta[30];
2646695Sphk    char pname[PATH_MAX];
2656695Sphk    char tname[PATH_MAX];
2666081Sphk    char junk[2];
2676081Sphk
2686081Sphk    ifp = stdin;
2696081Sphk    if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
2706081Sphk	{
2716081Sphk	err("cannot open '%s' for reading", input_file);
2726081Sphk	return 1;
2736081Sphk	}
2746081Sphk
2756081Sphk    while (fgets(line, sizeof(line), ifp) != NULL)
2766081Sphk	{
2776081Sphk	line_no++;
2786081Sphk
2796081Sphk	/*
28013015Sphk	 * Remove all trailing white space.
28113015Sphk	 */
28213015Sphk	i = strlen(line) - 1;
28313015Sphk	while (i > 0 && isspace(line[i]))
28413028Sphk		line[i--] = '\0';
28513015Sphk
28613015Sphk	/*
2876081Sphk	 * Look for the beginning of an encoded piece.
2886081Sphk	 */
2896081Sphk	if (!decoding)
2906081Sphk	    {
2916290Sphk	    char *s;
2926081Sphk
2936457Sphk	    if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
2946290Sphk		    delta, &pce, &npieces, junk) != 3)
2956290Sphk		continue;
2966081Sphk
2976290Sphk	    while ((s = strchr(delta, '/')) != NULL)
2986290Sphk		*s = '_';
2996081Sphk
3006695Sphk	    got_one++;
3016695Sphk	    strcpy(tname, piece_dir);
3026695Sphk	    strcat(tname, "/p.XXXXXX");
3036695Sphk	    if (mktemp(tname) == NULL)
3046695Sphk		{
3056695Sphk		err("*mktemp: '%s'", tname);
3066695Sphk		status++;
3076695Sphk		continue;
3086695Sphk		}
3096658Sphk	    if ((ofp = fopen(tname, "w")) == NULL)
3106290Sphk		{
3116658Sphk		err("cannot open '%s' for writing", tname);
3126290Sphk		status++;
3136290Sphk		continue;
3146081Sphk		}
3156290Sphk
3166290Sphk	    cksum = 0xffff;
3176290Sphk	    decoding++;
3186081Sphk	    continue;
3196081Sphk	    }
3206081Sphk
3216081Sphk	/*
3226081Sphk	 * We are decoding.  Stop if we see the end flag.
3236081Sphk	 */
3246081Sphk	if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
3256081Sphk	    {
3266081Sphk	    int e;
3276081Sphk
3286081Sphk	    decoding = 0;
3296081Sphk
3306081Sphk	    fflush(ofp);
3316081Sphk	    e = ferror(ofp);
3326081Sphk	    fclose(ofp);
3336081Sphk
3346081Sphk	    if (e)
3356658Sphk		err("error writing %s", tname);
3366081Sphk
3376081Sphk	    if (cksum != claimed_cksum)
3386081Sphk		err("checksum: read %d, calculated %d", claimed_cksum, cksum);
3396081Sphk
3406081Sphk	    if (e || cksum != claimed_cksum)
3416081Sphk		{
3426081Sphk		err("%s %d/%d discarded", delta, pce, npieces);
3436658Sphk		unlink(tname);
3446081Sphk		status++;
3456081Sphk		continue;
3466081Sphk		}
3476081Sphk
3486695Sphk	    mk_piece_name(pname, delta, pce, npieces);
3496658Sphk	    if (rename(tname, pname) < 0)
3506658Sphk		{
3516695Sphk		err("*rename: '%s' to '%s'", tname, pname);
3526695Sphk		err("%s %d/%d lost!", delta, pce, npieces);
3536658Sphk		unlink(tname);
3546658Sphk		status++;
3556658Sphk		continue;
3566658Sphk		}
3576658Sphk
3586081Sphk	    err("%s %d/%d stored", delta, pce, npieces);
3596081Sphk
3606081Sphk	    if (!combine_if_complete(delta, pce, npieces))
3616081Sphk		status++;
3626081Sphk	    continue;
3636081Sphk	    }
3646081Sphk
3656081Sphk	/*
3666081Sphk	 * Must be a line of encoded data.  Decode it, sum it, and save it.
3676081Sphk	 */
3686081Sphk	n = decode_line(line, out_buf);
3696695Sphk	if (n <= 0)
3706081Sphk	    {
3716081Sphk	    err("line %d: illegal character: '%c'", line_no, line[-n]);
3726081Sphk	    err("%s %d/%d discarded", delta, pce, npieces);
3736081Sphk
3746081Sphk	    fclose(ofp);
3756695Sphk	    unlink(tname);
3766081Sphk
3776081Sphk	    status++;
3786081Sphk	    decoding = 0;
3796081Sphk	    continue;
3806081Sphk	    }
3816081Sphk
3826081Sphk	for (i = 0; i < n; i++)
3836081Sphk	    add_ck(cksum, out_buf[i]);
3846081Sphk
3856081Sphk	fwrite(out_buf, sizeof(char), n, ofp);
3866081Sphk	}
3876081Sphk
3886081Sphk    if (decoding)
3896081Sphk	{
3906081Sphk	err("truncated file");
3916081Sphk	err("%s %d/%d discarded", delta, pce, npieces);
3926081Sphk
3936081Sphk	fclose(ofp);
3946695Sphk	unlink(tname);
3956081Sphk
3966081Sphk	status++;
3976081Sphk	}
3986081Sphk
3996081Sphk    if (ferror(ifp))
4006081Sphk	{
4016081Sphk	err("error reading %s", input_file == NULL ? "stdin" : input_file);
4026081Sphk	status++;
4036081Sphk	}
4046081Sphk
4056081Sphk    if (input_file != NULL)
4066081Sphk	fclose(ifp);
4076081Sphk
4086695Sphk    if (!got_one)
4096695Sphk	{
4106695Sphk	err("message contains no delta");
4116695Sphk	status++;
4126695Sphk	}
4136695Sphk
4146081Sphk    return (status != 0);
4156081Sphk    }
4166081Sphk
4176081Sphk
4186081Sphk/*
4196081Sphk * Put the pieces together to form a delta, if they are all present.
4206081Sphk * Returns 1 on success (even if we didn't do anything), and 0 on failure.
4216081Sphk */
4226081Sphkint
4236081Sphkcombine_if_complete(char *delta, int pce, int npieces)
4246081Sphk    {
4256695Sphk    int i, e;
4266695Sphk    int lfd;
4276081Sphk    struct stat sb;
4286695Sphk    char pname[PATH_MAX];
4296695Sphk    char dname[PATH_MAX];
4306695Sphk    char tname[PATH_MAX];
4316081Sphk
4326081Sphk    /*
4336695Sphk     * We can probably just rename() it into place if it is a small delta.
4346081Sphk     */
4356695Sphk    if (npieces == 1)
4366081Sphk	{
4376695Sphk	mk_delta_name(dname, delta);
4386695Sphk	mk_piece_name(pname, delta, 1, 1);
4396695Sphk	if (rename(pname, dname) == 0)
4406695Sphk	    {
4416695Sphk	    err("%s complete", delta);
4426081Sphk	    return 1;
4436695Sphk	    }
4446081Sphk	}
4456081Sphk
4466695Sphk    /*
4476695Sphk     * Grab a lock on the reassembly mutex file so that we can be sure we are
4486695Sphk     * working alone, not fighting another ctm_rmail!
4496695Sphk     */
4506695Sphk    strcpy(tname, delta_dir);
4516695Sphk    strcat(tname, "/.mutex_build");
4526695Sphk    if ((lfd = lock_file(tname)) < 0)
4536695Sphk	return 0;
4546081Sphk
4556081Sphk    /*
4566695Sphk     * Are all of the pieces present?  Of course the current one is,
4576695Sphk     * unless all pieces are missing because another ctm_rmail has
4586695Sphk     * processed them already.
4596081Sphk     */
4606695Sphk    for (i = 1; i <= npieces; i++)
4616081Sphk	{
4626695Sphk	if (i == pce)
4636695Sphk	    continue;
4646695Sphk	mk_piece_name(pname, delta, i, npieces);
4656695Sphk	if (stat(pname, &sb) < 0)
4666081Sphk	    {
4676695Sphk	    close(lfd);
4686081Sphk	    return 1;
4696081Sphk	    }
4706081Sphk	}
4716081Sphk
4726695Sphk    /*
4736695Sphk     * Stick them together.  Let combine() use our file name buffers, since
4746695Sphk     * we're such good buddies. :-)
4756695Sphk     */
4766695Sphk    e = combine(delta, npieces, dname, pname, tname);
4776695Sphk    close(lfd);
4786695Sphk    return e;
4796695Sphk    }
4806695Sphk
4816695Sphk
4826695Sphk/*
4836695Sphk * Put the pieces together to form a delta.
4846695Sphk * Returns 1 on success, and 0 on failure.
4856695Sphk * Note: dname, pname, and tname are room for some file names that just
4866695Sphk * happened to by lying around in the calling routine.  Waste not, want not!
4876695Sphk */
4886695Sphkint
4896695Sphkcombine(char *delta, int npieces, char *dname, char *pname, char *tname)
4906695Sphk    {
4916695Sphk    FILE *dfp, *pfp;
4926695Sphk    int i, n, e;
4936695Sphk    char buf[BUFSIZ];
4946695Sphk
4956695Sphk    strcpy(tname, delta_dir);
4966695Sphk    strcat(tname, "/d.XXXXXX");
4976695Sphk    if (mktemp(tname) == NULL)
4986081Sphk	{
4996695Sphk	err("*mktemp: '%s'", tname);
5006081Sphk	return 0;
5016081Sphk	}
5026695Sphk    if ((dfp = fopen(tname, "w")) == NULL)
5036695Sphk	{
5046695Sphk	err("cannot open '%s' for writing", tname);
5056695Sphk	return 0;
5066695Sphk	}
5076081Sphk
5086081Sphk    /*
5096695Sphk     * Reconstruct the delta by reading each piece in order.
5106081Sphk     */
5116081Sphk    for (i = 1; i <= npieces; i++)
5126081Sphk	{
5136081Sphk	mk_piece_name(pname, delta, i, npieces);
5146081Sphk	if ((pfp = fopen(pname, "r")) == NULL)
5156081Sphk	    {
5166081Sphk	    err("cannot open '%s' for reading", pname);
5176081Sphk	    fclose(dfp);
5186695Sphk	    unlink(tname);
5196081Sphk	    return 0;
5206081Sphk	    }
5216695Sphk	while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
5226695Sphk	    fwrite(buf, sizeof(char), n, dfp);
5236695Sphk	e = ferror(pfp);
5246081Sphk	fclose(pfp);
5256695Sphk	if (e)
5266695Sphk	    {
5276695Sphk	    err("error reading '%s'", pname);
5286695Sphk	    fclose(dfp);
5296695Sphk	    unlink(tname);
5306695Sphk	    return 0;
5316695Sphk	    }
5326081Sphk	}
5336081Sphk    fflush(dfp);
5346695Sphk    e = ferror(dfp);
5356695Sphk    fclose(dfp);
5366695Sphk    if (e)
5376081Sphk	{
5386695Sphk	err("error writing '%s'", tname);
5396695Sphk	unlink(tname);
5406081Sphk	return 0;
5416081Sphk	}
5426081Sphk
5436695Sphk    mk_delta_name(dname, delta);
5446695Sphk    if (rename(tname, dname) < 0)
5456695Sphk	{
5466695Sphk	err("*rename: '%s' to '%s'", tname, dname);
5476695Sphk	unlink(tname);
5486695Sphk	return 0;
5496695Sphk	}
5506695Sphk
5516081Sphk    /*
5526081Sphk     * Throw the pieces away.
5536081Sphk     */
5546081Sphk    for (i = 1; i <= npieces; i++)
5556081Sphk	{
5566081Sphk	mk_piece_name(pname, delta, i, npieces);
5576695Sphk	if (unlink(pname) < 0)
5586695Sphk	    err("*unlink: '%s'", pname);
5596081Sphk	}
5606081Sphk
5616081Sphk    err("%s complete", delta);
5626081Sphk    return 1;
5636081Sphk    }
5646081Sphk
5656081Sphk
5666081Sphk/*
5676081Sphk * MIME BASE64 decode table.
5686081Sphk */
5696081Sphkstatic unsigned char from_b64[0x80] =
5708857Srgrimes    {
5716081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5726081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5736081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5746081Sphk    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5758857Srgrimes    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5766081Sphk    0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
5776081Sphk    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
5786081Sphk    0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
5796081Sphk    0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
5806081Sphk    0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
5816081Sphk    0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
5826081Sphk    0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
5836081Sphk    0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
5846081Sphk    0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
5856081Sphk    0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
5866081Sphk    0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
5876081Sphk    };
5886081Sphk
5896081Sphk
5906081Sphk/*
5916081Sphk * Decode a line of ASCII into binary.  Returns the number of bytes in
5926081Sphk * the output buffer, or < 0 on indigestable input.  Error output is
5936081Sphk * the negative of the index of the inedible character.
5946081Sphk */
5956081Sphkint
5966081Sphkdecode_line(char *line, char *out_buf)
5976081Sphk    {
5986081Sphk    unsigned char *ip = (unsigned char *)line;
5996081Sphk    unsigned char *op = (unsigned char *)out_buf;
6006081Sphk    unsigned long bits;
6016081Sphk    unsigned x;
6026081Sphk
6036081Sphk    for (;;)
6046081Sphk	{
6056081Sphk	if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
6066081Sphk	    break;
6076081Sphk	bits = x << 18;
6086081Sphk	ip++;
6096081Sphk	if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6106081Sphk	    {
6116081Sphk	    bits |= x << 12;
6126081Sphk	    *op++ = bits >> 16;
6136081Sphk	    ip++;
6146081Sphk	    if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6156081Sphk		{
6166081Sphk		bits |= x << 6;
6176081Sphk		*op++ = bits >> 8;
6186081Sphk		ip++;
6196081Sphk		if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
6206081Sphk		    {
6216081Sphk		    bits |= x;
6226081Sphk		    *op++ = bits;
6236081Sphk		    ip++;
6246081Sphk		    }
6256081Sphk		}
6266081Sphk	    }
6276081Sphk	}
6286081Sphk
6296081Sphk    if (*ip == '\0' || *ip == '\n')
6306081Sphk	return op - (unsigned char *)out_buf;
6316081Sphk    else
6326081Sphk	return -(ip - (unsigned char *)line);
6336081Sphk    }
6346695Sphk
6356695Sphk
6366695Sphk/*
6376695Sphk * Create and lock the given file.
6386695Sphk *
6396695Sphk * Clearing the lock is as simple as closing the file descriptor we return.
6406695Sphk */
6416695Sphkint
6426695Sphklock_file(char *name)
6436695Sphk    {
6446695Sphk    int lfd;
6456695Sphk
6466695Sphk    if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
6476695Sphk	{
6486695Sphk	err("*open: '%s'", name);
6496695Sphk	return -1;
6506695Sphk	}
6516695Sphk    if (flock(lfd, LOCK_EX) < 0)
6526695Sphk	{
6536695Sphk	close(lfd);
6546695Sphk	err("*flock: '%s'", name);
6556695Sphk	return -1;
6566695Sphk	}
6576695Sphk    return lfd;
6586695Sphk    }
659