1 /*	$NetBSD: quote_822_local.c,v 1.1.1.1 2009/06/23 10:08:47 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	quote_822_local 3
6 /* SUMMARY
7 /*	quote local part of mailbox
8 /* SYNOPSIS
9 /*	#include <quote_822_local.h>
10 /*
11 /*	VSTRING	*quote_822_local(dst, src)
12 /*	VSTRING	*dst;
13 /*	const char *src;
14 /*
15 /*	VSTRING	*quote_822_local_flags(dst, src, flags)
16 /*	VSTRING	*dst;
17 /*	const char *src;
18 /*	int	flags;
19 /*
20 /*	VSTRING	*unquote_822_local(dst, src)
21 /*	VSTRING	*dst;
22 /*	const char *src;
23 /* DESCRIPTION
24 /*	quote_822_local() quotes the local part of a mailbox and
25 /*	returns a result that can be used in message headers as
26 /*	specified by RFC 822 (actually, an 8-bit clean version of
27 /*	RFC 822). It implements an 8-bit clean version of RFC 822.
28 /*
29 /*	quote_822_local_flags() provides finer control.
30 /*
31 /*	unquote_822_local() transforms the local part of a mailbox
32 /*	address to unquoted (internal) form.
33 /*
34 /*	Arguments:
35 /* .IP dst
36 /*	The result.
37 /* .IP src
38 /*	The input address.
39 /* .IP flags
40 /*	Bit-wise OR of zero or more of the following.
41 /* .RS
42 /* .IP QUOTE_FLAG_8BITCLEAN
43 /*	In violation with RFCs, treat 8-bit text as ordinary text.
44 /* .IP QUOTE_FLAG_EXPOSE_AT
45 /*	In violation with RFCs, treat `@' as an ordinary character.
46 /* .IP QUOTE_FLAG_APPEND
47 /*	Append to the result buffer, instead of overwriting it.
48 /* .RE
49 /* STANDARDS
50 /*	RFC 822 (ARPA Internet Text Messages)
51 /* BUGS
52 /*	The code assumes that the domain is RFC 822 clean.
53 /* LICENSE
54 /* .ad
55 /* .fi
56 /*	The Secure Mailer license must be distributed with this software.
57 /* AUTHOR(S)
58 /*	Wietse Venema
59 /*	IBM T.J. Watson Research
60 /*	P.O. Box 704
61 /*	Yorktown Heights, NY 10598, USA
62 /*--*/
63 
64 /* System library. */
65 
66 #include <sys_defs.h>
67 #include <string.h>
68 #include <ctype.h>
69 
70 /* Utility library. */
71 
72 #include <vstring.h>
73 
74 /* Global library. */
75 
76 /* Application-specific. */
77 
78 #include "quote_822_local.h"
79 
80 /* Local stuff. */
81 
82 #define YES	1
83 #define	NO	0
84 
85 /* is_822_dot_string - is this local-part an rfc 822 dot-string? */
86 
87 static int is_822_dot_string(const char *local_part, const char *end, int flags)
88 {
89     const char *cp;
90     int     ch;
91 
92     /*
93      * Detect any deviations from a sequence of atoms separated by dots. We
94      * could use lookup tables to speed up some of the work, but hey, how
95      * large can a local-part be anyway?
96      *
97      * RFC 822 expects 7-bit data. Rather than quoting every 8-bit character
98      * (and still passing it on as 8-bit data) we leave 8-bit data alone.
99      */
100     if (local_part == end || local_part[0] == 0 || local_part[0] == '.')
101 	return (NO);
102     for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
103 	if (ch == '.' && (cp + 1) < end && cp[1] == '.')
104 	    return (NO);
105 	if (ch > 127 && !(flags & QUOTE_FLAG_8BITCLEAN))
106 	    return (NO);
107 	if (ch == ' ')
108 	    return (NO);
109 	if (ISCNTRL(ch))
110 	    return (NO);
111 	if (ch == '(' || ch == ')'
112 	    || ch == '<' || ch == '>'
113 	    || (ch == '@' && !(flags & QUOTE_FLAG_EXPOSE_AT)) || ch == ','
114 	    || ch == ';' || ch == ':'
115 	    || ch == '\\' || ch == '"'
116 	    || ch == '[' || ch == ']')
117 	    return (NO);
118     }
119     if (cp[-1] == '.')
120 	return (NO);
121     return (YES);
122 }
123 
124 /* make_822_quoted_string - make quoted-string from local-part */
125 
126 static VSTRING *make_822_quoted_string(VSTRING *dst, const char *local_part,
127 				               const char *end, int flags)
128 {
129     const char *cp;
130     int     ch;
131 
132     /*
133      * Put quotes around the result, and prepend a backslash to characters
134      * that need quoting when they occur in a quoted-string.
135      */
136     VSTRING_ADDCH(dst, '"');
137     for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
138 	if ((ch > 127 && !(flags & QUOTE_FLAG_8BITCLEAN))
139 	    || ch == '"' || ch == '\\' || ch == '\r')
140 	    VSTRING_ADDCH(dst, '\\');
141 	VSTRING_ADDCH(dst, ch);
142     }
143     VSTRING_ADDCH(dst, '"');
144     return (dst);
145 }
146 
147 /* quote_822_local_flags - quote local part of mailbox according to rfc 822 */
148 
149 VSTRING *quote_822_local_flags(VSTRING *dst, const char *mbox, int flags)
150 {
151     const char *start;			/* first byte of localpart */
152     const char *end;			/* first byte after localpart */
153     const char *colon;
154 
155     /*
156      * According to RFC 822, a local-part is a dot-string or a quoted-string.
157      * We first see if the local-part is a dot-string. If it is not, we turn
158      * it into a quoted-string. Anything else would be too painful. But
159      * first, skip over any source route that precedes the local-part.
160      */
161     if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0)
162 	start = colon + 1;
163     else
164 	start = mbox;
165     if ((end = strrchr(start, '@')) == 0)
166 	end = start + strlen(start);
167     if ((flags & QUOTE_FLAG_APPEND) == 0)
168 	VSTRING_RESET(dst);
169     if (is_822_dot_string(start, end, flags)) {
170 	return (vstring_strcat(dst, mbox));
171     } else {
172 	vstring_strncat(dst, mbox, start - mbox);
173 	make_822_quoted_string(dst, start, end, flags & QUOTE_FLAG_8BITCLEAN);
174 	return (vstring_strcat(dst, end));
175     }
176 }
177 
178 /* unquote_822_local - unquote local part of mailbox according to rfc 822 */
179 
180 VSTRING *unquote_822_local(VSTRING *dst, const char *mbox)
181 {
182     const char *start;			/* first byte of localpart */
183     const char *end;			/* first byte after localpart */
184     const char *colon;
185     const char *cp;
186 
187     if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0) {
188 	start = colon + 1;
189 	vstring_strncpy(dst, mbox, start - mbox);
190     } else {
191 	start = mbox;
192 	VSTRING_RESET(dst);
193     }
194     if ((end = strrchr(start, '@')) == 0)
195 	end = start + strlen(start);
196     for (cp = start; cp < end; cp++) {
197 	if (*cp == '"')
198 	    continue;
199 	if (*cp == '\\') {
200 	    if (cp[1] == 0)
201 		continue;
202 	    cp++;
203 	}
204 	VSTRING_ADDCH(dst, *cp);
205     }
206     if (*end)
207 	vstring_strcat(dst, end);
208     else
209 	VSTRING_TERMINATE(dst);
210     return (dst);
211 }
212 
213 #ifdef TEST
214 
215  /*
216   * Proof-of-concept test program. Read an unquoted address from stdin, and
217   * show the quoted and unquoted results.
218   */
219 #include <vstream.h>
220 #include <vstring_vstream.h>
221 
222 #define STR	vstring_str
223 
224 int     main(int unused_argc, char **unused_argv)
225 {
226     VSTRING *raw = vstring_alloc(100);
227     VSTRING *quoted = vstring_alloc(100);
228     VSTRING *unquoted = vstring_alloc(100);
229 
230     while (vstring_fgets_nonl(raw, VSTREAM_IN)) {
231 	quote_822_local(quoted, STR(raw));
232 	vstream_printf("quoted:		%s\n", STR(quoted));
233 	unquote_822_local(unquoted, STR(quoted));
234 	vstream_printf("unquoted:	%s\n", STR(unquoted));
235 	vstream_fflush(VSTREAM_OUT);
236     }
237     vstring_free(unquoted);
238     vstring_free(quoted);
239     vstring_free(raw);
240     return (0);
241 }
242 
243 #endif
244