when a PRI call must be moved to a different B channel at the request of the other...
[asterisk-bristuff.git] / main / manager.c
blobb78ecf5e3539b1a2ab620a72175a67f21f4b4def
1 /*
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.
19 /*! \file
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * Channel Management and more
27 * \ref amiconf
30 /*! \addtogroup Group_AMI AMI functions
32 /*! @{
33 Doxygen group */
35 #include "asterisk.h"
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include <netdb.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50 #include <signal.h>
51 #include <errno.h>
52 #include <unistd.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"
71 #include "asterisk/term.h"
72 #include "asterisk/astobj2.h"
74 struct fast_originate_helper {
75 char tech[AST_MAX_EXTENSION];
76 char data[AST_MAX_EXTENSION];
77 int timeout;
78 char app[AST_MAX_APP];
79 char appdata[AST_MAX_EXTENSION];
80 char cid_name[AST_MAX_EXTENSION];
81 char cid_num[AST_MAX_EXTENSION];
82 char context[AST_MAX_CONTEXT];
83 char exten[AST_MAX_EXTENSION];
84 char idtext[AST_MAX_EXTENSION];
85 char account[AST_MAX_ACCOUNT_CODE];
86 int priority;
87 struct ast_variable *vars;
90 struct eventqent {
91 int usecount;
92 int category;
93 struct eventqent *next;
94 char eventdata[1];
97 static int enabled;
98 static int portno = DEFAULT_MANAGER_PORT;
99 static int asock = -1;
100 static int displayconnects = 1;
101 static int timestampevents;
102 static int httptimeout = 60;
104 static pthread_t t;
105 static int block_sockets;
106 static int num_sessions;
108 /* Protected by the sessions list lock */
109 struct eventqent *master_eventq = NULL;
111 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
112 #define MANAGER_EVENT_BUF_INITSIZE 256
114 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
115 #define ASTMAN_APPEND_BUF_INITSIZE 256
117 static struct permalias {
118 int num;
119 char *label;
120 } perms[] = {
121 { EVENT_FLAG_SYSTEM, "system" },
122 { EVENT_FLAG_CALL, "call" },
123 { EVENT_FLAG_LOG, "log" },
124 { EVENT_FLAG_VERBOSE, "verbose" },
125 { EVENT_FLAG_COMMAND, "command" },
126 { EVENT_FLAG_AGENT, "agent" },
127 { EVENT_FLAG_USER, "user" },
128 { EVENT_FLAG_CONFIG, "config" },
129 { -1, "all" },
130 { 0, "none" },
133 static const char *command_blacklist[] = {
134 "module load",
135 "module unload",
138 struct mansession {
139 /*! Execution thread */
140 pthread_t t;
141 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
142 ast_mutex_t __lock;
143 /*! socket address */
144 struct sockaddr_in sin;
145 /*! TCP socket */
146 int fd;
147 /*! Whether an HTTP manager is in use */
148 int inuse;
149 /*! Whether an HTTP session should be destroyed */
150 int needdestroy;
151 /*! Whether an HTTP session has someone waiting on events */
152 pthread_t waiting_thread;
153 /*! Unique manager identifer */
154 unsigned long managerid;
155 /*! Session timeout if HTTP */
156 time_t sessiontimeout;
157 /*! Output from manager interface */
158 struct ast_dynamic_str *outputstr;
159 /*! Logged in username */
160 char username[80];
161 /*! Authentication challenge */
162 char challenge[10];
163 /*! Authentication status */
164 int authenticated;
165 /*! Authorization for reading */
166 int readperm;
167 /*! Authorization for writing */
168 int writeperm;
169 /*! Buffer */
170 char inbuf[1024];
171 int inlen;
172 int send_events;
173 int displaysystemname; /*!< Add system name to manager responses and events */
174 /* Queued events that we've not had the ability to send yet */
175 struct eventqent *eventq;
176 /* Timeout for ast_carefulwrite() */
177 int writetimeout;
178 AST_LIST_ENTRY(mansession) list;
181 static AST_LIST_HEAD_STATIC(sessions, mansession);
183 struct ast_manager_user {
184 char username[80];
185 char *secret;
186 char *deny;
187 char *permit;
188 char *read;
189 char *write;
190 unsigned int displayconnects:1;
191 int keep;
192 AST_LIST_ENTRY(ast_manager_user) list;
195 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
197 static struct manager_action *first_action;
198 AST_RWLOCK_DEFINE_STATIC(actionlock);
200 /*! \brief Convert authority code to string with serveral options */
201 static char *authority_to_str(int authority, char *res, int reslen)
203 int running_total = 0, i;
205 memset(res, 0, reslen);
206 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
207 if (authority & perms[i].num) {
208 if (*res) {
209 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
210 running_total++;
212 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
213 running_total += strlen(perms[i].label);
217 if (ast_strlen_zero(res))
218 ast_copy_string(res, "<none>", reslen);
220 return res;
223 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
225 struct manager_action *cur;
226 int which = 0;
227 char *ret = NULL;
229 ast_rwlock_rdlock(&actionlock);
230 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
231 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
232 ret = ast_strdup(cur->action);
233 break; /* make sure we exit even if ast_strdup() returns NULL */
236 ast_rwlock_unlock(&actionlock);
238 return ret;
241 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
243 while (*src && (*maxlen > 6)) {
244 switch (*src) {
245 case '<':
246 strcpy(*dst, "&lt;");
247 (*dst) += 4;
248 *maxlen -= 4;
249 break;
250 case '>':
251 strcpy(*dst, "&gt;");
252 (*dst) += 4;
253 *maxlen -= 4;
254 break;
255 case '\"':
256 strcpy(*dst, "&quot;");
257 (*dst) += 6;
258 *maxlen -= 6;
259 break;
260 case '\'':
261 strcpy(*dst, "&apos;");
262 (*dst) += 6;
263 *maxlen -= 6;
264 break;
265 case '&':
266 strcpy(*dst, "&amp;");
267 (*dst) += 5;
268 *maxlen -= 5;
269 break;
270 default:
271 *(*dst)++ = lower ? tolower(*src) : *src;
272 (*maxlen)--;
274 src++;
278 struct variable_count {
279 char *varname;
280 int count;
283 static int compress_char(char c)
285 c &= 0x7f;
286 if (c < 32)
287 return 0;
288 else if (c >= 'a' && c <= 'z')
289 return c - 64;
290 else if (c > 'z')
291 return '_';
292 else
293 return c - 32;
296 static int variable_count_hash_fn(const void *vvc, const int flags)
298 const struct variable_count *vc = vvc;
299 int res = 0, i;
300 for (i = 0; i < 5; i++) {
301 if (vc->varname[i] == '\0')
302 break;
303 res += compress_char(vc->varname[i]) << (i * 6);
305 return res;
308 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
310 /* Due to the simplicity of struct variable_count, it makes no difference
311 * if you pass in objects or strings, the same operation applies. This is
312 * due to the fact that the hash occurs on the first element, which means
313 * the address of both the struct and the string are exactly the same. */
314 struct variable_count *vc = obj;
315 char *str = vstr;
316 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
319 static char *xml_translate(char *in, struct ast_variable *vars)
321 struct ast_variable *v;
322 char *dest = NULL;
323 char *out, *tmp, *var, *val;
324 char *objtype = NULL;
325 int colons = 0;
326 int breaks = 0;
327 size_t len;
328 int count = 1;
329 int escaped = 0;
330 int inobj = 0;
331 int x;
332 struct variable_count *vc = NULL;
333 struct ao2_container *vco = NULL;
335 for (v = vars; v; v = v->next) {
336 if (!dest && !strcasecmp(v->name, "ajaxdest"))
337 dest = v->value;
338 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
339 objtype = v->value;
341 if (!dest)
342 dest = "unknown";
343 if (!objtype)
344 objtype = "generic";
345 for (x = 0; in[x]; x++) {
346 if (in[x] == ':')
347 colons++;
348 else if (in[x] == '\n')
349 breaks++;
350 else if (strchr("&\"<>\'", in[x]))
351 escaped++;
353 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
354 out = ast_malloc(len);
355 if (!out)
356 return 0;
357 tmp = out;
358 while (*in) {
359 var = in;
360 while (*in && (*in >= 32))
361 in++;
362 if (*in) {
363 if ((count > 3) && inobj) {
364 ast_build_string(&tmp, &len, " /></response>\n");
365 inobj = 0;
367 /* Entity is closed, so close out the name cache */
368 ao2_ref(vco, -1);
369 vco = NULL;
371 count = 0;
372 while (*in && (*in < 32)) {
373 *in = '\0';
374 in++;
375 count++;
377 val = strchr(var, ':');
378 if (val) {
379 *val = '\0';
380 val++;
381 if (*val == ' ')
382 val++;
383 if (!inobj) {
384 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
385 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
386 inobj = 1;
389 /* Check if the var has been used already */
390 if ((vc = ao2_find(vco, var, 0)))
391 vc->count++;
392 else {
393 /* Create a new entry for this one */
394 vc = ao2_alloc(sizeof(*vc), NULL);
395 vc->varname = var;
396 vc->count = 1;
397 ao2_link(vco, vc);
400 ast_build_string(&tmp, &len, " ");
401 xml_copy_escape(&tmp, &len, var, 1);
402 if (vc->count > 1)
403 ast_build_string(&tmp, &len, "-%d", vc->count);
404 ast_build_string(&tmp, &len, "='");
405 xml_copy_escape(&tmp, &len, val, 0);
406 ast_build_string(&tmp, &len, "'");
407 ao2_ref(vc, -1);
411 if (inobj)
412 ast_build_string(&tmp, &len, " /></response>\n");
413 if (vco)
414 ao2_ref(vco, -1);
415 return out;
418 static char *html_translate(char *in)
420 int x;
421 int colons = 0;
422 int breaks = 0;
423 size_t len;
424 int count = 1;
425 char *tmp, *var, *val, *out;
427 for (x=0; in[x]; x++) {
428 if (in[x] == ':')
429 colons++;
430 if (in[x] == '\n')
431 breaks++;
433 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
434 out = ast_malloc(len);
435 if (!out)
436 return 0;
437 tmp = out;
438 while (*in) {
439 var = in;
440 while (*in && (*in >= 32))
441 in++;
442 if (*in) {
443 if ((count % 4) == 0){
444 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
446 count = 0;
447 while (*in && (*in < 32)) {
448 *in = '\0';
449 in++;
450 count++;
452 val = strchr(var, ':');
453 if (val) {
454 *val = '\0';
455 val++;
456 if (*val == ' ')
457 val++;
458 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
462 return out;
467 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
469 struct ast_manager_user *user = NULL;
471 AST_LIST_TRAVERSE(&users, user, list)
472 if (!strcasecmp(user->username, name))
473 break;
474 return user;
477 void astman_append(struct mansession *s, const char *fmt, ...)
479 va_list ap;
480 struct ast_dynamic_str *buf;
482 ast_mutex_lock(&s->__lock);
484 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
485 ast_mutex_unlock(&s->__lock);
486 return;
489 va_start(ap, fmt);
490 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
491 va_end(ap);
493 if (s->fd > -1)
494 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
495 else {
496 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) {
497 ast_mutex_unlock(&s->__lock);
498 return;
501 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
504 ast_mutex_unlock(&s->__lock);
507 static int handle_showmancmd(int fd, int argc, char *argv[])
509 struct manager_action *cur;
510 char authority[80];
511 int num;
513 if (argc != 4)
514 return RESULT_SHOWUSAGE;
516 ast_rwlock_rdlock(&actionlock);
517 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
518 for (num = 3; num < argc; num++) {
519 if (!strcasecmp(cur->action, argv[num])) {
520 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 : "");
524 ast_rwlock_unlock(&actionlock);
526 return RESULT_SUCCESS;
529 static int handle_showmanager(int fd, int argc, char *argv[])
531 struct ast_manager_user *user = NULL;
533 if (argc != 4)
534 return RESULT_SHOWUSAGE;
536 AST_LIST_LOCK(&users);
538 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
539 ast_cli(fd, "There is no manager called %s\n", argv[3]);
540 AST_LIST_UNLOCK(&users);
541 return -1;
544 ast_cli(fd,"\n");
545 ast_cli(fd,
546 " username: %s\n"
547 " secret: %s\n"
548 " deny: %s\n"
549 " permit: %s\n"
550 " read: %s\n"
551 " write: %s\n"
552 "displayconnects: %s\n",
553 (user->username ? user->username : "(N/A)"),
554 (user->secret ? "<Set>" : "(N/A)"),
555 (user->deny ? user->deny : "(N/A)"),
556 (user->permit ? user->permit : "(N/A)"),
557 (user->read ? user->read : "(N/A)"),
558 (user->write ? user->write : "(N/A)"),
559 (user->displayconnects ? "yes" : "no"));
561 AST_LIST_UNLOCK(&users);
563 return RESULT_SUCCESS;
567 static int handle_showmanagers(int fd, int argc, char *argv[])
569 struct ast_manager_user *user = NULL;
570 int count_amu = 0;
572 if (argc != 3)
573 return RESULT_SHOWUSAGE;
575 AST_LIST_LOCK(&users);
577 /* If there are no users, print out something along those lines */
578 if (AST_LIST_EMPTY(&users)) {
579 ast_cli(fd, "There are no manager users.\n");
580 AST_LIST_UNLOCK(&users);
581 return RESULT_SUCCESS;
584 ast_cli(fd, "\nusername\n--------\n");
586 AST_LIST_TRAVERSE(&users, user, list) {
587 ast_cli(fd, "%s\n", user->username);
588 count_amu++;
591 AST_LIST_UNLOCK(&users);
593 ast_cli(fd,"-------------------\n");
594 ast_cli(fd,"%d manager users configured.\n", count_amu);
596 return RESULT_SUCCESS;
600 /*! \brief CLI command
601 Should change to "manager show commands" */
602 static int handle_showmancmds(int fd, int argc, char *argv[])
604 struct manager_action *cur;
605 char authority[80];
606 char *format = " %-15.15s %-15.15s %-55.55s\n";
608 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
609 ast_cli(fd, format, "------", "---------", "--------");
611 ast_rwlock_rdlock(&actionlock);
612 for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
613 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
614 ast_rwlock_unlock(&actionlock);
616 return RESULT_SUCCESS;
619 /*! \brief CLI command show manager connected */
620 /* Should change to "manager show connected" */
621 static int handle_showmanconn(int fd, int argc, char *argv[])
623 struct mansession *s;
624 char *format = " %-15.15s %-15.15s\n";
626 ast_cli(fd, format, "Username", "IP Address");
628 AST_LIST_LOCK(&sessions);
629 AST_LIST_TRAVERSE(&sessions, s, list)
630 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
631 AST_LIST_UNLOCK(&sessions);
633 return RESULT_SUCCESS;
636 /*! \brief CLI command show manager connected */
637 /* Should change to "manager show connected" */
638 static int handle_showmaneventq(int fd, int argc, char *argv[])
640 struct eventqent *s;
642 AST_LIST_LOCK(&sessions);
643 for (s = master_eventq; s; s = s->next) {
644 ast_cli(fd, "Usecount: %d\n",s->usecount);
645 ast_cli(fd, "Category: %d\n", s->category);
646 ast_cli(fd, "Event:\n%s", s->eventdata);
648 AST_LIST_UNLOCK(&sessions);
650 return RESULT_SUCCESS;
653 static char showmancmd_help[] =
654 "Usage: manager show command <actionname>\n"
655 " Shows the detailed description for a specific Asterisk manager interface command.\n";
657 static char showmancmds_help[] =
658 "Usage: manager show commands\n"
659 " Prints a listing of all the available Asterisk manager interface commands.\n";
661 static char showmanconn_help[] =
662 "Usage: manager show connected\n"
663 " Prints a listing of the users that are currently connected to the\n"
664 "Asterisk manager interface.\n";
666 static char showmaneventq_help[] =
667 "Usage: manager show eventq\n"
668 " Prints a listing of all events pending in the Asterisk manger\n"
669 "event queue.\n";
671 static char showmanagers_help[] =
672 "Usage: manager show users\n"
673 " Prints a listing of all managers that are currently configured on that\n"
674 " system.\n";
676 static char showmanager_help[] =
677 " Usage: manager show user <user>\n"
678 " Display all information related to the manager user specified.\n";
680 static struct ast_cli_entry cli_show_manager_command_deprecated = {
681 { "show", "manager", "command", NULL },
682 handle_showmancmd, NULL,
683 NULL, complete_show_mancmd };
685 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
686 { "show", "manager", "commands", NULL },
687 handle_showmancmds, NULL,
688 NULL };
690 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
691 { "show", "manager", "connected", NULL },
692 handle_showmanconn, NULL,
693 NULL };
695 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
696 { "show", "manager", "eventq", NULL },
697 handle_showmaneventq, NULL,
698 NULL };
700 static struct ast_cli_entry cli_manager[] = {
701 { { "manager", "show", "command", NULL },
702 handle_showmancmd, "Show a manager interface command",
703 showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
705 { { "manager", "show", "commands", NULL },
706 handle_showmancmds, "List manager interface commands",
707 showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
709 { { "manager", "show", "connected", NULL },
710 handle_showmanconn, "List connected manager interface users",
711 showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
713 { { "manager", "show", "eventq", NULL },
714 handle_showmaneventq, "List manager interface queued events",
715 showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
717 { { "manager", "show", "users", NULL },
718 handle_showmanagers, "List configured manager users",
719 showmanagers_help, NULL, NULL },
721 { { "manager", "show", "user", NULL },
722 handle_showmanager, "Display information on a specific manager user",
723 showmanager_help, NULL, NULL },
726 static void unuse_eventqent(struct eventqent *e)
728 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
729 pthread_kill(t, SIGURG);
732 static void free_session(struct mansession *s)
734 struct eventqent *eqe;
735 if (s->fd > -1)
736 close(s->fd);
737 if (s->outputstr)
738 free(s->outputstr);
739 ast_mutex_destroy(&s->__lock);
740 while (s->eventq) {
741 eqe = s->eventq;
742 s->eventq = s->eventq->next;
743 unuse_eventqent(eqe);
745 free(s);
748 static void destroy_session(struct mansession *s)
750 AST_LIST_LOCK(&sessions);
751 AST_LIST_REMOVE(&sessions, s, list);
752 num_sessions--;
753 free_session(s);
754 AST_LIST_UNLOCK(&sessions);
757 const char *astman_get_header(const struct message *m, char *var)
759 char cmp[80];
760 int x;
762 snprintf(cmp, sizeof(cmp), "%s: ", var);
764 for (x = 0; x < m->hdrcount; x++) {
765 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
766 return m->headers[x] + strlen(cmp);
769 return "";
772 struct ast_variable *astman_get_variables(const struct message *m)
774 int varlen, x, y;
775 struct ast_variable *head = NULL, *cur;
776 char *var, *val;
778 char *parse;
779 AST_DECLARE_APP_ARGS(args,
780 AST_APP_ARG(vars)[32];
783 varlen = strlen("Variable: ");
785 for (x = 0; x < m->hdrcount; x++) {
786 if (strncasecmp("Variable: ", m->headers[x], varlen))
787 continue;
789 parse = ast_strdupa(m->headers[x] + varlen);
791 AST_STANDARD_APP_ARGS(args, parse);
792 if (args.argc) {
793 for (y = 0; y < args.argc; y++) {
794 if (!args.vars[y])
795 continue;
796 var = val = ast_strdupa(args.vars[y]);
797 strsep(&val, "=");
798 if (!val || ast_strlen_zero(var))
799 continue;
800 cur = ast_variable_new(var, val);
801 if (head) {
802 cur->next = head;
803 head = cur;
804 } else
805 head = cur;
810 return head;
813 /*! \note NOTE:
814 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
815 hold the session lock _or_ be running in an action callback (in which case s->busy will
816 be non-zero). In either of these cases, there is no need to lock-protect the session's
817 fd, since no other output will be sent (events will be queued), and no input will
818 be read until either the current action finishes or get_input() obtains the session
819 lock.
821 void astman_send_error(struct mansession *s, const struct message *m, char *error)
823 const char *id = astman_get_header(m,"ActionID");
825 astman_append(s, "Response: Error\r\n");
826 if (!ast_strlen_zero(id))
827 astman_append(s, "ActionID: %s\r\n", id);
828 astman_append(s, "Message: %s\r\n\r\n", error);
831 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
833 const char *id = astman_get_header(m,"ActionID");
835 astman_append(s, "Response: %s\r\n", resp);
836 if (!ast_strlen_zero(id))
837 astman_append(s, "ActionID: %s\r\n", id);
838 if (msg)
839 astman_append(s, "Message: %s\r\n\r\n", msg);
840 else
841 astman_append(s, "\r\n");
844 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
846 astman_send_response(s, m, "Success", msg);
849 /*! Tells you if smallstr exists inside bigstr
850 which is delim by delim and uses no buf or stringsep
851 ast_instring("this|that|more","this",',') == 1;
853 feel free to move this to app.c -anthm */
854 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
856 const char *val = bigstr, *next;
858 do {
859 if ((next = strchr(val, delim))) {
860 if (!strncmp(val, smallstr, (next - val)))
861 return 1;
862 else
863 continue;
864 } else
865 return !strcmp(smallstr, val);
867 } while (*(val = (next + 1)));
869 return 0;
872 static int get_perm(const char *instr)
874 int x = 0, ret = 0;
876 if (!instr)
877 return 0;
879 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
880 if (ast_instring(instr, perms[x].label, ','))
881 ret |= perms[x].num;
884 return ret;
887 static int ast_is_number(const char *string)
889 int ret = 1, x = 0;
891 if (!string)
892 return 0;
894 for (x = 0; x < strlen(string); x++) {
895 if (!(string[x] >= 48 && string[x] <= 57)) {
896 ret = 0;
897 break;
901 return ret ? atoi(string) : 0;
904 static int strings_to_mask(const char *string)
906 int x, ret = -1;
908 x = ast_is_number(string);
910 if (x)
911 ret = x;
912 else if (ast_strlen_zero(string))
913 ret = -1;
914 else if (ast_false(string))
915 ret = 0;
916 else if (ast_true(string)) {
917 ret = 0;
918 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
919 ret |= perms[x].num;
920 } else {
921 ret = 0;
922 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
923 if (ast_instring(string, perms[x].label, ','))
924 ret |= perms[x].num;
928 return ret;
931 /*! \brief
932 Rather than braindead on,off this now can also accept a specific int mask value
933 or a ',' delim list of mask strings (the same as manager.conf) -anthm
935 static int set_eventmask(struct mansession *s, const char *eventmask)
937 int maskint = strings_to_mask(eventmask);
939 ast_mutex_lock(&s->__lock);
940 if (maskint >= 0)
941 s->send_events = maskint;
942 ast_mutex_unlock(&s->__lock);
944 return maskint;
947 static int authenticate(struct mansession *s, const struct message *m)
949 struct ast_config *cfg;
950 char *cat;
951 const char *user = astman_get_header(m, "Username");
952 const char *pass = astman_get_header(m, "Secret");
953 const char *authtype = astman_get_header(m, "AuthType");
954 const char *key = astman_get_header(m, "Key");
955 const char *events = astman_get_header(m, "Events");
957 cfg = ast_config_load("manager.conf");
958 if (!cfg)
959 return -1;
960 cat = ast_category_browse(cfg, NULL);
961 while (cat) {
962 if (strcasecmp(cat, "general")) {
963 /* This is a user */
964 if (!strcasecmp(cat, user)) {
965 struct ast_variable *v;
966 struct ast_ha *ha = NULL;
967 char *password = NULL;
969 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
970 if (!strcasecmp(v->name, "secret")) {
971 password = v->value;
972 } else if (!strcasecmp(v->name, "displaysystemname")) {
973 if (ast_true(v->value)) {
974 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
975 s->displaysystemname = 1;
976 } else {
977 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
980 } else if (!strcasecmp(v->name, "permit") ||
981 !strcasecmp(v->name, "deny")) {
982 ha = ast_append_ha(v->name, v->value, ha);
983 } else if (!strcasecmp(v->name, "writetimeout")) {
984 int val = atoi(v->value);
986 if (val < 100)
987 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
988 else
989 s->writetimeout = val;
993 if (ha && !ast_apply_ha(ha, &(s->sin))) {
994 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
995 ast_free_ha(ha);
996 ast_config_destroy(cfg);
997 return -1;
998 } else if (ha)
999 ast_free_ha(ha);
1000 if (!strcasecmp(authtype, "MD5")) {
1001 if (!ast_strlen_zero(key) &&
1002 !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
1003 int x;
1004 int len = 0;
1005 char md5key[256] = "";
1006 struct MD5Context md5;
1007 unsigned char digest[16];
1008 MD5Init(&md5);
1009 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1010 MD5Update(&md5, (unsigned char *) password, strlen(password));
1011 MD5Final(digest, &md5);
1012 for (x=0; x<16; x++)
1013 len += sprintf(md5key + len, "%2.2x", digest[x]);
1014 if (!strcmp(md5key, key))
1015 break;
1016 else {
1017 ast_config_destroy(cfg);
1018 return -1;
1020 } else {
1021 ast_log(LOG_DEBUG, "MD5 authentication is not possible. challenge: '%s'\n",
1022 S_OR(s->challenge, ""));
1023 ast_config_destroy(cfg);
1024 return -1;
1026 } else if (password && !strcmp(password, pass)) {
1027 break;
1028 } else {
1029 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1030 ast_config_destroy(cfg);
1031 return -1;
1035 cat = ast_category_browse(cfg, cat);
1037 if (cat) {
1038 ast_copy_string(s->username, cat, sizeof(s->username));
1039 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
1040 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
1041 ast_config_destroy(cfg);
1042 if (events)
1043 set_eventmask(s, events);
1044 return 0;
1046 ast_config_destroy(cfg);
1047 cfg = ast_config_load("users.conf");
1048 if (!cfg)
1049 return -1;
1050 cat = ast_category_browse(cfg, NULL);
1051 while (cat) {
1052 struct ast_variable *v;
1053 const char *password = NULL;
1054 int hasmanager = 0;
1055 const char *readperms = NULL;
1056 const char *writeperms = NULL;
1058 if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
1059 cat = ast_category_browse(cfg, cat);
1060 continue;
1062 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
1063 if (!strcasecmp(v->name, "secret"))
1064 password = v->value;
1065 else if (!strcasecmp(v->name, "hasmanager"))
1066 hasmanager = ast_true(v->value);
1067 else if (!strcasecmp(v->name, "managerread"))
1068 readperms = v->value;
1069 else if (!strcasecmp(v->name, "managerwrite"))
1070 writeperms = v->value;
1072 if (!hasmanager)
1073 break;
1074 if (!password || strcmp(password, pass)) {
1075 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1076 ast_config_destroy(cfg);
1077 return -1;
1079 ast_copy_string(s->username, cat, sizeof(s->username));
1080 s->readperm = readperms ? get_perm(readperms) : -1;
1081 s->writeperm = writeperms ? get_perm(writeperms) : -1;
1082 ast_config_destroy(cfg);
1083 if (events)
1084 set_eventmask(s, events);
1085 return 0;
1087 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1088 ast_config_destroy(cfg);
1089 return -1;
1092 /*! \brief Manager PING */
1093 static char mandescr_ping[] =
1094 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1095 " manager connection open.\n"
1096 "Variables: NONE\n";
1098 static int action_ping(struct mansession *s, const struct message *m)
1100 astman_send_response(s, m, "Pong", NULL);
1101 return 0;
1104 static char mandescr_getconfig[] =
1105 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1106 "file by category and contents.\n"
1107 "Variables:\n"
1108 " Filename: Configuration filename (e.g. foo.conf)\n";
1110 static int action_getconfig(struct mansession *s, const struct message *m)
1112 struct ast_config *cfg;
1113 const char *fn = astman_get_header(m, "Filename");
1114 int catcount = 0;
1115 int lineno = 0;
1116 char *category=NULL;
1117 struct ast_variable *v;
1118 char idText[256] = "";
1119 const char *id = astman_get_header(m, "ActionID");
1121 if (!ast_strlen_zero(id))
1122 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1124 if (ast_strlen_zero(fn)) {
1125 astman_send_error(s, m, "Filename not specified");
1126 return 0;
1128 if (!(cfg = ast_config_load_with_comments(fn))) {
1129 astman_send_error(s, m, "Config file not found");
1130 return 0;
1132 astman_append(s, "Response: Success\r\n%s", idText);
1133 while ((category = ast_category_browse(cfg, category))) {
1134 lineno = 0;
1135 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1136 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1137 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1138 catcount++;
1140 ast_config_destroy(cfg);
1141 astman_append(s, "\r\n");
1143 return 0;
1147 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
1149 int x;
1150 char hdr[40];
1151 const char *action, *cat, *var, *value, *match;
1152 struct ast_category *category;
1153 struct ast_variable *v;
1155 for (x=0;x<100000;x++) {
1156 unsigned int object = 0;
1158 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1159 action = astman_get_header(m, hdr);
1160 if (ast_strlen_zero(action))
1161 break;
1162 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1163 cat = astman_get_header(m, hdr);
1164 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1165 var = astman_get_header(m, hdr);
1166 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1167 value = astman_get_header(m, hdr);
1168 if (!ast_strlen_zero(value) && *value == '>') {
1169 object = 1;
1170 value++;
1172 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1173 match = astman_get_header(m, hdr);
1174 if (!strcasecmp(action, "newcat")) {
1175 if (!ast_strlen_zero(cat)) {
1176 category = ast_category_new(cat);
1177 if (category) {
1178 ast_category_append(cfg, category);
1181 } else if (!strcasecmp(action, "renamecat")) {
1182 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1183 category = ast_category_get(cfg, cat);
1184 if (category)
1185 ast_category_rename(category, value);
1187 } else if (!strcasecmp(action, "delcat")) {
1188 if (!ast_strlen_zero(cat))
1189 ast_category_delete(cfg, (char *) cat);
1190 } else if (!strcasecmp(action, "update")) {
1191 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1192 ast_variable_update(category, var, value, match, object);
1193 } else if (!strcasecmp(action, "delete")) {
1194 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1195 ast_variable_delete(category, (char *) var, (char *) match);
1196 } else if (!strcasecmp(action, "append")) {
1197 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1198 (category = ast_category_get(cfg, cat)) &&
1199 (v = ast_variable_new(var, value))){
1200 if (object || (match && !strcasecmp(match, "object")))
1201 v->object = 1;
1202 ast_variable_append(category, v);
1208 static char mandescr_updateconfig[] =
1209 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1210 "configuration elements in Asterisk configuration files.\n"
1211 "Variables (X's represent 6 digit number beginning with 000000):\n"
1212 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1213 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1214 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1215 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1216 " Cat-XXXXXX: Category to operate on\n"
1217 " Var-XXXXXX: Variable to work on\n"
1218 " Value-XXXXXX: Value to work on\n"
1219 " Match-XXXXXX: Extra match required to match line\n";
1221 static int action_updateconfig(struct mansession *s, const struct message *m)
1223 struct ast_config *cfg;
1224 const char *sfn = astman_get_header(m, "SrcFilename");
1225 const char *dfn = astman_get_header(m, "DstFilename");
1226 int res;
1227 char idText[256] = "";
1228 const char *id = astman_get_header(m, "ActionID");
1229 const char *rld = astman_get_header(m, "Reload");
1231 if (!ast_strlen_zero(id))
1232 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1234 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1235 astman_send_error(s, m, "Filename not specified");
1236 return 0;
1238 if (!(cfg = ast_config_load_with_comments(sfn))) {
1239 astman_send_error(s, m, "Config file not found");
1240 return 0;
1242 handle_updates(s, m, cfg);
1243 res = config_text_file_save(dfn, cfg, "Manager");
1244 ast_config_destroy(cfg);
1245 if (res) {
1246 astman_send_error(s, m, "Save of config failed");
1247 return 0;
1249 astman_append(s, "Response: Success\r\n%s\r\n", idText);
1250 if (!ast_strlen_zero(rld)) {
1251 if (ast_true(rld))
1252 rld = NULL;
1253 ast_module_reload(rld);
1255 return 0;
1258 /*! \brief Manager WAITEVENT */
1259 static char mandescr_waitevent[] =
1260 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1261 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1262 "session, events will be generated and queued.\n"
1263 "Variables: \n"
1264 " Timeout: Maximum time to wait for events\n";
1266 static int action_waitevent(struct mansession *s, const struct message *m)
1268 const char *timeouts = astman_get_header(m, "Timeout");
1269 int timeout = -1, max;
1270 int x;
1271 int needexit = 0;
1272 time_t now;
1273 struct eventqent *eqe;
1274 const char *id = astman_get_header(m,"ActionID");
1275 char idText[256] = "";
1277 if (!ast_strlen_zero(id))
1278 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1280 if (!ast_strlen_zero(timeouts)) {
1281 sscanf(timeouts, "%i", &timeout);
1284 ast_mutex_lock(&s->__lock);
1285 if (s->waiting_thread != AST_PTHREADT_NULL) {
1286 pthread_kill(s->waiting_thread, SIGURG);
1288 if (s->sessiontimeout) {
1289 time(&now);
1290 max = s->sessiontimeout - now - 10;
1291 if (max < 0)
1292 max = 0;
1293 if ((timeout < 0) || (timeout > max))
1294 timeout = max;
1295 if (!s->send_events)
1296 s->send_events = -1;
1297 /* Once waitevent is called, always queue events from now on */
1299 ast_mutex_unlock(&s->__lock);
1300 s->waiting_thread = pthread_self();
1301 if (option_debug)
1302 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1303 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1304 ast_mutex_lock(&s->__lock);
1305 if (s->eventq && s->eventq->next)
1306 needexit = 1;
1307 if (s->waiting_thread != pthread_self())
1308 needexit = 1;
1309 if (s->needdestroy)
1310 needexit = 1;
1311 ast_mutex_unlock(&s->__lock);
1312 if (needexit)
1313 break;
1314 if (s->fd > 0) {
1315 if (ast_wait_for_input(s->fd, 1000))
1316 break;
1317 } else {
1318 sleep(1);
1321 if (option_debug)
1322 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1323 ast_mutex_lock(&s->__lock);
1324 if (s->waiting_thread == pthread_self()) {
1325 astman_send_response(s, m, "Success", "Waiting for Event...");
1326 /* Only show events if we're the most recent waiter */
1327 while(s->eventq->next) {
1328 eqe = s->eventq->next;
1329 if (((s->readperm & eqe->category) == eqe->category) &&
1330 ((s->send_events & eqe->category) == eqe->category)) {
1331 astman_append(s, "%s", eqe->eventdata);
1333 unuse_eventqent(s->eventq);
1334 s->eventq = eqe;
1336 astman_append(s,
1337 "Event: WaitEventComplete\r\n"
1338 "%s"
1339 "\r\n", idText);
1340 s->waiting_thread = AST_PTHREADT_NULL;
1341 } else {
1342 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1344 ast_mutex_unlock(&s->__lock);
1345 return 0;
1348 static char mandescr_listcommands[] =
1349 "Description: Returns the action name and synopsis for every\n"
1350 " action that is available to the user\n"
1351 "Variables: NONE\n";
1353 /*! \note The actionlock is read-locked by the caller of this function */
1354 static int action_listcommands(struct mansession *s, const struct message *m)
1356 struct manager_action *cur;
1357 char idText[256] = "";
1358 char temp[BUFSIZ];
1359 const char *id = astman_get_header(m,"ActionID");
1361 if (!ast_strlen_zero(id))
1362 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1363 astman_append(s, "Response: Success\r\n%s", idText);
1364 for (cur = first_action; cur; cur = cur->next) {
1365 if ((s->writeperm & cur->authority) == cur->authority)
1366 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1368 astman_append(s, "\r\n");
1370 return 0;
1373 static char mandescr_events[] =
1374 "Description: Enable/Disable sending of events to this manager\n"
1375 " client.\n"
1376 "Variables:\n"
1377 " EventMask: 'on' if all events should be sent,\n"
1378 " 'off' if no events should be sent,\n"
1379 " 'system,call,log' to select which flags events should have to be sent.\n";
1381 static int action_events(struct mansession *s, const struct message *m)
1383 const char *mask = astman_get_header(m, "EventMask");
1384 int res;
1386 res = set_eventmask(s, mask);
1387 if (res > 0)
1388 astman_send_response(s, m, "Events On", NULL);
1389 else if (res == 0)
1390 astman_send_response(s, m, "Events Off", NULL);
1392 return 0;
1395 static char mandescr_logoff[] =
1396 "Description: Logoff this manager session\n"
1397 "Variables: NONE\n";
1399 static int action_logoff(struct mansession *s, const struct message *m)
1401 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1402 return -1;
1405 static char mandescr_hangup[] =
1406 "Description: Hangup a channel\n"
1407 "Variables: \n"
1408 " Channel: The channel name to be hungup\n";
1410 static int action_hangup(struct mansession *s, const struct message *m)
1412 struct ast_channel *c = NULL;
1413 const char *name = astman_get_header(m, "Channel");
1414 if (ast_strlen_zero(name)) {
1415 astman_send_error(s, m, "No channel specified");
1416 return 0;
1418 c = ast_get_channel_by_name_locked(name);
1419 if (!c) {
1420 astman_send_error(s, m, "No such channel");
1421 return 0;
1423 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1424 ast_channel_unlock(c);
1425 astman_send_ack(s, m, "Channel Hungup");
1426 return 0;
1429 static char mandescr_setvar[] =
1430 "Description: Set a global or local channel variable.\n"
1431 "Variables: (Names marked with * are required)\n"
1432 " Channel: Channel to set variable for\n"
1433 " *Variable: Variable name\n"
1434 " *Value: Value\n";
1436 static int action_setvar(struct mansession *s, const struct message *m)
1438 struct ast_channel *c = NULL;
1439 const char *name = astman_get_header(m, "Channel");
1440 const char *varname = astman_get_header(m, "Variable");
1441 const char *varval = astman_get_header(m, "Value");
1443 if (ast_strlen_zero(varname)) {
1444 astman_send_error(s, m, "No variable specified");
1445 return 0;
1448 if (!ast_strlen_zero(name)) {
1449 c = ast_get_channel_by_name_locked(name);
1450 if (!c) {
1451 astman_send_error(s, m, "No such channel");
1452 return 0;
1456 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1458 if (c)
1459 ast_channel_unlock(c);
1461 astman_send_ack(s, m, "Variable Set");
1463 return 0;
1466 static char mandescr_getvar[] =
1467 "Description: Get the value of a global or local channel variable.\n"
1468 "Variables: (Names marked with * are required)\n"
1469 " Channel: Channel to read variable from\n"
1470 " *Variable: Variable name\n"
1471 " ActionID: Optional Action id for message matching.\n";
1473 static int action_getvar(struct mansession *s, const struct message *m)
1475 struct ast_channel *c = NULL;
1476 const char *name = astman_get_header(m, "Channel");
1477 const char *varname = astman_get_header(m, "Variable");
1478 const char *id = astman_get_header(m,"ActionID");
1479 char *varval;
1480 char workspace[1024] = "";
1482 if (ast_strlen_zero(varname)) {
1483 astman_send_error(s, m, "No variable specified");
1484 return 0;
1487 if (!ast_strlen_zero(name)) {
1488 c = ast_get_channel_by_name_locked(name);
1489 if (!c) {
1490 astman_send_error(s, m, "No such channel");
1491 return 0;
1495 if (varname[strlen(varname) - 1] == ')') {
1496 char *copy = ast_strdupa(varname);
1498 ast_func_read(c, copy, workspace, sizeof(workspace));
1499 varval = workspace;
1500 } else {
1501 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1504 if (c)
1505 ast_channel_unlock(c);
1506 astman_append(s, "Response: Success\r\n"
1507 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1508 if (!ast_strlen_zero(id))
1509 astman_append(s, "ActionID: %s\r\n",id);
1510 astman_append(s, "\r\n");
1512 return 0;
1516 /*! \brief Manager "status" command to show channels */
1517 /* Needs documentation... */
1518 static int action_status(struct mansession *s, const struct message *m)
1520 const char *id = astman_get_header(m,"ActionID");
1521 const char *name = astman_get_header(m,"Channel");
1522 char idText[256] = "";
1523 struct ast_channel *c;
1524 char bridge[256];
1525 struct timeval now = ast_tvnow();
1526 long elapsed_seconds = 0;
1527 int all = ast_strlen_zero(name); /* set if we want all channels */
1529 if (!ast_strlen_zero(id))
1530 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1531 if (all)
1532 c = ast_channel_walk_locked(NULL);
1533 else {
1534 c = ast_get_channel_by_name_locked(name);
1535 if (!c) {
1536 astman_send_error(s, m, "No such channel");
1537 return 0;
1540 astman_send_ack(s, m, "Channel status will follow");
1541 /* if we look by name, we break after the first iteration */
1542 while (c) {
1543 if (c->_bridge)
1544 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1545 else
1546 bridge[0] = '\0';
1547 if (c->pbx) {
1548 if (c->cdr) {
1549 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1551 astman_append(s,
1552 "Event: Status\r\n"
1553 "Privilege: Call\r\n"
1554 "Channel: %s\r\n"
1555 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1556 "CallerIDNum: %s\r\n"
1557 "CallerIDName: %s\r\n"
1558 "Account: %s\r\n"
1559 "State: %s\r\n"
1560 "Context: %s\r\n"
1561 "Extension: %s\r\n"
1562 "Priority: %d\r\n"
1563 "Seconds: %ld\r\n"
1564 "%s"
1565 "Uniqueid: %s\r\n"
1566 "%s"
1567 "\r\n",
1568 c->name,
1569 S_OR(c->cid.cid_num, "<unknown>"),
1570 S_OR(c->cid.cid_num, "<unknown>"),
1571 S_OR(c->cid.cid_name, "<unknown>"),
1572 c->accountcode,
1573 ast_state2str(c->_state), c->context,
1574 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1575 } else {
1576 astman_append(s,
1577 "Event: Status\r\n"
1578 "Privilege: Call\r\n"
1579 "Channel: %s\r\n"
1580 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1581 "CallerIDNum: %s\r\n"
1582 "CallerIDName: %s\r\n"
1583 "Account: %s\r\n"
1584 "State: %s\r\n"
1585 "%s"
1586 "Uniqueid: %s\r\n"
1587 "%s"
1588 "\r\n",
1589 c->name,
1590 S_OR(c->cid.cid_num, "<unknown>"),
1591 S_OR(c->cid.cid_num, "<unknown>"),
1592 S_OR(c->cid.cid_name, "<unknown>"),
1593 c->accountcode,
1594 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1596 ast_channel_unlock(c);
1597 if (!all)
1598 break;
1599 c = ast_channel_walk_locked(c);
1601 astman_append(s,
1602 "Event: StatusComplete\r\n"
1603 "%s"
1604 "\r\n",idText);
1605 return 0;
1608 static char mandescr_redirect[] =
1609 "Description: Redirect (transfer) a call.\n"
1610 "Variables: (Names marked with * are required)\n"
1611 " *Channel: Channel to redirect\n"
1612 " ExtraChannel: Second call leg to transfer (optional)\n"
1613 " *Exten: Extension to transfer to\n"
1614 " *Context: Context to transfer to\n"
1615 " *Priority: Priority to transfer to\n"
1616 " ActionID: Optional Action id for message matching.\n";
1618 /*! \brief action_redirect: The redirect manager command */
1619 static int action_redirect(struct mansession *s, const struct message *m)
1621 const char *name = astman_get_header(m, "Channel");
1622 const char *name2 = astman_get_header(m, "ExtraChannel");
1623 const char *exten = astman_get_header(m, "Exten");
1624 const char *context = astman_get_header(m, "Context");
1625 const char *priority = astman_get_header(m, "Priority");
1626 struct ast_channel *chan, *chan2 = NULL;
1627 int pi = 0;
1628 int res;
1630 if (ast_strlen_zero(name)) {
1631 astman_send_error(s, m, "Channel not specified");
1632 return 0;
1634 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1635 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1636 astman_send_error(s, m, "Invalid priority\n");
1637 return 0;
1640 /* XXX watch out, possible deadlock!!! */
1641 chan = ast_get_channel_by_name_locked(name);
1642 if (!chan) {
1643 char buf[BUFSIZ];
1644 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1645 astman_send_error(s, m, buf);
1646 return 0;
1648 if (ast_check_hangup(chan)) {
1649 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1650 ast_channel_unlock(chan);
1651 return 0;
1653 if (!ast_strlen_zero(name2))
1654 chan2 = ast_get_channel_by_name_locked(name2);
1655 if (chan2 && ast_check_hangup(chan2)) {
1656 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1657 ast_channel_unlock(chan);
1658 ast_channel_unlock(chan2);
1659 return 0;
1661 res = ast_async_goto(chan, context, exten, pi);
1662 if (!res) {
1663 if (!ast_strlen_zero(name2)) {
1664 if (chan2)
1665 res = ast_async_goto(chan2, context, exten, pi);
1666 else
1667 res = -1;
1668 if (!res)
1669 astman_send_ack(s, m, "Dual Redirect successful");
1670 else
1671 astman_send_error(s, m, "Secondary redirect failed");
1672 } else
1673 astman_send_ack(s, m, "Redirect successful");
1674 } else
1675 astman_send_error(s, m, "Redirect failed");
1676 if (chan)
1677 ast_channel_unlock(chan);
1678 if (chan2)
1679 ast_channel_unlock(chan2);
1680 return 0;
1683 static char mandescr_command[] =
1684 "Description: Run a CLI command.\n"
1685 "Variables: (Names marked with * are required)\n"
1686 " *Command: Asterisk CLI command to run\n"
1687 " ActionID: Optional Action id for message matching.\n";
1689 /*! \brief action_command: Manager command "command" - execute CLI command */
1690 static int action_command(struct mansession *s, const struct message *m)
1692 const char *cmd = astman_get_header(m, "Command");
1693 const char *id = astman_get_header(m, "ActionID");
1694 char *buf, *final_buf;
1695 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1696 int fd = mkstemp(template), i = 0;
1697 off_t l;
1699 for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1700 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1701 astman_send_error(s, m, "Command blacklisted");
1702 return 0;
1706 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1707 if (!ast_strlen_zero(id))
1708 astman_append(s, "ActionID: %s\r\n", id);
1709 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1710 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1711 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1713 /* This has a potential to overflow the stack. Hence, use the heap. */
1714 buf = ast_calloc(1, l + 1);
1715 final_buf = ast_calloc(1, l + 1);
1716 if (buf) {
1717 lseek(fd, 0, SEEK_SET);
1718 read(fd, buf, l);
1719 buf[l] = '\0';
1720 if (final_buf) {
1721 term_strip(final_buf, buf, l);
1722 final_buf[l] = '\0';
1724 astman_append(s, "%s", S_OR(final_buf, buf));
1725 ast_free(buf);
1727 close(fd);
1728 unlink(template);
1729 astman_append(s, "--END COMMAND--\r\n\r\n");
1730 if (final_buf)
1731 ast_free(final_buf);
1732 return 0;
1735 static void *fast_originate(void *data)
1737 struct fast_originate_helper *in = data;
1738 int res;
1739 int reason = 0;
1740 struct ast_channel *chan = NULL;
1741 char requested_channel[AST_CHANNEL_NAME];
1743 if (!ast_strlen_zero(in->app)) {
1744 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1745 S_OR(in->cid_num, NULL),
1746 S_OR(in->cid_name, NULL),
1747 in->vars, in->account, &chan);
1748 } else {
1749 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1750 S_OR(in->cid_num, NULL),
1751 S_OR(in->cid_name, NULL),
1752 in->vars, in->account, &chan);
1755 if (!chan)
1756 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1757 /* Tell the manager what happened with the channel */
1758 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1759 "%s"
1760 "Response: %s\r\n"
1761 "Channel: %s\r\n"
1762 "Context: %s\r\n"
1763 "Exten: %s\r\n"
1764 "Reason: %d\r\n"
1765 "Uniqueid: %s\r\n"
1766 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1767 "CallerIDNum: %s\r\n"
1768 "CallerIDName: %s\r\n",
1769 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1770 chan ? chan->uniqueid : "<null>",
1771 S_OR(in->cid_num, "<unknown>"),
1772 S_OR(in->cid_num, "<unknown>"),
1773 S_OR(in->cid_name, "<unknown>")
1776 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1777 if (chan)
1778 ast_channel_unlock(chan);
1779 free(in);
1780 return NULL;
1783 static char mandescr_originate[] =
1784 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1785 " Application/Data\n"
1786 "Variables: (Names marked with * are required)\n"
1787 " *Channel: Channel name to call\n"
1788 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1789 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1790 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1791 " Application: Application to use\n"
1792 " Data: Data to use (requires 'Application')\n"
1793 " Timeout: How long to wait for call to be answered (in ms)\n"
1794 " CallerID: Caller ID to be set on the outgoing channel\n"
1795 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1796 " Account: Account code\n"
1797 " Async: Set to 'true' for fast origination\n";
1799 static int action_originate(struct mansession *s, const struct message *m)
1801 const char *name = astman_get_header(m, "Channel");
1802 const char *exten = astman_get_header(m, "Exten");
1803 const char *context = astman_get_header(m, "Context");
1804 const char *priority = astman_get_header(m, "Priority");
1805 const char *timeout = astman_get_header(m, "Timeout");
1806 const char *callerid = astman_get_header(m, "CallerID");
1807 const char *account = astman_get_header(m, "Account");
1808 const char *app = astman_get_header(m, "Application");
1809 const char *appdata = astman_get_header(m, "Data");
1810 const char *async = astman_get_header(m, "Async");
1811 const char *id = astman_get_header(m, "ActionID");
1812 struct ast_variable *vars = astman_get_variables(m);
1813 char *tech, *data;
1814 char *l = NULL, *n = NULL;
1815 int pi = 0;
1816 int res;
1817 int to = 30000;
1818 int reason = 0;
1819 char tmp[256];
1820 char tmp2[256];
1822 pthread_t th;
1823 pthread_attr_t attr;
1824 if (!name) {
1825 astman_send_error(s, m, "Channel not specified");
1826 return 0;
1828 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1829 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1830 astman_send_error(s, m, "Invalid priority\n");
1831 return 0;
1834 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1835 astman_send_error(s, m, "Invalid timeout\n");
1836 return 0;
1838 ast_copy_string(tmp, name, sizeof(tmp));
1839 tech = tmp;
1840 data = strchr(tmp, '/');
1841 if (!data) {
1842 astman_send_error(s, m, "Invalid channel\n");
1843 return 0;
1845 *data++ = '\0';
1846 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1847 ast_callerid_parse(tmp2, &n, &l);
1848 if (n) {
1849 if (ast_strlen_zero(n))
1850 n = NULL;
1852 if (l) {
1853 ast_shrink_phone_number(l);
1854 if (ast_strlen_zero(l))
1855 l = NULL;
1857 if (ast_true(async)) {
1858 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1859 if (!fast) {
1860 res = -1;
1861 } else {
1862 if (!ast_strlen_zero(id))
1863 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1864 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1865 ast_copy_string(fast->data, data, sizeof(fast->data));
1866 ast_copy_string(fast->app, app, sizeof(fast->app));
1867 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1868 if (l)
1869 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1870 if (n)
1871 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1872 fast->vars = vars;
1873 ast_copy_string(fast->context, context, sizeof(fast->context));
1874 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1875 ast_copy_string(fast->account, account, sizeof(fast->account));
1876 fast->timeout = to;
1877 fast->priority = pi;
1878 pthread_attr_init(&attr);
1879 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1880 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1881 res = -1;
1882 } else {
1883 res = 0;
1885 pthread_attr_destroy(&attr);
1887 } else if (!ast_strlen_zero(app)) {
1888 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1889 } else {
1890 if (exten && context && pi)
1891 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1892 else {
1893 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1894 return 0;
1897 if (!res)
1898 astman_send_ack(s, m, "Originate successfully queued");
1899 else
1900 astman_send_error(s, m, "Originate failed");
1901 return 0;
1904 /*! \brief Help text for manager command mailboxstatus
1906 static char mandescr_mailboxstatus[] =
1907 "Description: Checks a voicemail account for status.\n"
1908 "Variables: (Names marked with * are required)\n"
1909 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1910 " ActionID: Optional ActionID for message matching.\n"
1911 "Returns number of messages.\n"
1912 " Message: Mailbox Status\n"
1913 " Mailbox: <mailboxid>\n"
1914 " Waiting: <count>\n"
1915 "\n";
1917 static int action_mailboxstatus(struct mansession *s, const struct message *m)
1919 const char *mailbox = astman_get_header(m, "Mailbox");
1920 const char *id = astman_get_header(m,"ActionID");
1921 char idText[256] = "";
1922 int ret;
1923 if (ast_strlen_zero(mailbox)) {
1924 astman_send_error(s, m, "Mailbox not specified");
1925 return 0;
1927 if (!ast_strlen_zero(id))
1928 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1929 ret = ast_app_has_voicemail(mailbox, NULL);
1930 astman_append(s, "Response: Success\r\n"
1931 "%s"
1932 "Message: Mailbox Status\r\n"
1933 "Mailbox: %s\r\n"
1934 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1935 return 0;
1938 static char mandescr_mailboxcount[] =
1939 "Description: Checks a voicemail account for new messages.\n"
1940 "Variables: (Names marked with * are required)\n"
1941 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1942 " ActionID: Optional ActionID for message matching.\n"
1943 "Returns number of new and old messages.\n"
1944 " Message: Mailbox Message Count\n"
1945 " Mailbox: <mailboxid>\n"
1946 " NewMessages: <count>\n"
1947 " OldMessages: <count>\n"
1948 "\n";
1949 static int action_mailboxcount(struct mansession *s, const struct message *m)
1951 const char *mailbox = astman_get_header(m, "Mailbox");
1952 const char *id = astman_get_header(m,"ActionID");
1953 char idText[256] = "";
1954 int newmsgs = 0, oldmsgs = 0;
1955 if (ast_strlen_zero(mailbox)) {
1956 astman_send_error(s, m, "Mailbox not specified");
1957 return 0;
1959 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1960 if (!ast_strlen_zero(id)) {
1961 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1963 astman_append(s, "Response: Success\r\n"
1964 "%s"
1965 "Message: Mailbox Message Count\r\n"
1966 "Mailbox: %s\r\n"
1967 "NewMessages: %d\r\n"
1968 "OldMessages: %d\r\n"
1969 "\r\n",
1970 idText,mailbox, newmsgs, oldmsgs);
1971 return 0;
1974 static char mandescr_extensionstate[] =
1975 "Description: Report the extension state for given extension.\n"
1976 " If the extension has a hint, will use devicestate to check\n"
1977 " the status of the device connected to the extension.\n"
1978 "Variables: (Names marked with * are required)\n"
1979 " *Exten: Extension to check state on\n"
1980 " *Context: Context for extension\n"
1981 " ActionId: Optional ID for this transaction\n"
1982 "Will return an \"Extension Status\" message.\n"
1983 "The response will include the hint for the extension and the status.\n";
1985 static int action_extensionstate(struct mansession *s, const struct message *m)
1987 const char *exten = astman_get_header(m, "Exten");
1988 const char *context = astman_get_header(m, "Context");
1989 const char *id = astman_get_header(m,"ActionID");
1990 char idText[256] = "";
1991 char hint[256] = "";
1992 int status;
1993 if (ast_strlen_zero(exten)) {
1994 astman_send_error(s, m, "Extension not specified");
1995 return 0;
1997 if (ast_strlen_zero(context))
1998 context = "default";
1999 status = ast_extension_state(NULL, context, exten);
2000 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2001 if (!ast_strlen_zero(id)) {
2002 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2004 astman_append(s, "Response: Success\r\n"
2005 "%s"
2006 "Message: Extension Status\r\n"
2007 "Exten: %s\r\n"
2008 "Context: %s\r\n"
2009 "Hint: %s\r\n"
2010 "Status: %d\r\n\r\n",
2011 idText,exten, context, hint, status);
2012 return 0;
2015 static char mandescr_timeout[] =
2016 "Description: Hangup a channel after a certain time.\n"
2017 "Variables: (Names marked with * are required)\n"
2018 " *Channel: Channel name to hangup\n"
2019 " *Timeout: Maximum duration of the call (sec)\n"
2020 "Acknowledges set time with 'Timeout Set' message\n";
2022 static int action_timeout(struct mansession *s, const struct message *m)
2024 struct ast_channel *c = NULL;
2025 const char *name = astman_get_header(m, "Channel");
2026 int timeout = atoi(astman_get_header(m, "Timeout"));
2027 if (ast_strlen_zero(name)) {
2028 astman_send_error(s, m, "No channel specified");
2029 return 0;
2031 if (!timeout) {
2032 astman_send_error(s, m, "No timeout specified");
2033 return 0;
2035 c = ast_get_channel_by_name_locked(name);
2036 if (!c) {
2037 astman_send_error(s, m, "No such channel");
2038 return 0;
2040 ast_channel_setwhentohangup(c, timeout);
2041 ast_channel_unlock(c);
2042 astman_send_ack(s, m, "Timeout Set");
2043 return 0;
2046 static int process_events(struct mansession *s)
2048 struct eventqent *eqe;
2049 int ret = 0;
2050 ast_mutex_lock(&s->__lock);
2051 if (!s->eventq)
2052 s->eventq = master_eventq;
2053 while(s->eventq->next) {
2054 eqe = s->eventq->next;
2055 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
2056 ((s->send_events & eqe->category) == eqe->category)) {
2057 if (s->fd > -1) {
2058 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
2059 ret = -1;
2060 } else if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
2061 ret = -1;
2062 else
2063 ast_dynamic_str_append(&s->outputstr, 0, "%s", eqe->eventdata);
2065 unuse_eventqent(s->eventq);
2066 s->eventq = eqe;
2068 ast_mutex_unlock(&s->__lock);
2069 return ret;
2072 static char mandescr_userevent[] =
2073 "Description: Send an event to manager sessions.\n"
2074 "Variables: (Names marked with * are required)\n"
2075 " *UserEvent: EventStringToSend\n"
2076 " Header1: Content1\n"
2077 " HeaderN: ContentN\n";
2079 static int action_userevent(struct mansession *s, const struct message *m)
2081 const char *event = astman_get_header(m, "UserEvent");
2082 char body[2048] = "";
2083 int x, bodylen = 0;
2084 for (x = 0; x < m->hdrcount; x++) {
2085 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2086 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2087 bodylen += strlen(m->headers[x]);
2088 ast_copy_string(body + bodylen, "\r\n", 3);
2089 bodylen += 2;
2093 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2094 return 0;
2097 static int process_message(struct mansession *s, const struct message *m)
2099 char action[80] = "";
2100 struct manager_action *tmp;
2101 const char *id = astman_get_header(m,"ActionID");
2102 char idText[256] = "";
2103 int ret = 0;
2105 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2106 if (option_debug)
2107 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
2109 if (ast_strlen_zero(action)) {
2110 astman_send_error(s, m, "Missing action in request");
2111 return 0;
2113 if (!ast_strlen_zero(id)) {
2114 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2116 if (!s->authenticated) {
2117 if (!strcasecmp(action, "Challenge")) {
2118 const char *authtype = astman_get_header(m, "AuthType");
2120 if (!strcasecmp(authtype, "MD5")) {
2121 if (ast_strlen_zero(s->challenge))
2122 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
2123 astman_append(s, "Response: Success\r\n"
2124 "%s"
2125 "Challenge: %s\r\n\r\n",
2126 idText, s->challenge);
2127 return 0;
2128 } else {
2129 astman_send_error(s, m, "Must specify AuthType");
2130 return 0;
2132 } else if (!strcasecmp(action, "Login")) {
2133 if (authenticate(s, m)) {
2134 sleep(1);
2135 astman_send_error(s, m, "Authentication failed");
2136 return -1;
2137 } else {
2138 s->authenticated = 1;
2139 if (option_verbose > 1) {
2140 if (displayconnects) {
2141 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n",
2142 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2145 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n",
2146 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2147 astman_send_ack(s, m, "Authentication accepted");
2149 } else if (!strcasecmp(action, "Logoff")) {
2150 astman_send_ack(s, m, "See ya");
2151 return -1;
2152 } else
2153 astman_send_error(s, m, "Authentication Required");
2154 } else {
2155 if (!strcasecmp(action, "Login"))
2156 astman_send_ack(s, m, "Already logged in");
2157 else {
2158 ast_rwlock_rdlock(&actionlock);
2159 for (tmp = first_action; tmp; tmp = tmp->next) {
2160 if (strcasecmp(action, tmp->action))
2161 continue;
2162 if ((s->writeperm & tmp->authority) == tmp->authority) {
2163 if (tmp->func(s, m))
2164 ret = -1;
2165 } else
2166 astman_send_error(s, m, "Permission denied");
2167 break;
2169 ast_rwlock_unlock(&actionlock);
2170 if (!tmp)
2171 astman_send_error(s, m, "Invalid/unknown command");
2174 if (ret)
2175 return ret;
2176 return process_events(s);
2179 static int get_input(struct mansession *s, char *output)
2181 /* output must have at least sizeof(s->inbuf) space */
2182 int res;
2183 int x;
2184 struct pollfd fds[1];
2185 for (x = 1; x < s->inlen; x++) {
2186 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
2187 /* Copy output data up to and including \r\n */
2188 memcpy(output, s->inbuf, x + 1);
2189 /* Add trailing \0 */
2190 output[x+1] = '\0';
2191 /* Move remaining data back to the front */
2192 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2193 s->inlen -= (x + 1);
2194 return 1;
2197 if (s->inlen >= sizeof(s->inbuf) - 1) {
2198 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2199 s->inlen = 0;
2201 fds[0].fd = s->fd;
2202 fds[0].events = POLLIN;
2203 do {
2204 ast_mutex_lock(&s->__lock);
2205 s->waiting_thread = pthread_self();
2206 ast_mutex_unlock(&s->__lock);
2208 res = poll(fds, 1, -1);
2210 ast_mutex_lock(&s->__lock);
2211 s->waiting_thread = AST_PTHREADT_NULL;
2212 ast_mutex_unlock(&s->__lock);
2213 if (res < 0) {
2214 if (errno == EINTR) {
2215 return 0;
2217 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2218 return -1;
2219 } else if (res > 0) {
2220 ast_mutex_lock(&s->__lock);
2221 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2222 ast_mutex_unlock(&s->__lock);
2223 if (res < 1)
2224 return -1;
2225 break;
2227 } while(1);
2228 s->inlen += res;
2229 s->inbuf[s->inlen] = '\0';
2230 return 0;
2233 static int do_message(struct mansession *s)
2235 struct message m = { 0 };
2236 char header_buf[sizeof(s->inbuf)] = { '\0' };
2237 int res;
2239 for (;;) {
2240 /* Check if any events are pending and do them if needed */
2241 if (s->eventq->next) {
2242 if (process_events(s))
2243 return -1;
2245 res = get_input(s, header_buf);
2246 if (res == 0) {
2247 continue;
2248 } else if (res > 0) {
2249 /* Strip trailing \r\n */
2250 if (strlen(header_buf) < 2)
2251 continue;
2252 header_buf[strlen(header_buf) - 2] = '\0';
2253 if (ast_strlen_zero(header_buf))
2254 return process_message(s, &m) ? -1 : 0;
2255 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2256 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2257 } else {
2258 return res;
2263 static void *session_do(void *data)
2265 struct mansession *s = data;
2266 int res;
2268 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2269 for (;;) {
2270 if ((res = do_message(s)) < 0)
2271 break;
2273 if (s->authenticated) {
2274 if (option_verbose > 1) {
2275 if (displayconnects)
2276 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2278 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2279 } else {
2280 if (option_verbose > 1) {
2281 if (displayconnects)
2282 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2284 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2287 /* It is possible under certain circumstances for this session thread
2288 to complete its work and exit *before* the thread that created it
2289 has finished executing the ast_pthread_create_background() function.
2290 If this occurs, some versions of glibc appear to act in a buggy
2291 fashion and attempt to write data into memory that it thinks belongs
2292 to the thread but is in fact not owned by the thread (or may have
2293 been freed completely).
2295 Causing this thread to yield to other threads at least one time
2296 appears to work around this bug.
2298 usleep(1);
2300 destroy_session(s);
2301 return NULL;
2304 static void *accept_thread(void *ignore)
2306 int as;
2307 struct sockaddr_in sin;
2308 socklen_t sinlen;
2309 struct eventqent *eqe;
2310 struct mansession *s;
2311 struct protoent *p;
2312 int arg = 1;
2313 int flags;
2314 pthread_attr_t attr;
2315 time_t now;
2316 struct pollfd pfds[1];
2318 pthread_attr_init(&attr);
2319 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2321 for (;;) {
2322 time(&now);
2323 AST_LIST_LOCK(&sessions);
2324 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2325 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2326 AST_LIST_REMOVE_CURRENT(&sessions, list);
2327 num_sessions--;
2328 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2329 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2330 s->username, ast_inet_ntoa(s->sin.sin_addr));
2332 free_session(s);
2333 break;
2336 AST_LIST_TRAVERSE_SAFE_END
2337 /* Purge master event queue of old, unused events, but make sure we
2338 always keep at least one in the queue */
2339 eqe = master_eventq;
2340 while (master_eventq->next && !master_eventq->usecount) {
2341 eqe = master_eventq;
2342 master_eventq = master_eventq->next;
2343 free(eqe);
2345 AST_LIST_UNLOCK(&sessions);
2347 sinlen = sizeof(sin);
2348 pfds[0].fd = asock;
2349 pfds[0].events = POLLIN;
2350 /* Wait for something to happen, but timeout every few seconds so
2351 we can ditch any old manager sessions */
2352 if (poll(pfds, 1, 5000) < 1)
2353 continue;
2354 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2355 if (as < 0) {
2356 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2357 continue;
2359 p = getprotobyname("tcp");
2360 if (p) {
2361 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2362 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2365 if (!(s = ast_calloc(1, sizeof(*s))))
2366 continue;
2368 memcpy(&s->sin, &sin, sizeof(sin));
2369 s->writetimeout = 100;
2370 s->waiting_thread = AST_PTHREADT_NULL;
2372 if (!block_sockets) {
2373 /* For safety, make sure socket is non-blocking */
2374 flags = fcntl(as, F_GETFL);
2375 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2376 } else {
2377 flags = fcntl(as, F_GETFL);
2378 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2380 ast_mutex_init(&s->__lock);
2381 s->fd = as;
2382 s->send_events = -1;
2383 AST_LIST_LOCK(&sessions);
2384 AST_LIST_INSERT_HEAD(&sessions, s, list);
2385 num_sessions++;
2386 /* Find the last place in the master event queue and hook ourselves
2387 in there */
2388 s->eventq = master_eventq;
2389 while(s->eventq->next)
2390 s->eventq = s->eventq->next;
2391 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2392 AST_LIST_UNLOCK(&sessions);
2393 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2394 destroy_session(s);
2396 pthread_attr_destroy(&attr);
2397 return NULL;
2400 static int append_event(const char *str, int category)
2402 struct eventqent *tmp, *prev = NULL;
2403 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2405 if (!tmp)
2406 return -1;
2408 tmp->next = NULL;
2409 tmp->category = category;
2410 strcpy(tmp->eventdata, str);
2412 if (master_eventq) {
2413 prev = master_eventq;
2414 while (prev->next)
2415 prev = prev->next;
2416 prev->next = tmp;
2417 } else {
2418 master_eventq = tmp;
2421 tmp->usecount = num_sessions;
2423 return 0;
2426 /*! \brief manager_event: Send AMI event to client */
2427 int manager_event(int category, const char *event, const char *fmt, ...)
2429 struct mansession *s;
2430 char auth[80];
2431 va_list ap;
2432 struct timeval now;
2433 struct ast_dynamic_str *buf;
2435 /* Abort if there aren't any manager sessions */
2436 if (!num_sessions)
2437 return 0;
2439 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2440 return -1;
2442 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2443 "Event: %s\r\nPrivilege: %s\r\n",
2444 event, authority_to_str(category, auth, sizeof(auth)));
2446 if (timestampevents) {
2447 now = ast_tvnow();
2448 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2449 "Timestamp: %ld.%06lu\r\n",
2450 now.tv_sec, (unsigned long) now.tv_usec);
2453 va_start(ap, fmt);
2454 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2455 va_end(ap);
2457 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2459 /* Append event to master list and wake up any sleeping sessions */
2460 AST_LIST_LOCK(&sessions);
2461 append_event(buf->str, category);
2462 AST_LIST_TRAVERSE(&sessions, s, list) {
2463 ast_mutex_lock(&s->__lock);
2464 if (s->waiting_thread != AST_PTHREADT_NULL)
2465 pthread_kill(s->waiting_thread, SIGURG);
2466 ast_mutex_unlock(&s->__lock);
2468 AST_LIST_UNLOCK(&sessions);
2470 return 0;
2473 int ast_manager_unregister(char *action)
2475 struct manager_action *cur, *prev;
2477 ast_rwlock_wrlock(&actionlock);
2478 cur = prev = first_action;
2479 while (cur) {
2480 if (!strcasecmp(action, cur->action)) {
2481 prev->next = cur->next;
2482 free(cur);
2483 if (option_verbose > 1)
2484 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2485 ast_rwlock_unlock(&actionlock);
2486 return 0;
2488 prev = cur;
2489 cur = cur->next;
2491 ast_rwlock_unlock(&actionlock);
2492 return 0;
2495 static int manager_state_cb(char *context, char *exten, int state, void *data)
2497 /* Notify managers of change */
2498 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2499 return 0;
2502 static int ast_manager_register_struct(struct manager_action *act)
2504 struct manager_action *cur, *prev = NULL;
2505 int ret;
2507 ast_rwlock_wrlock(&actionlock);
2508 cur = first_action;
2509 while (cur) { /* Walk the list of actions */
2510 ret = strcasecmp(cur->action, act->action);
2511 if (ret == 0) {
2512 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2513 ast_rwlock_unlock(&actionlock);
2514 return -1;
2515 } else if (ret > 0) {
2516 /* Insert these alphabetically */
2517 if (prev) {
2518 act->next = prev->next;
2519 prev->next = act;
2520 } else {
2521 act->next = first_action;
2522 first_action = act;
2524 break;
2526 prev = cur;
2527 cur = cur->next;
2530 if (!cur) {
2531 if (prev)
2532 prev->next = act;
2533 else
2534 first_action = act;
2535 act->next = NULL;
2538 if (option_verbose > 1)
2539 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2540 ast_rwlock_unlock(&actionlock);
2541 return 0;
2544 /*! \brief register a new command with manager, including online help. This is
2545 the preferred way to register a manager command */
2546 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2548 struct manager_action *cur;
2550 cur = ast_malloc(sizeof(*cur));
2551 if (!cur)
2552 return -1;
2554 cur->action = action;
2555 cur->authority = auth;
2556 cur->func = func;
2557 cur->synopsis = synopsis;
2558 cur->description = description;
2559 cur->next = NULL;
2561 ast_manager_register_struct(cur);
2563 return 0;
2565 /*! @}
2566 END Doxygen group */
2568 static struct mansession *find_session(unsigned long ident)
2570 struct mansession *s;
2572 AST_LIST_LOCK(&sessions);
2573 AST_LIST_TRAVERSE(&sessions, s, list) {
2574 ast_mutex_lock(&s->__lock);
2575 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2576 s->inuse++;
2577 break;
2579 ast_mutex_unlock(&s->__lock);
2581 AST_LIST_UNLOCK(&sessions);
2583 return s;
2586 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2588 int result = 0;
2589 struct mansession *s;
2591 AST_LIST_LOCK(&sessions);
2592 AST_LIST_TRAVERSE(&sessions, s, list) {
2593 ast_mutex_lock(&s->__lock);
2594 if ((s->managerid == ident) && (s->readperm & perm)) {
2595 result = 1;
2596 ast_mutex_unlock(&s->__lock);
2597 break;
2599 ast_mutex_unlock(&s->__lock);
2601 AST_LIST_UNLOCK(&sessions);
2602 return result;
2605 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2607 int result = 0;
2608 struct mansession *s;
2610 AST_LIST_LOCK(&sessions);
2611 AST_LIST_TRAVERSE(&sessions, s, list) {
2612 ast_mutex_lock(&s->__lock);
2613 if ((s->managerid == ident) && (s->writeperm & perm)) {
2614 result = 1;
2615 ast_mutex_unlock(&s->__lock);
2616 break;
2618 ast_mutex_unlock(&s->__lock);
2620 AST_LIST_UNLOCK(&sessions);
2621 return result;
2624 enum {
2625 FORMAT_RAW,
2626 FORMAT_HTML,
2627 FORMAT_XML,
2629 static char *contenttype[] = { "plain", "html", "xml" };
2631 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2633 struct mansession *s = NULL;
2634 unsigned long ident = 0;
2635 char workspace[512];
2636 char cookie[128];
2637 size_t len = sizeof(workspace);
2638 int blastaway = 0;
2639 char *c = workspace;
2640 char *retval = NULL;
2641 struct ast_variable *v;
2643 for (v = params; v; v = v->next) {
2644 if (!strcasecmp(v->name, "mansession_id")) {
2645 sscanf(v->value, "%lx", &ident);
2646 break;
2650 if (!(s = find_session(ident))) {
2651 /* Create new session */
2652 if (!(s = ast_calloc(1, sizeof(*s)))) {
2653 *status = 500;
2654 goto generic_callback_out;
2656 memcpy(&s->sin, requestor, sizeof(s->sin));
2657 s->fd = -1;
2658 s->waiting_thread = AST_PTHREADT_NULL;
2659 s->send_events = 0;
2660 ast_mutex_init(&s->__lock);
2661 ast_mutex_lock(&s->__lock);
2662 s->inuse = 1;
2663 /*!\note There is approximately a 1 in 1.8E19 chance that the following
2664 * calculation will produce 0, which is an invalid ID, but due to the
2665 * properties of the rand() function (and the constantcy of s), that
2666 * won't happen twice in a row.
2668 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
2669 AST_LIST_LOCK(&sessions);
2670 AST_LIST_INSERT_HEAD(&sessions, s, list);
2671 /* Hook into the last spot in the event queue */
2672 s->eventq = master_eventq;
2673 while (s->eventq->next)
2674 s->eventq = s->eventq->next;
2675 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2676 ast_atomic_fetchadd_int(&num_sessions, 1);
2677 AST_LIST_UNLOCK(&sessions);
2680 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2681 time(&s->sessiontimeout);
2682 if (!s->authenticated && (httptimeout > 5))
2683 s->sessiontimeout += 5;
2684 else
2685 s->sessiontimeout += httptimeout;
2686 ast_mutex_unlock(&s->__lock);
2688 if (s) {
2689 struct message m = { 0 };
2690 char tmp[80];
2691 unsigned int x;
2692 size_t hdrlen;
2694 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
2695 hdrlen = strlen(v->name) + strlen(v->value) + 3;
2696 m.headers[m.hdrcount] = alloca(hdrlen);
2697 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
2698 m.hdrcount = x + 1;
2701 if (process_message(s, &m)) {
2702 if (s->authenticated) {
2703 if (option_verbose > 1) {
2704 if (displayconnects)
2705 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2707 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2708 } else {
2709 if (option_verbose > 1) {
2710 if (displayconnects)
2711 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2713 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2715 s->needdestroy = 1;
2717 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2718 sprintf(tmp, "%08lx", s->managerid);
2719 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2720 if (format == FORMAT_HTML)
2721 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
2722 if (format == FORMAT_XML) {
2723 ast_build_string(&c, &len, "<ajax-response>\n");
2724 } else if (format == FORMAT_HTML) {
2725 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2726 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2728 ast_mutex_lock(&s->__lock);
2729 if (s->outputstr) {
2730 char *tmp;
2731 if (format == FORMAT_XML)
2732 tmp = xml_translate(s->outputstr->str, params);
2733 else if (format == FORMAT_HTML)
2734 tmp = html_translate(s->outputstr->str);
2735 else
2736 tmp = s->outputstr->str;
2737 if (tmp) {
2738 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2739 if (retval) {
2740 strcpy(retval, workspace);
2741 strcpy(retval + strlen(retval), tmp);
2742 c = retval + strlen(retval);
2743 len = 120;
2746 if (tmp != s->outputstr->str)
2747 free(tmp);
2748 free(s->outputstr);
2749 s->outputstr = NULL;
2751 ast_mutex_unlock(&s->__lock);
2752 /* Still okay because c would safely be pointing to workspace even
2753 if retval failed to allocate above */
2754 if (format == FORMAT_XML) {
2755 ast_build_string(&c, &len, "</ajax-response>\n");
2756 } else if (format == FORMAT_HTML)
2757 ast_build_string(&c, &len, "</table></body>\r\n");
2758 } else {
2759 *status = 500;
2760 *title = strdup("Server Error");
2762 ast_mutex_lock(&s->__lock);
2763 if (s->needdestroy) {
2764 if (s->inuse == 1) {
2765 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2766 blastaway = 1;
2767 } else {
2768 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2769 if (s->waiting_thread != AST_PTHREADT_NULL)
2770 pthread_kill(s->waiting_thread, SIGURG);
2771 s->inuse--;
2773 } else
2774 s->inuse--;
2775 ast_mutex_unlock(&s->__lock);
2777 if (blastaway)
2778 destroy_session(s);
2779 generic_callback_out:
2780 if (*status != 200)
2781 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2782 return retval;
2785 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2787 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2790 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2792 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2795 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2797 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2800 struct ast_http_uri rawmanuri = {
2801 .description = "Raw HTTP Manager Event Interface",
2802 .uri = "rawman",
2803 .has_subtree = 0,
2804 .callback = rawman_http_callback,
2807 struct ast_http_uri manageruri = {
2808 .description = "HTML Manager Event Interface",
2809 .uri = "manager",
2810 .has_subtree = 0,
2811 .callback = manager_http_callback,
2814 struct ast_http_uri managerxmluri = {
2815 .description = "XML Manager Event Interface",
2816 .uri = "mxml",
2817 .has_subtree = 0,
2818 .callback = mxml_http_callback,
2821 static int registered = 0;
2822 static int webregged = 0;
2824 int init_manager(void)
2826 struct ast_config *cfg = NULL;
2827 const char *val;
2828 char *cat = NULL;
2829 int oldportno = portno;
2830 static struct sockaddr_in ba;
2831 int x = 1;
2832 int flags;
2833 int webenabled = 0;
2834 int newhttptimeout = 60;
2835 struct ast_manager_user *user = NULL;
2837 if (!registered) {
2838 /* Register default actions */
2839 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2840 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2841 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2842 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2843 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2844 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2845 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2846 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2847 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2848 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2849 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2850 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2851 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2852 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2853 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2854 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2855 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2856 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2857 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2859 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2860 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2861 registered = 1;
2862 /* Append placeholder event so master_eventq never runs dry */
2863 append_event("Event: Placeholder\r\n\r\n", 0);
2865 portno = DEFAULT_MANAGER_PORT;
2866 displayconnects = 1;
2867 cfg = ast_config_load("manager.conf");
2868 if (!cfg) {
2869 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2870 return 0;
2872 val = ast_variable_retrieve(cfg, "general", "enabled");
2873 if (val)
2874 enabled = ast_true(val);
2876 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2877 if (val)
2878 block_sockets = ast_true(val);
2880 val = ast_variable_retrieve(cfg, "general", "webenabled");
2881 if (val)
2882 webenabled = ast_true(val);
2884 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2885 if (sscanf(val, "%d", &portno) != 1) {
2886 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2887 portno = DEFAULT_MANAGER_PORT;
2891 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2892 displayconnects = ast_true(val);
2894 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2895 timestampevents = ast_true(val);
2897 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2898 newhttptimeout = atoi(val);
2900 memset(&ba, 0, sizeof(ba));
2901 ba.sin_family = AF_INET;
2902 ba.sin_port = htons(portno);
2904 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2905 if (!inet_aton(val, &ba.sin_addr)) {
2906 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2907 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2912 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2913 #if 0
2914 /* Can't be done yet */
2915 close(asock);
2916 asock = -1;
2917 #else
2918 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2919 #endif
2922 AST_LIST_LOCK(&users);
2924 while ((cat = ast_category_browse(cfg, cat))) {
2925 struct ast_variable *var = NULL;
2927 if (!strcasecmp(cat, "general"))
2928 continue;
2930 /* Look for an existing entry, if none found - create one and add it to the list */
2931 if (!(user = ast_get_manager_by_name_locked(cat))) {
2932 if (!(user = ast_calloc(1, sizeof(*user))))
2933 break;
2934 /* Copy name over */
2935 ast_copy_string(user->username, cat, sizeof(user->username));
2936 /* Insert into list */
2937 AST_LIST_INSERT_TAIL(&users, user, list);
2940 /* Make sure we keep this user and don't destroy it during cleanup */
2941 user->keep = 1;
2943 var = ast_variable_browse(cfg, cat);
2944 while (var) {
2945 if (!strcasecmp(var->name, "secret")) {
2946 if (user->secret)
2947 free(user->secret);
2948 user->secret = ast_strdup(var->value);
2949 } else if (!strcasecmp(var->name, "deny") ) {
2950 if (user->deny)
2951 free(user->deny);
2952 user->deny = ast_strdup(var->value);
2953 } else if (!strcasecmp(var->name, "permit") ) {
2954 if (user->permit)
2955 free(user->permit);
2956 user->permit = ast_strdup(var->value);
2957 } else if (!strcasecmp(var->name, "read") ) {
2958 if (user->read)
2959 free(user->read);
2960 user->read = ast_strdup(var->value);
2961 } else if (!strcasecmp(var->name, "write") ) {
2962 if (user->write)
2963 free(user->write);
2964 user->write = ast_strdup(var->value);
2965 } else if (!strcasecmp(var->name, "displayconnects") )
2966 user->displayconnects = ast_true(var->value);
2967 else
2968 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2969 var = var->next;
2973 /* Perform cleanup - essentially prune out old users that no longer exist */
2974 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2975 if (user->keep) {
2976 user->keep = 0;
2977 continue;
2979 /* We do not need to keep this user so take them out of the list */
2980 AST_LIST_REMOVE_CURRENT(&users, list);
2981 /* Free their memory now */
2982 if (user->secret)
2983 free(user->secret);
2984 if (user->deny)
2985 free(user->deny);
2986 if (user->permit)
2987 free(user->permit);
2988 if (user->read)
2989 free(user->read);
2990 if (user->write)
2991 free(user->write);
2992 free(user);
2994 AST_LIST_TRAVERSE_SAFE_END
2996 AST_LIST_UNLOCK(&users);
2998 ast_config_destroy(cfg);
3000 if (webenabled && enabled) {
3001 if (!webregged) {
3002 ast_http_uri_link(&rawmanuri);
3003 ast_http_uri_link(&manageruri);
3004 ast_http_uri_link(&managerxmluri);
3005 webregged = 1;
3007 } else {
3008 if (webregged) {
3009 ast_http_uri_unlink(&rawmanuri);
3010 ast_http_uri_unlink(&manageruri);
3011 ast_http_uri_unlink(&managerxmluri);
3012 webregged = 0;
3016 if (newhttptimeout > 0)
3017 httptimeout = newhttptimeout;
3019 /* If not enabled, do nothing */
3020 if (!enabled)
3021 return 0;
3023 if (asock < 0) {
3024 asock = socket(AF_INET, SOCK_STREAM, 0);
3025 if (asock < 0) {
3026 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
3027 return -1;
3029 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
3030 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
3031 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
3032 close(asock);
3033 asock = -1;
3034 return -1;
3036 if (listen(asock, 2)) {
3037 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
3038 close(asock);
3039 asock = -1;
3040 return -1;
3042 flags = fcntl(asock, F_GETFL);
3043 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
3044 if (option_verbose)
3045 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
3046 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
3048 return 0;
3051 int reload_manager(void)
3053 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3054 return init_manager();