1 /* $NetBSD: xtext.c,v 1.3 2020/03/18 19:05:16 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* xtext 3 6 /* SUMMARY 7 /* quote/unquote text, xtext style. 8 /* SYNOPSIS 9 /* #include <xtext.h> 10 /* 11 /* VSTRING *xtext_quote(quoted, unquoted, special) 12 /* VSTRING *quoted; 13 /* const char *unquoted; 14 /* const char *special; 15 /* 16 /* VSTRING *xtext_quote_append(unquoted, quoted, special) 17 /* VSTRING *unquoted; 18 /* const char *quoted; 19 /* const char *special; 20 /* 21 /* VSTRING *xtext_unquote(unquoted, quoted) 22 /* VSTRING *unquoted; 23 /* const char *quoted; 24 /* 25 /* VSTRING *xtext_unquote_append(unquoted, quoted) 26 /* VSTRING *unquoted; 27 /* const char *quoted; 28 /* DESCRIPTION 29 /* xtext_quote() takes a null-terminated string and replaces characters 30 /* +, <33(10) and >126(10), as well as characters specified with "special" 31 /* by +XX, XX being the two-digit uppercase hexadecimal equivalent. 32 /* 33 /* xtext_quote_append() is like xtext_quote(), but appends the conversion 34 /* result to the result buffer. 35 /* 36 /* xtext_unquote() performs the opposite transformation. This function 37 /* understands lowercase, uppercase, and mixed case +XX sequences. The 38 /* result value is the unquoted argument in case of success, a null pointer 39 /* otherwise. 40 /* 41 /* xtext_unquote_append() is like xtext_unquote(), but appends 42 /* the conversion result to the result buffer. 43 /* BUGS 44 /* This module cannot process null characters in data. 45 /* LICENSE 46 /* .ad 47 /* .fi 48 /* The Secure Mailer license must be distributed with this software. 49 /* AUTHOR(S) 50 /* Wietse Venema 51 /* IBM T.J. Watson Research 52 /* P.O. Box 704 53 /* Yorktown Heights, NY 10598, USA 54 /* 55 /* Wietse Venema 56 /* Google, Inc. 57 /* 111 8th Avenue 58 /* New York, NY 10011, USA 59 /*--*/ 60 61 /* System library. */ 62 63 #include <sys_defs.h> 64 #include <string.h> 65 #include <ctype.h> 66 67 /* Utility library. */ 68 69 #include "msg.h" 70 #include "vstring.h" 71 #include "xtext.h" 72 73 /* Application-specific. */ 74 75 #define STR(x) vstring_str(x) 76 #define LEN(x) VSTRING_LEN(x) 77 78 /* xtext_quote_append - append unquoted data to quoted data */ 79 80 VSTRING *xtext_quote_append(VSTRING *quoted, const char *unquoted, 81 const char *special) 82 { 83 const char *cp; 84 int ch; 85 86 for (cp = unquoted; (ch = *(unsigned const char *) cp) != 0; cp++) { 87 if (ch != '+' && ch > 32 && ch < 127 88 && (*special == 0 || strchr(special, ch) == 0)) { 89 VSTRING_ADDCH(quoted, ch); 90 } else { 91 vstring_sprintf_append(quoted, "+%02X", ch); 92 } 93 } 94 VSTRING_TERMINATE(quoted); 95 return (quoted); 96 } 97 98 /* xtext_quote - unquoted data to quoted */ 99 100 VSTRING *xtext_quote(VSTRING *quoted, const char *unquoted, const char *special) 101 { 102 VSTRING_RESET(quoted); 103 xtext_quote_append(quoted, unquoted, special); 104 return (quoted); 105 } 106 107 /* xtext_unquote_append - quoted data to unquoted */ 108 109 VSTRING *xtext_unquote_append(VSTRING *unquoted, const char *quoted) 110 { 111 const unsigned char *cp; 112 int ch; 113 114 for (cp = (const unsigned char *) quoted; (ch = *cp) != 0; cp++) { 115 if (ch == '+') { 116 if (ISDIGIT(cp[1])) 117 ch = (cp[1] - '0') << 4; 118 else if (cp[1] >= 'a' && cp[1] <= 'f') 119 ch = (cp[1] - 'a' + 10) << 4; 120 else if (cp[1] >= 'A' && cp[1] <= 'F') 121 ch = (cp[1] - 'A' + 10) << 4; 122 else 123 return (0); 124 if (ISDIGIT(cp[2])) 125 ch |= (cp[2] - '0'); 126 else if (cp[2] >= 'a' && cp[2] <= 'f') 127 ch |= (cp[2] - 'a' + 10); 128 else if (cp[2] >= 'A' && cp[2] <= 'F') 129 ch |= (cp[2] - 'A' + 10); 130 else 131 return (0); 132 cp += 2; 133 } 134 VSTRING_ADDCH(unquoted, ch); 135 } 136 VSTRING_TERMINATE(unquoted); 137 return (unquoted); 138 } 139 /* xtext_unquote - quoted data to unquoted */ 140 141 VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted) 142 { 143 VSTRING_RESET(unquoted); 144 return (xtext_unquote_append(unquoted, quoted) ? unquoted : 0); 145 } 146 147 #ifdef TEST 148 149 /* 150 * Proof-of-concept test program: convert to quoted and back. 151 */ 152 #include <vstream.h> 153 154 #define BUFLEN 1024 155 156 static ssize_t read_buf(VSTREAM *fp, VSTRING *buf) 157 { 158 ssize_t len; 159 160 len = vstream_fread_buf(fp, buf, BUFLEN); 161 VSTRING_TERMINATE(buf); 162 return (len); 163 } 164 165 int main(int unused_argc, char **unused_argv) 166 { 167 VSTRING *unquoted = vstring_alloc(BUFLEN); 168 VSTRING *quoted = vstring_alloc(100); 169 ssize_t len; 170 171 /* 172 * Negative tests. 173 */ 174 if (xtext_unquote(unquoted, "++1") != 0) 175 msg_warn("undetected error pattern 1"); 176 if (xtext_unquote(unquoted, "+2+") != 0) 177 msg_warn("undetected error pattern 2"); 178 179 /* 180 * Positive tests. 181 */ 182 while ((len = read_buf(VSTREAM_IN, unquoted)) > 0) { 183 xtext_quote(quoted, STR(unquoted), "+="); 184 if (xtext_unquote(unquoted, STR(quoted)) == 0) 185 msg_fatal("bad input: %.100s", STR(quoted)); 186 if (LEN(unquoted) != len) 187 msg_fatal("len %ld != unquoted len %ld", 188 (long) len, (long) LEN(unquoted)); 189 if (vstream_fwrite(VSTREAM_OUT, STR(unquoted), LEN(unquoted)) != LEN(unquoted)) 190 msg_fatal("write error: %m"); 191 } 192 vstream_fflush(VSTREAM_OUT); 193 vstring_free(unquoted); 194 vstring_free(quoted); 195 return (0); 196 } 197 198 #endif 199