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