Ignore this ... playing with jira (AST-1)
[asterisk/asterisk.git] / main / app.c
index 1099fea..0eb8591 100644 (file)
@@ -49,9 +49,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
 #include "asterisk/indications.h"
+#include "asterisk/linkedlists.h"
 
 #define MAX_OTHER_FORMATS 10
 
+static AST_LIST_HEAD_STATIC(groups, ast_group_info);
 
 /* !
 This function presents a dialtone and reads an extension into 'collect' 
@@ -62,7 +64,7 @@ of 'maxlen' or 'size' minus the original strlen() of collect digits.
 */
 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout) 
 {
-       struct tone_zone_sound *ts;
+       struct ind_tone_zone_sound *ts;
        int res=0, x=0;
 
        if (maxlen > size)
@@ -73,7 +75,7 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
        else if (!timeout)
                timeout = 5;
        
-       ts = ast_get_indication_tone(chan->zone,"dial");
+       ts = ast_get_indication_tone(chan->zone, "dial");
        if (ts && ts->data[0])
                res = ast_playtones_start(chan, 0, ts->data, 0);
        else 
@@ -85,41 +87,64 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
                        ast_playtones_stop(chan);
                if (res < 1)
                        break;
+               if (res == '#')
+                       break;
                collect[x++] = res;
-               if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
-                       if (collect[x-1] == '#') {
-                               /* Not a valid extension, ending in #, assume the # was to finish dialing */
-                               collect[x-1] = '\0';
-                       }
+               if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num))
                        break;
-               }
        }
        if (res >= 0)
                res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
        return res;
 }
 
-/*! \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
-   "ludicrous time" (essentially never times out) */
-int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
+/*! \param c The channel to read from
+ *  \param prompt The file to stream to the channel
+ *  \param s The string to read in to.  Must be at least the size of your length
+ *  \param maxlen How many digits to read (maximum)
+ *  \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
+ *      "ludicrous time" (essentially never times out) */
+int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
 {
-       int res,to,fto;
+       int res = 0, to, fto;
+       char *front, *filename;
+
        /* XXX Merge with full version? XXX */
+       
        if (maxlen)
                s[0] = '\0';
-       if (prompt) {
-               res = ast_streamfile(c, prompt, c->language);
-               if (res < 0)
+
+       if (!prompt)
+               prompt="";
+
+       filename = ast_strdupa(prompt);
+       while ((front = strsep(&filename, "&"))) {
+               if (!ast_strlen_zero(front)) {
+                       res = ast_streamfile(c, front, c->language);
+                       if (res)
+                               continue;
+               }
+               if (ast_strlen_zero(filename)) {
+                       /* set timeouts for the last prompt */
+                       fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
+                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+
+                       if (timeout > 0) 
+                               fto = to = timeout;
+                       if (timeout < 0) 
+                               fto = to = 1000000000;
+               } else {
+                       /* there is more than one prompt, so
+                          get rid of the long timeout between 
+                          prompts, and make it 50ms */
+                       fto = 50;
+                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+               }
+               res = ast_readstring(c, s, maxlen, to, fto, "#");
+               if (!ast_strlen_zero(s))
                        return res;
        }
-       fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
-       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
-
-       if (timeout > 0) 
-               fto = to = timeout;
-       if (timeout < 0) 
-               fto = to = 1000000000;
-       res = ast_readstring(c, s, maxlen, to, fto, "#");
+       
        return res;
 }
 
@@ -212,10 +237,6 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
 {
        const char *ptr;
        int res = 0;
-       struct ast_frame f = {
-               .frametype = AST_FRAME_DTMF,
-               .src = "ast_dtmf_stream"
-       };
 
        if (!between)
                between = 100;
@@ -240,11 +261,8 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
                        if (*ptr == 'f' || *ptr == 'F') {
                                /* ignore return values if not supported by channel */
                                ast_indicate(chan, AST_CONTROL_FLASH);
-                       } else {
-                               f.subclass = *ptr;
-                               if ((res = ast_write(chan, &f)))
-                                       break;
-                       }
+                       } else
+                               ast_senddigit(chan, *ptr);
                        /* pause between digits */
                        if ((res = ast_safe_sleep(chan, between)))
                                break;
@@ -347,7 +365,7 @@ int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, in
                if (filename[0] == '/') 
                        ast_copy_string(tmpf, filename, sizeof(tmpf));
                else
-                       snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)ast_config_AST_DATA_DIR, "sounds", filename);
+                       snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", ast_config_AST_DATA_DIR, "sounds", filename);
                fd = open(tmpf, O_RDONLY);
                if (fd < 0){
                        ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
@@ -423,7 +441,8 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
 
                /* We go at next loop if we got the restart char */
                if (restart && strchr(restart, res)) {
-                       ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "we'll restart the stream here at next loop\n");
                        pause_restart_point = 0;
                        continue;
                }
