Mon Mar 12 2012 21:40:58

Asterisk developer's documentation


manager.c File Reference

The Asterisk Management Interface - AMI. More...

#include "asterisk.h"
#include "asterisk/_private.h"
#include "asterisk/paths.h"
#include <ctype.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <regex.h>
#include "asterisk/channel.h"
#include "asterisk/file.h"
#include "asterisk/manager.h"
#include "asterisk/module.h"
#include "asterisk/config.h"
#include "asterisk/callerid.h"
#include "asterisk/lock.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/pbx.h"
#include "asterisk/md5.h"
#include "asterisk/acl.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/http.h"
#include "asterisk/ast_version.h"
#include "asterisk/threadstorage.h"
#include "asterisk/linkedlists.h"
#include "asterisk/term.h"
#include "asterisk/astobj2.h"
#include "asterisk/features.h"
#include "asterisk/security_events.h"
#include "asterisk/aoc.h"
#include "asterisk/stringfields.h"
Include dependency graph for manager.c:

Go to the source code of this file.

Data Structures

struct  actions
 list of actions registered More...
struct  all_events
struct  ast_manager_user
 user descriptor, as read from the config file. More...
struct  channelvars
struct  eventqent
struct  fast_originate_helper
 helper function for originate More...
struct  manager_channel_variable
struct  manager_hooks
 list of hooks registered More...
struct  mansession
struct  mansession_session::mansession_datastores
struct  mansession_session
struct  permalias
struct  users
 list of users found in the config file More...
struct  variable_count

Defines

#define ASTMAN_APPEND_BUF_INITSIZE   256
 initial allocated size for the astman_append_buf
#define DEFAULT_REALM   "asterisk"
#define FORMAT   " %-25.25s %-15.15s\n"
#define FORMAT2   " %-25.25s %-15d\n"
#define GET_HEADER_FIRST_MATCH   0
#define GET_HEADER_LAST_MATCH   1
#define GET_HEADER_SKIP_EMPTY   2
#define HSMC_FORMAT   " %-15.15s %-15.15s %-55.55s\n"
#define HSMCONN_FORMAT1   " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
#define HSMCONN_FORMAT2   " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
#define MANAGER_EVENT_BUF_INITSIZE   256
#define MAX_BLACKLIST_CMD_LEN   2
 Descriptor for a manager session, either on the AMI socket or over HTTP.
#define MSG_MOREDATA   ((char *)astman_send_response)
 send a response with an optional message, and terminate it with an empty line. m is used only to grab the 'ActionID' field.
#define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
#define TEST_STRING   "<form action=\"manager\" method=\"post\">\n\ Action: <select name=\"action\">\n\ <option value=\"\">-----&gt;</option>\n\ <option value=\"login\">login</option>\n\ <option value=\"command\">Command</option>\n\ <option value=\"waitevent\">waitevent</option>\n\ <option value=\"listcommands\">listcommands</option>\n\ </select>\n\ or <input name=\"action\"><br/>\n\ CLI Command <input name=\"command\"><br>\n\ user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\ <input type=\"submit\">\n</form>\n"

Enumerations

enum  error_type {
  UNKNOWN_ACTION = 1, UNKNOWN_CATEGORY, UNSPECIFIED_CATEGORY, UNSPECIFIED_ARGUMENT,
  FAILURE_ALLOCATION, FAILURE_NEWCAT, FAILURE_DELCAT, FAILURE_EMPTYCAT,
  FAILURE_UPDATE, FAILURE_DELETE, FAILURE_APPEND
}
enum  output_format { FORMAT_RAW, FORMAT_HTML, FORMAT_XML }

Functions

int __ast_manager_event_multichan (int category, const char *event, int chancount, struct ast_channel **chans, const char *file, int line, const char *func, const char *fmt,...)
static const char * __astman_get_header (const struct message *m, char *var, int mode)
 Return a matching header value.
static void __init_astman_append_buf (void)
 thread local buffer for astman_append
static int __init_manager (int reload)
static void __init_manager_event_buf (void)
static void __init_manager_event_funcbuf (void)
static void __init_userevent_buf (void)
static int action_aocmessage (struct mansession *s, const struct message *m)
static int action_atxfer (struct mansession *s, const struct message *m)
static int action_challenge (struct mansession *s, const struct message *m)
static int action_command (struct mansession *s, const struct message *m)
 Manager command "command" - execute CLI command.
static int action_coresettings (struct mansession *s, const struct message *m)
 Show PBX core settings information.
static int action_coreshowchannels (struct mansession *s, const struct message *m)
 Manager command "CoreShowChannels" - List currently defined channels and some information about them.
static int action_corestatus (struct mansession *s, const struct message *m)
 Show PBX core status information.
static int action_createconfig (struct mansession *s, const struct message *m)
static void action_destroy (void *obj)
static int action_events (struct mansession *s, const struct message *m)
static int action_extensionstate (struct mansession *s, const struct message *m)
static struct manager_actionaction_find (const char *name)
static int action_getconfig (struct mansession *s, const struct message *m)
static int action_getconfigjson (struct mansession *s, const struct message *m)
static int action_getvar (struct mansession *s, const struct message *m)
static int action_hangup (struct mansession *s, const struct message *m)
static int action_listcategories (struct mansession *s, const struct message *m)
static int action_listcommands (struct mansession *s, const struct message *m)
static int action_login (struct mansession *s, const struct message *m)
static int action_logoff (struct mansession *s, const struct message *m)
static int action_mailboxcount (struct mansession *s, const struct message *m)
static int action_mailboxstatus (struct mansession *s, const struct message *m)
static int action_originate (struct mansession *s, const struct message *m)
static int action_ping (struct mansession *s, const struct message *m)
static int action_redirect (struct mansession *s, const struct message *m)
 action_redirect: The redirect manager command
static int action_reload (struct mansession *s, const struct message *m)
 Send a reload event.
static int action_sendtext (struct mansession *s, const struct message *m)
static int action_setvar (struct mansession *s, const struct message *m)
static int action_status (struct mansession *s, const struct message *m)
 Manager "status" command to show channels.
