| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- A telephony toolkit for Linux. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A full-featured Find-Me/Follow-Me Application | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  * Copyright (C) 2005-2006, BJ Weschke All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * BJ Weschke <bweschke@btwtech.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This code is released by the author with no restrictions on usage. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \file
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \brief Find-Me Follow-Me application | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \author BJ Weschke <bweschke@btwtech.com> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-06-08 08:10:58 +00:00
										 |  |  |  * \arg See \ref Config_followme | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  |  * \ingroup applications | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-01-22 17:46:43 +00:00
										 |  |  | /*** MODULEINFO
 | 
					
						
							| 
									
										
										
										
											2008-04-25 20:20:10 +00:00
										 |  |  | 	<depend>chan_local</depend> | 
					
						
							| 
									
										
										
										
											2011-07-14 20:13:06 +00:00
										 |  |  | 	<support_level>core</support_level> | 
					
						
							| 
									
										
										
										
											2008-01-22 17:46:43 +00:00
										 |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-06 21:37:12 +00:00
										 |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-20 23:16:15 +00:00
										 |  |  | #include "asterisk/paths.h"	/* use ast_config_AST_SPOOL_DIR */
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | #include "asterisk/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/file.h"
 | 
					
						
							|  |  |  | #include "asterisk/channel.h"
 | 
					
						
							|  |  |  | #include "asterisk/pbx.h"
 | 
					
						
							|  |  |  | #include "asterisk/module.h"
 | 
					
						
							|  |  |  | #include "asterisk/translate.h"
 | 
					
						
							|  |  |  | #include "asterisk/say.h"
 | 
					
						
							|  |  |  | #include "asterisk/features.h"
 | 
					
						
							|  |  |  | #include "asterisk/musiconhold.h"
 | 
					
						
							|  |  |  | #include "asterisk/cli.h"
 | 
					
						
							|  |  |  | #include "asterisk/manager.h"
 | 
					
						
							|  |  |  | #include "asterisk/config.h"
 | 
					
						
							|  |  |  | #include "asterisk/utils.h"
 | 
					
						
							|  |  |  | #include "asterisk/causes.h"
 | 
					
						
							|  |  |  | #include "asterisk/astdb.h"
 | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | #include "asterisk/dsp.h"
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | #include "asterisk/app.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-05 02:08:39 +00:00
										 |  |  | /*** DOCUMENTATION
 | 
					
						
							|  |  |  | 	<application name="FollowMe" language="en_US"> | 
					
						
							|  |  |  | 		<synopsis> | 
					
						
							|  |  |  | 			Find-Me/Follow-Me application. | 
					
						
							|  |  |  | 		</synopsis> | 
					
						
							|  |  |  | 		<syntax> | 
					
						
							|  |  |  | 			<parameter name="followmeid" required="true" /> | 
					
						
							|  |  |  | 			<parameter name="options"> | 
					
						
							|  |  |  | 				<optionlist> | 
					
						
							|  |  |  | 					<option name="s"> | 
					
						
							|  |  |  | 						<para>Playback the incoming status message prior to starting | 
					
						
							|  |  |  | 						the follow-me step(s)</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 					<option name="a"> | 
					
						
							|  |  |  | 						<para>Record the caller's name so it can be announced to the | 
					
						
							|  |  |  | 						callee on each step.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							|  |  |  | 					<option name="n"> | 
					
						
							|  |  |  | 						<para>Playback the unreachable status message if we've run out | 
					
						
							|  |  |  | 						of steps to reach the or the callee has elected not to be reachable.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							| 
									
										
										
										
											2009-11-23 22:37:39 +00:00
										 |  |  | 					<option name="d"> | 
					
						
							|  |  |  | 						<para>Disable the 'Please hold while we try to connect your call' announcement.</para> | 
					
						
							|  |  |  | 					</option> | 
					
						
							| 
									
										
										
										
											2008-11-05 02:08:39 +00:00
										 |  |  | 				</optionlist> | 
					
						
							|  |  |  | 			</parameter> | 
					
						
							|  |  |  | 		</syntax> | 
					
						
							|  |  |  | 		<description> | 
					
						
							|  |  |  | 			<para>This application performs Find-Me/Follow-Me functionality for the caller | 
					
						
							|  |  |  | 			as defined in the profile matching the <replaceable>followmeid</replaceable> parameter in | 
					
						
							|  |  |  | 			<filename>followme.conf</filename>. If the specified <replaceable>followmeid</replaceable> | 
					
						
							|  |  |  | 			profile doesn't exist in <filename>followme.conf</filename>, execution will be returned | 
					
						
							|  |  |  | 			to the dialplan and call execution will continue at the next priority.</para> | 
					
						
							|  |  |  | 			<para>Returns -1 on hangup.</para> | 
					
						
							|  |  |  | 		</description> | 
					
						
							|  |  |  | 	</application> | 
					
						
							|  |  |  |  ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | static char *app = "FollowMe"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Number structure */ | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | struct number { | 
					
						
							|  |  |  | 	char number[512];	/*!< Phone Number(s) and/or Extension(s) */ | 
					
						
							|  |  |  | 	long timeout;		/*!< Dial Timeout, if used. */ | 
					
						
							|  |  |  | 	int order;		/*!< The order to dial in */ | 
					
						
							|  |  |  | 	AST_LIST_ENTRY(number) entry; /*!< Next Number record */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Data structure for followme scripts */ | 
					
						
							|  |  |  | struct call_followme { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	ast_mutex_t lock; | 
					
						
							|  |  |  | 	char name[AST_MAX_EXTENSION];	/*!< Name - FollowMeID */ | 
					
						
							|  |  |  | 	char moh[AST_MAX_CONTEXT];	/*!< Music On Hold Class to be used */ | 
					
						
							|  |  |  | 	char context[AST_MAX_CONTEXT];  /*!< Context to dial from */ | 
					
						
							|  |  |  | 	unsigned int active;		/*!< Profile is active (1), or disabled (0). */ | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	int realtime;           /*!< Cached from realtime */ | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	char takecall[20];		/*!< Digit mapping to take a call */ | 
					
						
							|  |  |  | 	char nextindp[20];		/*!< Digit mapping to decline a call */ | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	char callfromprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char norecordingprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char optionsprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char plsholdprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char statusprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char sorryprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	AST_LIST_HEAD_NOLOCK(numbers, number) numbers;	   /*!< Head of the list of follow-me numbers */ | 
					
						
							|  |  |  | 	AST_LIST_HEAD_NOLOCK(blnumbers, number) blnumbers; /*!< Head of the list of black-listed numbers */ | 
					
						
							|  |  |  | 	AST_LIST_HEAD_NOLOCK(wlnumbers, number) wlnumbers; /*!< Head of the list of white-listed numbers */ | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	AST_LIST_ENTRY(call_followme) entry;           /*!< Next Follow-Me record */ | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct fm_args { | 
					
						
							|  |  |  | 	struct ast_channel *chan; | 
					
						
							|  |  |  | 	char *mohclass; | 
					
						
							|  |  |  | 	AST_LIST_HEAD_NOLOCK(cnumbers, number) cnumbers; | 
					
						
							|  |  |  | 	int status; | 
					
						
							|  |  |  | 	char context[AST_MAX_CONTEXT]; | 
					
						
							|  |  |  | 	char namerecloc[AST_MAX_CONTEXT]; | 
					
						
							|  |  |  | 	struct ast_channel *outbound; | 
					
						
							|  |  |  | 	char takecall[20];		/*!< Digit mapping to take a call */ | 
					
						
							|  |  |  | 	char nextindp[20];		/*!< Digit mapping to decline a call */ | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	char callfromprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char norecordingprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char optionsprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char plsholdprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char statusprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							|  |  |  | 	char sorryprompt[PATH_MAX];	/*!< Sound prompt name and path */ | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	struct ast_flags followmeflags; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct findme_user { | 
					
						
							|  |  |  | 	struct ast_channel *ochan; | 
					
						
							|  |  |  | 	int state; | 
					
						
							|  |  |  | 	char dialarg[256]; | 
					
						
							|  |  |  | 	char yn[10]; | 
					
						
							|  |  |  | 	int ynidx;  | 
					
						
							|  |  |  | 	long digts; | 
					
						
							|  |  |  | 	int cleared; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 	AST_LIST_ENTRY(findme_user) entry; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  | 	FOLLOWMEFLAG_STATUSMSG = (1 << 0), | 
					
						
							|  |  |  | 	FOLLOWMEFLAG_RECORDNAME = (1 << 1), | 
					
						
							| 
									
										
										
										
											2009-11-23 22:37:39 +00:00
										 |  |  | 	FOLLOWMEFLAG_UNREACHABLEMSG = (1 << 2), | 
					
						
							|  |  |  | 	FOLLOWMEFLAG_DISABLEHOLDPROMPT = (1 << 3) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AST_APP_OPTIONS(followme_opts, { | 
					
						
							|  |  |  | 	AST_APP_OPTION('s', FOLLOWMEFLAG_STATUSMSG ), | 
					
						
							|  |  |  | 	AST_APP_OPTION('a', FOLLOWMEFLAG_RECORDNAME ), | 
					
						
							|  |  |  | 	AST_APP_OPTION('n', FOLLOWMEFLAG_UNREACHABLEMSG ), | 
					
						
							| 
									
										
										
										
											2009-11-23 22:37:39 +00:00
										 |  |  | 	AST_APP_OPTION('d', FOLLOWMEFLAG_DISABLEHOLDPROMPT ), | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ynlongest = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-20 20:57:57 +00:00
										 |  |  | static const char *featuredigittostr; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | static int featuredigittimeout = 5000;		/*!< Feature Digit Timeout */ | 
					
						
							|  |  |  | static const char *defaultmoh = "default";    	/*!< Default Music-On-Hold Class */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char takecall[20] = "1", nextindp[20] = "2"; | 
					
						
							| 
									
										
										
										
											2006-06-09 20:40:10 +00:00
										 |  |  | static char callfromprompt[PATH_MAX] = "followme/call-from"; | 
					
						
							|  |  |  | static char norecordingprompt[PATH_MAX] = "followme/no-recording"; | 
					
						
							| 
									
										
										
										
											2006-08-07 18:47:33 +00:00
										 |  |  | static char optionsprompt[PATH_MAX] = "followme/options"; | 
					
						
							| 
									
										
										
										
											2006-06-09 20:40:10 +00:00
										 |  |  | static char plsholdprompt[PATH_MAX] = "followme/pls-hold-while-try"; | 
					
						
							| 
									
										
										
										
											2006-08-07 18:47:33 +00:00
										 |  |  | static char statusprompt[PATH_MAX] = "followme/status"; | 
					
						
							|  |  |  | static char sorryprompt[PATH_MAX] = "followme/sorry"; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | static AST_RWLIST_HEAD_STATIC(followmes, call_followme); | 
					
						
							| 
									
										
										
										
											2006-06-06 21:03:18 +00:00
										 |  |  | AST_LIST_HEAD_NOLOCK(findme_user_listptr, findme_user); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | static void free_numbers(struct call_followme *f) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* Free numbers attached to the profile */ | 
					
						
							|  |  |  | 	struct number *prev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((prev = AST_LIST_REMOVE_HEAD(&f->numbers, entry))) | 
					
						
							|  |  |  | 		/* Free the number */ | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 		ast_free(prev); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->numbers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry))) | 
					
						
							|  |  |  | 		/* Free the blacklisted number */ | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 		ast_free(prev); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry))) | 
					
						
							|  |  |  | 		/* Free the whitelisted number */ | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 		ast_free(prev); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Allocate and initialize followme profile */ | 
					
						
							|  |  |  | static struct call_followme *alloc_profile(const char *fmname) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	struct call_followme *f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(f = ast_calloc(1, sizeof(*f)))) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_init(&f->lock); | 
					
						
							|  |  |  | 	ast_copy_string(f->name, fmname, sizeof(f->name)); | 
					
						
							|  |  |  | 	f->moh[0] = '\0'; | 
					
						
							|  |  |  | 	f->context[0] = '\0'; | 
					
						
							|  |  |  | 	ast_copy_string(f->takecall, takecall, sizeof(f->takecall)); | 
					
						
							|  |  |  | 	ast_copy_string(f->nextindp, nextindp, sizeof(f->nextindp)); | 
					
						
							|  |  |  | 	ast_copy_string(f->callfromprompt, callfromprompt, sizeof(f->callfromprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(f->norecordingprompt, norecordingprompt, sizeof(f->norecordingprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(f->optionsprompt, optionsprompt, sizeof(f->optionsprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(f->plsholdprompt, plsholdprompt, sizeof(f->plsholdprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(f->statusprompt, statusprompt, sizeof(f->statusprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(f->sorryprompt, sorryprompt, sizeof(f->sorryprompt)); | 
					
						
							|  |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->numbers); | 
					
						
							|  |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers); | 
					
						
							|  |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&f->wlnumbers); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	return f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | static void init_profile(struct call_followme *f) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	f->active = 1; | 
					
						
							|  |  |  | 	ast_copy_string(f->moh, defaultmoh, sizeof(f->moh)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  |     | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Set parameter in profile from configuration file */ | 
					
						
							|  |  |  | static void profile_set_param(struct call_followme *f, const char *param, const char *val, int linenum, int failunknown) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!strcasecmp(param, "musicclass") || !strcasecmp(param, "musiconhold") || !strcasecmp(param, "music"))  | 
					
						
							|  |  |  | 		ast_copy_string(f->moh, val, sizeof(f->moh)); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	else if (!strcasecmp(param, "context"))  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->context, val, sizeof(f->context)); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	else if (!strcasecmp(param, "takecall")) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->takecall, val, sizeof(f->takecall)); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	else if (!strcasecmp(param, "declinecall")) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->nextindp, val, sizeof(f->nextindp)); | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	else if (!strcasecmp(param, "call-from-prompt") || !strcasecmp(param, "call_from_prompt")) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->callfromprompt, val, sizeof(f->callfromprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	else if (!strcasecmp(param, "followme-norecording-prompt") || !strcasecmp(param, "norecording_prompt"))  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->norecordingprompt, val, sizeof(f->norecordingprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	else if (!strcasecmp(param, "followme-options-prompt") || !strcasecmp(param, "options_prompt"))  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->optionsprompt, val, sizeof(f->optionsprompt)); | 
					
						
							| 
									
										
										
										
											2008-11-25 21:49:42 +00:00
										 |  |  | 	else if (!strcasecmp(param, "followme-pls-hold-prompt") || !strcasecmp(param, "pls_hold_prompt")) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->plsholdprompt, val, sizeof(f->plsholdprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	else if (!strcasecmp(param, "followme-status-prompt") || !strcasecmp(param, "status_prompt"))  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->statusprompt, val, sizeof(f->statusprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	else if (!strcasecmp(param, "followme-sorry-prompt") || !strcasecmp(param, "sorry_prompt"))  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(f->sorryprompt, val, sizeof(f->sorryprompt)); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	else if (failunknown) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		if (linenum >= 0) | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s at line %d of followme.conf\n", f->name, param, linenum); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unknown keyword in profile '%s': %s\n", f->name, param); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Add a new number */ | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | static struct number *create_followme_number(const char *number, int timeout, int numorder) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct number *cur; | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | 	char *buf = ast_strdupa(number); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	char *tmp; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	if (!(cur = ast_calloc(1, sizeof(*cur)))) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	cur->timeout = timeout; | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | 	if ((tmp = strchr(buf, ','))) | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 		*tmp = '\0'; | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | 	ast_copy_string(cur->number, buf, sizeof(cur->number)); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	cur->order = numorder; | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 	ast_debug(1, "Created a number, %s, order of , %d, with a timeout of %ld.\n", cur->number, cur->order, cur->timeout); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return cur; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | /*! \brief Reload followme application module */ | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | static int reload_followme(int reload) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	struct call_followme *f; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	struct ast_config *cfg; | 
					
						
							| 
									
										
										
										
											2006-06-07 20:02:07 +00:00
										 |  |  | 	char *cat = NULL, *tmp; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	struct ast_variable *var; | 
					
						
							|  |  |  | 	struct number *cur, *nm; | 
					
						
							|  |  |  | 	char numberstr[90]; | 
					
						
							|  |  |  | 	int timeout; | 
					
						
							|  |  |  | 	char *timeoutstr; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 	int numorder; | 
					
						
							| 
									
										
										
										
											2006-09-20 20:57:57 +00:00
										 |  |  | 	const char *takecallstr; | 
					
						
							|  |  |  | 	const char *declinecallstr; | 
					
						
							|  |  |  | 	const char *tmpstr; | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | 	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | 	if (!(cfg = ast_config_load("followme.conf", config_flags))) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n"); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2008-09-12 23:30:03 +00:00
										 |  |  | 	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2008-09-12 23:30:03 +00:00
										 |  |  | 	} else if (cfg == CONFIG_STATUS_FILEINVALID) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Config file followme.conf is in an invalid format.  Aborting.\n"); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_WRLOCK(&followmes); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Reset Global Var Values */ | 
					
						
							|  |  |  | 	featuredigittimeout = 5000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Mark all profiles as inactive for the moment */ | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_TRAVERSE(&followmes, f, entry) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		f->active = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	featuredigittostr = ast_variable_retrieve(cfg, "general", "featuredigittimeout"); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 19:42:37 +00:00
										 |  |  | 	if (!ast_strlen_zero(featuredigittostr)) { | 
					
						
							| 
									
										
										
										
											2009-08-10 19:20:57 +00:00
										 |  |  | 		if (!sscanf(featuredigittostr, "%30d", &featuredigittimeout)) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			featuredigittimeout = 5000; | 
					
						
							| 
									
										
										
										
											2006-06-07 19:42:37 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((takecallstr = ast_variable_retrieve(cfg, "general", "takecall")) && !ast_strlen_zero(takecallstr)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(takecall, takecallstr, sizeof(takecall)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((declinecallstr = ast_variable_retrieve(cfg, "general", "declinecall")) && !ast_strlen_zero(declinecallstr)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(nextindp, declinecallstr, sizeof(nextindp)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "call-from-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							|  |  |  | 		ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt)); | 
					
						
							|  |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "call_from_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(callfromprompt, tmpstr, sizeof(callfromprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "norecording_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(norecordingprompt, tmpstr, sizeof(norecordingprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "options-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "options_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							|  |  |  | 		ast_copy_string(optionsprompt, tmpstr, sizeof(optionsprompt)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls-hold-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "pls_hold_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(plsholdprompt, tmpstr, sizeof(plsholdprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "status-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "status_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(statusprompt, tmpstr, sizeof(statusprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry-prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} else if ((tmpstr = ast_variable_retrieve(cfg, "general", "sorry_prompt")) && !ast_strlen_zero(tmpstr)) { | 
					
						
							| 
									
										
										
										
											2010-03-05 19:10:47 +00:00
										 |  |  | 		ast_copy_string(sorryprompt, tmpstr, sizeof(sorryprompt)); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Chug through config file */ | 
					
						
							| 
									
										
										
										
											2006-06-07 20:02:07 +00:00
										 |  |  | 	while ((cat = ast_category_browse(cfg, cat))) { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		int new = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 20:02:07 +00:00
										 |  |  | 		if (!strcasecmp(cat, "general")) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		/* Look for an existing one */ | 
					
						
							|  |  |  | 		AST_LIST_TRAVERSE(&followmes, f, entry) { | 
					
						
							|  |  |  | 			if (!strcasecmp(f->name, cat)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 		ast_debug(1, "New profile %s.\n", cat); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		if (!f) { | 
					
						
							|  |  |  | 			/* Make one then */ | 
					
						
							|  |  |  | 			f = alloc_profile(cat); | 
					
						
							|  |  |  | 			new = 1; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Totally fail if we fail to find/create an entry */ | 
					
						
							|  |  |  | 		if (!f) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		if (!new) | 
					
						
							|  |  |  | 			ast_mutex_lock(&f->lock); | 
					
						
							|  |  |  | 		/* Re-initialize the profile */ | 
					
						
							|  |  |  | 		init_profile(f); | 
					
						
							|  |  |  | 		free_numbers(f); | 
					
						
							|  |  |  | 		var = ast_variable_browse(cfg, cat); | 
					
						
							| 
									
										
										
										
											2008-08-26 18:05:58 +00:00
										 |  |  | 		while (var) { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 			if (!strcasecmp(var->name, "number")) { | 
					
						
							|  |  |  | 				int idx = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Add a new number */ | 
					
						
							|  |  |  | 				ast_copy_string(numberstr, var->value, sizeof(numberstr)); | 
					
						
							|  |  |  | 				if ((tmp = strchr(numberstr, ','))) { | 
					
						
							|  |  |  | 					*tmp++ = '\0'; | 
					
						
							|  |  |  | 					timeoutstr = ast_strdupa(tmp); | 
					
						
							|  |  |  | 					if ((tmp = strchr(timeoutstr, ','))) { | 
					
						
							|  |  |  | 						*tmp++ = '\0'; | 
					
						
							|  |  |  | 						numorder = atoi(tmp); | 
					
						
							|  |  |  | 						if (numorder < 0) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 							numorder = 0; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					} else  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						numorder = 0; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					timeout = atoi(timeoutstr); | 
					
						
							|  |  |  | 					if (timeout < 0)  | 
					
						
							|  |  |  | 						timeout = 25; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					timeout = 25; | 
					
						
							|  |  |  | 					numorder = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				if (!numorder) { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					idx = 1; | 
					
						
							|  |  |  | 					AST_LIST_TRAVERSE(&f->numbers, nm, entry)  | 
					
						
							|  |  |  | 						idx++; | 
					
						
							|  |  |  | 					numorder = idx; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				cur = create_followme_number(numberstr, timeout, numorder); | 
					
						
							|  |  |  | 				AST_LIST_INSERT_TAIL(&f->numbers, cur, entry); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				profile_set_param(f, var->name, var->value, var->lineno, 1); | 
					
						
							|  |  |  | 				ast_debug(2, "Logging parameter %s with value %s from lineno %d\n", var->name, var->value, var->lineno); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var = var->next; | 
					
						
							|  |  |  | 		} /* End while(var) loop */ | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		if (!new)  | 
					
						
							|  |  |  | 			ast_mutex_unlock(&f->lock); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			AST_RWLIST_INSERT_HEAD(&followmes, f, entry); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	ast_config_destroy(cfg); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_UNLOCK(&followmes); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void clear_caller(struct findme_user *tmpuser) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_channel *outbound; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	if (tmpuser && tmpuser->ochan && tmpuser->state >= 0) { | 
					
						
							|  |  |  | 		outbound = tmpuser->ochan; | 
					
						
							|  |  |  | 		if (!outbound->cdr) { | 
					
						
							|  |  |  | 			outbound->cdr = ast_cdr_alloc(); | 
					
						
							|  |  |  | 			if (outbound->cdr) | 
					
						
							|  |  |  | 				ast_cdr_init(outbound->cdr, outbound); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (outbound->cdr) { | 
					
						
							|  |  |  | 			char tmp[256]; | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 20:45:26 +00:00
										 |  |  | 			snprintf(tmp, sizeof(tmp), "%s/%s", "Local", tmpuser->dialarg); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 			ast_cdr_setapp(outbound->cdr, "FollowMe", tmp); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			ast_cdr_update(outbound); | 
					
						
							|  |  |  | 			ast_cdr_start(outbound->cdr); | 
					
						
							|  |  |  | 			ast_cdr_end(outbound->cdr); | 
					
						
							|  |  |  | 			/* If the cause wasn't handled properly */ | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 			if (ast_cdr_disposition(outbound->cdr, outbound->hangupcause)) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				ast_cdr_failed(outbound->cdr); | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Unable to create Call Detail Record\n"); | 
					
						
							|  |  |  | 		ast_hangup(tmpuser->ochan); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void clear_calling_tree(struct findme_user_listptr *findme_user_list)  | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct findme_user *tmpuser; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { | 
					
						
							|  |  |  | 		clear_caller(tmpuser); | 
					
						
							|  |  |  | 		tmpuser->cleared = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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)  | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_channel *watchers[256]; | 
					
						
							|  |  |  | 	int pos; | 
					
						
							|  |  |  | 	struct ast_channel *winner; | 
					
						
							|  |  |  | 	struct ast_frame *f; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	int ctstatus = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	int dg; | 
					
						
							|  |  |  | 	struct findme_user *tmpuser; | 
					
						
							| 
									
										
										
										
											2006-06-06 20:18:01 +00:00
										 |  |  | 	int to = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	int livechannels = 0; | 
					
						
							|  |  |  | 	int tmpto; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	long totalwait = 0, wtd = 0, towas = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	char *callfromname; | 
					
						
							|  |  |  | 	char *pressbuttonname; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ------------ wait_for_winner_channel start --------------- */  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	callfromname = ast_strdupa(tpargs->callfromprompt); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 	pressbuttonname = ast_strdupa(tpargs->optionsprompt); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (AST_LIST_EMPTY(findme_user_list)) { | 
					
						
							|  |  |  | 		ast_verb(3, "couldn't reach at this number.\n"); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (!caller) { | 
					
						
							|  |  |  | 		ast_verb(3, "Original caller hungup. Cleanup.\n"); | 
					
						
							|  |  |  | 		clear_calling_tree(findme_user_list); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	totalwait = nm->timeout * 1000; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	while (!ctstatus) { | 
					
						
							|  |  |  | 		to = 1000; | 
					
						
							|  |  |  | 		pos = 1;  | 
					
						
							|  |  |  | 		livechannels = 0; | 
					
						
							|  |  |  | 		watchers[0] = caller; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		dg = 0; | 
					
						
							|  |  |  | 		winner = NULL; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) { | 
					
						
							|  |  |  | 			if (tmpuser->state >= 0 && tmpuser->ochan) { | 
					
						
							|  |  |  | 				if (tmpuser->state == 3)  | 
					
						
							|  |  |  | 					tmpuser->digts += (towas - wtd); | 
					
						
							|  |  |  | 				if (tmpuser->digts && (tmpuser->digts > featuredigittimeout)) { | 
					
						
							|  |  |  | 					ast_verb(3, "We've been waiting for digits longer than we should have.\n"); | 
					
						
							|  |  |  | 					if (!ast_strlen_zero(namerecloc)) { | 
					
						
							|  |  |  | 						tmpuser->state = 1; | 
					
						
							|  |  |  | 						tmpuser->digts = 0; | 
					
						
							|  |  |  | 						if (!ast_streamfile(tmpuser->ochan, callfromname, tmpuser->ochan->language)) { | 
					
						
							|  |  |  | 							ast_sched_runq(tmpuser->ochan->sched); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						} else { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 							ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname); | 
					
						
							|  |  |  | 							return NULL; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						tmpuser->state = 2; | 
					
						
							|  |  |  | 						tmpuser->digts = 0; | 
					
						
							|  |  |  | 						if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language)) | 
					
						
							|  |  |  | 							ast_sched_runq(tmpuser->ochan->sched); | 
					
						
							|  |  |  | 						else { | 
					
						
							|  |  |  | 							ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt); | 
					
						
							|  |  |  | 							return NULL; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				if (tmpuser->ochan->stream) { | 
					
						
							|  |  |  | 					ast_sched_runq(tmpuser->ochan->sched); | 
					
						
							|  |  |  | 					tmpto = ast_sched_wait(tmpuser->ochan->sched); | 
					
						
							|  |  |  | 					if (tmpto > 0 && tmpto < to) | 
					
						
							|  |  |  | 						to = tmpto; | 
					
						
							|  |  |  | 					else if (tmpto < 0 && !tmpuser->ochan->timingfunc) { | 
					
						
							|  |  |  | 						ast_stopstream(tmpuser->ochan); | 
					
						
							|  |  |  | 						if (tmpuser->state == 1) { | 
					
						
							|  |  |  | 							ast_verb(3, "Playback of the call-from file appears to be done.\n"); | 
					
						
							|  |  |  | 							if (!ast_streamfile(tmpuser->ochan, namerecloc, tmpuser->ochan->language)) { | 
					
						
							|  |  |  | 								tmpuser->state = 2; | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								ast_log(LOG_NOTICE, "Unable to playback %s. Maybe the caller didn't record their name?\n", namerecloc); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 								memset(tmpuser->yn, 0, sizeof(tmpuser->yn)); | 
					
						
							|  |  |  | 								tmpuser->ynidx = 0; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 								if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 									tmpuser->state = 3; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 								else { | 
					
						
							|  |  |  | 									ast_log(LOG_WARNING, "Unable to playback %s.\n", pressbuttonname); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 									return NULL; | 
					
						
							|  |  |  | 								}  | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						} else if (tmpuser->state == 2) { | 
					
						
							|  |  |  | 							ast_verb(3, "Playback of name file appears to be done.\n"); | 
					
						
							|  |  |  | 							memset(tmpuser->yn, 0, sizeof(tmpuser->yn)); | 
					
						
							|  |  |  | 							tmpuser->ynidx = 0; | 
					
						
							|  |  |  | 							if (!ast_streamfile(tmpuser->ochan, pressbuttonname, tmpuser->ochan->language)) { | 
					
						
							|  |  |  | 								tmpuser->state = 3; | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								return NULL; | 
					
						
							|  |  |  | 							}  | 
					
						
							|  |  |  | 						} else if (tmpuser->state == 3) { | 
					
						
							|  |  |  | 							ast_verb(3, "Playback of the next step file appears to be done.\n"); | 
					
						
							|  |  |  | 							tmpuser->digts = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 				watchers[pos++] = tmpuser->ochan; | 
					
						
							|  |  |  | 				livechannels++; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		tmpto = to; | 
					
						
							|  |  |  | 		if (to < 0) { | 
					
						
							|  |  |  | 			to = 1000; | 
					
						
							|  |  |  | 			tmpto = 1000; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		towas = to; | 
					
						
							|  |  |  | 		winner = ast_waitfor_n(watchers, pos, &to); | 
					
						
							|  |  |  | 		tmpto -= to; | 
					
						
							|  |  |  | 		totalwait -= tmpto; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		wtd = to; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		if (totalwait <= 0) { | 
					
						
							|  |  |  | 			ast_verb(3, "We've hit our timeout for this step. Drop everyone and move on to the next one. %ld\n", totalwait); | 
					
						
							|  |  |  | 			clear_calling_tree(findme_user_list); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (winner) { | 
					
						
							|  |  |  | 			/* Need to find out which channel this is */ | 
					
						
							|  |  |  | 			dg = 0; | 
					
						
							|  |  |  | 			while ((winner != watchers[dg]) && (dg < 256)) | 
					
						
							|  |  |  | 				dg++; | 
					
						
							|  |  |  | 			AST_LIST_TRAVERSE(findme_user_list, tmpuser, entry) | 
					
						
							|  |  |  | 				if (tmpuser->ochan == winner) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			f = ast_read(winner); | 
					
						
							|  |  |  | 			if (f) { | 
					
						
							|  |  |  | 				if (f->frametype == AST_FRAME_CONTROL) { | 
					
						
							| 
									
										
										
										
											2009-11-04 14:05:12 +00:00
										 |  |  | 					switch (f->subclass.integer) { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					case AST_CONTROL_HANGUP: | 
					
						
							| 
									
										
										
										
											2008-02-05 23:00:15 +00:00
										 |  |  | 						ast_verb(3, "%s received a hangup frame.\n", winner->name); | 
					
						
							| 
									
										
										
										
											2008-05-22 16:29:54 +00:00
										 |  |  | 						if (f->data.uint32) { | 
					
						
							|  |  |  | 							winner->hangupcause = f->data.uint32; | 
					
						
							| 
									
										
										
										
											2008-04-24 22:16:48 +00:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						if (dg == 0) { | 
					
						
							|  |  |  | 							ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n"); | 
					
						
							|  |  |  | 							clear_calling_tree(findme_user_list); | 
					
						
							|  |  |  | 							ctstatus = -1; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					case AST_CONTROL_ANSWER: | 
					
						
							| 
									
										
										
										
											2008-02-05 23:00:15 +00:00
										 |  |  | 						ast_verb(3, "%s answered %s\n", winner->name, caller->name); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						/* If call has been answered, then the eventual hangup is likely to be normal hangup */  | 
					
						
							|  |  |  | 						winner->hangupcause = AST_CAUSE_NORMAL_CLEARING; | 
					
						
							|  |  |  | 						caller->hangupcause = AST_CAUSE_NORMAL_CLEARING; | 
					
						
							|  |  |  | 						ast_verb(3, "Starting playback of %s\n", callfromname); | 
					
						
							|  |  |  | 						if (dg > 0) { | 
					
						
							|  |  |  | 							if (!ast_strlen_zero(namerecloc)) { | 
					
						
							|  |  |  | 								if (!ast_streamfile(winner, callfromname, winner->language)) { | 
					
						
							|  |  |  | 									ast_sched_runq(winner->sched); | 
					
						
							|  |  |  | 									tmpuser->state = 1; | 
					
						
							|  |  |  | 								} else { | 
					
						
							|  |  |  | 									ast_log(LOG_WARNING, "Unable to playback %s.\n", callfromname); | 
					
						
							|  |  |  | 									ast_frfree(f); | 
					
						
							|  |  |  | 									return NULL; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 								} | 
					
						
							|  |  |  | 							} else { | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 								tmpuser->state = 2; | 
					
						
							|  |  |  | 								if (!ast_streamfile(tmpuser->ochan, tpargs->norecordingprompt, tmpuser->ochan->language)) | 
					
						
							|  |  |  | 									ast_sched_runq(tmpuser->ochan->sched); | 
					
						
							|  |  |  | 								else { | 
					
						
							|  |  |  | 									ast_log(LOG_WARNING, "Unable to playback %s.\n", tpargs->norecordingprompt); | 
					
						
							|  |  |  | 									ast_frfree(f); | 
					
						
							|  |  |  | 									return NULL; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_BUSY: | 
					
						
							|  |  |  | 						ast_verb(3, "%s is busy\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_CONGESTION: | 
					
						
							|  |  |  | 						ast_verb(3, "%s is circuit-busy\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_RINGING: | 
					
						
							|  |  |  | 						ast_verb(3, "%s is ringing\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_PROGRESS: | 
					
						
							|  |  |  | 						ast_verb(3, "%s is making progress passing it to %s\n", winner->name, caller->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_VIDUPDATE: | 
					
						
							|  |  |  | 						ast_verb(3, "%s requested a video update, passing it to %s\n", winner->name, caller->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2008-03-05 22:43:22 +00:00
										 |  |  | 					case AST_CONTROL_SRCUPDATE: | 
					
						
							|  |  |  | 						ast_verb(3, "%s requested a source update, passing it to %s\n", winner->name, caller->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					case AST_CONTROL_PROCEEDING: | 
					
						
							|  |  |  | 						ast_verb(3, "%s is proceeding passing it to %s\n", winner->name,caller->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_HOLD: | 
					
						
							|  |  |  | 						ast_verb(3, "Call on %s placed on hold\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_UNHOLD: | 
					
						
							|  |  |  | 						ast_verb(3, "Call on %s left from hold\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case AST_CONTROL_OFFHOOK: | 
					
						
							|  |  |  | 					case AST_CONTROL_FLASH: | 
					
						
							|  |  |  | 						/* Ignore going off hook and flash */ | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					case -1: | 
					
						
							|  |  |  | 						ast_verb(3, "%s stopped sounds\n", winner->name); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					default: | 
					
						
							| 
									
										
										
										
											2009-11-04 14:05:12 +00:00
										 |  |  | 						ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}  | 
					
						
							|  |  |  | 				if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) { | 
					
						
							|  |  |  | 					if (winner->stream) | 
					
						
							|  |  |  | 						ast_stopstream(winner); | 
					
						
							|  |  |  | 					tmpuser->digts = 0; | 
					
						
							| 
									
										
										
										
											2009-11-04 14:05:12 +00:00
										 |  |  | 					ast_debug(1, "DTMF received: %c\n", (char) f->subclass.integer); | 
					
						
							|  |  |  | 					tmpuser->yn[tmpuser->ynidx] = (char) f->subclass.integer; | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 					tmpuser->ynidx++; | 
					
						
							|  |  |  | 					ast_debug(1, "DTMF string: %s\n", tmpuser->yn); | 
					
						
							|  |  |  | 					if (tmpuser->ynidx >= ynlongest) { | 
					
						
							|  |  |  | 						ast_debug(1, "reached longest possible match - doing evals\n"); | 
					
						
							|  |  |  | 						if (!strcmp(tmpuser->yn, tpargs->takecall)) { | 
					
						
							|  |  |  | 							ast_debug(1, "Match to take the call!\n"); | 
					
						
							|  |  |  | 							ast_frfree(f); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 							return tmpuser->ochan; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 						if (!strcmp(tmpuser->yn, tpargs->nextindp)) { | 
					
						
							|  |  |  | 							ast_debug(1, "Next in dial plan step requested.\n"); | 
					
						
							|  |  |  | 							*status = 1; | 
					
						
							|  |  |  | 							ast_frfree(f); | 
					
						
							|  |  |  | 							return NULL; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 				ast_frfree(f); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				if (winner) { | 
					
						
							|  |  |  | 					ast_debug(1, "we didn't get a frame. hanging up. dg is %d\n",dg);					       | 
					
						
							|  |  |  | 					if (!dg) { | 
					
						
							|  |  |  | 						clear_calling_tree(findme_user_list); | 
					
						
							|  |  |  | 						return NULL; | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						tmpuser->state = -1; | 
					
						
							|  |  |  | 						ast_hangup(winner);   | 
					
						
							|  |  |  | 						livechannels--; | 
					
						
							|  |  |  | 						ast_debug(1, "live channels left %d\n", livechannels); | 
					
						
							|  |  |  | 						if (!livechannels) { | 
					
						
							|  |  |  | 							ast_verb(3, "no live channels left. exiting.\n"); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 							return NULL; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		} else | 
					
						
							|  |  |  | 			ast_debug(1, "timed out waiting for action\n"); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	/* --- WAIT FOR WINNER NUMBER END! -----------*/ | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 20:39:11 +00:00
										 |  |  | static void findmeexec(struct fm_args *tpargs) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct number *nm; | 
					
						
							|  |  |  | 	struct ast_channel *outbound; | 
					
						
							|  |  |  | 	struct ast_channel *caller; | 
					
						
							| 
									
										
										
										
											2006-06-06 16:54:52 +00:00
										 |  |  | 	struct ast_channel *winner = NULL; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	char dialarg[512]; | 
					
						
							|  |  |  | 	int dg, idx; | 
					
						
							|  |  |  | 	char *rest, *number; | 
					
						
							|  |  |  | 	struct findme_user *tmpuser; | 
					
						
							|  |  |  | 	struct findme_user *fmuser; | 
					
						
							|  |  |  | 	struct findme_user_listptr *findme_user_list; | 
					
						
							|  |  |  | 	int status; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 	findme_user_list = ast_calloc(1, sizeof(*findme_user_list)); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(findme_user_list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We're going to figure out what the longest possible string of digits to collect is */ | 
					
						
							|  |  |  | 	ynlongest = 0; | 
					
						
							|  |  |  | 	if (strlen(tpargs->takecall) > ynlongest) | 
					
						
							|  |  |  | 		ynlongest = strlen(tpargs->takecall); | 
					
						
							|  |  |  | 	if (strlen(tpargs->nextindp) > ynlongest) | 
					
						
							|  |  |  | 		ynlongest = strlen(tpargs->nextindp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx = 1; | 
					
						
							|  |  |  | 	caller = tpargs->chan; | 
					
						
							|  |  |  | 	AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) | 
					
						
							|  |  |  | 		if (nm->order == idx) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (nm) { | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 		ast_debug(2, "Number %s timeout %ld\n", nm->number,nm->timeout); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		number = ast_strdupa(nm->number); | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 		ast_debug(3, "examining %s\n", number); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		do { | 
					
						
							|  |  |  | 			rest = strchr(number, '&'); | 
					
						
							|  |  |  | 			if (rest) { | 
					
						
							|  |  |  | 				*rest = 0; | 
					
						
							|  |  |  | 				rest++; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-07 00:29:26 +00:00
										 |  |  | 			/* We check if that context exists, before creating the ast_channel struct needed */ | 
					
						
							|  |  |  | 			if (!ast_exists_extension(caller, tpargs->context, number, 1, S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL))) { | 
					
						
							|  |  |  | 				/* XXX Should probably restructure to simply skip this item, instead of returning. XXX */ | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Extension '%s@%s' doesn't exist\n", number, tpargs->context); | 
					
						
							|  |  |  | 				free(findme_user_list); | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			if (!strcmp(tpargs->context, "")) | 
					
						
							| 
									
										
										
										
											2008-03-13 20:26:59 +00:00
										 |  |  | 				snprintf(dialarg, sizeof(dialarg), "%s", number); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			else | 
					
						
							| 
									
										
										
										
											2008-03-13 20:26:59 +00:00
										 |  |  | 				snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			tmpuser = ast_calloc(1, sizeof(*tmpuser)); | 
					
						
							|  |  |  | 			if (!tmpuser) { | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 				ast_free(findme_user_list); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-26 15:28:53 +00:00
										 |  |  | 			outbound = ast_request("Local", ast_best_codec(caller->nativeformats), caller, dialarg, &dg); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			if (outbound) { | 
					
						
							| 
									
										
										
										
											2010-07-14 15:48:36 +00:00
										 |  |  | 				ast_set_callerid(outbound, | 
					
						
							|  |  |  | 					S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL), | 
					
						
							|  |  |  | 					S_COR(caller->caller.id.name.valid, caller->caller.id.name.str, NULL), | 
					
						
							|  |  |  | 					S_COR(caller->caller.id.number.valid, caller->caller.id.number.str, NULL)); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				ast_channel_inherit_variables(tpargs->chan, outbound); | 
					
						
							| 
									
										
										
										
											2009-09-15 16:04:41 +00:00
										 |  |  | 				ast_channel_datastore_inherit(tpargs->chan, outbound); | 
					
						
							|  |  |  | 				ast_string_field_set(outbound, language, tpargs->chan->language); | 
					
						
							|  |  |  | 				ast_string_field_set(outbound, accountcode, tpargs->chan->accountcode); | 
					
						
							|  |  |  | 				ast_string_field_set(outbound, musicclass, tpargs->chan->musicclass); | 
					
						
							| 
									
										
										
										
											2007-07-26 15:49:18 +00:00
										 |  |  | 				ast_verb(3, "calling %s\n", dialarg); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 				if (!ast_call(outbound,dialarg,0)) { | 
					
						
							|  |  |  | 					tmpuser->ochan = outbound; | 
					
						
							|  |  |  | 					tmpuser->state = 0; | 
					
						
							|  |  |  | 					tmpuser->cleared = 0; | 
					
						
							|  |  |  | 					ast_copy_string(tmpuser->dialarg, dialarg, sizeof(dialarg)); | 
					
						
							|  |  |  | 					AST_LIST_INSERT_TAIL(findme_user_list, tmpuser, entry); | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2007-07-26 15:49:18 +00:00
										 |  |  | 					ast_verb(3, "couldn't reach at this number.\n");  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 					if (outbound) { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 						if (!outbound->cdr)  | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 							outbound->cdr = ast_cdr_alloc(); | 
					
						
							|  |  |  | 						if (outbound->cdr) { | 
					
						
							|  |  |  | 							char tmp[256]; | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 20:37:51 +00:00
										 |  |  | 							ast_cdr_init(outbound->cdr, outbound); | 
					
						
							| 
									
										
										
										
											2006-06-06 20:47:46 +00:00
										 |  |  | 							snprintf(tmp, sizeof(tmp), "%s/%s", "Local", dialarg); | 
					
						
							|  |  |  | 							ast_cdr_setapp(outbound->cdr, "FollowMe", tmp); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 							ast_cdr_update(outbound); | 
					
						
							|  |  |  | 							ast_cdr_start(outbound->cdr); | 
					
						
							|  |  |  | 							ast_cdr_end(outbound->cdr); | 
					
						
							|  |  |  | 							/* If the cause wasn't handled properly */ | 
					
						
							|  |  |  | 							if (ast_cdr_disposition(outbound->cdr,outbound->hangupcause)) | 
					
						
							|  |  |  | 								ast_cdr_failed(outbound->cdr); | 
					
						
							|  |  |  | 						} else { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 							ast_log(LOG_ERROR, "Unable to create Call Detail Record\n"); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 							ast_hangup(outbound); | 
					
						
							|  |  |  | 							outbound = NULL; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else  | 
					
						
							|  |  |  | 				ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n", dialarg, ast_cause2str(dg)); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			number = rest; | 
					
						
							|  |  |  | 		} while (number); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		status = 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		if (!AST_LIST_EMPTY(findme_user_list)) | 
					
						
							|  |  |  | 			winner = wait_for_winner(findme_user_list, nm, caller, tpargs->namerecloc, &status, tpargs); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-08 05:28:47 +00:00
										 |  |  | 		while ((fmuser = AST_LIST_REMOVE_HEAD(findme_user_list, entry))) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			if (!fmuser->cleared && fmuser->ochan != winner) | 
					
						
							|  |  |  | 				clear_caller(fmuser); | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 			ast_free(fmuser); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-11-08 05:28:47 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		fmuser = NULL; | 
					
						
							|  |  |  | 		tmpuser = NULL; | 
					
						
							|  |  |  | 		if (winner) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-05 17:46:51 +00:00
										 |  |  | 		if (!caller || ast_check_hangup(caller)) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			tpargs->status = 1; | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 			ast_free(findme_user_list); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 			return; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		idx++; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 			if (nm->order == idx) | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 	ast_free(findme_user_list); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	if (!winner)  | 
					
						
							|  |  |  | 		tpargs->status = 1; | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		tpargs->status = 100; | 
					
						
							|  |  |  | 		tpargs->outbound = winner; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | static struct call_followme *find_realtime(const char *name) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-08-26 18:46:07 +00:00
										 |  |  | 	struct ast_variable *var = ast_load_realtime("followme", "name", name, SENTINEL), *v; | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	struct ast_config *cfg; | 
					
						
							|  |  |  | 	const char *catg; | 
					
						
							|  |  |  | 	struct call_followme *new; | 
					
						
							|  |  |  | 	struct ast_str *str = ast_str_create(16); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!var) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(new = alloc_profile(name))) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (v = var; v; v = v->next) { | 
					
						
							|  |  |  | 		if (!strcasecmp(v->name, "active")) { | 
					
						
							|  |  |  | 			if (ast_false(v->value)) { | 
					
						
							|  |  |  | 				ast_mutex_destroy(&new->lock); | 
					
						
							|  |  |  | 				ast_free(new); | 
					
						
							|  |  |  | 				return NULL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			profile_set_param(new, v->name, v->value, 0, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_variables_destroy(var); | 
					
						
							|  |  |  | 	new->realtime = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Load numbers */ | 
					
						
							| 
									
										
										
										
											2008-08-26 18:46:07 +00:00
										 |  |  | 	if (!(cfg = ast_load_realtime_multientry("followme_numbers", "ordinal LIKE", "%", "name", name, SENTINEL))) { | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 		ast_mutex_destroy(&new->lock); | 
					
						
							|  |  |  | 		ast_free(new); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { | 
					
						
							|  |  |  | 		const char *numstr, *timeoutstr, *ordstr; | 
					
						
							|  |  |  | 		int timeout; | 
					
						
							|  |  |  | 		struct number *cur; | 
					
						
							|  |  |  | 		if (!(numstr = ast_variable_retrieve(cfg, catg, "phonenumber"))) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-08-10 19:20:57 +00:00
										 |  |  | 		if (!(timeoutstr = ast_variable_retrieve(cfg, catg, "timeout")) || sscanf(timeoutstr, "%30d", &timeout) != 1 || timeout < 1) { | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 			timeout = 25; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* This one has to exist; it was part of the query */ | 
					
						
							|  |  |  | 		ordstr = ast_variable_retrieve(cfg, catg, "ordinal"); | 
					
						
							| 
									
										
										
										
											2008-12-13 08:36:35 +00:00
										 |  |  | 		ast_str_set(&str, 0, "%s", numstr); | 
					
						
							|  |  |  | 		if ((cur = create_followme_number(ast_str_buffer(str), timeout, atoi(ordstr)))) { | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 			AST_LIST_INSERT_TAIL(&new->numbers, cur, entry); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_config_destroy(cfg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return new; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-09 01:27:00 +00:00
										 |  |  | static void end_bridge_callback(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char buf[80]; | 
					
						
							|  |  |  | 	time_t end; | 
					
						
							|  |  |  | 	struct ast_channel *chan = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	time(&end); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_channel_lock(chan); | 
					
						
							|  |  |  | 	if (chan->cdr->answer.tv_sec) { | 
					
						
							| 
									
										
										
										
											2010-03-20 11:43:08 +00:00
										 |  |  | 		snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->answer.tv_sec); | 
					
						
							| 
									
										
										
										
											2008-11-09 01:27:00 +00:00
										 |  |  | 		pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (chan->cdr->start.tv_sec) { | 
					
						
							| 
									
										
										
										
											2010-03-20 11:43:08 +00:00
										 |  |  | 		snprintf(buf, sizeof(buf), "%ld", (long) end - chan->cdr->start.tv_sec); | 
					
						
							| 
									
										
										
										
											2008-11-09 01:27:00 +00:00
										 |  |  | 		pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_channel_unlock(chan); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-18 18:31:08 +00:00
										 |  |  | static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bconfig->end_bridge_callback_data = originator; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-21 21:13:09 +00:00
										 |  |  | static int app_exec(struct ast_channel *chan, const char *data) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-03-29 05:52:20 +00:00
										 |  |  | 	struct fm_args targs = { 0, }; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	struct ast_bridge_config config; | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	struct call_followme *f; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	struct number *nm, *newnm; | 
					
						
							|  |  |  | 	int res = 0; | 
					
						
							|  |  |  | 	char *argstr; | 
					
						
							|  |  |  | 	char namerecloc[255]; | 
					
						
							|  |  |  | 	int duration = 0; | 
					
						
							|  |  |  | 	struct ast_channel *caller; | 
					
						
							|  |  |  | 	struct ast_channel *outbound; | 
					
						
							|  |  |  | 	AST_DECLARE_APP_ARGS(args, | 
					
						
							|  |  |  | 		AST_APP_ARG(followmeid); | 
					
						
							|  |  |  | 		AST_APP_ARG(options); | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(data)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	if (!(argstr = ast_strdupa((char *)data))) { | 
					
						
							|  |  |  | 		ast_log(LOG_ERROR, "Out of memory!\n"); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_STANDARD_APP_ARGS(args, argstr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(args.followmeid)) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "%s requires an argument (followmeid)\n", app); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_RDLOCK(&followmes); | 
					
						
							|  |  |  | 	AST_RWLIST_TRAVERSE(&followmes, f, entry) { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 		if (!strcasecmp(f->name, args.followmeid) && (f->active)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_UNLOCK(&followmes); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 	ast_debug(1, "New profile %s.\n", args.followmeid); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	if (!f) { | 
					
						
							|  |  |  | 		f = find_realtime(args.followmeid); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (!f) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	/* XXX TODO: Reinsert the db check value to see whether or not follow-me is on or off */ | 
					
						
							|  |  |  | 	if (args.options)  | 
					
						
							|  |  |  | 		ast_app_parse_options(followme_opts, &targs.followmeflags, NULL, args.options); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	/* Lock the profile lock and copy out everything we need to run with before unlocking it again */ | 
					
						
							|  |  |  | 	ast_mutex_lock(&f->lock); | 
					
						
							|  |  |  | 	targs.mohclass = ast_strdupa(f->moh); | 
					
						
							|  |  |  | 	ast_copy_string(targs.context, f->context, sizeof(targs.context)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.takecall, f->takecall, sizeof(targs.takecall)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.nextindp, f->nextindp, sizeof(targs.nextindp)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.callfromprompt, f->callfromprompt, sizeof(targs.callfromprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.norecordingprompt, f->norecordingprompt, sizeof(targs.norecordingprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.optionsprompt, f->optionsprompt, sizeof(targs.optionsprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.plsholdprompt, f->plsholdprompt, sizeof(targs.plsholdprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.statusprompt, f->statusprompt, sizeof(targs.statusprompt)); | 
					
						
							|  |  |  | 	ast_copy_string(targs.sorryprompt, f->sorryprompt, sizeof(targs.sorryprompt)); | 
					
						
							|  |  |  | 	/* Copy the numbers we're going to use into another list in case the master list should get modified 
 | 
					
						
							|  |  |  | 	   (and locked) while we're trying to do a follow-me */ | 
					
						
							|  |  |  | 	AST_LIST_HEAD_INIT_NOLOCK(&targs.cnumbers); | 
					
						
							|  |  |  | 	AST_LIST_TRAVERSE(&f->numbers, nm, entry) { | 
					
						
							|  |  |  | 		newnm = create_followme_number(nm->number, nm->timeout, nm->order); | 
					
						
							|  |  |  | 		AST_LIST_INSERT_TAIL(&targs.cnumbers, newnm, entry); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_mutex_unlock(&f->lock); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-07 18:20:31 +00:00
										 |  |  | 	/* Answer the call */ | 
					
						
							|  |  |  | 	if (chan->_state != AST_STATE_UP) { | 
					
						
							|  |  |  | 		ast_answer(chan); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_STATUSMSG))  | 
					
						
							|  |  |  | 		ast_stream_and_wait(chan, targs.statusprompt, ""); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	snprintf(namerecloc,sizeof(namerecloc),"%s/followme.%s",ast_config_AST_SPOOL_DIR,chan->uniqueid); | 
					
						
							|  |  |  | 	duration = 5; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_RECORDNAME))  | 
					
						
							| 
									
										
										
										
											2011-09-20 22:38:54 +00:00
										 |  |  | 		if (ast_play_and_record(chan, "vm-rec-name", namerecloc, 5, "sln", &duration, NULL, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL) < 0) | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 			goto outrun; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-04 20:59:04 +00:00
										 |  |  | 	if (!ast_fileexists(namerecloc, NULL, chan->language)) | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		ast_copy_string(namerecloc, "", sizeof(namerecloc)); | 
					
						
							| 
									
										
										
										
											2009-11-23 22:37:39 +00:00
										 |  |  | 	if (!ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_DISABLEHOLDPROMPT)) { | 
					
						
							|  |  |  | 		if (ast_streamfile(chan, targs.plsholdprompt, chan->language)) | 
					
						
							|  |  |  | 			goto outrun; | 
					
						
							|  |  |  | 		if (ast_waitstream(chan, "") < 0) | 
					
						
							|  |  |  | 			goto outrun; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	ast_moh_start(chan, S_OR(targs.mohclass, NULL), NULL); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	targs.status = 0; | 
					
						
							|  |  |  | 	targs.chan = chan; | 
					
						
							|  |  |  | 	ast_copy_string(targs.namerecloc, namerecloc, sizeof(targs.namerecloc)); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	findmeexec(&targs); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-08 05:28:47 +00:00
										 |  |  | 	while ((nm = AST_LIST_REMOVE_HEAD(&targs.cnumbers, entry))) | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		ast_free(nm); | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (!ast_strlen_zero(namerecloc)) | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		unlink(namerecloc); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	if (targs.status != 100) { | 
					
						
							|  |  |  | 		ast_moh_stop(chan); | 
					
						
							|  |  |  | 		if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG))  | 
					
						
							|  |  |  | 			ast_stream_and_wait(chan, targs.sorryprompt, ""); | 
					
						
							|  |  |  | 		res = 0; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		caller = chan; | 
					
						
							|  |  |  | 		outbound = targs.outbound; | 
					
						
							|  |  |  | 		/* Bridge the two channels. */ | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		memset(&config, 0, sizeof(config)); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); | 
					
						
							|  |  |  | 		ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); | 
					
						
							|  |  |  | 		ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); | 
					
						
							| 
									
										
										
										
											2008-10-31 18:55:33 +00:00
										 |  |  | 		config.end_bridge_callback = end_bridge_callback; | 
					
						
							| 
									
										
										
										
											2008-11-09 01:27:00 +00:00
										 |  |  | 		config.end_bridge_callback_data = chan; | 
					
						
							| 
									
										
										
										
											2008-11-18 18:31:08 +00:00
										 |  |  | 		config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		ast_moh_stop(caller); | 
					
						
							|  |  |  | 		/* Be sure no generators are left on it */ | 
					
						
							|  |  |  | 		ast_deactivate_generator(caller); | 
					
						
							|  |  |  | 		/* Make sure channels are compatible */ | 
					
						
							|  |  |  | 		res = ast_channel_make_compatible(caller, outbound); | 
					
						
							|  |  |  | 		if (res < 0) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", caller->name, outbound->name); | 
					
						
							|  |  |  | 			ast_hangup(outbound); | 
					
						
							|  |  |  | 			goto outrun; | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 		res = ast_bridge_call(caller, outbound, &config); | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 		if (outbound) | 
					
						
							|  |  |  | 			ast_hangup(outbound); | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	outrun: | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-25 16:02:56 +00:00
										 |  |  | 	if (f->realtime) { | 
					
						
							|  |  |  | 		/* Not in list */ | 
					
						
							|  |  |  | 		free_numbers(f); | 
					
						
							|  |  |  | 		ast_free(f); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int unload_module(void) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-09-05 20:36:27 +00:00
										 |  |  | 	struct call_followme *f; | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 20:44:50 +00:00
										 |  |  | 	ast_unregister_application(app); | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	/* Free Memory. Yeah! I'm free! */ | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_WRLOCK(&followmes); | 
					
						
							|  |  |  | 	while ((f = AST_RWLIST_REMOVE_HEAD(&followmes, entry))) { | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 		free_numbers(f); | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 		ast_free(f); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-30 16:02:01 +00:00
										 |  |  | 	AST_RWLIST_UNLOCK(&followmes); | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-06 20:44:50 +00:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int load_module(void) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | 	if(!reload_followme(0)) | 
					
						
							| 
									
										
										
										
											2006-08-31 21:00:20 +00:00
										 |  |  | 		return AST_MODULE_LOAD_DECLINE; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-05 02:08:39 +00:00
										 |  |  | 	return ast_register_application_xml(app, app_exec); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | static int reload(void) | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-08-16 21:09:46 +00:00
										 |  |  | 	reload_followme(1); | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-05 16:23:44 +00:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2006-06-06 07:29:57 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-21 02:11:39 +00:00
										 |  |  | AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Find-Me/Follow-Me Application", | 
					
						
							|  |  |  | 		.load = load_module, | 
					
						
							|  |  |  | 		.unload = unload_module, | 
					
						
							|  |  |  | 		.reload = reload, | 
					
						
							|  |  |  | 	       ); |