@@ -515,14 +534,15 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                return -1;
        }
 
-       ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
+       if (option_debug)
+               ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
        snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
 
        if (playfile || beep) {
                if (!beep)
                        d = ast_play_and_wait(chan, playfile);
                if (d > -1)
-                       d = ast_stream_and_wait(chan, "beep", chan->language, "");
+                       d = ast_stream_and_wait(chan, "beep", "");
                if (d < 0)
                        return -1;
        }
@@ -536,7 +556,8 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
 
        stringp = fmts;
        strsep(&stringp, "|");
-       ast_log(LOG_DEBUG, "Recording Formats: sfmts=%s\n", fmts);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Recording Formats: sfmts=%s\n", fmts);
        sfmt[0] = ast_strdupa(fmts);
 
        while ((fmt = strsep(&stringp, "|"))) {
@@ -549,7 +570,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
 
        end = start = time(NULL);  /* pre-initialize end to be same as start in case we never get into loop */
        for (x = 0; x < fmtcnt; x++) {
-               others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
+               others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, AST_FILE_MODE);
                if (option_verbose > 2)
                        ast_verbose(VERBOSE_PREFIX_3 "x=%d, open writing:  %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
 
@@ -591,7 +612,8 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                for (;;) {
                        res = ast_waitfor(chan, 2000);
                        if (!res) {
-                               ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
                                /* Try one more time in case of masq */
                                res = ast_waitfor(chan, 2000);
                                if (!res) {
@@ -736,7 +758,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
        }
        if (outmsg == 2) {
-               ast_stream_and_wait(chan, "auth-thankyou", chan->language, "");
+               ast_stream_and_wait(chan, "auth-thankyou", "");
        }
        if (sildet)
                ast_dsp_free(sildet);
@@ -784,61 +806,74 @@ int ast_app_group_split_group(const char *data, char *group, int group_max, char
        else
                res = -1;
 
-       if (cat)
-               snprintf(category, category_max, "%s_%s", GROUP_CATEGORY_PREFIX, cat);
-       else
-               ast_copy_string(category, GROUP_CATEGORY_PREFIX, category_max);
+       if (!ast_strlen_zero(cat))
+               ast_copy_string(category, cat, category_max);
 
        return res;
 }
 
 int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
 {
-       int res=0;
-       char group[80] = "";
-       char category[80] = "";
-
-       if (!ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category))) {
-               pbx_builtin_setvar_helper(chan, category, group);
-       } else
+       int res = 0;
+       char group[80] = "", category[80] = "";
+       struct ast_group_info *gi = NULL;
+       size_t len = 0;
+       
+       if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category)))
+               return -1;
+       
+       /* Calculate memory we will need if this is new */
+       len = sizeof(*gi) + strlen(group) + 1;
+       if (!ast_strlen_zero(category))
+               len += strlen(category) + 1;
+       
+       AST_LIST_LOCK(&groups);
+       AST_LIST_TRAVERSE(&groups, gi, list) {
+               if (gi->chan == chan && !strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+                       break;
+       }
+       
+       if (!gi && (gi = calloc(1, len))) {
+               gi->chan = chan;
+               gi->group = (char *) gi + sizeof(*gi);
+               strcpy(gi->group, group);
+               if (!ast_strlen_zero(category)) {
+                       gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
+                       strcpy(gi->category, category);
+               }
+               AST_LIST_INSERT_TAIL(&groups, gi, list);
+       } else {
                res = -1;
-
+       }
+       
+       AST_LIST_UNLOCK(&groups);
+       
        return res;
 }
 
 int ast_app_group_get_count(const char *group, const char *category)
 {
-       struct ast_channel *chan;
+       struct ast_group_info *gi = NULL;
        int count = 0;
-       const char *test;
-       char cat[80];
-       const char *s;
 
        if (ast_strlen_zero(group))
                return 0;
-
-       s = S_OR(category, GROUP_CATEGORY_PREFIX);
-       ast_copy_string(cat, s, sizeof(cat));
-
-       chan = NULL;
-       while ((chan = ast_channel_walk_locked(chan)) != NULL) {
-               test = pbx_builtin_getvar_helper(chan, cat);
-               if (test && !strcasecmp(test, group))
-                       count++;
-               ast_channel_unlock(chan);
+       
+       AST_LIST_LOCK(&groups);
+       AST_LIST_TRAVERSE(&groups, gi, list) {
+               if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || !strcasecmp(gi->category, category)))
+                       count++;
        }