static int action_timeout (struct mansession *s, const struct message *m)
static int action_updateconfig (struct mansession *s, const struct message *m)
static int action_userevent (struct mansession *s, const struct message *m)
static int action_waitevent (struct mansession *s, const struct message *m)
static struct eventqentadvance_event (struct eventqent *e)
static int aocmessage_get_unit_entry (const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
static void append_channel_vars (struct ast_str **pbuf, struct ast_channel *chan)
static int append_event (const char *str, int category)
int ast_hook_send_action (struct manager_custom_hook *hook, const char *msg)
 Registered hooks can call this function to invoke actions and they will receive responses through registered callback.
static int ast_instring (const char *bigstr, const char *smallstr, const char delim)
int ast_manager_register2 (const char *action, int auth, int(*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
 register a new command with manager, including online help. This is the preferred way to register a manager command
void ast_manager_register_hook (struct manager_custom_hook *hook)
 Add a custom hook to be called when an event is fired.
static int ast_manager_register_struct (struct manager_action *act)
int ast_manager_unregister (char *action)
 Unregister a registered manager command.
void ast_manager_unregister_hook (struct manager_custom_hook *hook)
 Delete a custom hook to be called when an event is fired.
void astman_append (struct mansession *s, const char *fmt,...)
static void astman_append_json (struct mansession *s, const char *str)
int astman_datastore_add (struct mansession *s, struct ast_datastore *datastore)
 Add a datastore to a session.
struct ast_datastoreastman_datastore_find (struct mansession *s, const struct ast_datastore_info *info, const char *uid)
 Find a datastore on a session.
int astman_datastore_remove (struct mansession *s, struct ast_datastore *datastore)
 Remove a datastore from a session.
const char * astman_get_header (const struct message *m, char *var)
 Return the first matching variable from an array.
struct ast_variableastman_get_variables (const struct message *m)
 Get a linked list of the Variable: headers.
int astman_is_authed (uint32_t ident)
 Determinie if a manager session ident is authenticated.
void astman_send_ack (struct mansession *s, const struct message *m, char *msg)
 Send ack in manager transaction.
void astman_send_error (struct mansession *s, const struct message *m, char *error)
 Send error in manager transaction.
void astman_send_listack (struct mansession *s, const struct message *m, char *msg, char *listflag)
 Send ack in manager list transaction.
void astman_send_response (struct mansession *s, const struct message *m, char *resp, char *msg)
 Send response in manager transaction.
static void astman_send_response_full (struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
static void astman_start_ack (struct mansession *s, const struct message *m)
int astman_verify_session_readpermissions (uint32_t ident, int perm)
 Verify a session's read permissions against a permission mask.
int astman_verify_session_writepermissions (uint32_t ident, int perm)
 Verify a session's write permissions against a permission mask.
static int auth_http_callback (struct ast_tcptls_session_instance *ser, enum ast_http_method method, enum output_format format, struct sockaddr_in *remote_address, const char *uri, struct ast_variable *get_params, struct ast_variable *headers)
static int auth_manager_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static int auth_mxml_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static int auth_rawman_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static int authenticate (struct mansession *s, const struct message *m)
static const char * authority_to_str (int authority, struct ast_str **res)
 Convert authority code to a list of options.
static int blackfilter_cmp_fn (void *obj, void *arg, void *data, int flags)
static struct mansession_sessionbuild_mansession (struct sockaddr_in sin)
 Allocate manager session structure and add it to the list of sessions.
static int check_blacklist (const char *cmd)
int check_manager_enabled (void)
 Check if AMI is enabled.
static int check_manager_session_inuse (const char *name)
int check_webmanager_enabled (void)
 Check if AMI/HTTP is enabled.
static int do_message (struct mansession *s)
static void event_filter_destructor (void *obj)
static void * fast_originate (void *data)
static struct mansession_sessionfind_session (uint32_t ident, int incinuse)
static struct mansession_sessionfind_session_by_nonce (const char *username, unsigned long nonce, int *stale)
static void free_channelvars (void)
static int generic_http_callback (struct ast_tcptls_session_instance *ser, enum ast_http_method method, enum output_format format, struct sockaddr_in *remote_address, const char *uri, struct ast_variable *get_params, struct ast_variable *headers)
static int get_input (struct mansession *s, char *output)
static struct ast_manager_userget_manager_by_name_locked (const char *name)
static int get_perm (const char *instr)
static struct eventqentgrab_last (void)
static char * handle_manager_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command manager reload.
static char * handle_manager_show_settings (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command manager show settings.
static char * handle_mandebug (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_showmanager (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_showmanagers (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_showmancmd (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_showmancmds (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command manager list commands.
static char * handle_showmanconn (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command manager list connected.
static char * handle_showmaneventq (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 CLI command manager list eventq.
static enum error_type handle_updates (struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
int init_manager (void)
 Called by Asterisk initialization.
static void json_escape (char *out, const char *in)
static void load_channelvars (struct ast_variable *var)
static struct ast_variableman_do_variable_value (struct ast_variable *head, const char *hdr_val)
static int manager_displayconnects (struct mansession_session *session)
 Get displayconnects config option.
static int manager_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static int manager_modulecheck (struct mansession *s, const struct message *m)
static int manager_moduleload (struct mansession *s, const struct message *m)
static int manager_state_cb (char *context, char *exten, int state, void *data)
static int mansession_cmp_fn (void *obj, void *arg, int flags)
static struct sockaddr_in * mansession_encode_sin_local (const struct mansession *s, struct sockaddr_in *sin_local)
static enum
ast_security_event_transport_type 
mansession_get_transport (const struct mansession *s)
static void mansession_lock (struct mansession *s)
 Lock the 'mansession' structure.
static void mansession_unlock (struct mansession *s)
 Unlock the 'mansession' structure.
static int match_filter (struct mansession *s, char *eventdata)
static int mxml_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
static int process_events (struct mansession *s)
static int process_message (struct mansession *s, const struct message *m)
static void process_output (struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
static void purge_events (void)
static void purge_old_stuff (void *data)
 cleanup code called at each iteration of server_root, guaranteed to happen every 5 seconds at most
static void purge_sessions (int n_max)
 remove at most n_max stale session from the list.
static int rawman_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
int reload_manager (void)
 Called by Asterisk module functions and the CLI command.
static void report_auth_success (const struct mansession *s)
static void report_failed_acl (const struct mansession *s, const char *username)
static void report_failed_challenge_response (const struct mansession *s, const char *response, const char *expected_response)
static void report_inval_password (const struct mansession *s, const char *username)
static void report_invalid_user (const struct mansession *s, const char *username)
static void report_req_bad_format (const struct mansession *s, const char *action)
static void report_req_not_allowed (const struct mansession *s, const char *action)
static void report_session_limit (const struct mansession *s)
static int send_string (struct mansession *s, char *string)
static void session_destroy (struct mansession_session *s)
static void session_destructor (void *obj)
static void * session_do (void *data)
 The body of the individual manager session. Call get_input() to read one line at a time (or be woken up on new events), collect the lines in a message until found an empty line, and execute the request. In any case, deliver events asynchronously through process_events() (called from here if no line is available, or at the end of process_message(). )
static int set_eventmask (struct mansession *s, const char *eventmask)
 Rather than braindead on,off this now can also accept a specific int mask value or a ',' delim list of mask strings (the same as manager.conf) -anthm.
static int strings_to_mask (const char *string)
static struct mansession_sessionunref_mansession (struct mansession_session *s)
 Unreference manager session object. If no more references, then go ahead and delete it.
static int variable_count_cmp_fn (void *obj, void *vstr, int flags)
static int variable_count_hash_fn (const void *vvc, const int flags)
static int whitefilter_cmp_fn (void *obj, void *arg, void *data, int flags)
static void xml_copy_escape (struct ast_str **out, const char *src, int mode)
static void xml_translate (struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
 Convert the input into XML or HTML. The input is supposed to be a sequence of lines of the form Name: value optionally followed by a blob of unformatted text. A blank line is a section separator. Basically, this is a mixture of the format of Manager Interface and CLI commands. The unformatted text is considered as a single value of a field named 'Opaque-data'.

Variables

static struct actions actions
static struct all_events all_events
static int allowmultiplelogin = 1
static struct ast_http_uri amanageruri
static struct ast_http_uri amanagerxmluri
static struct
ast_tcptls_session_args 
ami_desc
static struct ast_tls_config ami_tls_cfg
static struct
ast_tcptls_session_args 
amis_desc
static struct ast_http_uri arawmanuri
static struct ast_threadstorage astman_append_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_astman_append_buf , .custom_init = NULL , }
static int authlimit
static int authtimeout
static int block_sockets
static int broken_events_action
static struct channelvars channelvars
static struct ast_cli_entry cli_manager []
struct {
   const char *   words [AST_MAX_CMD_LEN]
command_blacklist []
static const char *const contenttype []
static const int DEFAULT_AUTHLIMIT = 50
static const int DEFAULT_AUTHTIMEOUT = 30
static const int DEFAULT_BLOCKSOCKETS = 0
static const int DEFAULT_BROKENEVENTSACTION = 0
static const int DEFAULT_DISPLAYCONNECTS = 1
static const int DEFAULT_ENABLED = 0
static const int DEFAULT_HTTPTIMEOUT = 60
static const int DEFAULT_MANAGERDEBUG = 0
static const int DEFAULT_TIMESTAMPEVENTS = 0
static const int DEFAULT_WEBENABLED = 0
static int displayconnects
static char global_realm [MAXHOSTNAMELEN]
static int httptimeout
static char * manager_channelvars
static int manager_debug = 0
static int manager_enabled = 0
static struct ast_threadstorage manager_event_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_manager_event_buf , .custom_init = NULL , }
static struct ast_threadstorage manager_event_funcbuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_manager_event_funcbuf , .custom_init = NULL , }
static struct manager_hooks manager_hooks
static struct ast_http_uri manageruri
static struct ast_http_uri managerxmluri
static struct permalias perms []
static struct ast_http_uri rawmanuri
static int registered = 0
static struct ao2_containersessions = NULL
static int timestampevents
static int unauth_sessions = 0
static struct ast_threadstorage userevent_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_userevent_buf , .custom_init = NULL , }
static struct users users
static int webmanager_enabled = 0
static int webregged = 0

Detailed Description

The Asterisk Management Interface - AMI.

Author:
Mark Spencer <markster@digium.com>
ExtRef:
OpenSSL http://www.openssl.org - for AMI/SSL

At the moment this file contains a number of functions, namely:

  • data structures storing AMI state
  • AMI-related API functions, used by internal asterisk components
  • handlers for AMI-related CLI functions
  • handlers for AMI functions (available through the AMI socket)
  • the code for the main AMI listener thread and individual session threads
  • the http handlers invoked for AMI-over-HTTP by the threads in main/http.c

manager.conf

Definition in file manager.c.


Define Documentation

#define FORMAT   " %-25.25s %-15.15s\n"
#define FORMAT2   " %-25.25s %-15d\n"
#define HSMC_FORMAT   " %-15.15s %-15.15s %-55.55s\n"

Referenced by handle_showmancmds().

#define HSMCONN_FORMAT1   " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"

Referenced by handle_showmanconn().

#define HSMCONN_FORMAT2   " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"

Referenced by handle_showmanconn().

#define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"

Referenced by generic_http_callback().

#define TEST_STRING   "<form action=\"manager\" method=\"post\">\n\ Action: <select name=\"action\">\n\ <option value=\"\">-----&gt;</option>\n\ <option value=\"login\">login</option>\n\ <option value=\"command\">Command</option>\n\ <option value=\"waitevent\">waitevent</option>\n\ <option value=\"listcommands\">listcommands</option>\n\ </select>\n\ or <input name=\"action\"><br/>\n\ CLI Command <input name=\"command\"><br>\n\ user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\ <input type=\"submit\">\n</form>\n"

Referenced by generic_http_callback().


Enumeration Type Documentation

END Doxygen group

Enumerator:
FORMAT_RAW 
FORMAT_HTML 
FORMAT_XML 

Definition at line 5286 of file manager.c.


Function Documentation

static int __init_manager ( int  reload) [static]

Definition at line 6464 of file manager.c.

References ast_manager_user::a1_hash, action_aocmessage(), action_atxfer(), action_challenge(), action_command(), action_coresettings(), action_coreshowchannels(), action_corestatus(), action_createconfig(), action_events(), action_extensionstate(), action_getconfig(), action_getconfigjson(), action_getvar(), action_hangup(), action_listcategories(), action_listcommands(), action_login(), action_logoff(), action_mailboxcount(), action_mailboxstatus(), action_originate(), action_ping(), action_redirect(), action_reload(), action_sendtext(), action_setvar(), action_status(), action_timeout(), action_updateconfig(), action_userevent(), action_waitevent(), ami_tls_cfg, ao2_container_alloc, ao2_t_alloc, ao2_t_callback, ao2_t_link, ao2_t_ref, append_event(), ARRAY_LEN, ast_append_ha(), ast_calloc, ast_category_browse(), AST_CERTFILE, ast_cli_register_multiple(), ast_config_AST_SYSTEM_NAME, ast_config_destroy(), ast_config_load2(), ast_copy_string(), ast_debug, ast_extension_state_add(), ast_free, ast_free_ha(), ast_http_uri_link(), ast_http_uri_unlink(), AST_LIST_INSERT_TAIL, ast_log(), ast_manager_register_xml, ast_md5_hash(), AST_RWLIST_INSERT_TAIL, AST_RWLIST_REMOVE_CURRENT, AST_RWLIST_TRAVERSE_SAFE_BEGIN, AST_RWLIST_TRAVERSE_SAFE_END, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_sockaddr_from_sin, ast_ssl_setup(), ast_strdup, ast_strlen_zero(), ast_tcptls_server_start(), ast_tls_read_conf(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_manager_user::blackfilters, block_sockets, ast_tls_config::certfile, ast_tls_config::cipher, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, DEFAULT_AUTHLIMIT, DEFAULT_AUTHTIMEOUT, DEFAULT_BLOCKSOCKETS, DEFAULT_BROKENEVENTSACTION, DEFAULT_DISPLAYCONNECTS, DEFAULT_ENABLED, DEFAULT_HTTPTIMEOUT, DEFAULT_MANAGER_PORT, DEFAULT_MANAGERDEBUG, DEFAULT_REALM, DEFAULT_TIMESTAMPEVENTS, DEFAULT_WEBENABLED, displayconnects, ast_manager_user::displayconnects, ast_tls_config::enabled, event_filter_destructor(), EVENT_FLAG_AOC, EVENT_FLAG_CALL, EVENT_FLAG_COMMAND, EVENT_FLAG_CONFIG, EVENT_FLAG_ORIGINATE, EVENT_FLAG_REPORTING, EVENT_FLAG_SYSTEM, EVENT_FLAG_USER, free_channelvars(), get_manager_by_name_locked(), get_perm(), global_realm, ast_manager_user::ha, inet_aton(), ast_manager_user::keep, ast_variable::lineno, load_channelvars(), ast_tcptls_session_args::local_address, LOG_NOTICE, LOG_WARNING, manager_event, manager_modulecheck(), manager_moduleload(), manager_state_cb(), mansession_cmp_fn(), ast_variable::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, ast_tls_config::pvtfile, ast_manager_user::readperm, S_OR, ast_manager_user::secret, ast_tcptls_session_args::tls_cfg, ast_manager_user::username, value, ast_variable::value, var, ast_manager_user::whitefilters, ast_manager_user::writeperm, and ast_manager_user::writetimeout.

Referenced by init_manager(), and reload_manager().

{
   struct ast_config *ucfg = NULL, *cfg = NULL;
   const char *val;
   char *cat = NULL;
   int newhttptimeout = DEFAULT_HTTPTIMEOUT;
   struct ast_manager_user *user = NULL;
   struct ast_variable *var;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
   char a1[256];
   char a1_hash[256];
   struct sockaddr_in ami_desc_local_address_tmp = { 0, };
   struct sockaddr_in amis_desc_local_address_tmp = { 0, };

   if (!registered) {
      /* Register default actions */
      ast_manager_register_xml("Ping", 0, action_ping);
      ast_manager_register_xml("Events", 0, action_events);
      ast_manager_register_xml("Logoff", 0, action_logoff);
      ast_manager_register_xml("Login", 0, action_login);
      ast_manager_register_xml("Challenge", 0, action_challenge);
      ast_manager_register_xml("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup);
      ast_manager_register_xml("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status);
      ast_manager_register_xml("Setvar", EVENT_FLAG_CALL, action_setvar);
      ast_manager_register_xml("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar);
      ast_manager_register_xml("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig);
      ast_manager_register_xml("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson);
      ast_manager_register_xml("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig);
      ast_manager_register_xml("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig);
      ast_manager_register_xml("ListCategories", EVENT_FLAG_CONFIG, action_listcategories);
      ast_manager_register_xml("Redirect", EVENT_FLAG_CALL, action_redirect);
      ast_manager_register_xml("Atxfer", EVENT_FLAG_CALL, action_atxfer);
      ast_manager_register_xml("Originate", EVENT_FLAG_ORIGINATE, action_originate);
      ast_manager_register_xml("Command", EVENT_FLAG_COMMAND, action_command);
      ast_manager_register_xml("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
      ast_manager_register_xml("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
      ast_manager_register_xml("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
      ast_manager_register_xml("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
      ast_manager_register_xml("ListCommands", 0, action_listcommands);
      ast_manager_register_xml("SendText", EVENT_FLAG_CALL, action_sendtext);
      ast_manager_register_xml("UserEvent", EVENT_FLAG_USER, action_userevent);
      ast_manager_register_xml("WaitEvent", 0, action_waitevent);
      ast_manager_register_xml("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings);
      ast_manager_register_xml("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus);
      ast_manager_register_xml("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload);
      ast_manager_register_xml("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels);
      ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
      ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
      ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);

      ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
      ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
      registered = 1;
      /* Append placeholder event so master_eventq never runs dry */
      append_event("Event: Placeholder\r\n\r\n", 0);
   }
   if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
      return 0;
   }

   manager_enabled = DEFAULT_ENABLED;
   webmanager_enabled = DEFAULT_WEBENABLED;
   manager_debug = DEFAULT_MANAGERDEBUG;
   displayconnects = DEFAULT_DISPLAYCONNECTS;
   broken_events_action = DEFAULT_BROKENEVENTSACTION;
   block_sockets = DEFAULT_BLOCKSOCKETS;
   timestampevents = DEFAULT_TIMESTAMPEVENTS;
   httptimeout = DEFAULT_HTTPTIMEOUT;
   authtimeout = DEFAULT_AUTHTIMEOUT;
   authlimit = DEFAULT_AUTHLIMIT;

   if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid. Asterisk management interface (AMI) disabled.\n");
      return 0;
   }

   /* default values */
   ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
   memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in));
   memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address));
   amis_desc_local_address_tmp.sin_port = htons(5039);
   ami_desc_local_address_tmp.sin_port = htons(DEFAULT_MANAGER_PORT);

   ami_tls_cfg.enabled = 0;
   if (ami_tls_cfg.certfile) {
      ast_free(ami_tls_cfg.certfile);
   }
   ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
   if (ami_tls_cfg.pvtfile) {
      ast_free(ami_tls_cfg.pvtfile);
   }
   ami_tls_cfg.pvtfile = ast_strdup("");
   if (ami_tls_cfg.cipher) {
      ast_free(ami_tls_cfg.cipher);
   }
   ami_tls_cfg.cipher = ast_strdup("");

   free_channelvars();

   for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
      val = var->value;

      if (!ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
         continue;
      }

      if (!strcasecmp(var->name, "enabled")) {
         manager_enabled = ast_true(val);
      } else if (!strcasecmp(var->name, "block-sockets")) {
         block_sockets = ast_true(val);
      } else if (!strcasecmp(var->name, "webenabled")) {
         webmanager_enabled = ast_true(val);
      } else if (!strcasecmp(var->name, "port")) {
         ami_desc_local_address_tmp.sin_port = htons(atoi(val));
      } else if (!strcasecmp(var->name, "bindaddr")) {
         if (!inet_aton(val, &ami_desc_local_address_tmp.sin_addr)) {
            ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
            memset(&ami_desc_local_address_tmp.sin_addr, 0,
                   sizeof(ami_desc_local_address_tmp.sin_addr));
         }
      } else if (!strcasecmp(var->name, "brokeneventsaction")) {
         broken_events_action = ast_true(val);
      } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
         allowmultiplelogin = ast_true(val);
      } else if (!strcasecmp(var->name, "displayconnects")) {
         displayconnects = ast_true(val);
      } else if (!strcasecmp(var->name, "timestampevents")) {
         timestampevents = ast_true(val);
      } else if (!strcasecmp(var->name, "debug")) {
         manager_debug = ast_true(val);
      } else if (!strcasecmp(var->name, "httptimeout")) {
         newhttptimeout = atoi(val);
      } else if (!strcasecmp(var->name, "authtimeout")) {
         int timeout = atoi(var->value);

         if (timeout < 1) {
            ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
         } else {
            authtimeout = timeout;
         }
      } else if (!strcasecmp(var->name, "authlimit")) {
         int limit = atoi(var->value);

         if (limit < 1) {
            ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
         } else {
            authlimit = limit;
         }
      } else if (!strcasecmp(var->name, "channelvars")) {
         load_channelvars(var);
      } else {
         ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
            var->name, val);
      }
   }

   ami_desc_local_address_tmp.sin_family = AF_INET;
   amis_desc_local_address_tmp.sin_family = AF_INET;

   /* if the amis address has not been set, default is the same as non secure ami */
   if (!amis_desc_local_address_tmp.sin_addr.s_addr) {
      amis_desc_local_address_tmp.sin_addr =
          ami_desc_local_address_tmp.sin_addr;
   }

   if (manager_enabled) {
      ast_sockaddr_from_sin(&ami_desc.local_address, &ami_desc_local_address_tmp);
      ast_sockaddr_from_sin(&amis_desc.local_address, &amis_desc_local_address_tmp);
   }

   AST_RWLIST_WRLOCK(&users);

   /* First, get users from users.conf */
   ucfg = ast_config_load2("users.conf", "manager", config_flags);
   if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
      const char *hasmanager;
      int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));

      while ((cat = ast_category_browse(ucfg, cat))) {
         if (!strcasecmp(cat, "general")) {
            continue;
         }

         hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
         if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
            const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
            const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
            const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
            const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
            const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");

            /* Look for an existing entry,
             * if none found - create one and add it to the list
             */
            if (!(user = get_manager_by_name_locked(cat))) {
               if (!(user = ast_calloc(1, sizeof(*user)))) {
                  break;
               }

               /* Copy name over */
               ast_copy_string(user->username, cat, sizeof(user->username));
               /* Insert into list */
               AST_LIST_INSERT_TAIL(&users, user, list);
               user->ha = NULL;
               user->keep = 1;
               user->readperm = -1;
               user->writeperm = -1;
               /* Default displayconnect from [general] */
               user->displayconnects = displayconnects;
               user->writetimeout = 100;
            }

            if (!user_secret) {
               user_secret = ast_variable_retrieve(ucfg, "general", "secret");
            }
            if (!user_read) {
               user_read = ast_variable_retrieve(ucfg, "general", "read");
            }
            if (!user_write) {
               user_write = ast_variable_retrieve(ucfg, "general", "write");
            }
            if (!user_displayconnects) {
               user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
            }
            if (!user_writetimeout) {
               user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
            }

            if (!ast_strlen_zero(user_secret)) {
               if (user->secret) {
                  ast_free(user->secret);
               }
               user->secret = ast_strdup(user_secret);
            }

            if (user_read) {
               user->readperm = get_perm(user_read);
            }
            if (user_write) {
               user->writeperm = get_perm(user_write);
            }
            if (user_displayconnects) {
               user->displayconnects = ast_true(user_displayconnects);
            }
            if (user_writetimeout) {
               int value = atoi(user_writetimeout);
               if (value < 100) {
                  ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
               } else {
                  user->writetimeout = value;
               }
            }
         }
      }
      ast_config_destroy(ucfg);
   }

   /* cat is NULL here in any case */

   while ((cat = ast_category_browse(cfg, cat))) {
      struct ast_ha *oldha;

      if (!strcasecmp(cat, "general")) {
         continue;
      }

      /* Look for an existing entry, if none found - create one and add it to the list */
      if (!(user = get_manager_by_name_locked(cat))) {
         if (!(user = ast_calloc(1, sizeof(*user)))) {
            break;
         }
         /* Copy name over */
         ast_copy_string(user->username, cat, sizeof(user->username));

         user->ha = NULL;
         user->readperm = 0;
         user->writeperm = 0;
         /* Default displayconnect from [general] */
         user->displayconnects = displayconnects;
         user->writetimeout = 100;
         user->whitefilters = ao2_container_alloc(1, NULL, NULL);
         user->blackfilters = ao2_container_alloc(1, NULL, NULL);

         /* Insert into list */
         AST_RWLIST_INSERT_TAIL(&users, user, list);
      } else {
         ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
         ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
      }

      /* Make sure we keep this user and don't destroy it during cleanup */
      user->keep = 1;
      oldha = user->ha;
      user->ha = NULL;

      var = ast_variable_browse(cfg, cat);
      for (; var; var = var->next) {
         if (!strcasecmp(var->name, "secret")) {
            if (user->secret) {
               ast_free(user->secret);
            }
            user->secret = ast_strdup(var->value);
         } else if (!strcasecmp(var->name, "deny") ||
                   !strcasecmp(var->name, "permit")) {
            user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
         }  else if (!strcasecmp(var->name, "read") ) {
            user->readperm = get_perm(var->value);
         }  else if (!strcasecmp(var->name, "write") ) {
            user->writeperm = get_perm(var->value);
         }  else if (!strcasecmp(var->name, "displayconnects") ) {
            user->displayconnects = ast_true(var->value);
         } else if (!strcasecmp(var->name, "writetimeout")) {
            int value = atoi(var->value);
            if (value < 100) {
               ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
            } else {
               user->writetimeout = value;
            }
         } else if (!strcasecmp(var->name, "eventfilter")) {
            const char *value = var->value;
            regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
            if (new_filter) {
               int is_blackfilter;
               if (value[0] == '!') {
                  is_blackfilter = 1;
                  value++;
               } else {
                  is_blackfilter = 0;
               }
               if (regcomp(new_filter, value, 0)) {
                  ao2_t_ref(new_filter, -1, "failed to make regx");
               } else {
                  if (is_blackfilter) {
                     ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
                  } else {
                     ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
                  }
               }
            }
         } else {
            ast_debug(1, "%s is an unknown option.\n", var->name);
         }
      }
      ast_free_ha(oldha);
   }
   ast_config_destroy(cfg);

   /* Perform cleanup - essentially prune out old users that no longer exist */
   AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
      if (user->keep) { /* valid record. clear flag for the next round */
         user->keep = 0;

         /* Calculate A1 for Digest auth */
         snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
         ast_md5_hash(a1_hash,a1);
         if (user->a1_hash) {
            ast_free(user->a1_hash);
         }
         user->a1_hash = ast_strdup(a1_hash);
         continue;
      }
      /* We do not need to keep this user so take them out of the list */
      AST_RWLIST_REMOVE_CURRENT(list);
      ast_debug(4, "Pruning user '%s'\n", user->username);
      /* Free their memory now */
      if (user->a1_hash) {
         ast_free(user->a1_hash);
      }
      if (user->secret) {
         ast_free(user->secret);
      }
      ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
      ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
      ao2_t_ref(user->whitefilters, -1, "decrement ref for white container, should be last one");
      ao2_t_ref(user->blackfilters, -1, "decrement ref for black container, should be last one");
      ast_free_ha(user->ha);
      ast_free(user);
   }
   AST_RWLIST_TRAVERSE_SAFE_END;

   AST_RWLIST_UNLOCK(&users);

   if (!reload) {
      /* If you have a NULL hash fn, you only need a single bucket */
      sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn);
   }

   if (webmanager_enabled && manager_enabled) {
      if (!webregged) {

         ast_http_uri_link(&rawmanuri);
         ast_http_uri_link(&manageruri);
         ast_http_uri_link(&managerxmluri);

         ast_http_uri_link(&arawmanuri);
         ast_http_uri_link(&amanageruri);
         ast_http_uri_link(&amanagerxmluri);
         webregged = 1;
      }
   } else {
      if (webregged) {
         ast_http_uri_unlink(&rawmanuri);
         ast_http_uri_unlink(&manageruri);
         ast_http_uri_unlink(&managerxmluri);

         ast_http_uri_unlink(&arawmanuri);
         ast_http_uri_unlink(&amanageruri);
         ast_http_uri_unlink(&amanagerxmluri);
         webregged = 0;
      }
   }

   if (newhttptimeout > 0) {
      httptimeout = newhttptimeout;
   }

   manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");

   ast_tcptls_server_start(&ami_desc);
   if (ast_ssl_setup(amis_desc.tls_cfg)) {
      ast_tcptls_server_start(&amis_desc);
   }
   return 0;
}
int astman_datastore_add ( struct mansession s,
struct ast_datastore datastore 
)

Add a datastore to a session.

Return values:
0success
non-zerofailure
Since:
1.6.1

Definition at line 6910 of file manager.c.

References AST_LIST_INSERT_HEAD, mansession_session::datastores, and mansession::session.

{
   AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);

   return 0;
}
struct ast_datastore* astman_datastore_find ( struct mansession s,
const struct ast_datastore_info info,
const char *  uid 
) [read]

Find a datastore on a session.

Return values:
pointerto the datastore if found
NULLif not found
Since:
1.6.1

Definition at line 6922 of file manager.c.

References AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, mansession_session::datastores, ast_datastore::info, mansession::session, and ast_datastore::uid.

{
   struct ast_datastore *datastore = NULL;

   if (info == NULL)
      return NULL;

   AST_LIST_TRAVERSE_SAFE_BEGIN(&s->session->datastores, datastore, entry) {
      if (datastore->info != info) {
         continue;
      }

      if (uid == NULL) {
         /* matched by type only */
         break;
      }

      if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
         /* Matched by type AND uid */
         break;
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;

   return datastore;
}
int astman_datastore_remove ( struct mansession s,
struct ast_datastore datastore 
)

Remove a datastore from a session.

Return values:
0success
non-zerofailure
Since:
1.6.1

Definition at line 6917 of file manager.c.

References AST_LIST_REMOVE, mansession_session::datastores, and mansession::session.

{
   return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
}
int astman_is_authed ( uint32_t  ident)

Determinie if a manager session ident is authenticated.

Definition at line 5362 of file manager.c.

References ao2_unlock, mansession_session::authenticated, find_session(), and unref_mansession().

Referenced by http_post_callback(), and static_callback().

{
   int authed;
   struct mansession_session *session;

   if (!(session = find_session(ident, 0)))
      return 0;

   authed = (session->authenticated != 0);

   ao2_unlock(session);
   unref_mansession(session);

   return authed;
}
int astman_verify_session_readpermissions ( uint32_t  ident,
int  perm 
)

Verify a session's read permissions against a permission mask.

Parameters:
identsession identity
permpermission mask to verify
Return values:
1if the session has the permission mask capabilities
0otherwise

Definition at line 5378 of file manager.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, mansession_session::managerid, mansession_session::readperm, and unref_mansession().

{
   int result = 0;
   struct mansession_session *session;
   struct ao2_iterator i;

   if (ident == 0) {
      return 0;
   }

   i = ao2_iterator_init(sessions, 0);
   while ((session = ao2_iterator_next(&i))) {
      ao2_lock(session);
      if ((session->managerid == ident) && (session->readperm & perm)) {
         result = 1;
         ao2_unlock(session);
         unref_mansession(session);
         break;
      }
      ao2_unlock(session);
      unref_mansession(session);
   }
   ao2_iterator_destroy(&i);
   return result;
}
int astman_verify_session_writepermissions ( uint32_t  ident,
int  perm 
)

Verify a session's write permissions against a permission mask.

Parameters:
identsession identity
permpermission mask to verify
Return values:
1if the session has the permission mask capabilities, otherwise 0
0otherwise

Definition at line 5404 of file manager.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, mansession_session::managerid, unref_mansession(), and mansession_session::writeperm.

Referenced by http_post_callback().

{
   int result = 0;
   struct mansession_session *session;
   struct ao2_iterator i;

   if (ident == 0) {
      return 0;
   }

   i = ao2_iterator_init(sessions, 0);
   while ((session = ao2_iterator_next(&i))) {
      ao2_lock(session);
      if ((session->managerid == ident) && (session->writeperm & perm)) {
         result = 1;
         ao2_unlock(session);
         unref_mansession(session);
         break;
      }
      ao2_unlock(session);
      unref_mansession(session);
   }
   ao2_iterator_destroy(&i);
   return result;
}
static int auth_http_callback ( struct ast_tcptls_session_instance ser,
enum ast_http_method  method,
enum output_format  format,
struct sockaddr_in *  remote_address,
const char *  uri,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 5908 of file manager.c.

References ast_manager_user::a1_hash, ao2_lock, ao2_unlock, ARRAY_LEN, ast_apply_ha(), ast_copy_string(), ast_debug, ast_free, ast_get_http_method(), ast_http_auth(), ast_http_error(), AST_HTTP_GET, ast_http_get_post_vars(), AST_HTTP_HEAD, AST_HTTP_POST, ast_http_send(), ast_inet_ntoa(), AST_LIST_HEAD_INIT_NOLOCK, ast_log(), ast_malloc, ast_md5_hash(), ast_mutex_destroy, ast_mutex_init, ast_parse_digest(), ast_random(), AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_sockaddr_from_sin, ast_str_append(), ast_str_create(), ast_string_field_free_memory, ast_string_field_init, ast_strlen_zero(), ast_variables_destroy(), ast_verb, mansession_session::authenticated, build_mansession(), ast_http_digest::cnonce, mansession_session::datastores, ast_manager_user::displayconnects, errno, mansession_session::f, mansession::f, mansession_session::fd, mansession::fd, find_session_by_nonce(), FORMAT_HTML, FORMAT_XML, get_manager_by_name_locked(), get_params(), global_realm, grab_last(), ast_manager_user::ha, message::hdrcount, message::headers, mansession_session::last_ev, mansession::lock, LOG_NOTICE, LOG_WARNING, mansession_session::managerid, ast_variable::name, ast_http_digest::nc, mansession_session::nc, mansession_session::needdestroy, ast_variable::next, ast_http_digest::nonce, mansession_session::noncetime, mansession_session::oldnonce, process_message(), process_output(), ast_http_digest::qop, mansession_session::readperm, ast_manager_user::readperm, ast_http_digest::response, mansession::session, session_destroy(), mansession_session::sessionstart, mansession_session::sessiontimeout, mansession_session::sin, ast_http_digest::uri, ast_http_digest::username, mansession_session::username, ast_manager_user::username, ast_variable::value, mansession_session::writeperm, ast_manager_user::writeperm, mansession_session::writetimeout, and ast_manager_user::writetimeout.

Referenced by auth_manager_http_callback(), auth_mxml_http_callback(), and auth_rawman_http_callback().

{
   struct mansession_session *session = NULL;
   struct mansession s = { .session = NULL, .tcptls_session = ser };
   struct ast_variable *v, *params = get_params;
   char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
   struct ast_str *http_header = NULL, *out = NULL;
   size_t result_size = 512;
   struct message m = { 0 };
   unsigned int idx;
   size_t hdrlen;

   time_t time_now = time(NULL);
   unsigned long nonce = 0, nc;
   struct ast_http_digest d = { NULL, };
   struct ast_manager_user *user = NULL;
   int stale = 0;
   char resp_hash[256]="";
   /* Cache for user data */
   char u_username[80];
   int u_readperm;
   int u_writeperm;
   int u_writetimeout;
   int u_displayconnects;
   struct ast_sockaddr addr;

   if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
      ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
      return -1;
   }

   /* Find "Authorization: " header */
   for (v = headers; v; v = v->next) {
      if (!strcasecmp(v->name, "Authorization")) {
         break;
      }
   }

   if (!v || ast_strlen_zero(v->value)) {
      goto out_401; /* Authorization Header not present - send auth request */
   }

   /* Digest found - parse */
   if (ast_string_field_init(&d, 128)) {
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
      return -1;
   }

   if (ast_parse_digest(v->value, &d, 0, 1)) {
      /* Error in Digest - send new one */
      nonce = 0;
      goto out_401;
   }
   if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
      ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
      nonce = 0;
      goto out_401;
   }

   AST_RWLIST_WRLOCK(&users);
   user = get_manager_by_name_locked(d.username);
   if(!user) {
      AST_RWLIST_UNLOCK(&users);
      ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
      nonce = 0;
      goto out_401;
   }

   ast_sockaddr_from_sin(&addr, remote_address);
   /* --- We have User for this auth, now check ACL */
   if (user->ha && !ast_apply_ha(user->ha, &addr)) {
      AST_RWLIST_UNLOCK(&users);
      ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username);
      ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
      return -1;
   }

   /* --- We have auth, so check it */

   /* compute the expected response to compare with what we received */
   {
      char a2[256];
      char a2_hash[256];
      char resp[256];

      /* XXX Now request method are hardcoded in A2 */
      snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri);
      ast_md5_hash(a2_hash, a2);

      if (d.qop) {
         /* RFC 2617 */
         snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
      }  else {
         /* RFC 2069 */
         snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
      }
      ast_md5_hash(resp_hash, resp);
   }

   if (!d.nonce  || strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
      /* Something was wrong, so give the client to try with a new challenge */
      AST_RWLIST_UNLOCK(&users);
      nonce = 0;
      goto out_401;
   }

   /*
    * User are pass Digest authentication.
    * Now, cache the user data and unlock user list.
    */
   ast_copy_string(u_username, user->username, sizeof(u_username));
   u_readperm = user->readperm;
   u_writeperm = user->writeperm;
   u_displayconnects = user->displayconnects;
   u_writetimeout = user->writetimeout;
   AST_RWLIST_UNLOCK(&users);

   if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
      /*
       * Create new session.
       * While it is not in the list we don't need any locking
       */
      if (!(session = build_mansession(*remote_address))) {
         ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
         return -1;
      }
      ao2_lock(session);

      ast_copy_string(session->username, u_username, sizeof(session->username));
      session->managerid = nonce;
      session->last_ev = grab_last();
      AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);

      session->readperm = u_readperm;
      session->writeperm = u_writeperm;
      session->writetimeout = u_writetimeout;

      if (u_displayconnects) {
         ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
      }
      session->noncetime = session->sessionstart = time_now;
      session->authenticated = 1;
   } else if (stale) {
      /*
       * Session found, but nonce is stale.
       *
       * This could be because an old request (w/old nonce) arrived.
       *
       * This may be as the result of http proxy usage (separate delay or
       * multipath) or in a situation where a page was refreshed too quickly
       * (seen in Firefox).
       *
       * In this situation, we repeat the 401 auth with the current nonce
       * value.
       */
      nonce = session->managerid;
      ao2_unlock(session);
      stale = 1;
      goto out_401;
   } else {
      sscanf(d.nc, "%30lx", &nc);
      if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
         /*
          * Nonce time expired (> 2 minutes) or something wrong with nonce
          * counter.
          *
          * Create new nonce key and resend Digest auth request. Old nonce
          * is saved for stale checking...
          */
         session->nc = 0; /* Reset nonce counter */
         session->oldnonce = session->managerid;
         nonce = session->managerid = ast_random();
         session->noncetime = time_now;
         ao2_unlock(session);
         stale = 1;
         goto out_401;
      } else {
         session->nc = nc; /* All OK, save nonce counter */
      }
   }


   /* Reset session timeout. */
   session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
   ao2_unlock(session);

   ast_mutex_init(&s.lock);
   s.session = session;
   s.fd = mkstemp(template);  /* create a temporary file for command output */
   unlink(template);
   if (s.fd <= -1) {
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
      goto auth_callback_out;
   }
   s.f = fdopen(s.fd, "w+");
   if (!s.f) {
      ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
      close(s.fd);
      goto auth_callback_out;
   }

   if (method == AST_HTTP_POST) {
      params = ast_http_get_post_vars(ser, headers);
   }

   for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
      hdrlen = strlen(v->name) + strlen(v->value) + 3;
      m.headers[m.hdrcount] = ast_malloc(hdrlen);
      if (!m.headers[m.hdrcount]) {
         /* Allocation failure */
         continue;
      }
      snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
      ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
      ++m.hdrcount;
   }

   if (process_message(&s, &m)) {
      if (u_displayconnects) {
         ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
      }

      session->needdestroy = 1;
   }

   /* Free request headers. */
   for (idx = 0; idx < m.hdrcount; ++idx) {
      ast_free((void *) m.headers[idx]);
      m.headers[idx] = NULL;
   }

   if (s.f) {
      result_size = ftell(s.f); /* Calculate approx. size of result */
   }

   http_header = ast_str_create(80);
   out = ast_str_create(result_size * 2 + 512);

   if (http_header == NULL || out == NULL) {
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
      goto auth_callback_out;
   }

   ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);

   if (format == FORMAT_XML) {
      ast_str_append(&out, 0, "<ajax-response>\n");
   } else if (format == FORMAT_HTML) {
      ast_str_append(&out, 0,
      "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
      "<html><head>\r\n"
      "<title>Asterisk&trade; Manager Interface</title>\r\n"
      "</head><body style=\"background-color: #ffffff;\">\r\n"
      "<form method=\"POST\">\r\n"
      "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
      "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
      "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
      "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
   }

   process_output(&s, &out, params, format);

   if (format == FORMAT_XML) {
      ast_str_append(&out, 0, "</ajax-response>\n");
   } else if (format == FORMAT_HTML) {
      ast_str_append(&out, 0, "</table></form></body></html>\r\n");
   }

   ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
   http_header = out = NULL;

auth_callback_out:
   ast_mutex_destroy(&s.lock);

   /* Clear resources and unlock manager session */
   if (method == AST_HTTP_POST && params) {
      ast_variables_destroy(params);
   }

   ast_free(http_header);
   ast_free(out);

   ao2_lock(session);
   if (session->f) {
      fclose(session->f);
   }
   session->f = NULL;
   session->fd = -1;
   ao2_unlock(session);

   if (session->needdestroy) {
      ast_debug(1, "Need destroy, doing it now!\n");
      session_destroy(session);
   }
   ast_string_field_free_memory(&d);
   return 0;

out_401:
   if (!nonce) {
      nonce = ast_random();
   }

   ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
   ast_string_field_free_memory(&d);
   return 0;
}
static int auth_manager_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6280 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, auth_http_callback(), FORMAT_HTML, and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
static int auth_mxml_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6291 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, auth_http_callback(), FORMAT_XML, and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
static int auth_rawman_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6302 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, auth_http_callback(), FORMAT_RAW, and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
static struct mansession_session* find_session ( uint32_t  ident,
int  incinuse 
) [static, read]

locate an http session in the list. The search key (ident) is the value of the mansession_id cookie (0 is not valid and means a session on the AMI socket).

Definition at line 5303 of file manager.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, ast_atomic_fetchadd_int(), mansession_session::inuse, mansession_session::managerid, mansession_session::needdestroy, and unref_mansession().

Referenced by astman_is_authed(), and generic_http_callback().

{
   struct mansession_session *session;
   struct ao2_iterator i;

   if (ident == 0) {
      return NULL;
   }

   i = ao2_iterator_init(sessions, 0);
   while ((session = ao2_iterator_next(&i))) {
      ao2_lock(session);
      if (session->managerid == ident && !session->needdestroy) {
         ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
         break;
      }
      ao2_unlock(session);
      unref_mansession(session);
   }
   ao2_iterator_destroy(&i);

   return session;
}
static struct mansession_session* find_session_by_nonce ( const char *  username,
unsigned long  nonce,
int *  stale 
) [static, read]

locate an http session in the list. The search keys (nonce) and (username) is value from received "Authorization" http header. As well as in find_session() function, the value of the nonce can't be zero. (0 meansi, that the session used for AMI socket connection). Flag (stale) is set, if client used valid, but old, nonce value.

Definition at line 5336 of file manager.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, mansession_session::managerid, mansession_session::oldnonce, unref_mansession(), and mansession_session::username.

Referenced by auth_http_callback().

{
   struct mansession_session *session;
   struct ao2_iterator i;

   if (nonce == 0 || username == NULL || stale == NULL) {
      return NULL;
   }

   i = ao2_iterator_init(sessions, 0);
   while ((session = ao2_iterator_next(&i))) {
      ao2_lock(session);
      if (!strcasecmp(session->username, username) && session->managerid == nonce) {
         *stale = 0;
         break;
      } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
         *stale = 1;
         break;
      }
      ao2_unlock(session);
      unref_mansession(session);
   }
   ao2_iterator_destroy(&i);
   return session;
}
static int generic_http_callback ( struct ast_tcptls_session_instance ser,
enum ast_http_method  method,
enum output_format  format,
struct sockaddr_in *  remote_address,
const char *  uri,
struct ast_variable get_params,
struct ast_variable headers 
) [static]
Note:
There is approximately a 1 in 1.8E19 chance that the following calculation will produce 0, which is an invalid ID, but due to the properties of the rand() function (and the constantcy of s), that won't happen twice in a row.

Definition at line 5693 of file manager.c.

References ao2_lock, ao2_unlock, ARRAY_LEN, ast_debug, ast_free, ast_http_error(), AST_HTTP_GET, ast_http_get_cookies(), ast_http_get_post_vars(), AST_HTTP_HEAD, AST_HTTP_POST, ast_http_send(), ast_inet_ntoa(), AST_LIST_HEAD_INIT_NOLOCK, ast_log(), ast_malloc, ast_mutex_destroy, ast_mutex_init, AST_PTHREADT_NULL, ast_random(), ast_str_append(), ast_str_create(), ast_variables_destroy(), ast_verb, mansession_session::authenticated, build_mansession(), errno, mansession_session::f, mansession::f, mansession_session::fd, mansession::fd, find_session(), FORMAT_HTML, FORMAT_XML, get_params(), grab_last(), message::hdrcount, message::headers, mansession_session::inuse, mansession::lock, LOG_WARNING, manager_displayconnects(), mansession_session::managerid, ast_variable::name, mansession_session::needdestroy, ast_variable::next, process_message(), process_output(), ROW_FMT, mansession_session::send_events, mansession::session, session_destroy(), mansession_session::sessiontimeout, mansession_session::sin, TEST_STRING, mansession_session::username, ast_variable::value, and mansession_session::waiting_thread.

Referenced by manager_http_callback(), mxml_http_callback(), and rawman_http_callback().

{
   struct mansession s = { .session = NULL, .tcptls_session = ser };
   struct mansession_session *session = NULL;
   uint32_t ident = 0;
   int blastaway = 0;
   struct ast_variable *v, *cookies, *params = get_params;
   char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
   struct ast_str *http_header = NULL, *out = NULL;
   struct message m = { 0 };
   unsigned int idx;
   size_t hdrlen;

   if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
      ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
      return -1;
   }

   cookies = ast_http_get_cookies(headers);
   for (v = cookies; v; v = v->next) {
      if (!strcasecmp(v->name, "mansession_id")) {
         sscanf(v->value, "%30x", &ident);
         break;
      }
   }
   if (cookies) {
      ast_variables_destroy(cookies);
   }

   if (!(session = find_session(ident, 1))) {

      /**/
      /* Create new session.
       * While it is not in the list we don't need any locking
       */
      if (!(session = build_mansession(*remote_address))) {
         ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
         return -1;
      }
      ao2_lock(session);
      session->sin = *remote_address;
      session->fd = -1;
      session->waiting_thread = AST_PTHREADT_NULL;
      session->send_events = 0;
      session->inuse = 1;
      /*!\note There is approximately a 1 in 1.8E19 chance that the following
       * calculation will produce 0, which is an invalid ID, but due to the
       * properties of the rand() function (and the constantcy of s), that
       * won't happen twice in a row.
       */
      while ((session->managerid = ast_random() ^ (unsigned long) session) == 0);
      session->last_ev = grab_last();
      AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
   }
   ao2_unlock(session);

   http_header = ast_str_create(128);
   out = ast_str_create(2048);

   ast_mutex_init(&s.lock);

   if (http_header == NULL || out == NULL) {
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n");
      goto generic_callback_out;
   }

   s.session = session;
   s.fd = mkstemp(template);  /* create a temporary file for command output */
   unlink(template);
   if (s.fd <= -1) {
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n");
      goto generic_callback_out;
   }
   s.f = fdopen(s.fd, "w+");
   if (!s.f) {
      ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
      ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n");
      close(s.fd);
      goto generic_callback_out;
   }

   if (method == AST_HTTP_POST) {
      params = ast_http_get_post_vars(ser, headers);
   }

   for (v = params; v && m.hdrcount < ARRAY_LEN(m.headers); v = v->next) {
      hdrlen = strlen(v->name) + strlen(v->value) + 3;
      m.headers[m.hdrcount] = ast_malloc(hdrlen);
      if (!m.headers[m.hdrcount]) {
         /* Allocation failure */
         continue;
      }
      snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
      ast_debug(1, "HTTP Manager add header %s\n", m.headers[m.hdrcount]);
      ++m.hdrcount;
   }

   if (process_message(&s, &m)) {
      if (session->authenticated) {
         if (manager_displayconnects(session)) {
            ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr));
         }
      } else {
         if (displayconnects) {
            ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr));
         }
      }
      session->needdestroy = 1;
   }

   /* Free request headers. */
   for (idx = 0; idx < m.hdrcount; ++idx) {
      ast_free((void *) m.headers[idx]);
      m.headers[idx] = NULL;
   }

   ast_str_append(&http_header, 0,
      "Content-type: text/%s\r\n"
      "Cache-Control: no-cache;\r\n"
      "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
      "Pragma: SuppressEvents\r\n",
      contenttype[format],
      session->managerid, httptimeout);

   if (format == FORMAT_XML) {
      ast_str_append(&out, 0, "<ajax-response>\n");
   } else if (format == FORMAT_HTML) {
      /*
       * When handling AMI-over-HTTP in HTML format, we provide a simple form for
       * debugging purposes. This HTML code should not be here, we
       * should read from some config file...
       */

#define ROW_FMT   "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
#define TEST_STRING \
   "<form action=\"manager\" method=\"post\">\n\
   Action: <select name=\"action\">\n\
      <option value=\"\">-----&gt;</option>\n\
      <option value=\"login\">login</option>\n\
      <option value=\"command\">Command</option>\n\
      <option value=\"waitevent\">waitevent</option>\n\
      <option value=\"listcommands\">listcommands</option>\n\
   </select>\n\
   or <input name=\"action\"><br/>\n\
   CLI Command <input name=\"command\"><br>\n\
   user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
   <input type=\"submit\">\n</form>\n"

      ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
      ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
      ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
      ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
   }

   process_output(&s, &out, params, format);

   if (format == FORMAT_XML) {
      ast_str_append(&out, 0, "</ajax-response>\n");
   } else if (format == FORMAT_HTML) {
      ast_str_append(&out, 0, "</table></body>\r\n");
   }

   ao2_lock(session);
   /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
   session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);

   if (session->needdestroy) {
      if (session->inuse == 1) {
         ast_debug(1, "Need destroy, doing it now!\n");
         blastaway = 1;
      } else {
         ast_debug(1, "Need destroy, but can't do it yet!\n");
         if (session->waiting_thread != AST_PTHREADT_NULL) {
            pthread_kill(session->waiting_thread, SIGURG);
         }
         session->inuse--;
      }
   } else {
      session->inuse--;
   }
   ao2_unlock(session);

   ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
   http_header = out = NULL;

