ctm_rmail.c revision 8857
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] = 5598857Srgrimes { 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, 5648857Srgrimes 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