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_EXTENSION
];
74 char data
[AST_MAX_EXTENSION
];
76 char app
[AST_MAX_APP
];
77 char appdata
[AST_MAX_EXTENSION
];
78 char cid_name
[AST_MAX_EXTENSION
];
79 char cid_num
[AST_MAX_EXTENSION
];
80 char context
[AST_MAX_CONTEXT
];
81 char exten
[AST_MAX_EXTENSION
];
82 char idtext
[AST_MAX_EXTENSION
];
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 */
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 const char *astman_get_header(const 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(const 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
, const struct message
*m
, char *error
)
744 const 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
, const struct message
*m
, char *resp
, char *msg
)
754 const 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
, const 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 strings_to_mask(const char *string
)
829 x
= ast_is_number((char *) 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
, const char *eventmask
)
858 int maskint
= 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
, const struct message
*m
)
870 struct ast_config
*cfg
;
872 const char *user
= astman_get_header(m
, "Username");
873 const char *pass
= astman_get_header(m
, "Secret");
874 const char *authtype
= astman_get_header(m
, "AuthType");
875 const char *key
= astman_get_header(m
, "Key");
876 const 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
, const 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
, const struct message
*m
)
986 struct ast_config
*cfg
;
987 const char *fn
= astman_get_header(m
, "Filename");
991 struct ast_variable
*v
;
992 char idText
[256] = "";
993 const 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
, const struct message
*m
, struct ast_config
*cfg
)
1025 const 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
, (char *) 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
, (char *) var
, (char *) value
, (char *) 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
, (char *) var
, (char *) 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
, const struct message
*m
)
1091 struct ast_config
*cfg
;
1092 const char *sfn
= astman_get_header(m
, "SrcFilename");
1093 const char *dfn
= astman_get_header(m
, "DstFilename");
1095 char idText
[256] = "";
1096 const char *id
= astman_get_header(m
, "ActionID");
1097 const 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
, const struct message
*m
)
1136 const char *timeouts
= astman_get_header(m
, "Timeout");
1137 int timeout
= -1, max
;
1141 struct eventqent
*eqe
;
1142 const 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
, const struct message
*m
)
1223 struct manager_action
*cur
= first_action
;
1224 char idText
[256] = "";
1226 const 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
, const struct message
*m
)
1253 const 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
, const 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
, const struct message
*m
)
1282 struct ast_channel
*c
= NULL
;
1283 const 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
, const struct message
*m
)
1308 struct ast_channel
*c
= NULL
;
1309 const char *name
= astman_get_header(m
, "Channel");
1310 const char *varname
= astman_get_header(m
, "Variable");
1311 const 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
, const struct message
*m
)
1350 struct ast_channel
*c
= NULL
;
1351 const char *name
= astman_get_header(m
, "Channel");
1352 const char *varname
= astman_get_header(m
, "Variable");
1353 const 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
, (char *) 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
, const struct message
*m
)
1392 const char *id
= astman_get_header(m
,"ActionID");
1393 const 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
, const struct message
*m
)
1493 const char *name
= astman_get_header(m
, "Channel");
1494 const char *name2
= astman_get_header(m
, "ExtraChannel");
1495 const char *exten
= astman_get_header(m
, "Exten");
1496 const char *context
= astman_get_header(m
, "Context");
1497 const 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
, const struct message
*m
)
1553 const char *cmd
= astman_get_header(m
, "Command");
1554 const 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
, const struct message
*m
)
1630 const char *name
= astman_get_header(m
, "Channel");
1631 const char *exten
= astman_get_header(m
, "Exten");
1632 const char *context
= astman_get_header(m
, "Context");
1633 const char *priority
= astman_get_header(m
, "Priority");
1634 const char *timeout
= astman_get_header(m
, "Timeout");
1635 const char *callerid
= astman_get_header(m
, "CallerID");
1636 const char *account
= astman_get_header(m
, "Account");
1637 const char *app
= astman_get_header(m
, "Application");
1638 const char *appdata
= astman_get_header(m
, "Data");
1639 const char *async
= astman_get_header(m
, "Async");
1640 const 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
, const struct message
*m
)
1747 const char *mailbox
= astman_get_header(m
, "Mailbox");
1748 const 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
, const struct message
*m
)
1779 const char *mailbox
= astman_get_header(m
, "Mailbox");
1780 const 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
, const struct message
*m
)
1815 const char *exten
= astman_get_header(m
, "Exten");
1816 const char *context
= astman_get_header(m
, "Context");
1817 const 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
, const struct message
*m
)
1852 struct ast_channel
*c
= NULL
;
1853 const 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
, const struct message
*m
)
1906 const 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
, const struct message
*m
)
1924 char action
[80] = "";
1925 struct manager_action
*tmp
= first_action
;
1926 const 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")) {
1942 const char *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 int do_message(struct mansession
*s
)
2055 struct message m
= { 0 };
2056 char header_buf
[sizeof(s
->inbuf
)] = { '\0' };
2060 res
= get_input(s
, header_buf
);
2062 /* Strip trailing \r\n */
2063 if (strlen(header_buf
) < 2)
2065 header_buf
[strlen(header_buf
) - 2] = '\0';
2066 if (ast_strlen_zero(header_buf
))
2067 return process_message(s
, &m
) ? -1 : 0;
2068 else if (m
.hdrcount
< (AST_MAX_MANHEADERS
- 1))
2069 m
.headers
[m
.hdrcount
++] = ast_strdupa(header_buf
);
2076 static void *session_do(void *data
)
2078 struct mansession
*s
= data
;
2081 ast_mutex_lock(&s
->__lock
);
2082 astman_append(s
, "Asterisk Call Manager/1.0\r\n");
2083 ast_mutex_unlock(&s
->__lock
);
2085 res
= do_message(s
);
2089 } else if (res
< 0) {
2091 } else if (s
->eventq
->next
) {
2092 if (process_events(s
))
2096 if (s
->authenticated
) {
2097 if (option_verbose
> 1) {
2098 if (displayconnects
)
2099 ast_verbose(VERBOSE_PREFIX_2
"Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2101 ast_log(LOG_EVENT
, "Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2103 if (option_verbose
> 1) {
2104 if (displayconnects
)
2105 ast_verbose(VERBOSE_PREFIX_2
"Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2107 ast_log(LOG_EVENT
, "Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2113 static void *accept_thread(void *ignore
)
2116 struct sockaddr_in sin
;
2118 struct eventqent
*eqe
;
2119 struct mansession
*s
;
2123 pthread_attr_t attr
;
2125 struct pollfd pfds
[1];
2127 pthread_attr_init(&attr
);
2128 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
2132 AST_LIST_LOCK(&sessions
);
2133 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions
, s
, list
) {
2134 if (s
->sessiontimeout
&& (now
> s
->sessiontimeout
) && !s
->inuse
) {
2135 AST_LIST_REMOVE_CURRENT(&sessions
, list
);
2136 if (s
->authenticated
&& (option_verbose
> 1) && displayconnects
) {
2137 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' timed out from %s\n",
2138 s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2144 AST_LIST_TRAVERSE_SAFE_END
2145 /* Purge master event queue of old, unused events, but make sure we
2146 always keep at least one in the queue */
2147 eqe
= master_eventq
;
2148 while (master_eventq
->next
&& !master_eventq
->usecount
) {
2149 eqe
= master_eventq
;
2150 master_eventq
= master_eventq
->next
;
2153 AST_LIST_UNLOCK(&sessions
);
2155 ast_atomic_fetchadd_int(&num_sessions
, -1);
2157 sinlen
= sizeof(sin
);
2159 pfds
[0].events
= POLLIN
;
2160 /* Wait for something to happen, but timeout every few seconds so
2161 we can ditch any old manager sessions */
2162 if (poll(pfds
, 1, 5000) < 1)
2164 as
= accept(asock
, (struct sockaddr
*)&sin
, &sinlen
);
2166 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
2169 p
= getprotobyname("tcp");
2171 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
2172 ast_log(LOG_WARNING
, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
2175 if (!(s
= ast_calloc(1, sizeof(*s
))))
2178 ast_atomic_fetchadd_int(&num_sessions
, 1);
2180 memcpy(&s
->sin
, &sin
, sizeof(sin
));
2181 s
->writetimeout
= 100;
2182 s
->waiting_thread
= AST_PTHREADT_NULL
;
2184 if (!block_sockets
) {
2185 /* For safety, make sure socket is non-blocking */
2186 flags
= fcntl(as
, F_GETFL
);
2187 fcntl(as
, F_SETFL
, flags
| O_NONBLOCK
);
2189 flags
= fcntl(as
, F_GETFL
);
2190 fcntl(as
, F_SETFL
, flags
& ~O_NONBLOCK
);
2192 ast_mutex_init(&s
->__lock
);
2194 s
->send_events
= -1;
2195 AST_LIST_LOCK(&sessions
);
2196 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2197 /* Find the last place in the master event queue and hook ourselves
2199 s
->eventq
= master_eventq
;
2200 while(s
->eventq
->next
)
2201 s
->eventq
= s
->eventq
->next
;
2202 AST_LIST_UNLOCK(&sessions
);
2203 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2204 if (ast_pthread_create_background(&s
->t
, &attr
, session_do
, s
))
2207 pthread_attr_destroy(&attr
);
2211 static int append_event(const char *str
, int category
)
2213 struct eventqent
*tmp
, *prev
= NULL
;
2214 tmp
= ast_malloc(sizeof(*tmp
) + strlen(str
));
2220 tmp
->category
= category
;
2221 strcpy(tmp
->eventdata
, str
);
2223 if (master_eventq
) {
2224 prev
= master_eventq
;
2229 master_eventq
= tmp
;
2232 tmp
->usecount
= num_sessions
;
2237 /*! \brief manager_event: Send AMI event to client */
2238 int manager_event(int category
, const char *event
, const char *fmt
, ...)
2240 struct mansession
*s
;
2244 struct ast_dynamic_str
*buf
;
2246 /* Abort if there aren't any manager sessions */
2250 if (!(buf
= ast_dynamic_str_thread_get(&manager_event_buf
, MANAGER_EVENT_BUF_INITSIZE
)))
2253 ast_dynamic_str_thread_set(&buf
, 0, &manager_event_buf
,
2254 "Event: %s\r\nPrivilege: %s\r\n",
2255 event
, authority_to_str(category
, auth
, sizeof(auth
)));
2257 if (timestampevents
) {
2259 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
,
2260 "Timestamp: %ld.%06lu\r\n",
2261 now
.tv_sec
, (unsigned long) now
.tv_usec
);
2265 ast_dynamic_str_thread_append_va(&buf
, 0, &manager_event_buf
, fmt
, ap
);
2268 ast_dynamic_str_thread_append(&buf
, 0, &manager_event_buf
, "\r\n");
2270 /* Append event to master list and wake up any sleeping sessions */
2271 AST_LIST_LOCK(&sessions
);
2272 append_event(buf
->str
, category
);
2273 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2274 ast_mutex_lock(&s
->__lock
);
2275 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2276 pthread_kill(s
->waiting_thread
, SIGURG
);
2277 ast_mutex_unlock(&s
->__lock
);
2279 AST_LIST_UNLOCK(&sessions
);
2284 int ast_manager_unregister(char *action
)
2286 struct manager_action
*cur
= first_action
, *prev
= first_action
;
2288 ast_mutex_lock(&actionlock
);
2290 if (!strcasecmp(action
, cur
->action
)) {
2291 prev
->next
= cur
->next
;
2293 if (option_verbose
> 1)
2294 ast_verbose(VERBOSE_PREFIX_2
"Manager unregistered action %s\n", action
);
2295 ast_mutex_unlock(&actionlock
);
2301 ast_mutex_unlock(&actionlock
);
2305 static int manager_state_cb(char *context
, char *exten
, int state
, void *data
)
2307 /* Notify managers of change */
2308 manager_event(EVENT_FLAG_CALL
, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten
, context
, state
);
2312 static int ast_manager_register_struct(struct manager_action
*act
)
2314 struct manager_action
*cur
= first_action
, *prev
= NULL
;
2317 ast_mutex_lock(&actionlock
);
2318 while (cur
) { /* Walk the list of actions */
2319 ret
= strcasecmp(cur
->action
, act
->action
);
2321 ast_log(LOG_WARNING
, "Manager: Action '%s' already registered\n", act
->action
);
2322 ast_mutex_unlock(&actionlock
);
2324 } else if (ret
> 0) {
2325 /* Insert these alphabetically */
2327 act
->next
= prev
->next
;
2330 act
->next
= first_action
;
2347 if (option_verbose
> 1)
2348 ast_verbose(VERBOSE_PREFIX_2
"Manager registered action %s\n", act
->action
);
2349 ast_mutex_unlock(&actionlock
);
2353 /*! \brief register a new command with manager, including online help. This is
2354 the preferred way to register a manager command */
2355 int ast_manager_register2(const char *action
, int auth
, int (*func
)(struct mansession
*s
, const struct message
*m
), const char *synopsis
, const char *description
)
2357 struct manager_action
*cur
;
2359 cur
= ast_malloc(sizeof(*cur
));
2363 cur
->action
= action
;
2364 cur
->authority
= auth
;
2366 cur
->synopsis
= synopsis
;
2367 cur
->description
= description
;
2370 ast_manager_register_struct(cur
);
2375 END Doxygen group */
2377 static struct mansession
*find_session(unsigned long ident
)
2379 struct mansession
*s
;
2381 AST_LIST_LOCK(&sessions
);
2382 AST_LIST_TRAVERSE(&sessions
, s
, list
) {
2383 ast_mutex_lock(&s
->__lock
);
2384 if (s
->sessiontimeout
&& (s
->managerid
== ident
) && !s
->needdestroy
) {
2388 ast_mutex_unlock(&s
->__lock
);
2390 AST_LIST_UNLOCK(&sessions
);
2401 static char *contenttype
[] = { "plain", "html", "xml" };
2403 static char *generic_http_callback(int format
, struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2405 struct mansession
*s
= NULL
;
2406 unsigned long ident
= 0;
2407 char workspace
[512];
2409 size_t len
= sizeof(workspace
);
2411 char *c
= workspace
;
2412 char *retval
= NULL
;
2413 struct ast_variable
*v
;
2415 for (v
= params
; v
; v
= v
->next
) {
2416 if (!strcasecmp(v
->name
, "mansession_id")) {
2417 sscanf(v
->value
, "%lx", &ident
);
2422 if (!(s
= find_session(ident
))) {
2423 /* Create new session */
2424 if (!(s
= ast_calloc(1, sizeof(*s
)))) {
2426 goto generic_callback_out
;
2428 memcpy(&s
->sin
, requestor
, sizeof(s
->sin
));
2430 s
->waiting_thread
= AST_PTHREADT_NULL
;
2432 ast_mutex_init(&s
->__lock
);
2433 ast_mutex_lock(&s
->__lock
);
2435 s
->managerid
= rand() | (unsigned long)s
;
2436 AST_LIST_LOCK(&sessions
);
2437 AST_LIST_INSERT_HEAD(&sessions
, s
, list
);
2438 /* Hook into the last spot in the event queue */
2439 s
->eventq
= master_eventq
;
2440 while (s
->eventq
->next
)
2441 s
->eventq
= s
->eventq
->next
;
2442 AST_LIST_UNLOCK(&sessions
);
2443 ast_atomic_fetchadd_int(&s
->eventq
->usecount
, 1);
2444 ast_atomic_fetchadd_int(&num_sessions
, 1);
2447 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2448 time(&s
->sessiontimeout
);
2449 if (!s
->authenticated
&& (httptimeout
> 5))
2450 s
->sessiontimeout
+= 5;
2452 s
->sessiontimeout
+= httptimeout
;
2453 ast_mutex_unlock(&s
->__lock
);
2456 struct message m
= { 0 };
2461 for (x
= 0; params
&& (x
< AST_MAX_MANHEADERS
); x
++, params
= params
->next
) {
2462 hdrlen
= strlen(params
->name
) + strlen(params
->value
) + 3;
2463 m
.headers
[m
.hdrcount
] = alloca(hdrlen
);
2464 snprintf((char *) m
.headers
[m
.hdrcount
], hdrlen
, "%s: %s", params
->name
, params
->value
);
2468 if (process_message(s
, &m
)) {
2469 if (s
->authenticated
) {
2470 if (option_verbose
> 1) {
2471 if (displayconnects
)
2472 ast_verbose(VERBOSE_PREFIX_2
"HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2474 ast_log(LOG_EVENT
, "HTTP Manager '%s' logged off from %s\n", s
->username
, ast_inet_ntoa(s
->sin
.sin_addr
));
2476 if (option_verbose
> 1) {
2477 if (displayconnects
)
2478 ast_verbose(VERBOSE_PREFIX_2
"HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2480 ast_log(LOG_EVENT
, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
2484 ast_build_string(&c
, &len
, "Content-type: text/%s\r\n", contenttype
[format
]);
2485 sprintf(tmp
, "%08lx", s
->managerid
);
2486 ast_build_string(&c
, &len
, "%s\r\n", ast_http_setcookie("mansession_id", tmp
, httptimeout
, cookie
, sizeof(cookie
)));
2487 if (format
== FORMAT_HTML
)
2488 ast_build_string(&c
, &len
, "<title>Asterisk™ Manager Interface</title>");
2489 if (format
== FORMAT_XML
) {
2490 ast_build_string(&c
, &len
, "<ajax-response>\n");
2491 } else if (format
== FORMAT_HTML
) {
2492 ast_build_string(&c
, &len
, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2493 ast_build_string(&c
, &len
, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1> Manager Tester</h1></td></tr>\r\n");
2497 if (format
== FORMAT_XML
)
2498 tmp
= xml_translate(s
->outputstr
->str
, params
);
2499 else if (format
== FORMAT_HTML
)
2500 tmp
= html_translate(s
->outputstr
->str
);
2502 tmp
= s
->outputstr
->str
;
2504 retval
= malloc(strlen(workspace
) + strlen(tmp
) + 128);
2506 strcpy(retval
, workspace
);
2507 strcpy(retval
+ strlen(retval
), tmp
);
2508 c
= retval
+ strlen(retval
);
2512 if (tmp
!= s
->outputstr
->str
)
2515 s
->outputstr
= NULL
;
2517 /* Still okay because c would safely be pointing to workspace even
2518 if retval failed to allocate above */
2519 if (format
== FORMAT_XML
) {
2520 ast_build_string(&c
, &len
, "</ajax-response>\n");
2521 } else if (format
== FORMAT_HTML
)
2522 ast_build_string(&c
, &len
, "</table></body>\r\n");
2525 *title
= strdup("Server Error");
2527 ast_mutex_lock(&s
->__lock
);
2528 if (s
->needdestroy
) {
2529 if (s
->inuse
== 1) {
2530 ast_log(LOG_DEBUG
, "Need destroy, doing it now!\n");
2533 ast_log(LOG_DEBUG
, "Need destroy, but can't do it yet!\n");
2534 if (s
->waiting_thread
!= AST_PTHREADT_NULL
)
2535 pthread_kill(s
->waiting_thread
, SIGURG
);
2540 ast_mutex_unlock(&s
->__lock
);
2544 generic_callback_out
:
2546 return ast_http_error(500, "Server Error", NULL
, "Internal Server Error (out of memory)\n");
2550 static char *manager_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_HTML
, requestor
, uri
, params
, status
, title
, contentlength
);
2555 static char *mxml_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2557 return generic_http_callback(FORMAT_XML
, requestor
, uri
, params
, status
, title
, contentlength
);
2560 static char *rawman_http_callback(struct sockaddr_in
*requestor
, const char *uri
, struct ast_variable
*params
, int *status
, char **title
, int *contentlength
)
2562 return generic_http_callback(FORMAT_RAW
, requestor
, uri
, params
, status
, title
, contentlength
);
2565 struct ast_http_uri rawmanuri
= {
2566 .description
= "Raw HTTP Manager Event Interface",
2569 .callback
= rawman_http_callback
,
2572 struct ast_http_uri manageruri
= {
2573 .description
= "HTML Manager Event Interface",
2576 .callback
= manager_http_callback
,
2579 struct ast_http_uri managerxmluri
= {
2580 .description
= "XML Manager Event Interface",
2583 .callback
= mxml_http_callback
,
2586 static int registered
= 0;
2587 static int webregged
= 0;
2589 int init_manager(void)
2591 struct ast_config
*cfg
= NULL
;
2594 int oldportno
= portno
;
2595 static struct sockaddr_in ba
;
2599 int newhttptimeout
= 60;
2600 struct ast_manager_user
*user
= NULL
;
2603 /* Register default actions */
2604 ast_manager_register2("Ping", 0, action_ping
, "Keepalive command", mandescr_ping
);
2605 ast_manager_register2("Events", 0, action_events
, "Control Event Flow", mandescr_events
);
2606 ast_manager_register2("Logoff", 0, action_logoff
, "Logoff Manager", mandescr_logoff
);
2607 ast_manager_register2("Hangup", EVENT_FLAG_CALL
, action_hangup
, "Hangup Channel", mandescr_hangup
);
2608 ast_manager_register("Status", EVENT_FLAG_CALL
, action_status
, "Lists channel status" );
2609 ast_manager_register2("Setvar", EVENT_FLAG_CALL
, action_setvar
, "Set Channel Variable", mandescr_setvar
);
2610 ast_manager_register2("Getvar", EVENT_FLAG_CALL
, action_getvar
, "Gets a Channel Variable", mandescr_getvar
);
2611 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG
, action_getconfig
, "Retrieve configuration", mandescr_getconfig
);
2612 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG
, action_updateconfig
, "Update basic configuration", mandescr_updateconfig
);
2613 ast_manager_register2("Redirect", EVENT_FLAG_CALL
, action_redirect
, "Redirect (transfer) a call", mandescr_redirect
);
2614 ast_manager_register2("Originate", EVENT_FLAG_CALL
, action_originate
, "Originate Call", mandescr_originate
);
2615 ast_manager_register2("Command", EVENT_FLAG_COMMAND
, action_command
, "Execute Asterisk CLI Command", mandescr_command
);
2616 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL
, action_extensionstate
, "Check Extension Status", mandescr_extensionstate
);
2617 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL
, action_timeout
, "Set Absolute Timeout", mandescr_timeout
);
2618 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL
, action_mailboxstatus
, "Check Mailbox", mandescr_mailboxstatus
);
2619 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL
, action_mailboxcount
, "Check Mailbox Message Count", mandescr_mailboxcount
);
2620 ast_manager_register2("ListCommands", 0, action_listcommands
, "List available manager commands", mandescr_listcommands
);
2621 ast_manager_register2("UserEvent", EVENT_FLAG_USER
, action_userevent
, "Send an arbitrary event", mandescr_userevent
);
2622 ast_manager_register2("WaitEvent", 0, action_waitevent
, "Wait for an event to occur", mandescr_waitevent
);
2624 ast_cli_register_multiple(cli_manager
, sizeof(cli_manager
) / sizeof(struct ast_cli_entry
));
2625 ast_extension_state_add(NULL
, NULL
, manager_state_cb
, NULL
);
2627 /* Append placeholder event so master_eventq never runs dry */
2628 append_event("Event: Placeholder\r\n\r\n", 0);
2630 portno
= DEFAULT_MANAGER_PORT
;
2631 displayconnects
= 1;
2632 cfg
= ast_config_load("manager.conf");
2634 ast_log(LOG_NOTICE
, "Unable to open management configuration manager.conf. Call management disabled.\n");
2637 val
= ast_variable_retrieve(cfg
, "general", "enabled");
2639 enabled
= ast_true(val
);
2641 val
= ast_variable_retrieve(cfg
, "general", "block-sockets");
2643 block_sockets
= ast_true(val
);
2645 val
= ast_variable_retrieve(cfg
, "general", "webenabled");
2647 webenabled
= ast_true(val
);
2649 if ((val
= ast_variable_retrieve(cfg
, "general", "port"))) {
2650 if (sscanf(val
, "%d", &portno
) != 1) {
2651 ast_log(LOG_WARNING
, "Invalid port number '%s'\n", val
);
2652 portno
= DEFAULT_MANAGER_PORT
;
2656 if ((val
= ast_variable_retrieve(cfg
, "general", "displayconnects")))
2657 displayconnects
= ast_true(val
);
2659 if ((val
= ast_variable_retrieve(cfg
, "general", "timestampevents")))
2660 timestampevents
= ast_true(val
);
2662 if ((val
= ast_variable_retrieve(cfg
, "general", "httptimeout")))
2663 newhttptimeout
= atoi(val
);
2665 memset(&ba
, 0, sizeof(ba
));
2666 ba
.sin_family
= AF_INET
;
2667 ba
.sin_port
= htons(portno
);
2669 if ((val
= ast_variable_retrieve(cfg
, "general", "bindaddr"))) {
2670 if (!inet_aton(val
, &ba
.sin_addr
)) {
2671 ast_log(LOG_WARNING
, "Invalid address '%s' specified, using 0.0.0.0\n", val
);
2672 memset(&ba
.sin_addr
, 0, sizeof(ba
.sin_addr
));
2677 if ((asock
> -1) && ((portno
!= oldportno
) || !enabled
)) {
2679 /* Can't be done yet */
2683 ast_log(LOG_WARNING
, "Unable to change management port / enabled\n");
2687 AST_LIST_LOCK(&users
);
2689 while ((cat
= ast_category_browse(cfg
, cat
))) {
2690 struct ast_variable
*var
= NULL
;
2692 if (!strcasecmp(cat
, "general"))
2695 /* Look for an existing entry, if none found - create one and add it to the list */
2696 if (!(user
= ast_get_manager_by_name_locked(cat
))) {
2697 if (!(user
= ast_calloc(1, sizeof(*user
))))
2699 /* Copy name over */
2700 ast_copy_string(user
->username
, cat
, sizeof(user
->username
));
2701 /* Insert into list */
2702 AST_LIST_INSERT_TAIL(&users
, user
, list
);
2705 /* Make sure we keep this user and don't destroy it during cleanup */
2708 var
= ast_variable_browse(cfg
, cat
);
2710 if (!strcasecmp(var
->name
, "secret")) {
2713 user
->secret
= ast_strdup(var
->value
);
2714 } else if (!strcasecmp(var
->name
, "deny") ) {
2717 user
->deny
= ast_strdup(var
->value
);
2718 } else if (!strcasecmp(var
->name
, "permit") ) {
2721 user
->permit
= ast_strdup(var
->value
);
2722 } else if (!strcasecmp(var
->name
, "read") ) {
2725 user
->read
= ast_strdup(var
->value
);
2726 } else if (!strcasecmp(var
->name
, "write") ) {
2729 user
->write
= ast_strdup(var
->value
);
2730 } else if (!strcasecmp(var
->name
, "displayconnects") )
2731 user
->displayconnects
= ast_true(var
->value
);
2733 ast_log(LOG_DEBUG
, "%s is an unknown option.\n", var
->name
);
2738 /* Perform cleanup - essentially prune out old users that no longer exist */
2739 AST_LIST_TRAVERSE_SAFE_BEGIN(&users
, user
, list
) {
2744 /* We do not need to keep this user so take them out of the list */
2745 AST_LIST_REMOVE_CURRENT(&users
, list
);
2746 /* Free their memory now */
2759 AST_LIST_TRAVERSE_SAFE_END
2761 AST_LIST_UNLOCK(&users
);
2763 ast_config_destroy(cfg
);
2765 if (webenabled
&& enabled
) {
2767 ast_http_uri_link(&rawmanuri
);
2768 ast_http_uri_link(&manageruri
);
2769 ast_http_uri_link(&managerxmluri
);
2774 ast_http_uri_unlink(&rawmanuri
);
2775 ast_http_uri_unlink(&manageruri
);
2776 ast_http_uri_unlink(&managerxmluri
);
2781 if (newhttptimeout
> 0)
2782 httptimeout
= newhttptimeout
;
2784 /* If not enabled, do nothing */
2789 asock
= socket(AF_INET
, SOCK_STREAM
, 0);
2791 ast_log(LOG_WARNING
, "Unable to create socket: %s\n", strerror(errno
));
2794 setsockopt(asock
, SOL_SOCKET
, SO_REUSEADDR
, &x
, sizeof(x
));
2795 if (bind(asock
, (struct sockaddr
*)&ba
, sizeof(ba
))) {
2796 ast_log(LOG_WARNING
, "Unable to bind socket: %s\n", strerror(errno
));
2801 if (listen(asock
, 2)) {
2802 ast_log(LOG_WARNING
, "Unable to listen on socket: %s\n", strerror(errno
));
2807 flags
= fcntl(asock
, F_GETFL
);
2808 fcntl(asock
, F_SETFL
, flags
| O_NONBLOCK
);
2810 ast_verbose("Asterisk Management interface listening on port %d\n", portno
);
2811 ast_pthread_create_background(&t
, NULL
, accept_thread
, NULL
);
2816 int reload_manager(void)
2818 manager_event(EVENT_FLAG_SYSTEM
, "Reload", "Message: Reload Requested\r\n");
2819 return init_manager();