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 #include "asterisk.h"
00040
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374197 $")
00042
00043 #include <sys/socket.h>
00044 #include <fcntl.h>
00045 #include <netdb.h>
00046 #include <netinet/in.h>
00047 #include <arpa/inet.h>
00048 #include <sys/signal.h>
00049
00050 #include "asterisk/lock.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/config.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/pbx.h"
00055 #include "asterisk/sched.h"
00056 #include "asterisk/io.h"
00057 #include "asterisk/acl.h"
00058 #include "asterisk/callerid.h"
00059 #include "asterisk/file.h"
00060 #include "asterisk/cli.h"
00061 #include "asterisk/app.h"
00062 #include "asterisk/musiconhold.h"
00063 #include "asterisk/manager.h"
00064 #include "asterisk/features.h"
00065 #include "asterisk/utils.h"
00066 #include "asterisk/causes.h"
00067 #include "asterisk/astdb.h"
00068 #include "asterisk/devicestate.h"
00069 #include "asterisk/monitor.h"
00070 #include "asterisk/stringfields.h"
00071 #include "asterisk/event.h"
00072 #include "asterisk/data.h"
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203 static const char tdesc[] = "Call Agent Proxy Channel";
00204 static const char config[] = "agents.conf";
00205
00206 static const char app[] = "AgentLogin";
00207 static const char app3[] = "AgentMonitorOutgoing";
00208
00209 static char moh[80] = "default";
00210
00211 #define AST_MAX_AGENT 80
00212 #define AST_MAX_BUF 256
00213 #define AST_MAX_FILENAME_LEN 256
00214
00215 static const char pa_family[] = "Agents";
00216 #define PA_MAX_LEN 2048
00217
00218 #define DEFAULT_ACCEPTDTMF '#'
00219 #define DEFAULT_ENDDTMF '*'
00220
00221 static ast_group_t group;
00222 static int autologoff;
00223 static int wrapuptime;
00224 static int ackcall;
00225 static int endcall;
00226 static int autologoffunavail = 0;
00227 static char acceptdtmf = DEFAULT_ACCEPTDTMF;
00228 static char enddtmf = DEFAULT_ENDDTMF;
00229
00230 static int maxlogintries = 3;
00231 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
00232
00233 static int recordagentcalls = 0;
00234 static char recordformat[AST_MAX_BUF] = "";
00235 static char recordformatext[AST_MAX_BUF] = "";
00236 static char urlprefix[AST_MAX_BUF] = "";
00237 static char savecallsin[AST_MAX_BUF] = "";
00238 static int updatecdr = 0;
00239 static char beep[AST_MAX_BUF] = "beep";
00240
00241 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
00242
00243 enum {
00244 AGENT_FLAG_ACKCALL = (1 << 0),
00245 AGENT_FLAG_AUTOLOGOFF = (1 << 1),
00246 AGENT_FLAG_WRAPUPTIME = (1 << 2),
00247 AGENT_FLAG_ACCEPTDTMF = (1 << 3),
00248 AGENT_FLAG_ENDDTMF = (1 << 4),
00249 };
00250
00251
00252 struct agent_pvt {
00253 ast_mutex_t lock;
00254 int dead;
00255 int pending;
00256 int abouttograb;
00257 int autologoff;
00258 int ackcall;
00259 int deferlogoff;
00260 char acceptdtmf;
00261 char enddtmf;
00262 time_t loginstart;
00263 time_t start;
00264 struct timeval lastdisc;
00265 int wrapuptime;
00266 ast_group_t group;
00267 int acknowledged;
00268 char moh[80];
00269 char agent[AST_MAX_AGENT];
00270 char password[AST_MAX_AGENT];
00271 char name[AST_MAX_AGENT];
00272 int app_lock_flag;
00273 ast_cond_t app_complete_cond;
00274 ast_cond_t login_wait_cond;
00275 volatile int app_sleep_cond;
00276 struct ast_channel *owner;
00277 char logincallerid[80];
00278 struct ast_channel *chan;
00279 unsigned int flags;
00280 AST_LIST_ENTRY(agent_pvt) list;
00281 };
00282
00283 #define DATA_EXPORT_AGENT(MEMBER) \
00284 MEMBER(agent_pvt, autologoff, AST_DATA_INTEGER) \
00285 MEMBER(agent_pvt, ackcall, AST_DATA_BOOLEAN) \
00286 MEMBER(agent_pvt, deferlogoff, AST_DATA_BOOLEAN) \
00287 MEMBER(agent_pvt, wrapuptime, AST_DATA_MILLISECONDS) \
00288 MEMBER(agent_pvt, acknowledged, AST_DATA_BOOLEAN) \
00289 MEMBER(agent_pvt, name, AST_DATA_STRING) \
00290 MEMBER(agent_pvt, password, AST_DATA_PASSWORD) \
00291 MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER) \
00292 MEMBER(agent_pvt, logincallerid, AST_DATA_STRING)
00293
00294 AST_DATA_STRUCTURE(agent_pvt, DATA_EXPORT_AGENT);
00295
00296 static AST_LIST_HEAD_STATIC(agents, agent_pvt);
00297
00298 #define CHECK_FORMATS(ast, p) do { \
00299 if (p->chan) {\
00300 if (!(ast_format_cap_identical(ast_channel_nativeformats(ast), ast_channel_nativeformats(p->chan)))) { \
00301 char tmp1[256], tmp2[256]; \
00302 ast_debug(1, "Native formats changing from '%s' to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast_channel_nativeformats(ast)), ast_getformatname_multiple(tmp2, sizeof(tmp2), ast_channel_nativeformats(p->chan))); \
00303 \
00304 ast_format_cap_copy(ast_channel_nativeformats(ast), ast_channel_nativeformats(p->chan)); \
00305 ast_debug(1, "Resetting read to '%s' and write to '%s'\n", ast_getformatname(ast_channel_readformat(ast)), ast_getformatname(ast_channel_writeformat(ast)));\
00306 ast_set_read_format(ast, ast_channel_readformat(ast)); \
00307 ast_set_write_format(ast, ast_channel_writeformat(ast)); \
00308 } \
00309 if ((ast_format_cmp(ast_channel_readformat(p->chan), ast_channel_rawreadformat(ast)) != AST_FORMAT_CMP_EQUAL) && !ast_channel_generator(p->chan)) \
00310 ast_set_read_format(p->chan, ast_channel_rawreadformat(ast)); \
00311 if ((ast_format_cmp(ast_channel_writeformat(p->chan), ast_channel_rawwriteformat(ast)) != AST_FORMAT_CMP_EQUAL) && !ast_channel_generator(p->chan)) \
00312 ast_set_write_format(p->chan, ast_channel_rawwriteformat(ast)); \
00313 } \
00314 } while(0)
00315
00316
00317
00318
00319
00320 #define CLEANUP(ast, p) do { \
00321 int x; \
00322 if (p->chan) { \
00323 for (x = 0; x < AST_MAX_FDS; x++) { \
00324 if (x != AST_TIMING_FD) { \
00325 ast_channel_set_fd(ast, x, ast_channel_fd(p->chan, x)); \
00326 } \
00327 } \
00328 ast_channel_set_fd(ast, AST_AGENT_FD, ast_channel_fd(p->chan, AST_TIMING_FD)); \
00329 } \
00330 } while(0)
00331
00332
00333 static struct ast_channel *agent_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
00334 static int agent_devicestate(const char *data);
00335 static int agent_digit_begin(struct ast_channel *ast, char digit);
00336 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00337 static int agent_call(struct ast_channel *ast, const char *dest, int timeout);
00338 static int agent_hangup(struct ast_channel *ast);
00339 static int agent_answer(struct ast_channel *ast);
00340 static struct ast_frame *agent_read(struct ast_channel *ast);
00341 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00342 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00343 static int agent_sendtext(struct ast_channel *ast, const char *text);
00344 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00345 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00346 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00347 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
00348 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
00349 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
00350 static int agent_logoff(const char *agent, int soft);
00351
00352
00353 static struct ast_channel_tech agent_tech = {
00354 .type = "Agent",
00355 .description = tdesc,
00356 .requester = agent_request,
00357 .devicestate = agent_devicestate,
00358 .send_digit_begin = agent_digit_begin,
00359 .send_digit_end = agent_digit_end,
00360 .call = agent_call,
00361 .hangup = agent_hangup,
00362 .answer = agent_answer,
00363 .read = agent_read,
00364 .write = agent_write,
00365 .write_video = agent_write,
00366 .send_html = agent_sendhtml,
00367 .send_text = agent_sendtext,
00368 .exception = agent_read,
00369 .indicate = agent_indicate,
00370 .fixup = agent_fixup,
00371 .bridged_channel = agent_bridgedchannel,
00372 .get_base_channel = agent_get_base_channel,
00373 .set_base_channel = agent_set_base_channel,
00374 };
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386 static struct ast_channel *agent_lock_owner(struct agent_pvt *pvt)
00387 {
00388 struct ast_channel *owner;
00389
00390 for (;;) {
00391 if (!pvt->owner) {
00392 return NULL;
00393 }
00394
00395
00396 owner = ast_channel_ref(pvt->owner);
00397
00398
00399 ast_mutex_unlock(&pvt->lock);
00400 ast_channel_lock(owner);
00401 ast_mutex_lock(&pvt->lock);
00402
00403
00404 if (owner != pvt->owner) {
00405 ast_channel_unlock(owner);
00406 owner = ast_channel_unref(owner);
00407 } else {
00408 return owner;
00409 }
00410 }
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 static struct agent_pvt *add_agent(const char *agent, int pending)
00422 {
00423 char *parse;
00424 AST_DECLARE_APP_ARGS(args,
00425 AST_APP_ARG(agt);
00426 AST_APP_ARG(password);
00427 AST_APP_ARG(name);
00428 );
00429 char *password = NULL;
00430 char *name = NULL;
00431 char *agt = NULL;
00432 struct agent_pvt *p;
00433
00434 parse = ast_strdupa(agent);
00435
00436
00437 AST_STANDARD_APP_ARGS(args, parse);
00438
00439 if(args.argc == 0) {
00440 ast_log(LOG_WARNING, "A blank agent line!\n");
00441 return NULL;
00442 }
00443
00444 if(ast_strlen_zero(args.agt) ) {
00445 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
00446 return NULL;
00447 } else
00448 agt = args.agt;
00449
00450 if(!ast_strlen_zero(args.password)) {
00451 password = args.password;
00452 while (*password && *password < 33) password++;
00453 }
00454 if(!ast_strlen_zero(args.name)) {
00455 name = args.name;
00456 while (*name && *name < 33) name++;
00457 }
00458
00459
00460 AST_LIST_TRAVERSE(&agents, p, list) {
00461 if (!pending && !strcmp(p->agent, agt))
00462 break;
00463 }
00464 if (!p) {
00465
00466 if (!(p = ast_calloc(1, sizeof(*p))))
00467 return NULL;
00468 ast_copy_string(p->agent, agt, sizeof(p->agent));
00469 ast_mutex_init(&p->lock);
00470 ast_cond_init(&p->app_complete_cond, NULL);
00471 ast_cond_init(&p->login_wait_cond, NULL);
00472 p->app_lock_flag = 0;
00473 p->app_sleep_cond = 1;
00474 p->group = group;
00475 p->pending = pending;
00476 AST_LIST_INSERT_TAIL(&agents, p, list);
00477 }
00478
00479 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
00480 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
00481 ast_copy_string(p->moh, moh, sizeof(p->moh));
00482 if (!ast_test_flag(p, AGENT_FLAG_ACKCALL)) {
00483 p->ackcall = ackcall;
00484 }
00485 if (!ast_test_flag(p, AGENT_FLAG_AUTOLOGOFF)) {
00486 p->autologoff = autologoff;
00487 }
00488 if (!ast_test_flag(p, AGENT_FLAG_ACCEPTDTMF)) {
00489 p->acceptdtmf = acceptdtmf;
00490 }
00491 if (!ast_test_flag(p, AGENT_FLAG_ENDDTMF)) {
00492 p->enddtmf = enddtmf;
00493 }
00494
00495
00496
00497 if (!ast_test_flag(p, AGENT_FLAG_WRAPUPTIME) && p->wrapuptime > wrapuptime) {
00498 struct timeval now = ast_tvnow();
00499
00500
00501
00502 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
00503 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
00504 p->lastdisc.tv_usec = now.tv_usec;
00505 }
00506 }
00507 p->wrapuptime = wrapuptime;
00508
00509 if (pending)
00510 p->dead = 1;
00511 else
00512 p->dead = 0;
00513 return p;
00514 }
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527 static int agent_cleanup(struct agent_pvt *p)
00528 {
00529 struct ast_channel *chan;
00530
00531 ast_mutex_lock(&p->lock);
00532 chan = p->owner;
00533 p->owner = NULL;
00534
00535 p->app_sleep_cond = 1;
00536 p->app_lock_flag = 0;
00537 ast_cond_signal(&p->app_complete_cond);
00538 if (chan) {
00539 ast_channel_tech_pvt_set(chan, NULL);
00540 chan = ast_channel_release(chan);
00541 }
00542 if (p->dead) {
00543 ast_mutex_unlock(&p->lock);
00544 ast_mutex_destroy(&p->lock);
00545 ast_cond_destroy(&p->app_complete_cond);
00546 ast_cond_destroy(&p->login_wait_cond);
00547 ast_free(p);
00548 } else {
00549 ast_mutex_unlock(&p->lock);
00550 }
00551 return 0;
00552 }
00553
00554 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
00555
00556 static int agent_answer(struct ast_channel *ast)
00557 {
00558 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
00559 return -1;
00560 }
00561
00562 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
00563 {
00564 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00565 char filename[AST_MAX_BUF];
00566 int res = -1;
00567 if (!p)
00568 return -1;
00569 if (!ast_channel_monitor(ast)) {
00570 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast_channel_uniqueid(ast));
00571
00572 if ((pointer = strchr(filename, '.')))
00573 *pointer = '-';
00574 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
00575 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
00576 ast_monitor_setjoinfiles(ast, 1);
00577 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
00578 #if 0
00579 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00580 #endif
00581 if (!ast_channel_cdr(ast))
00582 ast_channel_cdr_set(ast, ast_cdr_alloc());
00583 ast_cdr_setuserfield(ast, tmp2);
00584 res = 0;
00585 } else
00586 ast_log(LOG_ERROR, "Recording already started on that call.\n");
00587 return res;
00588 }
00589
00590 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
00591 {
00592 return __agent_start_monitoring(ast, ast_channel_tech_pvt(ast), needlock);
00593 }
00594
00595 static struct ast_frame *agent_read(struct ast_channel *ast)
00596 {
00597 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00598 struct ast_frame *f = NULL;
00599 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } };
00600 int cur_time = time(NULL);
00601 struct ast_channel *owner;
00602
00603 ast_mutex_lock(&p->lock);
00604 owner = agent_lock_owner(p);
00605
00606 CHECK_FORMATS(ast, p);
00607 if (!p->start) {
00608 p->start = cur_time;
00609 }
00610 if (p->chan) {
00611 ast_copy_flags(ast_channel_flags(p->chan), ast_channel_flags(ast), AST_FLAG_EXCEPTION);
00612 ast_channel_fdno_set(p->chan, (ast_channel_fdno(ast) == AST_AGENT_FD) ? AST_TIMING_FD : ast_channel_fdno(ast));
00613 f = ast_read(p->chan);
00614 } else
00615 f = &ast_null_frame;
00616 if (!f) {
00617
00618 if (p->chan) {
00619 ast_channel_internal_bridged_channel_set(p->chan, NULL);
00620 p->chan = NULL;
00621 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
00622 p->acknowledged = 0;
00623 }
00624 } else {
00625
00626
00627 if (!p->ackcall && !p->acknowledged && p->chan && (ast_channel_state(p->chan) == AST_STATE_UP)) {
00628 p->acknowledged = 1;
00629 }
00630
00631 if (!p->acknowledged) {
00632 int howlong = cur_time - p->start;
00633 if (p->autologoff && (howlong >= p->autologoff)) {
00634 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00635 if (owner || p->chan) {
00636 if (owner) {
00637 ast_softhangup(owner, AST_SOFTHANGUP_EXPLICIT);
00638 ast_channel_unlock(owner);
00639 owner = ast_channel_unref(owner);
00640 }
00641
00642 while (p->chan && ast_channel_trylock(p->chan)) {
00643 DEADLOCK_AVOIDANCE(&p->lock);
00644 }
00645 if (p->chan) {
00646 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00647 ast_channel_unlock(p->chan);
00648 }
00649 }
00650 }
00651 }
00652 switch (f->frametype) {
00653 case AST_FRAME_CONTROL:
00654 if (f->subclass.integer == AST_CONTROL_ANSWER) {
00655 if (p->ackcall) {
00656 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", ast_channel_name(p->chan), p->acceptdtmf);
00657
00658 ast_frfree(f);
00659 f = &ast_null_frame;
00660 } else {
00661 p->acknowledged = 1;
00662
00663
00664 ast_frfree(f);
00665 f = &answer_frame;
00666 }
00667 }
00668 break;
00669 case AST_FRAME_DTMF_BEGIN:
00670
00671 if((!p->acknowledged && f->subclass.integer == p->acceptdtmf) || (f->subclass.integer == p->enddtmf && endcall)){
00672 ast_frfree(f);
00673 f = &ast_null_frame;
00674 }
00675 break;
00676 case AST_FRAME_DTMF_END:
00677 if (!p->acknowledged && (f->subclass.integer == p->acceptdtmf)) {
00678 if (p->chan) {
00679 ast_verb(3, "%s acknowledged\n", ast_channel_name(p->chan));
00680 }
00681 p->acknowledged = 1;
00682 ast_frfree(f);
00683 f = &answer_frame;
00684 } else if (f->subclass.integer == p->enddtmf && endcall) {
00685
00686 ast_frfree(f);
00687 f = NULL;
00688 }
00689 break;
00690 case AST_FRAME_VOICE:
00691 case AST_FRAME_VIDEO:
00692
00693 if (!p->acknowledged) {
00694 ast_frfree(f);
00695 f = &ast_null_frame;
00696 }
00697 default:
00698
00699 break;
00700 }
00701 }
00702
00703 if (owner) {
00704 ast_channel_unlock(owner);
00705 owner = ast_channel_unref(owner);
00706 }
00707
00708 CLEANUP(ast,p);
00709 if (p->chan && !ast_channel_internal_bridged_channel(p->chan)) {
00710 if (strcasecmp(ast_channel_tech(p->chan)->type, "Local")) {
00711 ast_channel_internal_bridged_channel_set(p->chan, ast);
00712 if (p->chan)
00713 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", ast_channel_name(p->chan), ast_channel_name(ast_channel_internal_bridged_channel(p->chan)));
00714 }
00715 }
00716 ast_mutex_unlock(&p->lock);
00717 if (recordagentcalls && f == &answer_frame)
00718 agent_start_monitoring(ast,0);
00719 return f;
00720 }
00721
00722 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00723 {
00724 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00725 int res = -1;
00726 ast_mutex_lock(&p->lock);
00727 if (p->chan)
00728 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00729 ast_mutex_unlock(&p->lock);
00730 return res;
00731 }
00732
00733 static int agent_sendtext(struct ast_channel *ast, const char *text)
00734 {
00735 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00736 int res = -1;
00737 ast_mutex_lock(&p->lock);
00738 if (p->chan)
00739 res = ast_sendtext(p->chan, text);
00740 ast_mutex_unlock(&p->lock);
00741 return res;
00742 }
00743
00744 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00745 {
00746 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00747 int res = -1;
00748 CHECK_FORMATS(ast, p);
00749 ast_mutex_lock(&p->lock);
00750 if (!p->chan)
00751 res = 0;
00752 else {
00753 if ((f->frametype != AST_FRAME_VOICE) ||
00754 (f->frametype != AST_FRAME_VIDEO) ||
00755 (ast_format_cmp(&f->subclass.format, ast_channel_writeformat(p->chan)) != AST_FORMAT_CMP_NOT_EQUAL)) {
00756 res = ast_write(p->chan, f);
00757 } else {
00758 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
00759 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00760 ast_channel_name(ast), ast_channel_name(p->chan));
00761 res = 0;
00762 }
00763 }
00764 CLEANUP(ast, p);
00765 ast_mutex_unlock(&p->lock);
00766 return res;
00767 }
00768
00769 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00770 {
00771 struct agent_pvt *p = ast_channel_tech_pvt(newchan);
00772 ast_mutex_lock(&p->lock);
00773 if (p->owner != oldchan) {
00774 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00775 ast_mutex_unlock(&p->lock);
00776 return -1;
00777 }
00778 p->owner = newchan;
00779 ast_mutex_unlock(&p->lock);
00780 return 0;
00781 }
00782
00783 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00784 {
00785 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00786 int res = -1;
00787 ast_mutex_lock(&p->lock);
00788 if (p->chan && !ast_check_hangup(p->chan)) {
00789 while (ast_channel_trylock(p->chan)) {
00790 if ((res = ast_channel_unlock(ast))) {
00791 ast_log(LOG_ERROR, "chan_agent bug! Channel was not locked upon entry to agent_indicate: %s\n", res > 0 ? strerror(res) : "Bad ao2obj data");
00792 ast_mutex_unlock(&p->lock);
00793 return -1;
00794 }
00795 usleep(1);
00796 ast_channel_lock(ast);
00797 }
00798 res = ast_channel_tech(p->chan)->indicate ? ast_channel_tech(p->chan)->indicate(p->chan, condition, data, datalen) : -1;
00799 ast_channel_unlock(p->chan);
00800 } else
00801 res = 0;
00802 ast_mutex_unlock(&p->lock);
00803 return res;
00804 }
00805
00806 static int agent_digit_begin(struct ast_channel *ast, char digit)
00807 {
00808 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00809 ast_mutex_lock(&p->lock);
00810 if (p->chan) {
00811 ast_senddigit_begin(p->chan, digit);
00812 }
00813 ast_mutex_unlock(&p->lock);
00814 return 0;
00815 }
00816
00817 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00818 {
00819 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00820 ast_mutex_lock(&p->lock);
00821 if (p->chan) {
00822 ast_senddigit_end(p->chan, digit, duration);
00823 }
00824 ast_mutex_unlock(&p->lock);
00825 return 0;
00826 }
00827
00828 static int agent_call(struct ast_channel *ast, const char *dest, int timeout)
00829 {
00830 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00831 int res = -1;
00832 int newstate=0;
00833 struct ast_channel *chan;
00834
00835 ast_mutex_lock(&p->lock);
00836 p->acknowledged = 0;
00837
00838 if (p->pending) {
00839 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00840 ast_mutex_unlock(&p->lock);
00841 ast_setstate(ast, AST_STATE_DIALING);
00842 return 0;
00843 }
00844
00845 if (!p->chan) {
00846 ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
00847 ast_mutex_unlock(&p->lock);
00848 return res;
00849 }
00850 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, ast_channel_name(p->chan));
00851 ast_debug(3, "Playing beep, lang '%s'\n", ast_channel_language(p->chan));
00852
00853 chan = p->chan;
00854 ast_mutex_unlock(&p->lock);
00855
00856 res = ast_streamfile(chan, beep, ast_channel_language(chan));
00857 ast_debug(3, "Played beep, result '%d'\n", res);
00858 if (!res) {
00859 res = ast_waitstream(chan, "");
00860 ast_debug(3, "Waited for stream, result '%d'\n", res);
00861 }
00862
00863 ast_mutex_lock(&p->lock);
00864 if (!p->chan) {
00865
00866 res = -1;
00867 }
00868
00869 if (!res) {
00870 struct ast_format tmpfmt;
00871 res = ast_set_read_format_from_cap(p->chan, ast_channel_nativeformats(p->chan));
00872 ast_debug(3, "Set read format, result '%d'\n", res);
00873 if (res)
00874 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(&tmpfmt));
00875 } else {
00876
00877 p->chan = NULL;
00878 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
00879 }
00880
00881 if (!res) {
00882 struct ast_format tmpfmt;
00883 res = ast_set_write_format_from_cap(p->chan, ast_channel_nativeformats(p->chan));
00884 ast_debug(3, "Set write format, result '%d'\n", res);
00885 if (res)
00886 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(&tmpfmt));
00887 }
00888 if(!res) {
00889
00890 if (p->ackcall) {
00891 newstate = AST_STATE_RINGING;
00892 } else {
00893 newstate = AST_STATE_UP;
00894 if (recordagentcalls)
00895 agent_start_monitoring(ast, 0);
00896 p->acknowledged = 1;
00897 }
00898 res = 0;
00899 }
00900 CLEANUP(ast, p);
00901 ast_mutex_unlock(&p->lock);
00902 if (newstate)
00903 ast_setstate(ast, newstate);
00904 return res;
00905 }
00906
00907
00908 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
00909 {
00910 struct agent_pvt *p = NULL;
00911 struct ast_channel *base = chan;
00912
00913
00914 if (!chan || !ast_channel_tech_pvt(chan)) {
00915 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)ast_channel_tech_pvt(chan):(long)NULL);
00916 return NULL;
00917 }
00918 p = ast_channel_tech_pvt(chan);
00919 if (p->chan)
00920 base = p->chan;
00921 return base;
00922 }
00923
00924 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
00925 {
00926 struct agent_pvt *p = NULL;
00927
00928 if (!chan || !base) {
00929 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
00930 return -1;
00931 }
00932 p = ast_channel_tech_pvt(chan);
00933 if (!p) {
00934 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", ast_channel_name(chan));
00935 return -1;
00936 }
00937 p->chan = base;
00938 return 0;
00939 }
00940
00941 static int agent_hangup(struct ast_channel *ast)
00942 {
00943 struct agent_pvt *p = ast_channel_tech_pvt(ast);
00944 struct ast_channel *indicate_chan = NULL;
00945 char *tmp_moh;
00946
00947 if (p->pending) {
00948 AST_LIST_LOCK(&agents);
00949 AST_LIST_REMOVE(&agents, p, list);
00950 AST_LIST_UNLOCK(&agents);
00951 }
00952
00953 ast_mutex_lock(&p->lock);
00954 p->owner = NULL;
00955 ast_channel_tech_pvt_set(ast, NULL);
00956 p->app_sleep_cond = 1;
00957 p->acknowledged = 0;
00958
00959
00960 p->app_lock_flag = 0;
00961 ast_cond_signal(&p->app_complete_cond);
00962
00963
00964
00965
00966
00967
00968
00969
00970 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast_channel_state(ast)));
00971 if (p->start && (ast_channel_state(ast) != AST_STATE_UP)) {
00972 p->start = 0;
00973 } else
00974 p->start = 0;
00975 if (p->chan) {
00976 ast_channel_internal_bridged_channel_set(p->chan, NULL);
00977
00978 if (p->dead) {
00979 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00980 } else if (p->loginstart) {
00981 indicate_chan = ast_channel_ref(p->chan);
00982 tmp_moh = ast_strdupa(p->moh);
00983 }
00984 }
00985 ast_mutex_unlock(&p->lock);
00986
00987 if (indicate_chan) {
00988 ast_indicate_data(indicate_chan, AST_CONTROL_HOLD,
00989 S_OR(tmp_moh, NULL),
00990 !ast_strlen_zero(tmp_moh) ? strlen(tmp_moh) + 1 : 0);
00991 indicate_chan = ast_channel_unref(indicate_chan);
00992 }
00993
00994
00995 if (!p->loginstart) {
00996 p->logincallerid[0] = '\0';
00997 } else {
00998 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
00999 }
01000
01001 if (p->abouttograb) {
01002
01003
01004 p->abouttograb = 0;
01005 } else if (p->dead) {
01006 ast_mutex_destroy(&p->lock);
01007 ast_cond_destroy(&p->app_complete_cond);
01008 ast_cond_destroy(&p->login_wait_cond);
01009 ast_free(p);
01010 } else {
01011 if (p->chan) {
01012
01013 ast_mutex_lock(&p->lock);
01014
01015 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
01016 ast_mutex_unlock(&p->lock);
01017 }
01018 }
01019 return 0;
01020 }
01021
01022 static int agent_cont_sleep( void *data )
01023 {
01024 struct agent_pvt *p;
01025 int res;
01026
01027 p = (struct agent_pvt *)data;
01028
01029 ast_mutex_lock(&p->lock);
01030 res = p->app_sleep_cond;
01031 if (p->lastdisc.tv_sec) {
01032 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
01033 res = 1;
01034 }
01035 ast_mutex_unlock(&p->lock);
01036
01037 if (!res)
01038 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
01039
01040 return res;
01041 }
01042
01043 static int agent_ack_sleep(void *data)
01044 {
01045 struct agent_pvt *p;
01046 int res=0;
01047 int to = 1000;
01048 struct ast_frame *f;
01049
01050
01051
01052 p = (struct agent_pvt *) data;
01053 if (!p->chan)
01054 return -1;
01055
01056 for(;;) {
01057 to = ast_waitfor(p->chan, to);
01058 if (to < 0)
01059 return -1;
01060 if (!to)
01061 return 0;
01062 f = ast_read(p->chan);
01063 if (!f)
01064 return -1;
01065 if (f->frametype == AST_FRAME_DTMF)
01066 res = f->subclass.integer;
01067 else
01068 res = 0;
01069 ast_frfree(f);
01070 ast_mutex_lock(&p->lock);
01071 if (!p->app_sleep_cond) {
01072 ast_mutex_unlock(&p->lock);
01073 return 0;
01074 } else if (res == p->acceptdtmf) {
01075 ast_mutex_unlock(&p->lock);
01076 return 1;
01077 }
01078 ast_mutex_unlock(&p->lock);
01079 res = 0;
01080 }
01081 return res;
01082 }
01083
01084 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
01085 {
01086 struct agent_pvt *p = ast_channel_tech_pvt(bridge);
01087 struct ast_channel *ret = NULL;
01088
01089 if (p) {
01090 if (chan == p->chan)
01091 ret = ast_channel_internal_bridged_channel(bridge);
01092 else if (chan == ast_channel_internal_bridged_channel(bridge))
01093 ret = p->chan;
01094 }
01095
01096 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", ast_channel_name(chan), ast_channel_name(bridge), ret ? ast_channel_name(ret) : "<none>");
01097 return ret;
01098 }
01099
01100
01101 static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid, struct ast_callid *callid)
01102 {
01103 struct ast_channel *tmp;
01104 #if 0
01105 if (!p->chan) {
01106 ast_log(LOG_WARNING, "No channel? :(\n");
01107 return NULL;
01108 }
01109 #endif
01110 if (p->pending)
01111 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? ast_channel_exten(p->chan):"", p->chan ? ast_channel_context(p->chan):"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
01112 else
01113 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? ast_channel_exten(p->chan):"", p->chan ? ast_channel_context(p->chan):"", linkedid, 0, "Agent/%s", p->agent);
01114 if (!tmp) {
01115 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
01116 return NULL;
01117 }
01118
01119 if (callid) {
01120 ast_channel_callid_set(tmp, callid);
01121 }
01122
01123 ast_channel_tech_set(tmp, &agent_tech);
01124 if (p->chan) {
01125 ast_format_cap_copy(ast_channel_nativeformats(tmp), ast_channel_nativeformats(p->chan));
01126 ast_format_copy(ast_channel_writeformat(tmp), ast_channel_writeformat(p->chan));
01127 ast_format_copy(ast_channel_rawwriteformat(tmp), ast_channel_writeformat(p->chan));
01128 ast_format_copy(ast_channel_readformat(tmp), ast_channel_readformat(p->chan));
01129 ast_format_copy(ast_channel_rawreadformat(tmp), ast_channel_readformat(p->chan));
01130 ast_channel_language_set(tmp, ast_channel_language(p->chan));
01131 ast_channel_context_set(tmp, ast_channel_context(p->chan));
01132 ast_channel_exten_set(tmp, ast_channel_exten(p->chan));
01133
01134 } else {
01135 ast_format_set(ast_channel_writeformat(tmp), AST_FORMAT_SLINEAR, 0);
01136 ast_format_set(ast_channel_rawwriteformat(tmp), AST_FORMAT_SLINEAR, 0);
01137 ast_format_set(ast_channel_readformat(tmp), AST_FORMAT_SLINEAR, 0);
01138 ast_format_set(ast_channel_rawreadformat(tmp), AST_FORMAT_SLINEAR, 0);
01139 ast_format_cap_add(ast_channel_nativeformats(tmp), ast_channel_writeformat(tmp));
01140 }
01141
01142 ast_channel_tech_pvt_set(tmp, p);
01143 p->owner = tmp;
01144 ast_channel_priority_set(tmp, 1);
01145 return tmp;
01146 }
01147
01148
01149
01150
01151
01152
01153
01154 static int read_agent_config(int reload)
01155 {
01156 struct ast_config *cfg;
01157 struct ast_config *ucfg;
01158 struct ast_variable *v;
01159 struct agent_pvt *p;
01160 const char *catname;
01161 const char *hasagent;
01162 int genhasagent;
01163 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01164
01165 group = 0;
01166 autologoff = 0;
01167 wrapuptime = 0;
01168 ackcall = 0;
01169 endcall = 1;
01170 cfg = ast_config_load(config, config_flags);
01171 if (!cfg) {
01172 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01173 return 0;
01174 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01175 return -1;
01176 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
01177 ast_log(LOG_ERROR, "%s contains a parsing error. Aborting\n", config);
01178 return 0;
01179 }
01180 if ((ucfg = ast_config_load("users.conf", config_flags))) {
01181 if (ucfg == CONFIG_STATUS_FILEUNCHANGED) {
01182 ucfg = NULL;
01183 } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
01184 ast_log(LOG_ERROR, "users.conf contains a parsing error. Aborting\n");
01185 return 0;
01186 }
01187 }
01188
01189 AST_LIST_LOCK(&agents);
01190 AST_LIST_TRAVERSE(&agents, p, list) {
01191 p->dead = 1;
01192 }
01193 strcpy(moh, "default");
01194
01195 recordagentcalls = 0;
01196 strcpy(recordformat, "wav");
01197 strcpy(recordformatext, "wav");
01198 urlprefix[0] = '\0';
01199 savecallsin[0] = '\0';
01200
01201
01202 v = ast_variable_browse(cfg, "agents");
01203 while(v) {
01204
01205 if (!strcasecmp(v->name, "agent")) {
01206 add_agent(v->value, 0);
01207 } else if (!strcasecmp(v->name, "group")) {
01208 group = ast_get_group(v->value);
01209 } else if (!strcasecmp(v->name, "autologoff")) {
01210 autologoff = atoi(v->value);
01211 if (autologoff < 0)
01212 autologoff = 0;
01213 } else if (!strcasecmp(v->name, "ackcall")) {
01214 if (ast_true(v->value) || !strcasecmp(v->value, "always")) {
01215 ackcall = 1;
01216 }
01217 } else if (!strcasecmp(v->name, "endcall")) {
01218 endcall = ast_true(v->value);
01219 } else if (!strcasecmp(v->name, "acceptdtmf")) {
01220 acceptdtmf = *(v->value);
01221 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
01222 } else if (!strcasecmp(v->name, "enddtmf")) {
01223 enddtmf = *(v->value);
01224 } else if (!strcasecmp(v->name, "wrapuptime")) {
01225 wrapuptime = atoi(v->value);
01226 if (wrapuptime < 0)
01227 wrapuptime = 0;
01228 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01229 maxlogintries = atoi(v->value);
01230 if (maxlogintries < 0)
01231 maxlogintries = 0;
01232 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01233 strcpy(agentgoodbye,v->value);
01234 } else if (!strcasecmp(v->name, "musiconhold")) {
01235 ast_copy_string(moh, v->value, sizeof(moh));
01236 } else if (!strcasecmp(v->name, "updatecdr")) {
01237 if (ast_true(v->value))
01238 updatecdr = 1;
01239 else
01240 updatecdr = 0;
01241 } else if (!strcasecmp(v->name, "autologoffunavail")) {
01242 if (ast_true(v->value))
01243 autologoffunavail = 1;
01244 else
01245 autologoffunavail = 0;
01246 } else if (!strcasecmp(v->name, "recordagentcalls")) {
01247 recordagentcalls = ast_true(v->value);
01248 } else if (!strcasecmp(v->name, "recordformat")) {
01249 ast_copy_string(recordformat, v->value, sizeof(recordformat));
01250 if (!strcasecmp(v->value, "wav49"))
01251 strcpy(recordformatext, "WAV");
01252 else
01253 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01254 } else if (!strcasecmp(v->name, "urlprefix")) {
01255 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01256 if (urlprefix[strlen(urlprefix) - 1] != '/')
01257 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01258 } else if (!strcasecmp(v->name, "savecallsin")) {
01259 if (v->value[0] == '/')
01260 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01261 else
01262 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01263 if (savecallsin[strlen(savecallsin) - 1] != '/')
01264 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01265 } else if (!strcasecmp(v->name, "custom_beep")) {
01266 ast_copy_string(beep, v->value, sizeof(beep));
01267 }
01268 v = v->next;
01269 }
01270 if (ucfg) {
01271 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01272 catname = ast_category_browse(ucfg, NULL);
01273 while(catname) {
01274 if (strcasecmp(catname, "general")) {
01275 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01276 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01277 char tmp[256];
01278 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01279 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01280 if (!fullname)
01281 fullname = "";
01282 if (!secret)
01283 secret = "";
01284 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01285 add_agent(tmp, 0);
01286 }
01287 }
01288 catname = ast_category_browse(ucfg, catname);
01289 }
01290 ast_config_destroy(ucfg);
01291 }
01292 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01293 if (p->dead) {
01294 AST_LIST_REMOVE_CURRENT(list);
01295
01296 if (!p->owner) {
01297 if (!p->chan) {
01298 ast_mutex_destroy(&p->lock);
01299 ast_cond_destroy(&p->app_complete_cond);
01300 ast_cond_destroy(&p->login_wait_cond);
01301 ast_free(p);
01302 } else {
01303
01304 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01305 }
01306 }
01307 }
01308 }
01309 AST_LIST_TRAVERSE_SAFE_END;
01310 AST_LIST_UNLOCK(&agents);
01311 ast_config_destroy(cfg);
01312 return 1;
01313 }
01314
01315 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01316 {
01317 struct ast_channel *chan=NULL, *parent=NULL;
01318 struct agent_pvt *p;
01319 int res;
01320
01321 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
01322 if (needlock)
01323 AST_LIST_LOCK(&agents);
01324 AST_LIST_TRAVERSE(&agents, p, list) {
01325 if (p == newlyavailable) {
01326 continue;
01327 }
01328 ast_mutex_lock(&p->lock);
01329 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01330 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", ast_channel_name(p->owner), newlyavailable->agent);
01331
01332 chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? ast_channel_linkedid(p->owner) : NULL, NULL);
01333 parent = p->owner;
01334 p->abouttograb = 1;
01335 ast_mutex_unlock(&p->lock);
01336 break;
01337 }
01338 ast_mutex_unlock(&p->lock);
01339 }
01340 if (needlock)
01341 AST_LIST_UNLOCK(&agents);
01342 if (parent && chan) {
01343 if (newlyavailable->ackcall) {
01344
01345 res = 0;
01346 } else {
01347 ast_debug(3, "Playing beep, lang '%s'\n", ast_channel_language(newlyavailable->chan));
01348 res = ast_streamfile(newlyavailable->chan, beep, ast_channel_language(newlyavailable->chan));
01349 ast_debug(3, "Played beep, result '%d'\n", res);
01350 if (!res) {
01351 res = ast_waitstream(newlyavailable->chan, "");
01352 ast_debug(1, "Waited for stream, result '%d'\n", res);
01353 }
01354 }
01355 if (!res) {
01356
01357 if (p->abouttograb) {
01358 newlyavailable->acknowledged = 1;
01359
01360 ast_setstate(parent, AST_STATE_UP);
01361 ast_setstate(chan, AST_STATE_UP);
01362 ast_channel_context_set(parent, ast_channel_context(chan));
01363 ast_channel_masquerade(parent, chan);
01364 ast_hangup(chan);
01365 p->abouttograb = 0;
01366 } else {
01367 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
01368 agent_cleanup(newlyavailable);
01369 }
01370 } else {
01371 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
01372 agent_cleanup(newlyavailable);
01373 }
01374 }
01375 return 0;
01376 }
01377
01378 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01379 {
01380 struct agent_pvt *p;
01381 int res=0;
01382
01383 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
01384 if (needlock)
01385 AST_LIST_LOCK(&agents);
01386 AST_LIST_TRAVERSE(&agents, p, list) {
01387 if (p == newlyavailable) {
01388 continue;
01389 }
01390 ast_mutex_lock(&p->lock);
01391 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01392 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", ast_channel_name(p->owner), newlyavailable->agent);
01393 ast_mutex_unlock(&p->lock);
01394 break;
01395 }
01396 ast_mutex_unlock(&p->lock);
01397 }
01398 if (needlock)
01399 AST_LIST_UNLOCK(&agents);
01400 if (p) {
01401 ast_mutex_unlock(&newlyavailable->lock);
01402 ast_debug(3, "Playing beep, lang '%s'\n", ast_channel_language(newlyavailable->chan));
01403 res = ast_streamfile(newlyavailable->chan, beep, ast_channel_language(newlyavailable->chan));
01404 ast_debug(1, "Played beep, result '%d'\n", res);
01405 if (!res) {
01406 res = ast_waitstream(newlyavailable->chan, "");
01407 ast_debug(1, "Waited for stream, result '%d'\n", res);
01408 }
01409 ast_mutex_lock(&newlyavailable->lock);
01410 }
01411 return res;
01412 }
01413
01414
01415 static struct ast_channel *agent_request(const char *type, struct ast_format_cap *cap, const struct ast_channel* requestor, const char *data, int *cause)
01416 {
01417 struct agent_pvt *p;
01418 struct ast_channel *chan = NULL;
01419 const char *s;
01420 ast_group_t groupmatch;
01421 int groupoff;
01422 int waitforagent=0;
01423 int hasagent = 0;
01424 struct timeval now;
01425 struct ast_callid *callid = ast_read_threadstorage_callid();
01426
01427 s = data;
01428 if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01429 groupmatch = (1 << groupoff);
01430 } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01431 groupmatch = (1 << groupoff);
01432 waitforagent = 1;
01433 } else
01434 groupmatch = 0;
01435
01436
01437 AST_LIST_LOCK(&agents);
01438 AST_LIST_TRAVERSE(&agents, p, list) {
01439 ast_mutex_lock(&p->lock);
01440 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01441 if (p->chan)
01442 hasagent++;
01443 now = ast_tvnow();
01444 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
01445 p->lastdisc = ast_tv(0, 0);
01446
01447 if (!p->owner && p->chan) {
01448
01449 chan = agent_new(p, AST_STATE_DOWN, requestor ? ast_channel_linkedid(requestor) : NULL, callid);
01450 }
01451 if (chan) {
01452 ast_mutex_unlock(&p->lock);
01453 break;
01454 }
01455 }
01456 }
01457 ast_mutex_unlock(&p->lock);
01458 }
01459 if (!p) {
01460 AST_LIST_TRAVERSE(&agents, p, list) {
01461 ast_mutex_lock(&p->lock);
01462 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01463 if (p->chan) {
01464 hasagent++;
01465 }
01466 now = ast_tvnow();
01467 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
01468 p->lastdisc = ast_tv(0, 0);
01469
01470 if (!p->owner && p->chan) {
01471
01472 chan = agent_new(p, AST_STATE_DOWN, requestor ? ast_channel_linkedid(requestor) : NULL, callid);
01473 }
01474 if (chan) {
01475 ast_mutex_unlock(&p->lock);
01476 break;
01477 }
01478 }
01479 }
01480 ast_mutex_unlock(&p->lock);
01481 }
01482 }
01483
01484 if (!chan && waitforagent) {
01485
01486
01487 if (hasagent) {
01488 ast_debug(1, "Creating place holder for '%s'\n", s);
01489 p = add_agent(data, 1);
01490 p->group = groupmatch;
01491 chan = agent_new(p, AST_STATE_DOWN, requestor ? ast_channel_linkedid(requestor) : NULL, callid);
01492 if (!chan)
01493 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
01494 } else {
01495 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
01496 }
01497 }
01498 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01499 AST_LIST_UNLOCK(&agents);
01500
01501 if (callid) {
01502 callid = ast_callid_unref(callid);
01503 }
01504
01505 if (chan) {
01506 ast_mutex_lock(&p->lock);
01507 if (p->pending) {
01508 ast_mutex_unlock(&p->lock);
01509 return chan;
01510 }
01511
01512 if (!p->chan) {
01513 ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
01514 *cause = AST_CAUSE_UNREGISTERED;
01515 ast_mutex_unlock(&p->lock);
01516 agent_hangup(chan);
01517 return NULL;
01518 }
01519
01520
01521
01522 p->app_sleep_cond = 0;
01523 p->app_lock_flag = 1;
01524
01525 ast_queue_frame(p->chan, &ast_null_frame);
01526 ast_cond_wait(&p->login_wait_cond, &p->lock);
01527
01528 if (!p->chan) {
01529 ast_log(LOG_DEBUG, "Agent disconnected while we were connecting the call\n");
01530 p->app_sleep_cond = 1;
01531 p->app_lock_flag = 0;
01532 ast_cond_signal(&p->app_complete_cond);
01533 ast_mutex_unlock(&p->lock);
01534 *cause = AST_CAUSE_UNREGISTERED;
01535 agent_hangup(chan);
01536 return NULL;
01537 }
01538
01539 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
01540 ast_mutex_unlock(&p->lock);
01541 }
01542
01543 return chan;
01544 }
01545
01546 static force_inline int powerof(unsigned int d)
01547 {
01548 int x = ffs(d);
01549
01550 if (x)
01551 return x - 1;
01552
01553 return 0;
01554 }
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565
01566 static int action_agents(struct mansession *s, const struct message *m)
01567 {
01568 const char *id = astman_get_header(m,"ActionID");
01569 char idText[256] = "";
01570 struct agent_pvt *p;
01571 char *username = NULL;
01572 char *loginChan = NULL;
01573 char *talkingto = NULL;
01574 char *talkingtoChan = NULL;
01575 char *status = NULL;
01576 struct ast_channel *bridge;
01577
01578 if (!ast_strlen_zero(id))
01579 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01580 astman_send_ack(s, m, "Agents will follow");
01581 AST_LIST_LOCK(&agents);
01582 AST_LIST_TRAVERSE(&agents, p, list) {
01583 struct ast_channel *owner;
01584 ast_mutex_lock(&p->lock);
01585 owner = agent_lock_owner(p);
01586
01587
01588
01589
01590
01591
01592
01593 username = S_OR(p->name, "None");
01594
01595
01596 status = "AGENT_UNKNOWN";
01597
01598 if (p->chan) {
01599 loginChan = ast_strdupa(ast_channel_name(p->chan));
01600 if (owner && ast_channel_internal_bridged_channel(owner)) {
01601 talkingto = S_COR(ast_channel_caller(p->chan)->id.number.valid,
01602 ast_channel_caller(p->chan)->id.number.str, "n/a");
01603 if ((bridge = ast_bridged_channel(owner))) {
01604 talkingtoChan = ast_strdupa(ast_channel_name(bridge));
01605 } else {
01606 talkingtoChan = "n/a";
01607 }
01608 status = "AGENT_ONCALL";
01609 } else {
01610 talkingto = "n/a";
01611 talkingtoChan = "n/a";
01612 status = "AGENT_IDLE";
01613 }
01614 } else {
01615 loginChan = "n/a";
01616 talkingto = "n/a";
01617 talkingtoChan = "n/a";
01618 status = "AGENT_LOGGEDOFF";
01619 }
01620
01621 if (owner) {
01622 ast_channel_unlock(owner);
01623 owner = ast_channel_unref(owner);
01624 }
01625
01626 astman_append(s, "Event: Agents\r\n"
01627 "Agent: %s\r\n"
01628 "Name: %s\r\n"
01629 "Status: %s\r\n"
01630 "LoggedInChan: %s\r\n"
01631 "LoggedInTime: %d\r\n"
01632 "TalkingTo: %s\r\n"
01633 "TalkingToChan: %s\r\n"
01634 "%s"
01635 "\r\n",
01636 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
01637 ast_mutex_unlock(&p->lock);
01638 }
01639 AST_LIST_UNLOCK(&agents);
01640 astman_append(s, "Event: AgentsComplete\r\n"
01641 "%s"
01642 "\r\n",idText);
01643 return 0;
01644 }
01645
01646 static int agent_logoff(const char *agent, int soft)
01647 {
01648 struct agent_pvt *p;
01649 int ret = -1;
01650
01651 AST_LIST_LOCK(&agents);
01652 AST_LIST_TRAVERSE(&agents, p, list) {
01653 if (!strcasecmp(p->agent, agent)) {
01654 ret = 0;
01655 if (p->owner || p->chan) {
01656 if (!soft) {
01657 struct ast_channel *owner;
01658 ast_mutex_lock(&p->lock);
01659 owner = agent_lock_owner(p);
01660
01661 if (owner) {
01662 ast_softhangup(owner, AST_SOFTHANGUP_EXPLICIT);
01663 ast_channel_unlock(owner);
01664 owner = ast_channel_unref(owner);
01665 }
01666
01667 while (p->chan && ast_channel_trylock(p->chan)) {
01668 DEADLOCK_AVOIDANCE(&p->lock);
01669 }
01670 if (p->chan) {
01671 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01672 ast_channel_unlock(p->chan);
01673 }
01674
01675 ast_mutex_unlock(&p->lock);
01676 } else
01677 p->deferlogoff = 1;
01678 }
01679 break;
01680 }
01681 }
01682 AST_LIST_UNLOCK(&agents);
01683
01684 return ret;
01685 }
01686
01687 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01688 {
01689 int ret;
01690 const char *agent;
01691
01692 switch (cmd) {
01693 case CLI_INIT:
01694 e->command = "agent logoff";
01695 e->usage =
01696 "Usage: agent logoff <channel> [soft]\n"
01697 " Sets an agent as no longer logged in.\n"
01698 " If 'soft' is specified, do not hangup existing calls.\n";
01699 return NULL;
01700 case CLI_GENERATE:
01701 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
01702 }
01703
01704 if (a->argc < 3 || a->argc > 4)
01705 return CLI_SHOWUSAGE;
01706 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
01707 return CLI_SHOWUSAGE;
01708
01709 agent = a->argv[2] + 6;
01710 ret = agent_logoff(agent, a->argc == 4);
01711 if (ret == 0)
01712 ast_cli(a->fd, "Logging out %s\n", agent);
01713
01714 return CLI_SUCCESS;
01715 }
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725 static int action_agent_logoff(struct mansession *s, const struct message *m)
01726 {
01727 const char *agent = astman_get_header(m, "Agent");
01728 const char *soft_s = astman_get_header(m, "Soft");
01729 int soft;
01730 int ret;
01731
01732 if (ast_strlen_zero(agent)) {
01733 astman_send_error(s, m, "No agent specified");
01734 return 0;
01735 }
01736
01737 soft = ast_true(soft_s) ? 1 : 0;
01738 ret = agent_logoff(agent, soft);
01739 if (ret == 0)
01740 astman_send_ack(s, m, "Agent logged out");
01741 else
01742 astman_send_error(s, m, "No such agent");
01743
01744 return 0;
01745 }
01746
01747 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01748 {
01749 char *ret = NULL;
01750
01751 if (pos == 2) {
01752 struct agent_pvt *p;
01753 char name[AST_MAX_AGENT];
01754 int which = 0, len = strlen(word);
01755
01756 AST_LIST_LOCK(&agents);
01757 AST_LIST_TRAVERSE(&agents, p, list) {
01758 snprintf(name, sizeof(name), "Agent/%s", p->agent);
01759 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
01760 ret = ast_strdup(name);
01761 break;
01762 }
01763 }
01764 AST_LIST_UNLOCK(&agents);
01765 } else if (pos == 3 && state == 0)
01766 return ast_strdup("soft");
01767
01768 return ret;
01769 }
01770
01771
01772
01773
01774 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01775 {
01776 struct agent_pvt *p;
01777 char username[AST_MAX_BUF];
01778 char location[AST_MAX_BUF] = "";
01779 char talkingto[AST_MAX_BUF] = "";
01780 char music[AST_MAX_BUF];
01781 int count_agents = 0;
01782 int online_agents = 0;
01783 int offline_agents = 0;
01784
01785 switch (cmd) {
01786 case CLI_INIT:
01787 e->command = "agent show";
01788 e->usage =
01789 "Usage: agent show\n"
01790 " Provides summary information on agents.\n";
01791 return NULL;
01792 case CLI_GENERATE:
01793 return NULL;
01794 }
01795
01796 if (a->argc != 2)
01797 return CLI_SHOWUSAGE;
01798
01799 AST_LIST_LOCK(&agents);
01800 AST_LIST_TRAVERSE(&agents, p, list) {
01801 struct ast_channel *owner;
01802 ast_mutex_lock(&p->lock);
01803 owner = agent_lock_owner(p);
01804 if (p->pending) {
01805 if (p->group)
01806 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
01807 else
01808 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
01809 } else {
01810 if (!ast_strlen_zero(p->name))
01811 snprintf(username, sizeof(username), "(%s) ", p->name);
01812 else
01813 username[0] = '\0';
01814 if (p->chan) {
01815 snprintf(location, sizeof(location), "logged in on %s", ast_channel_name(p->chan));
01816 if (owner && ast_bridged_channel(owner)) {
01817 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_channel_name(ast_bridged_channel(p->owner)));
01818 } else {
01819 strcpy(talkingto, " is idle");
01820 }
01821 online_agents++;
01822 } else {
01823 strcpy(location, "not logged in");
01824 talkingto[0] = '\0';
01825 offline_agents++;
01826 }
01827 if (!ast_strlen_zero(p->moh))
01828 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01829 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
01830 username, location, talkingto, music);
01831 count_agents++;
01832 }
01833
01834 if (owner) {
01835 ast_channel_unlock(owner);
01836 owner = ast_channel_unref(owner);
01837 }
01838 ast_mutex_unlock(&p->lock);
01839 }
01840 AST_LIST_UNLOCK(&agents);
01841 if ( !count_agents )
01842 ast_cli(a->fd, "No Agents are configured in %s\n",config);
01843 else
01844 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01845 ast_cli(a->fd, "\n");
01846
01847 return CLI_SUCCESS;
01848 }
01849
01850
01851 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01852 {
01853 struct agent_pvt *p;
01854 char username[AST_MAX_BUF];
01855 char location[AST_MAX_BUF] = "";
01856 char talkingto[AST_MAX_BUF] = "";
01857 char music[AST_MAX_BUF];
01858 int count_agents = 0;
01859 int online_agents = 0;
01860 int agent_status = 0;
01861
01862 switch (cmd) {
01863 case CLI_INIT:
01864 e->command = "agent show online";
01865 e->usage =
01866 "Usage: agent show online\n"
01867 " Provides a list of all online agents.\n";
01868 return NULL;
01869 case CLI_GENERATE:
01870 return NULL;
01871 }
01872
01873 if (a->argc != 3)
01874 return CLI_SHOWUSAGE;
01875
01876 AST_LIST_LOCK(&agents);
01877 AST_LIST_TRAVERSE(&agents, p, list) {
01878 struct ast_channel *owner;
01879
01880 agent_status = 0;
01881 ast_mutex_lock(&p->lock);
01882 owner = agent_lock_owner(p);
01883
01884 if (!ast_strlen_zero(p->name))
01885 snprintf(username, sizeof(username), "(%s) ", p->name);
01886 else
01887 username[0] = '\0';
01888 if (p->chan) {
01889 snprintf(location, sizeof(location), "logged in on %s", ast_channel_name(p->chan));
01890 if (p->owner && ast_bridged_channel(p->owner)) {
01891 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_channel_name(ast_bridged_channel(p->owner)));
01892 } else {
01893 strcpy(talkingto, " is idle");
01894 }
01895 agent_status = 1;
01896 online_agents++;
01897 }
01898
01899 if (owner) {
01900 ast_channel_unlock(owner);
01901 owner = ast_channel_unref(owner);
01902 }
01903
01904 if (!ast_strlen_zero(p->moh))
01905 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01906 if (agent_status)
01907 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
01908 count_agents++;
01909 ast_mutex_unlock(&p->lock);
01910 }
01911 AST_LIST_UNLOCK(&agents);
01912 if (!count_agents)
01913 ast_cli(a->fd, "No Agents are configured in %s\n", config);
01914 else
01915 ast_cli(a->fd, "%d agents online\n", online_agents);
01916 ast_cli(a->fd, "\n");
01917 return CLI_SUCCESS;
01918 }
01919
01920 static const char agent_logoff_usage[] =
01921 "Usage: agent logoff <channel> [soft]\n"
01922 " Sets an agent as no longer logged in.\n"
01923 " If 'soft' is specified, do not hangup existing calls.\n";
01924
01925 static struct ast_cli_entry cli_agents[] = {
01926 AST_CLI_DEFINE(agents_show, "Show status of agents"),
01927 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
01928 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
01929 };
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939
01940
01941 static int login_exec(struct ast_channel *chan, const char *data)
01942 {
01943 int res=0;
01944 int tries = 0;
01945 int max_login_tries = maxlogintries;
01946 struct agent_pvt *p;
01947 struct ast_module_user *u;
01948 char user[AST_MAX_AGENT] = "";
01949 char pass[AST_MAX_AGENT];
01950 char agent[AST_MAX_AGENT] = "";
01951 char xpass[AST_MAX_AGENT] = "";
01952 char *errmsg;
01953 char *parse;
01954 AST_DECLARE_APP_ARGS(args,
01955 AST_APP_ARG(agent_id);
01956 AST_APP_ARG(options);
01957 AST_APP_ARG(extension);
01958 );
01959 const char *tmpoptions = NULL;
01960 int play_announcement = 1;
01961 char agent_goodbye[AST_MAX_FILENAME_LEN];
01962 int update_cdr = updatecdr;
01963 char *filename = "agent-loginok";
01964
01965 u = ast_module_user_add(chan);
01966
01967 parse = ast_strdupa(data);
01968
01969 AST_STANDARD_APP_ARGS(args, parse);
01970
01971 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01972
01973 ast_channel_lock(chan);
01974
01975 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01976 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01977 if (max_login_tries < 0)
01978 max_login_tries = 0;
01979 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01980 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,ast_channel_name(chan));
01981 }
01982 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01983 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01984 update_cdr = 1;
01985 else
01986 update_cdr = 0;
01987 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01988 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,ast_channel_name(chan));
01989 }
01990 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01991 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01992 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01993 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,ast_channel_name(chan));
01994 }
01995 ast_channel_unlock(chan);
01996
01997
01998 if (!ast_strlen_zero(args.options)) {
01999 if (strchr(args.options, 's')) {
02000 play_announcement = 0;
02001 }
02002 }
02003
02004 if (ast_channel_state(chan) != AST_STATE_UP)
02005 res = ast_answer(chan);
02006 if (!res) {
02007 if (!ast_strlen_zero(args.agent_id))
02008 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
02009 else
02010 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
02011 }
02012 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
02013 tries++;
02014
02015 AST_LIST_LOCK(&agents);
02016 AST_LIST_TRAVERSE(&agents, p, list) {
02017 if (!strcmp(p->agent, user) && !p->pending)
02018 ast_copy_string(xpass, p->password, sizeof(xpass));
02019 }
02020 AST_LIST_UNLOCK(&agents);
02021 if (!res) {
02022 if (!ast_strlen_zero(xpass))
02023 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
02024 else
02025 pass[0] = '\0';
02026 }
02027 errmsg = "agent-incorrect";
02028
02029 #if 0
02030 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
02031 #endif
02032
02033
02034 AST_LIST_LOCK(&agents);
02035 AST_LIST_TRAVERSE(&agents, p, list) {
02036 int unlock_channel = 1;
02037 ast_channel_lock(chan);
02038 ast_mutex_lock(&p->lock);
02039 if (!strcmp(p->agent, user) &&
02040 !strcmp(p->password, pass) && !p->pending) {
02041
02042
02043 p->lastdisc = ast_tvnow();
02044 p->lastdisc.tv_sec++;
02045
02046
02047 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
02048 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
02049 p->ackcall = 1;
02050 } else {
02051 p->ackcall = 0;
02052 }
02053 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
02054 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
02055 ast_set_flag(p, AGENT_FLAG_ACKCALL);
02056 } else {
02057 p->ackcall = ackcall;
02058 }
02059 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
02060 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
02061 if (p->autologoff < 0)
02062 p->autologoff = 0;
02063 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
02064 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
02065 ast_set_flag(p, AGENT_FLAG_AUTOLOGOFF);
02066 } else {
02067 p->autologoff = autologoff;
02068 }
02069 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
02070 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
02071 if (p->wrapuptime < 0)
02072 p->wrapuptime = 0;
02073 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
02074 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
02075 ast_set_flag(p, AGENT_FLAG_WRAPUPTIME);
02076 } else {
02077 p->wrapuptime = wrapuptime;
02078 }
02079 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
02080 if (!ast_strlen_zero(tmpoptions)) {
02081 p->acceptdtmf = *tmpoptions;
02082 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
02083 ast_set_flag(p, AGENT_FLAG_ACCEPTDTMF);
02084 }
02085 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
02086 if (!ast_strlen_zero(tmpoptions)) {
02087 p->enddtmf = *tmpoptions;
02088 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
02089 ast_set_flag(p, AGENT_FLAG_ENDDTMF);
02090 }
02091 ast_channel_unlock(chan);
02092 unlock_channel = 0;
02093
02094 if (!p->chan) {
02095 long logintime;
02096 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
02097
02098 p->logincallerid[0] = '\0';
02099 p->acknowledged = 0;
02100
02101 ast_mutex_unlock(&p->lock);
02102 AST_LIST_UNLOCK(&agents);
02103 if( !res && play_announcement==1 )
02104 res = ast_streamfile(chan, filename, ast_channel_language(chan));
02105 if (!res)
02106 ast_waitstream(chan, "");
02107 AST_LIST_LOCK(&agents);
02108 ast_mutex_lock(&p->lock);
02109 if (!res) {
02110 struct ast_format tmpfmt;
02111 res = ast_set_read_format_from_cap(chan, ast_channel_nativeformats(chan));
02112 if (res) {
02113 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(&tmpfmt));
02114 }
02115 }
02116 if (!res) {
02117 struct ast_format tmpfmt;
02118 res = ast_set_write_format_from_cap(chan, ast_channel_nativeformats(chan));
02119 if (res) {
02120 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(&tmpfmt));
02121 }
02122 }
02123
02124 if (p->chan)
02125 res = -1;
02126 if (!res) {
02127 ast_indicate_data(chan, AST_CONTROL_HOLD,
02128 S_OR(p->moh, NULL),
02129 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02130 if (p->loginstart == 0)
02131 time(&p->loginstart);
02132
02133
02134
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02147 "Agent: %s\r\n"
02148 "Channel: %s\r\n"
02149 "Uniqueid: %s\r\n",
02150 p->agent, ast_channel_name(chan), ast_channel_uniqueid(chan));
02151 if (update_cdr && ast_channel_cdr(chan))
02152 snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "Agent/%s", p->agent);
02153 ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGIN", "%s", ast_channel_name(chan));
02154 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
02155 ast_getformatname(ast_channel_readformat(chan)), ast_getformatname(ast_channel_writeformat(chan)));
02156
02157 p->chan = chan;
02158 if (p->ackcall) {
02159 check_beep(p, 0);
02160 } else {
02161 check_availability(p, 0);
02162 }
02163 ast_mutex_unlock(&p->lock);
02164 AST_LIST_UNLOCK(&agents);
02165 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
02166 while (res >= 0) {
02167 ast_mutex_lock(&p->lock);
02168 if (p->deferlogoff && p->chan) {
02169 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02170 p->deferlogoff = 0;
02171 }
02172 if (p->chan != chan)
02173 res = -1;
02174 ast_mutex_unlock(&p->lock);
02175
02176 sched_yield();
02177 if (res)
02178 break;
02179
02180 AST_LIST_LOCK(&agents);
02181 ast_mutex_lock(&p->lock);
02182 if (p->lastdisc.tv_sec) {
02183 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02184 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
02185 p->lastdisc = ast_tv(0, 0);
02186 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
02187 if (p->ackcall) {
02188 check_beep(p, 0);
02189 } else {
02190 check_availability(p, 0);
02191 }
02192 }
02193 }
02194 ast_mutex_unlock(&p->lock);
02195 AST_LIST_UNLOCK(&agents);
02196
02197
02198 ast_mutex_lock(&p->lock);
02199 if (p->app_lock_flag == 1) {
02200 ast_cond_signal(&p->login_wait_cond);
02201 ast_cond_wait(&p->app_complete_cond, &p->lock);
02202 }
02203 ast_mutex_unlock(&p->lock);
02204 if (p->ackcall) {
02205 res = agent_ack_sleep(p);
02206 } else {
02207 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02208 }
02209 if (p->ackcall && (res == 1)) {
02210 AST_LIST_LOCK(&agents);
02211 ast_mutex_lock(&p->lock);
02212 check_availability(p, 0);
02213 ast_mutex_unlock(&p->lock);
02214 AST_LIST_UNLOCK(&agents);
02215 res = 0;
02216 }
02217 sched_yield();
02218 }
02219 ast_mutex_lock(&p->lock);
02220
02221 if (p->chan == chan) {
02222 p->chan = NULL;
02223 }
02224
02225
02226 if (p->app_lock_flag == 1) {
02227 ast_cond_signal(&p->login_wait_cond);
02228 ast_cond_wait(&p->app_complete_cond, &p->lock);
02229 }
02230
02231 if (res && p->owner)
02232 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
02233
02234 p->acknowledged = 0;
02235 logintime = time(NULL) - p->loginstart;
02236 p->loginstart = 0;
02237 ast_mutex_unlock(&p->lock);
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02250 "Agent: %s\r\n"
02251 "Logintime: %ld\r\n"
02252 "Uniqueid: %s\r\n",
02253 p->agent, logintime, ast_channel_uniqueid(chan));
02254 ast_queue_log("NONE", ast_channel_uniqueid(chan), agent, "AGENTLOGOFF", "%s|%ld", ast_channel_name(chan), logintime);
02255 ast_verb(2, "Agent '%s' logged out\n", p->agent);
02256
02257 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
02258 if (p->dead && !p->owner) {
02259 ast_mutex_destroy(&p->lock);
02260 ast_cond_destroy(&p->app_complete_cond);
02261 ast_cond_destroy(&p->login_wait_cond);
02262 ast_free(p);
02263 }
02264 }
02265 else {
02266 ast_mutex_unlock(&p->lock);
02267 p = NULL;
02268 }
02269 res = -1;
02270 } else {
02271 ast_mutex_unlock(&p->lock);
02272 errmsg = "agent-alreadyon";
02273 p = NULL;
02274 }
02275 break;
02276 }
02277 ast_mutex_unlock(&p->lock);
02278 if (unlock_channel) {
02279 ast_channel_unlock(chan);
02280 }
02281 }
02282 if (!p)
02283 AST_LIST_UNLOCK(&agents);
02284
02285 if (!res && (max_login_tries==0 || tries < max_login_tries))
02286 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02287 }
02288
02289 if (!res)
02290 res = ast_safe_sleep(chan, 500);
02291
02292 ast_module_user_remove(u);
02293
02294 return -1;
02295 }
02296
02297
02298
02299
02300
02301
02302
02303
02304
02305 static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
02306 {
02307 int exitifnoagentid = 0;
02308 int nowarnings = 0;
02309 int changeoutgoing = 0;
02310 int res = 0;
02311 char agent[AST_MAX_AGENT];
02312
02313 if (data) {
02314 if (strchr(data, 'd'))
02315 exitifnoagentid = 1;
02316 if (strchr(data, 'n'))
02317 nowarnings = 1;
02318 if (strchr(data, 'c'))
02319 changeoutgoing = 1;
02320 }
02321 if (ast_channel_caller(chan)->id.number.valid
02322 && !ast_strlen_zero(ast_channel_caller(chan)->id.number.str)) {
02323 const char *tmp;
02324 char agentvar[AST_MAX_BUF];
02325 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID,
02326 ast_channel_caller(chan)->id.number.str);
02327 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02328 struct agent_pvt *p;
02329 ast_copy_string(agent, tmp, sizeof(agent));
02330 AST_LIST_LOCK(&agents);
02331 AST_LIST_TRAVERSE(&agents, p, list) {
02332 if (!strcasecmp(p->agent, tmp)) {
02333 if (changeoutgoing) snprintf(ast_channel_cdr(chan)->channel, sizeof(ast_channel_cdr(chan)->channel), "Agent/%s", p->agent);
02334 __agent_start_monitoring(chan, p, 1);
02335 break;
02336 }
02337 }
02338 AST_LIST_UNLOCK(&agents);
02339
02340 } else {
02341 res = -1;
02342 if (!nowarnings)
02343 ast_log(LOG_WARNING, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar);
02344 }
02345 } else {
02346 res = -1;
02347 if (!nowarnings)
02348 ast_log(LOG_WARNING, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n");
02349 }
02350 if (res) {
02351 if (exitifnoagentid)
02352 return res;
02353 }
02354 return 0;
02355 }
02356
02357
02358 static int agent_devicestate(const char *data)
02359 {
02360 struct agent_pvt *p;
02361 const char *s;
02362 ast_group_t groupmatch;
02363 int groupoff;
02364 int res = AST_DEVICE_INVALID;
02365
02366 s = data;
02367 if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1))
02368 groupmatch = (1 << groupoff);
02369 else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
02370 groupmatch = (1 << groupoff);
02371 } else
02372 groupmatch = 0;
02373
02374
02375 AST_LIST_LOCK(&agents);
02376 AST_LIST_TRAVERSE(&agents, p, list) {
02377 ast_mutex_lock(&p->lock);
02378 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02379 if (p->owner) {
02380 if (res != AST_DEVICE_INUSE)
02381 res = AST_DEVICE_BUSY;
02382 } else {
02383 if (res == AST_DEVICE_BUSY)
02384 res = AST_DEVICE_INUSE;
02385 if (p->chan) {
02386 if (res == AST_DEVICE_INVALID)
02387 res = AST_DEVICE_UNKNOWN;
02388 } else if (res == AST_DEVICE_INVALID)
02389 res = AST_DEVICE_UNAVAILABLE;
02390 }
02391 if (!strcmp(data, p->agent)) {
02392 ast_mutex_unlock(&p->lock);
02393 break;
02394 }
02395 }
02396 ast_mutex_unlock(&p->lock);
02397 }
02398 AST_LIST_UNLOCK(&agents);
02399 return res;
02400 }
02401
02402
02403
02404
02405 static struct agent_pvt *find_agent(char *agentid)
02406 {
02407 struct agent_pvt *cur;
02408
02409 AST_LIST_TRAVERSE(&agents, cur, list) {
02410 if (!strcmp(cur->agent, agentid))
02411 break;
02412 }
02413
02414 return cur;
02415 }
02416
02417 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02418 {
02419 char *parse;
02420 AST_DECLARE_APP_ARGS(args,
02421 AST_APP_ARG(agentid);
02422 AST_APP_ARG(item);
02423 );
02424 char *tmp;
02425 struct agent_pvt *agent;
02426
02427 buf[0] = '\0';
02428
02429 if (ast_strlen_zero(data)) {
02430 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02431 return -1;
02432 }
02433
02434 parse = ast_strdupa(data);
02435
02436 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02437 if (!args.item)
02438 args.item = "status";
02439
02440 AST_LIST_LOCK(&agents);
02441
02442 if (!(agent = find_agent(args.agentid))) {
02443 AST_LIST_UNLOCK(&agents);
02444 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02445 return -1;
02446 }
02447
02448 if (!strcasecmp(args.item, "status")) {
02449 char *status = "LOGGEDOUT";
02450 if (agent->chan) {
02451 status = "LOGGEDIN";
02452 }
02453 ast_copy_string(buf, status, len);
02454 } else if (!strcasecmp(args.item, "password"))
02455 ast_copy_string(buf, agent->password, len);
02456 else if (!strcasecmp(args.item, "name"))
02457 ast_copy_string(buf, agent->name, len);
02458 else if (!strcasecmp(args.item, "mohclass"))
02459 ast_copy_string(buf, agent->moh, len);
02460 else if (!strcasecmp(args.item, "channel")) {
02461 if (agent->chan) {
02462 ast_channel_lock(agent->chan);
02463 ast_copy_string(buf, ast_channel_name(agent->chan), len);
02464 ast_channel_unlock(agent->chan);
02465 tmp = strrchr(buf, '-');
02466 if (tmp)
02467 *tmp = '\0';
02468 }
02469 } else if (!strcasecmp(args.item, "fullchannel")) {
02470 if (agent->chan) {
02471 ast_channel_lock(agent->chan);
02472 ast_copy_string(buf, ast_channel_name(agent->chan), len);
02473 ast_channel_unlock(agent->chan);
02474 }
02475 } else if (!strcasecmp(args.item, "exten")) {
02476 buf[0] = '\0';
02477 }
02478
02479 AST_LIST_UNLOCK(&agents);
02480
02481 return 0;
02482 }
02483
02484 static struct ast_custom_function agent_function = {
02485 .name = "AGENT",
02486 .read = function_agent,
02487 };
02488
02489
02490
02491
02492
02493
02494
02495
02496 static int agents_data_provider_get(const struct ast_data_search *search,
02497 struct ast_data *data_root)
02498 {
02499 struct agent_pvt *p;
02500 struct ast_data *data_agent, *data_channel, *data_talkingto;
02501
02502 AST_LIST_LOCK(&agents);
02503 AST_LIST_TRAVERSE(&agents, p, list) {
02504 struct ast_channel *owner;
02505
02506 data_agent = ast_data_add_node(data_root, "agent");
02507 if (!data_agent) {
02508 continue;
02509 }
02510
02511 ast_mutex_lock(&p->lock);
02512 owner = agent_lock_owner(p);
02513
02514 if (!(p->pending)) {
02515 ast_data_add_str(data_agent, "id", p->agent);
02516 ast_data_add_structure(agent_pvt, data_agent, p);
02517
02518 ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0);
02519 if (p->chan) {
02520 data_channel = ast_data_add_node(data_agent, "loggedon");
02521 if (!data_channel) {
02522 ast_mutex_unlock(&p->lock);
02523 ast_data_remove_node(data_root, data_agent);
02524 if (owner) {
02525 ast_channel_unlock(owner);
02526 owner = ast_channel_unref(owner);
02527 }
02528 continue;
02529 }
02530 ast_channel_data_add_structure(data_channel, p->chan, 0);
02531 if (owner && ast_bridged_channel(owner)) {
02532 data_talkingto = ast_data_add_node(data_agent, "talkingto");
02533 if (!data_talkingto) {
02534 ast_mutex_unlock(&p->lock);
02535 ast_data_remove_node(data_root, data_agent);
02536 if (owner) {
02537 ast_channel_unlock(owner);
02538 owner = ast_channel_unref(owner);
02539 }
02540 continue;
02541 }
02542 ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(owner), 0);
02543 }
02544 } else {
02545 ast_data_add_node(data_agent, "talkingto");
02546 ast_data_add_node(data_agent, "loggedon");
02547 }
02548 ast_data_add_str(data_agent, "musiconhold", p->moh);
02549 }
02550
02551 if (owner) {
02552 ast_channel_unlock(owner);
02553 owner = ast_channel_unref(owner);
02554 }
02555
02556 ast_mutex_unlock(&p->lock);
02557
02558
02559 if (!ast_data_search_match(search, data_agent)) {
02560 ast_data_remove_node(data_root, data_agent);
02561 }
02562 }
02563 AST_LIST_UNLOCK(&agents);
02564
02565 return 0;
02566 }
02567
02568 static const struct ast_data_handler agents_data_provider = {
02569 .version = AST_DATA_HANDLER_VERSION,
02570 .get = agents_data_provider_get
02571 };
02572
02573 static const struct ast_data_entry agents_data_providers[] = {
02574 AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider),
02575 };
02576
02577
02578
02579
02580
02581
02582
02583
02584 static int load_module(void)
02585 {
02586 if (!(agent_tech.capabilities = ast_format_cap_alloc())) {
02587 ast_log(LOG_ERROR, "ast_format_cap_alloc_nolock fail.\n");
02588 return AST_MODULE_LOAD_FAILURE;
02589 }
02590 ast_format_cap_add_all(agent_tech.capabilities);
02591
02592 if (ast_channel_register(&agent_tech)) {
02593 agent_tech.capabilities = ast_format_cap_destroy(agent_tech.capabilities);
02594 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02595 return AST_MODULE_LOAD_FAILURE;
02596 }
02597
02598 if (!read_agent_config(0)) {
02599 agent_tech.capabilities = ast_format_cap_destroy(agent_tech.capabilities);
02600 return AST_MODULE_LOAD_DECLINE;
02601 }
02602
02603 ast_register_application_xml(app, login_exec);
02604 ast_register_application_xml(app3, agentmonitoroutgoing_exec);
02605
02606
02607 ast_data_register_multiple(agents_data_providers, ARRAY_LEN(agents_data_providers));
02608
02609
02610 ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
02611 ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
02612
02613
02614 ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
02615
02616
02617 ast_custom_function_register(&agent_function);
02618
02619 return AST_MODULE_LOAD_SUCCESS;
02620 }
02621
02622 static int reload(void)
02623 {
02624 return read_agent_config(1);
02625 }
02626
02627 static int unload_module(void)
02628 {
02629 struct agent_pvt *p;
02630
02631 ast_channel_unregister(&agent_tech);
02632
02633 ast_custom_function_unregister(&agent_function);
02634
02635 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
02636
02637 ast_unregister_application(app);
02638 ast_unregister_application(app3);
02639
02640 ast_manager_unregister("Agents");
02641 ast_manager_unregister("AgentLogoff");
02642
02643 ast_data_unregister(NULL);
02644
02645 AST_LIST_LOCK(&agents);
02646
02647 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02648 if (p->owner)
02649 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02650 ast_free(p);
02651 }
02652 AST_LIST_UNLOCK(&agents);
02653
02654 agent_tech.capabilities = ast_format_cap_destroy(agent_tech.capabilities);
02655 return 0;
02656 }
02657
02658 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Agent Proxy Channel",
02659 .load = load_module,
02660 .unload = unload_module,
02661 .reload = reload,
02662 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02663 .nonoptreq = "res_monitor,chan_local",
02664 );