1 /*
2  * Copyright (c) 2006-2010 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: dkimauthor.c 1366 2011-10-16 08:13:40Z takahiko $
10  */
11 
12 #include "rcsid.h"
13 RCSID("$Id: dkimauthor.c 1366 2011-10-16 08:13:40Z takahiko $");
14 
15 #include <assert.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <stdbool.h>
19 
20 #include "dkimlogger.h"
21 #include "inetmailbox.h"
22 #include "ptrop.h"
23 #include "xskip.h"
24 #include "strarray.h"
25 #include "dkim.h"
26 #include "dkimpolicybase.h"
27 #include "mailheaders.h"
28 
29 /**
30  * @return DSTAT_OK for success, otherwise status code that indicates error.
31  * @error DSTAT_PERMFAIL_AUTHOR_UNPARSABLE unable to parse Author header field value
32  * @error DSTAT_SYSERR_NORESOURCE memory allocation error
33  */
34 static DkimStatus
DkimAuthor_parse(const DkimPolicyBase * policy,const char * head,const char * tail,InetMailbox ** mailbox)35 DkimAuthor_parse(const DkimPolicyBase *policy, const char *head, const char *tail,
36                  InetMailbox **mailbox)
37 {
38     assert(NULL != head);
39     assert(NULL != tail);
40     assert(NULL != mailbox);
41 
42     const char *p, *errptr;
43     InetMailbox *build_mailbox = InetMailbox_build2822Mailbox(head, tail, &p, &errptr);
44 
45     if (NULL == build_mailbox) {
46         if (NULL != errptr) {   // parse error
47             DkimLogPermFail(policy, "Mailbox parse error: near %.50s", p);
48             return DSTAT_PERMFAIL_AUTHOR_UNPARSABLE;
49         } else {    // memory allocation error
50             DkimLogNoResource(policy);
51             return DSTAT_SYSERR_NORESOURCE;
52         }   // end if
53     }   // end if
54 
55     XSkip_fws(p, tail, &p); // ignore trailing FWS
56     if (p == tail) {
57         *mailbox = build_mailbox;
58         return DSTAT_OK;
59     } else {
60         // Though the parsing has succeeded, unmatched sequence has been left.
61         DkimLogPermFail(policy, "Author field has unused portion: %d bytes, near %.50s", tail - p,
62                         head);
63         InetMailbox_free(build_mailbox);
64         return DSTAT_PERMFAIL_AUTHOR_UNPARSABLE;
65     }   // end if
66 }   // end function: DkimAuthor_parse
67 
68 /**
69  * @param header_index A pointer to a variable to receive the index of the header field
70  *                     extracted as "Author" in the "headers" object.
71  *                     Undefined if the return value is not DSTAT_OK.
72  * @param header_field A pointer to a variable to receive the header field name
73  *                     in the "headers" object.
74  *                     Undefined if the return value is not DSTAT_OK.
75  * @param header_value A pointer to a variable to receive the header field value
76  *                     in the "headers" object.
77  *                     Undefined if the return value is not DSTAT_OK.
78  * @param mailbox A pointer to a variable to receive the InetMailbox object
79  *                build from the extracted "Author" header.
80  *                Undefined if the return value is not DSTAT_OK.
81  * @return DSTAT_OK for success, otherwise status code that indicates error.
82  * @error DSTAT_PERMFAIL_AUTHOR_AMBIGUOUS No or multiple Author headers are found
83  * @error DSTAT_PERMFAIL_AUTHOR_UNPARSABLE unable to parse Author header field value
84  * @error DSTAT_SYSERR_NORESOURCE memory allocation error
85  */
86 DkimStatus
DkimAuthor_extract(const DkimPolicyBase * policy,const MailHeaders * headers,size_t * header_index,const char ** header_field,const char ** header_value,InetMailbox ** mailbox)87 DkimAuthor_extract(const DkimPolicyBase *policy, const MailHeaders *headers, size_t *header_index,
88                    const char **header_field, const char **header_value, InetMailbox **mailbox)
89 {
90     assert(NULL != policy);
91     assert(NULL != headers);
92     assert(NULL != mailbox);
93 
94     size_t num = StrArray_getCount(policy->author_priority);
95     for (size_t i = 0; i < num; ++i) {
96         const char *targetField = StrArray_get(policy->author_priority, i);
97 
98         bool multiple;
99         int index = MailHeaders_getHeaderIndex(headers, targetField, &multiple);
100         if (index < 0) {
101             // No headers looking for are found. Try next "Author"-candidate header.
102             continue;
103         }   // end if
104         if (multiple) {
105             // Multiple "Author"-candidate headers are found. It must be unique.
106             DkimLogPermFail(policy, "Multiple %s Header is found, unable to extract Author",
107                             targetField);
108             return DSTAT_PERMFAIL_AUTHOR_AMBIGUOUS;
109         }   // end if
110 
111         // An unique "Author" header is found.
112 
113         // Extracts "mailbox" by parsing the found "Author" header.
114         const char *headerf, *headerv;
115         MailHeaders_get(headers, index, &headerf, &headerv);
116         DkimStatus dstat = DkimAuthor_parse(policy, headerv, STRTAIL(headerv), mailbox);
117         if (DSTAT_OK == dstat) {
118             // set the references to the original values if the parsing has succeeded.
119             SETDEREF(header_index, index);
120             SETDEREF(header_field, headerf);
121             SETDEREF(header_value, headerv);
122         }   // end if
123         // If the parsing fails, this function returns an error
124         // whether or not next "Author"-candidate header is found.
125         return dstat;
126     }   // end for
127 
128     DkimLogPermFail(policy, "No Author header found");
129     return DSTAT_PERMFAIL_AUTHOR_AMBIGUOUS;
130 }   // end function: DkimAuthor_extract
131