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