diff src/context.cpp @ 71:dd21c8e13074

start coding on new config syntax
author carl
date Sat, 09 Jul 2005 19:24:41 -0700
parents
children e6a2d0be7c5e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/context.cpp	Sat Jul 09 19:24:41 2005 -0700
@@ -0,0 +1,727 @@
+#include "context.h"
+
+static char* context_version="$Id:";
+
+char *token_black;
+char *token_content;
+char *token_context;
+char *token_dccfrom;
+char *token_dccto;
+char *token_default;
+char *token_dnsbl;
+char *token_dnsbll;
+char *token_envfrom;
+char *token_envto;
+char *token_filter;
+char *token_host_limit;
+char *token_html_limit;
+char *token_html_tags;
+char *token_ignore;
+char *token_include;
+char *token_inherit;
+char *token_lbrace;
+char *token_many;
+char *token_off;
+char *token_ok;
+char *token_ok2;
+char *token_on;
+char *token_rbrace;
+char *token_semi;
+char *token_soft;
+char *token_tld;
+char *token_unknown;
+char *token_white;
+
+string_set all_strings;     // owns all the strings, only modified by the config loader thread
+
+DNSBL::DNSBL(char *n, char *s, char *m) {
+    name    = n;
+    suffix  = s;
+    message = m;
+}
+
+
+CONFIG::CONFIG() {
+    reference_count    = 0;
+    generation         = 0;
+    load_time          = 0;
+    default_context    = NULL;
+}
+
+
+CONFIG::~CONFIG() {
+    for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
+        CONTEXT *c = *i;
+        delete c;
+    }
+}
+
+
+void CONFIG::add_context(CONTEXTP con) {
+    contexts.push_back(con);
+    if (!default_context && !con->get_parent()) {
+        // first global context
+        default_context = con;
+    }
+}
+
+
+CONTEXTP CONFIG::find_context(char *to, char *from) {
+    CONTEXTP con = NULL;
+    context_map::iterator i = env_to.find(from);
+    if (i != env_to.end()) {
+        con = (*i).second;
+        return con->find_from_context(from);
+    }
+    char *x = strchr(to, '@');
+    if (x) {
+        x++;
+        i = env_to.find(x);
+        if (i != env_to.end()) {
+            con = (*i).second;
+            return con->find_from_context(from);
+        }
+    }
+    if (default_context) {
+        return default_context->find_from_context(from);
+    }
+    return NULL;
+}
+
+
+void CONFIG::dump() {
+    if (default_context) default_context->dump();
+    for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) {
+        CONTEXTP c = *i;
+        CONTEXTP p = c->get_parent();
+        if (!p && (c != default_context)) c->dump();
+    }
+}
+
+
+CONTEXT::CONTEXT(CONTEXTP parent_, char *name_) {
+    parent              = parent_;
+    name                = name_;
+    env_from_default    = (parent) ? token_inherit : token_unknown;
+    content_filtering   = (parent) ? parent->content_filtering : false;
+    content_suffix      = NULL;
+    content_message     = NULL;
+    host_limit          = 0;
+    host_limit_message  = NULL;
+    host_random         = false;
+    tag_limit           = 0;
+    tag_limit_message   = NULL;
+}
+
+
+CONTEXT::~CONTEXT() {
+    for (dnsblp_map::iterator i=dnsbl_names.begin(); i!=dnsbl_names.end(); i++) {
+        DNSBLP d = (*i).second;
+        // delete the underlying DNSBL objects.
+        delete d;
+    }
+}
+
+
+char *CONTEXT::get_full_name(char *buffer, int size) {
+    if (!parent) return name;
+    const int maxlen = 1000;
+    char buf[maxlen];
+    snprintf(buffer, size, "%s.%s", parent->get_full_name(buf, maxlen), name);
+    return buffer;
+}
+
+
+bool CONTEXT::cover_env_to(char *to) {
+    const int maxlen = 1000;
+    char buffer[maxlen];
+    char *x = strchr(to, '@');
+    if (x) x++;
+    else   x = to;
+    string_set::iterator i = env_to.find(x);
+    if (i != env_to.end()) return true;
+    return (parent) ? parent->cover_env_to(to) : false;
+}
+
+
+char *CONTEXT::find_from(char *from) {
+    // do  we have a white/black/unknown for this full from value?
+    string_map::iterator i = env_from.find(from);
+    if (i != env_from.end()) return (*i).second;
+    // do we have a white/black/unknown for the source domain name?
+    char *x = strchr(from, '@');
+    if (x) {
+        x++;
+        i = env_from.find(x);
+        if (i != env_from.end()) return (*i).second;
+    }
+    if ((env_from_default == token_inherit) && parent) {
+        return parent->find_from(from);
+    }
+    return env_from_default;
+}
+
+
+CONTEXTP CONTEXT::find_from_context(char *from) {
+    // do we have a special child context for this full from value?
+    context_map::iterator j = env_from_context.find(from);
+    if (j != env_from_context.end()) {
+        CONTEXTP con = (*j).second;
+        return con->find_from_context(from);
+    }
+    char *x = strchr(from, '@');
+    if (x) {
+        x++;
+        // do we have a special context for the source domain name?
+        j = env_from_context.find(x);
+        if (j != env_from_context.end()) {
+            CONTEXTP con = (*j).second;
+            return con->find_from_context(from);
+        }
+    }
+    return this;
+}
+
+
+CONTEXTP CONTEXT::find_from_context_name(char *name) {
+    context_map::iterator i = children.find(name);
+    if (i != children.end()) return (*i).second;
+    return NULL;
+}
+
+
+DNSBLP CONTEXT::find_dnsbl(char *name) {
+    dnsblp_map::iterator i = dnsbl_names.find(name);
+    if (i != dnsbl_names.end()) return (*i).second;
+    if (parent) return parent->find_dnsbl(name);
+    return NULL;
+}
+
+
+void CONTEXT::dump(int level) {
+    const int maxlen = 1000;
+    char indent[maxlen];
+    int i = min(maxlen-1, level*4);
+    memset(indent, ' ', i);
+    indent[i] = '\0';
+    printf("%s context %s { \n", indent, name);
+
+    for (dnsblp_map::iterator i=dnsbl_names.begin(); i!=dnsbl_names.end(); i++) {
+        char *n = (*i).first;
+        DNSBL &d = *(*i).second;
+        printf("%s     dnsbl %s %s \"%s\"; \n", indent, n, d.suffix, d.message);
+    }
+
+    if (!dnsbl_list.empty()) {
+        printf("%s     dnsbl_list", indent);
+        for (dnsblp_list::iterator i=dnsbl_list.begin(); i!=dnsbl_list.end(); i++) {
+            DNSBL &d = *(*i);
+            printf(" %s", d.name);
+        }
+        printf("; \n");
+    }
+
+    if (content_filtering) {
+        printf("%s     content on { \n", indent, env_from_default);
+        if (content_suffix) {
+            printf("%s         filter %s \"%s\"; \n", indent, content_suffix, content_message);
+        }
+        if (!content_host_ignore.empty()) {
+            printf("%s         ignore { \n", indent);
+            for (string_set::iterator i=content_host_ignore.begin(); i!=content_host_ignore.end(); i++) {
+                printf("%s             %s; \n", indent, *i);
+            }
+            printf("%s         }; \n", indent);
+        }
+        if (!content_tlds.empty()) {
+            printf("%s         tld { \n", indent);
+            printf("%s             ", indent);
+            for (string_set::iterator i=content_tlds.begin(); i!=content_tlds.end(); i++) {
+                printf("%s; ", *i);
+            }
+            printf("\n%s         }; \n", indent);
+        }
+        if (!html_tags.empty()) {
+            printf("%s         html_tags { \n", indent);
+            printf("%s             ", indent);
+            for (string_set::iterator i=html_tags.begin(); i!=html_tags.end(); i++) {
+                printf("%s; ", *i);
+            }
+            printf("\n%s         }; \n", indent);
+        }
+        if (host_limit_message) {
+            printf("%s         host_limit on %d \"%s\"; \n", indent, host_limit, host_limit_message);
+        }
+        else if (host_random) {
+            printf("%s         host_limit soft %d; \n", indent, host_limit);
+        }
+        else {
+            printf("%s         host_limit off; \n", indent);
+        }
+        if (tag_limit_message) {
+            printf("%s         tag_limit on %d \"%s\"; \n", indent, tag_limit, tag_limit_message);
+        }
+        else {
+            printf("%s         tag_limit off; \n", indent);
+        }
+        printf("%s     }; \n", indent);
+        }
+    else {
+        printf("%s     content off {}; \n", indent, env_from_default);
+    }
+
+    printf("%s     env_to { \n", indent);
+    for (string_set::iterator i=env_to.begin(); i!=env_to.end(); i++) {
+        printf("%s         %s; \n", indent, *i);
+    }
+    printf("%s     }; \n", indent);
+
+    for (context_map::iterator i=children.begin(); i!=children.end(); i++) {
+        CONTEXTP c = (*i).second;
+        c->dump(level+1);
+    }
+
+    printf("%s     env_from %s { \n", indent, env_from_default);
+    if (!env_from.empty()) {
+        printf("%s     // white/black/unknown \n", indent);
+        for (string_map::iterator i=env_from.begin(); i!=env_from.end(); i++) {
+            char *f = (*i).first;
+            char *t = (*i).second;
+            printf("%s         %s \t %s; \n", indent, f, t);
+        }
+    }
+    if (!env_from_context.empty()) {
+        printf("%s     // child contexts \n", indent);
+        for (context_map::iterator j=env_from_context.begin(); j!=env_from_context.end(); j++) {
+            char    *f = (*j).first;
+            CONTEXTP t = (*j).second;
+            printf("%s         %s \t %s; \n", indent, f, t->name);
+        }
+    }
+    printf("%s     }; \n", indent);
+
+    printf("%s }; \n", indent);
+}
+
+
+////////////////////////////////////////////////
+// helper to discard the strings held by a string_set
+//
+static void discard(string_set &s) {
+    for (string_set::iterator i=s.begin(); i!=s.end(); i++) {
+        free(*i);
+    }
+    s.clear();
+}
+
+
+////////////////////////////////////////////////
+// helper to register a string in a string set
+//
+char* register_string(string_set &s, char *name) {
+    string_set::iterator i = s.find(name);
+    if (i != s.end()) return *i;
+    char *x = strdup(name);
+    s.insert(x);
+    return x;
+}
+
+
+////////////////////////////////////////////////
+// register a global string
+//
+char* register_string(char *name) {
+    return register_string(all_strings, name);
+}
+
+
+////////////////////////////////////////////////
+//
+bool tsa(TOKEN &tok, char *token);
+bool tsa(TOKEN &tok, char *token) {
+    char *have = tok.next();
+    if (have == token) return true;
+    tok.token_error(token, have);
+    return false;
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_dnsbl(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_dnsbl(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    char *name = tok.next();
+    char *suf = tok.next();
+    char *msg = tok.next();
+    if (!tsa(tok, token_semi)) return false;
+    DNSBLP dns = new DNSBL(name, suf, msg);
+    me.add_dnsbl(name, dns);
+    return true;
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_dnsbll(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_semi) break;
+        DNSBLP dns = me.find_dnsbl(have);
+        if (dns) {
+            me.add_dnsbl(dns);
+        }
+        else {
+            tok.token_error("dnsbl name", have);
+            return false;
+        }
+    }
+    return true;
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_content(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    char *setting = tok.next();
+    if (setting == token_on) {
+        me.set_content_filtering(true);
+    }
+    else if (setting == token_off) {
+        me.set_content_filtering(false);
+    }
+    else {
+        tok.token_error("on/off", setting);
+        return false;
+    }
+    if (!tsa(tok, token_lbrace)) return false;
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_filter) {
+            me.set_content_suffix(tok.next());
+            me.set_content_message(tok.next());
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_ignore) {
+            if (!tsa(tok, token_lbrace)) return false;
+            while (true) {
+                if (!have) break;
+                char *have = tok.next();
+                if (have == token_rbrace) {
+                    break;  // done
+                }
+                else {
+                    me.add_ignore(have);
+                }
+            }
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_tld) {
+            if (!tsa(tok, token_lbrace)) return false;
+            while (true) {
+                char *have = tok.next();
+                if (!have) break;
+                if (have == token_rbrace) {
+                    break;  // done
+                }
+                else {
+                    me.add_tld(have);
+                }
+            }
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_html_limit) {
+            have = tok.next();
+            if (have == token_on) {
+                me.set_tag_limit(tok.nextint());
+                me.set_tag_message(tok.next());
+            }
+            else if (have == token_off) {
+                me.set_tag_limit(0);
+                me.set_tag_message(NULL);
+            }
+            else {
+                tok.token_error("on/off", have);
+                return false;
+            }
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_html_tags) {
+            if (!tsa(tok, token_lbrace)) return false;
+            while (true) {
+                char *have = tok.next();
+                if (!have) break;
+                if (have == token_rbrace) {
+                    break;  // done
+                }
+                else {
+                    me.add_tag(have);
+                }
+            }
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_host_limit) {
+            have = tok.next();
+            if (have == token_on) {
+                me.set_host_limit(tok.nextint());
+                me.set_host_message(tok.next());
+                me.set_host_random(false);
+            }
+            else if (have == token_off) {
+                me.set_host_limit(0);
+                me.set_host_message(NULL);
+                me.set_host_random(false);
+            }
+            else if (have == token_soft) {
+                me.set_host_limit(tok.nextint());
+                me.set_host_message(NULL);
+                me.set_host_random(true);
+            }
+            else {
+                tok.token_error("on/off/soft", have);
+                return false;
+            }
+            if (!tsa(tok, token_semi)) return false;
+        }
+        else if (have == token_rbrace) {
+            break;  // done
+        }
+        else {
+            tok.token_error("content keyword", have);
+            return false;
+        }
+    }
+    return tsa(tok, token_semi);
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_envto(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_envto(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    if (!tsa(tok, token_lbrace)) return false;
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_rbrace) break;
+        if (have == token_semi) {
+            // optional separators
+        }
+        else if (have == token_dccto) {
+            char *flavor = tok.next();
+            if (!tsa(tok, token_lbrace)) return false;
+            bool keeping = false;
+            while (true) {
+                char *have = tok.next();
+                if (!have) break;
+                if (have == token_rbrace) break;
+                if (have == flavor) {
+                    keeping = true;
+                    continue;
+                }
+                else if ((have == token_ok) || (have == token_ok2) || (have == token_many)) {
+                    keeping = false;
+                    continue;
+                }
+                if (have == token_envto) {
+                    have = tok.next();
+                    if (keeping) {
+                        if (me.allow_env_to(have)) {
+                            me.add_to(have);
+                            dc.add_to(have, &me);
+                        }
+                    }
+                }
+                tok.skipeol();
+            }
+        }
+        else if (me.allow_env_to(have)) {
+            me.add_to(have);
+            dc.add_to(have, &me);
+        }
+        else {
+            tok.token_error("valid env_to address or domain name", have);
+            return false;
+        }
+    }
+    return tsa(tok, token_semi);
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me);
+bool parse_envfrom(TOKEN &tok, CONFIG &dc, CONTEXT &me) {
+    char *st = tok.next();
+    if ((st == token_black) || (st == token_white) || (st == token_unknown)) {
+        me.set_from_default(st);
+    }
+    else {
+        tok.push(st);
+    }
+    if (!tsa(tok, token_lbrace)) return false;
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_rbrace) break;
+        if (have == token_semi) {
+            // optional separators
+        }
+        else if (have == token_dccfrom) {
+            if (!tsa(tok, token_lbrace)) return false;
+            bool keeping = false;
+            bool many = false;
+            while (true) {
+                char *have = tok.next();
+                if (!have) break;
+                if (have == token_rbrace) break;
+                if (have == token_ok) {
+                    keeping = true;
+                    many    = false;
+                    continue;
+                }
+                else if (have == token_many) {
+                    keeping = true;
+                    many    = true;
+                    continue;
+                }
+                else if (have == token_ok2) {
+                    keeping = false;
+                    continue;
+                }
+                if (have == token_envfrom) {
+                    have = tok.next();
+                    if (keeping) {
+                        me.add_from(have, (many) ? token_black : token_white);
+                    }
+                }
+                tok.skipeol();
+            }
+        }
+        else {
+            // may be a valid email address or domain name
+            char *st = tok.next();
+            if ((st == token_black) || (st == token_white) || (st == token_unknown)) {
+                me.add_from(have, st);
+            }
+            else {
+                CONTEXTP con = me.find_from_context_name(st);
+                if (con) {
+                    me.add_from_context(have, con);
+                }
+                else {
+                    tok.token_error("white/black/unknown or child context name", st);
+                    return false;
+                }
+            }
+        }
+    }
+    return tsa(tok, token_semi);
+}
+
+
+////////////////////////////////////////////////
+//
+bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent);
+bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent) {
+    char *name = tok.next();
+    if (!tsa(tok, token_lbrace)) return false;
+    CONTEXTP con = new CONTEXT(parent, name);
+
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_rbrace) break;  // done
+        if (have == token_dnsbl) {
+            if (!parse_dnsbl(tok, dc, *con)) return false;
+        }
+        else if (have == token_dnsbll) {
+            if (!parse_dnsbll(tok, dc, *con)) return false;
+        }
+        else if (have == token_content) {
+            if (!parse_content(tok, dc, *con)) return false;
+        }
+        else if (have == token_envto) {
+            if (!parse_envto(tok, dc, *con)) return false;
+        }
+        else if (have == token_envfrom) {
+            if (!parse_envfrom(tok, dc, *con)) return false;
+        }
+        else if (have == token_context) {
+            if (!parse_context(tok, dc, con)) return false;
+        }
+        else {
+            tok.token_error("context keyword", have);
+            return false;
+        }
+    }
+
+    if (!tsa(tok, token_semi)) {
+        delete con;
+        return false;
+    }
+    dc.add_context(con);
+    if (parent) parent->add_context(con);
+    return true;
+}
+
+
+////////////////////////////////////////////////
+// parse a config file
+//
+bool load_conf(CONFIG &dc, char *fn) {
+    TOKEN tok(fn, &dc.config_files);
+    while (true) {
+        char *have = tok.next();
+        if (!have) break;
+        if (have == token_context) {
+            if (!parse_context(tok, dc, NULL)) {
+                return false;
+            }
+        }
+        else {
+            tok.token_error(token_context, have);
+            return false;
+        }
+    }
+    return true;
+}
+
+
+////////////////////////////////////////////////
+// init the tokens
+//
+void token_init() {
+    token_black      = register_string("black");
+    token_content    = register_string("content");
+    token_context    = register_string("context");
+    token_dccfrom    = register_string("dcc_from");
+    token_dccto      = register_string("dcc_to");
+    token_default    = register_string("default");
+    token_dnsbl      = register_string("dnsbl");
+    token_dnsbll     = register_string("dnsbl_list");
+    token_envfrom    = register_string("env_from");
+    token_envto      = register_string("env_to");
+    token_filter     = register_string("filter");
+    token_host_limit = register_string("host_limit");
+    token_html_limit = register_string("html_limit");
+    token_html_tags  = register_string("html_tags");
+    token_ignore     = register_string("ignore");
+    token_include    = register_string("include");
+    token_inherit    = register_string("inherit");
+    token_lbrace     = register_string("{");
+    token_many       = register_string("many");
+    token_off        = register_string("off");
+    token_ok         = register_string("ok");
+    token_ok2        = register_string("ok2");
+    token_on         = register_string("on");
+    token_rbrace     = register_string("}");
+    token_semi       = register_string(";");
+    token_soft       = register_string("soft");
+    token_tld        = register_string("tld");
+    token_unknown    = register_string("unknown");
+    token_white      = register_string("white");
+}