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