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 const char *end = body + strlen(body); 94 size_t linelen = 0; 95 char prev = '\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 void (*codec)(const char *line, FILE *f); 142 143 codec = encode ? encode_quoted_printable : decode_quoted_printable ; 144 145 while (getline(&line, &linecap, fp) > 0) 146 codec(line, fpo); 147 free(line); 148 } 149 150 static void 151 usage(void) 152 { 153 fprintf(stderr, 154 "usage: bintrans qp [-u] [-o outputfile] [file name]\n"); 155 } 156 157 int 158 main_quotedprintable(int argc, char *argv[]) 159 { 160 int i; 161 bool encode = true; 162 FILE *fp = stdin; 163 FILE *fpo = stdout; 164 165 for (i = 1; i < argc; ++i) { 166 if (argv[i][0] == '-') { 167 switch (argv[i][1]) { 168 case 'o': 169 if (++i >= argc) { 170 fprintf(stderr, "qp: -o requires a file name.\n"); 171 exit(EXIT_FAILURE); 172 } 173 fpo = fopen(argv[i], "w"); 174 if (fpo == NULL) { 175 perror(argv[i]); 176 exit(EXIT_FAILURE); 177 } 178 break; 179 case 'u': 180 encode = false; 181 break; 182 default: 183 usage(); 184 exit(EXIT_FAILURE); 185 } 186 } else { 187 fp = fopen(argv[i], "r"); 188 if (fp == NULL) { 189 perror(argv[i]); 190 exit(EXIT_FAILURE); 191 } 192 } 193 } 194 qp(fp, fpo, encode); 195 196 return (EXIT_SUCCESS); 197 } 198