/* $Id$ */ /*** This file is part of ivam2. ivam2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ivam2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ivam2; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "msntab.h" #define MAX_ENTRIES 100 #define MAX_INCLUDES 10 static struct tabentry *first = NULL; static struct tabentry *last = NULL; static int n_entries = 0; static int n_includes = 0; int regex_match(const char *re, const char *s) { regex_t r; int ret; char e[256]; if ((ret = regcomp(&r, re, REG_EXTENDED|REG_NOSUB)) != 0) { regerror(ret, &r, e, sizeof(e)); daemon_log(LOG_WARNING, "Failed to compile regular expression '%s', will never match: %s", re, e); return -1; } ret = regexec(&r, s, 0, NULL, 0) == 0 ? 0 : -1; regfree(&r); return ret; } int glob_match(const char *e, const char *s) { char p[256]; const char *n = e; for (;;) { size_t m, i = strcspn(n, ";,"); m = sizeof(p)-1 < i ? sizeof(p)-1 : i; strncpy(p, n, m); p[m] = 0; if (!fnmatch(p, s, 0)) return 0; if (*(n+i) == 0) return -1; n += i+1; } } /* ** ** The functions make_time, time_match, and day_match have ** been borrowed from the file rcvbox.c of vbox. ** ** Original Copyright: ** ** Copyright (C) 1996, 1997 Michael 'Ghandi' Herold */ static time_t make_time(time_t timenow, char *timestr, int mode) { struct tm *locala; struct tm localb; char timestring[5 + 1]; char *hourstr, *minsstr; int hourint, minsint, secsint; strncpy(timestring, timestr, 5); hourstr = timestring; minsstr = index(hourstr, ':'); if (!minsstr) { if (!mode) minsstr = "00"; else minsstr = "59"; } else *minsstr++ = '\0'; if (!mode) secsint = 0; else secsint = 59; hourint = atoi(hourstr); minsint = atoi(minsstr); if (hourint < 0 || hourint > 23) return 0; if (minsint < 0 || minsint > 59) return 0; locala = localtime(&timenow); if (!locala) return 0; localb = *locala; localb.tm_sec = secsint; localb.tm_min = minsint; localb.tm_hour = hourint; return mktime(&localb); } int time_match(char *timestr) { char *timestring; char *timeptr; char *timenxt; char *timebeg, *timeend; time_t timenow; time_t timesecsbeg, timesecsend; int res = 0; timestring = strdup(timestr); assert(timestring); timeptr = timestring; timenow = time(NULL); if (!strcmp(timestring, "*")) { res = 1; goto out; } if (!strcmp(timestring, "!") || !strcmp(timestring, "-")) goto out; while (timeptr) { timenxt = index(timeptr, ','); if (timenxt) *timenxt++ = '\0'; timebeg = timeptr; timeend = index(timebeg, '-'); if (timeend) *timeend++ = '\0'; else timeend = timebeg; if (!timeend) timeend = timebeg; timesecsbeg = make_time(timenow, timebeg, 0); timesecsend = make_time(timenow, timeend, 1); if (timesecsbeg < 0 || timesecsend < timesecsbeg) daemon_log(LOG_WARNING, "End time less then begin time in timestring %s", timestr); else if (timenow >= timesecsbeg && timenow <= timesecsend) { res = 1; goto out; } timeptr = timenxt; } out: if (timestring) free(timestring); return res; } int day_match(char *strdays) { static char *weekdaynames[] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT", "WEEKEND", "WORK", "WORK", "WORK", "WORK", "WORK", "WEEKEND", NULL }; struct tm *timelocal; char *beg, *nxt; char *days; int i; time_t timenow; int res = 0; days = strdup(strdays); assert(days); if (!strcmp(days, "*")) { res = 1; goto out; } if (!strcmp(days, "-") || !strcmp(days, "!") ) goto out; timenow = time(NULL); timelocal = localtime(&timenow); if (!timelocal) goto out; for (i = 0; i < strlen(days); i++) { if (!isalpha(days[i]) && days[i] != ',') { daemon_log(LOG_WARNING, "Error in daystring %s", strdays); goto out; } } beg = days; while (beg) { nxt = index(beg, ','); if (nxt) *nxt++ = 0; for (i = 0; weekdaynames[i]; i++) { if (!strcasecmp(weekdaynames[i], beg) && (i%7) == timelocal->tm_wday) { res = 1; goto out; } } beg = nxt; } out: if (days) free(days); return res; } struct tabentry* msntab_check_call(const char *callee, const char *caller) { struct tabentry *l = first; while (l) { int a, b, c, d; assert(l->local && l->remote && l->dayspec && l->timespec); if (l->local[0] == '~') a = !regex_match(l->local+1, callee); else a = !glob_match(l->local, callee); if (l->remote[0] == '~') b = !regex_match(l->remote+1, caller); else b = !glob_match(l->remote, caller); c = day_match(l->dayspec); d = time_match(l->timespec); if (a && b && c && d) { daemon_log(LOG_INFO, "MSN table entry from '%s:%u' matched.", l->filename, l->line); return msntab_ref(l); } l = l->next; } return NULL; } struct tabentry* msntab_ref(struct tabentry *t) { assert(t && t->ref_counter >= 1); t->ref_counter++; return t; } void msntab_unref(struct tabentry *t) { assert(t && t->ref_counter >= 1); t->ref_counter--; if (t->ref_counter == 0) { if (t->args) { char **a = t->args; while (*a) { free(*a); a++; } free(t->args); } free(t->local); free(t->remote); free(t->dayspec); free(t->timespec); free(t->filename); free(t); } } void msntab_flush(void) { while (first) { struct tabentry *l = first; first = first->next; if (first) first->prev = NULL; if (last == l) last = NULL; msntab_unref(l); } n_entries = 0; n_includes++; } #define MAX_ARGS 16 static char** parse_args(const char *s) { char *o, **a; char *c = (char*) s; int i = 0; a = malloc(sizeof(char *)*MAX_ARGS); memset(a, 0, sizeof(char *)*MAX_ARGS); while ((o = strsep(&c, " \t"))) { a[i++] = strdup(o); if (i >= MAX_ARGS-1) break; } return a; } static int parse_options(const char *s, struct tabentry *t) { char *o; char *c = (char*) s; assert(s && t); while ((o = strsep(&c, ","))) { if (!strcmp(o, "defaults")) continue; else if (!strcmp(o, "shbuf")) { t->shbuf = 1; continue; } else if (!strcmp(o, "pipehack")) { t->pipehack = 1; continue; } else if (!strncmp(o, "rings=", 6)) { t->rings = atoi(o+6); continue; } daemon_log(LOG_INFO, "Unknown option '%s'", o); return -1; } return 0; } int msntab_load(const char *fn) { int n; struct tabentry *t = NULL; FILE *f = NULL; daemon_log(LOG_INFO, "Loading MSN table '%s'.", fn); if (!(f = fopen(fn, "r"))) { daemon_log(LOG_ERR, "Failed to open MSN table '%s'.", fn); goto fail; } n = 0; while (!feof(f)) { char l[256], *c, *e, *local, *remote, *dayspec, *timespec, *options, *action; n++; if (!fgets(l, sizeof(l), f)) break; c = l+strspn(l, " \t"); if ((e = strchr(c, '\r'))) *e = 0; if ((e = strchr(c, '\n'))) *e = 0; e = strchr(c, 0); if (*c == '#' || *c == 0) continue; if (!(local = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on local MSN field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!strcmp(local, "@include")) { char *include; if (n_includes ++ >= MAX_INCLUDES) { daemon_log(LOG_ERR, "Recursive include directive detected."); goto fail; } if (!(include = strsep(&c, ""))) { daemon_log(LOG_ERR, "Parse failure on include field in '%s:%i'.", fn, n); goto fail; } if (msntab_load(include) < 0) goto fail; continue; } if (!(remote = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on remote MSN field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(dayspec = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on dayspec field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(timespec = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on timespec field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(options = strsep(&c, " \t"))) { daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n); goto fail; } if (c) c+=strspn(c, " \t"); if (!(action = strsep(&c, ""))) { daemon_log(LOG_ERR, "Parse failure on action field in '%s:%i'.", fn, n); goto fail; } t = malloc(sizeof(struct tabentry)); assert(t); memset(t, 0, sizeof(struct tabentry)); t->line = n; t->filename = strdup(fn); t->ref_counter = 1; t->local = strdup(local); assert(t->local); t->remote = strdup(remote); assert(t->remote); t->dayspec = strdup(dayspec); assert(t->dayspec); t->timespec = strdup(timespec); assert(t->timespec); if (action[0] == '@') { if (!strcmp(action, "@hangup")) t->action = CALL_ACTION_HANGUP; else if (!strcmp(action, "@ignore")) t->action = CALL_ACTION_IGNORE; else { daemon_log(LOG_ERR, "Unknown action command '%s' in '%s:%i'.", action, fn, n); goto fail; } } else { t->action = CALL_ACTION_ACCEPT; t->args = parse_args(action); } if (parse_options(options, t) < 0) { daemon_log(LOG_ERR, "Parse failure on options field in '%s:%i'.", fn, n); goto fail; } if (last) { t->prev = last; last->next = t; last = t; } else last = first = t; n_entries ++; t = NULL; if (n_entries > MAX_ENTRIES) { daemon_log(LOG_INFO, "Too many MSN table entries"); goto fail; } } fclose(f); daemon_log(LOG_INFO, "MSN table '%s' successfully read.", fn); return 0; fail: if (t) msntab_unref(t); if (f) fclose(f); return -1; } static void dump_entry(struct tabentry *t) { char s[256]; assert(t); s[0] = 0; if (t->args) { char **a = t->args; while (*a) { char *p = strchr(s, 0); snprintf(p, sizeof(s)-(p-s), a == t->args ? "%s" : " %s", *a); a++; } } else strncpy(s, "NOARGS", sizeof(s)); daemon_log(LOG_INFO, "[%s:%02u] %-12s -> %-12s; %s %s; shbuf=%c; pipehack=%c; rings=%u; action=%s; args=<%s>", t->filename, t->line, t->local, t->remote, t->dayspec, t->timespec, t->shbuf ? 'y' : 'n', t->pipehack ? 'y' : 'n', t->rings, t->action == CALL_ACTION_ACCEPT ? "ACCEPT" : (t->action == CALL_ACTION_HANGUP ? "HANGUP" : "IGNORE"), s); } void msntab_dump(void) { struct tabentry *l; daemon_log(LOG_INFO, "=== Dumping MSN table ==="); l = first; while (l) { dump_entry(l); l = l->next; } daemon_log(LOG_INFO, "=== MSN table dump complete ==="); }