generic_callback_out:
   ast_mutex_destroy(&s.lock);

   /* Clear resource */

   if (method == AST_HTTP_POST && params) {
      ast_variables_destroy(params);
   }
   if (http_header) {
      ast_free(http_header);
   }
   if (out) {
      ast_free(out);
   }

   if (session && blastaway) {
      session_destroy(session);
   } else if (session && session->f) {
      fclose(session->f);
      session->f = NULL;
   }

   return 0;
}
static char* handle_manager_show_settings ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

CLI command manager show settings.

Definition at line 6375 of file manager.c.

References ami_tls_cfg, ast_cli_args::argc, ast_cli(), AST_CLI_YESNO, ast_sockaddr_stringify(), block_sockets, ast_tls_config::certfile, ast_tls_config::cipher, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_tls_config::enabled, ast_cli_args::fd, FORMAT, FORMAT2, ast_tcptls_session_args::local_address, ast_tls_config::pvtfile, S_OR, and ast_cli_entry::usage.

{
   switch (cmd) {
   case CLI_INIT:
      e->command = "manager show settings";
      e->usage =
         "Usage: manager show settings\n"
         "       Provides detailed list of the configuration of the Manager.\n";
      return NULL;
   case CLI_GENERATE:
      return NULL;
   }
#define FORMAT "  %-25.25s  %-15.15s\n"
#define FORMAT2 "  %-25.25s  %-15d\n"
   if (a->argc != 3) {
      return CLI_SHOWUSAGE;
   }
   ast_cli(a->fd, "\nGlobal Settings:\n");
   ast_cli(a->fd, "----------------\n");
   ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
   ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
   ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
   ast_cli(a->fd, FORMAT2, "HTTP Timeout (minutes):", httptimeout);
   ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
   ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
   ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
   ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
   ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
   ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
   ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
   ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
   ast_cli(a->fd, FORMAT, "Channel vars:", S_OR(manager_channelvars, ""));
   ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
   ast_cli(a->fd, FORMAT, "Block sockets:", AST_CLI_YESNO(block_sockets));
#undef FORMAT
#undef FORMAT2

   return CLI_SUCCESS;
}
int init_manager ( void  )

