Index: apps/app_queue.c =================================================================== --- apps/app_queue.c (revision 195443) +++ apps/app_queue.c (working copy) @@ -418,7 +418,7 @@ Returns 0 if queue is found and setqueuevar is defined, -1 otherwise. - + Count number of members answering a queue. @@ -442,21 +442,6 @@ Returns the number of members currently associated with the specified queuename. - - - Count number of members answering a queue. - - - - - - Returns the number of members currently associated with the specified queuename. - This function has been deprecated in favor of the QUEUE_MEMBER() function - - - QUEUE_MEMBER_LIST - - Count number of calls currently waiting in a queue. @@ -482,17 +467,51 @@ QUEUE_MEMBER_COUNT - + - Gets or sets queue members penalty. + Gets or sets queue member fields. - - + + + + + Returns or sets the number of answered call counter for queue member + + + Returns or sets the last call timer for queue member + + + Returns or sets the penalty of queue member + + + Returns or sets the paused status for queue member + + + Returns or sets the wrapuptime on queue member + + + Returns or sets the wrapuptime on queue member for next call only. + + + Returns the count of logged-in members for the specified queue (deprecated, use QUEUE_MEMBER_COUNT instead) + + + Returns the count of logged-in members for the specified queue available to take a call (deprecated, use QUEUE_MEMBER_COUNT instead) + + + Returns the total amount of members for the specified queue (deprecated, use QUEUE_MEMBER_COUNT instead) + + + + - Gets or sets queue members penalty. + Gets or sets queue member fields. If is not specified on write, member will be updated in all queues. Read also allows returning count of logged/free/total amount of agents for backward compatibility. + + QUEUE_MEMBER_COUNT + ***/ @@ -680,8 +699,8 @@ int realtime; /*!< Is this member realtime? */ int status; /*!< Status of queue member */ int paused; /*!< Are we paused (not accepting calls)? */ - int wrapuptime; /*!< Member wrapuptime. */ - int current_wrapuptime; /*!< This wrapuptime can be modified just for the next call. */ + int wrapuptime; /*!< Member wrapuptime. */ + int next_wrapuptime; /*!< This wrapuptime can be modified just for the next call. */ time_t lastcall; /*!< When last successful call was hungup */ struct call_queue *lastqueue; /*!< Last queue we received a call */ unsigned int dead:1; /*!< Used to detect members deleted in realtime */ @@ -689,6 +708,13 @@ char rt_uniqueid[80]; /*!< Unique id of realtime member entry */ }; +#define QUEUE_MEMBER_FIELD_PENALTY "penalty" +#define QUEUE_MEMBER_FIELD_CALLS "calls" +#define QUEUE_MEMBER_FIELD_PAUSED "paused" +#define QUEUE_MEMBER_FIELD_LASTCALL "lastcall" +#define QUEUE_MEMBER_FIELD_WRAPUPTIME "wrapuptime" +#define QUEUE_MEMBER_FIELD_NEXTWRAPUPTIME "nextwrapuptime" + enum empty_conditions { QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), @@ -832,7 +858,8 @@ static struct ao2_container *queues; static void update_realtime_members(struct call_queue *q); -static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused); +static int set_member_field(struct call_queue *q, struct member *mem, const char *field, long value, int relative, const char *reason); +static struct member *interface_exists(struct call_queue *q, const char *interface); static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); /*! \brief sets the QUEUESTATUS channel variable */ @@ -1113,7 +1140,7 @@ cur->penalty = penalty; cur->paused = paused; cur->wrapuptime = wrapuptime; - cur->current_wrapuptime = wrapuptime; + cur->next_wrapuptime = wrapuptime; ast_copy_string(cur->interface, interface, sizeof(cur->interface)); if (!ast_strlen_zero(state_interface)) ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); @@ -1881,15 +1908,13 @@ static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value) { - int ret = -1; - if (ast_strlen_zero(mem->rt_uniqueid)) - return ret; + return RESULT_FAILURE; if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) - ret = 0; + return RESULT_SUCCESS; - return ret; + return RESULT_FAILURE; } @@ -2499,10 +2524,10 @@ lastqueue = tmp->lastqueue; /* check if the wrapuptime expired so we are able to call this member. */ - if (mem->current_wrapuptime >= 0) { + if (mem->next_wrapuptime >= 0) { /* the queue member has a wrapuptime configured and didn't expired yet. */ - wrapup_notexpired = wrapuptime_notexpired(tmp->lastcall, mem->current_wrapuptime); - } else if (mem->current_wrapuptime < 0) { + wrapup_notexpired = wrapuptime_notexpired(tmp->lastcall, mem->next_wrapuptime); + } else if (mem->next_wrapuptime < 0) { /* the member doesn't have a specific wrapuptime configured. */ if (lastqueue && lastqueue->wrapuptime) { /* if the queue member doesn't have a wrapuptime configured, check for the wrapuptime of the last queue @@ -2883,10 +2908,15 @@ } ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); if (qe->parent->autopause && pause) { - if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { - ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); - } else { - ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); + struct member *mem; + //TODO: update rna to pass member + if ((mem = interface_exists(qe->parent, interface))) { + //TODO: check all set_member_field for result + if (RESULT_SUCCESS==set_member_field(qe->parent, mem, QUEUE_MEMBER_FIELD_PAUSED, 1, 0, "Auto-Pause")) { + ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); + } else { + ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); + } } } return; @@ -3004,7 +3034,7 @@ } } peer = o; - o->member->current_wrapuptime = o->member->wrapuptime; + o->member->next_wrapuptime = o->member->wrapuptime; } } else if (o->chan && (o->chan == winner)) { @@ -3105,7 +3135,7 @@ } } peer = o; - o->member->current_wrapuptime = o->member->wrapuptime; + o->member->next_wrapuptime = o->member->wrapuptime; } break; case AST_CONTROL_BUSY: @@ -3912,7 +3942,7 @@ tmp->oldstatus = cur->status; tmp->lastcall = cur->lastcall; tmp->lastqueue = cur->lastqueue; - tmp->wrapuptime = cur->current_wrapuptime; + tmp->wrapuptime = cur->next_wrapuptime; tmp->update_connectedline = 1; ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); /* Special case: If we ring everyone, go ahead and ring them, otherwise @@ -4502,9 +4532,9 @@ foundinterface++; /* this wrapuptime will be used only for the next call to this agent. */ if (absolute_value) { - mem->current_wrapuptime = offset; + mem->next_wrapuptime = offset; } else { - mem->current_wrapuptime += offset; + mem->next_wrapuptime += offset; } /* modify static configuration. */ if (modify_defaults) { @@ -4524,12 +4554,12 @@ "Wrapuptime: %d\r\n", q->name, mem->interface, mem->wrapuptime); } else { - ast_queue_log(q->name, "NONE", interface, "NEXTWRAPUPTIME", "%d", mem->current_wrapuptime); + ast_queue_log(q->name, "NONE", interface, "NEXTWRAPUPTIME", "%d", mem->next_wrapuptime); manager_event(EVENT_FLAG_AGENT, "QueueMemberNextWrapuptime", "Queue: %s\r\n" "Interface: %s\r\n" "Wrapuptime: %d\r\n", - q->name, mem->interface, mem->current_wrapuptime); + q->name, mem->interface, mem->next_wrapuptime); } ao2_ref(mem, -1); @@ -4702,63 +4732,182 @@ return res; } -static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused) +/* \brief Updates field for identified member and identified queue. + * \return Returns RESULT_SUCCESS if update succeeded or RESULT_FAILURE on error. +*/ +static int set_member_field(struct call_queue *q, struct member *mem, const char *field, long value, int relative, const char *reason) { - int found = 0; + char *log_event = "UPDATEMEMBER", *manager_event = "QueueMemberUpdate", *strbuf; + + if (!strcasecmp(QUEUE_MEMBER_FIELD_CALLS, field)) { + + if (relative) value = mem->calls+(relative*value); + if (value<0) { + ast_log(LOG_VERBOSE, "Can't set call count to %ld for queue member %s:%s\n", value, q->name, mem->interface); + return RESULT_FAILURE; + } + + mem->calls = value; + manager_event = "QueueMemberCalls"; + log_event = "UPDATECALLS"; + ast_log(LOG_DEBUG, "Setting calls for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_LASTCALL, field)) { + + if (relative) value = mem->lastcall+(relative*value); + mem->lastcall = value; + manager_event = "QueueMemberLastcall"; + log_event = "UPDATELASTCALL"; + ast_log(LOG_DEBUG, "Setting lastcall for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_PENALTY, field)) { + + if (relative) value = mem->penalty+(relative*value); + if (value<0) { + ast_log(LOG_VERBOSE, "Can't set penalty to %ld for queue member %s:%s\n", value, q->name, mem->interface); + return RESULT_FAILURE; + } + + mem->penalty = value; + log_event = "PENALTY"; + manager_event = "QueueMemberPenalty"; + ast_log(LOG_DEBUG, "Setting penalty for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_WRAPUPTIME, field)) { + + if (relative) value = mem->wrapuptime+(relative*value); + if (value<0) { + ast_log(LOG_VERBOSE, "Can't set wrapuptime to %ld for queue member %s:%s\n", value, q->name, mem->interface); + return RESULT_FAILURE; + } + + mem->wrapuptime = value; + log_event = "WRAPUPTIME"; + manager_event = "QueueMemberWrapuptime"; + ast_log(LOG_DEBUG, "Setting wrapuptime for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_NEXTWRAPUPTIME, field)) { + + if (relative) value = mem->next_wrapuptime+(relative*value); + if (value<0) { + ast_log(LOG_VERBOSE, "Can't set nextwrapuptime to %ld for queue member %s:%s\n", value, q->name, mem->interface); + return RESULT_FAILURE; + } + + mem->next_wrapuptime = value; + log_event = "NEXTWRAPUPTIME"; + manager_event = "QueueMemberNextWrapuptime"; + ast_log(LOG_DEBUG, "Setting nextwrapuptime for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_PAUSED, field)) { + + // TODO: use ast_true + if (relative) { + ast_log(LOG_VERBOSE, "Can't increment or decrement paused status for queue member %s:%s\n", q->name, mem->interface); + return RESULT_FAILURE; + } else if (value<0 || value>1) { + ast_log(LOG_VERBOSE, "Can't set paused other than 0 or 1 for queue member %s:%s\n", q->name, mem->interface); + return RESULT_FAILURE; + } + + if (mem->paused == value) { + ast_log(LOG_NOTICE, "%spausing already-%spaused queue member %s:%s\n", (value ? "" : "un"), (value ? "" : "un"), q->name, mem->interface); + } + + mem->paused = value; + manager_event = "QueueMemberPaused"; + log_event = (value ? "PAUSE" : "UNPAUSE"); + ast_log(LOG_DEBUG, "Setting paused for queue member %s:%s to %ld\n", q->name, mem->interface, value); + + } else { + ast_log(LOG_VERBOSE, "Queue members don't have %s field.!\n", field); + return RESULT_FAILURE; + } + + if (mem->realtime) { + snprintf(strbuf, sizeof(strbuf), "%ld", value); + ast_log(LOG_DEBUG, "Updating realtime member field %s to %s\n", field, strbuf); + if (!update_realtime_member_field(mem, q->name, field, strbuf)) { + return RESULT_FAILURE; + } + } + + if (queue_persistent_members) { + dump_queue_members(q); + } + + // TODO: add actual value changed to + ast_queue_log(q->name, "NONE", mem->membername, log_event, "%ld|%s", value, S_OR(reason, "")); + + if (!ast_strlen_zero(reason)) { + manager_event(EVENT_FLAG_AGENT, manager_event, + "Queue: %s\r\n" + "Location: %s\r\n" + "MemberName: %s\r\n" + "Paused: %d\r\n" + "Calls: %d\r\n" + "Penalty: %d\r\n" + "Lastcall: %ld\r\n" + "Reason: %s\r\n", + q->name, mem->interface, mem->membername, mem->paused, mem->calls, mem->penalty, (long)mem->lastcall, reason); + } else { + manager_event(EVENT_FLAG_AGENT, manager_event, + "Queue: %s\r\n" + "Location: %s\r\n" + "MemberName: %s\r\n" + "Paused: %d\r\n" + "Calls: %d\r\n" + "Penalty: %d\r\n" + "Lastcall: %ld\r\n", + q->name, mem->interface, mem->membername, mem->paused, mem->calls, mem->penalty, (long)mem->lastcall); + } + + return RESULT_SUCCESS; +} + +/* \brief Finds a queue and member in it, and updates specified field. + * \return Returns RESULT_SUCCESS if update succeeded or RESULT_FAILURE on error. +*/ +static int update_member_field(const char *queuename, const char *interface, const char *field, const char *value_s, const char *reason) +{ + int relative = 0; + int res; struct call_queue *q; struct member *mem; struct ao2_iterator queue_iter; - int failed; + long value; + value = atol(value_s); + + /* Check for increment/decrement */ + if (!ast_strlen_zero(value_s)) { + if (value_s[0]=='+') { + relative = +1; + if (value==0) value = 1; + } else if (value_s[0]=='-') { + value = -value; + relative = -1; + if (value==0) value = 1; + } + } + /* Special event for when all queues are paused - individual events still generated */ /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ - if (ast_strlen_zero(queuename)) - ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); + if (!strcasecmp(QUEUE_MEMBER_FIELD_PAUSED, field) && ast_strlen_zero(queuename)) + ast_queue_log("NONE", "NONE", interface, (value ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); queue_iter = ao2_iterator_init(queues, 0); while ((q = ao2_iterator_next(&queue_iter))) { ao2_lock(q); if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { if ((mem = interface_exists(q, interface))) { - if (mem->paused == paused) { - ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); - } - - failed = 0; - if (mem->realtime) { - failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); - } - - if (failed) { - ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); + res = set_member_field(q, mem, field, value, relative, reason); + if (res == RESULT_FAILURE) { ao2_ref(mem, -1); ao2_unlock(q); continue; - } - found++; - mem->paused = paused; + } - if (queue_persistent_members) - dump_queue_members(q); - - ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); - - if (!ast_strlen_zero(reason)) { - manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", - "Queue: %s\r\n" - "Location: %s\r\n" - "MemberName: %s\r\n" - "Paused: %d\r\n" - "Reason: %s\r\n", - q->name, mem->interface, mem->membername, paused, reason); - } else { - manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", - "Queue: %s\r\n" - "Location: %s\r\n" - "MemberName: %s\r\n" - "Paused: %d\r\n", - q->name, mem->interface, mem->membername, paused); - } ao2_ref(mem, -1); } } @@ -4773,87 +4922,67 @@ queue_unref(q); } - return found ? RESULT_SUCCESS : RESULT_FAILURE; + return res; } -/* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */ -static int set_member_penalty(char *queuename, char *interface, int penalty) +/* \brief Gets members field + * \return Return the member field. +*/ +static int get_member_field(const char *queuename, const char *interface, const char *field) { - int foundinterface = 0, foundqueue = 0; struct call_queue *q; struct member *mem; struct ao2_iterator queue_iter; + int ret; - if (penalty < 0) { - ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); + if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { + ast_log(LOG_VERBOSE, "Queue name and interface must be specified\n"); return RESULT_FAILURE; } queue_iter = ao2_iterator_init(queues, 0); while ((q = ao2_iterator_next(&queue_iter))) { ao2_lock(q); - if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { - foundqueue++; - if ((mem = interface_exists(q, interface))) { - foundinterface++; - mem->penalty = penalty; - - ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); - manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", - "Queue: %s\r\n" - "Location: %s\r\n" - "Penalty: %d\r\n", - q->name, mem->interface, penalty); - ao2_ref(mem, -1); + if (!strcasecmp(q->name, queuename)) { + mem = interface_exists(q, interface); + if (!mem) { + ast_log(LOG_VERBOSE, "Member %s not found in queue %s!\n", interface, queuename); + ao2_unlock(q); + queue_unref(q); + return RESULT_FAILURE; } + + // Do cases for each field + if (!strcasecmp(QUEUE_MEMBER_FIELD_CALLS, field)) { + ret = mem->calls; + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_LASTCALL, field)) { + ret = mem->lastcall; + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_PENALTY, field)) { + ret = mem->penalty; + } else if (!strcasecmp(QUEUE_MEMBER_FIELD_PAUSED, field)) { + ret = mem->paused; + } else { + ast_log(LOG_VERBOSE, "Queue members don't have %s field.!\n", field); + } + ao2_ref(mem, -1); + ao2_unlock(q); + queue_unref(q); + return ret; } + ao2_unlock(q); queue_unref(q); } - if (foundinterface) { - return RESULT_SUCCESS; - } else if (!foundqueue) { - ast_log (LOG_ERROR, "Invalid queuename\n"); - } else { - ast_log (LOG_ERROR, "Invalid interface\n"); - } - return RESULT_FAILURE; } -/* \brief Gets members penalty. +/* \brief Gets members penalty. * \return Return the members penalty or RESULT_FAILURE on error. */ static int get_member_penalty(char *queuename, char *interface) { - int foundqueue = 0, penalty; - struct call_queue *q, tmpq = { - .name = queuename, - }; - struct member *mem; - - if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { - foundqueue = 1; - ao2_lock(q); - if ((mem = interface_exists(q, interface))) { - penalty = mem->penalty; - ao2_ref(mem, -1); - ao2_unlock(q); - queue_unref(q); - return penalty; - } - ao2_unlock(q); - queue_unref(q); - } - - /* some useful debuging */ - if (foundqueue) - ast_log (LOG_ERROR, "Invalid queuename\n"); - else - ast_log (LOG_ERROR, "Invalid interface\n"); - - return RESULT_FAILURE; + return get_member_field(queuename, interface, QUEUE_MEMBER_FIELD_PENALTY); } /*! \brief Reload dynamic queue members persisted into the astdb */ @@ -4984,7 +5113,7 @@ return -1; } - if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { + if (update_member_field(args.queuename, args.interface, QUEUE_MEMBER_FIELD_PAUSED, "1", args.reason)) { ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); return 0; @@ -5020,7 +5149,7 @@ return -1; } - if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { + if (update_member_field(args.queuename, args.interface, QUEUE_MEMBER_FIELD_PAUSED, "0", args.reason)) { ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); return 0; @@ -5620,49 +5749,6 @@ return 0; } -/*! - * \brief Get the total number of members in a specific queue (Deprecated) - * \retval number of members - * \retval -1 on error -*/ -static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) -{ - int count = 0; - struct member *m; - struct call_queue *q; - struct ao2_iterator mem_iter; - static int depflag = 1; - - if (depflag) { - depflag = 0; - ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n"); - } - - if (ast_strlen_zero(data)) { - ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); - return -1; - } - - if ((q = load_realtime_queue(data))) { - ao2_lock(q); - mem_iter = ao2_iterator_init(q->members, 0); - while ((m = ao2_iterator_next(&mem_iter))) { - /* Count the agents who are logged in and presently answering calls */ - if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { - count++; - } - ao2_ref(m, -1); - } - ao2_unlock(q); - queue_unref(q); - } else - ast_log(LOG_WARNING, "queue %s was not found\n", data); - - snprintf(buf, len, "%d", count); - - return 0; -} - /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */ static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { @@ -5748,7 +5834,7 @@ } /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */ -static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +static int queue_function_memberpenalty_read_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { int penalty; AST_DECLARE_APP_ARGS(args, @@ -5758,6 +5844,8 @@ /* Make sure the returned value on error is NULL. */ buf[0] = '\0'; + ast_log(LOG_VERBOSE, "The function QUEUE_MEMBER_PENALTY has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n"); + if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); return -1; @@ -5779,14 +5867,15 @@ } /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */ -static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +static int queue_function_memberpenalty_write_dep(struct ast_channel *chan, const char *cmd, char *data, const char *value) { - int penalty; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(queuename); AST_APP_ARG(interface); ); + ast_log(LOG_VERBOSE, "The function QUEUE_MEMBER_PENALTY has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n"); + if (ast_strlen_zero(data)) { ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(,)\n"); return -1; @@ -5799,15 +5888,13 @@ return -1; } - penalty = atoi(value); - if (ast_strlen_zero(args.interface)) { ast_log (LOG_ERROR, " parameter can't be null\n"); return -1; } /* if queuename = NULL then penalty will be set for interface in all the queues. */ - if (set_member_penalty(args.queuename, args.interface, penalty)) { + if (update_member_field(args.queuename, args.interface, QUEUE_MEMBER_FIELD_PENALTY, value, NULL)) { ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); return -1; } @@ -5815,19 +5902,83 @@ return 0; } + + +/*! \brief Dialplan function QUEUE_MEMBER() Gets the member parameters. */ +static int queue_function_member_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + int result; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(queuename); + AST_APP_ARG(field); + AST_APP_ARG(interface); + ); + /* Make sure the returned value on error is NULL. */ + buf[0] = '\0'; + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(,[,])\n"); + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(,[,])\n"); + return -1; + } + + + result = get_member_field(args.queuename, args.interface, args.field); + + if (result >= 0) /* remember that buf is already '\0' */ + snprintf (buf, len, "%d", result); + + return 0; +} + +/*! \brief Dialplan function QUEUE_MEMBER() Sets the member parameters. */ +static int queue_function_member_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(queuename); + AST_APP_ARG(field); + AST_APP_ARG(interface); + ); + + if (ast_strlen_zero(data)) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER([],[,])\n"); + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER([],[,])\n"); + return -1; + } + + if (!update_member_field(args.queuename, args.interface, args.field, value, NULL)) { + return -1; + } + + return 0; +} + + static struct ast_custom_function queuevar_function = { .name = "QUEUE_VARIABLES", .read = queue_function_var, }; - static struct ast_custom_function queuemembercount_function = { - .name = "QUEUE_MEMBER", + .name = "QUEUE_MEMBER_COUNT", .read = queue_function_qac, }; -static struct ast_custom_function queuemembercount_dep = { - .name = "QUEUE_MEMBER_COUNT", - .read = queue_function_qac_dep, +static struct ast_custom_function queuemember_function = { + .name = "QUEUE_MEMBER", + .read = queue_function_member_read, + .write = queue_function_member_write, }; static struct ast_custom_function queuewaitingcount_function = { @@ -5840,10 +5991,10 @@ .read = queue_function_queuememberlist, }; -static struct ast_custom_function queuememberpenalty_function = { +static struct ast_custom_function queuememberpenalty_dep = { .name = "QUEUE_MEMBER_PENALTY", - .read = queue_function_memberpenalty_read, - .write = queue_function_memberpenalty_write, + .read = queue_function_memberpenalty_read_dep, + .write = queue_function_memberpenalty_write_dep, }; /*! \brief Reload the rules defined in queuerules.conf @@ -6377,7 +6528,7 @@ mem->paused ? " (paused)" : "", ast_devstate2str(mem->status)); if (mem->wrapuptime >= 0) { - ast_str_append(&out, 0, " (wrapuptime = %d/%d)", mem->wrapuptime, mem->current_wrapuptime); + ast_str_append(&out, 0, " (wrapuptime = %d/%d)", mem->wrapuptime, mem->next_wrapuptime); } if (mem->calls) ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago) ", @@ -6774,7 +6925,7 @@ paused = abs(ast_true(paused_s)); - if (set_member_paused(queuename, interface, reason, paused)) + if (update_member_field(queuename, interface, QUEUE_MEMBER_FIELD_PAUSED, paused_s, reason)) astman_send_error(s, m, "Interface not found"); else astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); @@ -6888,22 +7039,20 @@ static int manager_queue_member_penalty(struct mansession *s, const struct message *m) { - const char *queuename, *interface, *penalty_s; - int penalty; + const char *queuename, *interface, *penalty; interface = astman_get_header(m, "Interface"); - penalty_s = astman_get_header(m, "Penalty"); + penalty = astman_get_header(m, "Penalty"); /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ queuename = astman_get_header(m, "Queue"); - if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { + if (ast_strlen_zero(interface) || ast_strlen_zero(penalty)) { astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); return 0; } + /* TODO: deprecate function */ - penalty = atoi(penalty_s); - - if (set_member_penalty((char *)queuename, (char *)interface, penalty)) + if (update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_PENALTY, (char *)penalty, NULL)) astman_send_error(s, m, "Invalid interface, queuename or penalty"); else astman_send_ack(s, m, "Interface penalty set successfully"); @@ -6911,6 +7060,62 @@ return 0; } +static int manager_queue_member(struct mansession *s, const struct message *m) +{ + const char *queuename, *interface, *val, *reason; + int res = -1; + + /* Optional - if not supplied, set the value for the given Interface in all queues */ + queuename = astman_get_header(m, "Queue"); + + interface = astman_get_header(m, "Interface"); + reason = astman_get_header(m, "Reason"); + + if (ast_strlen_zero(interface)) { + astman_send_error(s, m, "Need to set 'Interface'"); + return 0; + } + + /* TODO: use m->hdrcount and m->headers to iterate all arguments */ + if ((val = astman_get_header(m, "Penalty"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_PENALTY, (char *)val, reason); + } + + if ((val = astman_get_header(m, "Calls"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_CALLS, (char *)val, reason); + } + + if ((val = astman_get_header(m, "Paused"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_PAUSED, (char *)val, reason); + } + + if ((val = astman_get_header(m, "Lastcall"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_PAUSED, (char *)val, reason); + } + + if ((val = astman_get_header(m, "Wrapuptime"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_WRAPUPTIME, (char *)val, reason); + } + + if ((val = astman_get_header(m, "NextWrapuptime"))) { + res = res | update_member_field((char *)queuename, (char *)interface, QUEUE_MEMBER_FIELD_NEXTWRAPUPTIME, (char *)val, reason); + } + + if (res==-1) { + astman_send_error(s, m, "Need to set either of 'Penalty', 'Calls', 'Paused', 'Lastcall', 'Wrapuptime' or 'NextWrapuptime'"); + return 0; + } + + if (res==RESULT_FAILURE) + astman_send_error(s, m, "Invalid interface, queuename or fields"); + else + astman_send_ack(s, m, "Interface updated set successfully"); + + return 0; +} + + + /*! * \internal * \brief Modify the queue member wrapuptime based on the manager request. @@ -7162,6 +7367,7 @@ { char *queuename, *interface, *reason; int paused; + char *paused_val; switch (cmd) { case CLI_INIT: @@ -7189,8 +7395,9 @@ queuename = a->argc >= 6 ? a->argv[5] : NULL; reason = a->argc == 8 ? a->argv[7] : NULL; paused = !strcasecmp(a->argv[1], "pause"); + paused_val = paused ? "1" : "0"; - if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { + if (update_member_field(queuename, interface, QUEUE_MEMBER_FIELD_PAUSED, paused_val, reason) == RESULT_SUCCESS) { ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); if (!ast_strlen_zero(queuename)) ast_cli(a->fd, " in queue '%s'", queuename); @@ -7209,9 +7416,9 @@ } } -static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state) +static char *complete_queue_set_member(const char *line, const char *word, int pos, int state) { - /* 0 - queue; 1 - set; 2 - penalty; 3 - ; 4 - on; 5 - ; 6 - in; 7 - ;*/ + /* 0 - queue; 1 - set; 2 - penalty|calls|lastcall|paused; 3 - ; 4 - on; 5 - ; 6 - in|because; 7 - ; 8 - because; 9 - reason;*/ switch (pos) { case 4: if (state == 0) { @@ -7227,45 +7434,77 @@ } case 7: return complete_queue(line, word, pos, state); + case 8: + if (state == 0) { + return ast_strdup("because"); + } else { + return NULL; + } default: return NULL; } -} - -static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +} + +static char *handle_queue_set_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - char *queuename = NULL, *interface; - int penalty = 0; + char *queuename = NULL, *reason = NULL, *interface = NULL, *value = NULL, *field = NULL; switch (cmd) { case CLI_INIT: - e->command = "queue set penalty"; + e->command = "queue set"; e->usage = - "Usage: queue set penalty on [in ]\n" - " Set a member's penalty in the queue specified. If no queue is specified\n" - " then that interface's penalty is set in all queues to which that interface is a member\n"; + "Usage: queue set (penalty|calls|paused|lastcall|wrapuptime|nextwrapuptime) [+|-] on [in ] [because ]\n" + " Set a member's field in the queue specified. If no queue is specified\n" + " then that interface's field is set in all queues to which that interface is a member\n"; return NULL; case CLI_GENERATE: - return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); + return complete_queue_set_member(a->line, a->word, a->pos, a->n); } - if (a->argc != 6 && a->argc != 8) { + ast_log(LOG_WARNING, "handle_queue_set_member\n"); + + + if (a->argc != 6 && a->argc != 8 && a->argc !=10) { return CLI_SHOWUSAGE; - } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { + } else if (strcmp(a->argv[4], "on")) { return CLI_SHOWUSAGE; + } else if (a->argc == 6 && (strcmp(a->argv[6], "in") || strcmp(a->argv[6], "because"))) { + return CLI_SHOWUSAGE; + } else if (a->argc == 7 && (strcmp(a->argv[6], "because"))) { + return CLI_SHOWUSAGE; + } else if (a->argc >= 8 && strcmp(a->argv[8], "because")) { + return CLI_SHOWUSAGE; } - if (a->argc == 8) + ast_log(LOG_WARNING, "handle_queue_set_member 2\n"); + + if (a->argc == 6 && !strcmp(a->argv[6], "in")) queuename = a->argv[7]; + if (a->argc == 6 && !strcmp(a->argv[6], "because")) + reason = a->argv[7]; + if (a->argc == 8 && !strcmp(a->argv[8], "because")) + reason = a->argv[9]; + + ast_log(LOG_WARNING, "handle_queue_set_member 3\n"); + + field = a->argv[2]; + value = a->argv[3]; interface = a->argv[5]; - penalty = atoi(a->argv[3]); - switch (set_member_penalty(queuename, interface, penalty)) { + ast_log(LOG_WARNING, "handle_queue_set_member 4 (field: %s)\n", field); + + if (strcmp(field, "penalty") && strcmp(field, "calls") && strcmp(field, "paused") && + strcmp(field, "lastcall") && strcmp(field, "wrapuptime") && strcmp(field, "nextwrapuptime")) { + return CLI_SHOWUSAGE; + } + ast_log(LOG_WARNING, "handle_queue_set_member 5\n"); + + switch (update_member_field(queuename, interface, field, value, reason)) { case RESULT_SUCCESS: - ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); + ast_cli(a->fd, "Set %s on interface '%s' in queue '%s'\n", field, interface, S_OR(queuename,"")); return CLI_SUCCESS; case RESULT_FAILURE: - ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); + ast_cli(a->fd, "Failed to set %s on interface '%s' in queue '%s'\n", field, interface, S_OR(queuename,"")); return CLI_FAILURE; default: return CLI_FAILURE; @@ -7508,16 +7747,12 @@ static const char qum_cmd_usage[] = "Usage: queue unpause member in reason \n"; -static const char qsmp_cmd_usage[] = -"Usage: queue set member penalty from \n"; - static struct ast_cli_entry cli_queue[] = { AST_CLI_DEFINE(queue_show, "Show status of a specified queue"), AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"), AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"), AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"), - AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"), - AST_CLI_DEFINE(handle_queue_set_member_wrapuptime, "Set wrapuptime for a member of a specified queue"), + AST_CLI_DEFINE(handle_queue_set_member, "Update queue member fields"), AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"), AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"), AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"), @@ -7550,10 +7785,10 @@ res |= ast_unregister_application(app); res |= ast_custom_function_unregister(&queuevar_function); res |= ast_custom_function_unregister(&queuemembercount_function); - res |= ast_custom_function_unregister(&queuemembercount_dep); res |= ast_custom_function_unregister(&queuememberlist_function); res |= ast_custom_function_unregister(&queuewaitingcount_function); - res |= ast_custom_function_unregister(&queuememberpenalty_function); + res |= ast_custom_function_unregister(&queuememberpenalty_dep); + res |= ast_custom_function_unregister(&queuemember_function); if (device_state_sub) ast_event_unsubscribe(device_state_sub); @@ -7610,6 +7845,8 @@ res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log"); + res |= ast_manager_register("QueueMember", EVENT_FLAG_AGENT, manager_queue_member, "Update the fields for a queue member"); + /* TODO: deprecate in favor of QueueMember */ res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); res |= ast_manager_register("QueueMemberWrapuptime", EVENT_FLAG_AGENT, manager_queue_member_wrapuptime, "Set the wrapuptime for a queue member."); res |= ast_manager_register("QueueMemberNextWrapuptime", EVENT_FLAG_AGENT, manager_queue_member_next_wrapuptime, @@ -7619,10 +7856,10 @@ res |= ast_manager_register("QueueReset", 0, manager_queue_reset, "Reset queue statistics"); res |= ast_custom_function_register(&queuevar_function); res |= ast_custom_function_register(&queuemembercount_function); - res |= ast_custom_function_register(&queuemembercount_dep); res |= ast_custom_function_register(&queuememberlist_function); res |= ast_custom_function_register(&queuewaitingcount_function); - res |= ast_custom_function_register(&queuememberpenalty_function); + res |= ast_custom_function_register(&queuememberpenalty_dep); + res |= ast_custom_function_register(&queuemember_function); if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); @@ -7651,3 +7888,4 @@ .reload = reload, ); +