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 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00038
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 #include <string.h>
00043 #include <signal.h>
00044
00045 #include "asterisk/lock.h"
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/options.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/translate.h"
00053 #include "asterisk/say.h"
00054 #include "asterisk/features.h"
00055 #include "asterisk/musiconhold.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/config.h"
00059 #include "asterisk/monitor.h"
00060 #include "asterisk/utils.h"
00061 #include "asterisk/causes.h"
00062 #include "asterisk/astdb.h"
00063 #include "asterisk/app.h"
00064
00065 static char *app = "FollowMe";
00066 static char *synopsis = "Find-Me/Follow-Me application";
00067 static char *descrip =
00068 " FollowMe(followmeid|options):\n"
00069 "This application performs Find-Me/Follow-Me functionality for the caller\n"
00070 "as defined in the profile matching the <followmeid> parameter in\n"
00071 "followme.conf. If the specified <followmeid> profile doesn't exist in\n"
00072 "followme.conf, execution will be returned to the dialplan and call\n"
00073 "execution will continue at the next priority.\n\n"
00074 " Options:\n"
00075 " s - Playback the incoming status message prior to starting the follow-me step(s)\n"
00076 " a - Record the caller's name so it can be announced to the callee on each step\n"
00077 " n - Playback the unreachable status message if we've run out of steps to reach the\n"
00078 " or the callee has elected not to be reachable.\n"
00079 "Returns -1 on hangup\n";
00080
00081
00082 struct number {
00083 char number[512];
00084 long timeout;
00085 char language[MAX_LANGUAGE];
00086 int order;
00087 AST_LIST_ENTRY(number) entry;
00088 };
00089
00090
00091 struct call_followme {
00092 ast_mutex_t lock;
00093 char name[AST_MAX_EXTENSION];
00094 char moh[AST_MAX_CONTEXT];
00095 char context[AST_MAX_CONTEXT];
00096 unsigned int active;
00097 char takecall[20];
00098 char nextindp[20];
00099 char callfromprompt[PATH_MAX];
00100 char norecordingprompt[PATH_MAX];
00101 char optionsprompt[PATH_MAX];
00102 char plsholdprompt[PATH_MAX];
00103 char statusprompt[PATH_MAX];
00104 char sorryprompt[PATH_MAX];
00105
00106 AST_LIST_HEAD_NOLOCK(numbers, number) numbers;
00107 AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers;
00108 AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers;
00109 AST_LIST_ENTRY(call_followme) entry;
00110 };
00111
00112 struct fm_args {
00113 struct ast_channel *chan;
00114 char *mohclass;
00115 AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers;
00116 int status;
00117 char context[AST_MAX_CONTEXT];
00118 char namerecloc[AST_MAX_CONTEXT];
00119 struct ast_channel *outbound;
00120 char takecall[20];
00121 char nextindp[20];
00122 char callfromprompt[PATH_MAX];
00123 char norecordingprompt[PATH_MAX];
00124 char optionsprompt[PATH_MAX];
00125 char plsholdprompt[PATH_MAX];
00126 char statusprompt[PATH_MAX];
00127 char sorryprompt[PATH_MAX];
00128 struct ast_flags followmeflags;
00129 };
00130
00131 struct findme_user {
00132 struct ast_channel *ochan;
00133 int state;
00134 char dialarg[256];
00135 char yn[10];
00136 int ynidx;
00137 long digts;
00138 int cleared;
00139 AST_LIST_ENTRY(findme_user) entry;
00140 };
00141
00142 enum {
00143 FOLLOWMEFLAG_STATUSMSG = (1 << 0),
00144 FOLLOWMEFLAG_RECORDNAME = (1 << 1),
00145 FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2)
00146 };
00147
00148 AST_APP_OPTIONS(followme_opts, {
00149 AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ),
00150 AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ),
00151 AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ),
00152 });
00153
00154 static int ynlongest = 0;
00155 static time_t start_time, answer_time, end_time;
00156
00157 static const char *featuredigittostr;
00158 static int featuredigittimeout = 5000;
00159 static const char *defaultmoh = "default";
00160
00161 static char takecall[20] = "1", nextindp[20] = "2";
00162 static char callfromprompt[PATH_MAX] = "followme/call-from";
00163 static char norecordingprompt[PATH_MAX] = "followme/no-recording";
00164 static char optionsprompt[PATH_MAX] = "followme/options";
00165 static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try";
00166 static char statusprompt[PATH_MAX] = "followme/status";
00167 static char sorryprompt[PATH_MAX] = "followme/sorry";
00168
00169
00170 static AST_LIST_HEAD_STATIC(followmes, call_followme);
00171 AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user);
00172
00173 static void free_numbers(struct call_followme *f)
00174 {
00175
00176 struct number *prev;
00177
00178 while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry)))
00179
00180 free(prev);
00181 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00182
00183 while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry)))
00184
00185 free(prev);
00186 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00187
00188 while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry)))
00189
00190 free(prev);
00191 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00192
00193 }
00194
00195
00196
00197 static struct call_followme *alloc_profile(const char *fmname)
00198 {
00199 struct call_followme *f;
00200
00201 if (!(f = ast_calloc(1, sizeof(*f))))
00202 return NULL;
00203
00204 ast_mutex_init(&f->lock);
00205 ast_copy_string(f->name, fmname, sizeof(f->name));
00206 f->moh[0] = '\0';
00207 f->context[0] = '\0';
00208 ast_copy_string(f->takecall, takecall, sizeof(f->takecall));
00209 ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp));
00210 ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt));
00211 ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt));
00212 ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt));
00213 ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt));
00214 ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt));
00215 ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt));
00216 AST_LIST_HEAD_INIT_NOLOCK(&f->numbers);
00217 AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers);
00218 AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers);
00219 return f;
00220 }
00221
00222 static void init_profile(struct call_followme *f)
00223 {
00224 f->active = 1;
00225 ast_copy_string(f->moh, defaultmoh, sizeof(f->moh));
00226 }
00227
00228
00229
00230
00231 static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown)
00232 {
00233
00234 if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))
00235 ast_copy_string(f->moh, val, sizeof(f->moh));
00236 else if (!strcasecmp(param, "context"))
00237 ast_copy_string(f->context, val, sizeof(f->context));
00238 else if (!strcasecmp(param, "takecall"))
00239 ast_copy_string(f->takecall, val, sizeof(f->takecall));
00240 else if (!strcasecmp(param, "declinecall"))
00241 ast_copy_string(f->nextindp, val, sizeof(f->nextindp));
00242 else if (!strcasecmp(param, "call-from-prompt"))
00243 ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt));
00244 else if (!strcasecmp(param, "followme-norecording-prompt"))
00245 ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt));
00246 else if (!strcasecmp(param, "followme-options-prompt"))
00247 ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt));
00248 else if (!strcasecmp(param, "followme-pls-hold-prompt"))
00249 ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt));
00250 else if (!strcasecmp(param, "followme-status-prompt"))
00251 ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt));
00252 else if (!strcasecmp(param, "followme-sorry-prompt"))
00253 ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt));
00254 else if (failunknown) {
00255 if (linenum >= 0)
00256 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum);
00257 else
00258 ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param);
00259 }
00260 }
00261
00262
00263 static struct number *create_followme_number(char *number, char *language, int timeout, int numorder)
00264 {
00265 struct number *cur;
00266 char *tmp;
00267
00268
00269 if (!(cur = ast_calloc(1, sizeof(*cur))))
00270 return NULL;
00271
00272 cur->timeout = timeout;
00273 if ((tmp = strchr(number, ',')))
00274 *tmp = '\0';
00275 ast_copy_string(cur->number, number, sizeof(cur->number));
00276 ast_copy_string(cur->language, language, sizeof(cur->language));
00277 cur->order = numorder;
00278 if (option_debug)
00279 ast_log(LOG_DEBUG, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout);
00280
00281 return cur;
00282 }
00283
00284
00285 static int reload_followme(void)
00286 {
00287 struct call_followme *f;
00288 struct ast_config *cfg;
00289 char *cat = NULL, *tmp;
00290 struct ast_variable *var;
00291 struct number *cur, *nm;
00292 int new, idx;
00293 char numberstr[90];
00294 int timeout;
00295 char *timeoutstr;
00296 int numorder;
00297 const char *takecallstr;
00298 const char *declinecallstr;
00299 const char *tmpstr;
00300
00301 cfg = ast_config_load("followme.conf");
00302 if (!cfg) {
00303 ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n");
00304 return 0;
00305 }
00306
00307 AST_LIST_LOCK(&followmes);
00308
00309
00310 featuredigittimeout = 5000;
00311
00312
00313 AST_LIST_TRAVERSE(&followmes, f, entry) {
00314 f->active = 0;
00315 }
00316 featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout");
00317
00318 if (!ast_strlen_zero(featuredigittostr)) {
00319 if (!sscanf(featuredigittostr, "%d", &featuredigittimeout))
00320 featuredigittimeout = 5000;
00321 }
00322
00323 takecallstr = ast_variable_retrieve(cfg, "general", "takecall");
00324 if (!ast_strlen_zero(takecallstr))
00325 ast_copy_string(takecall, takecallstr, sizeof(takecall));
00326
00327 declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall");
00328 if (!ast_strlen_zero(declinecallstr))
00329 ast_copy_string(nextindp, declinecallstr, sizeof(nextindp));
00330
00331 tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt");
00332 if (!ast_strlen_zero(tmpstr))
00333 ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt));
00334
00335 tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt");
00336 if (!ast_strlen_zero(tmpstr))
00337 ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt));
00338
00339 tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt");
00340 if (!ast_strlen_zero(tmpstr))
00341 ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt));
00342
00343 tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt");
00344 if (!ast_strlen_zero(tmpstr))
00345 ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt));
00346
00347 tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt");
00348 if (!ast_strlen_zero(tmpstr))
00349 ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt));
00350
00351 tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt");
00352 if (!ast_strlen_zero(tmpstr))
00353 ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt));
00354
00355
00356 while ((cat = ast_category_browse(cfg, cat))) {
00357 if (!strcasecmp(cat, "general"))
00358 continue;
00359
00360
00361 AST_LIST_TRAVERSE(&followmes, f, entry) {
00362 if (!strcasecmp(f->name, cat))
00363 break;
00364 }
00365 if (option_debug)
00366 ast_log(LOG_DEBUG, "New profile %s.\n", cat);
00367 if (!f) {
00368
00369 f = alloc_profile(cat);
00370 new = 1;
00371 } else
00372 new = 0;
00373
00374 if (f) {
00375 if (!new)
00376 ast_mutex_lock(&f->lock);
00377
00378 init_profile(f);
00379 free_numbers(f);
00380 var = ast_variable_browse(cfg, cat);
00381 while(var) {
00382 if (!strcasecmp(var->name, "number")) {
00383
00384 ast_copy_string(numberstr, var->value, sizeof(numberstr));
00385 if ((tmp = strchr(numberstr, ','))) {
00386 *tmp = '\0';
00387 tmp++;
00388 timeoutstr = ast_strdupa(tmp);
00389 if ((tmp = strchr(timeoutstr, ','))) {
00390 *tmp = '\0';
00391 tmp++;
00392 numorder = atoi(tmp);
00393 if (numorder < 0)
00394 numorder = 0;
00395 } else
00396 numorder = 0;
00397 timeout = atoi(timeoutstr);
00398 if (timeout < 0)
00399 timeout = 25;
00400 } else {
00401 timeout = 25;
00402 numorder = 0;
00403 }
00404
00405 if (!numorder) {
00406 idx = 1;
00407 AST_LIST_TRAVERSE(&f->numbers, nm, entry)
00408 idx++;
00409 numorder = idx;
00410 }
00411 cur = create_followme_number(numberstr, "", timeout, numorder);
00412 AST_LIST_INSERT_TAIL(&f->numbers, cur, entry);
00413 } else {
00414 profile_set_param(f, var->name, var->value, var->lineno, 1);
00415 if (option_debug > 1)
00416 ast_log(LOG_DEBUG, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno);
00417 }
00418 var = var->next;
00419 }
00420
00421 if (!new)
00422 ast_mutex_unlock(&f->lock);
00423 else
00424 AST_LIST_INSERT_HEAD(&followmes, f, entry);
00425 }
00426 }
00427 ast_config_destroy(cfg);
00428
00429 AST_LIST_UNLOCK(&followmes);
00430
00431 return 1;
00432 }
00433
00434 static void clear_caller(struct findme_user *tmpuser)
00435 {
00436 struct ast_channel *outbound;
00437
00438 if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) {
00439 outbound = tmpuser->ochan;
00440 if (!outbound->cdr) {
00441 outbound->cdr = ast_cdr_alloc();
00442 if (outbound->cdr)
00443 ast_cdr_init(outbound->cdr, outbound);
00444 }
00445 if (outbound->cdr) {
00446 char tmp[256];
00447
00448 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg);
00449 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00450 ast_cdr_update(outbound);
00451 ast_cdr_start(outbound->cdr);
00452 ast_cdr_end(outbound->cdr);
00453
00454 if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause))
00455 ast_cdr_failed(outbound->cdr);
00456 } else
00457 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
00458 ast_hangup(tmpuser->ochan);
00459 }
00460
00461 }
00462
00463 static void clear_calling_tree(struct findme_user_listptr *findme_user_list)
00464 {
00465 struct findme_user *tmpuser;
00466
00467 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00468 clear_caller(tmpuser);
00469 tmpuser->cleared = 1;
00470 }
00471
00472 }
00473
00474
00475
00476 static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_user_list, struct number *nm, struct ast_channel *caller, char *namerecloc, int *status, struct fm_args *tpargs)
00477 {
00478 struct ast_channel *watchers[256];
00479 int pos;
00480 struct ast_channel *winner;
00481 struct ast_frame *f;
00482 int ctstatus;
00483 int dg;
00484 struct findme_user *tmpuser;
00485 int to = 0;
00486 int livechannels = 0;
00487 int tmpto;
00488 long totalwait = 0, wtd, towas = 0;
00489 char *callfromname;
00490 char *pressbuttonname;
00491
00492
00493
00494 callfromname = ast_strdupa(tpargs->callfromprompt);
00495 pressbuttonname = ast_strdupa(tpargs->optionsprompt);
00496
00497 if (!AST_LIST_EMPTY(findme_user_list)) {
00498 if (!caller) {
00499 if (option_verbose > 2)
00500 ast_verbose(VERBOSE_PREFIX_3 "Original caller hungup. Cleanup.\n");
00501 clear_calling_tree(findme_user_list);
00502 return NULL;
00503 }
00504 ctstatus = 0;
00505 totalwait = nm->timeout * 1000;
00506 wtd = 0;
00507 while (!ctstatus) {
00508 to = 1000;
00509 pos = 1;
00510 livechannels = 0;
00511 watchers[0] = caller;
00512
00513 dg = 0;
00514 winner = NULL;
00515 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) {
00516 if (tmpuser->state >= 0 && tmpuser->ochan) {
00517 if (tmpuser->state == 3)
00518 tmpuser->digts += (towas - wtd);
00519 if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) {
00520 if (option_verbose > 2)
00521 ast_verbose(VERBOSE_PREFIX_3 "We've been waiting for digits longer than we should have.\n");
00522 if (!ast_strlen_zero(namerecloc)) {
00523 tmpuser->state = 1;
00524 tmpuser->digts = 0;
00525 if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) {
00526 ast_sched_runq(tmpuser->ochan->sched);
00527 } else {
00528 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00529 return NULL;
00530 }
00531 } else {
00532 tmpuser->state = 2;
00533 tmpuser->digts = 0;
00534 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00535 ast_sched_runq(tmpuser->ochan->sched);
00536 else {
00537 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00538 return NULL;
00539 }
00540 }
00541 }
00542 if (tmpuser->ochan->stream) {
00543 ast_sched_runq(tmpuser->ochan->sched);
00544 tmpto = ast_sched_wait(tmpuser->ochan->sched);
00545 if (tmpto > 0 && tmpto < to)
00546 to = tmpto;
00547 else if (tmpto < 0 && !tmpuser->ochan->timingfunc) {
00548 ast_stopstream(tmpuser->ochan);
00549 if (tmpuser->state == 1) {
00550 if (option_verbose > 2)
00551 ast_verbose(VERBOSE_PREFIX_3 "Playback of the call-from file appears to be done.\n");
00552 if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) {
00553 tmpuser->state = 2;
00554 } else {
00555 ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc);
00556 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00557 tmpuser->ynidx = 0;
00558 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language))
00559 tmpuser->state = 3;
00560 else {
00561 ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname);
00562 return NULL;
00563 }
00564 }
00565 } else if (tmpuser->state == 2) {
00566 if (option_verbose > 2)
00567 ast_verbose(VERBOSE_PREFIX_3 "Playback of name file appears to be done.\n");
00568 memset(tmpuser->yn, 0, sizeof(tmpuser->yn));
00569 tmpuser->ynidx = 0;
00570 if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) {
00571 tmpuser->state = 3;
00572
00573 } else {
00574 return NULL;
00575 }
00576 } else if (tmpuser->state == 3) {
00577 if (option_verbose > 2)
00578 ast_verbose(VERBOSE_PREFIX_3 "Playback of the next step file appears to be done.\n");
00579 tmpuser->digts = 0;
00580 }
00581 }
00582 }
00583 watchers[pos++] = tmpuser->ochan;
00584 livechannels++;
00585 }
00586 }
00587
00588 tmpto = to;
00589 if (to < 0) {
00590 to = 1000;
00591 tmpto = 1000;
00592 }
00593 towas = to;
00594 winner = ast_waitfor_n(watchers, pos, &to);
00595 tmpto -= to;
00596 totalwait -= tmpto;
00597 wtd = to;
00598 if (totalwait <= 0) {
00599 if (option_verbose > 2)
00600 ast_verbose(VERBOSE_PREFIX_3 "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait);
00601 clear_calling_tree(findme_user_list);
00602 return NULL;
00603 }
00604 if (winner) {
00605
00606 dg = 0;
00607 while ((winner != watchers[dg]) && (dg < 256))
00608 dg++;
00609 AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry)
00610 if (tmpuser->ochan == winner)
00611 break;
00612 f = ast_read(winner);
00613 if (f) {
00614 if (f->frametype == AST_FRAME_CONTROL) {
00615 switch(f->subclass) {
00616 case AST_CONTROL_HANGUP:
00617 if (option_verbose > 2)
00618 ast_verbose( VERBOSE_PREFIX_3 "%s received a hangup frame.\n", winner->name);
00619 if (dg == 0) {
00620 if (option_verbose > 2)
00621 ast_verbose( VERBOSE_PREFIX_3 "The calling channel hungup. Need to drop everyone else.\n");
00622 clear_calling_tree(findme_user_list);
00623 ctstatus = -1;
00624 }
00625 break;
00626 case AST_CONTROL_ANSWER:
00627 if (option_verbose > 2)
00628 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", winner->name, caller->name);
00629
00630 winner->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00631 caller->hangupcause = AST_CAUSE_NORMAL_CLEARING;
00632 if (option_verbose > 2)
00633 ast_verbose( VERBOSE_PREFIX_3 "Starting playback of %s\n", callfromname);
00634 if (dg > 0) {
00635 if (!ast_strlen_zero(namerecloc)) {
00636 if (!ast_streamfile(winner, callfromname, winner->language)) {
00637 ast_sched_runq(winner->sched);
00638 tmpuser->state = 1;
00639 } else {
00640 ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname);
00641 ast_frfree(f);
00642 return NULL;
00643 }
00644 } else {
00645 tmpuser->state = 2;
00646 if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language))
00647 ast_sched_runq(tmpuser->ochan->sched);
00648 else {
00649 ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt);
00650 ast_frfree(f);
00651 return NULL;
00652 }
00653 }
00654 }
00655 break;
00656 case AST_CONTROL_BUSY:
00657 if (option_verbose > 2)
00658 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", winner->name);
00659 break;
00660 case AST_CONTROL_CONGESTION:
00661 if (option_verbose > 2)
00662 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", winner->name);
00663 break;
00664 case AST_CONTROL_RINGING:
00665 if (option_verbose > 2)
00666 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", winner->name);
00667 break;
00668 case AST_CONTROL_PROGRESS:
00669 if (option_verbose > 2)
00670 ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", winner->name, caller->name);
00671 break;
00672 case AST_CONTROL_VIDUPDATE:
00673 if (option_verbose > 2)
00674 ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", winner->name, caller->name);
00675 break;
00676 case AST_CONTROL_SRCUPDATE:
00677 if (option_verbose > 2)
00678 ast_verbose ( VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", winner->name, caller->name);
00679 break;
00680 case AST_CONTROL_PROCEEDING:
00681 if (option_verbose > 2)
00682 ast_verbose ( VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", winner->name,caller->name);
00683 break;
00684 case AST_CONTROL_HOLD:
00685 if (option_verbose > 2)
00686 ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", winner->name);
00687 break;
00688 case AST_CONTROL_UNHOLD:
00689 if (option_verbose > 2)
00690 ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", winner->name);
00691 break;
00692 case AST_CONTROL_OFFHOOK:
00693 case AST_CONTROL_FLASH:
00694
00695 break;
00696 case -1:
00697 if (option_verbose > 2)
00698 ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", winner->name);
00699 break;
00700 default:
00701 if (option_debug)
00702 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
00703 break;
00704 }
00705 }
00706 if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
00707 if (winner->stream)
00708 ast_stopstream(winner);
00709 tmpuser->digts = 0;
00710 if (option_debug)
00711 ast_log(LOG_DEBUG, "DTMF received: %c\n",(char) f->subclass);
00712 tmpuser->yn[tmpuser->ynidx] = (char) f->subclass;
00713 tmpuser->ynidx++;
00714 if (option_debug)
00715 ast_log(LOG_DEBUG, "DTMF string: %s\n", tmpuser->yn);
00716 if (tmpuser->ynidx >= ynlongest) {
00717 if (option_debug)
00718 ast_log(LOG_DEBUG, "reached longest possible match - doing evals\n");
00719 if (!strcmp(tmpuser->yn, tpargs->takecall)) {
00720 if (option_debug)
00721 ast_log(LOG_DEBUG, "Match to take the call!\n");
00722 ast_frfree(f);
00723 return tmpuser->ochan;
00724 }
00725 if (!strcmp(tmpuser->yn, tpargs->nextindp)) {
00726 if (option_debug)
00727 ast_log(LOG_DEBUG, "Next in dial plan step requested.\n");
00728 *status = 1;
00729 ast_frfree(f);
00730 return NULL;
00731 }
00732
00733 }
00734 }
00735
00736 ast_frfree(f);
00737 } else {
00738 if (winner) {
00739 if (option_debug)
00740 ast_log(LOG_DEBUG, "we didn't get a frame. hanging up. dg is %d\n",dg);
00741 if (!dg) {
00742 clear_calling_tree(findme_user_list);
00743 return NULL;
00744 } else {
00745 tmpuser->state = -1;
00746 ast_hangup(winner);
00747 livechannels--;
00748 if (option_debug)
00749 ast_log(LOG_DEBUG, "live channels left %d\n", livechannels);
00750 if (!livechannels) {
00751 if (option_verbose > 2)
00752 ast_verbose(VERBOSE_PREFIX_3 "no live channels left. exiting.\n");
00753 return NULL;
00754 }
00755 }
00756 }
00757 }
00758
00759 } else
00760 if (option_debug)
00761 ast_log(LOG_DEBUG, "timed out waiting for action\n");
00762 }
00763
00764 } else {
00765 if (option_verbose > 2)
00766 ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\n");
00767 }
00768
00769
00770 return NULL;
00771 }
00772
00773 static void findmeexec(struct fm_args *tpargs)
00774 {
00775 struct number *nm;
00776 struct ast_channel *outbound;
00777 struct ast_channel *caller;
00778 struct ast_channel *winner = NULL;
00779 char dialarg[512];
00780 int dg, idx;
00781 char *rest, *number;
00782 struct findme_user *tmpuser;
00783 struct findme_user *fmuser;
00784 struct findme_user *headuser;
00785 struct findme_user_listptr *findme_user_list;
00786 int status;
00787
00788 findme_user_list = ast_calloc(1, sizeof(*findme_user_list));
00789 AST_LIST_HEAD_INIT_NOLOCK(findme_user_list);
00790
00791
00792 ynlongest = 0;
00793 if (strlen(tpargs->takecall) > ynlongest)
00794 ynlongest = strlen(tpargs->takecall);
00795 if (strlen(tpargs->nextindp) > ynlongest)
00796 ynlongest = strlen(tpargs->nextindp);
00797
00798 idx = 1;
00799 caller = tpargs->chan;
00800 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00801 if (nm->order == idx)
00802 break;
00803
00804 while (nm) {
00805
00806 if (option_debug > 1)
00807 ast_log(LOG_DEBUG, "Number %s timeout %ld\n", nm->number,nm->timeout);
00808 time(&start_time);
00809
00810 number = ast_strdupa(nm->number);
00811 if (option_debug > 2)
00812 ast_log(LOG_DEBUG, "examining %s\n", number);
00813 do {
00814 rest = strchr(number, '&');
00815 if (rest) {
00816 *rest = 0;
00817 rest++;
00818 }
00819
00820 if (!strcmp(tpargs->context, ""))
00821 snprintf(dialarg, sizeof(dialarg), "%s", number);
00822 else
00823 snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context);
00824
00825 tmpuser = ast_calloc(1, sizeof(*tmpuser));
00826 if (!tmpuser) {
00827 ast_log(LOG_WARNING, "Out of memory!\n");
00828 free(findme_user_list);
00829 return;
00830 }
00831
00832 outbound = ast_request("Local", ast_best_codec(caller->nativeformats), dialarg, &dg);
00833 if (outbound) {
00834 ast_set_callerid(outbound, caller->cid.cid_num, caller->cid.cid_name, caller->cid.cid_num);
00835 ast_channel_inherit_variables(tpargs->chan, outbound);
00836 if (option_verbose > 2)
00837 ast_verbose(VERBOSE_PREFIX_3 "calling %s\n", dialarg);
00838 if (!ast_call(outbound,dialarg,0)) {
00839 tmpuser->ochan = outbound;
00840 tmpuser->state = 0;
00841 tmpuser->cleared = 0;
00842 ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg));
00843 AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry);
00844 } else {
00845 if (option_verbose > 2)
00846 ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\n");
00847 if (outbound) {
00848 if (!outbound->cdr)
00849 outbound->cdr = ast_cdr_alloc();
00850 if (outbound->cdr) {
00851 char tmp[256];
00852
00853 ast_cdr_init(outbound->cdr, outbound);
00854 snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg);
00855 ast_cdr_setapp(outbound->cdr, "FollowMe", tmp);
00856 ast_cdr_update(outbound);
00857 ast_cdr_start(outbound->cdr);
00858 ast_cdr_end(outbound->cdr);
00859
00860 if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause))
00861 ast_cdr_failed(outbound->cdr);
00862 } else {
00863 ast_log(LOG_ERROR, "Unable to create Call Detail Record\n");
00864 ast_hangup(outbound);
00865 outbound = NULL;
00866 }
00867 }
00868
00869 }
00870 } else
00871 ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg));
00872
00873 number = rest;
00874 } while (number);
00875
00876 status = 0;
00877 if (!AST_LIST_EMPTY(findme_user_list))
00878 winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs);
00879
00880
00881 AST_LIST_TRAVERSE_SAFE_BEGIN(findme_user_list, fmuser, entry) {
00882 if (!fmuser->cleared && fmuser->ochan != winner)
00883 clear_caller(fmuser);
00884 AST_LIST_REMOVE_CURRENT(findme_user_list, entry);
00885 free(fmuser);
00886 }
00887 AST_LIST_TRAVERSE_SAFE_END
00888 fmuser = NULL;
00889 tmpuser = NULL;
00890 headuser = NULL;
00891 if (winner)
00892 break;
00893
00894 if (!caller) {
00895 tpargs->status = 1;
00896 free(findme_user_list);
00897 return;
00898 }
00899
00900 idx++;
00901 AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry)
00902 if (nm->order == idx)
00903 break;
00904
00905 }
00906 free(findme_user_list);
00907 if (!winner)
00908 tpargs->status = 1;
00909 else {
00910 tpargs->status = 100;
00911 tpargs->outbound = winner;
00912 }
00913
00914
00915 return;
00916
00917 }
00918
00919 static int app_exec(struct ast_channel *chan, void *data)
00920 {
00921 struct fm_args targs;
00922 struct ast_bridge_config config;
00923 struct call_followme *f;
00924 struct number *nm, *newnm;
00925 int res = 0;
00926 struct ast_module_user *u;
00927 char *argstr;
00928 char namerecloc[255];
00929 char *fname = NULL;
00930 int duration = 0;
00931 struct ast_channel *caller;
00932 struct ast_channel *outbound;
00933 static char toast[80];
00934
00935 AST_DECLARE_APP_ARGS(args,
00936 AST_APP_ARG(followmeid);
00937 AST_APP_ARG(options);
00938 );
00939
00940 if (!(argstr = ast_strdupa((char *)data))) {
00941 ast_log(LOG_ERROR, "Out of memory!\n");
00942 return -1;
00943 }
00944
00945 if (!data) {
00946 ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n",app);
00947 return -1;
00948 }
00949
00950 u = ast_module_user_add(chan);
00951
00952 AST_STANDARD_APP_ARGS(args, argstr);
00953
00954 if (!ast_strlen_zero(args.followmeid))
00955 AST_LIST_LOCK(&followmes);
00956 AST_LIST_TRAVERSE(&followmes, f, entry) {
00957 if (!strcasecmp(f->name, args.followmeid) && (f->active))
00958 break;
00959 }
00960 AST_LIST_UNLOCK(&followmes);
00961
00962 if (option_debug)
00963 ast_log(LOG_DEBUG, "New profile %s.\n", args.followmeid);
00964 if (!f) {
00965 ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid);
00966 res = 0;
00967 } else {
00968
00969
00970
00971 if (args.options)
00972 ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options);
00973
00974
00975 ast_mutex_lock(&f->lock);
00976 targs.mohclass = ast_strdupa(f->moh);
00977 ast_copy_string(targs.context, f->context, sizeof(targs.context));
00978 ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall));
00979 ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp));
00980 ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt));
00981 ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt));
00982 ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt));
00983 ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt));
00984 ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt));
00985 ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt));
00986
00987
00988 AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers);
00989 AST_LIST_TRAVERSE(&f->numbers, nm, entry) {
00990 newnm = create_followme_number(nm->number, "", nm->timeout, nm->order);
00991 AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry);
00992 }
00993 ast_mutex_unlock(&f->lock);
00994
00995 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))
00996 ast_stream_and_wait(chan, targs.statusprompt, chan->language, "");
00997
00998 snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid);
00999 duration = 5;
01000
01001 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))
01002 if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, 128, 0, NULL) < 0)
01003 goto outrun;
01004
01005 if (!ast_fileexists(namerecloc, NULL, chan->language))
01006 ast_copy_string(namerecloc, "", sizeof(namerecloc));
01007
01008 if (ast_streamfile(chan, targs.plsholdprompt, chan->language))
01009 goto outrun;
01010 if (ast_waitstream(chan, "") < 0)
01011 goto outrun;
01012 ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL);
01013
01014 targs.status = 0;
01015 targs.chan = chan;
01016 ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc));
01017
01018 findmeexec(&targs);
01019
01020 AST_LIST_TRAVERSE_SAFE_BEGIN(&targs.cnumbers, nm, entry) {
01021 AST_LIST_REMOVE_CURRENT(&targs.cnumbers, entry);
01022 free(nm);
01023 }
01024 AST_LIST_TRAVERSE_SAFE_END
01025 if (targs.status != 100) {
01026 ast_moh_stop(chan);
01027 if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))
01028 ast_stream_and_wait(chan, targs.sorryprompt, chan->language, "");
01029 res = 0;
01030 } else {
01031 caller = chan;
01032 outbound = targs.outbound;
01033
01034
01035 memset(&config,0,sizeof(struct ast_bridge_config));
01036 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
01037 ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
01038 ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
01039
01040 ast_moh_stop(caller);
01041
01042 ast_deactivate_generator(caller);
01043
01044 res = ast_channel_make_compatible(caller, outbound);
01045 if (res < 0) {
01046 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name);
01047 ast_hangup(outbound);
01048 goto outrun;
01049 }
01050 time(&answer_time);
01051 res = ast_bridge_call(caller,outbound,&config);
01052 time(&end_time);
01053 snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
01054 pbx_builtin_setvar_helper(caller, "DIALEDTIME", toast);
01055 snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
01056 pbx_builtin_setvar_helper(caller, "ANSWEREDTIME", toast);
01057 if (outbound)
01058 ast_hangup(outbound);
01059 }
01060 }
01061 outrun:
01062
01063 if (!ast_strlen_zero(namerecloc)){
01064 fname = alloca(strlen(namerecloc) + 5);
01065 sprintf(fname, "%s.sln", namerecloc);
01066 unlink(fname);
01067 }
01068
01069 ast_module_user_remove(u);
01070
01071 return res;
01072 }
01073
01074 static int unload_module(void)
01075 {
01076 struct call_followme *f;
01077
01078 ast_module_user_hangup_all();
01079
01080 ast_unregister_application(app);
01081
01082
01083 AST_LIST_LOCK(&followmes);
01084 while ((f = AST_LIST_REMOVE_HEAD(&followmes, entry))) {
01085 free_numbers(f);
01086 free(f);
01087 }
01088
01089 AST_LIST_UNLOCK(&followmes);
01090
01091 return 0;
01092 }
01093
01094 static int load_module(void)
01095 {
01096 if(!reload_followme())
01097 return AST_MODULE_LOAD_DECLINE;
01098
01099 return ast_register_application(app, app_exec, synopsis, descrip);
01100 }
01101
01102 static int reload(void)
01103 {
01104 reload_followme();
01105
01106 return 0;
01107 }
01108
01109 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application",
01110 .load = load_module,
01111 .unload = unload_module,
01112 .reload = reload,
01113 );