Called by Asterisk initialization.

Definition at line 6900 of file manager.c.

References __init_manager().

Referenced by main().

{
   return __init_manager(0);
}
static void load_channelvars ( struct ast_variable var) [static]

Definition at line 6435 of file manager.c.

References ast_calloc, ast_free, AST_RWLIST_INSERT_TAIL, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_strdup, ast_strdupa, free_channelvars(), manager_channel_variable::isfunc, manager_channel_variable::name, manager_channel_variable::next, strsep(), and ast_variable::value.

Referenced by __init_manager().

{
   struct manager_channel_variable *mcv;
   char *remaining = ast_strdupa(var->value);
   char *next;

   ast_free(manager_channelvars);
   manager_channelvars = ast_strdup(var->value);

   /*
    * XXX TODO: To allow dialplan functions to have more than one
    * parameter requires eliminating the '|' as a separator so we
    * could use AST_STANDARD_APP_ARGS() to separate items.
    */
   free_channelvars();
   AST_RWLIST_WRLOCK(&channelvars);
   while ((next = strsep(&remaining, ",|"))) {
      if (!(mcv = ast_calloc(1, sizeof(*mcv) + strlen(next) + 1))) {
         break;
      }
      strcpy(mcv->name, next); /* SAFE */
      if (strchr(next, '(')) {
         mcv->isfunc = 1;
      }
      AST_RWLIST_INSERT_TAIL(&channelvars, mcv, entry);
   }
   AST_RWLIST_UNLOCK(&channelvars);
}
static int manager_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6221 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, FORMAT_HTML, generic_http_callback(), and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
static int mxml_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6232 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, FORMAT_XML, generic_http_callback(), and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
static void process_output ( struct mansession s,
struct ast_str **  out,
struct ast_variable params,
enum output_format  format 
) [static]