+       AST_LIST_UNLOCK(&groups);
 
        return count;
 }
 
 int ast_app_group_match_get_count(const char *groupmatch, const char *category)
 {
+       struct ast_group_info *gi = NULL;
        regex_t regexbuf;
-       struct ast_channel *chan;
        int count = 0;
-       const char *test;
-       char cat[80];
-       const char *s;
 
        if (ast_strlen_zero(groupmatch))
                return 0;
@@ -847,22 +882,64 @@ int ast_app_group_match_get_count(const char *groupmatch, const char *category)
        if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
                return 0;
 
-       s = S_OR(category, GROUP_CATEGORY_PREFIX);
-       ast_copy_string(cat, s, sizeof(cat));
-
-       chan = NULL;
-       while ((chan = ast_channel_walk_locked(chan)) != NULL) {
-               test = pbx_builtin_getvar_helper(chan, cat);
-               if (test && !regexec(&regexbuf, test, 0, NULL, 0))
+       AST_LIST_LOCK(&groups);
+       AST_LIST_TRAVERSE(&groups, gi, list) {
+               if (!regexec(&regexbuf, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || !strcasecmp(gi->category, category)))
                        count++;
-               ast_channel_unlock(chan);
        }
+       AST_LIST_UNLOCK(&groups);
 
        regfree(&regexbuf);
 
        return count;
 }
 
+int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
+{
+       struct ast_group_info *gi = NULL;
+
+       AST_LIST_LOCK(&groups);
+       AST_LIST_TRAVERSE(&groups, gi, list) {
+               if (gi->chan == old)
+                       gi->chan = new;
+       }
+       AST_LIST_UNLOCK(&groups);
+
+       return 0;
+}
+
+int ast_app_group_discard(struct ast_channel *chan)
+{
+       struct ast_group_info *gi = NULL;
+       
+       AST_LIST_LOCK(&groups);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+               if (gi->chan == chan) {
+                       AST_LIST_REMOVE_CURRENT(&groups, list);
+                       free(gi);
+               }
+       }
+        AST_LIST_TRAVERSE_SAFE_END
+       AST_LIST_UNLOCK(&groups);
+       
+       return 0;
+}
+
+int ast_app_group_list_lock(void)
+{
+       return AST_LIST_LOCK(&groups);
+}
+
+struct ast_group_info *ast_app_group_list_head(void)
+{
+       return AST_LIST_FIRST(&groups);
+}
+
+int ast_app_group_list_unlock(void)
+{
+       return AST_LIST_UNLOCK(&groups);
+}
+
 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
 {
        int argc;
@@ -920,7 +997,7 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
        }
 
        snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
-       fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
        if (fd < 0) {
                ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
                return AST_LOCK_PATH_NOT_FOUND;
@@ -938,7 +1015,8 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
                ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
                return AST_LOCK_TIMEOUT;
        } else {
-               ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
                return AST_LOCK_SUCCESS;
        }
 }
@@ -957,8 +1035,10 @@ int ast_unlock_path(const char *path)
 
        if ((res = unlink(s)))
                ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
-       else
-               ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+       else {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+       }
 
        return res;
 }
@@ -991,14 +1071,14 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
                                cmd = '3';
                                break;
                        } else {
-                               ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
+                               ast_stream_and_wait(chan, "vm-msgsaved", "");
                                cmd = 't';
                                return res;
                        }
                case '2':
                        /* Review */
                        ast_verbose(VERBOSE_PREFIX_3 "Reviewing the recording\n");
