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