/* * Asterisk -- A telephony toolkit for Linux. * * TimeCondition application * * Copyright (c) 2002 Tilghman Lesher. All rights reserved. * * Tilghman Lesher * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include static char *tdesc = "Cron-like conditional goto"; static char *app = "TimeCondition"; static char *timecondition_synopsis = "Conditionally branches, based upon the current system time"; static char *timecondition_descrip = "TimeCondition(min&hour&day&month&wkday:[[context|]extension|]priority)\n" "Allow a time specifier to determine when to branch to a new extension.\n" "Each portion of the time specifier permits the following grammar:\n" " time := ( '*' | INTEGER | range ) [ '/' divisor ] [ '|' time [ ... ] ]\n" " divisor := INTEGER\n" " range := INTEGER '-' INTEGER\n"; STANDARD_LOCAL_USER; LOCAL_USER_DECL; static int time_in_argument(int, char *); static int timecondition_exec(struct ast_channel *chan, void *data) { int res=0,i; struct localuser *u; char *s, *ts; char *exten, *pri, *context; char *tm_parse[5]; struct timeval my_unixtime; struct timezone my_zone; struct tm my_time; gettimeofday(&my_unixtime,&my_zone); localtime_r(&(my_unixtime.tv_sec),&my_time); #if 0 if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "TimeCondition: time=%d %04d-%02d-%02d %02d:%02d\n", (int)my_unixtime.tv_sec, my_time.tm_year + 1900, my_time.tm_mon + 1, my_time.tm_mday, my_time.tm_hour, my_time.tm_min); #endif if (!data) { ast_log(LOG_WARNING, "TimeCondition requires an argument (min&hour&day&month&wkday:[[context|]extension|]priority)\n"); return -1; } LOCAL_USER_ADD(u); s = strdup((void *) data); ts = s; /* Get our time arguments here */ for (i=0;i<4;i++) { tm_parse[i] = strsep(&ts,"&"); } tm_parse[4] = strsep(&ts,":"); /* Compare each portion of the time individually */ if (time_in_argument(my_time.tm_min,tm_parse[0]) && time_in_argument(my_time.tm_hour,tm_parse[1]) && time_in_argument(my_time.tm_mday,tm_parse[2]) && time_in_argument(my_time.tm_mon,tm_parse[3]) && time_in_argument(my_time.tm_wday,tm_parse[4]) ) { #if 0 if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "TimeCondition: Branch taken\n" ); #endif /* This is mostly copied straight from ../pbx.c:pbx_builtin_goto */ context = strsep(&ts, "|"); exten = strsep(&ts, "|"); if (!exten) { /* Only a priority */ pri = context; exten = NULL; context = NULL; } else { pri = strsep(&ts, "|"); if (!pri) { pri = exten; exten = context; context = NULL; } } if (atoi(pri) < 0) { ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", pri); free(s); LOCAL_USER_REMOVE(u); return -1; } /* At this point we have a priority and */ /* maybe an extension and a context */ chan->priority = atoi(pri) - 1; if (exten && strcasecmp(exten, "BYEXTENSION")) strncpy(chan->exten, exten, sizeof(chan->exten)-1); if (context) strncpy(chan->context, context, sizeof(chan->context)-1); if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "TimeCondition Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1); LOCAL_USER_REMOVE(u); } free(s); return res; } static int time_in_argument(int time_portion, char *time_argument) { /* * Argument grammar: * * ( NN[-NN] | '*' )[/NN][|...] */ int i; char *current_partition; /* Integer strings for parsing */ char n1string[11]="", n2string[11]="", n3string[11]=""; /* Converted integers */ int n1,n2,n3; /* Duplicated strings */ char *s, *ts, *t, *tt; s = strdup(time_argument); ts = s; #if 0 if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "TimeCondition: Called time_in_argument(%d,%s)\n",time_portion,time_argument ); #endif /* Grab the first section of the argument (up to any '|') Lather, rinse, repeat */ current_partition = strsep(&ts,"|"); t = strdup(current_partition); tt = t; #if 0 if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "TimeCondition: current_partition=%s\n",t ); #endif while ((tt != NULL) && (tt[0] != '\0')) { if (tt[0] == '*') { /* The only thing which can come after '*' is '/' */ if (current_partition[1] == '/') { n3 = atoi(t + 2); if ((time_portion % n3) == 0) { free(s); free(t); return 1; } } else { /* '*' matches everything */ free(s); free(t); return 1; } } else { char *current_number = n1string; int j=0; for (i=0;tt[i] != '\0';i++) { if ((tt[i] >= '0') && (tt[i] <= '9')) { current_number[j++] = tt[i]; } else { if (tt[i] == '-') { /* Next number copies into n2 */ j = 0; current_number = n2string; } else if (tt[i] == '/') { /* Final number into n3 */ j = 0; current_number = n3string; } else { /* Illegal character */ if (option_verbose > 2) ast_verbose( VERBOSE_PREFIX_3 "Illegal character in TimeCondition (x%hhx) i=%d\n", tt[i], i); free(s); free(t); return 0; } } } /* Now evaluate the numbers we just parsed */ n1 = atoi(n1string); n2 = atoi(n2string); n3 = atoi(n3string); if (n2string[0] == '\0') { /* Not a range */ if (n3string[0] == '\0') { /* No modifier */ if (n1 == time_portion) { free(s); free(t); return 1; } } else { /* This one is nonsensical ( NN/NN ), but we'll do it anyway, for completeness */ if ((n1 % n3) == 0) { free(s); free(t); return 1; } } } else { /* Houston, we have a range */ if ((time_portion >= n1) && (time_portion <= n2)) { if (n3string[0] == '\0') { /* No modifier */ free(s); free(t); return 1; } else { if ((time_portion % n3) == 0) { free(s); free(t); return 1; } } } } } /* Grab the next partition, if any */ free(t); current_partition = strsep(&ts,"|"); if (current_partition != NULL) { t = strdup(current_partition); tt = t; } else { t = NULL; tt = NULL; } } /* If we get to this point, there was no match */ free(s); if (t != NULL) free(t); return 0; } int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; return ast_unregister_application(app); } int load_module(void) { return ast_register_application(app, timecondition_exec, timecondition_synopsis, timecondition_descrip); } char *description(void) { return tdesc; } int usecount(void) { int res; STANDARD_USECOUNT(res); return res; } char *key() { return ASTERISK_GPL_KEY; }