xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/xtext.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
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 
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 /* xtext_unquote - quoted data to unquoted */
140 
xtext_unquote(VSTRING * unquoted,const char * quoted)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 
read_buf(VSTREAM * fp,VSTRING * buf)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 
main(int unused_argc,char ** unused_argv)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