ctm_rmail.c revision 7147
192654Sjeff/* 2139318Sbmilekic * Accept one (or more) ASCII encoded chunks that together make a compressed 3139318Sbmilekic * CTM delta. Decode them and reconstruct the deltas. Any completed 4139318Sbmilekic * deltas may be passed to ctm for unpacking. 5139318Sbmilekic * 692654Sjeff * Author: Stephen McKay 792654Sjeff * 892654Sjeff * NOTICE: This is free software. I hope you get some use from this program. 992654Sjeff * In return you should think about all the nice people who give away software. 1092654Sjeff * Maybe you should write some free software too. 1192654Sjeff */ 1292654Sjeff 1392654Sjeff#include <stdio.h> 1492654Sjeff#include <stdlib.h> 1592654Sjeff#include <string.h> 1692654Sjeff#include <errno.h> 1792654Sjeff#include <unistd.h> 1892654Sjeff#include <sys/types.h> 1992654Sjeff#include <sys/stat.h> 2092654Sjeff#include <fcntl.h> 2192654Sjeff#include <limits.h> 2292654Sjeff#include "error.h" 2392654Sjeff#include "options.h" 2492654Sjeff 2592654Sjeff#define CTM_STATUS ".ctm_status" 2692654Sjeff 2792654Sjeffchar *piece_dir = NULL; /* Where to store pieces of deltas. */ 2892654Sjeffchar *delta_dir = NULL; /* Where to store completed deltas. */ 2992654Sjeffchar *base_dir = NULL; /* The tree to apply deltas to. */ 3092654Sjeffint delete_after = 0; /* Delete deltas after ctm applies them. */ 3192654Sjeff 3292654Sjeffvoid apply_complete(void); 3392654Sjeffint read_piece(char *input_file); 3492654Sjeffint combine_if_complete(char *delta, int pce, int npieces); 3592654Sjeffint combine(char *delta, int npieces, char *dname, char *pname, char *tname); 3692654Sjeffint decode_line(char *line, char *out_buf); 3792654Sjeffint lock_file(char *name); 3892654Sjeff 3992654Sjeff/* 4092654Sjeff * If given a '-p' flag, read encoded delta pieces from stdin or file 4192654Sjeff * arguments, decode them and assemble any completed deltas. If given 4292654Sjeff * a '-b' flag, pass any completed deltas to 'ctm' for application to 4392654Sjeff * the source tree. The '-d' flag is mandatory, but either of '-p' or 4492654Sjeff * '-b' can be omitted. If given the '-l' flag, notes and errors will 4592654Sjeff * be timestamped and written to the given file. 4692654Sjeff * 4792654Sjeff * Exit status is 0 for success or 1 for indigestible input. That is, 48129906Sbmilekic * 0 means the encode input pieces were decoded and stored, and 1 means 4992654Sjeff * some input was discarded. If a delta fails to apply, this won't be 5092654Sjeff * reflected in the exit status. In this case, the delta is left in 5192654Sjeff * 'deltadir'. 5292654Sjeff */ 5392654Sjeffint 5492654Sjeffmain(int argc, char **argv) 5592654Sjeff { 5692654Sjeff char *log_file = NULL; 5792654Sjeff int status = 0; 5892654Sjeff int fork_ctm = 0; 59132987Sgreen 6092654Sjeff err_prog_name(argv[0]); 6192654Sjeff 62132987Sgreen OPTIONS("[-Df] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]") 63132987Sgreen FLAG('D', delete_after) 6492654Sjeff FLAG('f', fork_ctm) 6592654Sjeff STRING('p', piece_dir) 6692654Sjeff STRING('d', delta_dir) 67105689Ssheldonh STRING('b', base_dir) 6892654Sjeff STRING('l', log_file) 69132987Sgreen ENDOPTS 7092654Sjeff 7192654Sjeff if (delta_dir == NULL) 7292654Sjeff usage(); 7392654Sjeff 7492654Sjeff if (piece_dir == NULL && (base_dir == NULL || argc > 1)) 7592654Sjeff usage(); 7692654Sjeff 7792654Sjeff if (log_file != NULL) 7892654Sjeff err_set_log(log_file); 7992654Sjeff 8092654Sjeff /* 8192654Sjeff * Digest each file in turn, or just stdin if no files were given. 8292654Sjeff */ 8392654Sjeff if (argc <= 1) 8492654Sjeff { 8592654Sjeff if (piece_dir != NULL) 8692654Sjeff status = read_piece(NULL); 8792654Sjeff } 8892654Sjeff else 8992654Sjeff { 9092654Sjeff while (*++argv != NULL) 9192654Sjeff status |= read_piece(*argv); 9292654Sjeff } 9392654Sjeff 9492654Sjeff /* 95132987Sgreen * Maybe it's time to look for and apply completed deltas with ctm. 9692654Sjeff * 9792654Sjeff * Shall we report back to sendmail immediately, and let a child do 98132987Sgreen * the work? Sendmail will be waiting for us to complete, delaying 99132987Sgreen * other mail, and possibly some intermediate process (like MH slocal) 10092654Sjeff * will terminate us if we take too long! 10192654Sjeff * 10292654Sjeff * If fork() fails, it's unlikely we'll be able to run ctm, so give up. 10392654Sjeff * Also, the child exit status is unimportant. 10492654Sjeff */ 105132987Sgreen if (base_dir != NULL) 10692654Sjeff if (!fork_ctm || fork() == 0) 10792654Sjeff apply_complete(); 10892654Sjeff 10992654Sjeff return status; 11092654Sjeff } 11192654Sjeff 11292654Sjeff 11392654Sjeff/* 11492654Sjeff * Construct the file name of a piece of a delta. 11592654Sjeff */ 11692654Sjeff#define mk_piece_name(fn,d,p,n) \ 11792654Sjeff sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n)) 11892654Sjeff 11992654Sjeff/* 12092654Sjeff * Construct the file name of an assembled delta. 12192654Sjeff */ 12292654Sjeff#define mk_delta_name(fn,d) \ 12392654Sjeff sprintf((fn), "%s/%s", delta_dir, (d)) 12492654Sjeff 12592654Sjeff/* 12692654Sjeff * If the next required delta is now present, let ctm lunch on it and any 12792654Sjeff * contiguous deltas. 12892654Sjeff */ 12992654Sjeffvoid 13092654Sjeffapply_complete() 13192654Sjeff { 13292654Sjeff int i, dn; 13392654Sjeff int lfd; 13492654Sjeff FILE *fp, *ctm; 135105689Ssheldonh struct stat sb; 13692654Sjeff char class[20]; 13792654Sjeff char delta[30]; 13892654Sjeff char junk[2]; 13992654Sjeff char fname[PATH_MAX]; 14092654Sjeff char here[PATH_MAX]; 14192654Sjeff char buf[PATH_MAX*2]; 14292654Sjeff 14392654Sjeff /* 14492654Sjeff * Grab a lock on the ctm mutex file so that we can be sure we are 14592654Sjeff * working alone, not fighting another ctm_rmail! 14692654Sjeff */ 14792654Sjeff strcpy(fname, delta_dir); 14892654Sjeff strcat(fname, "/.mutex_apply"); 14992654Sjeff if ((lfd = lock_file(fname)) < 0) 15092654Sjeff return; 15192654Sjeff 15292654Sjeff /* 15392654Sjeff * Find out which delta ctm needs next. 15492654Sjeff */ 15592654Sjeff sprintf(fname, "%s/%s", base_dir, CTM_STATUS); 15692654Sjeff if ((fp = fopen(fname, "r")) == NULL) 15792654Sjeff { 15892654Sjeff close(lfd); 15992654Sjeff return; 16092654Sjeff } 16192654Sjeff 16292654Sjeff i = fscanf(fp, "%s %d %c", class, &dn, junk); 16392654Sjeff fclose(fp); 16492654Sjeff if (i != 2) 16592654Sjeff { 16695925Sarr close(lfd); 16792654Sjeff return; 16892654Sjeff } 16992654Sjeff 170120223Sjeff /* 171129906Sbmilekic * We might need to convert the delta filename to an absolute pathname. 172129906Sbmilekic */ 173129906Sbmilekic here[0] = '\0'; 174129906Sbmilekic if (delta_dir[0] != '/') 175129906Sbmilekic { 176129906Sbmilekic getcwd(here, sizeof(here)-1); 177129906Sbmilekic i = strlen(here) - 1; 178129906Sbmilekic if (i >= 0 && here[i] != '/') 179129906Sbmilekic { 180129906Sbmilekic here[++i] = '/'; 181129906Sbmilekic here[++i] = '\0'; 182129906Sbmilekic } 183129906Sbmilekic } 184129906Sbmilekic 185129906Sbmilekic /* 186129906Sbmilekic * Keep applying deltas until we run out or something bad happens. 187129906Sbmilekic */ 188129906Sbmilekic for (;;) 189129906Sbmilekic { 190129906Sbmilekic sprintf(delta, "%s.%04d.gz", class, ++dn); 191129906Sbmilekic mk_delta_name(fname, delta); 192129906Sbmilekic 193129906Sbmilekic if (stat(fname, &sb) < 0) 194129913Sbmilekic break; 195129913Sbmilekic 196129913Sbmilekic sprintf(buf, "(cd %s && ctm %s%s) 2>&1", base_dir, here, fname); 197129906Sbmilekic if ((ctm = popen(buf, "r")) == NULL) 198129906Sbmilekic { 199129906Sbmilekic err("ctm failed to apply %s", delta); 200129906Sbmilekic break; 201129906Sbmilekic } 202129906Sbmilekic 203129906Sbmilekic while (fgets(buf, sizeof(buf), ctm) != NULL) 204129906Sbmilekic { 205129906Sbmilekic i = strlen(buf) - 1; 206120223Sjeff if (i >= 0 && buf[i] == '\n') 207120223Sjeff buf[i] = '\0'; 208120223Sjeff err("ctm: %s", buf); 209120223Sjeff } 210120223Sjeff 21192654Sjeff if (pclose(ctm) != 0) 21292654Sjeff { 21392654Sjeff err("ctm failed to apply %s", delta); 21492654Sjeff break; 21592654Sjeff } 21692654Sjeff 21792654Sjeff if (delete_after) 21892654Sjeff unlink(fname); 21995758Sjeff 220103531Sjeff err("%s applied%s", delta, delete_after ? " and deleted" : ""); 221103531Sjeff } 222103531Sjeff 223103531Sjeff /* 224103531Sjeff * Closing the lock file clears the lock. 225103531Sjeff */ 226103531Sjeff close(lfd); 227103531Sjeff } 228129906Sbmilekic 229129906Sbmilekic 230129906Sbmilekic/* 23192654Sjeff * This cheap plastic checksum effectively rotates our checksum-so-far 23292654Sjeff * left one, then adds the character. We only want 16 bits of it, and 23392654Sjeff * don't care what happens to the rest. It ain't much, but it's small. 23492654Sjeff */ 23592654Sjeff#define add_ck(sum,x) \ 23692654Sjeff ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0)) 23792654Sjeff 23892654Sjeff 23992654Sjeff/* 24092654Sjeff * Decode the data between BEGIN and END, and stash it in the staging area. 24194161Sjeff * Multiple pieces can be present in a single file, bracketed by BEGIN/END. 24292654Sjeff * If we have all pieces of a delta, combine them. Returns 0 on success, 24392654Sjeff * and 1 for any sort of failure. 24492654Sjeff */ 24592654Sjeffint 24692654Sjeffread_piece(char *input_file) 24794161Sjeff { 24892654Sjeff int status = 0; 24992654Sjeff FILE *ifp, *ofp = 0; 25092654Sjeff int decoding = 0; 25192654Sjeff int got_one = 0; 25292654Sjeff int line_no = 0; 25392654Sjeff int i, n; 25492654Sjeff int pce, npieces; 25595766Sjeff unsigned claimed_cksum; 25692654Sjeff unsigned short cksum = 0; 25792654Sjeff char out_buf[200]; 25892654Sjeff char line[200]; 259111119Simp char delta[30]; 26092654Sjeff char pname[PATH_MAX]; 26192654Sjeff char tname[PATH_MAX]; 26292654Sjeff char junk[2]; 26395766Sjeff 26492654Sjeff ifp = stdin; 26592654Sjeff if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL) 26692654Sjeff { 26792654Sjeff err("cannot open '%s' for reading", input_file); 26892654Sjeff return 1; 26992654Sjeff } 27092654Sjeff 27195766Sjeff while (fgets(line, sizeof(line), ifp) != NULL) 27292654Sjeff { 27392654Sjeff line_no++; 27495766Sjeff 27592654Sjeff /* 27695766Sjeff * Look for the beginning of an encoded piece. 27792654Sjeff */ 27892654Sjeff if (!decoding) 27992654Sjeff { 28092654Sjeff char *s; 28192654Sjeff 28292654Sjeff if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c", 28392654Sjeff delta, &pce, &npieces, junk) != 3) 28492654Sjeff continue; 28592654Sjeff 28692654Sjeff while ((s = strchr(delta, '/')) != NULL) 28792654Sjeff *s = '_'; 28892654Sjeff 28992654Sjeff got_one++; 29092654Sjeff strcpy(tname, piece_dir); 29192654Sjeff strcat(tname, "/p.XXXXXX"); 29292654Sjeff if (mktemp(tname) == NULL) 29392654Sjeff { 29492654Sjeff err("*mktemp: '%s'", tname); 29592654Sjeff status++; 29692654Sjeff continue; 29792654Sjeff } 29892654Sjeff if ((ofp = fopen(tname, "w")) == NULL) 29992654Sjeff { 30092654Sjeff err("cannot open '%s' for writing", tname); 30192654Sjeff status++; 30292654Sjeff continue; 30392654Sjeff } 304100326Smarkm 30592654Sjeff cksum = 0xffff; 30692654Sjeff decoding++; 30792654Sjeff continue; 30892654Sjeff } 30992654Sjeff 31092654Sjeff /* 31192654Sjeff * We are decoding. Stop if we see the end flag. 31292654Sjeff */ 31392654Sjeff if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1) 31492654Sjeff { 31592654Sjeff int e; 31692654Sjeff 31792654Sjeff decoding = 0; 31892654Sjeff 31992654Sjeff fflush(ofp); 32092654Sjeff e = ferror(ofp); 32192654Sjeff fclose(ofp); 32292654Sjeff 32392654Sjeff if (e) 32492654Sjeff err("error writing %s", tname); 32592654Sjeff 32692654Sjeff if (cksum != claimed_cksum) 32792654Sjeff err("checksum: read %d, calculated %d", claimed_cksum, cksum); 32892654Sjeff 32992654Sjeff if (e || cksum != claimed_cksum) 33092654Sjeff { 33192654Sjeff err("%s %d/%d discarded", delta, pce, npieces); 33292654Sjeff unlink(tname); 33392654Sjeff status++; 33492654Sjeff continue; 33592654Sjeff } 33692654Sjeff 33792654Sjeff mk_piece_name(pname, delta, pce, npieces); 33892654Sjeff if (rename(tname, pname) < 0) 33992654Sjeff { 34092654Sjeff err("*rename: '%s' to '%s'", tname, pname); 34192654Sjeff err("%s %d/%d lost!", delta, pce, npieces); 34292654Sjeff unlink(tname); 34392654Sjeff status++; 34492654Sjeff continue; 34592654Sjeff } 34692654Sjeff 34792654Sjeff err("%s %d/%d stored", delta, pce, npieces); 34892654Sjeff 34992654Sjeff if (!combine_if_complete(delta, pce, npieces)) 35092654Sjeff status++; 35192654Sjeff continue; 35292654Sjeff } 35392654Sjeff 35492654Sjeff /* 35592654Sjeff * Must be a line of encoded data. Decode it, sum it, and save it. 35692654Sjeff */ 35792654Sjeff n = decode_line(line, out_buf); 35892654Sjeff if (n <= 0) 35992654Sjeff { 36092654Sjeff err("line %d: illegal character: '%c'", line_no, line[-n]); 36192654Sjeff err("%s %d/%d discarded", delta, pce, npieces); 36292654Sjeff 36392654Sjeff fclose(ofp); 36492654Sjeff unlink(tname); 365103531Sjeff 36692654Sjeff status++; 36792654Sjeff decoding = 0; 36892654Sjeff continue; 36992654Sjeff } 37092654Sjeff 371103531Sjeff for (i = 0; i < n; i++) 37292654Sjeff add_ck(cksum, out_buf[i]); 37392654Sjeff 374103531Sjeff fwrite(out_buf, sizeof(char), n, ofp); 37592654Sjeff } 37692654Sjeff 37792654Sjeff if (decoding) 37892654Sjeff { 37992654Sjeff err("truncated file"); 38092654Sjeff err("%s %d/%d discarded", delta, pce, npieces); 38192654Sjeff 38292654Sjeff fclose(ofp); 38392654Sjeff unlink(tname); 38492654Sjeff 38592654Sjeff status++; 38692654Sjeff } 38792654Sjeff 38892654Sjeff if (ferror(ifp)) 38992654Sjeff { 39092654Sjeff err("error reading %s", input_file == NULL ? "stdin" : input_file); 39192654Sjeff status++; 39292654Sjeff } 39392654Sjeff 39492654Sjeff if (input_file != NULL) 39592654Sjeff fclose(ifp); 39692654Sjeff 39792654Sjeff if (!got_one) 39892654Sjeff { 39992654Sjeff err("message contains no delta"); 40092654Sjeff status++; 40192654Sjeff } 40292654Sjeff 40392654Sjeff return (status != 0); 40492654Sjeff } 40592654Sjeff 40692654Sjeff 40792654Sjeff/* 40892654Sjeff * Put the pieces together to form a delta, if they are all present. 40992758Sjeff * Returns 1 on success (even if we didn't do anything), and 0 on failure. 41092758Sjeff */ 41192758Sjeffint 41292758Sjeffcombine_if_complete(char *delta, int pce, int npieces) 41392758Sjeff { 41492758Sjeff int i, e; 41592758Sjeff int lfd; 41692758Sjeff struct stat sb; 41792758Sjeff char pname[PATH_MAX]; 41892758Sjeff char dname[PATH_MAX]; 41992654Sjeff char tname[PATH_MAX]; 42092654Sjeff 421129906Sbmilekic /* 422129906Sbmilekic * We can probably just rename() it into place if it is a small delta. 423129906Sbmilekic */ 424129906Sbmilekic if (npieces == 1) 425129906Sbmilekic { 426129906Sbmilekic mk_delta_name(dname, delta); 427129906Sbmilekic mk_piece_name(pname, delta, 1, 1); 428129906Sbmilekic if (rename(pname, dname) == 0) 429129906Sbmilekic { 430129906Sbmilekic err("%s complete", delta); 431129906Sbmilekic return 1; 432129906Sbmilekic } 433129906Sbmilekic } 434129906Sbmilekic 435129906Sbmilekic /* 436129906Sbmilekic * Grab a lock on the reassembly mutex file so that we can be sure we are 437129906Sbmilekic * working alone, not fighting another ctm_rmail! 438129906Sbmilekic */ 439129906Sbmilekic strcpy(tname, delta_dir); 440129906Sbmilekic strcat(tname, "/.mutex_build"); 441129906Sbmilekic if ((lfd = lock_file(tname)) < 0) 442129906Sbmilekic return 0; 44392654Sjeff 44492654Sjeff /* 44592654Sjeff * Are all of the pieces present? Of course the current one is, 44692654Sjeff * unless all pieces are missing because another ctm_rmail has 44792654Sjeff * processed them already. 44892654Sjeff */ 44992654Sjeff for (i = 1; i <= npieces; i++) 45092654Sjeff { 45192654Sjeff if (i == pce) 45292654Sjeff continue; 45392654Sjeff mk_piece_name(pname, delta, i, npieces); 45492654Sjeff if (stat(pname, &sb) < 0) 45592654Sjeff { 45692654Sjeff close(lfd); 45792654Sjeff return 1; 45892654Sjeff } 45992654Sjeff } 46092654Sjeff 46192654Sjeff /* 46292654Sjeff * Stick them together. Let combine() use our file name buffers, since 46392654Sjeff * we're such good buddies. :-) 46492654Sjeff */ 46592654Sjeff e = combine(delta, npieces, dname, pname, tname); 46692654Sjeff close(lfd); 46792654Sjeff return e; 46892654Sjeff } 46992654Sjeff 47092654Sjeff 47192654Sjeff/* 47292654Sjeff * Put the pieces together to form a delta. 47392654Sjeff * Returns 1 on success, and 0 on failure. 47492654Sjeff * Note: dname, pname, and tname are room for some file names that just 47592654Sjeff * happened to by lying around in the calling routine. Waste not, want not! 47692654Sjeff */ 47792654Sjeffint 47892654Sjeffcombine(char *delta, int npieces, char *dname, char *pname, char *tname) 47994157Sjeff { 48092654Sjeff FILE *dfp, *pfp; 48192654Sjeff int i, n, e; 48292654Sjeff char buf[BUFSIZ]; 48392654Sjeff 48492654Sjeff strcpy(tname, delta_dir); 48592654Sjeff strcat(tname, "/d.XXXXXX"); 48692654Sjeff if (mktemp(tname) == NULL) 48792654Sjeff { 48892654Sjeff err("*mktemp: '%s'", tname); 48992654Sjeff return 0; 49092654Sjeff } 49192654Sjeff if ((dfp = fopen(tname, "w")) == NULL) 49292654Sjeff { 49392654Sjeff err("cannot open '%s' for writing", tname); 49492654Sjeff return 0; 49592654Sjeff } 49692654Sjeff 497129906Sbmilekic /* 498129906Sbmilekic * Reconstruct the delta by reading each piece in order. 499129906Sbmilekic */ 500129906Sbmilekic for (i = 1; i <= npieces; i++) 501129906Sbmilekic { 502129906Sbmilekic mk_piece_name(pname, delta, i, npieces); 503129906Sbmilekic if ((pfp = fopen(pname, "r")) == NULL) 504129906Sbmilekic { 505129906Sbmilekic err("cannot open '%s' for reading", pname); 506129906Sbmilekic fclose(dfp); 507129906Sbmilekic unlink(tname); 508129906Sbmilekic return 0; 509129906Sbmilekic } 510129906Sbmilekic while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0) 51192654Sjeff fwrite(buf, sizeof(char), n, dfp); 51292654Sjeff e = ferror(pfp); 513 fclose(pfp); 514 if (e) 515 { 516 err("error reading '%s'", pname); 517 fclose(dfp); 518 unlink(tname); 519 return 0; 520 } 521 } 522 fflush(dfp); 523 e = ferror(dfp); 524 fclose(dfp); 525 if (e) 526 { 527 err("error writing '%s'", tname); 528 unlink(tname); 529 return 0; 530 } 531 532 mk_delta_name(dname, delta); 533 if (rename(tname, dname) < 0) 534 { 535 err("*rename: '%s' to '%s'", tname, dname); 536 unlink(tname); 537 return 0; 538 } 539 540 /* 541 * Throw the pieces away. 542 */ 543 for (i = 1; i <= npieces; i++) 544 { 545 mk_piece_name(pname, delta, i, npieces); 546 if (unlink(pname) < 0) 547 err("*unlink: '%s'", pname); 548 } 549 550 err("%s complete", delta); 551 return 1; 552 } 553 554 555/* 556 * MIME BASE64 decode table. 557 */ 558static unsigned char from_b64[0x80] = 559 { 560 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 561 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 562 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 563 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 564 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 565 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 566 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 567 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 568 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 569 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 570 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 571 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 572 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 573 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 574 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 575 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff 576 }; 577 578 579/* 580 * Decode a line of ASCII into binary. Returns the number of bytes in 581 * the output buffer, or < 0 on indigestable input. Error output is 582 * the negative of the index of the inedible character. 583 */ 584int 585decode_line(char *line, char *out_buf) 586 { 587 unsigned char *ip = (unsigned char *)line; 588 unsigned char *op = (unsigned char *)out_buf; 589 unsigned long bits; 590 unsigned x; 591 592 for (;;) 593 { 594 if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40) 595 break; 596 bits = x << 18; 597 ip++; 598 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 599 { 600 bits |= x << 12; 601 *op++ = bits >> 16; 602 ip++; 603 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 604 { 605 bits |= x << 6; 606 *op++ = bits >> 8; 607 ip++; 608 if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40) 609 { 610 bits |= x; 611 *op++ = bits; 612 ip++; 613 } 614 } 615 } 616 } 617 618 if (*ip == '\0' || *ip == '\n') 619 return op - (unsigned char *)out_buf; 620 else 621 return -(ip - (unsigned char *)line); 622 } 623 624 625/* 626 * Create and lock the given file. 627 * 628 * Clearing the lock is as simple as closing the file descriptor we return. 629 */ 630int 631lock_file(char *name) 632 { 633 int lfd; 634 635 if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0) 636 { 637 err("*open: '%s'", name); 638 return -1; 639 } 640 if (flock(lfd, LOCK_EX) < 0) 641 { 642 close(lfd); 643 err("*flock: '%s'", name); 644 return -1; 645 } 646 return lfd; 647 } 648