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