183e8c231SBaptiste Daroussin /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
383e8c231SBaptiste Daroussin *
483e8c231SBaptiste Daroussin * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
583e8c231SBaptiste Daroussin *
683e8c231SBaptiste Daroussin * Redistribution and use in source and binary forms, with or without
783e8c231SBaptiste Daroussin * modification, are permitted provided that the following conditions
883e8c231SBaptiste Daroussin * are met:
983e8c231SBaptiste Daroussin * 1. Redistributions of source code must retain the above copyright
1083e8c231SBaptiste Daroussin * notice, this list of conditions and the following disclaimer.
1183e8c231SBaptiste Daroussin * 2. Redistributions in binary form must reproduce the above copyright
1283e8c231SBaptiste Daroussin * notice, this list of conditions and the following disclaimer in the
1383e8c231SBaptiste Daroussin * documentation and/or other materials provided with the distribution.
1483e8c231SBaptiste Daroussin *
1583e8c231SBaptiste Daroussin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1683e8c231SBaptiste Daroussin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1783e8c231SBaptiste Daroussin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1883e8c231SBaptiste Daroussin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1983e8c231SBaptiste Daroussin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2083e8c231SBaptiste Daroussin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2183e8c231SBaptiste Daroussin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2283e8c231SBaptiste Daroussin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2383e8c231SBaptiste Daroussin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2483e8c231SBaptiste Daroussin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2583e8c231SBaptiste Daroussin * SUCH DAMAGE.
2683e8c231SBaptiste Daroussin */
2783e8c231SBaptiste Daroussin
2883e8c231SBaptiste Daroussin #include <ctype.h>
29a8d9bd3fSBaptiste Daroussin #include <getopt.h>
3083e8c231SBaptiste Daroussin #include <stdbool.h>
3183e8c231SBaptiste Daroussin #include <stdio.h>
3283e8c231SBaptiste Daroussin #include <string.h>
3383e8c231SBaptiste Daroussin #include <stdlib.h>
3483e8c231SBaptiste Daroussin
3583e8c231SBaptiste Daroussin extern int main_quotedprintable(int, char *[]);
3683e8c231SBaptiste Daroussin
3783e8c231SBaptiste Daroussin static int
hexval(int c)3883e8c231SBaptiste Daroussin hexval(int c)
3983e8c231SBaptiste Daroussin {
4083e8c231SBaptiste Daroussin if ('0' <= c && c <= '9')
4183e8c231SBaptiste Daroussin return c - '0';
4283e8c231SBaptiste Daroussin return (10 + c - 'A');
4383e8c231SBaptiste Daroussin }
4483e8c231SBaptiste Daroussin
4583e8c231SBaptiste Daroussin
4683e8c231SBaptiste Daroussin static int
decode_char(const char * s)4783e8c231SBaptiste Daroussin decode_char(const char *s)
4883e8c231SBaptiste Daroussin {
4983e8c231SBaptiste Daroussin return (16 * hexval(toupper(s[1])) + hexval(toupper(s[2])));
5083e8c231SBaptiste Daroussin }
5183e8c231SBaptiste Daroussin
5283e8c231SBaptiste Daroussin
5383e8c231SBaptiste Daroussin static void
decode_quoted_printable(const char * body,FILE * fpo,bool rfc2047)54beab8b1dSBaptiste Daroussin decode_quoted_printable(const char *body, FILE *fpo, bool rfc2047)
5583e8c231SBaptiste Daroussin {
5683e8c231SBaptiste Daroussin while (*body != '\0') {
5783e8c231SBaptiste Daroussin switch (*body) {
5883e8c231SBaptiste Daroussin case '=':
5983e8c231SBaptiste Daroussin if (strlen(body) < 2) {
6083e8c231SBaptiste Daroussin fputc(*body, fpo);
6183e8c231SBaptiste Daroussin break;
6283e8c231SBaptiste Daroussin }
6383e8c231SBaptiste Daroussin
6483e8c231SBaptiste Daroussin if (body[1] == '\r' && body[2] == '\n') {
6583e8c231SBaptiste Daroussin body += 2;
6683e8c231SBaptiste Daroussin break;
6783e8c231SBaptiste Daroussin }
6883e8c231SBaptiste Daroussin if (body[1] == '\n') {
6983e8c231SBaptiste Daroussin body++;
7083e8c231SBaptiste Daroussin break;
7183e8c231SBaptiste Daroussin }
7283e8c231SBaptiste Daroussin if (strchr("0123456789ABCDEFabcdef", body[1]) == NULL) {
7383e8c231SBaptiste Daroussin fputc(*body, fpo);
7483e8c231SBaptiste Daroussin break;
7583e8c231SBaptiste Daroussin }
7683e8c231SBaptiste Daroussin if (strchr("0123456789ABCDEFabcdef", body[2]) == NULL) {
7783e8c231SBaptiste Daroussin fputc(*body, fpo);
7883e8c231SBaptiste Daroussin break;
7983e8c231SBaptiste Daroussin }
8083e8c231SBaptiste Daroussin fputc(decode_char(body), fpo);
8183e8c231SBaptiste Daroussin body += 2;
8283e8c231SBaptiste Daroussin break;
83beab8b1dSBaptiste Daroussin case '_':
84beab8b1dSBaptiste Daroussin if (rfc2047) {
85beab8b1dSBaptiste Daroussin fputc(0x20, fpo);
86beab8b1dSBaptiste Daroussin break;
87beab8b1dSBaptiste Daroussin }
88beab8b1dSBaptiste Daroussin /* FALLTHROUGH */
8983e8c231SBaptiste Daroussin default:
9083e8c231SBaptiste Daroussin fputc(*body, fpo);
9183e8c231SBaptiste Daroussin break;
9283e8c231SBaptiste Daroussin }
9383e8c231SBaptiste Daroussin body++;
9483e8c231SBaptiste Daroussin }
9583e8c231SBaptiste Daroussin }
9683e8c231SBaptiste Daroussin
9783e8c231SBaptiste Daroussin static void
encode_quoted_printable(const char * body,FILE * fpo,bool rfc2047)98beab8b1dSBaptiste Daroussin encode_quoted_printable(const char *body, FILE *fpo, bool rfc2047)
9983e8c231SBaptiste Daroussin {
10083e8c231SBaptiste Daroussin const char *end = body + strlen(body);
10183e8c231SBaptiste Daroussin size_t linelen = 0;
102bce34cbaSDag-Erling Smørgrav char prev = '\0';
10383e8c231SBaptiste Daroussin
10483e8c231SBaptiste Daroussin while (*body != '\0') {
10583e8c231SBaptiste Daroussin if (linelen == 75) {
10683e8c231SBaptiste Daroussin fputs("=\r\n", fpo);
10783e8c231SBaptiste Daroussin linelen = 0;
10883e8c231SBaptiste Daroussin }
10983e8c231SBaptiste Daroussin if (!isascii(*body) ||
11083e8c231SBaptiste Daroussin *body == '=' ||
11183e8c231SBaptiste Daroussin (*body == '.' && body + 1 < end &&
11283e8c231SBaptiste Daroussin (body[1] == '\n' || body[1] == '\r'))) {
11383e8c231SBaptiste Daroussin fprintf(fpo, "=%02X", (unsigned char)*body);
11483e8c231SBaptiste Daroussin linelen += 2;
11583e8c231SBaptiste Daroussin prev = *body;
11683e8c231SBaptiste Daroussin } else if (*body < 33 && *body != '\n') {
11783e8c231SBaptiste Daroussin if ((*body == ' ' || *body == '\t') &&
11883e8c231SBaptiste Daroussin body + 1 < end &&
11983e8c231SBaptiste Daroussin (body[1] != '\n' && body[1] != '\r')) {
120beab8b1dSBaptiste Daroussin if (*body == 0x20 && rfc2047)
121beab8b1dSBaptiste Daroussin fputc('_', fpo);
122beab8b1dSBaptiste Daroussin else
12383e8c231SBaptiste Daroussin fputc(*body, fpo);
12483e8c231SBaptiste Daroussin prev = *body;
12583e8c231SBaptiste Daroussin } else {
12683e8c231SBaptiste Daroussin fprintf(fpo, "=%02X", (unsigned char)*body);
12783e8c231SBaptiste Daroussin linelen += 2;
12883e8c231SBaptiste Daroussin prev = '_';
12983e8c231SBaptiste Daroussin }
13083e8c231SBaptiste Daroussin } else if (*body == '\n') {
13183e8c231SBaptiste Daroussin if (prev == ' ' || prev == '\t') {
13283e8c231SBaptiste Daroussin fputc('=', fpo);
13383e8c231SBaptiste Daroussin }
13483e8c231SBaptiste Daroussin fputc('\n', fpo);
13583e8c231SBaptiste Daroussin linelen = 0;
13683e8c231SBaptiste Daroussin prev = 0;
13783e8c231SBaptiste Daroussin } else {
13883e8c231SBaptiste Daroussin fputc(*body, fpo);
13983e8c231SBaptiste Daroussin prev = *body;
14083e8c231SBaptiste Daroussin }
14183e8c231SBaptiste Daroussin body++;
14283e8c231SBaptiste Daroussin linelen++;
14383e8c231SBaptiste Daroussin }
14483e8c231SBaptiste Daroussin }
14583e8c231SBaptiste Daroussin
14683e8c231SBaptiste Daroussin static void
qp(FILE * fp,FILE * fpo,bool encode,bool rfc2047)147beab8b1dSBaptiste Daroussin qp(FILE *fp, FILE *fpo, bool encode, bool rfc2047)
14883e8c231SBaptiste Daroussin {
14983e8c231SBaptiste Daroussin char *line = NULL;
15083e8c231SBaptiste Daroussin size_t linecap = 0;
151beab8b1dSBaptiste Daroussin void (*codec)(const char *line, FILE *f, bool rfc2047);
15283e8c231SBaptiste Daroussin
15383e8c231SBaptiste Daroussin codec = encode ? encode_quoted_printable : decode_quoted_printable ;
15483e8c231SBaptiste Daroussin
155bc2913d1SDag-Erling Smørgrav while (getline(&line, &linecap, fp) > 0)
156beab8b1dSBaptiste Daroussin codec(line, fpo, rfc2047);
15783e8c231SBaptiste Daroussin free(line);
15883e8c231SBaptiste Daroussin }
15983e8c231SBaptiste Daroussin
16083e8c231SBaptiste Daroussin static void
usage(void)16183e8c231SBaptiste Daroussin usage(void)
16283e8c231SBaptiste Daroussin {
16383e8c231SBaptiste Daroussin fprintf(stderr,
164beab8b1dSBaptiste Daroussin "usage: bintrans qp [-d] [-r] [-o outputfile] [file name]\n");
16583e8c231SBaptiste Daroussin }
16683e8c231SBaptiste Daroussin
16783e8c231SBaptiste Daroussin int
main_quotedprintable(int argc,char * argv[])16883e8c231SBaptiste Daroussin main_quotedprintable(int argc, char *argv[])
16983e8c231SBaptiste Daroussin {
170a8d9bd3fSBaptiste Daroussin int ch;
17183e8c231SBaptiste Daroussin bool encode = true;
172beab8b1dSBaptiste Daroussin bool rfc2047 = false;
17383e8c231SBaptiste Daroussin FILE *fp = stdin;
17483e8c231SBaptiste Daroussin FILE *fpo = stdout;
17583e8c231SBaptiste Daroussin
176a8d9bd3fSBaptiste Daroussin static const struct option opts[] =
177a8d9bd3fSBaptiste Daroussin {
178a8d9bd3fSBaptiste Daroussin { "decode", no_argument, NULL, 'd'},
179a8d9bd3fSBaptiste Daroussin { "output", required_argument, NULL, 'o'},
180beab8b1dSBaptiste Daroussin { "rfc2047", no_argument, NULL, 'r'},
181a8d9bd3fSBaptiste Daroussin {NULL, no_argument, NULL, 0}
182a8d9bd3fSBaptiste Daroussin };
183a8d9bd3fSBaptiste Daroussin
184*d8fd5514SKyle Evans while ((ch = getopt_long(argc, argv, "+do:ru", opts, NULL)) != -1) {
185a8d9bd3fSBaptiste Daroussin switch(ch) {
18683e8c231SBaptiste Daroussin case 'o':
187a8d9bd3fSBaptiste Daroussin fpo = fopen(optarg, "w");
18883e8c231SBaptiste Daroussin if (fpo == NULL) {
189a8d9bd3fSBaptiste Daroussin perror(optarg);
19083e8c231SBaptiste Daroussin exit(EXIT_FAILURE);
19183e8c231SBaptiste Daroussin }
19283e8c231SBaptiste Daroussin break;
19383e8c231SBaptiste Daroussin case 'u':
194a8d9bd3fSBaptiste Daroussin /* FALLTHROUGH for backward compatibility */
195a8d9bd3fSBaptiste Daroussin case 'd':
19683e8c231SBaptiste Daroussin encode = false;
19783e8c231SBaptiste Daroussin break;
198beab8b1dSBaptiste Daroussin case 'r':
199beab8b1dSBaptiste Daroussin rfc2047 = true;
200beab8b1dSBaptiste Daroussin break;
20183e8c231SBaptiste Daroussin default:
20283e8c231SBaptiste Daroussin usage();
20383e8c231SBaptiste Daroussin exit(EXIT_FAILURE);
20483e8c231SBaptiste Daroussin }
205a8d9bd3fSBaptiste Daroussin };
206a8d9bd3fSBaptiste Daroussin argc -= optind;
207a8d9bd3fSBaptiste Daroussin argv += optind;
208a8d9bd3fSBaptiste Daroussin if (argc > 0) {
209a8d9bd3fSBaptiste Daroussin fp = fopen(argv[0], "r");
21083e8c231SBaptiste Daroussin if (fp == NULL) {
211a8d9bd3fSBaptiste Daroussin perror(argv[0]);
21283e8c231SBaptiste Daroussin exit(EXIT_FAILURE);
21383e8c231SBaptiste Daroussin }
21483e8c231SBaptiste Daroussin }
215beab8b1dSBaptiste Daroussin qp(fp, fpo, encode, rfc2047);
21683e8c231SBaptiste Daroussin
21783e8c231SBaptiste Daroussin return (EXIT_SUCCESS);
21883e8c231SBaptiste Daroussin }
219