/src/dovecot/src/lib-smtp/smtp-address.c
Line | Count | Source |
1 | | /* Copyright (c) Dovecot authors, see top-level COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "message-address.h" |
6 | | #include "smtp-parser.h" |
7 | | #include "smtp-address.h" |
8 | | |
9 | | /* From RFC 5321: |
10 | | |
11 | | Reverse-path = Path / "<>" |
12 | | Forward-path = Path |
13 | | |
14 | | Path = "<" [ A-d-l ":" ] Mailbox ">" |
15 | | A-d-l = At-domain *( "," At-domain ) |
16 | | ; Note that this form, the so-called "source |
17 | | ; route", MUST BE accepted, SHOULD NOT be |
18 | | ; generated, and SHOULD be ignored. |
19 | | At-domain = "@" Domain |
20 | | |
21 | | Domain = sub-domain *("." sub-domain) |
22 | | |
23 | | sub-domain = Let-dig [Ldh-str] |
24 | | Let-dig = ALPHA / DIGIT |
25 | | Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig |
26 | | |
27 | | address-literal = "[" ( IPv4-address-literal / |
28 | | IPv6-address-literal / |
29 | | General-address-literal ) "]" |
30 | | ; See Section 4.1.3 |
31 | | |
32 | | Mailbox = Local-part "@" ( Domain / address-literal ) |
33 | | |
34 | | Local-part = Dot-string / Quoted-string |
35 | | ; MAY be case-sensitive |
36 | | Dot-string = Atom *("." Atom) |
37 | | Atom = 1*atext |
38 | | */ |
39 | | |
40 | | /* |
41 | | * SMTP address parsing |
42 | | */ |
43 | | |
44 | | struct smtp_address_parser { |
45 | | struct smtp_parser parser; |
46 | | |
47 | | struct smtp_address address; |
48 | | const unsigned char *address_end; |
49 | | |
50 | | bool parse:1; |
51 | | bool path:1; |
52 | | bool parsed_any:1; |
53 | | bool totally_broken:1; |
54 | | }; |
55 | | |
56 | | static int |
57 | | smtp_parser_parse_dot_string(struct smtp_parser *parser, const char **value_r) |
58 | 22.7k | { |
59 | 22.7k | const unsigned char *pbegin = parser->cur; |
60 | | |
61 | | /* Dot-string = Atom *("." Atom) |
62 | | */ |
63 | | |
64 | | /* NOTE: this deviates from Dot-String syntax to allow some Japanese |
65 | | mail addresses with dots at non-standard places to be accepted. */ |
66 | | |
67 | 22.7k | if (parser->cur >= parser->end || |
68 | 22.2k | (!smtp_char_is_atext(*parser->cur) && *parser->cur != '.')) |
69 | 9.72k | return 0; |
70 | 13.0k | parser->cur++; |
71 | | |
72 | 207k | while (parser->cur < parser->end && |
73 | 206k | (smtp_char_is_atext(*parser->cur) || *parser->cur == '.')) |
74 | 194k | parser->cur++; |
75 | | |
76 | 13.0k | if (value_r != NULL) |
77 | 13.0k | *value_r = t_strndup(pbegin, parser->cur - pbegin); |
78 | 13.0k | return 1; |
79 | 22.7k | } |
80 | | |
81 | | static int |
82 | | smtp_parse_localpart(struct smtp_parser *parser, const char **localpart_r) |
83 | 29.5k | { |
84 | 29.5k | int ret; |
85 | | |
86 | 29.5k | if ((ret = smtp_parser_parse_quoted_string(parser, localpart_r)) != 0) |
87 | 6.78k | return ret; |
88 | | |
89 | 22.7k | return smtp_parser_parse_dot_string(parser, localpart_r); |
90 | 29.5k | } |
91 | | |
92 | | static int |
93 | | smtp_address_parser_find_end(struct smtp_address_parser *aparser, |
94 | | enum smtp_address_parse_flags flags) |
95 | 0 | { |
96 | 0 | struct smtp_parser *parser = &aparser->parser; |
97 | 0 | const char *begin = (const char *)parser->begin, *end; |
98 | 0 | const char **address_p = NULL; |
99 | |
|
100 | 0 | if (aparser->address_end != NULL) |
101 | 0 | return 0; |
102 | | |
103 | 0 | if (aparser->parse && |
104 | 0 | HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW)) |
105 | 0 | address_p = &aparser->address.raw; |
106 | 0 | if (smtp_address_parse_any(begin, address_p, &end) < 0) { |
107 | 0 | parser->error = "Invalid character"; |
108 | 0 | aparser->totally_broken = TRUE; |
109 | 0 | return -1; |
110 | 0 | } |
111 | 0 | aparser->parsed_any = TRUE; |
112 | 0 | aparser->address_end = (const unsigned char *)end; |
113 | 0 | if (aparser->path) { |
114 | 0 | i_assert(aparser->address_end > parser->begin); |
115 | 0 | aparser->address_end--; |
116 | 0 | } |
117 | 0 | return 0; |
118 | 0 | } |
119 | | |
120 | | static int |
121 | | smtp_parse_mailbox(struct smtp_address_parser *aparser, |
122 | | enum smtp_address_parse_flags flags) |
123 | 29.5k | { |
124 | 29.5k | struct smtp_parser *parser = &aparser->parser; |
125 | 29.5k | const char **value = NULL; |
126 | 29.5k | const unsigned char *p, *dp; |
127 | 29.5k | int ret; |
128 | | |
129 | | /* Mailbox = Local-part "@" ( Domain / address-literal ) |
130 | | */ |
131 | | |
132 | 29.5k | value = (aparser->parse ? &aparser->address.localpart : NULL); |
133 | 29.5k | if ((flags & SMTP_ADDRESS_PARSE_FLAG_STRICT) != 0 || |
134 | 29.5k | (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART) == 0 || |
135 | 29.5k | aparser->path || *parser->cur == '\"') { |
136 | 29.5k | if ((ret = smtp_parse_localpart(parser, value)) <= 0) |
137 | 9.93k | return ret; |
138 | 29.5k | } else { |
139 | | /* find the end of the address */ |
140 | 0 | if (smtp_address_parser_find_end(aparser, flags) < 0) |
141 | 0 | return -1; |
142 | | /* use the right-most '@' as separator */ |
143 | 0 | dp = aparser->address_end - 1; |
144 | | /* an empty address leaves address_end at cur, so clamp to |
145 | | avoid dp < cur underflowing into p_strdup_until() below */ |
146 | 0 | if (dp < parser->cur) |
147 | 0 | dp = parser->cur; |
148 | 0 | while (dp > parser->cur && *dp != '@') |
149 | 0 | dp--; |
150 | 0 | if (dp == parser->cur) |
151 | 0 | dp = aparser->address_end; |
152 | | /* check whether the resulting localpart could be encoded as |
153 | | quoted string */ |
154 | 0 | for (p = parser->cur; p < dp; p++) { |
155 | 0 | if (!smtp_char_is_qtext(*p) && |
156 | 0 | !smtp_char_is_qpair(*p)) { |
157 | 0 | parser->error = |
158 | 0 | "Invalid character in localpart"; |
159 | 0 | return -1; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | if (aparser->parse) { |
163 | 0 | aparser->address.localpart = |
164 | 0 | p_strdup_until(parser->pool, parser->cur, dp); |
165 | 0 | } |
166 | 0 | parser->cur = dp; |
167 | 0 | } |
168 | | |
169 | 19.5k | if ((parser->cur >= parser->end || *parser->cur != '@') && |
170 | 2.57k | (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART) == 0) { |
171 | 867 | if (parser->cur >= parser->end || |
172 | 487 | (aparser->path && *parser->cur == '>')) |
173 | 577 | parser->error = "Missing domain"; |
174 | 290 | else |
175 | 290 | parser->error = "Invalid character in localpart"; |
176 | 867 | return -1; |
177 | 867 | } |
178 | | |
179 | 18.7k | if (parser->cur >= parser->end || *parser->cur != '@') |
180 | 1.71k | return 1; |
181 | 17.0k | parser->cur++; |
182 | | |
183 | 17.0k | value = (aparser->parse ? &aparser->address.domain : NULL); |
184 | 17.0k | if ((ret = smtp_parser_parse_domain(parser, value)) == 0 && |
185 | 1.43k | (ret = smtp_parser_parse_address_literal( |
186 | 1.43k | parser, value, NULL)) == 0) { |
187 | 976 | if (parser->cur >= parser->end || |
188 | 624 | (aparser->path && *parser->cur == '>')) { |
189 | 624 | parser->error = "Missing domain after '@'"; |
190 | 624 | return -1; |
191 | 624 | } else { |
192 | 352 | parser->error = "Invalid domain"; |
193 | 352 | return -1; |
194 | 352 | } |
195 | 976 | } |
196 | 16.0k | return ret; |
197 | 17.0k | } |
198 | | |
199 | | static int smtp_parse_source_route(struct smtp_parser *parser) |
200 | 30.8k | { |
201 | | /* Source-route = [ A-d-l ":" ] |
202 | | A-d-l = At-domain *( "," At-domain ) |
203 | | At-domain = "@" Domain |
204 | | */ |
205 | | |
206 | | /* "@" Domain */ |
207 | 30.8k | if (parser->cur >= parser->end || *parser->cur != '@') |
208 | 29.2k | return 0; |
209 | 1.58k | parser->cur++; |
210 | | |
211 | 2.39k | for (;;) { |
212 | | /* Domain */ |
213 | 2.39k | if (smtp_parser_parse_domain(parser, NULL) <= 0) { |
214 | 286 | parser->error = |
215 | 286 | "Missing domain after '@' in source route"; |
216 | 286 | return -1; |
217 | 286 | } |
218 | | |
219 | | /* *( "," At-domain ) */ |
220 | 2.10k | if (parser->cur >= parser->end || *parser->cur != ',') |
221 | 1.02k | break; |
222 | 1.08k | parser->cur++; |
223 | | |
224 | | /* "@" Domain */ |
225 | 1.08k | if (parser->cur >= parser->end || *parser->cur != '@') { |
226 | 281 | parser->error = "Missing '@' after ',' in source route"; |
227 | 281 | return -1; |
228 | 281 | } |
229 | 803 | parser->cur++; |
230 | 803 | } |
231 | | |
232 | | /* ":" */ |
233 | 1.02k | if (parser->cur >= parser->end || *parser->cur != ':') { |
234 | 751 | parser->error = "Missing ':' at end of source route"; |
235 | 751 | return -1; |
236 | 751 | } |
237 | 271 | parser->cur++; |
238 | 271 | return 1; |
239 | 1.02k | } |
240 | | |
241 | | static int |
242 | | smtp_parse_path(struct smtp_address_parser *aparser, |
243 | | enum smtp_address_parse_flags flags) |
244 | 31.1k | { |
245 | 31.1k | struct smtp_parser *parser = &aparser->parser; |
246 | 31.1k | int ret, sret = 0; |
247 | | |
248 | | /* Path = "<" [ A-d-l ":" ] Mailbox ">" |
249 | | */ |
250 | | |
251 | | /* "<" */ |
252 | 31.1k | if (parser->cur < parser->end && *parser->cur == '<') { |
253 | 30.8k | aparser->path = TRUE; |
254 | 30.8k | parser->cur++; |
255 | 30.8k | } else if ((flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) { |
256 | 301 | return 0; |
257 | 301 | } |
258 | | |
259 | | /* [ A-d-l ":" ] */ |
260 | 30.8k | if (aparser->path && (sret = smtp_parse_source_route(parser)) < 0) |
261 | 1.31k | return -1; |
262 | | |
263 | | /* Mailbox */ |
264 | 29.5k | if ((ret = smtp_parse_mailbox(aparser, flags)) < 0) |
265 | 2.52k | return -1; |
266 | 27.0k | if (ret == 0) { |
267 | 9.72k | if (parser->cur < parser->end && *parser->cur == '>') { |
268 | 8.94k | if (sret > 0) { |
269 | 240 | parser->error = |
270 | 240 | "Path only consists of source route"; |
271 | 240 | return -1; |
272 | 240 | } |
273 | 8.70k | if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) |
274 | 8.70k | == 0) { |
275 | 314 | parser->error = "Null path not allowed"; |
276 | 314 | return -1; |
277 | 314 | } |
278 | 8.70k | } else { |
279 | 776 | parser->error = "Invalid character in localpart"; |
280 | 776 | return -1; |
281 | 776 | } |
282 | 9.72k | } |
283 | | |
284 | | /* ">" */ |
285 | 25.6k | if (aparser->path) { |
286 | 25.6k | if (parser->cur >= parser->end || *parser->cur != '>') { |
287 | 768 | parser->error = "Missing '>' at end of path"; |
288 | 768 | return -1; |
289 | 768 | } |
290 | 24.9k | parser->cur++; |
291 | 24.9k | } else if (parser->cur < parser->end && *parser->cur == '>') { |
292 | 0 | parser->error = "Unmatched '>' at end of path"; |
293 | 0 | return -1; |
294 | 0 | } |
295 | 24.9k | return 1; |
296 | 25.6k | } |
297 | | |
298 | | int smtp_address_parse_mailbox(pool_t pool, const char *mailbox, |
299 | | enum smtp_address_parse_flags flags, |
300 | | struct smtp_address **address_r, |
301 | | const char **error_r) |
302 | 0 | { |
303 | 0 | struct smtp_address_parser aparser; |
304 | 0 | int ret; |
305 | |
|
306 | 0 | if (address_r != NULL) |
307 | 0 | *address_r = NULL; |
308 | 0 | if (error_r != NULL) |
309 | 0 | *error_r = NULL; |
310 | |
|
311 | 0 | if (error_r != NULL) |
312 | 0 | *error_r = NULL; |
313 | |
|
314 | 0 | if ((mailbox == NULL || *mailbox == '\0')) { |
315 | 0 | if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0) { |
316 | 0 | if (error_r != NULL) |
317 | 0 | *error_r = "Mailbox is empty string"; |
318 | 0 | return -1; |
319 | 0 | } |
320 | | |
321 | 0 | if (address_r != NULL) |
322 | 0 | *address_r = p_new(pool, struct smtp_address, 1); |
323 | 0 | return 0; |
324 | 0 | } |
325 | | |
326 | 0 | i_zero(&aparser); |
327 | 0 | smtp_parser_init(&aparser.parser, pool_datastack_create(), mailbox); |
328 | 0 | aparser.address_end = aparser.parser.end; |
329 | 0 | aparser.parse = (address_r != NULL); |
330 | |
|
331 | 0 | if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) { |
332 | 0 | if (error_r != NULL) { |
333 | 0 | *error_r = (ret < 0 ? aparser.parser.error : |
334 | 0 | "Invalid character in localpart"); |
335 | 0 | } |
336 | 0 | return -1; |
337 | 0 | } |
338 | 0 | if (aparser.parser.cur != aparser.parser.end) { |
339 | 0 | if (error_r != NULL) |
340 | 0 | *error_r = "Invalid character in mailbox"; |
341 | 0 | return -1; |
342 | 0 | } |
343 | | |
344 | 0 | if (address_r != NULL) |
345 | 0 | *address_r = smtp_address_clone(pool, &aparser.address); |
346 | 0 | return 0; |
347 | 0 | } |
348 | | |
349 | | static int |
350 | | smtp_address_parse_path_broken(struct smtp_address_parser *aparser, |
351 | | enum smtp_address_parse_flags flags, |
352 | | const char **endp_r) ATTR_NULL(3) |
353 | 6.23k | { |
354 | 6.23k | struct smtp_parser *parser = &aparser->parser; |
355 | 6.23k | const char *begin = (const char *)parser->begin, *end; |
356 | 6.23k | const char *raw = aparser->address.raw; |
357 | 6.23k | const char **address_p = NULL; |
358 | | |
359 | 6.23k | i_zero(&aparser->address); |
360 | 6.23k | aparser->address.raw = raw; |
361 | | |
362 | 6.23k | if (aparser->totally_broken || |
363 | 6.23k | HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN)) |
364 | 6.23k | return -1; |
365 | 0 | if (*begin != '<' && |
366 | 0 | HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL)) { |
367 | | /* brackets missing; totally broken */ |
368 | 0 | return -1; |
369 | 0 | } |
370 | 0 | i_assert(aparser->parse); |
371 | 0 | if (aparser->parsed_any) { |
372 | 0 | if (endp_r != NULL) |
373 | 0 | *endp_r = (const char *)aparser->address_end; |
374 | 0 | return 0; |
375 | 0 | } |
376 | | |
377 | 0 | if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW)) |
378 | 0 | address_p = &aparser->address.raw; |
379 | 0 | if (smtp_address_parse_any(begin, address_p, &end) < 0) { |
380 | | /* totally broken */ |
381 | 0 | return -1; |
382 | 0 | } |
383 | 0 | if (endp_r != NULL) |
384 | 0 | *endp_r = end; |
385 | 0 | return 0; |
386 | 0 | } |
387 | | |
388 | | int smtp_address_parse_path_full(pool_t pool, const char *path, |
389 | | enum smtp_address_parse_flags flags, |
390 | | struct smtp_address **address_r, |
391 | | const char **error_r, const char **endp_r) |
392 | 31.9k | { |
393 | 31.9k | struct smtp_address_parser aparser; |
394 | 31.9k | int ret; |
395 | | |
396 | 31.9k | if (address_r != NULL) |
397 | 31.9k | *address_r = NULL; |
398 | 31.9k | if (error_r != NULL) |
399 | 31.9k | *error_r = NULL; |
400 | 31.9k | if (endp_r != NULL) |
401 | 31.9k | *endp_r = NULL; |
402 | | |
403 | 31.9k | if (path == NULL || *path == '\0') { |
404 | 798 | if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0 || |
405 | 798 | (flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) { |
406 | 798 | if (error_r != NULL) |
407 | 798 | *error_r = "Path is empty string"; |
408 | 798 | return -1; |
409 | 798 | } |
410 | 0 | if (address_r != NULL) |
411 | 0 | *address_r = p_new(pool, struct smtp_address, 1); |
412 | 0 | if (endp_r != NULL) |
413 | 0 | *endp_r = path; |
414 | 0 | return 0; |
415 | 798 | } |
416 | | |
417 | 31.1k | i_zero(&aparser); |
418 | 31.1k | smtp_parser_init(&aparser.parser, pool_datastack_create(), path); |
419 | 31.1k | aparser.address_end = (endp_r != NULL ? NULL : aparser.parser.end); |
420 | 31.1k | aparser.parse = (address_r != NULL); |
421 | | |
422 | 31.1k | if ((ret = smtp_parse_path(&aparser, flags)) <= 0) { |
423 | 6.23k | if (error_r != NULL) { |
424 | 6.23k | *error_r = (ret < 0 ? aparser.parser.error : |
425 | 6.23k | "Missing '<' at beginning of path"); |
426 | 6.23k | } |
427 | 6.23k | ret = -1; |
428 | 24.9k | } else if (endp_r != NULL) { |
429 | 24.9k | if (aparser.parser.cur == aparser.parser.end || |
430 | 8.04k | *aparser.parser.cur == ' ' || |
431 | 24.9k | HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN)) { |
432 | 24.9k | *endp_r = (const char *)aparser.parser.cur; |
433 | 24.9k | ret = 0; |
434 | 24.9k | } else { |
435 | 0 | if (error_r != NULL) |
436 | 0 | *error_r = "Invalid character in path"; |
437 | 0 | ret = -1; |
438 | 0 | } |
439 | 24.9k | } else if (aparser.parser.cur == aparser.parser.end) { |
440 | 0 | ret = 0; |
441 | 0 | } else { |
442 | 0 | if (error_r != NULL) |
443 | 0 | *error_r = "Invalid character in path"; |
444 | 0 | ret = -1; |
445 | 0 | } |
446 | | |
447 | 31.1k | if (ret < 0) { |
448 | | /* normal parsing failed */ |
449 | 6.23k | if (smtp_address_parse_path_broken(&aparser, flags, |
450 | 6.23k | endp_r) < 0) { |
451 | | /* failed to parse it as a broken address as well */ |
452 | 6.23k | return -1; |
453 | 6.23k | } |
454 | | /* broken address */ |
455 | 24.9k | } else if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW) && |
456 | 12.5k | aparser.address.localpart != NULL) { |
457 | 4.16k | if (aparser.path && |
458 | 4.16k | ((const unsigned char *)(path + 1) < aparser.parser.cur)) { |
459 | 4.16k | aparser.address.raw = t_strdup_until( |
460 | 4.16k | path + 1, aparser.parser.cur - 1); |
461 | 4.16k | } else { |
462 | 0 | aparser.address.raw = t_strdup_until( |
463 | 0 | path, aparser.parser.cur); |
464 | 0 | } |
465 | 4.16k | } |
466 | | |
467 | 24.9k | if (address_r != NULL) |
468 | 24.9k | *address_r = smtp_address_clone(pool, &aparser.address); |
469 | 24.9k | return ret; |
470 | 31.1k | } |
471 | | |
472 | | int smtp_address_parse_path(pool_t pool, const char *path, |
473 | | enum smtp_address_parse_flags flags, |
474 | | struct smtp_address **address_r, |
475 | | const char **error_r) |
476 | 0 | { |
477 | 0 | return smtp_address_parse_path_full(pool, path, flags, |
478 | 0 | address_r, error_r, NULL); |
479 | 0 | } |
480 | | |
481 | | int smtp_address_parse_username(pool_t pool, const char *username, |
482 | | struct smtp_address **address_r, |
483 | | const char **error_r) |
484 | 0 | { |
485 | 0 | enum smtp_address_parse_flags flags = |
486 | 0 | SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART | |
487 | 0 | SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART; |
488 | |
|
489 | 0 | struct smtp_address_parser aparser; |
490 | 0 | int ret; |
491 | |
|
492 | 0 | if (address_r != NULL) |
493 | 0 | *address_r = NULL; |
494 | 0 | if (error_r != NULL) |
495 | 0 | *error_r = NULL; |
496 | |
|
497 | 0 | if ((username == NULL || *username == '\0')) { |
498 | 0 | if (error_r != NULL) |
499 | 0 | *error_r = "Username is empty string"; |
500 | 0 | return -1; |
501 | 0 | } |
502 | | |
503 | 0 | i_zero(&aparser); |
504 | 0 | smtp_parser_init(&aparser.parser, pool_datastack_create(), username); |
505 | 0 | aparser.address_end = aparser.parser.end; |
506 | 0 | aparser.parse = (address_r != NULL); |
507 | |
|
508 | 0 | if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) { |
509 | 0 | if (error_r != NULL) { |
510 | 0 | *error_r = (ret < 0 ? aparser.parser.error : |
511 | 0 | "Invalid character in user name"); |
512 | 0 | } |
513 | 0 | return -1; |
514 | 0 | } |
515 | 0 | if (aparser.parser.cur != aparser.parser.end) { |
516 | 0 | if (error_r != NULL) |
517 | 0 | *error_r = "Invalid character in user name"; |
518 | 0 | return -1; |
519 | 0 | } |
520 | | |
521 | 0 | if (address_r != NULL) |
522 | 0 | *address_r = smtp_address_clone(pool, &aparser.address); |
523 | 0 | return 0; |
524 | 0 | } |
525 | | |
526 | | void smtp_address_detail_parse(pool_t pool, const char *delimiters, |
527 | | struct smtp_address *address, |
528 | | const char **username_r, char *delim_r, |
529 | | const char **detail_r) |
530 | 0 | { |
531 | 0 | const char *localpart; |
532 | 0 | const char *user, *p; |
533 | 0 | size_t idx; |
534 | |
|
535 | 0 | i_assert(!smtp_address_isnull(address)); |
536 | | |
537 | 0 | localpart = address->localpart; |
538 | 0 | user = localpart; |
539 | 0 | *detail_r = ""; |
540 | 0 | *delim_r = '\0'; |
541 | | |
542 | | /* first character that matches the recipient_delimiter */ |
543 | 0 | idx = strcspn(localpart, delimiters); |
544 | 0 | p = (localpart[idx] != '\0' ? &localpart[idx] : NULL); |
545 | |
|
546 | 0 | if (p != NULL) { |
547 | 0 | *delim_r = *p; |
548 | | /* user+detail */ |
549 | 0 | user = p_strdup_until(pool, localpart, p); |
550 | 0 | *detail_r = p+1; |
551 | 0 | } |
552 | |
|
553 | 0 | if (address->domain == NULL || *address->domain == '\0') |
554 | 0 | *username_r = user; |
555 | 0 | else if (strchr(user, '@') == NULL) { |
556 | | /* username is just glued to the domain... no SMTP escaping */ |
557 | 0 | *username_r = p_strconcat(pool, user, "@", address->domain, |
558 | 0 | NULL); |
559 | 0 | } else { |
560 | 0 | struct smtp_address uaddr; |
561 | | |
562 | | /* username contains '@'; apply escaping */ |
563 | 0 | smtp_address_init(&uaddr, user, address->domain); |
564 | 0 | if (pool->datastack_pool) |
565 | 0 | *username_r = smtp_address_encode(&uaddr); |
566 | 0 | else { |
567 | 0 | *username_r = |
568 | 0 | p_strdup(pool, smtp_address_encode(&uaddr)); |
569 | 0 | } |
570 | 0 | } |
571 | 0 | } |
572 | | |
573 | | void smtp_address_detail_parse_temp(const char *delimiters, |
574 | | struct smtp_address *address, |
575 | | const char **username_r, char *delim_r, |
576 | | const char **detail_r) |
577 | 0 | { |
578 | 0 | smtp_address_detail_parse(pool_datastack_create(), delimiters, |
579 | 0 | address, username_r, delim_r, detail_r); |
580 | 0 | } |
581 | | |
582 | | int smtp_address_parse_any(const char *in, const char **address_r, |
583 | | const char **endp_r) |
584 | 0 | { |
585 | 0 | const unsigned char *p, *pend, *poffset; |
586 | 0 | bool path = FALSE; |
587 | 0 | bool quoted = FALSE; |
588 | |
|
589 | 0 | if (endp_r != NULL) |
590 | 0 | *endp_r = in; |
591 | |
|
592 | 0 | poffset = p = (const unsigned char *)in; |
593 | 0 | pend = p + strlen(in); |
594 | 0 | if (*p == '<') { |
595 | 0 | path = TRUE; |
596 | 0 | p++; |
597 | 0 | poffset = p; |
598 | 0 | } |
599 | 0 | if (*p == '"') { |
600 | 0 | quoted = TRUE; |
601 | 0 | p++; |
602 | 0 | } |
603 | |
|
604 | 0 | while (p < pend) { |
605 | 0 | if (quoted && *p == '\\') { |
606 | 0 | p++; |
607 | 0 | if (p == pend || *p < 0x20) |
608 | 0 | return -1; |
609 | 0 | p++; |
610 | 0 | if (p == pend) |
611 | 0 | break; |
612 | 0 | } |
613 | 0 | switch (*p) { |
614 | 0 | case '"': |
615 | 0 | quoted = FALSE; |
616 | 0 | break; |
617 | 0 | case ' ': |
618 | 0 | if (!quoted) { |
619 | 0 | if (path) |
620 | 0 | return -1; |
621 | 0 | if (address_r != NULL) |
622 | 0 | *address_r = t_strdup_until(poffset, p); |
623 | 0 | if (endp_r != NULL) |
624 | 0 | *endp_r = (const char *)p; |
625 | 0 | return 0; |
626 | 0 | } |
627 | 0 | break; |
628 | 0 | case '>': |
629 | 0 | if (!quoted) { |
630 | 0 | if (address_r != NULL) |
631 | 0 | *address_r = t_strdup_until(poffset, p); |
632 | 0 | if (endp_r != NULL) |
633 | 0 | *endp_r = (const char *)(p + 1); |
634 | 0 | return 0; |
635 | 0 | } |
636 | 0 | break; |
637 | 0 | default: |
638 | 0 | if (*p < 0x20) |
639 | 0 | return -1; |
640 | 0 | break; |
641 | 0 | } |
642 | 0 | p++; |
643 | 0 | } |
644 | 0 | if (quoted || path) |
645 | 0 | return -1; |
646 | 0 | if (address_r != NULL) |
647 | 0 | *address_r = t_strdup_until(poffset, p); |
648 | 0 | if (endp_r != NULL) |
649 | 0 | *endp_r = (const char *)p; |
650 | 0 | return 0; |
651 | 0 | } |
652 | | |
653 | | /* |
654 | | * SMTP address construction |
655 | | */ |
656 | | |
657 | | void smtp_address_write(string_t *out, const struct smtp_address *address) |
658 | | ATTR_NULL(2) |
659 | 50.9k | { |
660 | 50.9k | bool quoted = FALSE; |
661 | 50.9k | const unsigned char *p, *pend, *pblock; |
662 | 50.9k | size_t begin; |
663 | | |
664 | 50.9k | if (smtp_address_isnull(address)) |
665 | 14.8k | return; |
666 | 36.0k | begin = str_len(out); |
667 | | |
668 | | /* encode localpart */ |
669 | 36.0k | p = (const unsigned char *)address->localpart; |
670 | 36.0k | pend = p + strlen(address->localpart); |
671 | 36.0k | pblock = p; |
672 | 139k | while (p < pend) { |
673 | 2.00M | while (p < pend && smtp_char_is_atext(*p)) |
674 | 1.87M | p++; |
675 | | |
676 | 125k | if (!quoted && p < pend && (*p != '.' || p == pblock)) { |
677 | 6.71k | quoted = TRUE; |
678 | 6.71k | str_insert(out, begin, "\""); |
679 | 6.71k | } |
680 | | |
681 | 125k | str_append_data(out, pblock, p - pblock); |
682 | 125k | if (p >= pend) |
683 | 22.0k | break; |
684 | | |
685 | 103k | if (!quoted) { |
686 | 10.8k | str_append_c(out, '.'); |
687 | 93.0k | } else { |
688 | 93.0k | i_assert(smtp_char_is_qpair(*p)); |
689 | 93.0k | if (!smtp_char_is_qtext(*p)) |
690 | 4.29k | str_append_c(out, '\\'); |
691 | 93.0k | str_append_c(out, *p); |
692 | 93.0k | } |
693 | | |
694 | 103k | p++; |
695 | 103k | pblock = p; |
696 | 103k | } |
697 | | |
698 | 36.0k | if (p == pblock && !quoted) { |
699 | 8.72k | quoted = TRUE; |
700 | 8.72k | str_insert(out, begin, "\""); |
701 | 8.72k | } |
702 | | |
703 | 36.0k | if (quoted) |
704 | 15.4k | str_append_c(out, '\"'); |
705 | | |
706 | 36.0k | if (address->domain == NULL || *address->domain == '\0') |
707 | 765 | return; |
708 | | |
709 | 35.3k | str_append_c(out, '@'); |
710 | 35.3k | str_append(out, address->domain); |
711 | 35.3k | } |
712 | | |
713 | | void smtp_address_write_path(string_t *out, const struct smtp_address *address) |
714 | 0 | { |
715 | 0 | str_append_c(out, '<'); |
716 | 0 | smtp_address_write(out, address); |
717 | 0 | str_append_c(out, '>'); |
718 | 0 | } |
719 | | |
720 | | const char *smtp_address_encode(const struct smtp_address *address) |
721 | 50.9k | { |
722 | 50.9k | string_t *str = t_str_new(256); |
723 | 50.9k | smtp_address_write(str, address); |
724 | 50.9k | return str_c(str); |
725 | 50.9k | } |
726 | | |
727 | | const char *smtp_address_encode_path(const struct smtp_address *address) |
728 | 0 | { |
729 | 0 | string_t *str = t_str_new(256); |
730 | 0 | smtp_address_write_path(str, address); |
731 | 0 | return str_c(str); |
732 | 0 | } |
733 | | |
734 | | const char *smtp_address_encode_raw(const struct smtp_address *address) |
735 | 4.40k | { |
736 | 4.40k | if (address != NULL && address->raw != NULL && *address->raw != '\0') |
737 | 897 | return address->raw; |
738 | | |
739 | 3.50k | return smtp_address_encode(address); |
740 | 4.40k | } |
741 | | |
742 | | const char *smtp_address_encode_raw_path(const struct smtp_address *address) |
743 | 0 | { |
744 | 0 | if (address != NULL && address->raw != NULL && *address->raw != '\0') |
745 | 0 | return t_strconcat("<", address->raw, ">", NULL); |
746 | | |
747 | 0 | return smtp_address_encode_path(address); |
748 | 0 | } |
749 | | |
750 | | /* |
751 | | * SMTP address manipulation |
752 | | */ |
753 | | |
754 | | void smtp_address_init(struct smtp_address *address, |
755 | | const char *localpart, const char *domain) |
756 | 0 | { |
757 | 0 | i_zero(address); |
758 | 0 | if (localpart == NULL || *localpart == '\0') |
759 | 0 | return; |
760 | | |
761 | 0 | address->localpart = localpart; |
762 | 0 | if (domain != NULL && *domain != '\0') |
763 | 0 | address->domain = domain; |
764 | 0 | } |
765 | | |
766 | | int smtp_address_init_from_msg(struct smtp_address *address, |
767 | | const struct message_address *msg_addr) |
768 | 0 | { |
769 | 0 | const unsigned char *p; |
770 | |
|
771 | 0 | i_zero(address); |
772 | 0 | if (msg_addr->mailbox == NULL || *msg_addr->mailbox == '\0') |
773 | 0 | return 0; |
774 | | |
775 | | /* The message_address_parse() function allows UTF-8 codepoints in |
776 | | the localpart. For SMTP addresses that is not an option, so we |
777 | | need to check this upon conversion. */ |
778 | 0 | for (p = (const unsigned char *)msg_addr->mailbox; *p != '\0'; p++) { |
779 | 0 | if (!smtp_char_is_qpair(*p)) |
780 | 0 | return -1; |
781 | 0 | } |
782 | | |
783 | 0 | address->localpart = msg_addr->mailbox; |
784 | 0 | if (msg_addr->domain != NULL && *msg_addr->domain != '\0') |
785 | 0 | address->domain = msg_addr->domain; |
786 | 0 | return 0; |
787 | 0 | } |
788 | | |
789 | | struct smtp_address * |
790 | | smtp_address_clone(pool_t pool, const struct smtp_address *src) |
791 | 61.4k | { |
792 | 61.4k | struct smtp_address *new; |
793 | 61.4k | size_t size, lpsize = 0, dsize = 0, rsize = 0; |
794 | 61.4k | char *data, *localpart = NULL, *domain = NULL, *raw = NULL; |
795 | | |
796 | 61.4k | if (src == NULL) |
797 | 15.3k | return NULL; |
798 | | |
799 | | /* @UNSAFE */ |
800 | | |
801 | 46.1k | size = sizeof(struct smtp_address); |
802 | 46.1k | if (!smtp_address_isnull(src)) { |
803 | 29.8k | lpsize = strlen(src->localpart) + 1; |
804 | 29.8k | size = MALLOC_ADD(size, lpsize); |
805 | 29.8k | } |
806 | 46.1k | if (src->domain != NULL && *src->domain != '\0') { |
807 | 28.5k | dsize = strlen(src->domain) + 1; |
808 | 28.5k | size = MALLOC_ADD(size, dsize); |
809 | 28.5k | } |
810 | 46.1k | if (src->raw != NULL && *src->raw != '\0') { |
811 | 6.58k | rsize = strlen(src->raw) + 1; |
812 | 6.58k | size = MALLOC_ADD(size, rsize); |
813 | 6.58k | } |
814 | | |
815 | 46.1k | data = p_malloc(pool, size); |
816 | 46.1k | new = (struct smtp_address *)data; |
817 | 46.1k | if (lpsize > 0) { |
818 | 29.8k | localpart = PTR_OFFSET(data, sizeof(*new)); |
819 | 29.8k | memcpy(localpart, src->localpart, lpsize); |
820 | 29.8k | } |
821 | 46.1k | if (dsize > 0) { |
822 | 28.5k | domain = PTR_OFFSET(data, sizeof(*new) + lpsize); |
823 | 28.5k | memcpy(domain, src->domain, dsize); |
824 | 28.5k | } |
825 | 46.1k | if (rsize > 0) { |
826 | 6.58k | raw = PTR_OFFSET(data, sizeof(*new) + lpsize + dsize); |
827 | 6.58k | memcpy(raw, src->raw, rsize); |
828 | 6.58k | } |
829 | 46.1k | new->localpart = localpart; |
830 | 46.1k | new->domain = domain; |
831 | 46.1k | new->raw = raw; |
832 | | |
833 | 46.1k | return new; |
834 | 61.4k | } |
835 | | |
836 | | struct smtp_address * |
837 | | smtp_address_create(pool_t pool, const char *localpart, const char *domain) |
838 | 0 | { |
839 | 0 | struct smtp_address addr; |
840 | |
|
841 | 0 | smtp_address_init(&addr, localpart, domain); |
842 | 0 | return smtp_address_clone(pool, &addr); |
843 | 0 | } |
844 | | |
845 | | |
846 | | int smtp_address_create_from_msg(pool_t pool, |
847 | | const struct message_address *msg_addr, |
848 | | struct smtp_address **address_r) |
849 | 0 | { |
850 | 0 | struct smtp_address addr; |
851 | |
|
852 | 0 | if (smtp_address_init_from_msg(&addr, msg_addr) < 0) { |
853 | 0 | *address_r = NULL; |
854 | 0 | return -1; |
855 | 0 | } |
856 | 0 | *address_r = smtp_address_clone(pool, &addr); |
857 | 0 | return 0; |
858 | 0 | } |
859 | | |
860 | | struct smtp_address *smtp_address_clone_temp(const struct smtp_address *src) |
861 | 0 | { |
862 | 0 | struct smtp_address *new; |
863 | |
|
864 | 0 | if (src == NULL) |
865 | 0 | return NULL; |
866 | | |
867 | 0 | new = t_new(struct smtp_address, 1); |
868 | 0 | new->localpart = t_strdup_empty(src->localpart); |
869 | 0 | new->domain = t_strdup_empty(src->domain); |
870 | 0 | new->raw = t_strdup_empty(src->raw); |
871 | 0 | return new; |
872 | 0 | } |
873 | | |
874 | | struct smtp_address * |
875 | | smtp_address_create_temp(const char *localpart, const char *domain) |
876 | 0 | { |
877 | 0 | struct smtp_address addr; |
878 | |
|
879 | 0 | smtp_address_init(&addr, localpart, domain); |
880 | 0 | return smtp_address_clone_temp(&addr); |
881 | 0 | } |
882 | | |
883 | | int smtp_address_create_from_msg_temp(const struct message_address *msg_addr, |
884 | | struct smtp_address **address_r) |
885 | 0 | { |
886 | 0 | struct smtp_address addr; |
887 | |
|
888 | 0 | if (smtp_address_init_from_msg(&addr, msg_addr) < 0) { |
889 | 0 | *address_r = NULL; |
890 | 0 | return -1; |
891 | 0 | } |
892 | 0 | *address_r = smtp_address_clone_temp(&addr); |
893 | 0 | return 0; |
894 | 0 | } |
895 | | |
896 | | struct smtp_address * |
897 | | smtp_address_add_detail(pool_t pool, const struct smtp_address *address, |
898 | | const char *detail, char delim_c) |
899 | 0 | { |
900 | 0 | struct smtp_address *new_addr; |
901 | 0 | const char delim[] = {delim_c, '\0'}; |
902 | |
|
903 | 0 | i_assert(!smtp_address_isnull(address)); |
904 | | |
905 | 0 | new_addr = p_new(pool, struct smtp_address, 1); |
906 | 0 | new_addr->localpart = p_strconcat(pool, address->localpart, delim, |
907 | 0 | detail, NULL); |
908 | 0 | new_addr->domain = p_strdup_empty(pool, address->domain); |
909 | |
|
910 | 0 | return new_addr; |
911 | 0 | } |
912 | | |
913 | | struct smtp_address * |
914 | | smtp_address_add_detail_temp(const struct smtp_address *address, |
915 | | const char *detail, char delim_c) |
916 | 0 | { |
917 | 0 | struct smtp_address *new_addr; |
918 | 0 | const char delim[] = {delim_c, '\0'}; |
919 | |
|
920 | 0 | i_assert(!smtp_address_isnull(address)); |
921 | | |
922 | 0 | new_addr = t_new(struct smtp_address, 1); |
923 | 0 | new_addr->localpart = t_strconcat(address->localpart, delim, detail, |
924 | 0 | NULL); |
925 | 0 | new_addr->domain = t_strdup_empty(address->domain); |
926 | |
|
927 | 0 | return new_addr; |
928 | 0 | } |
929 | | |
930 | | int smtp_address_cmp(const struct smtp_address *address1, |
931 | | const struct smtp_address *address2) |
932 | 0 | { |
933 | 0 | bool null1, null2; |
934 | 0 | int ret; |
935 | |
|
936 | 0 | null1 = smtp_address_isnull(address1); |
937 | 0 | null2 = smtp_address_isnull(address2); |
938 | 0 | if (null1) |
939 | 0 | return (null2 ? 0 : -1); |
940 | 0 | else if (null2) |
941 | 0 | return 1; |
942 | 0 | if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0) |
943 | 0 | return ret; |
944 | 0 | return null_strcmp(address1->localpart, address2->localpart); |
945 | 0 | } |
946 | | |
947 | | int smtp_address_cmp_icase(const struct smtp_address *address1, |
948 | | const struct smtp_address *address2) |
949 | 0 | { |
950 | 0 | bool null1, null2; |
951 | 0 | int ret; |
952 | |
|
953 | 0 | null1 = smtp_address_isnull(address1); |
954 | 0 | null2 = smtp_address_isnull(address2); |
955 | 0 | if (null1) |
956 | 0 | return (null2 ? 0 : -1); |
957 | 0 | else if (null2) |
958 | 0 | return 1; |
959 | 0 | if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0) |
960 | 0 | return ret; |
961 | 0 | return null_strcasecmp(address1->localpart, address2->localpart); |
962 | 0 | } |