64bits fix
[apps/pfixtools.git] / postlicyd / query.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
5 /*                                                                            */
6 /*  Redistribution and use in source and binary forms, with or without        */
7 /*  modification, are permitted provided that the following conditions        */
8 /*  are met:                                                                  */
9 /*                                                                            */
10 /*  1. Redistributions of source code must retain the above copyright         */
11 /*     notice, this list of conditions and the following disclaimer.          */
12 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
20 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
21 /*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
22 /*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
23 /*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
24 /*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
25 /*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
26 /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
27 /*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
28 /*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
29 /*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
30 /******************************************************************************/
31
32 /*
33  * Copyright © 2007 Pierre Habouzit
34  * Copyright © 2008 Florent Bruneau
35  */
36
37 #include "query.h"
38 #include "policy_tokens.h"
39 #include "str.h"
40
41 bool query_parse(query_t *query, char *p)
42 {
43 #define PARSE_CHECK(expr, error, ...)                                        \
44     do {                                                                     \
45         if (!(expr)) {                                                       \
46             err(error, ##__VA_ARGS__);                                       \
47             return false;                                                    \
48         }                                                                    \
49     } while (0)
50
51     p_clear(query, 1);
52     query->state = SMTP_UNKNOWN;
53     while (*p != '\n') {
54         char *k, *v;
55         int klen, vlen, vtk;
56
57         while (isblank(*p))
58             p++;
59         p = strchr(k = p, '=');
60         PARSE_CHECK(p, "could not find '=' in line");
61         for (klen = p - k; klen && isblank(k[klen]); klen--);
62         p += 1; /* skip = */
63
64         while (isblank(*p))
65             p++;
66         p = strchr(v = p, '\n');
67         PARSE_CHECK(p, "could not find final \\n in line");
68         for (vlen = p - v; vlen && isblank(v[vlen]); vlen--);
69         p += 1; /* skip \n */
70
71         vtk = policy_tokenize(v, vlen);
72         switch (policy_tokenize(k, klen)) {
73 #define CASE(up, low)  case PTK_##up: query->low = v; v[vlen] = '\0';  break;
74             CASE(HELO_NAME,           helo_name);
75             CASE(QUEUE_ID,            queue_id);
76             CASE(RECIPIENT_COUNT,     recipient_count);
77             CASE(CLIENT_ADDRESS,      client_address);
78             CASE(CLIENT_NAME,         client_name);
79             CASE(REVERSE_CLIENT_NAME, reverse_client_name);
80             CASE(INSTANCE,            instance);
81             CASE(SASL_METHOD,         sasl_method);
82             CASE(SASL_USERNAME,       sasl_username);
83             CASE(SASL_SENDER,         sasl_sender);
84             CASE(SIZE,                size);
85             CASE(CCERT_SUBJECT,       ccert_subject);
86             CASE(CCERT_ISSUER,        ccert_issuer);
87             CASE(CCERT_FINGERPRINT,   ccert_fingerprint);
88             CASE(ENCRYPTION_PROTOCOL, encryption_protocol);
89             CASE(ENCRYPTION_CIPHER,   encryption_cipher);
90             CASE(ENCRYPTION_KEYSIZE,  encryption_keysize);
91             CASE(ETRN_DOMAIN,         etrn_domain);
92             CASE(STRESS,              stress);
93 #undef CASE
94
95           case PTK_SENDER:
96             query->sender = v;
97             v[vlen] = '\0';
98             query->sender_domain = memchr(query->sender, '@', vlen);
99             if (query->sender_domain != NULL) {
100                 ++query->sender_domain;
101             }
102             break;
103
104           case PTK_RECIPIENT:
105             query->recipient = v;
106             v[vlen] = '\0';
107             query->recipient_domain = memchr(query->recipient, '@', vlen);
108             if (query->recipient_domain != NULL) {
109                 ++query->recipient_domain;
110             }
111             break;
112
113           case PTK_REQUEST:
114             PARSE_CHECK(vtk == PTK_SMTPD_ACCESS_POLICY,
115                         "unexpected `request' value: %.*s", vlen, v);
116             break;
117
118           case PTK_PROTOCOL_NAME:
119             PARSE_CHECK(vtk == PTK_SMTP || vtk == PTK_ESMTP,
120                         "unexpected `protocol_name' value: %.*s", vlen, v);
121             query->esmtp = vtk == PTK_ESMTP;
122             break;
123
124           case PTK_PROTOCOL_STATE:
125             switch (vtk) {
126 #define CASE(name)  case PTK_##name: query->state = SMTP_##name; break;
127                 CASE(CONNECT);
128                 CASE(EHLO);
129                 CASE(HELO);
130                 CASE(MAIL);
131                 CASE(RCPT);
132                 CASE(DATA);
133                 CASE(END_OF_MESSAGE);
134                 CASE(VRFY);
135                 CASE(ETRN);
136               default:
137                 PARSE_CHECK(false, "unexpected `protocol_state` value: %.*s",
138                             vlen, v);
139 #undef CASE
140             }
141             break;
142
143           default:
144             warn("unexpected key, skipped: %.*s", klen, k);
145             continue;
146         }
147     }
148
149     return query->state != SMTP_UNKNOWN;
150 #undef PARSE_CHECK
151 }
152
153 const char *query_field_for_id(const query_t *query, postlicyd_token id)
154 {
155     switch (id) {
156 #define CASE(Up, Low)                                                          \
157       case PTK_ ## Up: return query->Low;
158       CASE(HELO_NAME, helo_name)
159       CASE(QUEUE_ID, queue_id)
160       CASE(SENDER, sender)
161       CASE(SENDER_DOMAIN, sender_domain)
162       CASE(RECIPIENT, recipient)
163       CASE(RECIPIENT_DOMAIN, recipient_domain)
164       CASE(RECIPIENT_COUNT, recipient_count)
165       CASE(CLIENT_ADDRESS, client_address)
166       CASE(CLIENT_NAME, client_name)
167       CASE(REVERSE_CLIENT_NAME, reverse_client_name)
168       CASE(INSTANCE, instance)
169       CASE(SASL_METHOD, sasl_method)
170       CASE(SASL_USERNAME, sasl_username)
171       CASE(SASL_SENDER, sasl_sender)
172       CASE(SIZE, size)
173       CASE(CCERT_SUBJECT, ccert_subject)
174       CASE(CCERT_ISSUER, ccert_issuer)
175       CASE(CCERT_FINGERPRINT, ccert_fingerprint)
176       CASE(ENCRYPTION_PROTOCOL, encryption_protocol)
177       CASE(ENCRYPTION_CIPHER, encryption_cipher)
178       CASE(ENCRYPTION_KEYSIZE, encryption_keysize)
179       CASE(ETRN_DOMAIN, etrn_domain)
180       CASE(STRESS, stress)
181 #undef CASE
182       default: return NULL;
183     }
184 }
185
186 const char *query_field_for_name(const query_t *query, const char *name)
187 {
188     postlicyd_token id = policy_tokenize(name, strlen(name));
189     if (id == PTK_UNKNOWN) {
190         warn("unknown query field %s", name);
191         return NULL;
192     }
193     return query_field_for_id(query, id);
194 }
195
196 ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *query)
197 {
198     size_t written = 0;
199     size_t pos = 0;
200     const char *end = fmt + m_strlen(fmt);
201
202 #define WRITE(Src, Len)                                                        \
203     do {                                                                       \
204         size_t __len     = (Len);                                              \
205         if (written < len) {                                                   \
206             size_t __to_write = MIN(len - written - 1, __len);                 \
207             memcpy(dest + written, (Src), __to_write);                         \
208             written += __to_write;                                             \
209         }                                                                      \
210         pos += __len;                                                          \
211     } while (0)
212     while (*fmt != '\0') {
213         const char *next_format = strstr(fmt, "${");
214         if (next_format == NULL) {
215             next_format = end;
216         }
217         WRITE(fmt, next_format - fmt);
218         fmt = next_format;
219         if (*fmt != '\0') {
220             fmt += 2;
221             next_format = strchr(fmt, '}');
222             if (next_format == NULL) {
223                 return -1;
224             }
225
226             postlicyd_token tok = policy_tokenize(fmt, next_format - fmt);
227             if (tok == PTK_UNKNOWN) {
228                 warn("unknown field name \"%.*s\"", (int)(next_format - fmt), fmt);
229             }
230             const char *field = query == NULL ? NULL : query_field_for_id(query, tok);
231             if (field == NULL) {
232                 WRITE("(null)", 6);
233             } else {
234                 WRITE(field, m_strlen(field));
235             }
236             fmt = next_format + 1;
237         }
238     }
239
240     if (written > 0 && len > 0) {
241         dest[written] = '\0';
242     }
243     return pos;
244 }