hashsum.c: Document `--progress' in the `--help' display.
[u/mdw/catacomb] / cc-enc.c
1 /* -*-c-*-
2 *
3 * $Id$
4 *
5 * Catcrypt data encoding
6 *
7 * (c) 2004 Straylight/Edgeware
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30 /*----- Header files ------------------------------------------------------*/
31
32 #define _FILE_OFFSET_BITS 64
33
34 #include <errno.h>
35 #include <stdio.h>
36
37 #include <mLib/alloc.h>
38 #include <mLib/base64.h>
39 #include <mLib/dstr.h>
40 #include <mLib/mdwopt.h>
41 #include <mLib/report.h>
42 #include <mLib/sub.h>
43
44 #include "cc.h"
45
46 typedef int encbdryp(const char *, void *);
47
48 /*----- Main code ---------------------------------------------------------*/
49
50 /* --- Binary --- */
51
52 static enc *bin_encinit(FILE *fp, const char *msg)
53 { enc *e = CREATE(enc); return (e); }
54 static enc *bin_decinit(FILE *fp, encbdryp *func, void *p)
55 { enc *e = CREATE(enc); return (e); }
56
57 static int bin_read(enc *e, void *p, size_t sz)
58 {
59 size_t n;
60
61 if (!sz) return (0);
62 n = fread(p, 1, sz, e->fp);
63 if (!n || ferror(e->fp)) return (-1);
64 return (n);
65 }
66
67 static int bin_write(enc *e, const void *p, size_t sz)
68 { if (sz && fwrite(p, 1, sz, e->fp) < sz) return (-1); return (0); }
69
70 static int bin_done(enc *e) { return (0); }
71
72 static void bin_destroy(enc *e) { DESTROY(e); }
73
74 /* --- PEM --- */
75
76 typedef struct pem_encctx {
77 enc e;
78 char *msg;
79 unsigned f;
80 base64_ctx b;
81 dstr d;
82 size_t n;
83 #define PEMF_NL 1u
84 #define PEMF_EOF 2u
85 } pem_encctx;
86
87 static enc *pem_encinit(FILE *fp, const char *msg)
88 {
89 pem_encctx *pe = CREATE(pem_encctx);
90 base64_init(&pe->b);
91 fprintf(fp, "-----BEGIN %s-----\n", msg);
92 pe->msg = xstrdup(msg);
93 dstr_create(&pe->d);
94 pe->n = 0;
95 pe->f = 0;
96 return (&pe->e);
97 }
98
99 int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); }
100
101 static enc *pem_decinit(FILE *fp, encbdryp *func, void *p)
102 {
103 char buf[128];
104 int i, d;
105 pem_encctx *pe;
106 int ch;
107
108 /* --- Go until I find a newline and `-----' --- */
109
110 top:
111 d = 0;
112 for (;;) {
113 if ((ch = getc(fp)) == EOF) goto fail;
114 switch (ch) {
115 case '\n': d = 0; break;
116 case '-': if (d >= 0) { d++; if (d == 5) goto banner; }; break;
117 default: d = -1; break;
118 }
119 }
120
121 /* --- See what the banner looks like --- */
122
123 banner:
124 i = d = 0;
125 for (;;) {
126 if ((ch = getc(fp)) == EOF) goto fail;
127 if (ch == '-') { d++; continue; }
128 if (ch == '\n') break;
129 if (i + d + 1 >= sizeof(buf)) goto top;
130 while (d) { buf[i++] = '-'; d--; }
131 buf[i++] = ch;
132 }
133 buf[i] = 0;
134
135 /* --- Check we have the right framing --- */
136
137 if (d != 5) goto top;
138 if (strncmp(buf, "BEGIN ", 6) != 0 || (func && !func(buf + 6, p)))
139 goto top;
140
141 /* --- Ready --- */
142
143 pe = CREATE(pem_encctx);
144 base64_init(&pe->b);
145 pe->msg = xstrdup(buf + 6);
146 dstr_create(&pe->d);
147 pe->n = 0;
148 pe->f = PEMF_NL;
149 return (&pe->e);
150
151 /* --- Failed --- */
152
153 fail:
154 die(EXIT_FAILURE, "initial encapsulation boundary not found");
155 return (0);
156 }
157
158 #define PEM_CHUNKSZ 4096
159
160 static int pem_read(enc *e, void *p, size_t sz)
161 {
162 pem_encctx *pe = (pem_encctx *)e;
163 char buf[PEM_CHUNKSZ];
164 char *pp = p;
165 int ch;
166 size_t n;
167 int rc = 0;
168
169 for (;;) {
170 n = pe->d.len - pe->n;
171 if (n > sz) n = sz;
172 memcpy(pp, pe->d.buf + pe->n, n);
173 pe->n += n;
174 pp += n;
175 rc += n;
176 sz -= n;
177 if (!sz) break;
178 if (pe->f & PEMF_EOF) return (rc ? rc : -1);
179 dstr_reset(&pe->d);
180 n = 0;
181 for (;;) {
182 if ((ch = getc(pe->e.fp)) == EOF) return (-1);
183 if ((pe->f & PEMF_NL) && ch == '-') {
184 ungetc(ch, pe->e.fp);
185 pe->f |= PEMF_EOF;
186 break;
187 }
188 if (ch == '\n') { pe->f |= PEMF_NL; continue; }
189 pe->f &= ~PEMF_NL;
190 buf[n++] = ch;
191 if (n >= PEM_CHUNKSZ) break;
192 }
193 if (n)
194 base64_decode(&pe->b, buf, n, &pe->d);
195 if (pe->f & PEMF_EOF)
196 base64_decode(&pe->b, 0, 0, &pe->d);
197 pe->n = 0;
198 }
199 return (rc);
200 }
201
202 static int pem_write(enc *e, const void *p, size_t sz)
203 {
204 pem_encctx *pe = (pem_encctx *)e;
205 const char *pp = p;
206 size_t n;
207
208 while (sz) {
209 n = PEM_CHUNKSZ;
210 if (n > sz) n = sz;
211 dstr_reset(&pe->d);
212 base64_encode(&pe->b, pp, n, &pe->d);
213 if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
214 return (-1);
215 pp += n;
216 sz -= n;
217 }
218 return (0);
219 }
220
221 static int pem_encdone(enc *e)
222 {
223 pem_encctx *pe = (pem_encctx *)e;
224 dstr_reset(&pe->d);
225 base64_encode(&pe->b, 0, 0, &pe->d);
226 if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
227 return (-1);
228 if (pe->b.lnlen) fputc('\n', pe->e.fp);
229 fprintf(pe->e.fp, "-----END %s-----\n", pe->msg);
230 return (0);
231 }
232
233 static int pem_decdone(enc *e)
234 {
235 pem_encctx *pe = (pem_encctx *)e;
236 char buf[128];
237 int i, d;
238 int ch;
239
240 for (d = 0; d < 5; d++)
241 if ((ch = getc(pe->e.fp)) != '-') goto fail;
242 i = d = 0;
243 for (;;) {
244 if ((ch = getc(pe->e.fp)) == EOF) goto fail;
245 if (ch == '-') { d++; continue; }
246 if (ch == '\n') break;
247 if (i + d + 1 >= sizeof(buf)) goto fail;
248 while (d) { buf[i++] = '-'; d--; }
249 buf[i++] = ch;
250 }
251 if (d != 5) goto fail;
252 buf[i] = 0;
253 if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0)
254 goto fail;
255 return (0);
256
257 fail:
258 die(EXIT_FAILURE, "final encapsulation boundary not found");
259 return (-1);
260 }
261
262 static void pem_destroy(enc *e)
263 {
264 pem_encctx *pe = (pem_encctx *)e;
265 dstr_destroy(&pe->d);
266 xfree(pe->msg);
267 DESTROY(pe);
268 }
269
270 /* --- Encoder table --- */
271
272 const encops enctab[] = {
273 { "binary", "rb", "wb", 1, 1,
274 bin_encinit, bin_decinit,
275 bin_read, bin_write,
276 bin_done, bin_done,
277 bin_destroy },
278 { "pem", "r", "w", 3, 4,
279 pem_encinit, pem_decinit,
280 pem_read, pem_write,
281 pem_encdone, pem_decdone,
282 pem_destroy },
283 { 0 }
284 };
285
286 /* --- @getenc@ --- *
287 *
288 * Arguments: @const char *enc@ = name of wanted encoding
289 *
290 * Returns: Pointer to encoder operations.
291 *
292 * Use: Finds a named encoder or decoder.
293 */
294
295 const encops *getenc(const char *enc)
296 {
297 const encops *eo;
298
299 for (eo = enctab; eo->name; eo++) {
300 if (strcmp(eo->name, enc) == 0)
301 goto e_found;
302 }
303 die(EXIT_FAILURE, "couldn't find encoding `%s'", enc);
304 e_found:
305 return (eo);
306 }
307
308 /* --- @initenc@ --- *
309 *
310 * Arguments: @const encops *eo@ = operations (from @getenc@)
311 * @FILE *fp@ = file handle to attach
312 * @const char *msg@ = banner message
313 *
314 * Returns: The encoder object.
315 *
316 * Use: Initializes an encoder.
317 */
318
319 enc *initenc(const encops *eo, FILE *fp, const char *msg)
320 {
321 enc *e = eo->initenc(fp, msg);
322 e->ops = eo;
323 e->fp = fp;
324 return (e);
325 }
326
327 /* --- @initdec@ --- *
328 *
329 * Arguments: @const encops *eo@ = operations (from @getenc@)
330 * @FILE *fp@ = file handle to attach
331 * @int (*func)(const char *, void *)@ = banner check function
332 * @void *p@ = argument for @func@
333 *
334 * Returns: The encoder object.
335 *
336 * Use: Initializes an encoder.
337 */
338
339 enc *initdec(const encops *eo, FILE *fp,
340 int (*func)(const char *, void *), void *p)
341 {
342 enc *e = eo->initdec(fp, func, p);
343 e->ops = eo;
344 e->fp = fp;
345 return (e);
346 }
347
348 /* --- @freeenc@ --- *
349 *
350 * Arguments: @enc *e@ = encoder object
351 *
352 * Returns: ---
353 *
354 * Use: Frees an encoder object.
355 */
356
357 void freeenc(enc *e) { e->ops->destroy(e); }
358
359 /*----- Encoding and decoding commands ------------------------------------*/
360
361 int cmd_encode(int argc, char *argv[])
362 {
363 const char *fn, *of = 0;
364 FILE *ofp = 0;
365 FILE *fp = 0;
366 const char *ef = "binary";
367 const char *bd = "MESSAGE";
368 fprogress ff;
369 int i;
370 size_t n;
371 char buf[4096];
372 unsigned f = 0;
373 const encops *eo;
374 enc *e;
375
376 #define f_bogus 1u
377 #define f_progress 2u
378
379 for (;;) {
380 static const struct option opt[] = {
381 { "format", OPTF_ARGREQ, 0, 'f' },
382 { "boundary", OPTF_ARGREQ, 0, 'b' },
383 { "output", OPTF_ARGREQ, 0, 'o' },
384 { "progress", 0, 0, 'p' },
385 { 0, 0, 0, 0 }
386 };
387 i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0);
388 if (i < 0) break;
389 switch (i) {
390 case 'f': ef = optarg; break;
391 case 'b': bd = optarg; break;
392 case 'o': of = optarg; break;
393 case 'p': f |= f_progress; break;
394 default: f |= f_bogus; break;
395 }
396 }
397 if (argc - optind > 1 || (f & f_bogus))
398 die(EXIT_FAILURE, "Usage: encode [-OPTIONS] [FILE]");
399
400 if ((eo = getenc(ef)) == 0)
401 die(EXIT_FAILURE, "encoding `%s' not found", ef);
402
403 fn = optind < argc ? argv[optind++] : "-";
404 if (strcmp(fn, "-") == 0)
405 fp = stdin;
406 else if ((fp = fopen(fn, "rb")) == 0) {
407 die(EXIT_FAILURE, "couldn't open file `%s': %s",
408 fn, strerror(errno));
409 }
410
411 if (!of || strcmp(of, "-") == 0)
412 ofp = stdout;
413 else if ((ofp = fopen(of, eo->wmode)) == 0) {
414 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
415 ofp, strerror(errno));
416 }
417
418 e = initenc(eo, ofp, bd);
419
420 if (f & f_progress) {
421 if (fprogress_init(&ff, fn, fp)) {
422 die(EXIT_FAILURE, "failed to initialize progress display: %s",
423 strerror(errno));
424 }
425 }
426
427 do {
428 n = fread(buf, 1, sizeof(buf), fp);
429 if (f & f_progress) fprogress_update(&ff, n);
430 if (e->ops->write(e, buf, n)) {
431 if (f & f_progress) fprogress_done(&ff);
432 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
433 }
434 } while (n == sizeof(buf));
435 if (f & f_progress) fprogress_done(&ff);
436 e->ops->encdone(e);
437 freeenc(e);
438 return (0);
439
440 #undef f_bogus
441 #undef f_progress
442 }
443
444 int cmd_decode(int argc, char *argv[])
445 {
446 const char *fn, *of = 0;
447 FILE *ofp = 0;
448 FILE *fp = 0;
449 const char *ef = "binary";
450 const char *bd = 0;
451 fprogress ff;
452 int i;
453 char buf[4096];
454 unsigned f = 0;
455 const encops *eo;
456 enc *e;
457
458 #define f_bogus 1u
459 #define f_progress 2u
460
461 for (;;) {
462 static const struct option opt[] = {
463 { "format", OPTF_ARGREQ, 0, 'f' },
464 { "boundary", OPTF_ARGREQ, 0, 'b' },
465 { "output", OPTF_ARGREQ, 0, 'o' },
466 { "progress", 0, 0, 'p' },
467 { 0, 0, 0, 0 }
468 };
469 i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0);
470 if (i < 0) break;
471 switch (i) {
472 case 'f': ef = optarg; break;
473 case 'b': bd = optarg; break;
474 case 'o': of = optarg; break;
475 case 'p': f |= f_progress; break;
476 default: f |= f_bogus; break;
477 }
478 }
479 if (argc - optind > 1 || (f & f_bogus))
480 die(EXIT_FAILURE, "Usage: decode [-OPTIONS] [FILE]");
481
482 if ((eo = getenc(ef)) == 0)
483 die(EXIT_FAILURE, "encoding `%s' not found", ef);
484
485 fn = optind < argc ? argv[optind++] : "-";
486 if (strcmp(fn, "-") == 0)
487 fp = stdin;
488 else if ((fp = fopen(fn, eo->rmode)) == 0) {
489 die(EXIT_FAILURE, "couldn't open file `%s': %s",
490 fn, strerror(errno));
491 }
492
493 if (!of || strcmp(of, "-") == 0)
494 ofp = stdout;
495 else if ((ofp = fopen(of, "wb")) == 0) {
496 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
497 ofp, strerror(errno));
498 }
499
500 e = initdec(eo, fp, checkbdry, (/*unconst*/ void *)bd);
501
502 if (f & f_progress) {
503 if (fprogress_init(&ff, fn, fp)) {
504 die(EXIT_FAILURE, "failed to initialize progress display: %s",
505 strerror(errno));
506 }
507 }
508
509 do {
510 if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) {
511 if (f & f_progress) fprogress_done(&ff);
512 die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
513 }
514 if (f & f_progress)
515 fprogress_update(&ff, i*e->ops->ncook/e->ops->nraw);
516 if (fwrite(buf, 1, i, ofp) < i) {
517 if (f & f_progress) fprogress_done(&ff);
518 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
519 }
520 } while (i == sizeof(buf));
521 e->ops->decdone(e);
522 if (f & f_progress) fprogress_done(&ff);
523 freeenc(e);
524 return (0);
525
526 #undef f_bogus
527 #undef f_progress
528 }
529
530 /*----- That's all, folks -------------------------------------------------*/