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