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