pbx_timing.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2016, CFWare, LLC
  5. *
  6. * Corey Farrell <git@cfware.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief PBX timing routines.
  21. *
  22. * \author Corey Farrell <git@cfware.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  29. #include "asterisk/localtime.h"
  30. #include "asterisk/logger.h"
  31. #include "asterisk/pbx.h"
  32. #include "asterisk/strings.h"
  33. #include "asterisk/utils.h"
  34. /*! \brief Helper for get_range.
  35. * return the index of the matching entry, starting from 1.
  36. * If names is not supplied, try numeric values.
  37. */
  38. static int lookup_name(const char *s, const char * const names[], int max)
  39. {
  40. int i;
  41. if (names && *s > '9') {
  42. for (i = 0; names[i]; i++) {
  43. if (!strcasecmp(s, names[i])) {
  44. return i;
  45. }
  46. }
  47. }
  48. /* Allow months and weekdays to be specified as numbers, as well */
  49. if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
  50. /* What the array offset would have been: "1" would be at offset 0 */
  51. return i - 1;
  52. }
  53. return -1; /* error return */
  54. }
  55. /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
  56. * names, if supplied, is an array of names that should be mapped to numbers.
  57. */
  58. static unsigned get_range(char *src, int max, const char * const names[], const char *msg)
  59. {
  60. int start, end; /* start and ending position */
  61. unsigned int mask = 0;
  62. char *part;
  63. /* Check for whole range */
  64. if (ast_strlen_zero(src) || !strcmp(src, "*")) {
  65. return (1 << max) - 1;
  66. }
  67. while ((part = strsep(&src, "&"))) {
  68. /* Get start and ending position */
  69. char *endpart = strchr(part, '-');
  70. if (endpart) {
  71. *endpart++ = '\0';
  72. }
  73. /* Find the start */
  74. if ((start = lookup_name(part, names, max)) < 0) {
  75. ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
  76. continue;
  77. }
  78. if (endpart) { /* find end of range */
  79. if ((end = lookup_name(endpart, names, max)) < 0) {
  80. ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
  81. continue;
  82. }
  83. } else {
  84. end = start;
  85. }
  86. /* Fill the mask. Remember that ranges are cyclic */
  87. mask |= (1 << end); /* initialize with last element */
  88. while (start != end) {
  89. mask |= (1 << start);
  90. if (++start >= max) {
  91. start = 0;
  92. }
  93. }
  94. }
  95. return mask;
  96. }
  97. /*! \brief store a bitmask of valid times, one bit each 1 minute */
  98. static void get_timerange(struct ast_timing *i, char *times)
  99. {
  100. char *endpart, *part;
  101. int x;
  102. int st_h, st_m;
  103. int endh, endm;
  104. int minute_start, minute_end;
  105. /* start disabling all times, fill the fields with 0's, as they may contain garbage */
  106. memset(i->minmask, 0, sizeof(i->minmask));
  107. /* 1-minute per bit */
  108. /* Star is all times */
  109. if (ast_strlen_zero(times) || !strcmp(times, "*")) {
  110. /* 48, because each hour takes 2 integers; 30 bits each */
  111. for (x = 0; x < 48; x++) {
  112. i->minmask[x] = 0x3fffffff; /* 30 bits */
  113. }
  114. return;
  115. }
  116. /* Otherwise expect a range */
  117. while ((part = strsep(&times, "&"))) {
  118. if (!(endpart = strchr(part, '-'))) {
  119. if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
  120. ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
  121. continue;
  122. }
  123. i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
  124. continue;
  125. }
  126. *endpart++ = '\0';
  127. /* why skip non digits? Mostly to skip spaces */
  128. while (*endpart && !isdigit(*endpart)) {
  129. endpart++;
  130. }
  131. if (!*endpart) {
  132. ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
  133. continue;
  134. }
  135. if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
  136. ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
  137. continue;
  138. }
  139. if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
  140. ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
  141. continue;
  142. }
  143. minute_start = st_h * 60 + st_m;
  144. minute_end = endh * 60 + endm;
  145. /* Go through the time and enable each appropriate bit */
  146. for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
  147. i->minmask[x / 30] |= (1 << (x % 30));
  148. }
  149. /* Do the last one */
  150. i->minmask[x / 30] |= (1 << (x % 30));
  151. }
  152. /* All done */
  153. return;
  154. }
  155. static const char * const days[] =
  156. {
  157. "sun",
  158. "mon",
  159. "tue",
  160. "wed",
  161. "thu",
  162. "fri",
  163. "sat",
  164. NULL,
  165. };
  166. static const char * const months[] =
  167. {
  168. "jan",
  169. "feb",
  170. "mar",
  171. "apr",
  172. "may",
  173. "jun",
  174. "jul",
  175. "aug",
  176. "sep",
  177. "oct",
  178. "nov",
  179. "dec",
  180. NULL,
  181. };
  182. /*! /brief Build timing
  183. *
  184. * /param i info
  185. * /param info_in
  186. *
  187. */
  188. int ast_build_timing(struct ast_timing *i, const char *info_in)
  189. {
  190. char *info;
  191. int j, num_fields, last_sep = -1;
  192. i->timezone = NULL;
  193. /* Check for empty just in case */
  194. if (ast_strlen_zero(info_in)) {
  195. return 0;
  196. }
  197. /* make a copy just in case we were passed a static string */
  198. info = ast_strdupa(info_in);
  199. /* count the number of fields in the timespec */
  200. for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
  201. if (info[j] == ',') {
  202. last_sep = j;
  203. num_fields++;
  204. }
  205. }
  206. /* save the timezone, if it is specified */
  207. if (num_fields == 5) {
  208. i->timezone = ast_strdup(info + last_sep + 1);
  209. }
  210. /* Assume everything except time */
  211. i->monthmask = 0xfff; /* 12 bits */
  212. i->daymask = 0x7fffffffU; /* 31 bits */
  213. i->dowmask = 0x7f; /* 7 bits */
  214. /* on each call, use strsep() to move info to the next argument */
  215. get_timerange(i, strsep(&info, "|,"));
  216. if (info)
  217. i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
  218. if (info)
  219. i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
  220. if (info)
  221. i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
  222. return 1;
  223. }
  224. int ast_check_timing(const struct ast_timing *i)
  225. {
  226. return ast_check_timing2(i, ast_tvnow());
  227. }
  228. int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
  229. {
  230. struct ast_tm tm;
  231. ast_localtime(&tv, &tm, i->timezone);
  232. /* If it's not the right month, return */
  233. if (!(i->monthmask & (1 << tm.tm_mon)))
  234. return 0;
  235. /* If it's not that time of the month.... */
  236. /* Warning, tm_mday has range 1..31! */
  237. if (!(i->daymask & (1 << (tm.tm_mday-1))))
  238. return 0;
  239. /* If it's not the right day of the week */
  240. if (!(i->dowmask & (1 << tm.tm_wday)))
  241. return 0;
  242. /* Sanity check the hour just to be safe */
  243. if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
  244. ast_log(LOG_WARNING, "Insane time...\n");
  245. return 0;
  246. }
  247. /* Now the tough part, we calculate if it fits
  248. in the right time based on min/hour */
  249. if (!(i->minmask[tm.tm_hour * 2 + (tm.tm_min >= 30 ? 1 : 0)] & (1 << (tm.tm_min >= 30 ? tm.tm_min - 30 : tm.tm_min))))
  250. return 0;
  251. /* If we got this far, then we're good */
  252. return 1;
  253. }
  254. int ast_destroy_timing(struct ast_timing *i)
  255. {
  256. if (i->timezone) {
  257. ast_free(i->timezone);
  258. i->timezone = NULL;
  259. }
  260. return 0;
  261. }