Index: apps/app_queue.c
===================================================================
--- apps/app_queue.c (revision 372389)
+++ apps/app_queue.c (working copy)
@@ -450,6 +450,17 @@
[QUEUE_MEMBER_PENALTY]
+
+
+ Camp a call for an extension.
+
+
+
+
+
+ Camps an incoming call onto a particular extension.
+
+
Return Queue information in variables.
@@ -910,6 +921,8 @@
static char *app_ql = "QueueLog" ;
+static char *app_co = "CampOn" ;
+
/*! \brief Persistent Members astdb family */
static const char * const pm_family = "Queue/PersistentMembers";
/* The maximum length of each persistent member queue database entry */
@@ -1023,6 +1036,7 @@
int linpos; /*!< If using linear strategy, what position are we at? */
int linwrapped; /*!< Is the linpos wrapped? */
time_t start; /*!< When we started holding */
+ time_t recall; /*!< Recall time for campon message */
time_t expire; /*!< When this entry should expire (time out of queue) */
int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
struct ast_channel *chan; /*!< Our channel */
@@ -6848,6 +6908,294 @@
}
}
+/* Campon function - play file and wait for digit response
+ ret val: 1 = jump to another dialplan priority, 0 = continue */
+static int campon_message(struct queue_ent *qe, char *fname)
+{
+ int d;
+
+ d = ast_streamfile(qe->chan, fname, qe->chan->language);
+ if ( !d ) {
+ d = ast_waitstream(qe->chan, AST_DIGIT_ANY);
+ }
+ if ( !d ) {
+ d = ast_waitfordigit(qe->chan, 2000);
+ }
+ ast_stopstream(qe->chan);
+
+ if (d && valid_exit(qe, d)) {
+ return d;
+ }
+
+ return 0;
+}
+
+/* Campon on exec - called from dialplan, copied from queue_exec and
+ reload_queue. Uses the queue function to campon the call, with recall
+ every 45 seconds to replay another message to allow caller to exit
+ by pressing 1 or 2. Dynamically creates a queue if one does not exist */
+
+/*! \brief CampOn application */
+static int co_exec(struct ast_channel *chan, const char *data)
+{
+ int res = -1;
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(interface);
+ AST_APP_ARG(penalty);
+ AST_APP_ARG(options);
+ AST_APP_ARG(membername);
+ AST_APP_ARG(state_interface);
+ );
+
+ char *queuename = NULL;
+ char *options = NULL;
+ struct call_queue *q;
+ int periodicannouncefrequency;
+ int timeout;
+ int ringinuse;
+ char mohname[32];
+ char context[32];
+ time_t curr_time;
+
+ int prio = 0;
+ int min_penalty = 0;
+ int max_penalty = 0;
+
+ int paused = 0;
+ int penalty = 0;
+ int position = 1;
+
+ enum queue_result reason = QUEUE_UNKNOWN;
+ int tries = 0;
+ int noption = 0;
+
+ int ringing = 0;
+
+ /* Our queue entry */
+ struct queue_ent qe = { 0 };
+ struct ao2_iterator queue_iter;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "CAMPON requires an argument (technology/number|optional timeout|optional URL)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ /* Parse our arguments XXX Check for failure XXX */
+ if (ast_strlen_zero(args.interface)) {
+ ast_log(LOG_WARNING, "CAMPON requires an interface argument (technology/number))\n");
+ return -1;
+ }
+
+ if (!ast_strlen_zero(args.penalty)) {
+ if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
+ ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
+ penalty = 0;
+ }
+ }
+
+ /* Answer the Channel if it's not already up */
+ if ( chan->_state != AST_STATE_UP ) {
+ ast_answer(chan);
+ }
+
+ queuename = args.interface;
+
+ /* make sure queue entry for this phone/extension exists otherwise create */
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+ if ( !strcmp(q->name, queuename) ) {
+ queue_t_unref(q, "Done with iterator");
+ break;
+ }
+ queue_t_unref(q, "Done with iterator");
+ }
+ ao2_iterator_destroy(&queue_iter);
+
+ if (!q) {
+ /* get options from queues.conf [campon] */
+ queue_iter = ao2_iterator_init(queues, 0);
+ while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+ if ( !strcmp(q->name, "campon") ) {
+ queue_t_unref(q, "Done with iterator");
+ break;
+ }
+ queue_t_unref(q, "Done with iterator");
+ }
+ ao2_iterator_destroy(&queue_iter);
+
+ if (q) {
+ /* found an entry for [campon] in queues.conf */
+ timeout = q->timeout;
+ ringinuse = q->ringinuse;
+ strcpy(mohname, q->moh);
+ strcpy(context, q->context);
+ periodicannouncefrequency = q->periodicannouncefrequency;
+ } else {
+ /* use default values */
+ timeout = 45;
+ ringinuse = 0;
+ strcpy(mohname, "default");
+ strcpy(context, "");
+ periodicannouncefrequency = 15;
+ }
+
+ /* Make a new queue for the phone/extensions */
+ if (!(q = alloc_queue(queuename))) {
+ return -1;
+ }
+
+ /* initialize the queue */
+ q->strategy = QUEUE_STRATEGY_RINGALL;
+ init_queue(q);
+ q->timeout = timeout;
+ q->ringinuse = ringinuse;
+ ast_string_field_set(q, moh, mohname);
+ ast_string_field_set(q, announce, "");
+ ast_string_field_set(q, context, context);
+
+ q->periodicannouncefrequency = periodicannouncefrequency;
+ ast_str_set(&q->sound_periodicannounce[0], 0, "campon-repeat");
+
+ queues_t_link(queues, q, "Add queue to container");
+
+ free_members(q, 1);
+ queue_t_unref(q, "Expiring creation reference");
+
+ switch (add_to_queue(queuename, args.interface, args.membername, penalty, paused, 0, args.state_interface)) {
+ case RES_OKAY:
+ ast_queue_log(queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
+ ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, queuename);
+ break;
+ case RES_EXISTS:
+ ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, queuename);
+ break;
+ case RES_NOSUCHQUEUE:
+ ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
+ break;
+ case RES_OUTOFMEMORY:
+ ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, queuename);
+ break;
+ }
+ }
+
+ /* Setup our queue entry */
+ qe.start = time(NULL);
+ qe.expire = qe.start + 600;
+ qe.chan = chan;
+ qe.prio = prio;
+ qe.max_penalty = max_penalty;
+ qe.min_penalty = min_penalty;
+ qe.last_pos_said = 0;
+ qe.last_pos = 0;
+ qe.last_periodic_announce_time = time(NULL);
+ qe.last_periodic_announce_sound = 0;
+ qe.valid_digits = 0;
+
+ if (join_queue(queuename, &qe, &reason, position)) {
+ ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
+ set_queue_result(chan, reason);
+ pbx_builtin_setvar_helper(chan, "CAMPONSTATUS", "JOINUNAVIAL");
+ return 0;
+ }
+
+ /* play Initial campon message, exit to single digit exten if exists */
+ if (campon_message(&qe, "campon")) {
+ leave_queue(&qe);
+ return 0; /* jump to another dialplan priority */
+ }
+
+ /* Start music on hold */
+ if (ringing) {
+ ast_indicate(qe.chan, AST_CONTROL_RINGING);
+ } else {
+ ast_moh_start(qe.chan, qe.moh, NULL);
+ }
+
+ ast_log(LOG_NOTICE,"Waiting Turn Queue '%s' Timeout='%d'\n", queuename, qe.parent->timeout);
+ for (;;) {
+ res = wait_our_turn(&qe, ringing, &reason);
+ if (res < 0) {
+ /* They hungup, return immediately */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
+ }
+ res = -1;
+ break;
+ } else if (!res) {
+ /* It's their turn */
+ break;
+ } else {
+ /* They pressed a key */
+ if (!ast_goto_if_exists(chan, qe.parent->context, qe.digits, 1)) {
+ break;
+ } else {
+ qe.digits[0] = '\0';
+ }
+ }
+ }
+
+ if (!res) {
+ qe.recall = time(NULL) + qe.parent->timeout;
+ ast_log(LOG_NOTICE,"Trying Queue '%s' Timeout='%d'\n", queuename, qe.parent->timeout);
+ for (;;) {
+ res = try_calling(&qe, options, NULL, NULL, &tries, &noption, NULL, NULL, NULL, ringing);
+ if (res)
+ break;
+ res = wait_a_bit(&qe);
+ curr_time = time(NULL);
+ if (curr_time >= qe.recall) {
+ if (ringing) {
+ ast_indicate(qe.chan, -1);
+ } else {
+ ast_moh_stop(qe.chan);
+ }
+ ast_stopstream(qe.chan);
+ if (campon_message(&qe, "campon-repeat")) {
+ res = 1; /* jump to another dialplan priority */
+ break;
+ } else {
+ /* Start music on hold */
+ if (ringing) {
+ ast_indicate(qe.chan, AST_CONTROL_RINGING);
+ } else {
+ ast_moh_start(qe.chan, qe.moh, NULL);
+ }
+ qe.recall = time(NULL) + qe.parent->timeout;
+ ast_log(LOG_NOTICE,"Re-Trying Queue '%s' Timeout='%d'\n", queuename, qe.parent->timeout);
+ }
+ }
+ if (res < 0) {
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
+ }
+ res = -1;
+ break;
+ } else if (res && valid_exit(&qe, res)) {
+ break;
+ }
+ }
+ }
+
+ /* Don't allow return code > 0 */
+ if (res >= 0) {
+ res = 0;
+ if (ringing) {
+ ast_indicate(qe.chan, -1);
+ } else {
+ ast_moh_stop(qe.chan);
+ }
+ ast_stopstream(qe.chan);
+ }
+ leave_queue(&qe);
+ return res;
+}
+
+
/*! \brief reload the queues.conf file
*
* This function reloads the information in the general section of the queues.conf
@@ -8372,6 +8720,7 @@
res |= ast_manager_unregister("QueuePause");
res |= ast_manager_unregister("QueueLog");
res |= ast_manager_unregister("QueuePenalty");
+ res |= ast_unregister_application(app_co);
res |= ast_unregister_application(app_aqm);
res |= ast_unregister_application(app_rqm);
res |= ast_unregister_application(app_pqm);
@@ -8441,6 +8790,7 @@
res |= ast_register_application_xml(app_pqm, pqm_exec);
res |= ast_register_application_xml(app_upqm, upqm_exec);
res |= ast_register_application_xml(app_ql, ql_exec);
+ res |= ast_register_application_xml(app_co, co_exec);
res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);