Skip to content
Snippets Groups Projects
manager.c 68.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • static char mandescr_logoff[] = 
    "Description: Logoff this manager session\n"
    "Variables: NONE\n";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_logoff(struct mansession *s, struct message *m)
    {
    
    	astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    static char mandescr_hangup[] = 
    "Description: Hangup a channel\n"
    "Variables: \n"
    "	Channel: The channel name to be hungup\n";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_hangup(struct mansession *s, struct message *m)
    {
    	struct ast_channel *c = NULL;
    
    	char *name = astman_get_header(m, "Channel");
    
    	if (ast_strlen_zero(name)) {
    
    		astman_send_error(s, m, "No channel specified");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!c) {
    
    		astman_send_error(s, m, "No such channel");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    	ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
    
    	astman_send_ack(s, m, "Channel Hungup");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static char mandescr_setvar[] = 
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    "Description: Set a global or local channel variable.\n"
    
    "Variables: (Names marked with * are required)\n"
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    "	Channel: Channel to set variable for\n"
    
    "	*Variable: Variable name\n"
    "	*Value: Value\n";
    
    
    static int action_setvar(struct mansession *s, struct message *m)
    {
            struct ast_channel *c = NULL;
            char *name = astman_get_header(m, "Channel");
            char *varname = astman_get_header(m, "Variable");
            char *varval = astman_get_header(m, "Value");
    	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (ast_strlen_zero(varname)) {
    
    		astman_send_error(s, m, "No variable specified");
    		return 0;
    	}
    
    	
    	if (ast_strlen_zero(varval)) {
    		astman_send_error(s, m, "No value specified");
    		return 0;
    	}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (!ast_strlen_zero(name)) {
    		c = ast_get_channel_by_name_locked(name);
    		if (!c) {
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    
    	pbx_builtin_setvar_helper(c, varname, varval);
    
    
    	astman_send_ack(s, m, "Variable Set");	
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    
    
    static char mandescr_getvar[] = 
    
    "Description: Get the value of a global or local channel variable.\n"
    
    "Variables: (Names marked with * are required)\n"
    
    "	Channel: Channel to read variable from\n"
    
    "	*Variable: Variable name\n"
    "	ActionID: Optional Action id for message matching.\n";
    
    
    static int action_getvar(struct mansession *s, struct message *m)
    {
    
    	struct ast_channel *c = NULL;
    	char *name = astman_get_header(m, "Channel");
    	char *varname = astman_get_header(m, "Variable");
    
    	char *id = astman_get_header(m,"ActionID");
    
    		astman_send_error(s, m, "No variable specified");
    		return 0;
    	}
    
    
    		c = ast_get_channel_by_name_locked(name);
    		if (!c) {
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    
    		ast_func_read(c, varname, workspace, sizeof(workspace));
    
    	} else {
    		pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
    	}
    
    	astman_append(s, "Response: Success\r\n"
    
    		"Variable: %s\r\nValue: %s\r\n", varname, varval);
    
    		astman_append(s, "ActionID: %s\r\n",id);
    	astman_append(s, "\r\n");
    
    /*! \brief  action_status: Manager "status" command to show channels */
    
    /* Needs documentation... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_status(struct mansession *s, struct message *m)
    {
    
    	char *id = astman_get_header(m,"ActionID");
    
        	char *name = astman_get_header(m,"Channel");
    
    	char idText[256] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *c;
    	char bridge[256];
    
    	long elapsed_seconds = 0;
    
    	int all = ast_strlen_zero(name); /* set if we want all channels */
    
    	astman_send_ack(s, m, "Channel status will follow");
    
                    snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    	if (all)
    		c = ast_channel_walk_locked(NULL);
    	else {
    		c = ast_get_channel_by_name_locked(name);
    
    		if (!c) {
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    	}
    
    	/* if we look by name, we break after the first iteration */
    
    		if (c->_bridge)
    			snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->pbx) {
    
    			if (c->cdr) {
    				elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Event: Status\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Channel: %s\r\n"
    
    			"CallerID: %s\r\n"		/* This parameter is deprecated and will be removed post-1.4 */
    			"CallerIDNum: %s\r\n"
    
    			"CallerIDName: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"State: %s\r\n"
    			"Context: %s\r\n"
    			"Extension: %s\r\n"
    			"Priority: %d\r\n"
    
    			"Seconds: %ld\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"%s"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Uniqueid: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"\r\n",
    
    			S_OR(c->cid.cid_num, "<unknown>"), 
    			S_OR(c->cid.cid_num, "<unknown>"), 
    			S_OR(c->cid.cid_name, "<unknown>"), 
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_state2str(c->_state), c->context,
    
    			c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Event: Status\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Channel: %s\r\n"
    
    			"CallerID: %s\r\n"		/* This parameter is deprecated and will be removed post-1.4 */
    			"CallerIDNum: %s\r\n"
    
    			"CallerIDName: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"State: %s\r\n"
    			"%s"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Uniqueid: %s\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"\r\n",
    
    			S_OR(c->cid.cid_num, "<unknown>"), 
    			S_OR(c->cid.cid_num, "<unknown>"), 
    			S_OR(c->cid.cid_name, "<unknown>"), 
    
    			ast_state2str(c->_state), bridge, c->uniqueid, idText);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		c = ast_channel_walk_locked(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	"Event: StatusComplete\r\n"
    	"%s"
    	"\r\n",idText);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static char mandescr_redirect[] = 
    "Description: Redirect (transfer) a call.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Channel: Channel to redirect\n"
    "	ExtraChannel: Second call leg to transfer (optional)\n"
    "	*Exten: Extension to transfer to\n"
    "	*Context: Context to transfer to\n"
    "	*Priority: Priority to transfer to\n"
    "	ActionID: Optional Action id for message matching.\n";
    
    
    /*! \brief  action_redirect: The redirect manager command */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_redirect(struct mansession *s, struct message *m)
    {
    
    	char *name = astman_get_header(m, "Channel");
    	char *name2 = astman_get_header(m, "ExtraChannel");
    	char *exten = astman_get_header(m, "Exten");
    	char *context = astman_get_header(m, "Context");
    	char *priority = astman_get_header(m, "Priority");
    
    	struct ast_channel *chan, *chan2 = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pi = 0;
    	int res;
    
    		astman_send_error(s, m, "Channel not specified");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
    
    		if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
    			astman_send_error(s, m, "Invalid priority\n");
    			return 0;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* XXX watch out, possible deadlock!!! */
    
    	chan = ast_get_channel_by_name_locked(name);
    
    		char buf[BUFSIZ];
    		snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
    		astman_send_error(s, m, buf);
    
    	if (!ast_strlen_zero(name2))
    
    		chan2 = ast_get_channel_by_name_locked(name2);
    	res = ast_async_goto(chan, context, exten, pi);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res) {
    
    		if (!ast_strlen_zero(name2)) {
    
    			if (chan2)
    				res = ast_async_goto(chan2, context, exten, pi);
    			else
    				res = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!res)
    
    				astman_send_ack(s, m, "Dual Redirect successful");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else
    
    				astman_send_error(s, m, "Secondary redirect failed");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else
    
    			astman_send_ack(s, m, "Redirect successful");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    
    		astman_send_error(s, m, "Redirect failed");
    
    		ast_channel_unlock(chan);
    
    		ast_channel_unlock(chan2);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static char mandescr_command[] = 
    "Description: Run a CLI command.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Command: Asterisk CLI command to run\n"
    "	ActionID: Optional Action id for message matching.\n";
    
    /*! \brief  action_command: Manager command "command" - execute CLI command */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_command(struct mansession *s, struct message *m)
    {
    
    	char *cmd = astman_get_header(m, "Command");
    
    	char *id = astman_get_header(m, "ActionID");
    
    	astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
    
    		astman_append(s, "ActionID: %s\r\n", id);
    
    	/* FIXME: Wedge a ActionID response in here, waiting for later changes */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli_command(s->fd, cmd);
    
    	astman_append(s, "--END COMMAND--\r\n\r\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static void *fast_originate(void *data)
    {
    	struct fast_originate_helper *in = data;
    	int res;
    	int reason = 0;
    
    	if (!ast_strlen_zero(in->app)) {
    
    		res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
    
    			S_OR(in->cid_num, NULL), 
    			S_OR(in->cid_name, NULL),
    
    			in->vars, in->account, &chan);
    
    		res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
    
    			S_OR(in->cid_num, NULL), 
    			S_OR(in->cid_name, NULL),
    
    			in->vars, in->account, &chan);
    
    	
    	/* Tell the manager what happened with the channel */
    	manager_event(EVENT_FLAG_CALL,
    
    		res ? "OriginateFailure" : "OriginateSuccess",
    
    		"%s"
    		"Channel: %s/%s\r\n"
    		"Context: %s\r\n"
    		"Exten: %s\r\n"
    		"Reason: %d\r\n"
    		"Uniqueid: %s\r\n"
    
    		"CallerID: %s\r\n"		/* This parameter is deprecated and will be removed post-1.4 */
    		"CallerIDNum: %s\r\n"
    
    		"CallerIDName: %s\r\n",
    		in->idtext, in->tech, in->data, in->context, in->exten, reason, 
    		chan ? chan->uniqueid : "<null>",
    
    		S_OR(in->cid_num, "<unknown>"),
    		S_OR(in->cid_num, "<unknown>"),
    		S_OR(in->cid_name, "<unknown>")
    
    	/* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
    	if (chan)
    
    		ast_channel_unlock(chan);
    
    static char mandescr_originate[] = 
    "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
    "  Application/Data\n"
    "Variables: (Names marked with * are required)\n"
    "	*Channel: Channel name to call\n"
    "	Exten: Extension to use (requires 'Context' and 'Priority')\n"
    "	Context: Context to use (requires 'Exten' and 'Priority')\n"
    "	Priority: Priority to use (requires 'Exten' and 'Context')\n"
    "	Application: Application to use\n"
    "	Data: Data to use (requires 'Application')\n"
    "	Timeout: How long to wait for call to be answered (in ms)\n"
    "	CallerID: Caller ID to be set on the outgoing channel\n"
    
    "	Variable: Channel variable to set, multiple Variable: headers are allowed\n"
    
    "	Account: Account code\n"
    "	Async: Set to 'true' for fast origination\n";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int action_originate(struct mansession *s, struct message *m)
    {
    
    	char *name = astman_get_header(m, "Channel");
    	char *exten = astman_get_header(m, "Exten");
    	char *context = astman_get_header(m, "Context");
    	char *priority = astman_get_header(m, "Priority");
    	char *timeout = astman_get_header(m, "Timeout");
    	char *callerid = astman_get_header(m, "CallerID");
    
    	char *account = astman_get_header(m, "Account");
    
    	char *app = astman_get_header(m, "Application");
    	char *appdata = astman_get_header(m, "Data");
    
    	char *async = astman_get_header(m, "Async");
    
    	char *id = astman_get_header(m, "ActionID");
    
    	struct ast_variable *vars = astman_get_variables(m);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *tech, *data;
    
    	char *l = NULL, *n = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pi = 0;
    	int res;
    	int to = 30000;
    	int reason = 0;
    	char tmp[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!name) {
    
    		astman_send_error(s, m, "Channel not specified");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
    
    		if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
    			astman_send_error(s, m, "Invalid priority\n");
    			return 0;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
    
    		astman_send_error(s, m, "Invalid timeout\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	ast_copy_string(tmp, name, sizeof(tmp));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tech = tmp;
    	data = strchr(tmp, '/');
    	if (!data) {
    
    		astman_send_error(s, m, "Invalid channel\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	*data++ = '\0';
    
    	ast_copy_string(tmp2, callerid, sizeof(tmp2));
    
    	ast_callerid_parse(tmp2, &n, &l);
    	if (n) {
    		if (ast_strlen_zero(n))
    			n = NULL;
    	}
    	if (l) {
    		ast_shrink_phone_number(l);
    		if (ast_strlen_zero(l))
    			l = NULL;
    	}
    
    		struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
    
    			memset(fast, 0, sizeof(struct fast_originate_helper));
    
    				snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
    
    			ast_copy_string(fast->tech, tech, sizeof(fast->tech));
       			ast_copy_string(fast->data, data, sizeof(fast->data));
    			ast_copy_string(fast->app, app, sizeof(fast->app));
    			ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
    
    				ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
    
    				ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
    
    			ast_copy_string(fast->context, context, sizeof(fast->context));
    			ast_copy_string(fast->exten, exten, sizeof(fast->exten));
    
    			ast_copy_string(fast->account, account, sizeof(fast->account));
    
    			fast->timeout = to;
    			fast->priority = pi;
    			pthread_attr_init(&attr);
    			pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    			if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
    
    	} else if (!ast_strlen_zero(app)) {
    
            	res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
    
    	        	res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
    
    		else {
    			astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
    			return 0;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res)
    
    		astman_send_ack(s, m, "Originate successfully queued");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		astman_send_error(s, m, "Originate failed");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*! 	\brief Help text for manager command mailboxstatus
     */
    
    static char mandescr_mailboxstatus[] = 
    "Description: Checks a voicemail account for status.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
    "	ActionID: Optional ActionID for message matching.\n"
    "Returns number of messages.\n"
    "	Message: Mailbox Status\n"
    "	Mailbox: <mailboxid>\n"
    "	Waiting: <count>\n"
    "\n";
    
    static int action_mailboxstatus(struct mansession *s, struct message *m)
    {
    
    	char *mailbox = astman_get_header(m, "Mailbox");
    
    	char *id = astman_get_header(m,"ActionID");
    	char idText[256] = "";
    
    	if (ast_strlen_zero(mailbox)) {
    
    		astman_send_error(s, m, "Mailbox not specified");
    
                    snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    	ret = ast_app_has_voicemail(mailbox, NULL);
    
    	astman_append(s, "Response: Success\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    				   "Message: Mailbox Status\r\n"
    				   "Mailbox: %s\r\n"
    
    		 		   "Waiting: %d\r\n\r\n", idText, mailbox, ret);
    
    static char mandescr_mailboxcount[] = 
    "Description: Checks a voicemail account for new messages.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
    "	ActionID: Optional ActionID for message matching.\n"
    "Returns number of new and old messages.\n"
    "	Message: Mailbox Message Count\n"
    "	Mailbox: <mailboxid>\n"
    "	NewMessages: <count>\n"
    "	OldMessages: <count>\n"
    "\n";
    
    static int action_mailboxcount(struct mansession *s, struct message *m)
    {
    	char *mailbox = astman_get_header(m, "Mailbox");
    
    	char *id = astman_get_header(m,"ActionID");
    	char idText[256] = "";
    
    	int newmsgs = 0, oldmsgs = 0;
    
    	if (ast_strlen_zero(mailbox)) {
    
    		astman_send_error(s, m, "Mailbox not specified");
    
    		snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
    
    	astman_append(s, "Response: Success\r\n"
    
    				   "Message: Mailbox Message Count\r\n"
    				   "Mailbox: %s\r\n"
    		 		   "NewMessages: %d\r\n"
    				   "OldMessages: %d\r\n" 
    				   "\r\n",
    
    				    idText,mailbox, newmsgs, oldmsgs);
    
    static char mandescr_extensionstate[] = 
    "Description: Report the extension state for given extension.\n"
    "  If the extension has a hint, will use devicestate to check\n"
    "  the status of the device connected to the extension.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Exten: Extension to check state on\n"
    "	*Context: Context for extension\n"
    "	ActionId: Optional ID for this transaction\n"
    "Will return an \"Extension Status\" message.\n"
    "The response will include the hint for the extension and the status.\n";
    
    
    static int action_extensionstate(struct mansession *s, struct message *m)
    {
    	char *exten = astman_get_header(m, "Exten");
    	char *context = astman_get_header(m, "Context");
    
    	char *id = astman_get_header(m,"ActionID");
    	char idText[256] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char hint[256] = "";
    
    		astman_send_error(s, m, "Extension not specified");
    
    		context = "default";
    	status = ast_extension_state(NULL, context, exten);
    
    	ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
    
                    snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    	astman_append(s, "Response: Success\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    				   "Message: Extension Status\r\n"
    				   "Exten: %s\r\n"
    				   "Context: %s\r\n"
    				   "Hint: %s\r\n"
    
    		 		   "Status: %d\r\n\r\n",
    				   idText,exten, context, hint, status);
    
    static char mandescr_timeout[] = 
    "Description: Hangup a channel after a certain time.\n"
    "Variables: (Names marked with * are required)\n"
    "	*Channel: Channel name to hangup\n"
    "	*Timeout: Maximum duration of the call (sec)\n"
    "Acknowledges set time with 'Timeout Set' message\n";
    
    
    static int action_timeout(struct mansession *s, struct message *m)
    {
    	struct ast_channel *c = NULL;
    	char *name = astman_get_header(m, "Channel");
    	int timeout = atoi(astman_get_header(m, "Timeout"));
    
    	if (ast_strlen_zero(name)) {
    
    		astman_send_error(s, m, "No channel specified");
    
    		return 0;
    	}
    	if (!timeout) {
    
    		astman_send_error(s, m, "No timeout specified");
    
    		astman_send_error(s, m, "No such channel");
    
    		return 0;
    	}
    	ast_channel_setwhentohangup(c, timeout);
    
    	astman_send_ack(s, m, "Timeout Set");
    
    static int process_events(struct mansession *s)
    {
    	struct eventqent *eqe;
    	int ret = 0;
    	ast_mutex_lock(&s->__lock);
    	if (s->fd > -1) {
    		s->busy--;
    		if (!s->eventq)
    			s->eventq = master_eventq;
    		while(s->eventq->next) {
    			eqe = s->eventq->next;
    			if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
    			    ((s->send_events & eqe->category) == eqe->category)) {
    				if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
    					ret = -1;
    			}
    			unuse_eventqent(s->eventq);
    			s->eventq = eqe;
    		}
    	}
    	ast_mutex_unlock(&s->__lock);
    	return ret;
    }
    
    
    static char mandescr_userevent[] =
    "Description: Send an event to manager sessions.\n"
    "Variables: (Names marked with * are required)\n"
    "       *UserEvent: EventStringToSend\n"
    "       Header1: Content1\n"
    "       HeaderN: ContentN\n";
    
    static int action_userevent(struct mansession *s, struct message *m)
    {
    	char *event = astman_get_header(m, "UserEvent");
    	char body[2048] = "";
    	int x, bodylen = 0;
    	for (x = 0; x < m->hdrcount; x++) {
    		if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
    			ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
    			bodylen += strlen(m->headers[x]);
    			ast_copy_string(body + bodylen, "\r\n", 3);
    			bodylen += 2;
    		}
    	}
    
    	manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int process_message(struct mansession *s, struct message *m)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct manager_action *tmp = first_action;
    
    	char *id = astman_get_header(m,"ActionID");
    	char idText[256] = "";
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
    
    	ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
    
    	if (ast_strlen_zero(action)) {
    
    		astman_send_error(s, m, "Missing action in request");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	if (!ast_strlen_zero(id)) {
    
    		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!s->authenticated) {
    
    		if (!strcasecmp(action, "Challenge")) {
    			char *authtype;
    
    			authtype = astman_get_header(m, "AuthType");
    
    			if (!strcasecmp(authtype, "MD5")) {
    
    				if (ast_strlen_zero(s->challenge))
    
    					snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
    
    				ast_mutex_lock(&s->__lock);
    
    				astman_append(s, "Response: Success\r\n"
    
    						"%s"
    						"Challenge: %s\r\n\r\n",
    
    						idText, s->challenge);
    
    				ast_mutex_unlock(&s->__lock);
    
    				return 0;
    			} else {
    
    				astman_send_error(s, m, "Must specify AuthType");
    
    				return 0;
    			}
    		} else if (!strcasecmp(action, "Login")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (authenticate(s, m)) {
    				sleep(1);
    
    				astman_send_error(s, m, "Authentication failed");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1;
    			} else {
    				s->authenticated = 1;
    
    					if (displayconnects) {
    
    						ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    
    				ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    
    				astman_send_ack(s, m, "Authentication accepted");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		} else if (!strcasecmp(action, "Logoff")) {
    
    			astman_send_ack(s, m, "See ya");
    
    			astman_send_error(s, m, "Authentication Required");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		ast_mutex_lock(&s->__lock);
    
    		ast_mutex_unlock(&s->__lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!strcasecmp(action, tmp->action)) {
    				if ((s->writeperm & tmp->authority) == tmp->authority) {
    					if (tmp->func(s, m))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    
    					astman_send_error(s, m, "Permission denied");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			tmp = tmp->next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		if (!tmp)
    
    			astman_send_error(s, m, "Invalid/unknown command");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (ret)
    		return ret;
    	return process_events(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int get_input(struct mansession *s, char *output)
    {
    	/* output must have at least sizeof(s->inbuf) space */
    	int res;
    	int x;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	for (x = 1; x < s->inlen; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
    			/* Copy output data up to and including \r\n */
    			memcpy(output, s->inbuf, x + 1);
    			/* Add trailing \0 */
    			output[x+1] = '\0';
    			/* Move remaining data back to the front */
    			memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
    			s->inlen -= (x + 1);
    			return 1;
    		}
    	} 
    	if (s->inlen >= sizeof(s->inbuf) - 1) {
    
    		ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		s->inlen = 0;
    	}
    
    	fds[0].fd = s->fd;
    	fds[0].events = POLLIN;
    
    		ast_mutex_lock(&s->__lock);
    		s->waiting_thread = pthread_self();
    		ast_mutex_unlock(&s->__lock);
    
    
    		res = poll(fds, 1, -1);
    
    
    		ast_mutex_lock(&s->__lock);
    		s->waiting_thread = AST_PTHREADT_NULL;
    		ast_mutex_unlock(&s->__lock);
    
    		if (res < 0) {
    
    			if (errno == EINTR) {
    				if (s->dead)
    					return -1;
    
    			ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
    	 		return -1;
    		} else if (res > 0) {
    
    			ast_mutex_lock(&s->__lock);
    
    			res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
    
    			ast_mutex_unlock(&s->__lock);
    
    			if (res < 1)
    				return -1;
    			break;
    		}
    	} while(1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	s->inlen += res;
    	s->inbuf[s->inlen] = '\0';
    	return 0;
    }
    
    static void *session_do(void *data)
    {
    	struct mansession *s = data;
    	struct message m;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    	
    
    	ast_mutex_lock(&s->__lock);
    
    	astman_append(s, "Asterisk Call Manager/1.0\r\n");
    
    	ast_mutex_unlock(&s->__lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (;;) {
    		res = get_input(s, m.headers[m.hdrcount]);
    		if (res > 0) {
    			/* Strip trailing \r\n */
    			if (strlen(m.headers[m.hdrcount]) < 2)
    				continue;
    			m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
    
    			if (ast_strlen_zero(m.headers[m.hdrcount])) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (process_message(s, &m))
    					break;
    
    			} else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				m.hdrcount++;
    
    		} else if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    		} else if (s->eventq->next) {
    			if (process_events(s))
    				break;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if (s->authenticated) {
    
    		if (option_verbose > 1) {
    			if (displayconnects) 
    
    				ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    
    		ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    			if (displayconnects)
    
    				ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    		}
    
    		ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	destroy_session(s);
    	return NULL;
    }
    
    static void *accept_thread(void *ignore)
    {
    	int as;
    	struct sockaddr_in sin;
    
    	struct eventqent *eqe;
    
    	struct mansession *s, *prev = NULL, *next;
    
    	struct protoent *p;
    	int arg = 1;
    
    	int flags;
    
    	pthread_attr_t attr;
    
    
    	pthread_attr_init(&attr);
    	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (;;) {
    
    			next = s->next;
    			if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
    
    				num_sessions--;
    
    				if (prev)
    					prev->next = next;
    				else
    					sessions = next;
    				if (s->authenticated && (option_verbose > 1) && displayconnects) {
    					ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
    						s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
    				}
    				free_session(s);
    			} else
    				prev = s;
    			s = next;
    		}
    
    		/* Purge master event queue of old, unused events, but make sure we
    		   always keep at least one in the queue */
    		eqe = master_eventq;
    		while (master_eventq->next && !master_eventq->usecount) {
    			eqe = master_eventq;
    			master_eventq = master_eventq->next;
    			free(eqe);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		sinlen = sizeof(sin);
    
    		pfds[0].fd = asock;
    		pfds[0].events = POLLIN;
    		/* Wait for something to happen, but timeout every few seconds so
    		   we can ditch any old manager sessions */
    		if (poll(pfds, 1, 5000) < 1)
    			continue;
    
    		as = accept(asock, (struct sockaddr *)&sin, &sinlen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (as < 0) {
    			ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
    			continue;
    		}
    
    		p = getprotobyname("tcp");
    
    			if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
    				ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		s = malloc(sizeof(struct mansession));
    		if (!s) {
    			ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
    			continue;
    		} 
    		memset(s, 0, sizeof(struct mansession));
    		memcpy(&s->sin, &sin, sizeof(sin));
    
    		if (!block_sockets) {
    
    			/* For safety, make sure socket is non-blocking */
    			flags = fcntl(as, F_GETFL);
    			fcntl(as, F_SETFL, flags | O_NONBLOCK);
    		}
    
    		ast_mutex_init(&s->__lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		s->fd = as;
    
    		num_sessions++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		s->next = sessions;
    		sessions = s;
    
    		/* Find the last place in the master event queue and hook ourselves
    		   in there */
    		s->eventq = master_eventq;
    		while(s->eventq->next)
    			s->eventq = s->eventq->next;
    		ast_mutex_lock(&s->eventq->lock);
    		s->eventq->usecount++;
    		ast_mutex_unlock(&s->eventq->lock);
    
    		if (ast_pthread_create(&s->t, &attr, session_do, s))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			destroy_session(s);
    	}
    
    	pthread_attr_destroy(&attr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return NULL;
    
    static int append_event(const char *str, int category)
    
    	struct eventqent *tmp, *prev = NULL;
    
    	tmp = malloc(sizeof(struct eventqent) + strlen(str));
    	if (tmp) {
    
    		ast_mutex_init(&tmp->lock);
    
    		tmp->category = category;
    
    		strcpy(tmp->eventdata, str);
    
    		if (master_eventq) {
    			prev = master_eventq;
    
    			while (prev->next) 
    
    				prev = prev->next;
    			prev->next = tmp;
    		} else {
    
    			master_eventq = tmp;
    
    		tmp->usecount = num_sessions;
    
    /*! \brief  manager_event: Send AMI event to client */
    
    int manager_event(int category, const char *event, const char *fmt, ...)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct mansession *s;
    
    	char *tmp_next = tmp;
    	size_t tmp_left = sizeof(tmp) - 2;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	va_list ap;
    
    	struct timeval now;
    
    	/* Abort if there aren't any manager sessions */
    	if (!num_sessions)
    		return 0;
    
    	ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
    			 event, authority_to_str(category, auth, sizeof(auth)));
    	if (timestampevents) {
    		now = ast_tvnow();
    		ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
    				 now.tv_sec, (unsigned long) now.tv_usec);
    	}
    	va_start(ap, fmt);
    	ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
    	va_end(ap);
    	*tmp_next++ = '\r';
    	*tmp_next++ = '\n';