-                       cmd = ast_stream_and_wait(chan, recordfile, chan->language, AST_DIGIT_ANY);
+                       cmd = ast_stream_and_wait(chan, recordfile, AST_DIGIT_ANY);
                        break;
                case '3':
                        message_exists = 0;
@@ -1073,7 +1153,7 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
        char *c;
        char *n;
        
-       switch(option->action) {
+       switch (option->action) {
        case AST_ACTION_UPONE:
                return RES_UPONE;
        case AST_ACTION_EXIT:
@@ -1085,14 +1165,14 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
        case AST_ACTION_NOOP:
                return 0;
        case AST_ACTION_BACKGROUND:
-               res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, AST_DIGIT_ANY);
+               res = ast_stream_and_wait(chan, (char *)option->adata, AST_DIGIT_ANY);
                if (res < 0) {
                        ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
                        res = 0;
                }
                return res;
        case AST_ACTION_PLAYBACK:
-               res = ast_stream_and_wait(chan, (char *)option->adata, chan->language, "");
+               res = ast_stream_and_wait(chan, (char *)option->adata, "");
                if (res < 0) {
                        ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
                        res = 0;
@@ -1121,7 +1201,7 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
                res = 0;
                c = ast_strdupa(option->adata);
                while ((n = strsep(&c, ";"))) {
-                       if ((res = ast_stream_and_wait(chan, n, chan->language,
+                       if ((res = ast_stream_and_wait(chan, n,
                                        (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
                                break;
                }
@@ -1184,11 +1264,12 @@ static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_me
                        return -1;
                }
        }
-       while(!res) {
-               while(menu->options[pos].option) {
+       while (!res) {
+               while (menu->options[pos].option) {
                        if (!strcasecmp(menu->options[pos].option, exten)) {
                                res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
-                               ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
                                if (res < 0)
                                        break;
                                else if (res & RES_UPONE)
@@ -1204,7 +1285,8 @@ static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_me
                                        if (!maxretries)
                                                maxretries = 3;
                                        if ((maxretries > 0) && (retries >= maxretries)) {
-                                               ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
+                                               if (option_debug)
+                                                       ast_log(LOG_DEBUG, "Max retries %d exceeded\n", maxretries);
                                                return -2;
                                        } else {
                                                if (option_exists(menu, "g") > -1) 
@@ -1215,24 +1297,28 @@ static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_me
                                        pos = 0;
                                        continue;
                                } else if (res && strchr(AST_DIGIT_ANY, res)) {
-                                       ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
+                                       if (option_debug)
+                                               ast_log(LOG_DEBUG, "Got start of extension, %c\n", res);
                                        exten[1] = '\0';
                                        exten[0] = res;
                                        if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
                                                break;
                                        if (option_exists(menu, exten) < 0) {
                                                if (option_exists(menu, "i")) {
-                                                       ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
+                                                       if (option_debug)
+                                                               ast_log(LOG_DEBUG, "Invalid extension entered, going to 'i'!\n");
                                                        strcpy(exten, "i");
                                                        pos = 0;
                                                        continue;
                                                } else {
-                                                       ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
+                                                       if (option_debug)
+                                                               ast_log(LOG_DEBUG, "Aborting on invalid entry, with no 'i' option!\n");
                                                        res = -2;
                                                        break;
                                                }
                                        } else {
-                                               ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
+                                               if (option_debug)
+                                                       ast_log(LOG_DEBUG, "New existing extension: %s\n", exten);
                                                pos = 0;
                                                continue;
                                        }
@@ -1240,7 +1326,8 @@ static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_me
                        }
                        pos++;
                }
-               ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Stopping option '%s', res is %d\n", exten, res);
                pos = 0;
                if (!strcasecmp(exten, "s"))
                        strcpy(exten, "g");
@@ -1309,14 +1396,14 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
                if (*s == '(') {
                        /* Has argument */
                        arg = ++s;
-                       s = strchr(s, ')');
-                       if (*s) {
+                       if ((s = strchr(s, ')'))) {
                                if (argloc)
                                        args[argloc - 1] = arg;
                                *s++ = '\0';
                        } else {
                                ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
                                res = -1;
+                               break;
                        }
                } else if (argloc) {
                        args[argloc - 1] = NULL;