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