1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	quote_821_local 3
6/* SUMMARY
7/*	quote local part of address
8/* SYNOPSIS
9/*	#include "quote_821_local.h"
10/*
11/*	VSTRING	*quote_821_local(dst, src)
12/*	VSTRING	*dst;
13/*	char	*src;
14/*
15/*	VSTRING	*quote_821_local_flags(dst, src, flags)
16/*	VSTRING	*dst;
17/*	const char *src;
18/*	int	flags;
19/* DESCRIPTION
20/*	quote_821_local() quotes the local part of a mailbox address and
21/*	returns a result that can be used in SMTP commands as specified
22/*	by RFC 821. It implements an 8-bit clean version of RFC 821.
23/*
24/*	quote_821_local_flags() provides finer control.
25/*
26/*	Arguments:
27/* .IP dst
28/*	The result.
29/* .IP src
30/*	The input address.
31/* .IP flags
32/*	Bit-wise OR of zero or more of the following.
33/* .RS
34/* .IP QUOTE_FLAG_8BITCLEAN
35/*	In violation with RFCs, treat 8-bit text as ordinary text.
36/* .IP QUOTE_FLAG_EXPOSE_AT
37/*	In violation with RFCs, treat `@' as an ordinary character.
38/* .IP QUOTE_FLAG_APPEND
39/*	Append to the result buffer, instead of overwriting it.
40/* .RE
41/* STANDARDS
42/*	RFC 821 (SMTP protocol)
43/* BUGS
44/*	The code assumes that the domain is RFC 821 clean.
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
56/* System library. */
57
58#include <sys_defs.h>
59#include <string.h>
60#include <ctype.h>
61
62/* Utility library. */
63
64#include <vstring.h>
65
66/* Global library. */
67
68#include "quote_821_local.h"
69
70/* Application-specific. */
71
72#define YES	1
73#define	NO	0
74
75/* is_821_dot_string - is this local-part an rfc 821 dot-string? */
76
77static int is_821_dot_string(const char *local_part, const char *end, int flags)
78{
79    const char *cp;
80    int     ch;
81
82    /*
83     * Detect any deviations from the definition of dot-string. We could use
84     * lookup tables to speed up some of the work, but hey, how large can a
85     * local-part be anyway?
86     */
87    if (local_part == end || local_part[0] == 0 || local_part[0] == '.')
88	return (NO);
89    for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
90	if (ch == '.' && cp[1] == '.')
91	    return (NO);
92	if (ch > 127 && !(flags & QUOTE_FLAG_8BITCLEAN))
93	    return (NO);
94	if (ch == ' ')
95	    return (NO);
96	if (ISCNTRL(ch))
97	    return (NO);
98	if (ch == '<' || ch == '>'
99	    || ch == '(' || ch == ')'
100	    || ch == '[' || ch == ']'
101	    || ch == '\\' || ch == ','
102	    || ch == ';' || ch == ':'
103	    || (ch == '@' && !(flags & QUOTE_FLAG_EXPOSE_AT)) || ch == '"')
104	    return (NO);
105    }
106    if (cp[-1] == '.')
107	return (NO);
108    return (YES);
109}
110
111/* make_821_quoted_string - make quoted-string from local-part */
112
113static VSTRING *make_821_quoted_string(VSTRING *dst, const char *local_part,
114				               const char *end, int flags)
115{
116    const char *cp;
117    int     ch;
118
119    /*
120     * Put quotes around the result, and prepend a backslash to characters
121     * that need quoting when they occur in a quoted-string.
122     */
123    VSTRING_ADDCH(dst, '"');
124    for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) {
125	if ((ch > 127 && !(flags & QUOTE_FLAG_8BITCLEAN))
126	    || ch == '\r' || ch == '\n' || ch == '"' || ch == '\\')
127	    VSTRING_ADDCH(dst, '\\');
128	VSTRING_ADDCH(dst, ch);
129    }
130    VSTRING_ADDCH(dst, '"');
131    VSTRING_TERMINATE(dst);
132    return (dst);
133}
134
135/* quote_821_local_flags - quote local part of address according to rfc 821 */
136
137VSTRING *quote_821_local_flags(VSTRING *dst, const char *addr, int flags)
138{
139    const char   *at;
140
141    /*
142     * According to RFC 821, a local-part is a dot-string or a quoted-string.
143     * We first see if the local-part is a dot-string. If it is not, we turn
144     * it into a quoted-string. Anything else would be too painful.
145     */
146    if ((at = strrchr(addr, '@')) == 0)		/* just in case */
147	at = addr + strlen(addr);		/* should not happen */
148    if ((flags & QUOTE_FLAG_APPEND) == 0)
149	VSTRING_RESET(dst);
150    if (is_821_dot_string(addr, at, flags)) {
151	return (vstring_strcat(dst, addr));
152    } else {
153	make_821_quoted_string(dst, addr, at, flags & QUOTE_FLAG_8BITCLEAN);
154	return (vstring_strcat(dst, at));
155    }
156}
157
158#ifdef TEST
159
160 /*
161  * Test program for local-part quoting as per rfc 821
162  */
163#include <stdlib.h>
164#include <vstream.h>
165#include <vstring_vstream.h>
166#include "quote_821_local.h"
167
168int     main(void)
169{
170    VSTRING *src = vstring_alloc(100);
171    VSTRING *dst = vstring_alloc(100);
172
173    while (vstring_fgets_nonl(src, VSTREAM_IN)) {
174	vstream_fprintf(VSTREAM_OUT, "%s\n",
175			vstring_str(quote_821_local(dst, vstring_str(src))));
176	vstream_fflush(VSTREAM_OUT);
177    }
178    exit(0);
179}
180
181#endif
182