Definition at line 5660 of file manager.c.

References ast_log(), ast_str_append(), mansession::f, mansession::fd, FORMAT_HTML, FORMAT_XML, LOG_WARNING, and xml_translate().

Referenced by auth_http_callback(), and generic_http_callback().

{
   char *buf;
   size_t l;

   if (!s->f)
      return;

   /* Ensure buffer is NULL-terminated */
   fprintf(s->f, "%c", 0);
   fflush(s->f);

   if ((l = ftell(s->f))) {
      if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s->fd, 0))) {
         ast_log(LOG_WARNING, "mmap failed.  Manager output was not processed\n");
      } else {
         if (format == FORMAT_XML || format == FORMAT_HTML) {
            xml_translate(out, buf, params, format);
         } else {
            ast_str_append(out, 0, "%s", buf);
         }
         munmap(buf, l);
      }
   } else if (format == FORMAT_XML || format == FORMAT_HTML) {
      xml_translate(out, "", params, format);
   }

   fclose(s->f);
   s->f = NULL;
   close(s->fd);
   s->fd = -1;
}
static void purge_old_stuff ( void *  data) [static]

cleanup code called at each iteration of server_root, guaranteed to happen every 5 seconds at most

Definition at line 6346 of file manager.c.

References purge_events(), and purge_sessions().

