2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * Channel Management and more
30 /*! \addtogroup Group_AMI AMI functions
37 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
44 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
54 #include "asterisk/channel.h"
55 #include "asterisk/file.h"
56 #include "asterisk/manager.h"
57 #include "asterisk/config.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/lock.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/options.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/http.h"
69 #include "asterisk/threadstorage.h"
70 #include "asterisk/linkedlists.h"
72 struct fast_originate_helper
{
73 char tech
[AST_MAX_MANHEADER_LEN
];
74 char data
[AST_MAX_MANHEADER_LEN
];
76 char app
[AST_MAX_APP
];
77 char appdata
[AST_MAX_MANHEADER_LEN
];
78 char cid_name
[AST_MAX_MANHEADER_LEN
];
79 char cid_num
[AST_MAX_MANHEADER_LEN
];
80 char context
[AST_MAX_CONTEXT
];
81 char exten
[AST_MAX_EXTENSION
];
82 char idtext
[AST_MAX_MANHEADER_LEN
];
83 char account
[AST_MAX_ACCOUNT_CODE
];
85 struct ast_variable
*vars
;
91 struct eventqent
*next
;
96 static int portno
= DEFAULT_MANAGER_PORT
;
97 static int asock
= -1;
98 static int displayconnects
= 1;
99 static int timestampevents
;
100 static int httptimeout
= 60;
103 static int block_sockets
;
104 static int num_sessions
;
106 /* Protected by the sessions list lock */
107 struct eventqent
*master_eventq
= NULL
;
109 AST_THREADSTORAGE(manager_event_buf
, manager_event_buf_init
);
110 #define MANAGER_EVENT_BUF_INITSIZE 256
112 AST_THREADSTORAGE(astman_append_buf
, astman_append_buf_init
);
113 #define ASTMAN_APPEND_BUF_INITSIZE 256
115 static struct permalias
{
119 { EVENT_FLAG_SYSTEM
, "system" },
120 { EVENT_FLAG_CALL
, "call" },
121 { EVENT_FLAG_LOG
, "log" },
122 { EVENT_FLAG_VERBOSE
, "verbose" },
123 { EVENT_FLAG_COMMAND
, "command" },
124 { EVENT_FLAG_AGENT
, "agent" },
125 { EVENT_FLAG_USER
, "user" },
126 { EVENT_FLAG_CONFIG
, "config" },
132 /*! Execution thread */
134 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
136 /*! socket address */
137 struct sockaddr_in sin
;
140 /*! Whether an HTTP manager is in use */
142 /*! Whether an HTTP session should be destroyed */
144 /*! Whether an HTTP session has someone waiting on events */
145 pthread_t waiting_thread
;
146 /*! Unique manager identifer */
147 unsigned long managerid
;
148 /*! Session timeout if HTTP */
149 time_t sessiontimeout
;
150 /*! Output from manager interface */
151 struct ast_dynamic_str
*outputstr
;
152 /*! Logged in username */
154 /*! Authentication challenge */
156 /*! Authentication status */
158 /*! Authorization for reading */
160 /*! Authorization for writing */
163 char inbuf
[AST_MAX_MANHEADER_LEN
];
166 int displaysystemname
; /*!< Add system name to manager responses and events */
167 /* Queued events that we've not had the ability to send yet */
168 struct eventqent
*eventq
;
169 /* Timeout for ast_carefulwrite() */
171 AST_LIST_ENTRY(mansession
) list
;
174 static AST_LIST_HEAD_STATIC(sessions
, mansession
);
176 struct ast_manager_user
{
183 unsigned int displayconnects
:1;
185 AST_LIST_ENTRY(ast_manager_user
) list
;
188 static AST_LIST_HEAD_STATIC(users
, ast_manager_user
);
190 static struct manager_action
*first_action
;
191 AST_MUTEX_DEFINE_STATIC(actionlock
);
193 /*! \brief Convert authority code to string with serveral options */
194 static char *authority_to_str(int authority
, char *res
, int reslen
)
196 int running_total
= 0, i
;
198 memset(res
, 0, reslen
);
199 for (i
= 0; i
< (sizeof(perms
) / sizeof(perms
[0])) - 1; i
++) {
200 if (authority
& perms
[i
].num
) {
202 strncat(res
, ",", (reslen
> running_total
) ? reslen
- running_total
: 0);
205 strncat(res
, perms
[i
].label
, (reslen
> running_total
) ? reslen
- running_total
: 0);
206 running_total
+= strlen(perms
[i
].label
);
210 if (ast_strlen_zero(res
))
211 ast_copy_string(res
, "<none>", reslen
);
216 static char *complete_show_mancmd(const char *line
, const char *word
, int pos
, int state
)
218 struct manager_action
*cur
;
222 ast_mutex_lock(&actionlock
);
223 for (cur
= first_action
; cur
; cur
= cur
->next
) { /* Walk the list of actions */
224 if (!strncasecmp(word
, cur
->action
, strlen(word
)) && ++which
> state
) {
225 ret
= ast_strdup(cur
->action
);
226 break; /* make sure we exit even if ast_strdup() returns NULL */
229 ast_mutex_unlock(&actionlock
);
234 static void xml_copy_escape(char **dst
, size_t *maxlen
, const char *src
, int lower
)
236 while (*src
&& (*maxlen
> 6)) {
239 strcpy(*dst
, "<");
244 strcpy(*dst
, ">");
249 strcpy(*dst
, """);
254 strcpy(*dst
, "'");
259 strcpy(*dst
, "&");
264 *(*dst
)++ = lower
? tolower(*src
) : *src
;
271 static char *xml_translate(char *in
, struct ast_variable
*vars
)
273 struct ast_variable
*v
;
275 char *out
, *tmp
, *var
, *val
;
276 char *objtype
= NULL
;
285 for (v
= vars
; v
; v
= v
->next
) {
286 if (!dest
&& !strcasecmp(v
->name
, "ajaxdest"))
288 else if (!objtype
&& !strcasecmp(v
->name
, "ajaxobjtype"))
295 for (x
= 0; in
[x
]; x
++) {
298 else if (in
[x
] == '\n')
300 else if (strchr("&\"<>", in
[x
]))
303 len
= (size_t) (strlen(in
) + colons
* 5 + breaks
* (40 + strlen(dest
) + strlen(objtype
)) + escaped
* 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&" */
304 out
= ast_malloc(len
);
310 while (*in
&& (*in
>= 32))
313 if ((count
> 3) && inobj
) {
314 ast_build_string(&tmp
, &len
, " /></response>\n");
318 while (*in
&& (*in
< 32)) {
323 val
= strchr(var
, ':');
330 ast_build_string(&tmp
, &len
, "<response type='object' id='%s'><%s", dest
, objtype
);
333 ast_build_string(&tmp
, &len
, " ");
334 xml_copy_escape(&tmp
, &len
, var
, 1);
335 ast_build_string(&tmp
, &len
, "='");
336 xml_copy_escape(&tmp
, &len
, val
, 0);
337 ast_build_string(&tmp
, &len
, "'");
342 ast_build_string(&tmp
, &len
, " /></response>\n");
346 static char *html_translate(char *in
)
353 char *tmp
, *var
, *val
, *out
;
355 for (x
=0; in
[x
]; x
++) {
361 len
= strlen(in
) + colons
* 40 + breaks
* 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
362 out
= ast_malloc(len
);
368 while (*in
&& (*in
>= 32))
371 if ((count
% 4) == 0){
372 ast_build_string(&tmp
, &len
, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
375 while (*in
&& (*in
< 32)) {
380 val
= strchr(var
, ':');
386 ast_build_string(&tmp
, &len
, "<tr><td>%s</td><td>%s</td></tr>\r\n", var
, val
);
395 static struct ast_manager_user
*ast_get_manager_by_name_locked(const char *name
)
397 struct ast_manager_user
*user
= NULL
;
399 AST_LIST_TRAVERSE(&users
, user
, list
)
400 if (!strcasecmp(user
->username
, name
))
405 void astman_append(struct mansession
*s
, const char *fmt
, ...)
408 struct ast_dynamic_str
*buf
;
410 if (!(buf
= ast_dynamic_str_thread_get(&astman_append_buf
, ASTMAN_APPEND_BUF_INITSIZE
)))
414 ast_dynamic_str_thread_set_va(&buf
, 0, &astman_append_buf
, fmt
, ap
);
418 ast_carefulwrite(s
->fd
, buf
->str
, strlen(buf
->str
), s
->writetimeout
);
420 if (!s
->outputstr
&& !(s
->outputstr
= ast_calloc(1, sizeof(*s
->outputstr
))))
423 ast_dynamic_str_append(&s
->outputstr
, 0, "%s", buf
->str
);
427 static int handle_showmancmd(int fd
, int argc
, char *argv
[])
429 struct manager_action
*cur
= first_action
;
434 return RESULT_SHOWUSAGE
;
436 ast_mutex_lock(&actionlock
);
437 for (; cur
; cur
= cur
->next
) { /* Walk the list of actions */
438 for (num
= 3; num
< argc
; num
++) {
439 if (!strcasecmp(cur
->action
, argv
[num
])) {
440 ast_cli(fd
, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur
->action
, cur
->synopsis
, authority_to_str(cur
->authority
, authority
, sizeof(authority
) -1), cur
->description
? cur
->description
: "");
444 ast_mutex_unlock(&actionlock
);
446 return RESULT_SUCCESS
;
449 static int handle_showmanager(int fd
, int argc
, char *argv
[])
451 struct ast_manager_user
*user
= NULL
;
454 return RESULT_SHOWUSAGE
;
456 AST_LIST_LOCK(&users
);
458 if (!(user
= ast_get_manager_by_name_locked(argv
[3]))) {
459 ast_cli(fd
, "There is no manager called %s\n", argv
[3]);
460 AST_LIST_UNLOCK(&users
);
472 "displayconnects: %s\n",
473 (user
->username
? user
->username
: "(N/A)"),
474 (user
->secret
? user
->secret
: "(N/A)"),
475 (user
->deny
? user
->deny
: "(N/A)"),
476 (user
->permit
? user
->permit
: "(N/A)"),
477 (user
->read
? user
->read
: "(N/A)"),
478 (user
->write
? user
->write
: "(N/A)"),
479 (user
->displayconnects
? "yes" : "no"));
481 AST_LIST_UNLOCK(&users
);
483 return RESULT_SUCCESS
;
487 static int handle_showmanagers(int fd
, int argc
, char *argv
[])
489 struct ast_manager_user
*user
= NULL
;
493 return RESULT_SHOWUSAGE
;
495 AST_LIST_LOCK(&users
);
497 /* If there are no users, print out something along those lines */
498 if (AST_LIST_EMPTY(&users
)) {
499 ast_cli(fd
, "There are no manager users.\n");
500 AST_LIST_UNLOCK(&users
);
501 return RESULT_SUCCESS
;
504 ast_cli(fd
, "\nusername\n--------\n");
506 AST_LIST_TRAVERSE(&users
, user
, list
) {
507 ast_cli(fd
, "%s\n", user
->username
);
511 AST_LIST_UNLOCK(&users
);
513 ast_cli(fd
,"-------------------\n");
514 ast_cli(fd
,"%d manager users configured.\n", count_amu
);
516 return RESULT_SUCCESS
;
520 /*! \brief CLI command
521 Should change to "manager show commands" */
522 static int handle_showmancmds(int fd
, int argc
, char *argv
[])
524 struct manager_action
*cur
= first_action
;
526 char *format
= " %-15.15s %-15.15s %-55.55s\n";
528 ast_cli(fd
, format
, "Action", "Privilege", "Synopsis");
529 ast_cli(fd
, format
, "------", "---------", "--------");
531 ast_mutex_lock(&actionlock
);
532 for (; cur
; cur
= cur
->next
) /* Walk the list of actions */
533 ast_cli(fd
, format
, cur
->action
, authority_to_str(cur
->authority
, authority
, sizeof(authority
) -1), cur
->synopsis
);
534 ast_mutex_unlock(&actionlock
);
536 return RESULT_SUCCESS
;
539 /*! \brief CLI command show manager connected */
540 /* Should change to "manager show connected" */
541 static int handle_showmanconn(int fd
, int argc
, char *argv
[])
543 struct mansession
*s
;
544 char *format
= " %-15.15s %-15.15s\n";
546 ast_cli(fd
, format
, "Username", "IP Address");
548 AST_LIST_LOCK(&sessions
);
549 AST_LIST_TRAVERSE(&sessions
, s
, list
)
550 ast_cli(fd
, format
,s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
551 AST_LIST_UNLOCK(&sessions
);
553 return RESULT_SUCCESS
;
556 /*! \brief CLI command show manager connected */
557 /* Should change to "manager show connected" */
558 static int handle_showmaneventq(int fd
, int argc
, char *argv
[])
562 AST_LIST_LOCK(&sessions
);
563 for (s
= master_eventq
; s
; s
= s
->next
) {
564 ast_cli(fd
, "Usecount: %d\n",s
->usecount
);
565 ast_cli(fd
, "Category: %d\n", s
->category
);
566 ast_cli(fd
, "Event:\n%s", s
->eventdata
);
568 AST_LIST_UNLOCK(&sessions
);
570 return RESULT_SUCCESS
;
573 static char showmancmd_help
[] =
574 "Usage: manager show command <actionname>\n"
575 " Shows the detailed description for a specific Asterisk manager interface command.\n";
577 static char showmancmds_help
[] =
578 "Usage: manager show commands\n"
579 " Prints a listing of all the available Asterisk manager interface commands.\n";
581 static char showmanconn_help
[] =
582 "Usage: manager show connected\n"
583 " Prints a listing of the users that are currently connected to the\n"
584 "Asterisk manager interface.\n";
586 static char showmaneventq_help
[] =
587 "Usage: manager show eventq\n"
588 " Prints a listing of all events pending in the Asterisk manger\n"
591 static char showmanagers_help
[] =
592 "Usage: manager show users\n"
593 " Prints a listing of all managers that are currently configured on that\n"
596 static char showmanager_help
[] =
597 " Usage: manager show user <user>\n"
598 " Display all information related to the manager user specified.\n";
600 static struct ast_cli_entry cli_show_manager_command_deprecated
= {
601 { "show", "manager", "command", NULL
},
602 handle_showmancmd
, NULL
,
603 NULL
, complete_show_mancmd
};
605 static struct ast_cli_entry cli_show_manager_commands_deprecated
= {
606 { "show", "manager", "commands", NULL
},
607 handle_showmancmds
, NULL
,
610 static struct ast_cli_entry cli_show_manager_connected_deprecated
= {
611 { "show", "manager", "connected", NULL
},
612 handle_showmanconn
, NULL
,
615 static struct ast_cli_entry cli_show_manager_eventq_deprecated
= {
616 { "show", "manager", "eventq", NULL
},
617 handle_showmaneventq
, NULL
,
620 static struct ast_cli_entry cli_manager
[] = {
621 { { "manager", "show", "command", NULL
},
622 handle_showmancmd
, "Show a manager interface command",
623 showmancmd_help
, complete_show_mancmd
, &cli_show_manager_command_deprecated
},
625 { { "manager", "show", "commands", NULL
},
626 handle_showmancmds
, "List manager interface commands",
627 showmancmds_help
, NULL
, &cli_show_manager_commands_deprecated
},
629 { { "manager", "show", "connected", NULL
},
630 handle_showmanconn
, "List connected manager interface users",
631 showmanconn_help
, NULL
, &cli_show_manager_connected_deprecated
},
633 { { "manager", "show", "eventq", NULL
},
634 handle_showmaneventq
, "List manager interface queued events",
635 showmaneventq_help
, NULL
, &cli_show_manager_eventq_deprecated
},
637 { { "manager", "show", "users", NULL
},
638 handle_showmanagers
, "List configured manager users",
639 showmanagers_help
, NULL
, NULL
},
641 { { "manager", "show", "user", NULL
},
642 handle_showmanager
, "Display information on a specific manager user",
643 showmanager_help
, NULL
, NULL
},
646 static void unuse_eventqent(struct eventqent
*e
)
648 if (ast_atomic_dec_and_test(&e
->usecount
) && e
->next
)
649 pthread_kill(t
, SIGURG
);
652 static void free_session(struct mansession
*s
)
654 struct eventqent
*eqe
;
659 ast_mutex_destroy(&s
->__lock
);
662 s
->eventq
= s
->eventq
->next
;
663 unuse_eventqent(eqe
);
668 static void destroy_session(struct mansession
*s
)
670 AST_LIST_LOCK(&sessions
);
671 AST_LIST_REMOVE(&sessions
, s
, list
);
672 AST_LIST_UNLOCK(&sessions
);
674 ast_atomic_fetchadd_int(&num_sessions
, -1);
678 char *astman_get_header(struct message
*m
, char *var
)
683 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
685 for (x
= 0; x
< m
->hdrcount
; x
++) {
686 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
687 return m
->headers
[x
] + strlen(cmp
);
693 struct ast_variable
*astman_get_variables(struct message
*m
)
696 struct ast_variable
*head
= NULL
, *cur
;
700 AST_DECLARE_APP_ARGS(args
,
701 AST_APP_ARG(vars
)[32];
704 varlen
= strlen("Variable: ");
706 for (x
= 0; x
< m
->hdrcount
; x
++) {
707 if (strncasecmp("Variable: ", m
->headers
[x
], varlen
))
710 parse
= ast_strdupa(m
->headers
[x
] + varlen
);
712 AST_STANDARD_APP_ARGS(args
, parse
);
714 for (y
= 0; y
< args
.argc
; y
++) {
717 var
= val
= ast_strdupa(args
.vars
[y
]);
719 if (!val
|| ast_strlen_zero(var
))
721 cur
= ast_variable_new(var
, val
);
735 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
736 hold the session lock _or_ be running in an action callback (in which case s->busy will
737 be non-zero). In either of these cases, there is no need to lock-protect the session's
738 fd, since no other output will be sent (events will be queued), and no input will
739 be read until either the current action finishes or get_input() obtains the session
742 void astman_send_error(struct mansession
*s
, struct message
*m
, char *error
)
744 char *id
= astman_get_header(m
,"ActionID");
746 astman_append(s
, "Response: Error\r\n");
747 if (!ast_strlen_zero(id
))
748 astman_append(s
, "ActionID: %s\r\n", id
);
749 astman_append(s
, "Message: %s\r\n\r\n", error
);
752 void astman_send_response(struct mansession
*s
, struct message
*m
, char *resp
, char *msg
)
754 char *id
= astman_get_header(m
,"ActionID");
756 astman_append(s
, "Response: %s\r\n", resp
);
757 if (!ast_strlen_zero(id
))
758 astman_append(s
, "ActionID: %s\r\n", id
);
760 astman_append(s
, "Message: %s\r\n\r\n", msg
);
762 astman_append(s
, "\r\n");
765 void astman_send_ack(struct mansession
*s
, struct message
*m
, char *msg
)
767 astman_send_response(s
, m
, "Success", msg
);
770 /*! Tells you if smallstr exists inside bigstr
771 which is delim by delim and uses no buf or stringsep
772 ast_instring("this|that|more","this",',') == 1;
774 feel free to move this to app.c -anthm */
775 static int ast_instring(const char *bigstr
, const char *smallstr
, char delim
)
777 const char *val
= bigstr
, *next
;
780 if ((next
= strchr(val
, delim
))) {
781 if (!strncmp(val
, smallstr
, (next
- val
)))
786 return !strcmp(smallstr
, val
);
788 } while (*(val
= (next
+ 1)));
793 static int get_perm(const char *instr
)
800 for (x
= 0; x
< (sizeof(perms
) / sizeof(perms
[0])); x
++) {
801 if (ast_instring(instr
, perms
[x
].label
, ','))
808 static int ast_is_number(char *string
)
815 for (x
= 0; x
< strlen(string
); x
++) {
816 if (!(string
[x
] >= 48 && string
[x
] <= 57)) {
822 return ret
? atoi(string
) : 0;
825 static int ast_strings_to_mask(char *string
)
829 x
= ast_is_number(string
);
833 else if (ast_strlen_zero(string
))
835 else if (ast_false(string
))
837 else if (ast_true(string
)) {
839 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++)
843 for (x
=0; x
<sizeof(perms
) / sizeof(perms
[0]); x
++) {
844 if (ast_instring(string
, perms
[x
].label
, ','))
853 Rather than braindead on,off this now can also accept a specific int mask value
854 or a ',' delim list of mask strings (the same as manager.conf) -anthm
856 static int set_eventmask(struct mansession
*s
, char *eventmask
)
858 int maskint
= ast_strings_to_mask(eventmask
);
860 ast_mutex_lock(&s
->__lock
);
862 s
->send_events
= maskint
;
863 ast_mutex_unlock(&s
->__lock
);
868 static int authenticate(struct mansession
*s
, struct message
*m
)
870 struct ast_config
*cfg
;
872 char *user
= astman_get_header(m
, "Username");
873 char *pass
= astman_get_header(m
, "Secret");
874 char *authtype
= astman_get_header(m
, "AuthType");
875 char *key
= astman_get_header(m
, "Key");
876 char *events
= astman_get_header(m
, "Events");
878 cfg
= ast_config_load("manager.conf");
881 cat
= ast_category_browse(cfg
, NULL
);
883 if (strcasecmp(cat
, "general")) {
885 if (!strcasecmp(cat
, user
)) {
886 struct ast_variable
*v
;
887 struct ast_ha
*ha
= NULL
;
888 char *password
= NULL
;
890 for (v
= ast_variable_browse(cfg
, cat
); v
; v
= v
->next
) {
891 if (!strcasecmp(v
->name
, "secret")) {
893 } else if (!strcasecmp(v
->name
, "displaysystemname")) {
894 if (ast_true(v
->value
)) {
895 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME
)) {
896 s
->displaysystemname
= 1;
898 ast_log(LOG_ERROR
, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
901 } else if (!strcasecmp(v
->name
, "permit") ||
902 !strcasecmp(v
->name
, "deny")) {
903 ha
= ast_append_ha(v
->name
, v
->value
, ha
);
904 } else if (!strcasecmp(v
->name
, "writetimeout")) {
905 int val
= atoi(v
->value
);
908 ast_log(LOG_WARNING
, "Invalid writetimeout value '%s' at line %d\n", v
->value
, v
->lineno
);
910 s
->writetimeout
= val
;
914 if (ha
&& !ast_apply_ha(ha
, &(s
->sin
))) {
915 ast_log(LOG_NOTICE
, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
917 ast_config_destroy(cfg
);
921 if (!strcasecmp(authtype
, "MD5")) {
922 if (!ast_strlen_zero(key
) && s
->challenge
) {
925 char md5key
[256] = "";
926 struct MD5Context md5
;
927 unsigned char digest
[16];
929 MD5Update(&md5
, (unsigned char *) s
->challenge
, strlen(s
->challenge
));
930 MD5Update(&md5
, (unsigned char *) password
, strlen(password
));
931 MD5Final(digest
, &md5
);
933 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
934 if (!strcmp(md5key
, key
))
937 ast_config_destroy(cfg
);
941 } else if (password
&& !strcmp(password
, pass
)) {
944 ast_log(LOG_NOTICE
, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
945 ast_config_destroy(cfg
);
950 cat
= ast_category_browse(cfg
, cat
);
953 ast_copy_string(s
->username
, cat
, sizeof(s
->username
));
954 s
->readperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "read"));
955 s
->writeperm
= get_perm(ast_variable_retrieve(cfg
, cat
, "write"));
956 ast_config_destroy(cfg
);
958 set_eventmask(s
, events
);
961 ast_log(LOG_NOTICE
, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s
->sin
.sin_addr
), user
);
962 ast_config_destroy(cfg
);
966 /*! \brief Manager PING */
967 static char mandescr_ping
[] =
968 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
969 " manager connection open.\n"
972 static int action_ping(struct mansession
*s
, struct message
*m
)
974 astman_send_response(s
, m
, "Pong", NULL
);
978 static char mandescr_getconfig
[] =
979 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
980 "file by category and contents.\n"
982 " Filename: Configuration filename (e.g. foo.conf)\n";
984 static int action_getconfig(struct mansession
*s
, struct message
*m
)
986 struct ast_config
*cfg
;
987 char *fn
= astman_get_header(m
, "Filename");
991 struct ast_variable
*v
;
992 char idText
[256] = "";
993 char *id
= astman_get_header(m
, "ActionID");
995 if (!ast_strlen_zero(id
))
996 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
998 if (ast_strlen_zero(fn
)) {
999 astman_send_error(s
, m
, "Filename not specified");
1002 if (!(cfg
= ast_config_load_with_comments(fn
))) {
1003 astman_send_error(s
, m
, "Config file not found");
1006 astman_append(s
, "Response: Success\r\n%s", idText
);
1007 while ((category
= ast_category_browse(cfg
, category
))) {
1009 astman_append(s
, "Category-%06d: %s\r\n", catcount
, category
);
1010 for (v
= ast_variable_browse(cfg
, category
); v
; v
= v
->next
)
1011 astman_append(s
, "Line-%06d-%06d: %s=%s\r\n", catcount
, lineno
++, v
->name
, v
->value
);
1014 ast_config_destroy(cfg
);
1015 astman_append(s
, "\r\n");
1021 static void handle_updates(struct mansession
*s
, struct message
*m
, struct ast_config
*cfg
)
1025 char *action
, *cat
, *var
, *value
, *match
;
1026 struct ast_category
*category
;
1027 struct ast_variable
*v
;
1029 for (x
=0;x
<100000;x
++) {
1030 snprintf(hdr
, sizeof(hdr
), "Action-%06d", x
);
1031 action
= astman_get_header(m
, hdr
);
1032 if (ast_strlen_zero(action
))
1034 snprintf(hdr
, sizeof(hdr
), "Cat-%06d", x
);
1035 cat
= astman_get_header(m
, hdr
);
1036 snprintf(hdr
, sizeof(hdr
), "Var-%06d", x
);
1037 var
= astman_get_header(m
, hdr
);
1038 snprintf(hdr
, sizeof(hdr
), "Value-%06d", x
);
1039 value
= astman_get_header(m
, hdr
);
1040 snprintf(hdr
, sizeof(hdr
), "Match-%06d", x
);
1041 match
= astman_get_header(m
, hdr
);
1042 if (!strcasecmp(action
, "newcat")) {
1043 if (!ast_strlen_zero(cat
)) {
1044 category
= ast_category_new(cat
);
1046 ast_category_append(cfg
, category
);
1049 } else if (!strcasecmp(action
, "renamecat")) {
1050 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(value
)) {
1051 category
= ast_category_get(cfg
, cat
);
1053 ast_category_rename(category
, value
);
1055 } else if (!strcasecmp(action
, "delcat")) {
1056 if (!ast_strlen_zero(cat
))
1057 ast_category_delete(cfg
, cat
);
1058 } else if (!strcasecmp(action
, "update")) {
1059 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1060 ast_variable_update(category
, var
, value
, match
);
1061 } else if (!strcasecmp(action
, "delete")) {
1062 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) && (category
= ast_category_get(cfg
, cat
)))
1063 ast_variable_delete(category
, var
, match
);
1064 } else if (!strcasecmp(action
, "append")) {
1065 if (!ast_strlen_zero(cat
) && !ast_strlen_zero(var
) &&
1066 (category
= ast_category_get(cfg
, cat
)) &&
1067 (v
= ast_variable_new(var
, value
))){
1068 if (match
&& !strcasecmp(match
, "object"))
1070 ast_variable_append(category
, v
);
1076 static char mandescr_updateconfig
[] =
1077 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1078 "file by category and contents.\n"
1079 "Variables (X's represent 6 digit number beginning with 000000):\n"
1080 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1081 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1082 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1083 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1084 " Cat-XXXXXX: Category to operate on\n"
1085 " Var-XXXXXX: Variable to work on\n"
1086 " Value-XXXXXX: Value to work on\n"
1087 " Match-XXXXXX: Extra match required to match line\n";
1089 static int action_updateconfig(struct mansession
*s
, struct message
*m
)
1091 struct ast_config
*cfg
;
1092 char *sfn
= astman_get_header(m
, "SrcFilename");
1093 char *dfn
= astman_get_header(m
, "DstFilename");
1095 char idText
[256] = "";
1096 char *id
= astman_get_header(m
, "ActionID");
1097 char *rld
= astman_get_header(m
, "Reload");
1099 if (!ast_strlen_zero(id
))
1100 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1102 if (ast_strlen_zero(sfn
) || ast_strlen_zero(dfn
)) {
1103 astman_send_error(s
, m
, "Filename not specified");
1106 if (!(cfg
= ast_config_load_with_comments(sfn
))) {
1107 astman_send_error(s
, m
, "Config file not found");
1110 handle_updates(s
, m
, cfg
);
1111 res
= config_text_file_save(dfn
, cfg
, "Manager");
1112 ast_config_destroy(cfg
);
1114 astman_send_error(s
, m
, "Save of config failed");
1117 astman_append(s
, "Response: Success\r\n%s\r\n", idText
);
1118 if (!ast_strlen_zero(rld
)) {
1121 ast_module_reload(rld
);
1126 /*! \brief Manager WAITEVENT */
1127 static char mandescr_waitevent
[] =
1128 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1129 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1130 "session, events will be generated and queued.\n"
1132 " Timeout: Maximum time to wait for events\n";
1134 static int action_waitevent(struct mansession
*s
, struct message
*m
)
1136 char *timeouts
= astman_get_header(m
, "Timeout");
1137 int timeout
= -1, max
;
1141 struct eventqent
*eqe
;
1142 char *id
= astman_get_header(m
,"ActionID");
1143 char idText
[256] = "";
1145 if (!ast_strlen_zero(id
))
1146 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1148 if (!ast_strlen_zero(timeouts
)) {
1149 sscanf(timeouts
, "%i", &timeout
);
1152 ast_mutex_lock(&s
->__lock
);
1153 if (s
->waiting_thread
!= AST_PTHREADT_NULL
) {
1154 pthread_kill(s
->waiting_thread
, SIGURG
);
1156 if (s
->sessiontimeout
) {
1158 max
= s
->sessiontimeout
- now
- 10;
1161 if ((timeout
< 0) || (timeout
> max
))
1163 if (!s
->send_events
)
1164 s
->send_events
= -1;
1165 /* Once waitevent is called, always queue events from now on */
1167 ast_mutex_unlock(&s
->__lock
);
1168 s
->waiting_thread
= pthread_self();
1170 ast_log(LOG_DEBUG
, "Starting waiting for an event!\n");
1171 for (x
=0; ((x
< timeout
) || (timeout
< 0)); x
++) {
1172 ast_mutex_lock(&s
->__lock
);
1173 if (s
->eventq
&& s
->eventq
->next
)
1175 if (s
->waiting_thread
!= pthread_self())
1179 ast_mutex_unlock(&s
->__lock
);
1183 if (ast_wait_for_input(s
->fd
, 1000))
1190 ast_log(LOG_DEBUG
, "Finished waiting for an event!\n");
1191 ast_mutex_lock(&s
->__lock
);
1192 if (s
->waiting_thread
== pthread_self()) {
1193 astman_send_response(s
, m
, "Success", "Waiting for Event...");
1194 /* Only show events if we're the most recent waiter */
1195 while(s
->eventq
->next
) {
1196 eqe
= s
->eventq
->next
;
1197 if (((s
->readperm
& eqe
->category
) == eqe
->category
) &&
1198 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
1199 astman_append(s
, "%s", eqe
->eventdata
);
1201 unuse_eventqent(s
->eventq
);
1205 "Event: WaitEventComplete\r\n"
1208 s
->waiting_thread
= AST_PTHREADT_NULL
;
1210 ast_log(LOG_DEBUG
, "Abandoning event request!\n");
1212 ast_mutex_unlock(&s
->__lock
);
1216 static char mandescr_listcommands
[] =
1217 "Description: Returns the action name and synopsis for every\n"
1218 " action that is available to the user\n"
1219 "Variables: NONE\n";
1221 static int action_listcommands(struct mansession
*s
, struct message
*m
)
1223 struct manager_action
*cur
= first_action
;
1224 char idText
[256] = "";
1226 char *id
= astman_get_header(m
,"ActionID");
1228 if (!ast_strlen_zero(id
))
1229 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1230 astman_append(s
, "Response: Success\r\n%s", idText
);
1231 ast_mutex_lock(&actionlock
);
1232 while (cur
) { /* Walk the list of actions */
1233 if ((s
->writeperm
& cur
->authority
) == cur
->authority
)
1234 astman_append(s
, "%s: %s (Priv: %s)\r\n", cur
->action
, cur
->synopsis
, authority_to_str(cur
->authority
, temp
, sizeof(temp
)));
1237 ast_mutex_unlock(&actionlock
);
1238 astman_append(s
, "\r\n");
1243 static char mandescr_events
[] =
1244 "Description: Enable/Disable sending of events to this manager\n"
1247 " EventMask: 'on' if all events should be sent,\n"
1248 " 'off' if no events should be sent,\n"
1249 " 'system,call,log' to select which flags events should have to be sent.\n";
1251 static int action_events(struct mansession
*s
, struct message
*m
)
1253 char *mask
= astman_get_header(m
, "EventMask");
1256 res
= set_eventmask(s
, mask
);
1258 astman_send_response(s
, m
, "Events On", NULL
);
1260 astman_send_response(s
, m
, "Events Off", NULL
);
1265 static char mandescr_logoff
[] =
1266 "Description: Logoff this manager session\n"
1267 "Variables: NONE\n";
1269 static int action_logoff(struct mansession
*s
, struct message
*m
)
1271 astman_send_response(s
, m
, "Goodbye", "Thanks for all the fish.");
1275 static char mandescr_hangup
[] =
1276 "Description: Hangup a channel\n"
1278 " Channel: The channel name to be hungup\n";
1280 static int action_hangup(struct mansession
*s
, struct message
*m
)
1282 struct ast_channel
*c
= NULL
;
1283 char *name
= astman_get_header(m
, "Channel");
1284 if (ast_strlen_zero(name
)) {
1285 astman_send_error(s
, m
, "No channel specified");
1288 c
= ast_get_channel_by_name_locked(name
);
1290 astman_send_error(s
, m
, "No such channel");
1293 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
1294 ast_channel_unlock(c
);
1295 astman_send_ack(s
, m
, "Channel Hungup");
1299 static char mandescr_setvar
[] =
1300 "Description: Set a global or local channel variable.\n"
1301 "Variables: (Names marked with * are required)\n"
1302 " Channel: Channel to set variable for\n"
1303 " *Variable: Variable name\n"
1306 static int action_setvar(struct mansession
*s
, struct message
*m
)
1308 struct ast_channel
*c
= NULL
;
1309 char *name
= astman_get_header(m
, "Channel");
1310 char *varname
= astman_get_header(m
, "Variable");
1311 char *varval
= astman_get_header(m
, "Value");
1313 if (ast_strlen_zero(varname
)) {
1314 astman_send_error(s
, m
, "No variable specified");
1318 if (ast_strlen_zero(varval
)) {
1319 astman_send_error(s
, m
, "No value specified");
1323 if (!ast_strlen_zero(name
)) {
1324 c
= ast_get_channel_by_name_locked(name
);
1326 astman_send_error(s
, m
, "No such channel");
1331 pbx_builtin_setvar_helper(c
, varname
, varval
);
1334 ast_channel_unlock(c
);
1336 astman_send_ack(s
, m
, "Variable Set");
1341 static char mandescr_getvar
[] =
1342 "Description: Get the value of a global or local channel variable.\n"
1343 "Variables: (Names marked with * are required)\n"
1344 " Channel: Channel to read variable from\n"
1345 " *Variable: Variable name\n"
1346 " ActionID: Optional Action id for message matching.\n";
1348 static int action_getvar(struct mansession
*s
, struct message
*m
)
1350 struct ast_channel
*c
= NULL
;
1351 char *name
= astman_get_header(m
, "Channel");
1352 char *varname
= astman_get_header(m
, "Variable");
1353 char *id
= astman_get_header(m
,"ActionID");
1355 char workspace
[1024];
1357 if (ast_strlen_zero(varname
)) {
1358 astman_send_error(s
, m
, "No variable specified");
1362 if (!ast_strlen_zero(name
)) {
1363 c
= ast_get_channel_by_name_locked(name
);
1365 astman_send_error(s
, m
, "No such channel");
1370 if (varname
[strlen(varname
) - 1] == ')') {
1371 ast_func_read(c
, varname
, workspace
, sizeof(workspace
));
1373 pbx_retrieve_variable(c
, varname
, &varval
, workspace
, sizeof(workspace
), NULL
);
1377 ast_channel_unlock(c
);
1378 astman_append(s
, "Response: Success\r\n"
1379 "Variable: %s\r\nValue: %s\r\n", varname
, varval
);
1380 if (!ast_strlen_zero(id
))
1381 astman_append(s
, "ActionID: %s\r\n",id
);
1382 astman_append(s
, "\r\n");
1388 /*! \brief Manager "status" command to show channels */
1389 /* Needs documentation... */
1390 static int action_status(struct mansession
*s
, struct message
*m
)
1392 char *id
= astman_get_header(m
,"ActionID");
1393 char *name
= astman_get_header(m
,"Channel");
1394 char idText
[256] = "";
1395 struct ast_channel
*c
;
1397 struct timeval now
= ast_tvnow();
1398 long elapsed_seconds
= 0;
1399 int all
= ast_strlen_zero(name
); /* set if we want all channels */
1401 astman_send_ack(s
, m
, "Channel status will follow");
1402 if (!ast_strlen_zero(id
))
1403 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1405 c
= ast_channel_walk_locked(NULL
);
1407 c
= ast_get_channel_by_name_locked(name
);
1409 astman_send_error(s
, m
, "No such channel");
1413 /* if we look by name, we break after the first iteration */
1416 snprintf(bridge
, sizeof(bridge
), "Link: %s\r\n", c
->_bridge
->name
);
1421 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1425 "Privilege: Call\r\n"
1427 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1428 "CallerIDNum: %s\r\n"
1429 "CallerIDName: %s\r\n"
1441 S_OR(c
->cid
.cid_num
, "<unknown>"),
1442 S_OR(c
->cid
.cid_num
, "<unknown>"),
1443 S_OR(c
->cid
.cid_name
, "<unknown>"),
1445 ast_state2str(c
->_state
), c
->context
,
1446 c
->exten
, c
->priority
, (long)elapsed_seconds
, bridge
, c
->uniqueid
, idText
);
1450 "Privilege: Call\r\n"
1452 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1453 "CallerIDNum: %s\r\n"
1454 "CallerIDName: %s\r\n"
1462 S_OR(c
->cid
.cid_num
, "<unknown>"),
1463 S_OR(c
->cid
.cid_num
, "<unknown>"),
1464 S_OR(c
->cid
.cid_name
, "<unknown>"),
1466 ast_state2str(c
->_state
), bridge
, c
->uniqueid
, idText
);
1468 ast_channel_unlock(c
);
1471 c
= ast_channel_walk_locked(c
);
1474 "Event: StatusComplete\r\n"
1480 static char mandescr_redirect
[] =
1481 "Description: Redirect (transfer) a call.\n"
1482 "Variables: (Names marked with * are required)\n"
1483 " *Channel: Channel to redirect\n"
1484 " ExtraChannel: Second call leg to transfer (optional)\n"
1485 " *Exten: Extension to transfer to\n"
1486 " *Context: Context to transfer to\n"
1487 " *Priority: Priority to transfer to\n"
1488 " ActionID: Optional Action id for message matching.\n";
1490 /*! \brief action_redirect: The redirect manager command */
1491 static int action_redirect(struct mansession
*s
, struct message
*m
)
1493 char *name
= astman_get_header(m
, "Channel");
1494 char *name2
= astman_get_header(m
, "ExtraChannel");
1495 char *exten
= astman_get_header(m
, "Exten");
1496 char *context
= astman_get_header(m
, "Context");
1497 char *priority
= astman_get_header(m
, "Priority");
1498 struct ast_channel
*chan
, *chan2
= NULL
;
1502 if (ast_strlen_zero(name
)) {
1503 astman_send_error(s
, m
, "Channel not specified");
1506 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1507 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1508 astman_send_error(s
, m
, "Invalid priority\n");
1512 /* XXX watch out, possible deadlock!!! */
1513 chan
= ast_get_channel_by_name_locked(name
);
1516 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", name
);
1517 astman_send_error(s
, m
, buf
);
1520 if (!ast_strlen_zero(name2
))
1521 chan2
= ast_get_channel_by_name_locked(name2
);
1522 res
= ast_async_goto(chan
, context
, exten
, pi
);
1524 if (!ast_strlen_zero(name2
)) {
1526 res
= ast_async_goto(chan2
, context
, exten
, pi
);
1530 astman_send_ack(s
, m
, "Dual Redirect successful");
1532 astman_send_error(s
, m
, "Secondary redirect failed");
1534 astman_send_ack(s
, m
, "Redirect successful");
1536 astman_send_error(s
, m
, "Redirect failed");
1538 ast_channel_unlock(chan
);
1540 ast_channel_unlock(chan2
);
1544 static char mandescr_command
[] =
1545 "Description: Run a CLI command.\n"
1546 "Variables: (Names marked with * are required)\n"
1547 " *Command: Asterisk CLI command to run\n"
1548 " ActionID: Optional Action id for message matching.\n";
1550 /*! \brief action_command: Manager command "command" - execute CLI command */
1551 static int action_command(struct mansession
*s
, struct message
*m
)
1553 char *cmd
= astman_get_header(m
, "Command");
1554 char *id
= astman_get_header(m
, "ActionID");
1555 astman_append(s
, "Response: Follows\r\nPrivilege: Command\r\n");
1556 if (!ast_strlen_zero(id
))
1557 astman_append(s
, "ActionID: %s\r\n", id
);
1558 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1559 ast_cli_command(s
->fd
, cmd
);
1560 astman_append(s
, "--END COMMAND--\r\n\r\n");
1564 static void *fast_originate(void *data
)
1566 struct fast_originate_helper
*in
= data
;
1569 struct ast_channel
*chan
= NULL
;
1570 char requested_channel
[AST_CHANNEL_NAME
];
1572 if (!ast_strlen_zero(in
->app
)) {
1573 res
= ast_pbx_outgoing_app(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->app
, in
->appdata
, &reason
, 1,
1574 S_OR(in
->cid_num
, NULL
),
1575 S_OR(in
->cid_name
, NULL
),
1576 in
->vars
, in
->account
, &chan
);
1578 res
= ast_pbx_outgoing_exten(in
->tech
, AST_FORMAT_SLINEAR
, in
->data
, in
->timeout
, in
->context
, in
->exten
, in
->priority
, &reason
, 1,
1579 S_OR(in
->cid_num
, NULL
),
1580 S_OR(in
->cid_name
, NULL
),
1581 in
->vars
, in
->account
, &chan
);
1585 snprintf(requested_channel
, AST_CHANNEL_NAME
, "%s/%s", in
->tech
, in
->data
);
1586 /* Tell the manager what happened with the channel */
1587 manager_event(EVENT_FLAG_CALL
, "OriginateResponse",
1595 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1596 "CallerIDNum: %s\r\n"
1597 "CallerIDName: %s\r\n",
1598 in
->idtext
, res
? "Failure" : "Success", chan
? chan
->name
: requested_channel
, in
->context
, in
->exten
, reason
,
1599 chan
? chan
->uniqueid
: "<null>",
1600 S_OR(in
->cid_num
, "<unknown>"),
1601 S_OR(in
->cid_num
, "<unknown>"),
1602 S_OR(in
->cid_name
, "<unknown>")
1605 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1607 ast_channel_unlock(chan
);
1612 static char mandescr_originate
[] =
1613 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1614 " Application/Data\n"
1615 "Variables: (Names marked with * are required)\n"
1616 " *Channel: Channel name to call\n"
1617 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1618 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1619 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1620 " Application: Application to use\n"
1621 " Data: Data to use (requires 'Application')\n"
1622 " Timeout: How long to wait for call to be answered (in ms)\n"
1623 " CallerID: Caller ID to be set on the outgoing channel\n"
1624 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1625 " Account: Account code\n"
1626 " Async: Set to 'true' for fast origination\n";
1628 static int action_originate(struct mansession
*s
, struct message
*m
)
1630 char *name
= astman_get_header(m
, "Channel");
1631 char *exten
= astman_get_header(m
, "Exten");
1632 char *context
= astman_get_header(m
, "Context");
1633 char *priority
= astman_get_header(m
, "Priority");
1634 char *timeout
= astman_get_header(m
, "Timeout");
1635 char *callerid
= astman_get_header(m
, "CallerID");
1636 char *account
= astman_get_header(m
, "Account");
1637 char *app
= astman_get_header(m
, "Application");
1638 char *appdata
= astman_get_header(m
, "Data");
1639 char *async
= astman_get_header(m
, "Async");
1640 char *id
= astman_get_header(m
, "ActionID");
1641 struct ast_variable
*vars
= astman_get_variables(m
);
1643 char *l
= NULL
, *n
= NULL
;
1652 pthread_attr_t attr
;
1654 astman_send_error(s
, m
, "Channel not specified");
1657 if (!ast_strlen_zero(priority
) && (sscanf(priority
, "%d", &pi
) != 1)) {
1658 if ((pi
= ast_findlabel_extension(NULL
, context
, exten
, priority
, NULL
)) < 1) {
1659 astman_send_error(s
, m
, "Invalid priority\n");
1663 if (!ast_strlen_zero(timeout
) && (sscanf(timeout
, "%d", &to
) != 1)) {
1664 astman_send_error(s
, m
, "Invalid timeout\n");
1667 ast_copy_string(tmp
, name
, sizeof(tmp
));
1669 data
= strchr(tmp
, '/');
1671 astman_send_error(s
, m
, "Invalid channel\n");
1675 ast_copy_string(tmp2
, callerid
, sizeof(tmp2
));
1676 ast_callerid_parse(tmp2
, &n
, &l
);
1678 if (ast_strlen_zero(n
))
1682 ast_shrink_phone_number(l
);
1683 if (ast_strlen_zero(l
))
1686 if (ast_true(async
)) {
1687 struct fast_originate_helper
*fast
= ast_calloc(1, sizeof(*fast
));
1691 if (!ast_strlen_zero(id
))
1692 snprintf(fast
->idtext
, sizeof(fast
->idtext
), "ActionID: %s\r\n", id
);
1693 ast_copy_string(fast
->tech
, tech
, sizeof(fast
->tech
));
1694 ast_copy_string(fast
->data
, data
, sizeof(fast
->data
));
1695 ast_copy_string(fast
->app
, app
, sizeof(fast
->app
));
1696 ast_copy_string(fast
->appdata
, appdata
, sizeof(fast
->appdata
));
1698 ast_copy_string(fast
->cid_num
, l
, sizeof(fast
->cid_num
));
1700 ast_copy_string(fast
->cid_name
, n
, sizeof(fast
->cid_name
));
1702 ast_copy_string(fast
->context
, context
, sizeof(fast
->context
));
1703 ast_copy_string(fast
->exten
, exten
, sizeof(fast
->exten
));
1704 ast_copy_string(fast
->account
, account
, sizeof(fast
->account
));
1706 fast
->priority
= pi
;
1707 pthread_attr_init(&attr
);
1708 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1709 if (ast_pthread_create(&th
, &attr
, fast_originate
, fast
)) {
1715 } else if (!ast_strlen_zero(app
)) {
1716 res
= ast_pbx_outgoing_app(tech
, AST_FORMAT_SLINEAR
, data
, to
, app
, appdata
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1718 if (exten
&& context
&& pi
)
1719 res
= ast_pbx_outgoing_exten(tech
, AST_FORMAT_SLINEAR
, data
, to
, context
, exten
, pi
, &reason
, 1, l
, n
, vars
, account
, NULL
);
1721 astman_send_error(s
, m
, "Originate with 'Exten' requires 'Context' and 'Priority'");
1726 astman_send_ack(s
, m
, "Originate successfully queued");
1728 astman_send_error(s
, m
, "Originate failed");
1732 /*! \brief Help text for manager command mailboxstatus
1734 static char mandescr_mailboxstatus
[] =
1735 "Description: Checks a voicemail account for status.\n"
1736 "Variables: (Names marked with * are required)\n"
1737 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1738 " ActionID: Optional ActionID for message matching.\n"
1739 "Returns number of messages.\n"
1740 " Message: Mailbox Status\n"
1741 " Mailbox: <mailboxid>\n"
1742 " Waiting: <count>\n"
1745 static int action_mailboxstatus(struct mansession
*s
, struct message
*m
)
1747 char *mailbox
= astman_get_header(m
, "Mailbox");
1748 char *id
= astman_get_header(m
,"ActionID");
1749 char idText
[256] = "";
1751 if (ast_strlen_zero(mailbox
)) {
1752 astman_send_error(s
, m
, "Mailbox not specified");
1755 if (!ast_strlen_zero(id
))
1756 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1757 ret
= ast_app_has_voicemail(mailbox
, NULL
);
1758 astman_append(s
, "Response: Success\r\n"
1760 "Message: Mailbox Status\r\n"
1762 "Waiting: %d\r\n\r\n", idText
, mailbox
, ret
);
1766 static char mandescr_mailboxcount
[] =
1767 "Description: Checks a voicemail account for new messages.\n"
1768 "Variables: (Names marked with * are required)\n"
1769 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1770 " ActionID: Optional ActionID for message matching.\n"
1771 "Returns number of new and old messages.\n"
1772 " Message: Mailbox Message Count\n"
1773 " Mailbox: <mailboxid>\n"
1774 " NewMessages: <count>\n"
1775 " OldMessages: <count>\n"
1777 static int action_mailboxcount(struct mansession
*s
, struct message
*m
)
1779 char *mailbox
= astman_get_header(m
, "Mailbox");
1780 char *id
= astman_get_header(m
,"ActionID");
1781 char idText
[256] = "";
1782 int newmsgs
= 0, oldmsgs
= 0;
1783 if (ast_strlen_zero(mailbox
)) {
1784 astman_send_error(s
, m
, "Mailbox not specified");
1787 ast_app_inboxcount(mailbox
, &newmsgs
, &oldmsgs
);
1788 if (!ast_strlen_zero(id
)) {
1789 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n",id
);
1791 astman_append(s
, "Response: Success\r\n"
1793 "Message: Mailbox Message Count\r\n"
1795 "NewMessages: %d\r\n"
1796 "OldMessages: %d\r\n"
1798 idText
,mailbox
, newmsgs
, oldmsgs
);
1802 static char mandescr_extensionstate
[] =
1803 "Description: Report the extension state for given extension.\n"
1804 " If the extension has a hint, will use devicestate to check\n"
1805 " the status of the device connected to the extension.\n"
1806 "Variables: (Names marked with * are required)\n"
1807 " *Exten: Extension to check state on\n"
1808 " *Context: Context for extension\n"
1809 " ActionId: Optional ID for this transaction\n"
1810 "Will return an \"Extension Status\" message.\n"
1811 "The response will include the hint for the extension and the status.\n";
1813 static int action_extensionstate(struct mansession
*s
, struct message
*m
)
1815 char *exten
= astman_get_header(m
, "Exten");
1816 char *context
= astman_get_header(m
, "Context");
1817 char *id
= astman_get_header(m
,"ActionID");
1818 char idText
[256] = "";
1819 char hint
[256] = "";
1821 if (ast_strlen_zero(exten
)) {
1822 astman_send_error(s
, m
, "Extension not specified");
1825 if (ast_strlen_zero(context
))
1826 context
= "default";
1827 status
= ast_extension_state(NULL
, context
, exten
);
1828 ast_get_hint(hint
, sizeof(hint
) - 1, NULL
, 0, NULL
, context
, exten
);
1829 if (!ast_strlen_zero(id
)) {
1830 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1832 astman_append(s
, "Response: Success\r\n"
1834 "Message: Extension Status\r\n"
1838 "Status: %d\r\n\r\n",
1839 idText
,exten
, context
, hint
, status
);
1843 static char mandescr_timeout
[] =
1844 "Description: Hangup a channel after a certain time.\n"
1845 "Variables: (Names marked with * are required)\n"
1846 " *Channel: Channel name to hangup\n"
1847 " *Timeout: Maximum duration of the call (sec)\n"
1848 "Acknowledges set time with 'Timeout Set' message\n";
1850 static int action_timeout(struct mansession
*s
, struct message
*m
)
1852 struct ast_channel
*c
= NULL
;
1853 char *name
= astman_get_header(m
, "Channel");
1854 int timeout
= atoi(astman_get_header(m
, "Timeout"));
1855 if (ast_strlen_zero(name
)) {
1856 astman_send_error(s
, m
, "No channel specified");
1860 astman_send_error(s
, m
, "No timeout specified");
1863 c
= ast_get_channel_by_name_locked(name
);
1865 astman_send_error(s
, m
, "No such channel");
1868 ast_channel_setwhentohangup(c
, timeout
);
1869 ast_channel_unlock(c
);
1870 astman_send_ack(s
, m
, "Timeout Set");
1874 static int process_events(struct mansession
*s
)
1876 struct eventqent
*eqe
;
1878 ast_mutex_lock(&s
->__lock
);
1881 s
->eventq
= master_eventq
;
1882 while(s
->eventq
->next
) {
1883 eqe
= s
->eventq
->next
;
1884 if ((s
->authenticated
&& (s
->readperm
& eqe
->category
) == eqe
->category
) &&
1885 ((s
->send_events
& eqe
->category
) == eqe
->category
)) {
1886 if (!ret
&& ast_carefulwrite(s
->fd
, eqe
->eventdata
, strlen(eqe
->eventdata
), s
->writetimeout
) < 0)
1889 unuse_eventqent(s
->eventq
);
1893 ast_mutex_unlock(&s
->__lock
);
1897 static char mandescr_userevent
[] =
1898 "Description: Send an event to manager sessions.\n"
1899 "Variables: (Names marked with * are required)\n"
1900 " *UserEvent: EventStringToSend\n"
1901 " Header1: Content1\n"
1902 " HeaderN: ContentN\n";
1904 static int action_userevent(struct mansession
*s
, struct message
*m
)
1906 char *event
= astman_get_header(m
, "UserEvent");
1907 char body
[2048] = "";
1909 for (x
= 0; x
< m
->hdrcount
; x
++) {
1910 if (strncasecmp("UserEvent:", m
->headers
[x
], strlen("UserEvent:"))) {
1911 ast_copy_string(body
+ bodylen
, m
->headers
[x
], sizeof(body
) - bodylen
- 3);
1912 bodylen
+= strlen(m
->headers
[x
]);
1913 ast_copy_string(body
+ bodylen
, "\r\n", 3);
1918 manager_event(EVENT_FLAG_USER
, "UserEvent", "UserEvent: %s\r\n%s", event
, body
);
1922 static int process_message(struct mansession
*s
, struct message
*m
)
1924 char action
[80] = "";
1925 struct manager_action
*tmp
= first_action
;
1926 char *id
= astman_get_header(m
,"ActionID");
1927 char idText
[256] = "";
1930 ast_copy_string(action
, astman_get_header(m
, "Action"), sizeof(action
));
1931 ast_log( LOG_DEBUG
, "Manager received command '%s'\n", action
);
1933 if (ast_strlen_zero(action
)) {
1934 astman_send_error(s
, m
, "Missing action in request");
1937 if (!ast_strlen_zero(id
)) {
1938 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
1940 if (!s
->authenticated
) {
1941 if (!strcasecmp(action
, "Challenge")) {
1943 authtype
= astman_get_header(m
, "AuthType");
1944 if (!strcasecmp(authtype
, "MD5")) {
1945 if (ast_strlen_zero(s
->challenge
))
1946 snprintf(s
->challenge
, sizeof(s
->challenge
), "%ld", ast_random());
1947 ast_mutex_lock(&s
->__lock
);
1948 astman_append(s
, "Response: Success\r\n"
1950 "Challenge: %s\r\n\r\n",
1951 idText
, s
->challenge
);
1952 ast_mutex_unlock(&s
->__lock
);
1955 astman_send_error(s
, m
, "Must specify AuthType");
1958 } else if (!strcasecmp(action
, "Login")) {
1959 if (authenticate(s
, m
)) {
1961 astman_send_error(s
, m
, "Authentication failed");
1964 s
->authenticated
= 1;
1965 if (option_verbose
> 1) {
1966 if (displayconnects
) {
1967 ast_verbose(VERBOSE_PREFIX_2
"%sManager '%s' logged on from %s\n", (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
1970 ast_log(LOG_EVENT
, "%sManager '%s' logged on from %s\n", (s
->sessiontimeout
? "HTTP " : ""), s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
1971 astman_send_ack(s
, m
, "Authentication accepted");
1973 } else if (!strcasecmp(action
, "Logoff")) {
1974 astman_send_ack(s
, m
, "See ya");
1977 astman_send_error(s
, m
, "Authentication Required");
1980 if (!strcasecmp(action
, tmp
->action
)) {
1981 if ((s
->writeperm
& tmp
->authority
) == tmp
->authority
) {
1982 if (tmp
->func(s
, m
))
1985 astman_send_error(s
, m
, "Permission denied");
1992 astman_send_error(s
, m
, "Invalid/unknown command");
1996 return process_events(s
);
1999 static int get_input(struct mansession
*s
, char *output
)
2001 /* output must have at least sizeof(s->inbuf) space */
2004 struct pollfd fds
[1];
2005 for (x
= 1; x
< s
->inlen
; x
++) {
2006 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
2007 /* Copy output data up to and including \r\n */
2008 memcpy(output
, s
->inbuf
, x
+ 1);
2009 /* Add trailing \0 */
2011 /* Move remaining data back to the front */
2012 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
2013 s
->inlen
-= (x
+ 1);
2017 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
2018 ast_log(LOG_WARNING
, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
2022 fds
[0].events
= POLLIN
;
2024 ast_mutex_lock(&s
->__lock
);
2025 s
->waiting_thread
= pthread_self();
2026 ast_mutex_unlock(&s
->__lock
);
2028 res
= poll(fds
, 1, -1);
2030 ast_mutex_lock(&s
->__lock
);
2031 s
->waiting_thread
= AST_PTHREADT_NULL
;
2032 ast_mutex_unlock(&s
->__lock
);
2034 if (errno
== EINTR
) {
2037 ast_log(LOG_WARNING
, "Select returned error: %s\n", strerror(errno
));
2039 } else if (res
> 0) {
2040 ast_mutex_lock(&s
->__lock
);
2041 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
2042 ast_mutex_unlock(&s
->__lock
);
2049 s
->inbuf
[s
->inlen
] = '\0';
2053 static void *session_do(void *data
)
2055 struct mansession
*s
= data
;
2059 ast_mutex_lock(&s
->__lock
);
2060 astman_append(s
, "Asterisk Call Manager/1.0\r\n");
2061 ast_mutex_unlock(&s
->__lock
);
2062 memset(&m
, 0, sizeof(m
));
2064 res
= get_input(s
, m
.headers
[m
.hdrcount
]);
2066 /* Strip trailing \r\n */
2067 if (strlen(m
.headers
[m
.hdrcount
]) < 2)
2069 m
.headers
[m
.hdrcount
][strlen(m
.headers
[m
.hdrcount
]) - 2] = '\0';
2070 if (ast_strlen_zero(m
.headers
[m
.hdrcount
])) {
2071 if (process_message(s
, &m
))
2073 memset(&m
, 0, sizeof(m
));
2074 } else if (m
.hdrcount
< AST_MAX_MANHEADERS
- 1)
2076 } else if (res
< 0) {
2078 } else if (s
->eventq
->next
) {
2079 if (process_events(s
))
2083 if (s
->authenticated
) {
2084 if (option_verbose
> 1) {
2085 if (displayconnects
)
2086 ast_verbose(VERBOSE_PREFIX_2
"Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2088 ast_log(LOG_EVENT
, "Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2090 if (option_verbose
> 1) {
2091 if (displayconnects
)
2092 ast_verbose(VERBOSE_PREFIX_2
"Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2094 ast_log(LOG_EVENT
, "Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2100 static void *accept_thread(void *ignore
)
2103 struct sockaddr_in sin
;
2105 struct eventqent
*eqe
;
2106 struct mansession
*s
;
2110 pthread_attr_t attr
;
2112 struct pollfd pfds
[1];
2114 pthread_attr_init(&attr
);
2115 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
2119 AST_LIST_LOCK(&sessions
);
2120 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions
, s
, list
) {
2121 if (s
->sessiontimeout
&& (now
> s
->sessiontimeout
) && !s
->inuse
) {
2122 AST_LIST_REMOVE_CURRENT(&sessions
, list
);
2123 if (s
->authenticated
&& (option_verbose
> 1) && displayconnects
) {
2124 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' timed out from %s\n",
2125 s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2131 AST_LIST_TRAVERSE_SAFE_END
2132 /* Purge master event queue of old, unused events, but make sure we
2133 always keep at least one in the queue */
2134 eqe
= master_eventq
;
2135 while (master_eventq
->next
&& !master_eventq
->usecount
) {
2136 eqe
= master_eventq
;
2137 master_eventq
= master_eventq
->next
;
2140 AST_LIST_UNLOCK(&sessions
);
2142 ast_atomic_fetchadd_int(&num_sessions
, -1);
2144 sinlen
= sizeof(sin
);
2146 pfds
[0].events
= POLLIN
;
2147 /* Wait for something to happen, but timeout every few seconds so
2148 we can ditch any old manager sessions */
2149 if (poll(pfds
, 1, 5000) < 1)
2151 as
= accept(asock
, (struct sockaddr
*)&sin
, &sinlen
);
2153 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
2156 p
= getprotobyname("tcp");
2158 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
2159 ast_log(LOG_WARNING
, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
2162 if (!(s
= ast_calloc(1, sizeof(*s
))))
2165 ast_atomic_fetchadd_int(&num_sessions
, 1);
2167 memcpy(&s
->sin
, &sin
, sizeof(sin
));
2168 s
->writetimeout
= 100;
2169 s
->waiting_thread
= AST_PTHREADT_NULL
;
2171 if (!block_sockets
) {
2172 /* For safety, make sure socket is non-blocking */
2173 flags
= fcntl(as
, F_GETFL
);
2174 fcntl(as
, F_SETFL
, flags
| O_NONBLOCK
);
2176 flags
= fcntl(as
, F_GETFL
);
2177 fcntl(as
, F_SETFL
, flags
& ~O_NONBLOCK
);
2179 ast_mutex_init(&s
->__lock
);
2181 s
->send_events
= -1;
2182 AST_LIST_LOCK(&sessions
);
2183 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2184 /* Find the last place in the master event queue and hook ourselves
2186 s
->eventq
= master_eventq
;
2187 while(s
->eventq
->next
)
2188 s
->eventq
= s
->eventq
->next
;
2189 AST_LIST_UNLOCK(&sessions
);
2190 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2191 if (ast_pthread_create_background(&s
->t
, &attr
, session_do
, s
))
2194 pthread_attr_destroy(&attr
);
2198 static int append_event(const char *str
, int category
)
2200 struct eventqent
*tmp
, *prev
= NULL
;
2201 tmp
= ast_malloc(sizeof(*tmp
) + strlen(str
));
2207 tmp
->category
= category
;
2208 strcpy(tmp
->eventdata
, str
);
2210 if (master_eventq
) {
2211 prev
= master_eventq
;
2216 master_eventq
= tmp
;
2219 tmp
->usecount
= num_sessions
;
2224 /*! \brief manager_event: Send AMI event to client */
2225 int manager_event(int category
, const char *event
, const char *fmt
, ...)
2227 struct mansession
*s
;
2231 struct ast_dynamic_str
*buf
;
2233 /* Abort if there aren't any manager sessions */
2237 if (!(buf
= ast_dynamic_str_thread_get(&manager_event_buf
, MANAGER_EVENT_BUF_INITSIZE
)))
2240 ast_dynamic_str_thread_set(&buf
, 0, &manager_event_buf
,
2241 "Event: %s\r\nPrivilege: %s\r\n",
2242 event
, authority_to_str(category
, auth
, sizeof(auth
)));
2244 if (timestampevents
) {
2246 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
,
2247 "Timestamp: %ld.%06lu\r\n",
2248 now
.tv_sec
, (unsigned long) now
.tv_usec
);
2252 ast_dynamic_str_thread_append_va(&buf
, 0, &manager_event_buf
, fmt
, ap
);
2255 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
, "\r\n");
2257 /* Append event to master list and wake up any sleeping sessions */
2258 AST_LIST_LOCK(&sessions
);
2259 append_event(buf
->str
, category
);
2260 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2261 ast_mutex_lock(&s
->__lock
);
2262 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2263 pthread_kill(s
->waiting_thread
, SIGURG
);
2264 ast_mutex_unlock(&s
->__lock
);
2266 AST_LIST_UNLOCK(&sessions
);
2271 int ast_manager_unregister(char *action
)
2273 struct manager_action
*cur
= first_action
, *prev
= first_action
;
2275 ast_mutex_lock(&actionlock
);
2277 if (!strcasecmp(action
, cur
->action
)) {
2278 prev
->next
= cur
->next
;
2280 if (option_verbose
> 1)
2281 ast_verbose(VERBOSE_PREFIX_2
"Manager unregistered action %s\n", action
);
2282 ast_mutex_unlock(&actionlock
);
2288 ast_mutex_unlock(&actionlock
);
2292 static int manager_state_cb(char *context
, char *exten
, int state
, void *data
)
2294 /* Notify managers of change */
2295 manager_event(EVENT_FLAG_CALL
, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten
, context
, state
);
2299 static int ast_manager_register_struct(struct manager_action
*act
)
2301 struct manager_action
*cur
= first_action
, *prev
= NULL
;
2304 ast_mutex_lock(&actionlock
);
2305 while (cur
) { /* Walk the list of actions */
2306 ret
= strcasecmp(cur
->action
, act
->action
);
2308 ast_log(LOG_WARNING
, "Manager: Action '%s' already registered\n", act
->action
);
2309 ast_mutex_unlock(&actionlock
);
2311 } else if (ret
> 0) {
2312 /* Insert these alphabetically */
2314 act
->next
= prev
->next
;
2317 act
->next
= first_action
;
2334 if (option_verbose
> 1)
2335 ast_verbose(VERBOSE_PREFIX_2
"Manager registered action %s\n", act
->action
);
2336 ast_mutex_unlock(&actionlock
);
2340 /*! \brief register a new command with manager, including online help. This is
2341 the preferred way to register a manager command */
2342 int ast_manager_register2(const char *action
, int auth
, int (*func
)(struct mansession
*s
, struct message
*m
), const char *synopsis
, const char *description
)
2344 struct manager_action
*cur
;
2346 cur
= ast_malloc(sizeof(*cur
));
2350 cur
->action
= action
;
2351 cur
->authority
= auth
;
2353 cur
->synopsis
= synopsis
;
2354 cur
->description
= description
;
2357 ast_manager_register_struct(cur
);
2362 END Doxygen group */
2364 static struct mansession
*find_session(unsigned long ident
)
2366 struct mansession
*s
;
2368 AST_LIST_LOCK(&sessions
);
2369 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2370 ast_mutex_lock(&s
->__lock
);
2371 if (s
->sessiontimeout
&& (s
->managerid
== ident
) && !s
->needdestroy
) {
2375 ast_mutex_unlock(&s
->__lock
);
2377 AST_LIST_UNLOCK(&sessions
);
2383 static void vars2msg(struct message
*m
, struct ast_variable
*vars
)
2386 for (x
= 0; vars
&& (x
< AST_MAX_MANHEADERS
); x
++, vars
= vars
->next
) {
2389 m
->hdrcount
= x
+ 1;
2390 snprintf(m
->headers
[x
], sizeof(m
->headers
[x
]), "%s: %s", vars
->name
, vars
->value
);
2399 static char *contenttype
[] = { "plain", "html", "xml" };
2401 static char *generic_http_callback(int format
, struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2403 struct mansession
*s
= NULL
;
2404 unsigned long ident
= 0;
2405 char workspace
[512];
2407 size_t len
= sizeof(workspace
);
2409 char *c
= workspace
;
2410 char *retval
= NULL
;
2412 struct ast_variable
*v
;
2414 for (v
= params
; v
; v
= v
->next
) {
2415 if (!strcasecmp(v
->name
, "mansession_id")) {
2416 sscanf(v
->value
, "%lx", &ident
);
2421 if (!(s
= find_session(ident
))) {
2422 /* Create new session */
2423 if (!(s
= ast_calloc(1, sizeof(*s
)))) {
2425 goto generic_callback_out
;
2427 memcpy(&s
->sin
, requestor
, sizeof(s
->sin
));
2429 s
->waiting_thread
= AST_PTHREADT_NULL
;
2431 ast_mutex_init(&s
->__lock
);
2432 ast_mutex_lock(&s
->__lock
);
2434 s
->managerid
= rand() | (unsigned long)s
;
2435 AST_LIST_LOCK(&sessions
);
2436 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2437 /* Hook into the last spot in the event queue */
2438 s
->eventq
= master_eventq
;
2439 while (s
->eventq
->next
)
2440 s
->eventq
= s
->eventq
->next
;
2441 AST_LIST_UNLOCK(&sessions
);
2442 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2443 ast_atomic_fetchadd_int(&num_sessions
, 1);
2446 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2447 time(&s
->sessiontimeout
);
2448 if (!s
->authenticated
&& (httptimeout
> 5))
2449 s
->sessiontimeout
+= 5;
2451 s
->sessiontimeout
+= httptimeout
;
2452 ast_mutex_unlock(&s
->__lock
);
2454 memset(&m
, 0, sizeof(m
));
2457 ast_build_string(&c
, &len
, "Content-type: text/%s\r\n", contenttype
[format
]);
2458 sprintf(tmp
, "%08lx", s
->managerid
);
2459 ast_build_string(&c
, &len
, "%s\r\n", ast_http_setcookie("mansession_id", tmp
, httptimeout
, cookie
, sizeof(cookie
)));
2460 if (format
== FORMAT_HTML
)
2461 ast_build_string(&c
, &len
, "<title>Asterisk™ Manager Test Interface</title>");
2462 vars2msg(&m
, params
);
2463 if (format
== FORMAT_XML
) {
2464 ast_build_string(&c
, &len
, "<ajax-response>\n");
2465 } else if (format
== FORMAT_HTML
) {
2466 ast_build_string(&c
, &len
, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2467 ast_build_string(&c
, &len
, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2469 if (process_message(s
, &m
)) {
2470 if (s
->authenticated
) {
2471 if (option_verbose
> 1) {
2472 if (displayconnects
)
2473 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2475 ast_log(LOG_EVENT
, "HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2477 if (option_verbose
> 1) {
2478 if (displayconnects
)
2479 ast_verbose(VERBOSE_PREFIX_2
"HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2481 ast_log(LOG_EVENT
, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2487 if (format
== FORMAT_XML
)
2488 tmp
= xml_translate(s
->outputstr
->str
, params
);
2489 else if (format
== FORMAT_HTML
)
2490 tmp
= html_translate(s
->outputstr
->str
);
2492 tmp
= s
->outputstr
->str
;
2494 retval
= malloc(strlen(workspace
) + strlen(tmp
) + 128);
2496 strcpy(retval
, workspace
);
2497 strcpy(retval
+ strlen(retval
), tmp
);
2498 c
= retval
+ strlen(retval
);
2502 if (tmp
!= s
->outputstr
->str
)
2505 s
->outputstr
= NULL
;
2507 /* Still okay because c would safely be pointing to workspace even
2508 if retval failed to allocate above */
2509 if (format
== FORMAT_XML
) {
2510 ast_build_string(&c
, &len
, "</ajax-response>\n");
2511 } else if (format
== FORMAT_HTML
)
2512 ast_build_string(&c
, &len
, "</table></body>\r\n");
2515 *title
= strdup("Server Error");
2517 ast_mutex_lock(&s
->__lock
);
2518 if (s
->needdestroy
) {
2519 if (s
->inuse
== 1) {
2520 ast_log(LOG_DEBUG
, "Need destroy, doing it now!\n");
2523 ast_log(LOG_DEBUG
, "Need destroy, but can't do it yet!\n");
2524 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2525 pthread_kill(s
->waiting_thread
, SIGURG
);
2530 ast_mutex_unlock(&s
->__lock
);
2534 generic_callback_out
:
2536 return ast_http_error(500, "Server Error", NULL
, "Internal Server Error (out of memory)\n");
2540 static char *manager_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2542 return generic_http_callback(FORMAT_HTML
, requestor
, uri
, params
, status
, title
, contentlength
);
2545 static char *mxml_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2547 return generic_http_callback(FORMAT_XML
, requestor
, uri
, params
, status
, title
, contentlength
);
2550 static char *rawman_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2552 return generic_http_callback(FORMAT_RAW
, requestor
, uri
, params
, status
, title
, contentlength
);
2555 struct ast_http_uri rawmanuri
= {
2556 .description
= "Raw HTTP Manager Event Interface",
2559 .callback
= rawman_http_callback
,
2562 struct ast_http_uri manageruri
= {
2563 .description
= "HTML Manager Event Interface",
2566 .callback
= manager_http_callback
,
2569 struct ast_http_uri managerxmluri
= {
2570 .description
= "XML Manager Event Interface",
2573 .callback
= mxml_http_callback
,
2576 static int registered
= 0;
2577 static int webregged
= 0;
2579 int init_manager(void)
2581 struct ast_config
*cfg
= NULL
;
2584 int oldportno
= portno
;
2585 static struct sockaddr_in ba
;
2589 int newhttptimeout
= 60;
2590 struct ast_manager_user
*user
= NULL
;
2593 /* Register default actions */
2594 ast_manager_register2("Ping", 0, action_ping
, "Keepalive command", mandescr_ping
);
2595 ast_manager_register2("Events", 0, action_events
, "Control Event Flow", mandescr_events
);
2596 ast_manager_register2("Logoff", 0, action_logoff
, "Logoff Manager", mandescr_logoff
);
2597 ast_manager_register2("Hangup", EVENT_FLAG_CALL
, action_hangup
, "Hangup Channel", mandescr_hangup
);
2598 ast_manager_register("Status", EVENT_FLAG_CALL
, action_status
, "Lists channel status" );
2599 ast_manager_register2("Setvar", EVENT_FLAG_CALL
, action_setvar
, "Set Channel Variable", mandescr_setvar
);
2600 ast_manager_register2("Getvar", EVENT_FLAG_CALL
, action_getvar
, "Gets a Channel Variable", mandescr_getvar
);
2601 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG
, action_getconfig
, "Retrieve configuration", mandescr_getconfig
);
2602 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG
, action_updateconfig
, "Update basic configuration", mandescr_updateconfig
);
2603 ast_manager_register2("Redirect", EVENT_FLAG_CALL
, action_redirect
, "Redirect (transfer) a call", mandescr_redirect
);
2604 ast_manager_register2("Originate", EVENT_FLAG_CALL
, action_originate
, "Originate Call", mandescr_originate
);
2605 ast_manager_register2("Command", EVENT_FLAG_COMMAND
, action_command
, "Execute Asterisk CLI Command", mandescr_command
);
2606 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL
, action_extensionstate
, "Check Extension Status", mandescr_extensionstate
);
2607 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL
, action_timeout
, "Set Absolute Timeout", mandescr_timeout
);
2608 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL
, action_mailboxstatus
, "Check Mailbox", mandescr_mailboxstatus
);
2609 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL
, action_mailboxcount
, "Check Mailbox Message Count", mandescr_mailboxcount
);
2610 ast_manager_register2("ListCommands", 0, action_listcommands
, "List available manager commands", mandescr_listcommands
);
2611 ast_manager_register2("UserEvent", EVENT_FLAG_USER
, action_userevent
, "Send an arbitrary event", mandescr_userevent
);
2612 ast_manager_register2("WaitEvent", 0, action_waitevent
, "Wait for an event to occur", mandescr_waitevent
);
2614 ast_cli_register_multiple(cli_manager
, sizeof(cli_manager
) / sizeof(struct ast_cli_entry
));
2615 ast_extension_state_add(NULL
, NULL
, manager_state_cb
, NULL
);
2617 /* Append placeholder event so master_eventq never runs dry */
2618 append_event("Event: Placeholder\r\n\r\n", 0);
2620 portno
= DEFAULT_MANAGER_PORT
;
2621 displayconnects
= 1;
2622 cfg
= ast_config_load("manager.conf");
2624 ast_log(LOG_NOTICE
, "Unable to open management configuration manager.conf. Call management disabled.\n");
2627 val
= ast_variable_retrieve(cfg
, "general", "enabled");
2629 enabled
= ast_true(val
);
2631 val
= ast_variable_retrieve(cfg
, "general", "block-sockets");
2633 block_sockets
= ast_true(val
);
2635 val
= ast_variable_retrieve(cfg
, "general", "webenabled");
2637 webenabled
= ast_true(val
);
2639 if ((val
= ast_variable_retrieve(cfg
, "general", "port"))) {
2640 if (sscanf(val
, "%d", &portno
) != 1) {
2641 ast_log(LOG_WARNING
, "Invalid port number '%s'\n", val
);
2642 portno
= DEFAULT_MANAGER_PORT
;
2646 if ((val
= ast_variable_retrieve(cfg
, "general", "displayconnects")))
2647 displayconnects
= ast_true(val
);
2649 if ((val
= ast_variable_retrieve(cfg
, "general", "timestampevents")))
2650 timestampevents
= ast_true(val
);
2652 if ((val
= ast_variable_retrieve(cfg
, "general", "httptimeout")))
2653 newhttptimeout
= atoi(val
);
2655 memset(&ba
, 0, sizeof(ba
));
2656 ba
.sin_family
= AF_INET
;
2657 ba
.sin_port
= htons(portno
);
2659 if ((val
= ast_variable_retrieve(cfg
, "general", "bindaddr"))) {
2660 if (!inet_aton(val
, &ba
.sin_addr
)) {
2661 ast_log(LOG_WARNING
, "Invalid address '%s' specified, using 0.0.0.0\n", val
);
2662 memset(&ba
.sin_addr
, 0, sizeof(ba
.sin_addr
));
2667 if ((asock
> -1) && ((portno
!= oldportno
) || !enabled
)) {
2669 /* Can't be done yet */
2673 ast_log(LOG_WARNING
, "Unable to change management port / enabled\n");
2677 AST_LIST_LOCK(&users
);
2679 while ((cat
= ast_category_browse(cfg
, cat
))) {
2680 struct ast_variable
*var
= NULL
;
2682 if (!strcasecmp(cat
, "general"))
2685 /* Look for an existing entry, if none found - create one and add it to the list */
2686 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
2687 if (!(user
= ast_calloc(1, sizeof(*user
))))
2689 /* Copy name over */
2690 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
2691 /* Insert into list */
2692 AST_LIST_INSERT_TAIL(&users
, user
, list
);
2695 /* Make sure we keep this user and don't destroy it during cleanup */
2698 var
= ast_variable_browse(cfg
, cat
);
2700 if (!strcasecmp(var
->name
, "secret")) {
2703 user
->secret
= ast_strdup(var
->value
);
2704 } else if (!strcasecmp(var
->name
, "deny") ) {
2707 user
->deny
= ast_strdup(var
->value
);
2708 } else if (!strcasecmp(var
->name
, "permit") ) {
2711 user
->permit
= ast_strdup(var
->value
);
2712 } else if (!strcasecmp(var
->name
, "read") ) {
2715 user
->read
= ast_strdup(var
->value
);
2716 } else if (!strcasecmp(var
->name
, "write") ) {
2719 user
->write
= ast_strdup(var
->value
);
2720 } else if (!strcasecmp(var
->name
, "displayconnects") )
2721 user
->displayconnects
= ast_true(var
->value
);
2723 ast_log(LOG_DEBUG
, "%s is an unknown option.\n", var
->name
);
2728 /* Perform cleanup - essentially prune out old users that no longer exist */
2729 AST_LIST_TRAVERSE_SAFE_BEGIN(&users
, user
, list
) {
2734 /* We do not need to keep this user so take them out of the list */
2735 AST_LIST_REMOVE_CURRENT(&users
, list
);
2736 /* Free their memory now */
2749 AST_LIST_TRAVERSE_SAFE_END
2751 AST_LIST_UNLOCK(&users
);
2753 ast_config_destroy(cfg
);
2755 if (webenabled
&& enabled
) {
2757 ast_http_uri_link(&rawmanuri
);
2758 ast_http_uri_link(&manageruri
);
2759 ast_http_uri_link(&managerxmluri
);
2764 ast_http_uri_unlink(&rawmanuri
);
2765 ast_http_uri_unlink(&manageruri
);
2766 ast_http_uri_unlink(&managerxmluri
);
2771 if (newhttptimeout
> 0)
2772 httptimeout
= newhttptimeout
;
2774 /* If not enabled, do nothing */
2779 asock
= socket(AF_INET
, SOCK_STREAM
, 0);
2781 ast_log(LOG_WARNING
, "Unable to create socket: %s\n", strerror(errno
));
2784 setsockopt(asock
, SOL_SOCKET
, SO_REUSEADDR
, &x
, sizeof(x
));
2785 if (bind(asock
, (struct sockaddr
*)&ba
, sizeof(ba
))) {
2786 ast_log(LOG_WARNING
, "Unable to bind socket: %s\n", strerror(errno
));
2791 if (listen(asock
, 2)) {
2792 ast_log(LOG_WARNING
, "Unable to listen on socket: %s\n", strerror(errno
));
2797 flags
= fcntl(asock
, F_GETFL
);
2798 fcntl(asock
, F_SETFL
, flags
| O_NONBLOCK
);
2800 ast_verbose("Asterisk Management interface listening on port %d\n", portno
);
2801 ast_pthread_create_background(&t
, NULL
, accept_thread
, NULL
);
2806 int reload_manager(void)
2808 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Message: Reload Requested\r\n");
2809 return init_manager();