1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <ctype.h> 29 #include <stdbool.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <stdlib.h> 33 34 extern int main_quotedprintable(int, char *[]); 35 36 static int 37 hexval(int c) 38 { 39 if ('0' <= c && c <= '9') 40 return c - '0'; 41 return (10 + c - 'A'); 42 } 43 44 45 static int 46 decode_char(const char *s) 47 { 48 return (16 * hexval(toupper(s[1])) + hexval(toupper(s[2]))); 49 } 50 51 52 static void 53 decode_quoted_printable(const char *body, FILE *fpo) 54 { 55 while (*body != '\0') { 56 switch (*body) { 57 case '=': 58 if (strlen(body) < 2) { 59 fputc(*body, fpo); 60 break; 61 } 62 63 if (body[1] == '\r' && body[2] == '\n') { 64 body += 2; 65 break; 66 } 67 if (body[1] == '\n') { 68 body++; 69 break; 70 } 71 if (strchr("0123456789ABCDEFabcdef", body[1]) == NULL) { 72 fputc(*body, fpo); 73 break; 74 } 75 if (strchr("0123456789ABCDEFabcdef", body[2]) == NULL) { 76 fputc(*body, fpo); 77 break; 78 } 79 fputc(decode_char(body), fpo); 80 body += 2; 81 break; 82 default: 83 fputc(*body, fpo); 84 break; 85 } 86 body++; 87 } 88 } 89 90 static void 91 encode_quoted_printable(const char *body, FILE *fpo) 92 { 93 char prev; 94 const char *end = body + strlen(body); 95 size_t linelen = 0; 96 97 while (*body != '\0') { 98 if (linelen == 75) { 99 fputs("=\r\n", fpo); 100 linelen = 0; 101 } 102 if (!isascii(*body) || 103 *body == '=' || 104 (*body == '.' && body + 1 < end && 105 (body[1] == '\n' || body[1] == '\r'))) { 106 fprintf(fpo, "=%02X", (unsigned char)*body); 107 linelen += 2; 108 prev = *body; 109 } else if (*body < 33 && *body != '\n') { 110 if ((*body == ' ' || *body == '\t') && 111 body + 1 < end && 112 (body[1] != '\n' && body[1] != '\r')) { 113 fputc(*body, fpo); 114 prev = *body; 115 } else { 116 fprintf(fpo, "=%02X", (unsigned char)*body); 117 linelen += 2; 118 prev = '_'; 119 } 120 } else if (*body == '\n') { 121 if (prev == ' ' || prev == '\t') { 122 fputc('=', fpo); 123 } 124 fputc('\n', fpo); 125 linelen = 0; 126 prev = 0; 127 } else { 128 fputc(*body, fpo); 129 prev = *body; 130 } 131 body++; 132 linelen++; 133 } 134 } 135 136 static void 137 qp(FILE *fp, FILE *fpo, bool encode) 138 { 139 char *line = NULL; 140 size_t linecap = 0; 141 ssize_t linelen; 142 void (*codec)(const char *line, FILE *f); 143 144 codec = encode ? encode_quoted_printable : decode_quoted_printable ; 145 146 while ((linelen = getline(&line, &linecap, fp)) > 0) 147 codec(line, fpo); 148 free(line); 149 } 150 151 static void 152 usage(void) 153 { 154 fprintf(stderr, 155 "usage: bintrans qp [-u] [-o outputfile] [file name]\n"); 156 } 157 158 int 159 main_quotedprintable(int argc, char *argv[]) 160 { 161 int i; 162 bool encode = true; 163 FILE *fp = stdin; 164 FILE *fpo = stdout; 165 166 for (i = 1; i < argc; ++i) { 167 if (argv[i][0] == '-') { 168 switch (argv[i][1]) { 169 case 'o': 170 if (++i >= argc) { 171 fprintf(stderr, "qp: -o requires a file name.\n"); 172 exit(EXIT_FAILURE); 173 } 174 fpo = fopen(argv[i], "w"); 175 if (fpo == NULL) { 176 perror(argv[i]); 177 exit(EXIT_FAILURE); 178 } 179 break; 180 case 'u': 181 encode = false; 182 break; 183 default: 184 usage(); 185 exit(EXIT_FAILURE); 186 } 187 } else { 188 fp = fopen(argv[i], "r"); 189 if (fp == NULL) { 190 perror(argv[i]); 191 exit(EXIT_FAILURE); 192 } 193 } 194 } 195 qp(fp, fpo, encode); 196 197 return (EXIT_SUCCESS); 198 } 199