static int rawman_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
) [static]

Definition at line 6243 of file manager.c.

References ast_sockaddr_from_sin, ast_sockaddr_to_sin, FORMAT_RAW, generic_http_callback(), and ast_tcptls_session_instance::remote_address.

{
   int retval;
   struct sockaddr_in ser_remote_address_tmp;

   ast_sockaddr_to_sin(&ser->remote_address, &ser_remote_address_tmp);
   retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
   ast_sockaddr_from_sin(&ser->remote_address, &ser_remote_address_tmp);
   return retval;
}
int reload_manager ( void  )

Called by Asterisk module functions and the CLI command.

Definition at line 6905 of file manager.c.

References __init_manager().

Referenced by handle_manager_reload().

{
   return __init_manager(1);
}
static int variable_count_cmp_fn ( void *  obj,
void *  vstr,
int  flags 
) [static]

Definition at line 5504 of file manager.c.

References CMP_MATCH, CMP_STOP, str, and variable_count::varname.

Referenced by xml_translate().

{
   /* Due to the simplicity of struct variable_count, it makes no difference
    * if you pass in objects or strings, the same operation applies. This is
    * due to the fact that the hash occurs on the first element, which means
    * the address of both the struct and the string are exactly the same. */
   struct variable_count *vc = obj;
   char *str = vstr;
   return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
}
static int variable_count_hash_fn ( const void *  vvc,
const int  flags 
) [static]

