00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #include <stdlib.h>
00058 #include <errno.h>
00059 #include <unistd.h>
00060 #include <string.h>
00061 #include <stdlib.h>
00062 #include <stdio.h>
00063 #include <sys/time.h>
00064 #include <sys/signal.h>
00065 #include <netinet/in.h>
00066
00067 #include "asterisk.h"
00068
00069 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 10018 $")
00070
00071 #include "asterisk/lock.h"
00072 #include "asterisk/file.h"
00073 #include "asterisk/logger.h"
00074 #include "asterisk/channel.h"
00075 #include "asterisk/pbx.h"
00076 #include "asterisk/options.h"
00077 #include "asterisk/app.h"
00078 #include "asterisk/linkedlists.h"
00079 #include "asterisk/module.h"
00080 #include "asterisk/translate.h"
00081 #include "asterisk/say.h"
00082 #include "asterisk/features.h"
00083 #include "asterisk/musiconhold.h"
00084 #include "asterisk/cli.h"
00085 #include "asterisk/manager.h"
00086 #include "asterisk/config.h"
00087 #include "asterisk/monitor.h"
00088 #include "asterisk/utils.h"
00089 #include "asterisk/causes.h"
00090 #include "asterisk/astdb.h"
00091 #include "asterisk/devicestate.h"
00092
00093 #define QUEUE_STRATEGY_RINGALL 0
00094 #define QUEUE_STRATEGY_ROUNDROBIN 1
00095 #define QUEUE_STRATEGY_LEASTRECENT 2
00096 #define QUEUE_STRATEGY_FEWESTCALLS 3
00097 #define QUEUE_STRATEGY_RANDOM 4
00098 #define QUEUE_STRATEGY_RRMEMORY 5
00099
00100 static struct strategy {
00101 int strategy;
00102 char *name;
00103 } strategies[] = {
00104 { QUEUE_STRATEGY_RINGALL, "ringall" },
00105 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
00106 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
00107 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
00108 { QUEUE_STRATEGY_RANDOM, "random" },
00109 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
00110 };
00111
00112 #define DEFAULT_RETRY 5
00113 #define DEFAULT_TIMEOUT 15
00114 #define RECHECK 1
00115
00116 #define RES_OKAY 0
00117 #define RES_EXISTS (-1)
00118 #define RES_OUTOFMEMORY (-2)
00119 #define RES_NOSUCHQUEUE (-3)
00120
00121 static char *tdesc = "True Call Queueing";
00122
00123 static char *app = "Queue";
00124
00125 static char *synopsis = "Queue a call for a call queue";
00126
00127 static char *descrip =
00128 " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
00129 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
00130 "This application will return to the dialplan if the queue does not exist, or\n"
00131 "any of the join options cause the caller to not enter the queue.\n"
00132 "The option string may contain zero or more of the following characters:\n"
00133 " 'd' -- data-quality (modem) call (minimum delay).\n"
00134 " 'h' -- allow callee to hang up by hitting *.\n"
00135 " 'H' -- allow caller to hang up by hitting *.\n"
00136 " 'n' -- no retries on the timeout; will exit this application and \n"
00137 " go to the next step.\n"
00138 " 'r' -- ring instead of playing MOH\n"
00139 " 't' -- allow the called user transfer the calling user\n"
00140 " 'T' -- to allow the calling user to transfer the call.\n"
00141 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
00142 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
00143 " In addition to transferring the call, a call may be parked and then picked\n"
00144 "up by another user.\n"
00145 " The optional URL will be sent to the called party if the channel supports\n"
00146 "it.\n"
00147 " The timeout will cause the queue to fail out after a specified number of\n"
00148 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
00149 " This application sets the following channel variable upon completion:\n"
00150 " QUEUESTATUS The status of the call as a text string, one of\n"
00151 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
00152
00153 static char *app_aqm = "AddQueueMember" ;
00154 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
00155 static char *app_aqm_descrip =
00156 " AddQueueMember(queuename[|interface[|penalty[|options]]]):\n"
00157 "Dynamically adds interface to an existing queue.\n"
00158 "If the interface is already in the queue and there exists an n+101 priority\n"
00159 "then it will then jump to this priority. Otherwise it will return an error\n"
00160 "The option string may contain zero or more of the following characters:\n"
00161 " 'j' -- jump to +101 priority when appropriate.\n"
00162 " This application sets the following channel variable upon completion:\n"
00163 " AQMSTATUS The status of the attempt to add a queue member as a \n"
00164 " text string, one of\n"
00165 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
00166 "Example: AddQueueMember(techsupport|SIP/3000)\n"
00167 "";
00168
00169 static char *app_rqm = "RemoveQueueMember" ;
00170 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
00171 static char *app_rqm_descrip =
00172 " RemoveQueueMember(queuename[|interface[|options]]):\n"
00173 "Dynamically removes interface to an existing queue\n"
00174 "If the interface is NOT in the queue and there exists an n+101 priority\n"
00175 "then it will then jump to this priority. Otherwise it will return an error\n"
00176 "The option string may contain zero or more of the following characters:\n"
00177 " 'j' -- jump to +101 priority when appropriate.\n"
00178 " This application sets the following channel variable upon completion:\n"
00179 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
00180 " text string, one of\n"
00181 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
00182 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
00183 "";
00184
00185 static char *app_pqm = "PauseQueueMember" ;
00186 static char *app_pqm_synopsis = "Pauses a queue member" ;
00187 static char *app_pqm_descrip =
00188 " PauseQueueMember([queuename]|interface[|options]):\n"
00189 "Pauses (blocks calls for) a queue member.\n"
00190 "The given interface will be paused in the given queue. This prevents\n"
00191 "any calls from being sent from the queue to the interface until it is\n"
00192 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
00193 "queuename is given, the interface is paused in every queue it is a\n"
00194 "member of. If the interface is not in the named queue, or if no queue\n"
00195 "is given and the interface is not in any queue, it will jump to\n"
00196 "priority n+101, if it exists and the appropriate options are set.\n"
00197 "The application will fail if the interface is not found and no extension\n"
00198 "to jump to exists.\n"
00199 "The option string may contain zero or more of the following characters:\n"
00200 " 'j' -- jump to +101 priority when appropriate.\n"
00201 " This application sets the following channel variable upon completion:\n"
00202 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
00203 " text string, one of\n"
00204 " PAUSED | NOTFOUND\n"
00205 "Example: PauseQueueMember(|SIP/3000)\n";
00206
00207 static char *app_upqm = "UnpauseQueueMember" ;
00208 static char *app_upqm_synopsis = "Unpauses a queue member" ;
00209 static char *app_upqm_descrip =
00210 " UnpauseQueueMember([queuename]|interface[|options]):\n"
00211 "Unpauses (resumes calls to) a queue member.\n"
00212 "This is the counterpart to PauseQueueMember and operates exactly the\n"
00213 "same way, except it unpauses instead of pausing the given interface.\n"
00214 "The option string may contain zero or more of the following characters:\n"
00215 " 'j' -- jump to +101 priority when appropriate.\n"
00216 " This application sets the following channel variable upon completion:\n"
00217 " UPQMSTATUS The status of the attempt to unpause a queue \n"
00218 " member as a text string, one of\n"
00219 " UNPAUSED | NOTFOUND\n"
00220 "Example: UnpauseQueueMember(|SIP/3000)\n";
00221
00222
00223 static const char *pm_family = "/Queue/PersistentMembers";
00224
00225 #define PM_MAX_LEN 2048
00226
00227
00228 static int queue_persistent_members = 0;
00229
00230
00231 static int use_weight = 0;
00232
00233 enum queue_result {
00234 QUEUE_UNKNOWN = 0,
00235 QUEUE_TIMEOUT = 1,
00236 QUEUE_JOINEMPTY = 2,
00237 QUEUE_LEAVEEMPTY = 3,
00238 QUEUE_JOINUNAVAIL = 4,
00239 QUEUE_LEAVEUNAVAIL = 5,
00240 QUEUE_FULL = 6,
00241 };
00242
00243 const struct {
00244 enum queue_result id;
00245 char *text;
00246 } queue_results[] = {
00247 { QUEUE_UNKNOWN, "UNKNOWN" },
00248 { QUEUE_TIMEOUT, "TIMEOUT" },
00249 { QUEUE_JOINEMPTY,"JOINEMPTY" },
00250 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
00251 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
00252 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
00253 { QUEUE_FULL, "FULL" },
00254 };
00255
00256
00257
00258
00259
00260 struct localuser {
00261 struct ast_channel *chan;
00262 char interface[256];
00263 int stillgoing;
00264 int metric;
00265 int oldstatus;
00266 time_t lastcall;
00267 struct member *member;
00268 struct localuser *next;
00269 };
00270
00271 LOCAL_USER_DECL;
00272
00273
00274 struct queue_ent {
00275 struct ast_call_queue *parent;
00276 char moh[80];
00277 char announce[80];
00278 char context[AST_MAX_CONTEXT];
00279 char digits[AST_MAX_EXTENSION];
00280 int pos;
00281 int prio;
00282 int last_pos_said;
00283 time_t last_periodic_announce_time;
00284 time_t last_pos;
00285 int opos;
00286 int handled;
00287 time_t start;
00288 time_t expire;
00289 struct ast_channel *chan;
00290 struct queue_ent *next;
00291 };
00292
00293 struct member {
00294 char interface[80];
00295 int penalty;
00296 int calls;
00297 int dynamic;
00298 int status;
00299 int paused;
00300 time_t lastcall;
00301 int dead;
00302 struct member *next;
00303 };
00304
00305
00306 #define QUEUE_EMPTY_NORMAL 1
00307 #define QUEUE_EMPTY_STRICT 2
00308 #define ANNOUNCEHOLDTIME_ALWAYS 1
00309 #define ANNOUNCEHOLDTIME_ONCE 2
00310
00311 struct ast_call_queue {
00312 ast_mutex_t lock;
00313 char name[80];
00314 char moh[80];
00315 char announce[80];
00316 char context[AST_MAX_CONTEXT];
00317 unsigned int monjoin:1;
00318 unsigned int dead:1;
00319 unsigned int joinempty:2;
00320 unsigned int eventwhencalled:1;
00321 unsigned int leavewhenempty:2;
00322 unsigned int reportholdtime:1;
00323 unsigned int wrapped:1;
00324 unsigned int timeoutrestart:1;
00325 unsigned int announceholdtime:2;
00326 unsigned int strategy:3;
00327 unsigned int maskmemberstatus:1;
00328 unsigned int realtime:1;
00329 int announcefrequency;
00330 int periodicannouncefrequency;
00331 int roundingseconds;
00332 int holdtime;
00333 int callscompleted;
00334 int callsabandoned;
00335 int servicelevel;
00336 int callscompletedinsl;
00337 char monfmt[8];
00338 char sound_next[80];
00339 char sound_thereare[80];
00340 char sound_calls[80];
00341 char sound_holdtime[80];
00342 char sound_minutes[80];
00343 char sound_lessthan[80];
00344 char sound_seconds[80];
00345 char sound_thanks[80];
00346 char sound_reporthold[80];
00347 char sound_periodicannounce[80];
00348
00349 int count;
00350 int maxlen;
00351 int wrapuptime;
00352
00353 int retry;
00354 int timeout;
00355 int weight;
00356
00357
00358 int rrpos;
00359 int memberdelay;
00360
00361 struct member *members;
00362 struct queue_ent *head;
00363 struct ast_call_queue *next;
00364 };
00365
00366 static struct ast_call_queue *queues = NULL;
00367 AST_MUTEX_DEFINE_STATIC(qlock);
00368
00369 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
00370 {
00371 int i;
00372
00373 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00374 if (queue_results[i].id == res) {
00375 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00376 return;
00377 }
00378 }
00379 }
00380
00381 static char *int2strat(int strategy)
00382 {
00383 int x;
00384 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
00385 if (strategy == strategies[x].strategy)
00386 return strategies[x].name;
00387 }
00388 return "<unknown>";
00389 }
00390
00391 static int strat2int(const char *strategy)
00392 {
00393 int x;
00394 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
00395 if (!strcasecmp(strategy, strategies[x].name))
00396 return strategies[x].strategy;
00397 }
00398 return -1;
00399 }
00400
00401
00402 static inline void insert_entry(struct ast_call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
00403 {
00404 struct queue_ent *cur;
00405
00406 if (!q || !new)
00407 return;
00408 if (prev) {
00409 cur = prev->next;
00410 prev->next = new;
00411 } else {
00412 cur = q->head;
00413 q->head = new;
00414 }
00415 new->next = cur;
00416 new->parent = q;
00417 new->pos = ++(*pos);
00418 new->opos = *pos;
00419 }
00420
00421 enum queue_member_status {
00422 QUEUE_NO_MEMBERS,
00423 QUEUE_NO_REACHABLE_MEMBERS,
00424 QUEUE_NORMAL
00425 };
00426
00427 static enum queue_member_status get_member_status(const struct ast_call_queue *q)
00428 {
00429 struct member *member;
00430 enum queue_member_status result = QUEUE_NO_MEMBERS;
00431
00432 for (member = q->members; member; member = member->next) {
00433 switch (member->status) {
00434 case AST_DEVICE_INVALID:
00435
00436 break;
00437 case AST_DEVICE_UNAVAILABLE:
00438 result = QUEUE_NO_REACHABLE_MEMBERS;
00439 break;
00440 default:
00441 return QUEUE_NORMAL;
00442 }
00443 }
00444
00445 return result;
00446 }
00447
00448 struct statechange {
00449 int state;
00450 char dev[0];
00451 };
00452
00453 static void *changethread(void *data)
00454 {
00455 struct ast_call_queue *q;
00456 struct statechange *sc = data;
00457 struct member *cur;
00458 char *loc;
00459 char *technology;
00460
00461 technology = ast_strdupa(sc->dev);
00462 loc = strchr(technology, '/');
00463 if (loc) {
00464 *loc = '\0';
00465 loc++;
00466 } else {
00467 free(sc);
00468 return NULL;
00469 }
00470 if (option_debug)
00471 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00472 ast_mutex_lock(&qlock);
00473 for (q = queues; q; q = q->next) {
00474 ast_mutex_lock(&q->lock);
00475 cur = q->members;
00476 while(cur) {
00477 if (!strcasecmp(sc->dev, cur->interface)) {
00478 if (cur->status != sc->state) {
00479 cur->status = sc->state;
00480 if (!q->maskmemberstatus) {
00481 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00482 "Queue: %s\r\n"
00483 "Location: %s\r\n"
00484 "Membership: %s\r\n"
00485 "Penalty: %d\r\n"
00486 "CallsTaken: %d\r\n"
00487 "LastCall: %d\r\n"
00488 "Status: %d\r\n"
00489 "Paused: %d\r\n",
00490 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
00491 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00492 }
00493 }
00494 }
00495 cur = cur->next;
00496 }
00497 ast_mutex_unlock(&q->lock);
00498 }
00499 ast_mutex_unlock(&qlock);
00500 free(sc);
00501 return NULL;
00502 }
00503
00504 static int statechange_queue(const char *dev, int state, void *ign)
00505 {
00506
00507
00508 struct statechange *sc;
00509 pthread_t t;
00510 pthread_attr_t attr;
00511
00512 sc = malloc(sizeof(struct statechange) + strlen(dev) + 1);
00513 if (sc) {
00514 sc->state = state;
00515 strcpy(sc->dev, dev);
00516 pthread_attr_init(&attr);
00517 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00518 if (ast_pthread_create(&t, &attr, changethread, sc)) {
00519 ast_log(LOG_WARNING, "Failed to create update thread!\n");
00520 free(sc);
00521 }
00522 }
00523 return 0;
00524 }
00525
00526 static struct member *create_queue_member(char *interface, int penalty, int paused)
00527 {
00528 struct member *cur;
00529
00530
00531
00532 cur = malloc(sizeof(struct member));
00533
00534 if (cur) {
00535 memset(cur, 0, sizeof(struct member));
00536 cur->penalty = penalty;
00537 cur->paused = paused;
00538 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00539 if (!strchr(cur->interface, '/'))
00540 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00541 cur->status = ast_device_state(interface);
00542 }
00543
00544 return cur;
00545 }
00546
00547 static struct ast_call_queue *alloc_queue(const char *queuename)
00548 {
00549 struct ast_call_queue *q;
00550
00551 q = malloc(sizeof(*q));
00552 if (q) {
00553 memset(q, 0, sizeof(*q));
00554 ast_mutex_init(&q->lock);
00555 ast_copy_string(q->name, queuename, sizeof(q->name));
00556 }
00557 return q;
00558 }
00559
00560 static void init_queue(struct ast_call_queue *q)
00561 {
00562 q->dead = 0;
00563 q->retry = DEFAULT_RETRY;
00564 q->timeout = -1;
00565 q->maxlen = 0;
00566 q->announcefrequency = 0;
00567 q->announceholdtime = 0;
00568 q->roundingseconds = 0;
00569 q->servicelevel = 0;
00570 q->moh[0] = '\0';
00571 q->announce[0] = '\0';
00572 q->context[0] = '\0';
00573 q->monfmt[0] = '\0';
00574 q->periodicannouncefrequency = 0;
00575 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00576 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00577 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00578 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00579 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00580 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00581 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00582 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00583 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00584 ast_copy_string(q->sound_periodicannounce, "queue-periodic-announce", sizeof(q->sound_periodicannounce));
00585 }
00586
00587 static void clear_queue(struct ast_call_queue *q)
00588 {
00589 q->holdtime = 0;
00590 q->callscompleted = 0;
00591 q->callsabandoned = 0;
00592 q->callscompletedinsl = 0;
00593 q->wrapuptime = 0;
00594 }
00595
00596
00597
00598
00599
00600
00601
00602
00603 static void queue_set_param(struct ast_call_queue *q, const char *param, const char *val, int linenum, int failunknown)
00604 {
00605 if (!strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00606 ast_copy_string(q->moh, val, sizeof(q->moh));
00607 } else if (!strcasecmp(param, "announce")) {
00608 ast_copy_string(q->announce, val, sizeof(q->announce));
00609 } else if (!strcasecmp(param, "context")) {
00610 ast_copy_string(q->context, val, sizeof(q->context));
00611 } else if (!strcasecmp(param, "timeout")) {
00612 q->timeout = atoi(val);
00613 if (q->timeout < 0)
00614 q->timeout = DEFAULT_TIMEOUT;
00615 } else if (!strcasecmp(param, "monitor-join")) {
00616 q->monjoin = ast_true(val);
00617 } else if (!strcasecmp(param, "monitor-format")) {
00618 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00619 } else if (!strcasecmp(param, "queue-youarenext")) {
00620 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00621 } else if (!strcasecmp(param, "queue-thereare")) {
00622 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00623 } else if (!strcasecmp(param, "queue-callswaiting")) {
00624 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00625 } else if (!strcasecmp(param, "queue-holdtime")) {
00626 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00627 } else if (!strcasecmp(param, "queue-minutes")) {
00628 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00629 } else if (!strcasecmp(param, "queue-seconds")) {
00630 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00631 } else if (!strcasecmp(param, "queue-lessthan")) {
00632 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00633 } else if (!strcasecmp(param, "queue-thankyou")) {
00634 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00635 } else if (!strcasecmp(param, "queue-reporthold")) {
00636 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
00637 } else if (!strcasecmp(param, "announce-frequency")) {
00638 q->announcefrequency = atoi(val);
00639 } else if (!strcasecmp(param, "announce-round-seconds")) {
00640 q->roundingseconds = atoi(val);
00641 if (q->roundingseconds>60 || q->roundingseconds<0) {
00642 if (linenum >= 0) {
00643 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00644 "using 0 instead for queue '%s' at line %d of queues.conf\n",
00645 val, param, q->name, linenum);
00646 } else {
00647 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
00648 "using 0 instead for queue '%s'\n", val, param, q->name);
00649 }
00650 q->roundingseconds=0;
00651 }
00652 } else if (!strcasecmp(param, "announce-holdtime")) {
00653 if (!strcasecmp(val, "once"))
00654 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
00655 else if (ast_true(val))
00656 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
00657 else
00658 q->announceholdtime = 0;
00659 } else if (!strcasecmp(param, "periodic-announce")) {
00660 ast_copy_string(q->sound_periodicannounce, val, sizeof(q->sound_periodicannounce));
00661 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
00662 q->periodicannouncefrequency = atoi(val);
00663 } else if (!strcasecmp(param, "retry")) {
00664 q->retry = atoi(val);
00665 if (q->retry < 0)
00666 q->retry = DEFAULT_RETRY;
00667 } else if (!strcasecmp(param, "wrapuptime")) {
00668 q->wrapuptime = atoi(val);
00669 } else if (!strcasecmp(param, "maxlen")) {
00670 q->maxlen = atoi(val);
00671 if (q->maxlen < 0)
00672 q->maxlen = 0;
00673 } else if (!strcasecmp(param, "servicelevel")) {
00674 q->servicelevel= atoi(val);
00675 } else if (!strcasecmp(param, "strategy")) {
00676 q->strategy = strat2int(val);
00677 if (q->strategy < 0) {
00678 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
00679 val, q->name);
00680 q->strategy = 0;
00681 }
00682 } else if (!strcasecmp(param, "joinempty")) {
00683 if (!strcasecmp(val, "strict"))
00684 q->joinempty = QUEUE_EMPTY_STRICT;
00685 else if (ast_true(val))
00686 q->joinempty = QUEUE_EMPTY_NORMAL;
00687 else
00688 q->joinempty = 0;
00689 } else if (!strcasecmp(param, "leavewhenempty")) {
00690 if (!strcasecmp(val, "strict"))
00691 q->leavewhenempty = QUEUE_EMPTY_STRICT;
00692 else if (ast_true(val))
00693 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
00694 else
00695 q->leavewhenempty = 0;
00696 } else if (!strcasecmp(param, "eventmemberstatus")) {
00697 q->maskmemberstatus = !ast_true(val);
00698 } else if (!strcasecmp(param, "eventwhencalled")) {
00699 q->eventwhencalled = ast_true(val);
00700 } else if (!strcasecmp(param, "reportholdtime")) {
00701 q->reportholdtime = ast_true(val);
00702 } else if (!strcasecmp(param, "memberdelay")) {
00703 q->memberdelay = atoi(val);
00704 } else if (!strcasecmp(param, "weight")) {
00705 q->weight = atoi(val);
00706 if (q->weight)
00707 use_weight++;
00708
00709
00710 } else if (!strcasecmp(param, "timeoutrestart")) {
00711 q->timeoutrestart = ast_true(val);
00712 } else if(failunknown) {
00713 if (linenum >= 0) {
00714 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
00715 q->name, param, linenum);
00716 } else {
00717 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
00718 }
00719 }
00720 }
00721
00722 static void rt_handle_member_record(struct ast_call_queue *q, char *interface, const char *penalty_str)
00723 {
00724 struct member *m, *prev_m;
00725 int penalty = 0;
00726
00727 if(penalty_str) {
00728 penalty = atoi(penalty_str);
00729 if(penalty < 0)
00730 penalty = 0;
00731 }
00732
00733
00734 prev_m = NULL;
00735 m = q->members;
00736 while (m && strcmp(m->interface, interface)) {
00737 prev_m = m;
00738 m = m->next;
00739 }
00740
00741
00742 if (!m) {
00743 m = create_queue_member(interface, penalty, 0);
00744 if (m) {
00745 m->dead = 0;
00746 if (prev_m) {
00747 prev_m->next = m;
00748 } else {
00749 q->members = m;
00750 }
00751 }
00752 } else {
00753 m->dead = 0;
00754 m->penalty = penalty;
00755 }
00756 }
00757
00758 static void free_members(struct ast_call_queue *q, int all)
00759 {
00760
00761 struct member *curm, *next, *prev = NULL;
00762
00763 for (curm = q->members; curm; curm = next) {
00764 next = curm->next;
00765 if (all || !curm->dynamic) {
00766 if (prev)
00767 prev->next = next;
00768 else
00769 q->members = next;
00770 free(curm);
00771 } else
00772 prev = curm;
00773 }
00774 }
00775
00776 static void destroy_queue(struct ast_call_queue *q)
00777 {
00778 free_members(q, 1);
00779 ast_mutex_destroy(&q->lock);
00780 free(q);
00781 }
00782
00783 static void remove_queue(struct ast_call_queue *q)
00784 {
00785 struct ast_call_queue *cur, *prev = NULL;
00786
00787 ast_mutex_lock(&qlock);
00788 for (cur = queues; cur; cur = cur->next) {
00789 if (cur == q) {
00790 if (prev)
00791 prev->next = cur->next;
00792 else
00793 queues = cur->next;
00794 } else {
00795 prev = cur;
00796 }
00797 }
00798 ast_mutex_unlock(&qlock);
00799 }
00800
00801
00802
00803
00804 static struct ast_call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
00805 {
00806 struct ast_variable *v;
00807 struct ast_call_queue *q, *prev_q = NULL;
00808 struct member *m, *prev_m, *next_m;
00809 char *interface;
00810 char *tmp, *tmp_name;
00811 char tmpbuf[64];
00812
00813
00814 for (q = queues; q; q = q->next) {
00815 if (!strcasecmp(q->name, queuename)) {
00816 break;
00817 }
00818 prev_q = q;
00819 }
00820
00821
00822 if (q) {
00823 ast_mutex_lock(&q->lock);
00824 if (!q->realtime) {
00825 if (q->dead) {
00826 ast_mutex_unlock(&q->lock);
00827 return NULL;
00828 } else {
00829 ast_mutex_unlock(&q->lock);
00830 return q;
00831 }
00832 }
00833 } else if (!member_config)
00834
00835 return NULL;
00836
00837
00838 if (!queue_vars) {
00839
00840 if (q) {
00841
00842
00843
00844 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
00845
00846 q->dead = 1;
00847
00848 if (!q->count) {
00849
00850 if (!prev_q) {
00851 queues = q->next;
00852 } else {
00853 prev_q->next = q->next;
00854 }
00855 ast_mutex_unlock(&q->lock);
00856 destroy_queue(q);
00857 } else
00858 ast_mutex_unlock(&q->lock);
00859 }
00860 return NULL;
00861 }
00862
00863
00864 if (!q) {
00865 q = alloc_queue(queuename);
00866 if (!q)
00867 return NULL;
00868 ast_mutex_lock(&q->lock);
00869 clear_queue(q);
00870 q->realtime = 1;
00871 q->next = queues;
00872 queues = q;
00873 }
00874 init_queue(q);
00875
00876 v = queue_vars;
00877 memset(tmpbuf, 0, sizeof(tmpbuf));
00878 while(v) {
00879
00880 if((tmp = strchr(v->name, '_')) != NULL) {
00881 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
00882 tmp_name = tmpbuf;
00883 tmp = tmp_name;
00884 while((tmp = strchr(tmp, '_')) != NULL)
00885 *tmp++ = '-';
00886 } else
00887 tmp_name = v->name;
00888 queue_set_param(q, tmp_name, v->value, -1, 0);
00889 v = v->next;
00890 }
00891
00892
00893 m = q->members;
00894 while (m) {
00895 if (!m->dynamic)
00896 m->dead = 1;
00897 m = m->next;
00898 }
00899
00900 interface = ast_category_browse(member_config, NULL);
00901 while (interface) {
00902 rt_handle_member_record(q, interface, ast_variable_retrieve(member_config, interface, "penalty"));
00903 interface = ast_category_browse(member_config, interface);
00904 }
00905
00906
00907 m = q->members;
00908 prev_m = NULL;
00909 while (m) {
00910 next_m = m->next;
00911 if (m->dead) {
00912 if (prev_m) {
00913 prev_m->next = next_m;
00914 } else {
00915 q->members = next_m;
00916 }
00917 free(m);
00918 } else {
00919 prev_m = m;
00920 }
00921 m = next_m;
00922 }
00923
00924 ast_mutex_unlock(&q->lock);
00925
00926 return q;
00927 }
00928
00929 static struct ast_call_queue *load_realtime_queue(char *queuename)
00930 {
00931 struct ast_variable *queue_vars = NULL;
00932 struct ast_config *member_config = NULL;
00933 struct ast_call_queue *q;
00934
00935
00936 ast_mutex_lock(&qlock);
00937 for (q = queues; q; q = q->next) {
00938 if (!strcasecmp(q->name, queuename)) {
00939 break;
00940 }
00941 }
00942 ast_mutex_unlock(&qlock);
00943
00944 if (!q) {
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
00955 if (queue_vars) {
00956 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
00957 if (!member_config) {
00958 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
00959 return NULL;
00960 }
00961 }
00962
00963 ast_mutex_lock(&qlock);
00964
00965 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
00966 if (member_config)
00967 ast_config_destroy(member_config);
00968 if (queue_vars)
00969 ast_variables_destroy(queue_vars);
00970
00971 ast_mutex_unlock(&qlock);
00972 }
00973 return q;
00974 }
00975
00976 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
00977 {
00978 struct ast_call_queue *q;
00979 struct queue_ent *cur, *prev = NULL;
00980 int res = -1;
00981 int pos = 0;
00982 int inserted = 0;
00983 enum queue_member_status stat;
00984
00985 q = load_realtime_queue(queuename);
00986 if (!q)
00987 return res;
00988
00989 ast_mutex_lock(&qlock);
00990 ast_mutex_lock(&q->lock);
00991
00992
00993 stat = get_member_status(q);
00994 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
00995 *reason = QUEUE_JOINEMPTY;
00996 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
00997 *reason = QUEUE_JOINUNAVAIL;
00998 else if (q->maxlen && (q->count >= q->maxlen))
00999 *reason = QUEUE_FULL;
01000 else {
01001
01002
01003
01004 inserted = 0;
01005 prev = NULL;
01006 cur = q->head;
01007 while(cur) {
01008
01009
01010
01011 if ((!inserted) && (qe->prio > cur->prio)) {
01012 insert_entry(q, prev, qe, &pos);
01013 inserted = 1;
01014 }
01015 cur->pos = ++pos;
01016 prev = cur;
01017 cur = cur->next;
01018 }
01019
01020 if (!inserted)
01021 insert_entry(q, prev, qe, &pos);
01022 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01023 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01024 ast_copy_string(qe->context, q->context, sizeof(qe->context));
01025 q->count++;
01026 res = 0;
01027 manager_event(EVENT_FLAG_CALL, "Join",
01028 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
01029 qe->chan->name,
01030 qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
01031 qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
01032 q->name, qe->pos, q->count );
01033 #if 0
01034 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01035 #endif
01036 }
01037 ast_mutex_unlock(&q->lock);
01038 ast_mutex_unlock(&qlock);
01039 return res;
01040 }
01041
01042 static int play_file(struct ast_channel *chan, char *filename)
01043 {
01044 int res;
01045
01046 ast_stopstream(chan);
01047 res = ast_streamfile(chan, filename, chan->language);
01048
01049 if (!res)
01050 res = ast_waitstream(chan, AST_DIGIT_ANY);
01051 else
01052 res = 0;
01053
01054 ast_stopstream(chan);
01055
01056 return res;
01057 }
01058
01059 static int valid_exit(struct queue_ent *qe, char digit)
01060 {
01061 int digitlen = strlen(qe->digits);
01062
01063
01064 if (digitlen < sizeof(qe->digits) - 2) {
01065 qe->digits[digitlen] = digit;
01066 qe->digits[digitlen + 1] = '\0';
01067 } else {
01068 qe->digits[0] = '\0';
01069 return 0;
01070 }
01071
01072
01073 if (ast_strlen_zero(qe->context))
01074 return 0;
01075
01076
01077 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01078 qe->digits[0] = '\0';
01079 return 0;
01080 }
01081
01082
01083 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01084
01085 return 1;
01086 }
01087 return 0;
01088 }
01089
01090 static int say_position(struct queue_ent *qe)
01091 {
01092 int res = 0, avgholdmins, avgholdsecs;
01093 time_t now;
01094
01095
01096 time(&now);
01097 if ( (now - qe->last_pos) < 15 )
01098 return 0;
01099
01100
01101 if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
01102 return 0;
01103
01104 ast_moh_stop(qe->chan);
01105
01106 if (qe->pos == 1) {
01107 res = play_file(qe->chan, qe->parent->sound_next);
01108 if (res && valid_exit(qe, res))
01109 goto playout;
01110 else
01111 goto posout;
01112 } else {
01113 res = play_file(qe->chan, qe->parent->sound_thereare);
01114 if (res && valid_exit(qe, res))
01115 goto playout;
01116 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL);
01117 if (res && valid_exit(qe, res))
01118 goto playout;
01119 res = play_file(qe->chan, qe->parent->sound_calls);
01120 if (res && valid_exit(qe, res))
01121 goto playout;
01122 }
01123
01124 avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
01125
01126
01127 if(qe->parent->roundingseconds) {
01128 avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
01129 avgholdsecs*= qe->parent->roundingseconds;
01130 } else {
01131 avgholdsecs=0;
01132 }
01133
01134 if (option_verbose > 2)
01135 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01136
01137
01138
01139 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01140 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01141 res = play_file(qe->chan, qe->parent->sound_holdtime);
01142 if (res && valid_exit(qe, res))
01143 goto playout;
01144
01145 if (avgholdmins>0) {
01146 if (avgholdmins < 2) {
01147 res = play_file(qe->chan, qe->parent->sound_lessthan);
01148 if (res && valid_exit(qe, res))
01149 goto playout;
01150
01151 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
01152 if (res && valid_exit(qe, res))
01153 goto playout;
01154 } else {
01155 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
01156 if (res && valid_exit(qe, res))
01157 goto playout;
01158 }
01159
01160 res = play_file(qe->chan, qe->parent->sound_minutes);
01161 if (res && valid_exit(qe, res))
01162 goto playout;
01163 }
01164 if (avgholdsecs>0) {
01165 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
01166 if (res && valid_exit(qe, res))
01167 goto playout;
01168
01169 res = play_file(qe->chan, qe->parent->sound_seconds);
01170 if (res && valid_exit(qe, res))
01171 goto playout;
01172 }
01173
01174 }
01175
01176 posout:
01177 if (option_verbose > 2)
01178 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01179 qe->chan->name, qe->parent->name, qe->pos);
01180 res = play_file(qe->chan, qe->parent->sound_thanks);
01181
01182 playout:
01183
01184 qe->last_pos = now;
01185 qe->last_pos_said = qe->pos;
01186 ast_moh_start(qe->chan, qe->moh);
01187
01188 return res;
01189 }
01190
01191 static void recalc_holdtime(struct queue_ent *qe)
01192 {
01193 int oldvalue, newvalue;
01194
01195
01196
01197
01198
01199 newvalue = time(NULL) - qe->start;
01200
01201 ast_mutex_lock(&qe->parent->lock);
01202 if (newvalue <= qe->parent->servicelevel)
01203 qe->parent->callscompletedinsl++;
01204 oldvalue = qe->parent->holdtime;
01205 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
01206 ast_mutex_unlock(&qe->parent->lock);
01207 }
01208
01209
01210 static void leave_queue(struct queue_ent *qe)
01211 {
01212 struct ast_call_queue *q;
01213 struct queue_ent *cur, *prev = NULL;
01214 int pos = 0;
01215
01216 q = qe->parent;
01217 if (!q)
01218 return;
01219 ast_mutex_lock(&q->lock);
01220
01221 prev = NULL;
01222 cur = q->head;
01223 while(cur) {
01224 if (cur == qe) {
01225 q->count--;
01226
01227
01228 manager_event(EVENT_FLAG_CALL, "Leave",
01229 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
01230 qe->chan->name, q->name, q->count);
01231 #if 0
01232 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01233 #endif
01234
01235 if (prev)
01236 prev->next = cur->next;
01237 else
01238 q->head = cur->next;
01239 } else {
01240
01241 cur->pos = ++pos;
01242 prev = cur;
01243 }
01244 cur = cur->next;
01245 }
01246 ast_mutex_unlock(&q->lock);
01247 if (q->dead && !q->count) {
01248
01249 remove_queue(q);
01250 destroy_queue(q);
01251 }
01252 }
01253
01254
01255 static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception)
01256 {
01257 struct localuser *oo;
01258
01259 while(outgoing) {
01260
01261 if (outgoing->chan && (outgoing->chan != exception))
01262 ast_hangup(outgoing->chan);
01263 oo = outgoing;
01264 outgoing=outgoing->next;
01265 free(oo);
01266 }
01267 }
01268
01269 static int update_status(struct ast_call_queue *q, struct member *member, int status)
01270 {
01271 struct member *cur;
01272
01273
01274
01275 ast_mutex_lock(&q->lock);
01276 cur = q->members;
01277 while(cur) {
01278 if (member == cur) {
01279 cur->status = status;
01280 if (!q->maskmemberstatus) {
01281 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01282 "Queue: %s\r\n"
01283 "Location: %s\r\n"
01284 "Membership: %s\r\n"
01285 "Penalty: %d\r\n"
01286 "CallsTaken: %d\r\n"
01287 "LastCall: %d\r\n"
01288 "Status: %d\r\n"
01289 "Paused: %d\r\n",
01290 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
01291 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
01292 }
01293 break;
01294 }
01295 cur = cur->next;
01296 }
01297 ast_mutex_unlock(&q->lock);
01298 return 0;
01299 }
01300
01301 static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
01302 {
01303 if (status == AST_CAUSE_BUSY)
01304 status = AST_DEVICE_BUSY;
01305 else if (status == AST_CAUSE_UNREGISTERED)
01306 status = AST_DEVICE_UNAVAILABLE;
01307 else if (status == AST_CAUSE_NOSUCHDRIVER)
01308 status = AST_DEVICE_INVALID;
01309 else
01310 status = AST_DEVICE_UNKNOWN;
01311 return update_status(q, member, status);
01312 }
01313
01314
01315
01316 static int compare_weight(struct ast_call_queue *rq, struct member *member)
01317 {
01318 struct ast_call_queue *q;
01319 struct member *mem;
01320 int found = 0;
01321
01322
01323
01324 for (q = queues; q; q = q->next) {
01325 if (q == rq)
01326 continue;
01327 ast_mutex_lock(&q->lock);
01328 if (q->count && q->members) {
01329 for (mem = q->members; mem; mem = mem->next) {
01330 if (!strcmp(mem->interface, member->interface)) {
01331 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01332 if (q->weight > rq->weight) {
01333 ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
01334 found = 1;
01335 break;
01336 }
01337 }
01338 }
01339 }
01340 ast_mutex_unlock(&q->lock);
01341 if (found)
01342 break;
01343 }
01344 ast_mutex_unlock(&qlock);
01345 return found;
01346 }
01347
01348 static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies)
01349 {
01350 int res;
01351 int status;
01352 char tech[256];
01353 char *location;
01354
01355 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01356 if (option_debug)
01357 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
01358 if (qe->chan->cdr)
01359 ast_cdr_busy(qe->chan->cdr);
01360 tmp->stillgoing = 0;
01361 (*busies)++;
01362 return 0;
01363 }
01364
01365 if (tmp->member->paused) {
01366 if (option_debug)
01367 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
01368 if (qe->chan->cdr)
01369 ast_cdr_busy(qe->chan->cdr);
01370 tmp->stillgoing = 0;
01371 return 0;
01372 }
01373 if (use_weight && compare_weight(qe->parent,tmp->member)) {
01374 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01375 if (qe->chan->cdr)
01376 ast_cdr_busy(qe->chan->cdr);
01377 tmp->stillgoing = 0;
01378 (*busies)++;
01379 return 0;
01380 }
01381
01382 ast_copy_string(tech, tmp->interface, sizeof(tech));
01383 if ((location = strchr(tech, '/')))
01384 *location++ = '\0';
01385 else
01386 location = "";
01387
01388
01389 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01390 if (!tmp->chan) {
01391 #if 0
01392 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech);
01393 #endif
01394 if (qe->chan->cdr)
01395 ast_cdr_busy(qe->chan->cdr);
01396 tmp->stillgoing = 0;
01397 update_dial_status(qe->parent, tmp->member, status);
01398 (*busies)++;
01399 return 0;
01400 } else if (status != tmp->oldstatus)
01401 update_dial_status(qe->parent, tmp->member, status);
01402
01403 tmp->chan->appl = "AppQueue";
01404 tmp->chan->data = "(Outgoing Line)";
01405 tmp->chan->whentohangup = 0;
01406 if (tmp->chan->cid.cid_num)
01407 free(tmp->chan->cid.cid_num);
01408 tmp->chan->cid.cid_num = NULL;
01409 if (tmp->chan->cid.cid_name)
01410 free(tmp->chan->cid.cid_name);
01411 tmp->chan->cid.cid_name = NULL;
01412 if (tmp->chan->cid.cid_ani)
01413 free(tmp->chan->cid.cid_ani);
01414 tmp->chan->cid.cid_ani = NULL;
01415 if (qe->chan->cid.cid_num)
01416 tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
01417 if (qe->chan->cid.cid_name)
01418 tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
01419 if (qe->chan->cid.cid_ani)
01420 tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
01421
01422
01423 ast_channel_inherit_variables(qe->chan, tmp->chan);
01424
01425
01426 tmp->chan->adsicpe = qe->chan->adsicpe;
01427
01428
01429 res = ast_call(tmp->chan, location, 0);
01430 if (res) {
01431
01432 if (option_debug)
01433 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01434 else if (option_verbose > 2)
01435 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01436 ast_hangup(tmp->chan);
01437 tmp->chan = NULL;
01438 tmp->stillgoing = 0;
01439 (*busies)++;
01440 return 0;
01441 } else {
01442 if (qe->parent->eventwhencalled) {
01443 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01444 "AgentCalled: %s\r\n"
01445 "ChannelCalling: %s\r\n"
01446 "CallerID: %s\r\n"
01447 "CallerIDName: %s\r\n"
01448 "Context: %s\r\n"
01449 "Extension: %s\r\n"
01450 "Priority: %d\r\n",
01451 tmp->interface, qe->chan->name,
01452 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01453 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01454 qe->chan->context, qe->chan->exten, qe->chan->priority);
01455 }
01456 if (option_verbose > 2)
01457 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01458 }
01459 return 1;
01460 }
01461
01462 static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies)
01463 {
01464 struct localuser *cur;
01465 struct localuser *best;
01466 int bestmetric=0;
01467
01468 do {
01469 best = NULL;
01470 cur = outgoing;
01471 while(cur) {
01472 if (cur->stillgoing &&
01473 !cur->chan &&
01474 (!best || (cur->metric < bestmetric))) {
01475 bestmetric = cur->metric;
01476 best = cur;
01477 }
01478 cur = cur->next;
01479 }
01480 if (best) {
01481 if (!qe->parent->strategy) {
01482
01483 cur = outgoing;
01484 while(cur) {
01485 if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) {
01486 if (option_debug)
01487 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01488 ring_entry(qe, cur, busies);
01489 }
01490 cur = cur->next;
01491 }
01492 } else {
01493
01494 if (option_debug)
01495 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01496 ring_entry(qe, best, busies);
01497 }
01498 }
01499 } while (best && !best->chan);
01500 if (!best) {
01501 if (option_debug)
01502 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01503 return 0;
01504 }
01505 return 1;
01506 }
01507
01508 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
01509 {
01510 struct localuser *cur;
01511 struct localuser *best;
01512 int bestmetric=0;
01513
01514 best = NULL;
01515 cur = outgoing;
01516 while(cur) {
01517 if (cur->stillgoing &&
01518 !cur->chan &&
01519 (!best || (cur->metric < bestmetric))) {
01520 bestmetric = cur->metric;
01521 best = cur;
01522 }
01523 cur = cur->next;
01524 }
01525 if (best) {
01526
01527 if (option_debug)
01528 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01529 qe->parent->rrpos = best->metric % 1000;
01530 } else {
01531
01532 if (qe->parent->wrapped) {
01533
01534 qe->parent->rrpos = 0;
01535 } else {
01536
01537 qe->parent->rrpos++;
01538 }
01539 }
01540 qe->parent->wrapped = 0;
01541 return 0;
01542 }
01543
01544 static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
01545 {
01546 int res;
01547
01548 ast_stopstream(chan);
01549 res = ast_streamfile(chan, filename, chan->language);
01550
01551 if (!res) {
01552
01553 res = ast_waitstream(chan, AST_DIGIT_ANY);
01554 if (res <= 0 || !valid_exit(qe, res))
01555 res = 0;
01556
01557
01558 ast_stopstream(chan);
01559 } else {
01560 res = 0;
01561 }
01562
01563
01564
01565
01566
01567
01568 return res;
01569 }
01570
01571 static int say_periodic_announcement(struct queue_ent *qe)
01572 {
01573 int res = 0;
01574 time_t now;
01575
01576
01577 time(&now);
01578
01579
01580 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
01581 return 0;
01582
01583
01584 ast_moh_stop(qe->chan);
01585
01586 if (option_verbose > 2)
01587 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
01588
01589
01590 res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce);
01591
01592
01593 ast_moh_start(qe->chan, qe->moh);
01594
01595
01596 qe->last_periodic_announce_time = now;
01597
01598 return res;
01599 }
01600
01601 static void record_abandoned(struct queue_ent *qe)
01602 {
01603 ast_mutex_lock(&qe->parent->lock);
01604 qe->parent->callsabandoned++;
01605 ast_mutex_unlock(&qe->parent->lock);
01606 }
01607
01608
01609 #define AST_MAX_WATCHERS 256
01610
01611 #define BUILD_WATCHERS do { \
01612 o = outgoing; \
01613 found = -1; \
01614 pos = 1; \
01615 numlines = 0; \
01616 watchers[0] = in; \
01617 while(o) { \
01618 \
01619 if (o->stillgoing) { \
01620 stillgoing = 1; \
01621 if (o->chan) { \
01622 watchers[pos++] = o->chan; \
01623 found = 1; \
01624 } \
01625 } \
01626 o = o->next; \
01627 numlines++; \
01628 } \
01629 } while(0)
01630
01631 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect)
01632 {
01633 char *queue = qe->parent->name;
01634 struct localuser *o;
01635 int found;
01636 int numlines;
01637 int status;
01638 int sentringing = 0;
01639 int numbusies = prebusies;
01640 int numnochan = 0;
01641 int stillgoing = 0;
01642 int orig = *to;
01643 struct ast_frame *f;
01644 struct localuser *peer = NULL;
01645 struct ast_channel *watchers[AST_MAX_WATCHERS];
01646 int pos;
01647 struct ast_channel *winner;
01648 struct ast_channel *in = qe->chan;
01649
01650 while(*to && !peer) {
01651 BUILD_WATCHERS;
01652 if ((found < 0) && stillgoing && !qe->parent->strategy) {
01653
01654
01655 ring_one(qe, outgoing, &numbusies);
01656 BUILD_WATCHERS;
01657 }
01658 if (found < 0) {
01659 if (numlines == (numbusies + numnochan)) {
01660 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
01661 } else {
01662 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
01663 }
01664 *to = 0;
01665 return NULL;
01666 }
01667 winner = ast_waitfor_n(watchers, pos, to);
01668 o = outgoing;
01669 while(o) {
01670 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
01671 if (!peer) {
01672 if (option_verbose > 2)
01673 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01674 peer = o;
01675 }
01676 } else if (o->chan && (o->chan == winner)) {
01677 if (!ast_strlen_zero(o->chan->call_forward)) {
01678 char tmpchan[256]="";
01679 char *stuff;
01680 char *tech;
01681 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
01682 if ((stuff = strchr(tmpchan, '/'))) {
01683 *stuff = '\0';
01684 stuff++;
01685 tech = tmpchan;
01686 } else {
01687 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
01688 stuff = tmpchan;
01689 tech = "Local";
01690 }
01691
01692 if (option_verbose > 2)
01693 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
01694
01695 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
01696 if (status != o->oldstatus)
01697 update_dial_status(qe->parent, o->member, status);
01698 if (!o->chan) {
01699 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
01700 o->stillgoing = 0;
01701 numnochan++;
01702 } else {
01703 if (o->chan->cid.cid_num)
01704 free(o->chan->cid.cid_num);
01705 o->chan->cid.cid_num = NULL;
01706 if (o->chan->cid.cid_name)
01707 free(o->chan->cid.cid_name);
01708 o->chan->cid.cid_name = NULL;
01709
01710 if (in->cid.cid_num) {
01711 o->chan->cid.cid_num = strdup(in->cid.cid_num);
01712 if (!o->chan->cid.cid_num)
01713 ast_log(LOG_WARNING, "Out of memory\n");
01714 }
01715 if (in->cid.cid_name) {
01716 o->chan->cid.cid_name = strdup(in->cid.cid_name);
01717 if (!o->chan->cid.cid_name)
01718 ast_log(LOG_WARNING, "Out of memory\n");
01719 }
01720 ast_copy_string(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode));
01721 o->chan->cdrflags = in->cdrflags;
01722
01723 if (in->cid.cid_ani) {
01724 if (o->chan->cid.cid_ani)
01725 free(o->chan->cid.cid_ani);
01726 o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
01727 if (o->chan->cid.cid_ani)
01728 strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
01729 else
01730 ast_log(LOG_WARNING, "Out of memory\n");
01731 }
01732 if (o->chan->cid.cid_rdnis)
01733 free(o->chan->cid.cid_rdnis);
01734 if (!ast_strlen_zero(in->macroexten))
01735 o->chan->cid.cid_rdnis = strdup(in->macroexten);
01736 else
01737 o->chan->cid.cid_rdnis = strdup(in->exten);
01738 if (ast_call(o->chan, tmpchan, 0)) {
01739 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
01740 o->stillgoing = 0;
01741 ast_hangup(o->chan);
01742 o->chan = NULL;
01743 numnochan++;
01744 }
01745 }
01746
01747 ast_hangup(winner);
01748 continue;
01749 }
01750 f = ast_read(winner);
01751 if (f) {
01752 if (f->frametype == AST_FRAME_CONTROL) {
01753 switch(f->subclass) {
01754 case AST_CONTROL_ANSWER:
01755
01756 if (!peer) {
01757 if (option_verbose > 2)
01758 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
01759 peer = o;
01760 }
01761 break;
01762 case AST_CONTROL_BUSY:
01763 if (option_verbose > 2)
01764 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
01765 o->stillgoing = 0;
01766 if (in->cdr)
01767 ast_cdr_busy(in->cdr);
01768 ast_hangup(o->chan);
01769 o->chan = NULL;
01770 if (qe->parent->strategy) {
01771 if (qe->parent->timeoutrestart)
01772 *to = orig;
01773 ring_one(qe, outgoing, &numbusies);
01774 }
01775 numbusies++;
01776 break;
01777 case AST_CONTROL_CONGESTION:
01778 if (option_verbose > 2)
01779 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
01780 o->stillgoing = 0;
01781 if (in->cdr)
01782 ast_cdr_busy(in->cdr);
01783 ast_hangup(o->chan);
01784 o->chan = NULL;
01785 if (qe->parent->strategy) {
01786 if (qe->parent->timeoutrestart)
01787 *to = orig;
01788 ring_one(qe, outgoing, &numbusies);
01789 }
01790 numbusies++;
01791 break;
01792 case AST_CONTROL_RINGING:
01793 if (option_verbose > 2)
01794 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
01795 if (!sentringing) {
01796 #if 0
01797 ast_indicate(in, AST_CONTROL_RINGING);
01798 #endif
01799 sentringing++;
01800 }
01801 break;
01802 case AST_CONTROL_OFFHOOK:
01803
01804 break;
01805 default:
01806 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
01807 }
01808 }
01809 ast_frfree(f);
01810 } else {
01811 o->stillgoing = 0;
01812 ast_hangup(o->chan);
01813 o->chan = NULL;
01814 if (qe->parent->strategy) {
01815 if (qe->parent->timeoutrestart)
01816 *to = orig;
01817 ring_one(qe, outgoing, &numbusies);
01818 }
01819 }
01820 }
01821 o = o->next;
01822 }
01823 if (winner == in) {
01824 f = ast_read(in);
01825 #if 0
01826 if (f && (f->frametype != AST_FRAME_VOICE))
01827 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
01828 else if (!f || (f->frametype != AST_FRAME_VOICE))
01829 printf("Hangup received on %s\n", in->name);
01830 #endif
01831 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
01832
01833 *to=-1;
01834 if (f)
01835 ast_frfree(f);
01836 return NULL;
01837 }
01838 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
01839 if (option_verbose > 3)
01840 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
01841 *to=0;
01842 ast_frfree(f);
01843 return NULL;
01844 }
01845 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
01846 if (option_verbose > 3)
01847 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
01848 *to=0;
01849 *digit=f->subclass;
01850 ast_frfree(f);
01851 return NULL;
01852 }
01853 ast_frfree(f);
01854 }
01855 if (!*to && (option_verbose > 2))
01856 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
01857 }
01858
01859 return peer;
01860
01861 }
01862
01863 static int is_our_turn(struct queue_ent *qe)
01864 {
01865 struct queue_ent *ch;
01866 int res;
01867
01868
01869 ch = qe->parent->head;
01870
01871 if (ch == qe) {
01872 if (option_debug)
01873 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
01874 res = 1;
01875 } else {
01876 if (option_debug)
01877 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
01878 res = 0;
01879 }
01880 return res;
01881 }
01882
01883 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
01884 {
01885 int res = 0;
01886
01887
01888 for (;;) {
01889 enum queue_member_status stat;
01890
01891 if (is_our_turn(qe))
01892 break;
01893
01894
01895 if (qe->expire && (time(NULL) > qe->expire)) {
01896 *reason = QUEUE_TIMEOUT;
01897 ast_queue_log(qe->parent->name, qe->chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe->pos);
01898 break;
01899 }
01900
01901 stat = get_member_status(qe->parent);
01902
01903
01904 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
01905 *reason = QUEUE_LEAVEEMPTY;
01906 leave_queue(qe);
01907 break;
01908 }
01909
01910
01911 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
01912 *reason = QUEUE_LEAVEUNAVAIL;
01913 leave_queue(qe);
01914 break;
01915 }
01916
01917
01918 if (qe->parent->announcefrequency && !ringing)
01919 res = say_position(qe);
01920 if (res)
01921 break;
01922
01923
01924 if (qe->parent->periodicannouncefrequency && !ringing)
01925 res = say_periodic_announcement(qe);
01926
01927
01928 if (!res) res = ast_waitfordigit(qe->chan, RECHECK * 1000);
01929 if (res)
01930 break;
01931 }
01932 return res;
01933 }
01934
01935 static int update_queue(struct ast_call_queue *q, struct member *member)
01936 {
01937 struct member *cur;
01938
01939
01940
01941 ast_mutex_lock(&q->lock);
01942 cur = q->members;
01943 while(cur) {
01944 if (member == cur) {
01945 time(&cur->lastcall);
01946 cur->calls++;
01947 break;
01948 }
01949 cur = cur->next;
01950 }
01951 q->callscompleted++;
01952 ast_mutex_unlock(&q->lock);
01953 return 0;
01954 }
01955
01956 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
01957 {
01958 switch (q->strategy) {
01959 case QUEUE_STRATEGY_RINGALL:
01960
01961 tmp->metric = mem->penalty * 1000000;
01962 break;
01963 case QUEUE_STRATEGY_ROUNDROBIN:
01964 if (!pos) {
01965 if (!q->wrapped) {
01966
01967 q->rrpos = 0;
01968 } else {
01969
01970 q->rrpos++;
01971 }
01972 q->wrapped = 0;
01973 }
01974
01975 case QUEUE_STRATEGY_RRMEMORY:
01976 if (pos < q->rrpos) {
01977 tmp->metric = 1000 + pos;
01978 } else {
01979 if (pos > q->rrpos)
01980
01981 q->wrapped = 1;
01982 tmp->metric = pos;
01983 }
01984 tmp->metric += mem->penalty * 1000000;
01985 break;
01986 case QUEUE_STRATEGY_RANDOM:
01987 tmp->metric = rand() % 1000;
01988 tmp->metric += mem->penalty * 1000000;
01989 break;
01990 case QUEUE_STRATEGY_FEWESTCALLS:
01991 tmp->metric = mem->calls;
01992 tmp->metric += mem->penalty * 1000000;
01993 break;
01994 case QUEUE_STRATEGY_LEASTRECENT:
01995 if (!mem->lastcall)
01996 tmp->metric = 0;
01997 else
01998 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
01999 tmp->metric += mem->penalty * 1000000;
02000 break;
02001 default:
02002 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02003 break;
02004 }
02005 return 0;
02006 }
02007
02008 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on)
02009 {
02010 struct member *cur;
02011 struct localuser *outgoing=NULL, *tmp = NULL;
02012 int to;
02013 char restofit[AST_MAX_EXTENSION];
02014 char oldexten[AST_MAX_EXTENSION]="";
02015 char oldcontext[AST_MAX_CONTEXT]="";
02016 char queuename[256]="";
02017 char *newnum;
02018 char *monitorfilename;
02019 struct ast_channel *peer;
02020 struct ast_channel *which;
02021 struct localuser *lpeer;
02022 struct member *member;
02023 int res = 0, bridge = 0;
02024 int numbusies = 0;
02025 int x=0;
02026 char *announce = NULL;
02027 char digit = 0;
02028 time_t callstart;
02029 time_t now = time(NULL);
02030 struct ast_bridge_config bridge_config;
02031 char nondataquality = 1;
02032
02033 memset(&bridge_config, 0, sizeof(bridge_config));
02034 time(&now);
02035
02036 for (; options && *options; options++)
02037 switch (*options) {
02038 case 't':
02039 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02040 break;
02041 case 'T':
02042 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02043 break;
02044 case 'w':
02045 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02046 break;
02047 case 'W':
02048 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02049 break;
02050 case 'd':
02051 nondataquality = 0;
02052 break;
02053 case 'h':
02054 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02055 break;
02056 case 'H':
02057 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02058 break;
02059 case 'n':
02060 if ((now - qe->start >= qe->parent->timeout))
02061 *go_on = 1;
02062 break;
02063 }
02064
02065
02066 if (use_weight)
02067 ast_mutex_lock(&qlock);
02068 ast_mutex_lock(&qe->parent->lock);
02069 if (option_debug)
02070 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02071 qe->chan->name);
02072 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02073 cur = qe->parent->members;
02074 if (!ast_strlen_zero(qe->announce))
02075 announce = qe->announce;
02076 if (!ast_strlen_zero(announceoverride))
02077 announce = announceoverride;
02078
02079 while(cur) {
02080 tmp = malloc(sizeof(*tmp));
02081 if (!tmp) {
02082 ast_mutex_unlock(&qe->parent->lock);
02083 if (use_weight)
02084 ast_mutex_unlock(&qlock);
02085 ast_log(LOG_WARNING, "Out of memory\n");
02086 goto out;
02087 }
02088 memset(tmp, 0, sizeof(*tmp));
02089 tmp->stillgoing = -1;
02090 if (option_debug) {
02091 if (url)
02092 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
02093 else
02094 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
02095 }
02096
02097 tmp->member = cur;
02098 tmp->oldstatus = cur->status;
02099 tmp->lastcall = cur->lastcall;
02100 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02101
02102 if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
02103 newnum++;
02104 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
02105 snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
02106 if (option_debug)
02107 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
02108 }
02109
02110
02111 calc_metric(qe->parent, cur, x++, qe, tmp);
02112
02113
02114
02115 tmp->next = outgoing;
02116 outgoing = tmp;
02117
02118 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02119 break;
02120
02121 cur = cur->next;
02122 }
02123 if (qe->parent->timeout)
02124 to = qe->parent->timeout * 1000;
02125 else
02126 to = -1;
02127 ring_one(qe, outgoing, &numbusies);
02128 ast_mutex_unlock(&qe->parent->lock);
02129 if (use_weight)
02130 ast_mutex_unlock(&qlock);
02131 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT));
02132 ast_mutex_lock(&qe->parent->lock);
02133 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02134 store_next(qe, outgoing);
02135 }
02136 ast_mutex_unlock(&qe->parent->lock);
02137 if (lpeer)
02138 peer = lpeer->chan;
02139 else
02140 peer = NULL;
02141 if (!peer) {
02142 if (to) {
02143
02144 res = -1;
02145 } else {
02146 res = digit;
02147 }
02148 if (option_debug)
02149 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
02150 goto out;
02151 }
02152 if (peer) {
02153
02154
02155
02156 qe->handled++;
02157 if (!strcmp(qe->chan->type,"Zap"))
02158 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02159 if (!strcmp(peer->type,"Zap"))
02160 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02161
02162 recalc_holdtime(qe);
02163 member = lpeer->member;
02164 hangupcalls(outgoing, peer);
02165 outgoing = NULL;
02166 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02167 int res2;
02168 res2 = ast_autoservice_start(qe->chan);
02169 if (!res2) {
02170 if (qe->parent->memberdelay) {
02171 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02172 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02173 }
02174 if (!res2 && announce) {
02175 if (play_file(peer, announce))
02176 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
02177 }
02178 if (!res2 && qe->parent->reportholdtime) {
02179 if (!play_file(peer, qe->parent->sound_reporthold)) {
02180 int holdtime;
02181
02182 time(&now);
02183 holdtime = abs((now - qe->start) / 60);
02184 if (holdtime < 2) {
02185 play_file(peer, qe->parent->sound_lessthan);
02186 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02187 } else
02188 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02189 play_file(peer, qe->parent->sound_minutes);
02190 }
02191 }
02192 }
02193 res2 |= ast_autoservice_stop(qe->chan);
02194 if (peer->_softhangup) {
02195
02196 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
02197 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
02198 record_abandoned(qe);
02199 if (qe->parent->eventwhencalled) {
02200 manager_event(EVENT_FLAG_AGENT, "AgentDump",
02201 "Queue: %s\r\n"
02202 "Uniqueid: %s\r\n"
02203 "Channel: %s\r\n"
02204 "Member: %s\r\n",
02205 queuename, qe->chan->uniqueid, peer->name, member->interface);
02206 }
02207 ast_hangup(peer);
02208 goto out;
02209 } else if (res2) {
02210
02211 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02212 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02213 record_abandoned(qe);
02214 ast_hangup(peer);
02215 return -1;
02216 }
02217 }
02218
02219 ast_moh_stop(qe->chan);
02220
02221 if (qe->chan->cdr)
02222 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02223
02224 res = ast_channel_make_compatible(qe->chan, peer);
02225 if (res < 0) {
02226 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
02227 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02228 record_abandoned(qe);
02229 ast_hangup(peer);
02230 return -1;
02231 }
02232
02233 if (qe->parent->monfmt && *qe->parent->monfmt) {
02234 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02235 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02236 which = qe->chan;
02237 else
02238 which = peer;
02239 if (monitorfilename)
02240 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02241 else if (qe->chan->cdr)
02242 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02243 else {
02244
02245 char tmpid[256];
02246 snprintf(tmpid, sizeof(tmpid), "chan-%x", rand());
02247 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02248 }
02249 if (qe->parent->monjoin)
02250 ast_monitor_setjoinfiles(which, 1);
02251 }
02252
02253 leave_queue(qe);
02254 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
02255 if (option_debug)
02256 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
02257 ast_channel_sendurl(peer, url);
02258 }
02259 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
02260 if (qe->parent->eventwhencalled)
02261 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
02262 "Queue: %s\r\n"
02263 "Uniqueid: %s\r\n"
02264 "Channel: %s\r\n"
02265 "Member: %s\r\n"
02266 "Holdtime: %ld\r\n",
02267 queuename, qe->chan->uniqueid, peer->name, member->interface,
02268 (long)time(NULL) - qe->start);
02269 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
02270 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
02271 time(&callstart);
02272
02273 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
02274
02275 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
02276 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
02277 } else if (qe->chan->_softhangup) {
02278 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
02279 (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02280 if (qe->parent->eventwhencalled)
02281 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02282 "Queue: %s\r\n"
02283 "Uniqueid: %s\r\n"
02284 "Channel: %s\r\n"
02285 "Member: %s\r\n"
02286 "HoldTime: %ld\r\n"
02287 "TalkTime: %ld\r\n"
02288 "Reason: caller\r\n",
02289 queuename, qe->chan->uniqueid, peer->name, member->interface,
02290 (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02291 } else {
02292 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
02293 if (qe->parent->eventwhencalled)
02294 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
02295 "Queue: %s\r\n"
02296 "Uniqueid: %s\r\n"
02297 "Channel: %s\r\n"
02298 "HoldTime: %ld\r\n"
02299 "TalkTime: %ld\r\n"
02300 "Reason: agent\r\n",
02301 queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start),
02302 (long)(time(NULL) - callstart));
02303 }
02304
02305 if(bridge != AST_PBX_NO_HANGUP_PEER)
02306 ast_hangup(peer);
02307 update_queue(qe->parent, member);
02308 if (bridge == 0)
02309 res = 1;
02310 else
02311 res = bridge;
02312 }
02313 out:
02314 hangupcalls(outgoing, NULL);
02315 return res;
02316 }
02317
02318 static int wait_a_bit(struct queue_ent *qe)
02319 {
02320
02321 int retrywait = qe->parent->retry * 1000;
02322
02323 return ast_waitfordigit(qe->chan, retrywait);
02324 }
02325
02326 static struct member * interface_exists(struct ast_call_queue *q, char *interface)
02327 {
02328 struct member *mem;
02329
02330 if (q)
02331 for (mem = q->members; mem; mem = mem->next)
02332 if (!strcasecmp(interface, mem->interface))
02333 return mem;
02334
02335 return NULL;
02336 }
02337
02338
02339
02340
02341
02342
02343
02344 static void dump_queue_members(struct ast_call_queue *pm_queue)
02345 {
02346 struct member *cur_member;
02347 char value[PM_MAX_LEN];
02348 int value_len = 0;
02349 int res;
02350
02351 memset(value, 0, sizeof(value));
02352
02353 if (!pm_queue)
02354 return;
02355
02356 for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
02357 if (!cur_member->dynamic)
02358 continue;
02359
02360 res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s",
02361 cur_member->interface, cur_member->penalty, cur_member->paused,
02362 cur_member->next ? "|" : "");
02363 if (res != strlen(value + value_len)) {
02364 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
02365 break;
02366 }
02367 value_len += res;
02368 }
02369
02370 if (value_len && !cur_member) {
02371 if (ast_db_put(pm_family, pm_queue->name, value))
02372 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
02373 } else
02374
02375 ast_db_del(pm_family, pm_queue->name);
02376 }
02377
02378 static int remove_from_queue(char *queuename, char *interface)
02379 {
02380 struct ast_call_queue *q;
02381 struct member *last_member, *look;
02382 int res = RES_NOSUCHQUEUE;
02383
02384 ast_mutex_lock(&qlock);
02385 for (q = queues ; q ; q = q->next) {
02386 ast_mutex_lock(&q->lock);
02387 if (!strcmp(q->name, queuename)) {
02388 if ((last_member = interface_exists(q, interface))) {
02389 if ((look = q->members) == last_member) {
02390 q->members = last_member->next;
02391 } else {
02392 while (look != NULL) {
02393 if (look->next == last_member) {
02394 look->next = last_member->next;
02395 break;
02396 } else {
02397 look = look->next;
02398 }
02399 }
02400 }
02401 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
02402 "Queue: %s\r\n"
02403 "Location: %s\r\n",
02404 q->name, last_member->interface);
02405 free(last_member);
02406
02407 if (queue_persistent_members)
02408 dump_queue_members(q);
02409
02410 res = RES_OKAY;
02411 } else {
02412 res = RES_EXISTS;
02413 }
02414 ast_mutex_unlock(&q->lock);
02415 break;
02416 }
02417 ast_mutex_unlock(&q->lock);
02418 }
02419 ast_mutex_unlock(&qlock);
02420 return res;
02421 }
02422
02423 static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump)
02424 {
02425 struct ast_call_queue *q;
02426 struct member *new_member;
02427 int res = RES_NOSUCHQUEUE;
02428
02429
02430
02431 q = load_realtime_queue(queuename);
02432
02433 ast_mutex_lock(&qlock);
02434
02435 if (q) {
02436 ast_mutex_lock(&q->lock);
02437 if (interface_exists(q, interface) == NULL) {
02438 new_member = create_queue_member(interface, penalty, paused);
02439
02440 if (new_member != NULL) {
02441 new_member->dynamic = 1;
02442 new_member->next = q->members;
02443 q->members = new_member;
02444 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
02445 "Queue: %s\r\n"
02446 "Location: %s\r\n"
02447 "Membership: %s\r\n"
02448 "Penalty: %d\r\n"
02449 "CallsTaken: %d\r\n"
02450 "LastCall: %d\r\n"
02451 "Status: %d\r\n"
02452 "Paused: %d\r\n",
02453 q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
02454 new_member->penalty, new_member->calls, (int)new_member->lastcall, new_member->status, new_member->paused);
02455
02456 if (dump)
02457 dump_queue_members(q);
02458
02459 res = RES_OKAY;
02460 } else {
02461 res = RES_OUTOFMEMORY;
02462 }
02463 } else {
02464 res = RES_EXISTS;
02465 }
02466 ast_mutex_unlock(&q->lock);
02467 }
02468 ast_mutex_unlock(&qlock);
02469 return res;
02470 }
02471
02472 static int set_member_paused(char *queuename, char *interface, int paused)
02473 {
02474 int found = 0;
02475 struct ast_call_queue *q;
02476 struct member *mem;
02477
02478
02479
02480 if (ast_strlen_zero(queuename))
02481 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
02482
02483 ast_mutex_lock(&qlock);
02484 for (q = queues ; q ; q = q->next) {
02485 ast_mutex_lock(&q->lock);
02486 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
02487 if ((mem = interface_exists(q, interface))) {
02488 found++;
02489 if (mem->paused == paused)
02490 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
02491 mem->paused = paused;
02492
02493 if (queue_persistent_members)
02494 dump_queue_members(q);
02495
02496 ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
02497
02498 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
02499 "Queue: %s\r\n"
02500 "Location: %s\r\n"
02501 "Paused: %d\r\n",
02502 q->name, mem->interface, paused);
02503 }
02504 }
02505 ast_mutex_unlock(&q->lock);
02506 }
02507 ast_mutex_unlock(&qlock);
02508
02509 if (found)
02510 return RESULT_SUCCESS;
02511 else
02512 return RESULT_FAILURE;
02513 }
02514
02515
02516 static void reload_queue_members(void)
02517 {
02518 char *cur_ptr;
02519 char *queue_name;
02520 char *member;
02521 char *interface;
02522 char *penalty_tok;
02523 int penalty = 0;
02524 char *paused_tok;
02525 int paused = 0;
02526 struct ast_db_entry *db_tree;
02527 struct ast_db_entry *entry;
02528 struct ast_call_queue *cur_queue;
02529 char queue_data[PM_MAX_LEN];
02530
02531 ast_mutex_lock(&qlock);
02532
02533
02534 db_tree = ast_db_gettree(pm_family, NULL);
02535 for (entry = db_tree; entry; entry = entry->next) {
02536
02537 queue_name = entry->key + strlen(pm_family) + 2;
02538
02539 cur_queue = queues;
02540 while (cur_queue) {
02541 ast_mutex_lock(&cur_queue->lock);
02542 if (!strcmp(queue_name, cur_queue->name))
02543 break;
02544 ast_mutex_unlock(&cur_queue->lock);
02545 cur_queue = cur_queue->next;
02546 }
02547
02548 if (!cur_queue) {
02549
02550
02551 ast_db_del(pm_family, queue_name);
02552 continue;
02553 } else
02554 ast_mutex_unlock(&cur_queue->lock);
02555
02556 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
02557 continue;
02558
02559 cur_ptr = queue_data;
02560 while ((member = strsep(&cur_ptr, "|"))) {
02561 if (ast_strlen_zero(member))
02562 continue;
02563
02564 interface = strsep(&member, ";");
02565 penalty_tok = strsep(&member, ";");
02566 paused_tok = strsep(&member, ";");
02567
02568 if (!penalty_tok) {
02569 ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name);
02570 break;
02571 }
02572 penalty = strtol(penalty_tok, NULL, 10);
02573 if (errno == ERANGE) {
02574 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
02575 break;
02576 }
02577
02578 if (!paused_tok) {
02579 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
02580 break;
02581 }
02582 paused = strtol(paused_tok, NULL, 10);
02583 if ((errno == ERANGE) || paused < 0 || paused > 1) {
02584 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
02585 break;
02586 }
02587
02588 if (option_debug)
02589 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused);
02590
02591 if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) {
02592 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
02593 break;
02594 }
02595 }
02596 }
02597
02598 ast_mutex_unlock(&qlock);
02599 if (db_tree) {
02600 ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
02601 ast_db_freetree(db_tree);
02602 }
02603 }
02604
02605 static int pqm_exec(struct ast_channel *chan, void *data)
02606 {
02607 struct localuser *u;
02608 char *parse;
02609 int priority_jump = 0;
02610 AST_DECLARE_APP_ARGS(args,
02611 AST_APP_ARG(queuename);
02612 AST_APP_ARG(interface);
02613 AST_APP_ARG(options);
02614 );
02615
02616 if (ast_strlen_zero(data)) {
02617 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02618 return -1;
02619 }
02620
02621 LOCAL_USER_ADD(u);
02622
02623 if (!(parse = ast_strdupa(data))) {
02624 ast_log(LOG_WARNING, "Memory Error!\n");
02625 LOCAL_USER_REMOVE(u);
02626 return -1;
02627 }
02628
02629 AST_STANDARD_APP_ARGS(args, parse);
02630
02631 if (args.options) {
02632 if (strchr(args.options, 'j'))
02633 priority_jump = 1;
02634 }
02635
02636 if (ast_strlen_zero(args.interface)) {
02637 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02638 LOCAL_USER_REMOVE(u);
02639 return -1;
02640 }
02641
02642 if (set_member_paused(args.queuename, args.interface, 1)) {
02643 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
02644 if (priority_jump || option_priority_jumping) {
02645 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02646 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02647 LOCAL_USER_REMOVE(u);
02648 return 0;
02649 }
02650 }
02651 LOCAL_USER_REMOVE(u);
02652 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
02653 return -1;
02654 }
02655
02656 LOCAL_USER_REMOVE(u);
02657 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
02658 return 0;
02659 }
02660
02661 static int upqm_exec(struct ast_channel *chan, void *data)
02662 {
02663 struct localuser *u;
02664 char *parse;
02665 int priority_jump = 0;
02666 AST_DECLARE_APP_ARGS(args,
02667 AST_APP_ARG(queuename);
02668 AST_APP_ARG(interface);
02669 AST_APP_ARG(options);
02670 );
02671
02672 if (ast_strlen_zero(data)) {
02673 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
02674 return -1;
02675 }
02676
02677 LOCAL_USER_ADD(u);
02678
02679 if (!(parse = ast_strdupa(data))) {
02680 ast_log(LOG_WARNING, "Memory Error!\n");
02681 LOCAL_USER_REMOVE(u);
02682 return -1;
02683 }
02684
02685 AST_STANDARD_APP_ARGS(args, parse);
02686
02687 if (args.options) {
02688 if (strchr(args.options, 'j'))
02689 priority_jump = 1;
02690 }
02691
02692 if (ast_strlen_zero(args.interface)) {
02693 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
02694 LOCAL_USER_REMOVE(u);
02695 return -1;
02696 }
02697
02698 if (set_member_paused(args.queuename, args.interface, 0)) {
02699 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
02700 if (priority_jump || option_priority_jumping) {
02701 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
02702 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02703 LOCAL_USER_REMOVE(u);
02704 return 0;
02705 }
02706 }
02707 LOCAL_USER_REMOVE(u);
02708 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
02709 return -1;
02710 }
02711
02712 LOCAL_USER_REMOVE(u);
02713 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
02714 return 0;
02715 }
02716
02717 static int rqm_exec(struct ast_channel *chan, void *data)
02718 {
02719 int res=-1;
02720 struct localuser *u;
02721 char *parse, *temppos = NULL;
02722 int priority_jump = 0;
02723 AST_DECLARE_APP_ARGS(args,
02724 AST_APP_ARG(queuename);
02725 AST_APP_ARG(interface);
02726 AST_APP_ARG(options);
02727 );
02728
02729
02730 if (ast_strlen_zero(data)) {
02731 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
02732 return -1;
02733 }
02734
02735 LOCAL_USER_ADD(u);
02736
02737 if (!(parse = ast_strdupa(data))) {
02738 ast_log(LOG_WARNING, "Memory Error!\n");
02739 LOCAL_USER_REMOVE(u);
02740 return -1;
02741 }
02742
02743 AST_STANDARD_APP_ARGS(args, parse);
02744
02745 if (ast_strlen_zero(args.interface)) {
02746 args.interface = ast_strdupa(chan->name);
02747 temppos = strrchr(args.interface, '-');
02748 if (temppos)
02749 *temppos = '\0';
02750 }
02751
02752 if (args.options) {
02753 if (strchr(args.options, 'j'))
02754 priority_jump = 1;
02755 }
02756
02757 switch (remove_from_queue(args.queuename, args.interface)) {
02758 case RES_OKAY:
02759 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
02760 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
02761 res = 0;
02762 break;
02763 case RES_EXISTS:
02764 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
02765 if (priority_jump || option_priority_jumping)
02766 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02767 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
02768 res = 0;
02769 break;
02770 case RES_NOSUCHQUEUE:
02771 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
02772 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
02773 res = 0;
02774 break;
02775 case RES_OUTOFMEMORY:
02776 ast_log(LOG_ERROR, "Out of memory\n");
02777 break;
02778 }
02779
02780 LOCAL_USER_REMOVE(u);
02781 return res;
02782 }
02783
02784 static int aqm_exec(struct ast_channel *chan, void *data)
02785 {
02786 int res=-1;
02787 struct localuser *u;
02788 char *parse, *temppos = NULL;
02789 int priority_jump = 0;
02790 AST_DECLARE_APP_ARGS(args,
02791 AST_APP_ARG(queuename);
02792 AST_APP_ARG(interface);
02793 AST_APP_ARG(penalty);
02794 AST_APP_ARG(options);
02795 );
02796 int penalty = 0;
02797
02798 if (ast_strlen_zero(data)) {
02799 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options]])\n");
02800 return -1;
02801 }
02802
02803 LOCAL_USER_ADD(u);
02804
02805 if (!(parse = ast_strdupa(data))) {
02806 ast_log(LOG_WARNING, "Memory Error!\n");
02807 LOCAL_USER_REMOVE(u);
02808 return -1;
02809 }
02810
02811 AST_STANDARD_APP_ARGS(args, parse);
02812
02813 if (ast_strlen_zero(args.interface)) {
02814 args.interface = ast_strdupa(chan->name);
02815 temppos = strrchr(args.interface, '-');
02816 if (temppos)
02817 *temppos = '\0';
02818 }
02819
02820 if (!ast_strlen_zero(args.penalty)) {
02821 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
02822 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
02823 penalty = 0;
02824 }
02825 }
02826
02827 if (args.options) {
02828 if (strchr(args.options, 'j'))
02829 priority_jump = 1;
02830 }
02831
02832
02833 switch (add_to_queue(args.queuename, args.interface, penalty, 0, queue_persistent_members)) {
02834 case RES_OKAY:
02835 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
02836 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
02837 res = 0;
02838 break;
02839 case RES_EXISTS:
02840 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
02841 if (priority_jump || option_priority_jumping)
02842 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
02843 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
02844 res = 0;
02845 break;
02846 case RES_NOSUCHQUEUE:
02847 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
02848 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
02849 res = 0;
02850 break;
02851 case RES_OUTOFMEMORY:
02852 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
02853 break;
02854 }
02855
02856 LOCAL_USER_REMOVE(u);
02857 return res;
02858 }
02859
02860 static int queue_exec(struct ast_channel *chan, void *data)
02861 {
02862 int res=-1;
02863 int ringing=0;
02864 struct localuser *u;
02865 char *queuename;
02866 char info[512];
02867 char *info_ptr = info;
02868 char *options = NULL;
02869 char *url = NULL;
02870 char *announceoverride = NULL;
02871 char *user_priority;
02872 int prio;
02873 char *queuetimeoutstr = NULL;
02874 enum queue_result reason = QUEUE_UNKNOWN;
02875
02876
02877 int go_on = 0;
02878
02879
02880 struct queue_ent qe;
02881
02882 if (ast_strlen_zero(data)) {
02883 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n");
02884 return -1;
02885 }
02886
02887 LOCAL_USER_ADD(u);
02888
02889
02890 memset(&qe, 0, sizeof(qe));
02891 qe.start = time(NULL);
02892
02893
02894 ast_copy_string(info, (char *) data, sizeof(info));
02895 queuename = strsep(&info_ptr, "|");
02896 options = strsep(&info_ptr, "|");
02897 url = strsep(&info_ptr, "|");
02898 announceoverride = strsep(&info_ptr, "|");
02899 queuetimeoutstr = info_ptr;
02900
02901
02902 if (queuetimeoutstr)
02903 qe.expire = qe.start + atoi(queuetimeoutstr);
02904 else
02905 qe.expire = 0;
02906
02907
02908 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
02909 if (user_priority) {
02910 if (sscanf(user_priority, "%d", &prio) == 1) {
02911 if (option_debug)
02912 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
02913 chan->name, prio);
02914 } else {
02915 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
02916 user_priority, chan->name);
02917 prio = 0;
02918 }
02919 } else {
02920 if (option_debug > 2)
02921 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
02922 prio = 0;
02923 }
02924
02925 if (options && (strchr(options, 'r')))
02926 ringing = 1;
02927
02928 if (option_debug)
02929 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
02930 queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
02931
02932 qe.chan = chan;
02933 qe.prio = (int)prio;
02934 qe.last_pos_said = 0;
02935 qe.last_pos = 0;
02936 qe.last_periodic_announce_time = time(NULL);
02937 if (!join_queue(queuename, &qe, &reason)) {
02938 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "",
02939 chan->cid.cid_num ? chan->cid.cid_num : "");
02940 check_turns:
02941 if (ringing) {
02942 ast_indicate(chan, AST_CONTROL_RINGING);
02943 } else {
02944 ast_moh_start(chan, qe.moh);
02945 }
02946 for (;;) {
02947
02948
02949 res = wait_our_turn(&qe, ringing, &reason);
02950
02951 if (res < 0) {
02952
02953 record_abandoned(&qe);
02954 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
02955 if (option_verbose > 2) {
02956 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename);
02957 res = -1;
02958 }
02959 break;
02960 }
02961 if (!res)
02962 break;
02963 if (valid_exit(&qe, res)) {
02964 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
02965 break;
02966 }
02967 }
02968 if (!res) {
02969 int makeannouncement = 0;
02970 for (;;) {
02971
02972
02973
02974
02975
02976 enum queue_member_status stat;
02977
02978
02979 if (qe.expire && (time(NULL) > qe.expire)) {
02980 record_abandoned(&qe);
02981 reason = QUEUE_TIMEOUT;
02982 res = 0;
02983 ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
02984 break;
02985 }
02986
02987 if (makeannouncement) {
02988
02989 if (qe.parent->announcefrequency && !ringing)
02990 res = say_position(&qe);
02991 if (res && valid_exit(&qe, res)) {
02992 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
02993 break;
02994 }
02995
02996 }
02997 makeannouncement = 1;
02998
02999
03000 if (qe.parent->periodicannouncefrequency && !ringing)
03001 res = say_periodic_announcement(&qe);
03002
03003 if (res && valid_exit(&qe, res)) {
03004 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
03005 break;
03006 }
03007
03008
03009 res = try_calling(&qe, options, announceoverride, url, &go_on);
03010 if (res) {
03011 if (res < 0) {
03012 if (!qe.handled) {
03013 record_abandoned(&qe);
03014 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03015 }
03016 } else if (res > 0)
03017 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03018 break;
03019 }
03020
03021 stat = get_member_status(qe.parent);
03022
03023
03024 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03025 record_abandoned(&qe);
03026 reason = QUEUE_LEAVEEMPTY;
03027 res = 0;
03028 break;
03029 }
03030
03031
03032 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03033 record_abandoned(&qe);
03034 reason = QUEUE_LEAVEUNAVAIL;
03035 res = 0;
03036 break;
03037 }
03038
03039
03040 if (qe.expire && (time(NULL) > qe.expire)) {
03041 record_abandoned(&qe);
03042 reason = QUEUE_TIMEOUT;
03043 res = 0;
03044 ast_queue_log(queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03045 break;
03046 }
03047
03048
03049 res = wait_a_bit(&qe);
03050 if (res < 0) {
03051 record_abandoned(&qe);
03052 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
03053 if (option_verbose > 2) {
03054 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename);
03055 res = -1;
03056 }
03057 break;
03058 }
03059 if (res && valid_exit(&qe, res)) {
03060 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
03061 break;
03062 }
03063
03064 if (go_on) {
03065 if (option_verbose > 2) {
03066 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03067 res = -1;
03068 }
03069 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03070 record_abandoned(&qe);
03071 reason = QUEUE_TIMEOUT;
03072 res = 0;
03073 break;
03074 }
03075
03076
03077
03078
03079 if (!is_our_turn(&qe)) {
03080 if (option_debug)
03081 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03082 qe.chan->name);
03083 goto check_turns;
03084 }
03085 }
03086 }
03087
03088 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03089 res = 0;
03090 if (ringing) {
03091 ast_indicate(chan, -1);
03092 } else {
03093 ast_moh_stop(chan);
03094 }
03095 ast_stopstream(chan);
03096 }
03097 leave_queue(&qe);
03098 if (reason != QUEUE_UNKNOWN)
03099 set_queue_result(chan, reason);
03100 } else {
03101 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
03102 set_queue_result(chan, reason);
03103 res = 0;
03104 }
03105 LOCAL_USER_REMOVE(u);
03106 return res;
03107 }
03108
03109 static char *queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
03110 {
03111 int count = 0;
03112 struct ast_call_queue *q;
03113 struct localuser *u;
03114 struct member *m;
03115
03116 LOCAL_USER_ACF_ADD(u);
03117
03118 ast_copy_string(buf, "0", len);
03119
03120 if (ast_strlen_zero(data)) {
03121 ast_log(LOG_ERROR, "QUEUEAGENTCOUNT requires an argument: queuename\n");
03122 LOCAL_USER_REMOVE(u);
03123 return buf;
03124 }
03125
03126 ast_mutex_lock(&qlock);
03127
03128
03129 for (q = queues; q; q = q->next) {
03130 if (!strcasecmp(q->name, data)) {
03131 ast_mutex_lock(&q->lock);
03132 break;
03133 }
03134 }
03135
03136 ast_mutex_unlock(&qlock);
03137
03138 if (q) {
03139 for (m = q->members; m; m = m->next) {
03140
03141 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
03142 count++;
03143 }
03144 }
03145 ast_mutex_unlock(&q->lock);
03146 }
03147
03148 snprintf(buf, len, "%d", count);
03149 LOCAL_USER_REMOVE(u);
03150 return buf;
03151 }
03152
03153 static struct ast_custom_function queueagentcount_function = {
03154 .name = "QUEUEAGENTCOUNT",
03155 .synopsis = "Count number of agents answering a queue",
03156 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
03157 .read = queue_function_qac,
03158 };
03159
03160 static void reload_queues(void)
03161 {
03162 struct ast_call_queue *q, *ql, *qn;
03163 struct ast_config *cfg;
03164 char *cat, *tmp;
03165 struct ast_variable *var;
03166 struct member *prev, *cur;
03167 int new;
03168 char *general_val = NULL;
03169 char interface[80];
03170 int penalty;
03171
03172 cfg = ast_config_load("queues.conf");
03173 if (!cfg) {
03174 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
03175 return;
03176 }
03177 memset(interface, 0, sizeof(interface));
03178 ast_mutex_lock(&qlock);
03179 use_weight=0;
03180
03181 q = queues;
03182 while(q) {
03183 q->dead = 1;
03184 q = q->next;
03185 }
03186
03187 cat = ast_category_browse(cfg, NULL);
03188 while(cat) {
03189 if (!strcasecmp(cat, "general")) {
03190
03191 queue_persistent_members = 0;
03192 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
03193 queue_persistent_members = ast_true(general_val);
03194 } else {
03195
03196 q = queues;
03197 while(q) {
03198 if (!strcmp(q->name, cat))
03199 break;
03200 q = q->next;
03201 }
03202 if (!q) {
03203
03204 q = alloc_queue(cat);
03205 new = 1;
03206 } else
03207 new = 0;
03208 if (q) {
03209 if (!new)
03210 ast_mutex_lock(&q->lock);
03211
03212 init_queue(q);
03213 clear_queue(q);
03214 free_members(q, 0);
03215 prev = q->members;
03216 if (prev) {
03217
03218 while(prev->next)
03219 prev = prev->next;
03220 }
03221 var = ast_variable_browse(cfg, cat);
03222 while(var) {
03223 if (!strcasecmp(var->name, "member")) {
03224
03225 ast_copy_string(interface, var->value, sizeof(interface));
03226 if ((tmp = strchr(interface, ','))) {
03227 *tmp = '\0';
03228 tmp++;
03229 penalty = atoi(tmp);
03230 if (penalty < 0) {
03231 penalty = 0;
03232 }
03233 } else
03234 penalty = 0;
03235 cur = create_queue_member(interface, penalty, 0);
03236 if (cur) {
03237 if (prev)
03238 prev->next = cur;
03239 else
03240 q->members = cur;
03241 prev = cur;
03242 }
03243 } else {
03244 queue_set_param(q, var->name, var->value, var->lineno, 1);
03245 }
03246 var = var->next;
03247 }
03248 if (!new)
03249 ast_mutex_unlock(&q->lock);
03250 if (new) {
03251 q->next = queues;
03252 queues = q;
03253 }
03254 }
03255 }
03256 cat = ast_category_browse(cfg, cat);
03257 }
03258 ast_config_destroy(cfg);
03259 q = queues;
03260 ql = NULL;
03261 while(q) {
03262 qn = q->next;
03263 if (q->dead) {
03264 if (ql)
03265 ql->next = q->next;
03266 else
03267 queues = q->next;
03268 if (!q->count) {
03269 destroy_queue(q);
03270 } else
03271 ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
03272 } else {
03273 for (cur = q->members; cur; cur = cur->next)
03274 cur->status = ast_device_state(cur->interface);
03275 ql = q;
03276 }
03277 q = qn;
03278 }
03279 ast_mutex_unlock(&qlock);
03280 }
03281
03282 static int __queues_show(int manager, int fd, int argc, char **argv, int queue_show)
03283 {
03284 struct ast_call_queue *q;
03285 struct queue_ent *qe;
03286 struct member *mem;
03287 int pos;
03288 time_t now;
03289 char max_buf[80];
03290 char *max;
03291 size_t max_left;
03292 float sl = 0;
03293 char *term = manager ? "\r\n" : "\n";
03294
03295 time(&now);
03296 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
03297 return RESULT_SHOWUSAGE;
03298
03299
03300 if (queue_show)
03301 load_realtime_queue(argv[2]);
03302
03303 ast_mutex_lock(&qlock);
03304
03305 q = queues;
03306 if (!q) {
03307 ast_mutex_unlock(&qlock);
03308 if (queue_show)
03309 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03310 else
03311 ast_cli(fd, "No queues.%s", term);
03312 return RESULT_SUCCESS;
03313 }
03314 while (q) {
03315 ast_mutex_lock(&q->lock);
03316 if (queue_show) {
03317 if (strcasecmp(q->name, argv[2]) != 0) {
03318 ast_mutex_unlock(&q->lock);
03319 q = q->next;
03320 if (!q) {
03321 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
03322 break;
03323 }
03324 continue;
03325 }
03326 }
03327 max_buf[0] = '\0';
03328 max = max_buf;
03329 max_left = sizeof(max_buf);
03330 if (q->maxlen)
03331 ast_build_string(&max, &max_left, "%d", q->maxlen);
03332 else
03333 ast_build_string(&max, &max_left, "unlimited");
03334 sl = 0;
03335 if(q->callscompleted > 0)
03336 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03337 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
03338 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
03339 if (q->members) {
03340 ast_cli(fd, " Members: %s", term);
03341 for (mem = q->members; mem; mem = mem->next) {
03342 max_buf[0] = '\0';
03343 max = max_buf;
03344 max_left = sizeof(max_buf);
03345 if (mem->penalty)
03346 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
03347 if (mem->dynamic)
03348 ast_build_string(&max, &max_left, " (dynamic)");
03349 if (mem->paused)
03350 ast_build_string(&max, &max_left, " (paused)");
03351 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
03352 if (mem->calls) {
03353 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
03354 mem->calls, (long)(time(NULL) - mem->lastcall));
03355 } else
03356 ast_build_string(&max, &max_left, " has taken no calls yet");
03357 ast_cli(fd, " %s%s%s", mem->interface, max_buf, term);
03358 }
03359 } else
03360 ast_cli(fd, " No Members%s", term);
03361 if (q->head) {
03362 pos = 1;
03363 ast_cli(fd, " Callers: %s", term);
03364 for (qe = q->head; qe; qe = qe->next)
03365 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, qe->chan->name,
03366 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio, term);
03367 } else
03368 ast_cli(fd, " No Callers%s", term);
03369 ast_cli(fd, "%s", term);
03370 ast_mutex_unlock(&q->lock);
03371 q = q->next;
03372 if (queue_show)
03373 break;
03374 }
03375 ast_mutex_unlock(&qlock);
03376 return RESULT_SUCCESS;
03377 }
03378
03379 static int queues_show(int fd, int argc, char **argv)
03380 {
03381 return __queues_show(0, fd, argc, argv, 0);
03382 }
03383
03384 static int queue_show(int fd, int argc, char **argv)
03385 {
03386 return __queues_show(0, fd, argc, argv, 1);
03387 }
03388
03389 static char *complete_queue(char *line, char *word, int pos, int state)
03390 {
03391 struct ast_call_queue *q;
03392 int which=0;
03393
03394 ast_mutex_lock(&qlock);
03395 for (q = queues; q; q = q->next) {
03396 if (!strncasecmp(word, q->name, strlen(word))) {
03397 if (++which > state)
03398 break;
03399 }
03400 }
03401 ast_mutex_unlock(&qlock);
03402 return q ? strdup(q->name) : NULL;
03403 }
03404
03405
03406
03407
03408 static int manager_queues_show( struct mansession *s, struct message *m )
03409 {
03410 char *a[] = { "show", "queues" };
03411 __queues_show(1, s->fd, 2, a, 0);
03412 ast_cli(s->fd, "\r\n\r\n");
03413
03414 return RESULT_SUCCESS;
03415 }
03416
03417
03418 static int manager_queues_status( struct mansession *s, struct message *m )
03419 {
03420 time_t now;
03421 int pos;
03422 char *id = astman_get_header(m,"ActionID");
03423 char *queuefilter = astman_get_header(m,"Queue");
03424 char *memberfilter = astman_get_header(m,"Member");
03425 char idText[256] = "";
03426 struct ast_call_queue *q;
03427 struct queue_ent *qe;
03428 float sl = 0;
03429 struct member *mem;
03430
03431 astman_send_ack(s, m, "Queue status will follow");
03432 time(&now);
03433 ast_mutex_lock(&qlock);
03434 if (!ast_strlen_zero(id)) {
03435 snprintf(idText,256,"ActionID: %s\r\n",id);
03436 }
03437 for (q = queues; q; q = q->next) {
03438 ast_mutex_lock(&q->lock);
03439
03440
03441 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
03442 if(q->callscompleted > 0)
03443 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
03444 ast_cli(s->fd, "Event: QueueParams\r\n"
03445 "Queue: %s\r\n"
03446 "Max: %d\r\n"
03447 "Calls: %d\r\n"
03448 "Holdtime: %d\r\n"
03449 "Completed: %d\r\n"
03450 "Abandoned: %d\r\n"
03451 "ServiceLevel: %d\r\n"
03452 "ServicelevelPerf: %2.1f\r\n"
03453 "Weight: %d\r\n"
03454 "%s"
03455 "\r\n",
03456 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
03457 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
03458
03459 for (mem = q->members; mem; mem = mem->next) {
03460 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
03461 ast_cli(s->fd, "Event: QueueMember\r\n"
03462 "Queue: %s\r\n"
03463 "Location: %s\r\n"
03464 "Membership: %s\r\n"
03465 "Penalty: %d\r\n"
03466 "CallsTaken: %d\r\n"
03467 "LastCall: %d\r\n"
03468 "Status: %d\r\n"
03469 "Paused: %d\r\n"
03470 "%s"
03471 "\r\n",
03472 q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
03473 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
03474 }
03475 }
03476
03477 pos = 1;
03478 for (qe = q->head; qe; qe = qe->next) {
03479 ast_cli(s->fd, "Event: QueueEntry\r\n"
03480 "Queue: %s\r\n"
03481 "Position: %d\r\n"
03482 "Channel: %s\r\n"
03483 "CallerID: %s\r\n"
03484 "CallerIDName: %s\r\n"
03485 "Wait: %ld\r\n"
03486 "%s"
03487 "\r\n",
03488 q->name, pos++, qe->chan->name,
03489 qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
03490 qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
03491 (long)(now - qe->start), idText);
03492 }
03493 }
03494 ast_mutex_unlock(&q->lock);
03495 }
03496 ast_mutex_unlock(&qlock);
03497
03498 ast_cli(s->fd,
03499 "Event: QueueStatusComplete\r\n"
03500 "%s"
03501 "\r\n",idText);
03502
03503
03504 return RESULT_SUCCESS;
03505 }
03506
03507 static int manager_add_queue_member(struct mansession *s, struct message *m)
03508 {
03509 char *queuename, *interface, *penalty_s, *paused_s;
03510 int paused, penalty = 0;
03511
03512 queuename = astman_get_header(m, "Queue");
03513 interface = astman_get_header(m, "Interface");
03514 penalty_s = astman_get_header(m, "Penalty");
03515 paused_s = astman_get_header(m, "Paused");
03516
03517 if (ast_strlen_zero(queuename)) {
03518 astman_send_error(s, m, "'Queue' not specified.");
03519 return 0;
03520 }
03521
03522 if (ast_strlen_zero(interface)) {
03523 astman_send_error(s, m, "'Interface' not specified.");
03524 return 0;
03525 }
03526
03527 if (ast_strlen_zero(penalty_s))
03528 penalty = 0;
03529 else if (sscanf(penalty_s, "%d", &penalty) != 1) {
03530 penalty = 0;
03531 }
03532
03533 if (ast_strlen_zero(paused_s))
03534 paused = 0;
03535 else
03536 paused = abs(ast_true(paused_s));
03537
03538 switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) {
03539 case RES_OKAY:
03540 astman_send_ack(s, m, "Added interface to queue");
03541 break;
03542 case RES_EXISTS:
03543 astman_send_error(s, m, "Unable to add interface: Already there");
03544 break;
03545 case RES_NOSUCHQUEUE:
03546 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
03547 break;
03548 case RES_OUTOFMEMORY:
03549 astman_send_error(s, m, "Out of memory");
03550 break;
03551 }
03552 return 0;
03553 }
03554
03555 static int manager_remove_queue_member(struct mansession *s, struct message *m)
03556 {
03557 char *queuename, *interface;
03558
03559 queuename = astman_get_header(m, "Queue");
03560 interface = astman_get_header(m, "Interface");
03561
03562 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
03563 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
03564 return 0;
03565 }
03566
03567 switch (remove_from_queue(queuename, interface)) {
03568 case RES_OKAY:
03569 astman_send_ack(s, m, "Removed interface from queue");
03570 break;
03571 case RES_EXISTS:
03572 astman_send_error(s, m, "Unable to remove interface: Not there");
03573 break;
03574 case RES_NOSUCHQUEUE:
03575 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
03576 break;
03577 case RES_OUTOFMEMORY:
03578 astman_send_error(s, m, "Out of memory");
03579 break;
03580 }
03581 return 0;
03582 }
03583
03584 static int manager_pause_queue_member(struct mansession *s, struct message *m)
03585 {
03586 char *queuename, *interface, *paused_s;
03587 int paused;
03588
03589 interface = astman_get_header(m, "Interface");
03590 paused_s = astman_get_header(m, "Paused");
03591 queuename = astman_get_header(m, "Queue");
03592
03593 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
03594 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
03595 return 0;
03596 }
03597
03598 paused = abs(ast_true(paused_s));
03599
03600 if (set_member_paused(queuename, interface, paused))
03601 astman_send_error(s, m, "Interface not found");
03602 else
03603 if (paused)
03604 astman_send_ack(s, m, "Interface paused successfully");
03605 else
03606 astman_send_ack(s, m, "Interface unpaused successfully");
03607
03608 return 0;
03609 }
03610
03611 static int handle_add_queue_member(int fd, int argc, char *argv[])
03612 {
03613 char *queuename, *interface;
03614 int penalty;
03615
03616 if ((argc != 6) && (argc != 8)) {
03617 return RESULT_SHOWUSAGE;
03618 } else if (strcmp(argv[4], "to")) {
03619 return RESULT_SHOWUSAGE;
03620 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
03621 return RESULT_SHOWUSAGE;
03622 }
03623
03624 queuename = argv[5];
03625 interface = argv[3];
03626 if (argc == 8) {
03627 if (sscanf(argv[7], "%d", &penalty) == 1) {
03628 if (penalty < 0) {
03629 ast_cli(fd, "Penalty must be >= 0\n");
03630 penalty = 0;
03631 }
03632 } else {
03633 ast_cli(fd, "Penalty must be an integer >= 0\n");
03634 penalty = 0;
03635 }
03636 } else {
03637 penalty = 0;
03638 }
03639
03640 switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) {
03641 case RES_OKAY:
03642 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
03643 return RESULT_SUCCESS;
03644 case RES_EXISTS:
03645 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
03646 return RESULT_FAILURE;
03647 case RES_NOSUCHQUEUE:
03648 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
03649 return RESULT_FAILURE;
03650 case RES_OUTOFMEMORY:
03651 ast_cli(fd, "Out of memory\n");
03652 return RESULT_FAILURE;
03653 default:
03654 return RESULT_FAILURE;
03655 }
03656 }
03657
03658 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
03659 {
03660
03661 switch (pos) {
03662 case 3:
03663
03664 return NULL;
03665 case 4:
03666 if (state == 0) {
03667 return strdup("to");
03668 } else {
03669 return NULL;
03670 }
03671 case 5:
03672
03673 return complete_queue(line, word, pos, state);
03674 case 6:
03675 if (state == 0) {
03676 return strdup("penalty");
03677 } else {
03678 return NULL;
03679 }
03680 case 7:
03681 if (state < 100) {
03682 char *num = malloc(3);
03683 if (num) {
03684 sprintf(num, "%d", state);
03685 }
03686 return num;
03687 } else {
03688 return NULL;
03689 }
03690 default:
03691 return NULL;
03692 }
03693 }
03694
03695 static int handle_remove_queue_member(int fd, int argc, char *argv[])
03696 {
03697 char *queuename, *interface;
03698
03699 if (argc != 6) {
03700 return RESULT_SHOWUSAGE;
03701 } else if (strcmp(argv[4], "from")) {
03702 return RESULT_SHOWUSAGE;
03703 }
03704
03705 queuename = argv[5];
03706 interface = argv[3];
03707
03708 switch (remove_from_queue(queuename, interface)) {
03709 case RES_OKAY:
03710 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
03711 return RESULT_SUCCESS;
03712 case RES_EXISTS:
03713 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
03714 return RESULT_FAILURE;
03715 case RES_NOSUCHQUEUE:
03716 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
03717 return RESULT_FAILURE;
03718 case RES_OUTOFMEMORY:
03719 ast_cli(fd, "Out of memory\n");
03720 return RESULT_FAILURE;
03721 default:
03722 return RESULT_FAILURE;
03723 }
03724 }
03725
03726 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
03727 {
03728 int which = 0;
03729 struct ast_call_queue *q;
03730 struct member *m;
03731
03732
03733 if ((pos > 5) || (pos < 3)) {
03734 return NULL;
03735 }
03736 if (pos == 4) {
03737 if (state == 0) {
03738 return strdup("from");
03739 } else {
03740 return NULL;
03741 }
03742 }
03743
03744 if (pos == 5) {
03745
03746 return complete_queue(line, word, pos, state);
03747 }
03748
03749 if (queues != NULL) {
03750 for (q = queues ; q ; q = q->next) {
03751 ast_mutex_lock(&q->lock);
03752 for (m = q->members ; m ; m = m->next) {
03753 if (++which > state) {
03754 ast_mutex_unlock(&q->lock);
03755 return strdup(m->interface);
03756 }
03757 }
03758 ast_mutex_unlock(&q->lock);
03759 }
03760 }
03761 return NULL;
03762 }
03763
03764 static char show_queues_usage[] =
03765 "Usage: show queues\n"
03766 " Provides summary information on call queues.\n";
03767
03768 static struct ast_cli_entry cli_show_queues = {
03769 { "show", "queues", NULL }, queues_show,
03770 "Show status of queues", show_queues_usage, NULL };
03771
03772 static char show_queue_usage[] =
03773 "Usage: show queue\n"
03774 " Provides summary information on a specified queue.\n";
03775
03776 static struct ast_cli_entry cli_show_queue = {
03777 { "show", "queue", NULL }, queue_show,
03778 "Show status of a specified queue", show_queue_usage, complete_queue };
03779
03780 static char aqm_cmd_usage[] =
03781 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
03782
03783 static struct ast_cli_entry cli_add_queue_member = {
03784 { "add", "queue", "member", NULL }, handle_add_queue_member,
03785 "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };
03786
03787 static char rqm_cmd_usage[] =
03788 "Usage: remove queue member <channel> from <queue>\n";
03789
03790 static struct ast_cli_entry cli_remove_queue_member = {
03791 { "remove", "queue", "member", NULL }, handle_remove_queue_member,
03792 "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member };
03793
03794 int unload_module(void)
03795 {
03796 int res;
03797
03798 res = ast_cli_unregister(&cli_show_queue);
03799 res |= ast_cli_unregister(&cli_show_queues);
03800 res |= ast_cli_unregister(&cli_add_queue_member);
03801 res |= ast_cli_unregister(&cli_remove_queue_member);
03802 res |= ast_manager_unregister("Queues");
03803 res |= ast_manager_unregister("QueueStatus");
03804 res |= ast_manager_unregister("QueueAdd");
03805 res |= ast_manager_unregister("QueueRemove");
03806 res |= ast_manager_unregister("QueuePause");
03807 ast_devstate_del(statechange_queue, NULL);
03808 res |= ast_unregister_application(app_aqm);
03809 res |= ast_unregister_application(app_rqm);
03810 res |= ast_unregister_application(app_pqm);
03811 res |= ast_unregister_application(app_upqm);
03812 res |= ast_custom_function_unregister(&queueagentcount_function);
03813 res |= ast_unregister_application(app);
03814
03815 STANDARD_HANGUP_LOCALUSERS;
03816
03817 return res;
03818 }
03819
03820 int load_module(void)
03821 {
03822 int res;
03823
03824 res = ast_register_application(app, queue_exec, synopsis, descrip);
03825 res |= ast_cli_register(&cli_show_queue);
03826 res |= ast_cli_register(&cli_show_queues);
03827 res |= ast_cli_register(&cli_add_queue_member);
03828 res |= ast_cli_register(&cli_remove_queue_member);
03829 res |= ast_devstate_add(statechange_queue, NULL);
03830 res |= ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
03831 res |= ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
03832 res |= ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." );
03833 res |= ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." );
03834 res |= ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" );
03835 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
03836 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
03837 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ;
03838 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ;
03839 res |= ast_custom_function_register(&queueagentcount_function);
03840
03841 if (!res) {
03842 reload_queues();
03843 if (queue_persistent_members)
03844 reload_queue_members();
03845 }
03846
03847 return res;
03848 }
03849
03850
03851 int reload(void)
03852 {
03853 reload_queues();
03854 return 0;
03855 }
03856
03857 char *description(void)
03858 {
03859 return tdesc;
03860 }
03861
03862 int usecount(void)
03863 {
03864 int res;
03865 STANDARD_USECOUNT(res);
03866 return res;
03867 }
03868
03869 char *key()
03870 {
03871 return ASTERISK_GPL_KEY;
03872 }