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 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 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 54*beab8b1dSBaptiste 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; 83*beab8b1dSBaptiste Daroussin case '_': 84*beab8b1dSBaptiste Daroussin if (rfc2047) { 85*beab8b1dSBaptiste Daroussin fputc(0x20, fpo); 86*beab8b1dSBaptiste Daroussin break; 87*beab8b1dSBaptiste Daroussin } 88*beab8b1dSBaptiste 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 98*beab8b1dSBaptiste 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')) { 120*beab8b1dSBaptiste Daroussin if (*body == 0x20 && rfc2047) 121*beab8b1dSBaptiste Daroussin fputc('_', fpo); 122*beab8b1dSBaptiste 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 147*beab8b1dSBaptiste Daroussin qp(FILE *fp, FILE *fpo, bool encode, bool rfc2047) 14883e8c231SBaptiste Daroussin { 14983e8c231SBaptiste Daroussin char *line = NULL; 15083e8c231SBaptiste Daroussin size_t linecap = 0; 151*beab8b1dSBaptiste 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) 156*beab8b1dSBaptiste Daroussin codec(line, fpo, rfc2047); 15783e8c231SBaptiste Daroussin free(line); 15883e8c231SBaptiste Daroussin } 15983e8c231SBaptiste Daroussin 16083e8c231SBaptiste Daroussin static void 16183e8c231SBaptiste Daroussin usage(void) 16283e8c231SBaptiste Daroussin { 16383e8c231SBaptiste Daroussin fprintf(stderr, 164*beab8b1dSBaptiste Daroussin "usage: bintrans qp [-d] [-r] [-o outputfile] [file name]\n"); 16583e8c231SBaptiste Daroussin } 16683e8c231SBaptiste Daroussin 16783e8c231SBaptiste Daroussin int 16883e8c231SBaptiste Daroussin main_quotedprintable(int argc, char *argv[]) 16983e8c231SBaptiste Daroussin { 170a8d9bd3fSBaptiste Daroussin int ch; 17183e8c231SBaptiste Daroussin bool encode = true; 172*beab8b1dSBaptiste 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'}, 180*beab8b1dSBaptiste Daroussin { "rfc2047", no_argument, NULL, 'r'}, 181a8d9bd3fSBaptiste Daroussin {NULL, no_argument, NULL, 0} 182a8d9bd3fSBaptiste Daroussin }; 183a8d9bd3fSBaptiste Daroussin 184*beab8b1dSBaptiste Daroussin 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; 198*beab8b1dSBaptiste Daroussin case 'r': 199*beab8b1dSBaptiste Daroussin rfc2047 = true; 200*beab8b1dSBaptiste 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 } 215*beab8b1dSBaptiste Daroussin qp(fp, fpo, encode, rfc2047); 21683e8c231SBaptiste Daroussin 21783e8c231SBaptiste Daroussin return (EXIT_SUCCESS); 21883e8c231SBaptiste Daroussin } 219