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