1 /*
2 * Copyright (c) 2006-2011 Internet Initiative Japan Inc. All rights reserved.
3 *
4 * The terms and conditions of the accompanying program
5 * shall be provided separately by Internet Initiative Japan Inc.
6 * Any use, reproduction or distribution of the program are permitted
7 * provided that you agree to be bound to such terms and conditions.
8 *
9 * $Id: inetmailbox.c 1365 2011-10-16 08:08:36Z takahiko $
10 */
11
12 #include "rcsid.h"
13 RCSID("$Id: inetmailbox.c 1365 2011-10-16 08:08:36Z takahiko $");
14
15 #include <assert.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <stdbool.h>
20 #include <sys/types.h>
21
22 #include "ptrop.h"
23 #include "xskip.h"
24 #include "xparse.h"
25 #include "xbuffer.h"
26 #include "inetmailbox.h"
27
28 struct InetMailbox {
29 char *localpart;
30 char *domain;
31 char buf[];
32 };
33
34 /**
35 * create InetMailbox object
36 * @return initialized InetMailbox object, or NULL if memory allocation failed.
37 */
38 static InetMailbox *
InetMailbox_new(size_t buflen)39 InetMailbox_new(size_t buflen)
40 {
41 InetMailbox *self = (InetMailbox *) malloc(sizeof(InetMailbox) + buflen);
42 if (NULL == self) {
43 return NULL;
44 } // end if
45 memset(self, 0, sizeof(InetMailbox)); // 0 で埋めるのは先頭部分だけで十分
46
47 return self;
48 } // end function: InetMailbox_new
49
50 /**
51 * release InetMailbox object
52 * @param self InetMailbox object to release
53 */
54 void
InetMailbox_free(InetMailbox * self)55 InetMailbox_free(InetMailbox *self)
56 {
57 assert(NULL != self);
58 free(self);
59 } // end function: InetMailbox_free
60
61 const char *
InetMailbox_getLocalPart(const InetMailbox * self)62 InetMailbox_getLocalPart(const InetMailbox *self)
63 {
64 return self->localpart;
65 } // end function: InetMailbox_getLocalPart
66
67 const char *
InetMailbox_getDomain(const InetMailbox * self)68 InetMailbox_getDomain(const InetMailbox *self)
69 {
70 return self->domain;
71 } // end function: InetMailbox_getDomain
72
73 /*
74 * いわゆる "<>" かどうかを調べる.
75 */
76 bool
InetMailbox_isNullAddr(const InetMailbox * self)77 InetMailbox_isNullAddr(const InetMailbox *self)
78 {
79 return (NULL != self->localpart) && ('\0' == *(self->localpart))
80 && ('\0' == *(self->domain));
81 } // end function: InetMailbox_isNullAddr
82
83 /*
84 * @param errptr エラー情報を返す. メモリの確保に失敗した場合は NULL をセットする.
85 * parse に失敗した場合は失敗した位置へのポインタを返す.
86 *
87 * addr-spec = local-part "@" domain
88 */
89 static InetMailbox *
InetMailbox_parse(const char * head,const char * tail,const char ** nextp,xparse_funcp xparse_localpart,bool require_localpart,xparse_funcp xparse_domain,bool require_domain,const char ** errptr)90 InetMailbox_parse(const char *head, const char *tail, const char **nextp,
91 xparse_funcp xparse_localpart, bool require_localpart,
92 xparse_funcp xparse_domain, bool require_domain, const char **errptr)
93 {
94 const char *p = head;
95
96 XBuffer *xbuf = XBuffer_new(tail - head);
97 if (NULL == xbuf) {
98 SETDEREF(errptr, NULL);
99 goto cleanup;
100 } // end if
101
102 if (0 >= xparse_localpart(p, tail, &p, xbuf) && require_localpart) {
103 SETDEREF(errptr, p);
104 goto cleanup;
105 } // end if
106
107 if (0 != XBuffer_status(xbuf)) {
108 SETDEREF(errptr, NULL);
109 goto cleanup;
110 } // end if
111
112 size_t localpartlen = XBuffer_getSize(xbuf);
113 if (0 > XBuffer_appendChar(xbuf, '\0')) { // local-part と domain の区切りの NULL 文字
114 SETDEREF(errptr, NULL);
115 goto cleanup;
116 } // end if
117
118 if (0 >= XSkip_char(p, tail, '@', &p)) {
119 SETDEREF(errptr, p);
120 goto cleanup;
121 } // end if
122
123 if (0 >= xparse_domain(p, tail, &p, xbuf) && require_domain) {
124 SETDEREF(errptr, p);
125 goto cleanup;
126 } // end if
127
128 if (0 != XBuffer_status(xbuf)) {
129 SETDEREF(errptr, NULL);
130 goto cleanup;
131 } // end if
132
133 size_t xbuflen = XBuffer_getSize(xbuf);
134 InetMailbox *self = InetMailbox_new(xbuflen + 1); // 1 は NULL 文字の分
135 if (NULL == self) {
136 SETDEREF(errptr, NULL);
137 goto cleanup;
138 } // end if
139
140 memcpy(self->buf, XBuffer_getBytes(xbuf), xbuflen);
141 self->buf[xbuflen] = '\0';
142 self->localpart = self->buf;
143 self->domain = self->buf + localpartlen + 1;
144
145 XBuffer_free(xbuf);
146 *nextp = p;
147 SETDEREF(errptr, NULL);
148 return self;
149
150 cleanup:
151 if (NULL != xbuf) {
152 XBuffer_free(xbuf);
153 } // end if
154 *nextp = head;
155 return NULL;
156 } // end function: InetMailbox_parse
157
158 /*
159 * [RFC2822]
160 * mailbox = name-addr / addr-spec
161 * mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
162 * name-addr = [display-name] angle-addr
163 * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
164 * display-name = phrase
165 * addr-spec = local-part "@" domain
166 */
167 InetMailbox *
InetMailbox_build2822Mailbox(const char * head,const char * tail,const char ** nextp,const char ** errptr)168 InetMailbox_build2822Mailbox(const char *head, const char *tail, const char **nextp,
169 const char **errptr)
170 {
171 bool guessNameaddr; // mailbox = name-addr を想定している ('<' が見つかった) 場合に真
172
173 // ABNF をまとめると
174 // mailbox = ([phrase] [CFWS] "<" addr-spec ">" [CFWS]) / addr-spec
175 // 判断基準は '<', '>' の存在だけ
176
177 // display-name を捨てて addr-spec にたどり着くために
178 // name-addr にマッチするか調べる
179 const char *p = head;
180 XSkip_phrase(p, tail, &p); // display-name の実体
181 XSkip_cfws(p, tail, &p);
182 if (0 < XSkip_char(p, tail, '<', &p)) {
183 // mailbox = name-addr
184 guessNameaddr = true;
185 } else {
186 // mailbox = addr-spec
187 p = head;
188 guessNameaddr = false;
189 } // end if
190
191 InetMailbox *self =
192 InetMailbox_parse(p, tail, &p, XParse_2822LocalPart, true, XParse_2822Domain, true, errptr);
193 if (NULL == self) {
194 goto cleanup;
195 } // end if
196
197 if (guessNameaddr) {
198 // mailbox = name-addr なのに '>' が存在しない
199 if (0 >= XSkip_char(p, tail, '>', &p)) {
200 SETDEREF(errptr, p);
201 goto cleanup;
202 } // end if
203 XSkip_cfws(p, tail, &p);
204 } // end if
205
206 *nextp = p;
207 return self;
208
209 cleanup:
210 if (NULL != self) {
211 InetMailbox_free(self);
212 } // end if
213 *nextp = head;
214 return NULL;
215 } // end function: InetMailbox_build2822Mailbox
216
217 /*
218 * @attention source route は取り扱わない.
219 *
220 * [RFC2821]
221 * Mailbox = Local-part "@" Domain
222 * Local-part = Dot-string / Quoted-string
223 * ; MAY be case-sensitive
224 */
225 InetMailbox *
InetMailbox_build2821Mailbox(const char * head,const char * tail,const char ** nextp,const char ** errptr)226 InetMailbox_build2821Mailbox(const char *head, const char *tail, const char **nextp,
227 const char **errptr)
228 {
229 const char *p = head;
230 InetMailbox *self =
231 InetMailbox_parse(p, tail, &p, XParse_2821LocalPart, true, XParse_2821Domain, true, errptr);
232 if (NULL == self) {
233 *nextp = head;
234 return NULL;
235 } // end if
236
237 *nextp = p;
238 return self;
239 } // end function: InetMailbox_build2821Mailbox
240
241 /*
242 * @attention source route は取り扱わない.
243 *
244 * [RFC2821]
245 * Reverse-path = Path
246 * Forward-path = Path
247 * Path = "<" [ A-d-l ":" ] Mailbox ">"
248 * A-d-l = At-domain *( "," A-d-l )
249 * ; Note that this form, the so-called "source route",
250 * ; MUST BE accepted, SHOULD NOT be generated, and SHOULD be
251 * ; ignored.
252 * At-domain = "@" domain
253 * Mailbox = Local-part "@" Domain
254 * Local-part = Dot-string / Quoted-string
255 * ; MAY be case-sensitive
256 */
257 static InetMailbox *
InetMailbox_build2821PathImpl(const char * head,const char * tail,const char ** nextp,bool require_bracket,const char ** errptr)258 InetMailbox_build2821PathImpl(const char *head, const char *tail, const char **nextp,
259 bool require_bracket, const char **errptr)
260 {
261 InetMailbox *self = NULL;
262 bool have_bracket = false;
263
264 const char *p = head;
265 if (0 < XSkip_char(p, tail, '<', &p)) {
266 have_bracket = true;
267 } else {
268 if (require_bracket) {
269 SETDEREF(errptr, p);
270 goto cleanup;
271 } // end if
272 } // end if
273
274 self =
275 InetMailbox_parse(p, tail, &p, XParse_2821LocalPart, true, XParse_2821Domain, true, errptr);
276 if (NULL == self) {
277 goto cleanup;
278 } // end if
279
280 if (have_bracket && 0 >= XSkip_char(p, tail, '>', &p)) {
281 // "<" で始まっているのに対応する ">" が見つからなかった場合
282 SETDEREF(errptr, p);
283 goto cleanup;
284 } // end if
285
286 *nextp = p;
287 return self;
288
289 cleanup:
290 if (NULL != self) {
291 InetMailbox_free(self);
292 } // end if
293 *nextp = head;
294 return NULL;
295 } // end function: InetMailbox_build2821PathImpl
296
297 InetMailbox *
InetMailbox_build2821Path(const char * head,const char * tail,const char ** nextp,const char ** errptr)298 InetMailbox_build2821Path(const char *head, const char *tail, const char **nextp,
299 const char **errptr)
300 {
301 return InetMailbox_build2821PathImpl(head, tail, nextp, true, errptr);
302 } // end function: InetMailbox_build2821Path
303
304 /*
305 * sendmail の envelope from/rcpt に "<", ">" なしのメールアドレスを受け付ける実装に対応しつつ InetMailbox オブジェクトを構築する.
306 * "<>" は受け付けない.
307 */
308 InetMailbox *
InetMailbox_buildSendmailPath(const char * head,const char * tail,const char ** nextp,const char ** errptr)309 InetMailbox_buildSendmailPath(const char *head, const char *tail, const char **nextp,
310 const char **errptr)
311 {
312 return InetMailbox_build2821PathImpl(head, tail, nextp, false, errptr);
313 } // end function: InetMailbox_buildSendmailPath
314
315 static InetMailbox *
InetMailbox_build2821ReversePathImpl(const char * head,const char * tail,const char ** nextp,bool require_bracket,const char ** errptr)316 InetMailbox_build2821ReversePathImpl(const char *head, const char *tail, const char **nextp,
317 bool require_bracket, const char **errptr)
318 {
319 if (0 < XSkip_string(head, tail, "<>", nextp)) {
320 // "<>" 用
321 SETDEREF(errptr, NULL);
322 return InetMailbox_build("", "");
323 } // end if
324
325 return InetMailbox_build2821PathImpl(head, tail, nextp, require_bracket, errptr);
326 } // end function: InetMailbox_build2821ReversePathImpl
327
328 /*
329 * @attention 厳密には Reverse-path には "<>" は含まれない
330 */
331 InetMailbox *
InetMailbox_build2821ReversePath(const char * head,const char * tail,const char ** nextp,const char ** errptr)332 InetMailbox_build2821ReversePath(const char *head, const char *tail, const char **nextp,
333 const char **errptr)
334 {
335 return InetMailbox_build2821ReversePathImpl(head, tail, nextp, true, errptr);
336 } // end function: InetMailbox_build2821ReversePath
337
338 /*
339 * sendmail の envelope from/rcpt に "<", ">" なしのメールアドレスを受け付ける実装に対応しつつ InetMailbox オブジェクトを構築する.
340 * "<>" を受け付ける.
341 */
342 InetMailbox *
InetMailbox_buildSendmailReversePath(const char * head,const char * tail,const char ** nextp,const char ** errptr)343 InetMailbox_buildSendmailReversePath(const char *head, const char *tail, const char **nextp,
344 const char **errptr)
345 {
346 return InetMailbox_build2821ReversePathImpl(head, tail, nextp, false, errptr);
347 } // end function: InetMailbox_buildSendmailReversePath
348
349 /*
350 * [RFC6376]
351 * sig-i-tag = %x69 [FWS] "=" [FWS] [ Local-part ]
352 * "@" domain-name
353 */
354 InetMailbox *
InetMailbox_buildDkimIdentity(const char * head,const char * tail,const char ** nextp,const char ** errptr)355 InetMailbox_buildDkimIdentity(const char *head, const char *tail, const char **nextp,
356 const char **errptr)
357 {
358 return InetMailbox_parse(head, tail, nextp, XParse_2821LocalPart, false, XParse_domainName,
359 true, errptr);
360 } // end function: InetMailbox_buildDkimIdentity
361
362
363 InetMailbox *
InetMailbox_buildWithLength(const char * localpart,size_t localpart_len,const char * domain,size_t domain_len)364 InetMailbox_buildWithLength(const char *localpart, size_t localpart_len, const char *domain,
365 size_t domain_len)
366 {
367 assert(NULL != localpart);
368 assert(NULL != domain);
369
370 InetMailbox *self = InetMailbox_new(localpart_len + domain_len + 2);
371 if (NULL == self) {
372 return NULL;
373 } // end if
374
375 memcpy(self->buf, localpart, localpart_len);
376 self->buf[localpart_len] = '\0';
377 memcpy(self->buf + localpart_len + 1, domain, domain_len);
378 self->buf[localpart_len + 1 + domain_len] = '\0';
379 self->localpart = self->buf;
380 self->domain = self->buf + localpart_len + 1;
381
382 return self;
383 } // end function: InetMailbox_build
384
385 /**
386 * local-part と domain を指定して InetMailbox オブジェクトを構築する
387 * @param localpart local-part を指定する. NULL は許されない.
388 * @param domain domain を指定する. NULL は許されない.
389 * @return 構築した InetMailbox オブジェクト. 失敗した場合は NULL
390 */
391 InetMailbox *
InetMailbox_build(const char * localpart,const char * domain)392 InetMailbox_build(const char *localpart, const char *domain)
393 {
394 return InetMailbox_buildWithLength(localpart, strlen(localpart), domain, strlen(domain));
395 } // end function: InetMailbox_build
396
397 /**
398 * InetMailbox オブジェクトを複製する.
399 * @param mailbox 複製したい InetMailbox オブジェクト.
400 * @return 構築した InetMailbox オブジェクト. 失敗した場合は NULL.
401 */
402 InetMailbox *
InetMailbox_duplicate(const InetMailbox * mailbox)403 InetMailbox_duplicate(const InetMailbox *mailbox)
404 {
405 assert(NULL != mailbox);
406 return InetMailbox_build(mailbox->localpart, mailbox->domain);
407 } // end function: InetMailbox_duplicate
408
409 /*
410 * local-part + "@" + domain の長さを返す.
411 */
412 size_t
InetMailbox_getRawAddrLength(const InetMailbox * self)413 InetMailbox_getRawAddrLength(const InetMailbox *self)
414 {
415 assert(NULL != self);
416 return strlen(self->localpart) + strlen(self->domain) + 1; // 1 は '@' の分
417 } // end function: InetMailbox_getRawAddrLength
418
419 /*
420 * @return 成功した場合は 0, エラーが発生した場合はエラーコード
421 */
422 int
InetMailbox_writeRawAddr(const InetMailbox * self,XBuffer * xbuf)423 InetMailbox_writeRawAddr(const InetMailbox *self, XBuffer *xbuf)
424 {
425 assert(NULL != self);
426 assert(NULL != xbuf);
427 XBuffer_appendString(xbuf, self->localpart);
428 XBuffer_appendChar(xbuf, '@');
429 XBuffer_appendString(xbuf, self->domain);
430 return XBuffer_status(xbuf);
431 } // end function: InetMailbox_writeRawAddr
432
433 /*
434 * localpart が dot-atom-text にマッチするかを調べる.
435 * マッチしない場合は, ヘッダに書き出す際には localpart を DQUOTE で括る必要がある.
436 */
437 bool
InetMailbox_isLocalPartQuoted(const InetMailbox * self)438 InetMailbox_isLocalPartQuoted(const InetMailbox *self)
439 {
440 assert(NULL != self);
441 assert(NULL != self->localpart);
442 const char *nextp = NULL;
443 const char *localparttail = STRTAIL(self->localpart);
444 XSkip_looseDotAtomText(self->localpart, localparttail, &nextp);
445 return nextp < localparttail;
446 } // end function: InetMailbox_isLocalPartQuoted
447
448 /*
449 * @attention "<>" は扱わない. "<>" も扱いたい場合は InetMailbox_writeMailbox() を使用のこと.
450 */
451 int
InetMailbox_writeAddrSpec(const InetMailbox * self,XBuffer * xbuf)452 InetMailbox_writeAddrSpec(const InetMailbox *self, XBuffer *xbuf)
453 // localpart に NULL, CR, LF は含まれないという前提
454 {
455 assert(NULL != self);
456 assert(NULL != xbuf);
457
458 const char *localparttail = STRTAIL(self->localpart);
459 bool quoted = InetMailbox_isLocalPartQuoted(self);
460
461 if (quoted) {
462 XBuffer_appendChar(xbuf, '"');
463 } // end if
464
465 for (const char *p = self->localpart; p < localparttail; ++p) {
466 switch (*p) {
467 case '\r':
468 case '\n':
469 // quoted-pair にもならない, そもそも含まれていてはならない
470 // abort();
471 break;
472
473 case ' ':
474 case '"':
475 case '\\':
476 case '\t':
477 // text にはマッチするが qtext にはマッチしない文字を quote する
478 XBuffer_appendChar(xbuf, '\\');
479 break;
480
481 default:
482 // do nothing
483 break;
484 } // end switch
485 XBuffer_appendChar(xbuf, *p);
486 } // end for
487
488 if (quoted) {
489 XBuffer_appendChar(xbuf, '"');
490 } // end if
491
492 XBuffer_appendChar(xbuf, '@');
493 XBuffer_appendString(xbuf, self->domain);
494 return XBuffer_status(xbuf);
495 } // end function: InetMailbox_writeAddrSpec
496
497 int
InetMailbox_writeMailbox(const InetMailbox * self,XBuffer * xbuf)498 InetMailbox_writeMailbox(const InetMailbox *self, XBuffer *xbuf)
499 // localpart に NULL, CR, LF は含まれないという前提
500 {
501 if (InetMailbox_isNullAddr(self)) {
502 XBuffer_appendString(xbuf, "<>");
503 return XBuffer_status(xbuf);
504 } else {
505 return InetMailbox_writeAddrSpec(self, xbuf);
506 } // end if
507 } // end function: InetMailbox_writeMailbox
508