Definition at line 5497 of file manager.c.

References ast_str_hash(), and variable_count::varname.

Referenced by xml_translate().

{
   const struct variable_count *vc = vvc;

   return ast_str_hash(vc->varname);
}
static void xml_copy_escape ( struct ast_str **  out,
const char *  src,
int  mode 
) [static]

Definition at line 5435 of file manager.c.

References ast_str_append(), and ast_frame::src.

Referenced by xml_translate().

{
   /* store in a local buffer to avoid calling ast_str_append too often */
   char buf[256];
   char *dst = buf;
   int space = sizeof(buf);
   /* repeat until done and nothing to flush */
   for ( ; *src || dst != buf ; src++) {
      if (*src == '\0' || space < 10) {   /* flush */
         *dst++ = '\0';
         ast_str_append(out, 0, "%s", buf);
         dst = buf;
         space = sizeof(buf);
         if (*src == '\0') {
            break;
         }
      }

      if ( (mode & 2) && !isalnum(*src)) {
         *dst++ = '_';
         space--;
         continue;
      }
      switch (*src) {
      case '<':
         strcpy(dst, "&lt;");
         dst += 4;
         space -= 4;
         break;
      case '>':
         strcpy(dst, "&gt;");
         dst += 4;
         space -= 4;
         break;
      case '\"':
         strcpy(dst, "&quot;");
         dst += 6;
         space -= 6;
         break;
      case '\'':
         strcpy(dst, "&apos;");
         dst += 6;
         space -= 6;
         break;
      case '&':
         strcpy(dst, "&amp;");
         dst += 5;
         space -= 5;
         break;

      default:
         *dst++ = mode ? tolower(*src) : *src;
         space--;
      }
   }
}
static void xml_translate ( struct ast_str **  out,
char *  in,
struct ast_variable get_vars,
enum output_format  format 
) [static]

