Fix the copyright and licensing stuff.
[apps/pfixtools.git] / postlicyd / query.c
index 01b7ca4..8d991ba 100644 (file)
 /*     products derived from this software without specific prior written     */
 /*     permission.                                                            */
 /*                                                                            */
-/*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
-/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
-/*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
-/*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
-/*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
-/*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
-/*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
-/*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
-/*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
-/*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
-/*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
+/*  THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS   */
+/*  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED         */
+/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE    */
+/*  DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY         */
+/*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */
+/*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS   */
+/*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)     */
+/*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,       */
+/*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN  */
+/*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
+/*  POSSIBILITY OF SUCH DAMAGE.                                               */
+/*                                                                            */
+/*   Copyright (c) 2006-2008 the Authors                                      */
+/*   see AUTHORS and source files for details                                 */
 /******************************************************************************/
 
 /*
 
 #include "query.h"
 #include "policy_tokens.h"
+#include "str.h"
+
+const static_str_t smtp_state_names[SMTP_count] = {
+  { "CONNECT", 7 },
+  { "HELO", 4 },
+  { "MAIL", 4 },
+  { "RCPT", 4 },
+  { "DATA", 4 },
+  { "END-OF-MESSAGE", 14 },
+  { "VRFY", 4 },
+  { "ETRN", 4 },
+};
+
+static const static_str_t static_ESMTP = { "ESMTP", 5 };
+static const static_str_t static_SMTP  = { "SMTP",  4 };
 
 bool query_parse(query_t *query, char *p)
 {
@@ -69,7 +87,7 @@ bool query_parse(query_t *query, char *p)
 
         vtk = policy_tokenize(v, vlen);
         switch (policy_tokenize(k, klen)) {
-#define CASE(up, low)  case PTK_##up: query->low = v; v[vlen] = '\0';  break;
+#define CASE(up, low)  case PTK_##up: query->low.str = v; query->low.len = vlen; v[vlen] = '\0';  break;
             CASE(HELO_NAME,           helo_name);
             CASE(QUEUE_ID,            queue_id);
             CASE(RECIPIENT_COUNT,     recipient_count);
@@ -92,20 +110,27 @@ bool query_parse(query_t *query, char *p)
 #undef CASE
 
           case PTK_SENDER:
-            query->sender = v;
+            query->sender.str = v;
+            query->sender.len = vlen;
             v[vlen] = '\0';
-            query->sender_domain = memchr(query->sender, '@', vlen);
-            if (query->sender_domain != NULL) {
-                ++query->sender_domain;
+            query->sender_domain.str = memchr(query->sender.str, '@', vlen);
+            if (query->sender_domain.str != NULL) {
+                ++query->sender_domain.str;
+                query->sender_domain.len = query->sender.len
+                                         - (query->sender_domain.str - query->sender.str);
             }
             break;
 
           case PTK_RECIPIENT:
-            query->recipient = v;
+            query->recipient.str = v;
+            query->recipient.len = vlen;
             v[vlen] = '\0';
-            query->recipient_domain = memchr(query->recipient, '@', vlen);
-            if (query->recipient_domain != NULL) {
-                ++query->recipient_domain;
+            query->recipient_domain.str = memchr(query->recipient.str, '@', vlen);
+            if (query->recipient_domain.str != NULL) {
+                ++query->recipient_domain.str;
+                query->recipient_domain.len = query->recipient.len
+                                         - (query->recipient_domain.str - query->recipient.str);
+
             }
             break;
 
@@ -149,11 +174,11 @@ bool query_parse(query_t *query, char *p)
 #undef PARSE_CHECK
 }
 
-const char *query_field_for_id(const query_t *query, postlicyd_token id)
+const static_str_t *query_field_for_id(const query_t *query, postlicyd_token id)
 {
     switch (id) {
 #define CASE(Up, Low)                                                          \
-      case PTK_ ## Up: return query->Low;
+      case PTK_ ## Up: return &query->Low;
       CASE(HELO_NAME, helo_name)
       CASE(QUEUE_ID, queue_id)
       CASE(SENDER, sender)
@@ -178,13 +203,96 @@ const char *query_field_for_id(const query_t *query, postlicyd_token id)
       CASE(ETRN_DOMAIN, etrn_domain)
       CASE(STRESS, stress)
 #undef CASE
+      case PTK_PROTOCOL_NAME:
+        return query->esmtp ? &static_ESMTP : &static_SMTP;
+
+      case PTK_PROTOCOL_STATE:
+        return &smtp_state_names[query->state];
+
       default: return NULL;
     }
 }
 
-const char *query_field_for_name(const query_t *query, const char *name)
+const static_str_t *query_field_for_name(const query_t *query, const char *name)
 {
     postlicyd_token id = policy_tokenize(name, strlen(name));
+    if (id == PTK_UNKNOWN) {
+        warn("unknown query field %s", name);
+        return NULL;
+    }
     return query_field_for_id(query, id);
 }
 
+ssize_t query_format(char *dest, size_t len, const char *fmt, const query_t *query)
+{
+    size_t written = 0;
+    size_t pos = 0;
+
+#define WRITE(Src, Len)                                                        \
+    do {                                                                       \
+        size_t __len     = (Len);                                              \
+        if (written < len) {                                                   \
+            size_t __to_write = MIN(len - written - 1, __len);                 \
+            memcpy(dest + written, (Src), __to_write);                         \
+            written += __to_write;                                             \
+        }                                                                      \
+        pos += __len;                                                          \
+    } while (0)
+    while (*fmt != '\0') {
+        const char *next_format = strchr(fmt, '$');
+        while (next_format != NULL && next_format[1] != '{') {
+            next_format = strchr(next_format + 1, '$');
+        }
+        if (next_format == NULL) {
+            next_format = fmt + m_strlen(fmt);
+        }
+        WRITE(fmt, next_format - fmt);
+        fmt = next_format;
+        if (*fmt != '\0') {
+            fmt += 2;
+            next_format = strchr(fmt, '}');
+            if (next_format == NULL) {
+                return -1;
+            }
+
+            postlicyd_token tok = policy_tokenize(fmt, next_format - fmt);
+            if (tok == PTK_UNKNOWN) {
+                warn("unknown field name \"%.*s\"", (int)(next_format - fmt), fmt);
+            }
+            const static_str_t *field = query == NULL ? NULL
+                                                      : query_field_for_id(query, tok);
+            if (field == NULL) {
+                WRITE("(null)", 6);
+            } else {
+                WRITE(field->str, field->len);
+            }
+            fmt = next_format + 1;
+        }
+    }
+
+    if (written > 0 && len > 0) {
+        dest[written] = '\0';
+    }
+    return pos;
+}
+
+bool query_format_buffer(buffer_t *buf, const char *fmt, const query_t *query)
+{
+    buffer_ensure(buf, m_strlen(fmt) + 64);
+
+    ssize_t size = array_free_space(*buf);
+    ssize_t format_size = query_format(array_end(*buf),
+                                       size, fmt, query);
+    if (format_size == -1) {
+        return false;
+    } else if (format_size > size) {
+        buffer_ensure(buf, format_size + 1);
+        query_format(array_end(*buf),
+                     array_free_space(*buf),
+                     fmt, query);
+        array_len(*buf) += format_size;
+    } else {
+        array_len(*buf) += format_size;
+    }
+    return true;
+}