xref: /freebsd/usr.bin/bintrans/qp.c (revision d8fd551438706b3766da23e72ef077945ba43cd3)
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