Convert the input into XML or HTML. The input is supposed to be a sequence of lines of the form Name: value optionally followed by a blob of unformatted text. A blank line is a section separator. Basically, this is a mixture of the format of Manager Interface and CLI commands. The unformatted text is considered as a single value of a field named 'Opaque-data'.

At the moment the output format is the following (but it may change depending on future requirements so don't count too much on it when writing applications):

General: the unformatted text is used as a value of XML output: to be completed

 *   Each section is within <response type="object" id="xxx">
 *   where xxx is taken from ajaxdest variable or defaults to unknown
 *   Each row is reported as an attribute Name="value" of an XML
 *   entity named from the variable ajaxobjtype, default to "generic"
 * 

HTML output: each Name-value pair is output as a single row of a two-column table. Sections (blank lines in the input) are separated by a


Definition at line 5543 of file manager.c.

References ao2_alloc, ao2_container_alloc, ao2_find, ao2_link, ao2_ref, ast_debug, ast_skip_blanks(), ast_str_append(), ast_strlen_zero(), ast_trim_blanks(), variable_count::count, FORMAT_XML, ast_variable::name, ast_variable::next, strsep(), ast_variable::value, var, variable_count_cmp_fn(), variable_count_hash_fn(), variable_count::varname, and xml_copy_escape().

Referenced by process_output().

{
   struct ast_variable *v;
   const char *dest = NULL;
   char *var, *val;
   const char *objtype = NULL;
   int in_data = 0;  /* parsing data */
   int inobj = 0;
   int xml = (format == FORMAT_XML);
   struct variable_count *vc = NULL;
   struct ao2_container *vco = NULL;

   if (xml) {
      /* dest and objtype need only for XML format */
      for (v = get_vars; v; v = v->next) {
         if (!strcasecmp(v->name, "ajaxdest")) {
            dest = v->value;
         } else if (!strcasecmp(v->name, "ajaxobjtype")) {
            objtype = v->value;
         }
      }
      if (ast_strlen_zero(dest)) {
         dest = "unknown";
      }
      if (ast_strlen_zero(objtype)) {
         objtype = "generic";
      }
   }

   /* we want to stop when we find an empty line */
   while (in && *in) {
      val = strsep(&in, "\r\n"); /* mark start and end of line */
      if (in && *in == '\n') {   /* remove trailing \n if any */
         in++;
      }
      ast_trim_blanks(val);
      ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
      if (ast_strlen_zero(val)) {
         /* empty line */
         if (in_data) {
            /* close data in Opaque mode */
            ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
            in_data = 0;
         }

         if (inobj) {
            /* close block */
            ast_str_append(out, 0, xml ? " /></response>\n" :
               "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
            inobj = 0;
            ao2_ref(vco, -1);
            vco = NULL;
         }
         continue;
      }

      if (!inobj) {
         /* start new block */
         if (xml) {
            ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
         }
         vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
         inobj = 1;
      }

      if (in_data) {
         /* Process data field in Opaque mode. This is a
          * followup, so we re-add line feeds. */
         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
         xml_copy_escape(out, val, 0);   /* data field */
         continue;
      }

      /* We expect "Name: value" line here */
      var = strsep(&val, ":");
      if (val) {
         /* found the field name */
         val = ast_skip_blanks(val);
         ast_trim_blanks(var);
      } else {
         /* field name not found, switch to opaque mode */
         val = var;
         var = "Opaque-data";
         in_data = 1;
      }


      ast_str_append(out, 0, xml ? " " : "<tr><td>");
      if ((vc = ao2_find(vco, var, 0))) {
         vc->count++;
      } else {
         /* Create a new entry for this one */
         vc = ao2_alloc(sizeof(*vc), NULL);
         vc->varname = var;
         vc->count = 1;
         ao2_link(vco, vc);
      }

      xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
      if (vc->count > 1) {
         ast_str_append(out, 0, "-%d", vc->count);
      }
      ao2_ref(vc, -1);
      ast_str_append(out, 0, xml ? "='" : "</td><td>");
      xml_copy_escape(out, val, 0); /* data field */
      if (!in_data || !*in) {
         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
      }
   }

   if (inobj) {
      ast_str_append(out, 0, xml ? " /></response>\n" :
         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
      ao2_ref(vco, -1);
   }
}

Variable Documentation

struct ast_http_uri amanageruri [static]

Definition at line 6322 of file manager.c.

struct ast_http_uri amanagerxmluri [static]

Definition at line 6331 of file manager.c.

Definition at line 6353 of file manager.c.

struct ast_tls_config ami_tls_cfg [static]

Definition at line 6352 of file manager.c.

Referenced by __init_manager(), and handle_manager_show_settings().

Definition at line 6364 of file manager.c.

struct ast_http_uri arawmanuri [static]

Definition at line 6313 of file manager.c.

struct ast_cli_entry cli_manager[] [static]

Definition at line 6415 of file manager.c.

const char* const contenttype[] [static]
Initial value:
 {
   [FORMAT_RAW] = "plain",
   [FORMAT_HTML] = "html",
   [FORMAT_XML] =  "xml",
}

Definition at line 5292 of file manager.c.

struct ast_http_uri manageruri [static]

Definition at line 6262 of file manager.c.

struct ast_http_uri managerxmluri [static]

Definition at line 6270 of file manager.c.

struct ast_http_uri rawmanuri [static]

Definition at line 6254 of file manager.c.

int registered = 0 [static]

Definition at line 6340 of file manager.c.

int webregged = 0 [static]

Definition at line 6341 of file manager.c.

const char* words[AST_MAX_CMD_LEN]

Definition at line 921 of file manager.c.

Referenced by check_blacklist().