1 /* $NetBSD: xtext.c,v 1.4 2025/02/25 19:15:46 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
xtext_quote_append(VSTRING * quoted,const char * unquoted,const char * special)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
xtext_quote(VSTRING * quoted,const char * unquoted,const char * special)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
xtext_unquote_append(VSTRING * unquoted,const char * quoted)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
140 /* xtext_unquote - quoted data to unquoted */
141
xtext_unquote(VSTRING * unquoted,const char * quoted)142 VSTRING *xtext_unquote(VSTRING *unquoted, const char *quoted)
143 {
144 VSTRING_RESET(unquoted);
145 return (xtext_unquote_append(unquoted, quoted) ? unquoted : 0);
146 }
147
148 #ifdef TEST
149
150 /*
151 * Proof-of-concept test program: convert to quoted and back.
152 */
153 #include <vstream.h>
154
155 #define BUFLEN 1024
156
read_buf(VSTREAM * fp,VSTRING * buf)157 static ssize_t read_buf(VSTREAM *fp, VSTRING *buf)
158 {
159 ssize_t len;
160
161 len = vstream_fread_buf(fp, buf, BUFLEN);
162 VSTRING_TERMINATE(buf);
163 return (len);
164 }
165
main(int unused_argc,char ** unused_argv)166 int main(int unused_argc, char **unused_argv)
167 {
168 VSTRING *unquoted = vstring_alloc(BUFLEN);
169 VSTRING *quoted = vstring_alloc(100);
170 ssize_t len;
171
172 /*
173 * Negative tests.
174 */
175 if (xtext_unquote(unquoted, "++1") != 0)
176 msg_warn("undetected error pattern 1");
177 if (xtext_unquote(unquoted, "+2+") != 0)
178 msg_warn("undetected error pattern 2");
179
180 /*
181 * Positive tests.
182 */
183 while ((len = read_buf(VSTREAM_IN, unquoted)) > 0) {
184 xtext_quote(quoted, STR(unquoted), "+=");
185 if (xtext_unquote(unquoted, STR(quoted)) == 0)
186 msg_fatal("bad input: %.100s", STR(quoted));
187 if (LEN(unquoted) != len)
188 msg_fatal("len %ld != unquoted len %ld",
189 (long) len, (long) LEN(unquoted));
190 if (vstream_fwrite(VSTREAM_OUT, STR(unquoted), LEN(unquoted)) != LEN(unquoted))
191 msg_fatal("write error: %m");
192 }
193 vstream_fflush(VSTREAM_OUT);
194 vstring_free(unquoted);
195 vstring_free(quoted);
196 return (0);
197 }
198
199 #endif
200