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 #include "asterisk.h"
00037
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 350364 $")
00039
00040 #include <signal.h>
00041
00042 #include "asterisk/paths.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/translate.h"
00049 #include "asterisk/say.h"
00050 #include "asterisk/features.h"
00051 #include "asterisk/musiconhold.h"
00052 #include "asterisk/cli.h"
00053 #include "asterisk/manager.h"
00054 #include "asterisk/config.h"
00055 #include "asterisk/utils.h"
00056 #include "asterisk/causes.h"
00057 #include "asterisk/astdb.h"
00058 #include "asterisk/dsp.h"
00059 #include "asterisk/app.h"
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
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 static char *app = "FollowMe";
00104
00105
00106 struct number {
00107 char number[512];
00108 long timeout;
00109 int order;
00110 AST_LIST_ENTRY(number) entry;
00111 };
00112
00113
00114 struct call_followme {
00115 ast_mutex_t lock;
00116 char name[AST_MAX_EXTENSION];
00117 char moh[AST_MAX_CONTEXT];
00118 char context[AST_MAX_CONTEXT];
00119 unsigned int active;
00120 int realtime;
00121 char takecall[20];
00122 char nextindp[20];
00123 char callfromprompt[PATH_MAX];
00124 char norecordingprompt[PATH_MAX];
00125 char optionsprompt[PATH_MAX];
00126 char plsholdprompt[PATH_MAX];
00127 char statusprompt[PATH_MAX];
00128 char sorryprompt[PATH_MAX];
00129
00130 AST_LIST_HEAD_NOLOCK(numbers, number) numbers;
00131 AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers;
00132 AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers;
00133 AST_LIST_ENTRY(call_followme) entry;
00134 };
00135
00136 struct fm_args {
00137
00138 struct ast_channel *chan;
00139 char *mohclass;
00140 AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00141
00142 struct ast_channel *outbound;
00143
00144 struct ast_party_connected_line connected_in;
00145
00146 struct ast_party_connected_line connected_out;
00147
00148 int pending_in_connected_update:1;
00149
00150 int pending_out_connected_update:1;
00151 int status;
00152 char context[AST_MAX_CONTEXT];
00153 char namerecloc[AST_MAX_CONTEXT];
00154 char takecall[20];
00155 char nextindp[20];
00156 char callfromprompt[PATH_MAX];
00157 char norecordingprompt[PATH_MAX];
00158 char optionsprompt[PATH_MAX];
00159 char plsholdprompt[PATH_MAX];
00160 char statusprompt[PATH_MAX];
00161 char sorryprompt[PATH_MAX];
00162 struct ast_flags followmeflags;
00163 };
00164
00165 struct findme_user {
00166 struct ast_channel *ochan;
00167
00168 struct ast_party_connected_line connected;
00169 long digts;
00170 int ynidx;
00171 int state;
00172 char dialarg[256];
00173 char yn[10];
00174
00175 int cleared:1;
00176
00177 int pending_connected_update:1;
00178 AST_LIST_ENTRY(findme_user) entry;
00179 };
00180
00181 enum {
00182 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00183 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00184 FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2),
00185 FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3),
00186 FOLLOWMEFLAG_IGNORE_CONNECTEDLINE = (1 << 4),
00187 };
00188
00189 AST_APP_OPTIONS(followme_opts, {
00190 AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME),
00191 AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT),
00192 AST_APP_OPTION('I', FOLLOWMEFLAG_IGNORE_CONNECTEDLINE),
00193 AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG),
00194 AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG),
00195 });
00196
00197 static int ynlongest = 0;
00198
00199 static const char *featuredigittostr;
00200 static int featuredigittimeout = 5000;
00201 static const char *defaultmoh = "default";
00202
00203 static char takecall[20] = "1", nextindp[20] = "2";
00204 static char callfromprompt[PATH_MAX] = "followme/call-from";
00205 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00206 static char optionsprompt[PATH_MAX] = "followme/options";
00207 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00208 static char statusprompt[PATH_MAX] = "followme/status";
00209 static char sorryprompt[PATH_MAX] = "followme/sorry";
00210
00211
00212 static AST_RWLIST_HEAD_STATIC(followmes, call_followme);
00213 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00214
00215 static void free_numbers(struct call_followme *f)
00216 {
00217
00218 struct number *prev;
00219
00220 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00221
00222 ast_free(prev);
00223 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00224
00225 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00226
00227 ast_free(prev);
00228 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00229
00230 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00231
00232 ast_free(prev);
00233 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00234 }
00235
00236
00237
00238 static struct call_followme *alloc_profile(const char *fmname)
00239 {
00240 struct call_followme *f;
00241
00242 if (!(f = ast_calloc(1, sizeof(*f))))
00243 return NULL;
00244
00245 ast_mutex_init(&f->lock);
00246 ast_copy_string(f->name, fmname, sizeof(f->name));
00247 f->moh[0] = '\0';
00248 f->context[0] = '\0';
00249 ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00250 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00251 ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00252 ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00253 ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00254 ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00255 ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00256 ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00257 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00258 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00259 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00260 return f;
00261 }
00262
00263 static void init_profile(struct call_followme *f)
00264 {
00265 f->active = 1;
00266 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00267 }
00268
00269
00270
00271
00272 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
00273 {
00274
00275 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
00276 ast_copy_string(f->moh, val, sizeof(f->moh));
00277 else if (!strcasecmp(param, "context"))
00278 ast_copy_string(f->context, val, sizeof(f->context));
00279 else if (!strcasecmp(param, "takecall"))
00280 ast_copy_string(f->takecall, val, sizeof(f->takecall));
00281 else if (!strcasecmp(param, "declinecall"))
00282 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00283 else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt"))
00284 ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00285 else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))
00286 ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00287 else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))
00288 ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00289 else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt"))
00290 ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00291 else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))
00292 ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00293 else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))
00294 ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00295 else if (failunknown) {
00296 if (linenum >= 0)
00297 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00298 else
00299 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00300 }
00301 }
00302
00303
00304 static struct number *create_followme_number(const char *number, int timeout, int numorder)
00305 {
00306 struct number *cur;
00307 char *buf = ast_strdupa(number);
00308 char *tmp;
00309
00310 if (!(cur = ast_calloc(1, sizeof(*cur))))
00311 return NULL;
00312
00313 cur->timeout = timeout;
00314 if ((tmp = strchr(buf, ',')))
00315 *tmp = '\0';
00316 ast_copy_string(cur->number, buf, sizeof(cur->number));
00317 cur->order = numorder;
00318 ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00319
00320 return cur;
00321 }
00322
00323
00324 static int reload_followme(int reload)
00325 {
00326 struct call_followme *f;
00327 struct ast_config *cfg;
00328 char *cat = NULL, *tmp;
00329 struct ast_variable *var;
00330 struct number *cur, *nm;
00331 char numberstr[90];
00332 int timeout;
00333 int numorder;
00334 const char *takecallstr;
00335 const char *declinecallstr;
00336 const char *tmpstr;
00337 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00338
00339 if (!(cfg = ast_config_load("followme.conf", config_flags))) {
00340 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00341 return 0;
00342 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00343 return 0;
00344 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00345 ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format. Aborting.\n");
00346 return 0;
00347 }
00348
00349 AST_RWLIST_WRLOCK(&followmes);
00350
00351
00352 featuredigittimeout = 5000;
00353
00354
00355 AST_RWLIST_TRAVERSE(&followmes, f, entry) {
00356 f->active = 0;
00357 }
00358
00359 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00360
00361 if (!ast_strlen_zero(featuredigittostr)) {
00362 if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout))
00363 featuredigittimeout = 5000;
00364 }
00365
00366 if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) {
00367 ast_copy_string(takecall, takecallstr, sizeof(takecall));
00368 }
00369
00370 if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) {
00371 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00372 }
00373
00374 if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) {
00375 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00376 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) {
00377 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00378 }
00379
00380 if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) {
00381 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00382 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) {
00383 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00384 }
00385
00386
00387 if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) {
00388 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00389 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) {
00390 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00391 }
00392
00393 if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) {
00394 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00395 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) {
00396 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00397 }
00398
00399 if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) {
00400 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00401 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) {
00402 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00403 }
00404
00405 if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) {
00406 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00407 } else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) {
00408 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00409 }
00410
00411
00412 while ((cat = ast_category_browse(cfg, cat))) {
00413 int new = 0;
00414
00415 if (!strcasecmp(cat, "general"))
00416 continue;
00417
00418
00419 AST_LIST_TRAVERSE(&followmes, f, entry) {
00420 if (!strcasecmp(f->name, cat))
00421 break;
00422 }
00423
00424 ast_debug(1, "New profile %s.\n", cat);
00425
00426 if (!f) {
00427
00428 f = alloc_profile(cat);
00429 new = 1;
00430 }
00431
00432
00433 if (!f)
00434 continue;
00435
00436 if (!new)
00437 ast_mutex_lock(&f->lock);
00438
00439 init_profile(f);
00440 free_numbers(f);
00441 var = ast_variable_browse(cfg, cat);
00442 while (var) {
00443 if (!strcasecmp(var->name, "number")) {
00444 int idx = 0;
00445
00446
00447 ast_copy_string(numberstr, var->value, sizeof(numberstr));
00448 if ((tmp = strchr(numberstr, ','))) {
00449 *tmp++ = '\0';
00450 timeout = atoi(tmp);
00451 if (timeout < 0) {
00452 timeout = 25;
00453 }
00454 if ((tmp = strchr(tmp, ','))) {
00455 *tmp++ = '\0';
00456 numorder = atoi(tmp);
00457 if (numorder < 0)
00458 numorder = 0;
00459 } else
00460 numorder = 0;
00461 } else {
00462 timeout = 25;
00463 numorder = 0;
00464 }
00465
00466 if (!numorder) {
00467 idx = 1;
00468 AST_LIST_TRAVERSE(&f->numbers, nm, entry)
00469 idx++;
00470 numorder = idx;
00471 }
00472 cur = create_followme_number(numberstr, timeout, numorder);
00473 if (cur) {
00474 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00475 }
00476 } else {
00477 profile_set_param(f, var->name, var->value, var->lineno, 1);
00478 ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00479 }
00480 var = var->next;
00481 }
00482
00483 if (!new)
00484 ast_mutex_unlock(&f->lock);
00485 else
00486 AST_RWLIST_INSERT_HEAD(&followmes, f, entry);
00487 }
00488
00489 ast_config_destroy(cfg);
00490
00491 AST_RWLIST_UNLOCK(&followmes);
00492
00493 return 1;
00494 }
00495
00496 static void clear_caller(struct findme_user *tmpuser)
00497 {
00498 struct ast_channel *outbound;
00499
00500 if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00501 outbound = tmpuser->ochan;
00502 ast_channel_lock(outbound);
00503 if (!outbound->cdr) {
00504 outbound->cdr = ast_cdr_alloc();
00505 if (outbound->cdr) {
00506 ast_cdr_init(outbound->cdr, outbound);
00507 }
00508 }
00509 if (outbound->cdr) {
00510 char tmp[256];
00511
00512 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00513 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00514 ast_cdr_update(outbound);
00515 ast_cdr_start(outbound->cdr);
00516 ast_cdr_end(outbound->cdr);
00517
00518 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00519 ast_cdr_failed(outbound->cdr);
00520 }
00521 } else {
00522 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00523 }
00524 ast_channel_unlock(outbound);
00525 ast_hangup(outbound);
00526 tmpuser->ochan = NULL;
00527 }
00528 }
00529
00530 static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
00531 {
00532 struct findme_user *tmpuser;
00533
00534 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00535 clear_caller(tmpuser);
00536 tmpuser->cleared = 1;
00537 }
00538 }
00539
00540 static void destroy_calling_tree(struct findme_user_listptr *findme_user_list)
00541 {
00542 struct findme_user *fmuser;
00543
00544 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00545 if (!fmuser->cleared) {
00546 clear_caller(fmuser);
00547 }
00548 ast_party_connected_line_free(&fmuser->connected);
00549 ast_free(fmuser);
00550 }
00551 ast_free(findme_user_list);
00552 }
00553
00554 static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, struct fm_args *tpargs)
00555 {
00556 struct ast_party_connected_line connected;
00557 struct ast_channel *watchers[256];
00558 int pos;
00559 struct ast_channel *winner;
00560 struct ast_frame *f;
00561 int ctstatus = 0;
00562 int dg;
00563 struct findme_user *tmpuser;
00564 int to = 0;
00565 int livechannels = 0;
00566 int tmpto;
00567 long totalwait = 0, wtd = 0, towas = 0;
00568 char *callfromname;
00569 char *pressbuttonname;
00570
00571
00572
00573 callfromname = ast_strdupa(tpargs->callfromprompt);
00574 pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00575
00576 if (AST_LIST_EMPTY(findme_user_list)) {
00577 ast_verb(3, "couldn't reach at this number.\n");
00578 return NULL;
00579 }
00580
00581 if (!caller) {
00582 ast_verb(3, "Original caller hungup. Cleanup.\n");
00583 clear_calling_tree(findme_user_list);
00584 return NULL;
00585 }
00586
00587 totalwait = nm->timeout * 1000;
00588
00589 while (!ctstatus) {
00590 to = 1000;
00591 pos = 1;
00592 livechannels = 0;
00593 watchers[0] = caller;
00594
00595 dg = 0;
00596 winner = NULL;
00597 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00598 if (tmpuser->state >= 0 && tmpuser->ochan) {
00599 if (tmpuser->state == 3)
00600 tmpuser->digts += (towas - wtd);
00601 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00602 ast_verb(3, "We've been waiting for digits longer than we should have.\n");
00603 if (!ast_strlen_zero(namerecloc)) {
00604 tmpuser->state = 1;
00605 tmpuser->digts = 0;
00606 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00607 ast_sched_runq(tmpuser->ochan->sched);
00608 } else {
00609 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00610 return NULL;
00611 }
00612 } else {
00613 tmpuser->state = 2;
00614 tmpuser->digts = 0;
00615 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00616 ast_sched_runq(tmpuser->ochan->sched);
00617 else {
00618 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00619 return NULL;
00620 }
00621 }
00622 }
00623 if (tmpuser->ochan->stream) {
00624 ast_sched_runq(tmpuser->ochan->sched);
00625 tmpto = ast_sched_wait(tmpuser->ochan->sched);
00626 if (tmpto > 0 && tmpto < to)
00627 to = tmpto;
00628 else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00629 ast_stopstream(tmpuser->ochan);
00630 if (tmpuser->state == 1) {
00631 ast_verb(3, "Playback of the call-from file appears to be done.\n");
00632 if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00633 tmpuser->state = 2;
00634 } else {
00635 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00636 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00637 tmpuser->ynidx = 0;
00638 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00639 tmpuser->state = 3;
00640 else {
00641 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00642 return NULL;
00643 }
00644 }
00645 } else if (tmpuser->state == 2) {
00646 ast_verb(3, "Playback of name file appears to be done.\n");
00647 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00648 tmpuser->ynidx = 0;
00649 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00650 tmpuser->state = 3;
00651 } else {
00652 return NULL;
00653 }
00654 } else if (tmpuser->state == 3) {
00655 ast_verb(3, "Playback of the next step file appears to be done.\n");
00656 tmpuser->digts = 0;
00657 }
00658 }
00659 }
00660 watchers[pos++] = tmpuser->ochan;
00661 livechannels++;
00662 }
00663 }
00664
00665 tmpto = to;
00666 if (to < 0) {
00667 to = 1000;
00668 tmpto = 1000;
00669 }
00670 towas = to;
00671 winner = ast_waitfor_n(watchers, pos, &to);
00672 tmpto -= to;
00673 totalwait -= tmpto;
00674 wtd = to;
00675 if (totalwait <= 0) {
00676 ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00677 clear_calling_tree(findme_user_list);
00678 return NULL;
00679 }
00680 if (winner) {
00681
00682 for (dg = 0; dg < ARRAY_LEN(watchers); ++dg) {
00683 if (winner == watchers[dg]) {
00684 break;
00685 }
00686 }
00687 if (dg) {
00688
00689 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00690 if (tmpuser->ochan == winner) {
00691 break;
00692 }
00693 }
00694 } else {
00695 tmpuser = NULL;
00696 }
00697 f = ast_read(winner);
00698 if (f) {
00699 if (f->frametype == AST_FRAME_CONTROL) {
00700 switch (f->subclass.integer) {
00701 case AST_CONTROL_HANGUP:
00702 ast_verb(3, "%s received a hangup frame.\n", winner->name);
00703 if (f->data.uint32) {
00704 winner->hangupcause = f->data.uint32;
00705 }
00706 if (dg == 0) {
00707 ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
00708 clear_calling_tree(findme_user_list);
00709 ctstatus = -1;
00710 }
00711 break;
00712 case AST_CONTROL_ANSWER:
00713 ast_verb(3, "%s answered %s\n", winner->name, caller->name);
00714
00715 winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00716 caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00717 ast_verb(3, "Starting playback of %s\n", callfromname);
00718 if (dg > 0) {
00719 if (!ast_strlen_zero(namerecloc)) {
00720 if (!ast_streamfile(winner, callfromname, winner->language)) {
00721 ast_sched_runq(winner->sched);
00722 tmpuser->state = 1;
00723 } else {
00724 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00725 ast_frfree(f);
00726 return NULL;
00727 }
00728 } else {
00729 tmpuser->state = 2;
00730 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00731 ast_sched_runq(tmpuser->ochan->sched);
00732 else {
00733 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00734 ast_frfree(f);
00735 return NULL;
00736 }
00737 }
00738 }
00739 break;
00740 case AST_CONTROL_BUSY:
00741 ast_verb(3, "%s is busy\n", winner->name);
00742 break;
00743 case AST_CONTROL_CONGESTION:
00744 ast_verb(3, "%s is circuit-busy\n", winner->name);
00745 break;
00746 case AST_CONTROL_RINGING:
00747 ast_verb(3, "%s is ringing\n", winner->name);
00748 break;
00749 case AST_CONTROL_PROGRESS:
00750 ast_verb(3, "%s is making progress\n", winner->name);
00751 break;
00752 case AST_CONTROL_VIDUPDATE:
00753 ast_verb(3, "%s requested a video update\n", winner->name);
00754 break;
00755 case AST_CONTROL_SRCUPDATE:
00756 ast_verb(3, "%s requested a source update\n", winner->name);
00757 break;
00758 case AST_CONTROL_PROCEEDING:
00759 ast_verb(3, "%s is proceeding\n", winner->name);
00760 break;
00761 case AST_CONTROL_HOLD:
00762 ast_verb(3, "%s placed call on hold\n", winner->name);
00763 break;
00764 case AST_CONTROL_UNHOLD:
00765 ast_verb(3, "%s removed call from hold\n", winner->name);
00766 break;
00767 case AST_CONTROL_OFFHOOK:
00768 case AST_CONTROL_FLASH:
00769
00770 break;
00771 case AST_CONTROL_CONNECTED_LINE:
00772 if (!tmpuser) {
00773
00774
00775
00776
00777 ast_verb(3,
00778 "%s connected line has changed. Saving it until we have a winner.\n",
00779 winner->name);
00780 ast_party_connected_line_set_init(&connected, &tpargs->connected_in);
00781 if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00782 ast_party_connected_line_set(&tpargs->connected_in,
00783 &connected, NULL);
00784 tpargs->pending_in_connected_update = 1;
00785 }
00786 ast_party_connected_line_free(&connected);
00787 break;
00788 }
00789 if (ast_test_flag(&tpargs->followmeflags, FOLLOWMEFLAG_IGNORE_CONNECTEDLINE)) {
00790 ast_verb(3, "Connected line update from %s prevented.\n",
00791 winner->name);
00792 } else {
00793 ast_verb(3,
00794 "%s connected line has changed. Saving it until answer.\n",
00795 winner->name);
00796 ast_party_connected_line_set_init(&connected, &tmpuser->connected);
00797 if (!ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected)) {
00798 ast_party_connected_line_set(&tmpuser->connected,
00799 &connected, NULL);
00800 tmpuser->pending_connected_update = 1;
00801 }
00802 ast_party_connected_line_free(&connected);
00803 }
00804 break;
00805 case AST_CONTROL_REDIRECTING:
00806
00807
00808
00809
00810 break;
00811 case -1:
00812 ast_verb(3, "%s stopped sounds\n", winner->name);
00813 break;
00814 default:
00815 ast_debug(1, "Dunno what to do with control type %d from %s\n",
00816 f->subclass.integer, winner->name);
00817 break;
00818 }
00819 }
00820 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00821 if (winner->stream)
00822 ast_stopstream(winner);
00823 tmpuser->digts = 0;
00824 ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer);
00825 tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer;
00826 tmpuser->ynidx++;
00827 ast_debug(1, "DTMF string: %s\n", tmpuser->yn);
00828 if (tmpuser->ynidx >= ynlongest) {
00829 ast_debug(1, "reached longest possible match - doing evals\n");
00830 if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00831 ast_debug(1, "Match to take the call!\n");
00832 ast_frfree(f);
00833 return tmpuser->ochan;
00834 }
00835 if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00836 ast_debug(1, "Next in dial plan step requested.\n");
00837 ast_frfree(f);
00838 return NULL;
00839 }
00840 }
00841 }
00842
00843 ast_frfree(f);
00844 } else {
00845 ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n", dg);
00846 if (!dg) {
00847
00848 clear_calling_tree(findme_user_list);
00849 return NULL;
00850 } else {
00851
00852 tmpuser->state = -1;
00853 tmpuser->ochan = NULL;
00854 ast_hangup(winner);
00855 --livechannels;
00856 ast_debug(1, "live channels left %d\n", livechannels);
00857 if (!livechannels) {
00858 ast_verb(3, "no live channels left. exiting.\n");
00859 return NULL;
00860 }
00861 }
00862 }
00863 } else {
00864 ast_debug(1, "timed out waiting for action\n");
00865 }
00866 }
00867
00868
00869 return NULL;
00870 }
00871
00872 static void findmeexec(struct fm_args *tpargs)
00873 {
00874 struct number *nm;
00875 struct ast_channel *outbound;
00876 struct ast_channel *caller;
00877 struct ast_channel *winner = NULL;
00878 char dialarg[512];
00879 char num[512];
00880 int dg, idx;
00881 char *rest, *number;
00882 struct findme_user *tmpuser;
00883 struct findme_user *fmuser;
00884 struct findme_user_listptr *findme_user_list;
00885
00886 findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00887 AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00888
00889
00890 ynlongest = 0;
00891 if (strlen(tpargs->takecall) > ynlongest) {
00892 ynlongest = strlen(tpargs->takecall);
00893 }
00894 if (strlen(tpargs->nextindp) > ynlongest) {
00895 ynlongest = strlen(tpargs->nextindp);
00896 }
00897
00898 caller = tpargs->chan;
00899 for (idx = 1; !ast_check_hangup(caller); ++idx) {
00900
00901 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) {
00902 if (nm->order == idx) {
00903 break;
00904 }
00905 }
00906 if (!nm) {
00907 break;
00908 }
00909
00910 ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout);
00911
00912 ast_copy_string(num, nm->number, sizeof(num));
00913 for (number = num; number; number = rest) {
00914 rest = strchr(number, '&');
00915 if (rest) {
00916 *rest++ = 0;
00917 }
00918
00919
00920 if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) {
00921 ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context);
00922 continue;
00923 }
00924
00925 if (!strcmp(tpargs->context, "")) {
00926 snprintf(dialarg, sizeof(dialarg), "%s", number);
00927 } else {
00928 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00929 }
00930
00931 tmpuser = ast_calloc(1, sizeof(*tmpuser));
00932 if (!tmpuser) {
00933 continue;
00934 }
00935
00936 outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg);
00937 if (outbound) {
00938 ast_channel_lock_both(caller, outbound);
00939 ast_connected_line_copy_from_caller(&outbound->connected, &caller->caller);
00940 ast_channel_inherit_variables(caller, outbound);
00941 ast_channel_datastore_inherit(caller, outbound);
00942 ast_string_field_set(outbound, language, caller->language);
00943 ast_string_field_set(outbound, accountcode, caller->accountcode);
00944 ast_string_field_set(outbound, musicclass, caller->musicclass);
00945 ast_channel_unlock(outbound);
00946 ast_channel_unlock(caller);
00947 ast_verb(3, "calling Local/%s\n", dialarg);
00948 if (!ast_call(outbound, dialarg, 0)) {
00949 tmpuser->ochan = outbound;
00950 tmpuser->state = 0;
00951 tmpuser->cleared = 0;
00952 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00953 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00954 } else {
00955 ast_verb(3, "couldn't reach at this number.\n");
00956 ast_channel_lock(outbound);
00957 if (!outbound->cdr) {
00958 outbound->cdr = ast_cdr_alloc();
00959 }
00960 if (outbound->cdr) {
00961 char tmp[256];
00962
00963 ast_cdr_init(outbound->cdr, outbound);
00964 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00965 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00966 ast_cdr_update(outbound);
00967 ast_cdr_start(outbound->cdr);
00968 ast_cdr_end(outbound->cdr);
00969
00970 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) {
00971 ast_cdr_failed(outbound->cdr);
00972 }
00973 } else {
00974 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00975 }
00976 ast_channel_unlock(outbound);
00977 ast_hangup(outbound);
00978 ast_free(tmpuser);
00979 }
00980 } else {
00981 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00982 ast_free(tmpuser);
00983 }
00984 }
00985
00986 if (AST_LIST_EMPTY(findme_user_list)) {
00987 continue;
00988 }
00989
00990 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, tpargs);
00991 if (!winner) {
00992 continue;
00993 }
00994
00995
00996 while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) {
00997 if (fmuser->ochan == winner) {
00998
00999 tpargs->connected_out = fmuser->connected;
01000 tpargs->pending_out_connected_update = fmuser->pending_connected_update;
01001 ast_free(fmuser);
01002 break;
01003 } else {
01004
01005 if (!fmuser->cleared) {
01006 clear_caller(fmuser);
01007 }
01008 ast_party_connected_line_free(&fmuser->connected);
01009 ast_free(fmuser);
01010 }
01011 }
01012 break;
01013 }
01014 destroy_calling_tree(findme_user_list);
01015 if (!winner) {
01016 tpargs->status = 1;
01017 } else {
01018 tpargs->status = 100;
01019 tpargs->outbound = winner;
01020 }
01021 }
01022
01023 static struct call_followme *find_realtime(const char *name)
01024 {
01025 struct ast_variable *var;
01026 struct ast_variable *v;
01027 struct ast_config *cfg;
01028 const char *catg;
01029 struct call_followme *new_follower;
01030 struct ast_str *str;
01031
01032 str = ast_str_create(16);
01033 if (!str) {
01034 return NULL;
01035 }
01036
01037 var = ast_load_realtime("followme", "name", name, SENTINEL);
01038 if (!var) {
01039 ast_free(str);
01040 return NULL;
01041 }
01042
01043 if (!(new_follower = alloc_profile(name))) {
01044 ast_variables_destroy(var);
01045 ast_free(str);
01046 return NULL;
01047 }
01048
01049 for (v = var; v; v = v->next) {
01050 if (!strcasecmp(v->name, "active")) {
01051 if (ast_false(v->value)) {
01052 ast_mutex_destroy(&new_follower->lock);
01053 ast_free(new_follower);
01054 ast_variables_destroy(var);
01055 ast_free(str);
01056 return NULL;
01057 }
01058 } else {
01059 profile_set_param(new_follower, v->name, v->value, 0, 0);
01060 }
01061 }
01062
01063 ast_variables_destroy(var);
01064 new_follower->realtime = 1;
01065
01066
01067 cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name",
01068 name, SENTINEL);
01069 if (!cfg) {
01070 ast_mutex_destroy(&new_follower->lock);
01071 ast_free(new_follower);
01072 ast_free(str);
01073 return NULL;
01074 }
01075
01076 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
01077 const char *numstr;
01078 const char *timeoutstr;
01079 const char *ordstr;
01080 int timeout;
01081 struct number *cur;
01082
01083 if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) {
01084 continue;
01085 }
01086 if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout"))
01087 || sscanf(timeoutstr, "%30d", &timeout) != 1
01088 || timeout < 1) {
01089 timeout = 25;
01090 }
01091
01092 ordstr = ast_variable_retrieve(cfg, catg, "ordinal");
01093 ast_str_set(&str, 0, "%s", numstr);
01094 if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) {
01095 AST_LIST_INSERT_TAIL(&new_follower->numbers, cur, entry);
01096 }
01097 }
01098 ast_config_destroy(cfg);
01099
01100 ast_free(str);
01101 return new_follower;
01102 }
01103
01104 static void end_bridge_callback(void *data)
01105 {
01106 char buf[80];
01107 time_t end;
01108 struct ast_channel *chan = data;
01109
01110 time(&end);
01111
01112 ast_channel_lock(chan);
01113 if (chan->cdr->answer.tv_sec) {
01114 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec);
01115 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
01116 }
01117
01118 if (chan->cdr->start.tv_sec) {
01119 snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec);
01120 pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
01121 }
01122 ast_channel_unlock(chan);
01123 }
01124
01125 static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
01126 {
01127 bconfig->end_bridge_callback_data = originator;
01128 }
01129
01130 static int app_exec(struct ast_channel *chan, const char *data)
01131 {
01132 struct fm_args targs = { 0, };
01133 struct ast_bridge_config config;
01134 struct call_followme *f;
01135 struct number *nm, *newnm;
01136 int res = 0;
01137 char *argstr;
01138 char namerecloc[255];
01139 int duration = 0;
01140 struct ast_channel *caller;
01141 struct ast_channel *outbound;
01142 AST_DECLARE_APP_ARGS(args,
01143 AST_APP_ARG(followmeid);
01144 AST_APP_ARG(options);
01145 );
01146
01147 if (ast_strlen_zero(data)) {
01148 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01149 return -1;
01150 }
01151
01152 argstr = ast_strdupa((char *) data);
01153
01154 AST_STANDARD_APP_ARGS(args, argstr);
01155
01156 if (ast_strlen_zero(args.followmeid)) {
01157 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app);
01158 return -1;
01159 }
01160
01161 AST_RWLIST_RDLOCK(&followmes);
01162 AST_RWLIST_TRAVERSE(&followmes, f, entry) {
01163 if (!strcasecmp(f->name, args.followmeid) && (f->active))
01164 break;
01165 }
01166 AST_RWLIST_UNLOCK(&followmes);
01167
01168 ast_debug(1, "New profile %s.\n", args.followmeid);
01169
01170 if (!f) {
01171 f = find_realtime(args.followmeid);
01172 }
01173
01174 if (!f) {
01175 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
01176 return 0;
01177 }
01178
01179
01180 if (args.options)
01181 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
01182
01183
01184 ast_mutex_lock(&f->lock);
01185 targs.mohclass = ast_strdupa(f->moh);
01186 ast_copy_string(targs.context, f->context, sizeof(targs.context));
01187 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
01188 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
01189 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
01190 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
01191 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
01192 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
01193 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
01194 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
01195
01196
01197 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
01198 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
01199 newnm = create_followme_number(nm->number, nm->timeout, nm->order);
01200 if (newnm) {
01201 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
01202 }
01203 }
01204 ast_mutex_unlock(&f->lock);
01205
01206
01207 if (chan->_state != AST_STATE_UP) {
01208 ast_answer(chan);
01209 }
01210
01211 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
01212 ast_stream_and_wait(chan, targs.statusprompt, "");
01213
01214 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
01215 duration = 5;
01216
01217 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
01218 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0)
01219 goto outrun;
01220
01221 if (!ast_fileexists(namerecloc, NULL, chan->language))
01222 ast_copy_string(namerecloc, "", sizeof(namerecloc));
01223 if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) {
01224 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01225 goto outrun;
01226 if (ast_waitstream(chan, "") < 0)
01227 goto outrun;
01228 }
01229 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01230
01231 targs.status = 0;
01232 targs.chan = chan;
01233 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01234 ast_channel_lock(chan);
01235 ast_connected_line_copy_from_caller(&targs.connected_in, &chan->caller);
01236 ast_channel_unlock(chan);
01237
01238 findmeexec(&targs);
01239
01240 while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry)))
01241 ast_free(nm);
01242
01243 if (!ast_strlen_zero(namerecloc))
01244 unlink(namerecloc);
01245
01246 if (targs.status != 100) {
01247 ast_moh_stop(chan);
01248 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
01249 ast_stream_and_wait(chan, targs.sorryprompt, "");
01250 res = 0;
01251 } else {
01252 caller = chan;
01253 outbound = targs.outbound;
01254
01255
01256 memset(&config, 0, sizeof(config));
01257 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01258 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01259 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01260 config.end_bridge_callback = end_bridge_callback;
01261 config.end_bridge_callback_data = chan;
01262 config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
01263
01264 ast_moh_stop(caller);
01265
01266 ast_deactivate_generator(caller);
01267
01268 res = ast_channel_make_compatible(caller, outbound);
01269 if (res < 0) {
01270 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01271 ast_hangup(outbound);
01272 goto outrun;
01273 }
01274
01275
01276 if (targs.pending_out_connected_update) {
01277 if (ast_channel_connected_line_macro(outbound, caller, &targs.connected_out, 1, 0)) {
01278 ast_channel_update_connected_line(caller, &targs.connected_out, NULL);
01279 }
01280 }
01281
01282
01283 if (targs.pending_in_connected_update) {
01284 if (ast_channel_connected_line_macro(caller, outbound, &targs.connected_in, 0, 0)) {
01285 ast_channel_update_connected_line(outbound, &targs.connected_in, NULL);
01286 }
01287 }
01288
01289 res = ast_bridge_call(caller, outbound, &config);
01290 ast_hangup(outbound);
01291 }
01292
01293 outrun:
01294 ast_party_connected_line_free(&targs.connected_in);
01295 ast_party_connected_line_free(&targs.connected_out);
01296 if (f->realtime) {
01297
01298 free_numbers(f);
01299 ast_free(f);
01300 }
01301
01302 return res;
01303 }
01304
01305 static int unload_module(void)
01306 {
01307 struct call_followme *f;
01308
01309 ast_unregister_application(app);
01310
01311
01312 AST_RWLIST_WRLOCK(&followmes);
01313 while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) {
01314 free_numbers(f);
01315 ast_free(f);
01316 }
01317
01318 AST_RWLIST_UNLOCK(&followmes);
01319
01320 return 0;
01321 }
01322
01323 static int load_module(void)
01324 {
01325 if(!reload_followme(0))
01326 return AST_MODULE_LOAD_DECLINE;
01327
01328 return ast_register_application_xml(app, app_exec);
01329 }
01330
01331 static int reload(void)
01332 {
01333 reload_followme(1);
01334
01335 return 0;
01336 }
01337
01338 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01339 .load = load_module,
01340 .unload = unload_module,
01341 .reload = reload,
01342 );