remove the dlfcn compatibility stuff, because no platforms that Asterisk currently...
[asterisk-bristuff.git] / main / manager.c
blob177e3e8f1edd24f359fca1f963196d57043d0c3e
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 #define MAX_BLACKLIST_CMD_LEN 2
134 static struct {
135 char *words[AST_MAX_CMD_LEN];
136 } command_blacklist[] = {
137 {{ "module", "load", NULL }},
138 {{ "module", "unload", NULL }},
141 struct mansession {
142 /*! Execution thread */
143 pthread_t t;
144 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
145 ast_mutex_t __lock;
146 /*! socket address */
147 struct sockaddr_in sin;
148 /*! TCP socket */
149 int fd;
150 /*! Whether an HTTP manager is in use */
151 int inuse;
152 /*! Whether an HTTP session should be destroyed */
153 int needdestroy;
154 /*! Whether an HTTP session has someone waiting on events */
155 pthread_t waiting_thread;
156 /*! Unique manager identifer */
157 uint32_t managerid;
158 /*! Session timeout if HTTP */
159 time_t sessiontimeout;
160 /*! Output from manager interface */
161 struct ast_dynamic_str *outputstr;
162 /*! Logged in username */
163 char username[80];
164 /*! Authentication challenge */
165 char challenge[10];
166 /*! Authentication status */
167 int authenticated;
168 /*! Authorization for reading */
169 int readperm;
170 /*! Authorization for writing */
171 int writeperm;
172 /*! Buffer */
173 char inbuf[1024];
174 int inlen;
175 int send_events;
176 int displaysystemname; /*!< Add system name to manager responses and events */
177 /* Queued events that we've not had the ability to send yet */
178 struct eventqent *eventq;
179 /* Timeout for ast_carefulwrite() */
180 int writetimeout;
181 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
182 AST_LIST_ENTRY(mansession) list;
185 static AST_LIST_HEAD_STATIC(sessions, mansession);
187 struct ast_manager_user {
188 char username[80];
189 char *secret;
190 char *deny;
191 char *permit;
192 char *read;
193 char *write;
194 unsigned int displayconnects:1;
195 int keep;
196 AST_LIST_ENTRY(ast_manager_user) list;
199 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
201 static struct manager_action *first_action;
202 AST_RWLOCK_DEFINE_STATIC(actionlock);
204 /*! \brief Convert authority code to string with serveral options */
205 static char *authority_to_str(int authority, char *res, int reslen)
207 int running_total = 0, i;
209 memset(res, 0, reslen);
210 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
211 if (authority & perms[i].num) {
212 if (*res) {
213 strncat(res, ",", (reslen > running_total) ? reslen - running_total - 1 : 0);
214 running_total++;
216 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total - 1 : 0);
217 running_total += strlen(perms[i].label);
221 if (ast_strlen_zero(res))
222 ast_copy_string(res, "<none>", reslen);
224 return res;
227 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
229 struct manager_action *cur;
230 int which = 0;
231 char *ret = NULL;
233 ast_rwlock_rdlock(&actionlock);
234 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
235 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
236 ret = ast_strdup(cur->action);
237 break; /* make sure we exit even if ast_strdup() returns NULL */
240 ast_rwlock_unlock(&actionlock);
242 return ret;
245 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
247 while (*src && (*maxlen > 6)) {
248 switch (*src) {
249 case '<':
250 strcpy(*dst, "&lt;");
251 (*dst) += 4;
252 *maxlen -= 4;
253 break;
254 case '>':
255 strcpy(*dst, "&gt;");
256 (*dst) += 4;
257 *maxlen -= 4;
258 break;
259 case '\"':
260 strcpy(*dst, "&quot;");
261 (*dst) += 6;
262 *maxlen -= 6;
263 break;
264 case '\'':
265 strcpy(*dst, "&apos;");
266 (*dst) += 6;
267 *maxlen -= 6;
268 break;
269 case '&':
270 strcpy(*dst, "&amp;");
271 (*dst) += 5;
272 *maxlen -= 5;
273 break;
274 default:
275 *(*dst)++ = lower ? tolower(*src) : *src;
276 (*maxlen)--;
278 src++;
282 struct variable_count {
283 char *varname;
284 int count;
287 static int compress_char(char c)
289 c &= 0x7f;
290 if (c < 32)
291 return 0;
292 else if (c >= 'a' && c <= 'z')
293 return c - 64;
294 else if (c > 'z')
295 return '_';
296 else
297 return c - 32;
300 static int variable_count_hash_fn(const void *vvc, const int flags)
302 const struct variable_count *vc = vvc;
303 int res = 0, i;
304 for (i = 0; i < 5; i++) {
305 if (vc->varname[i] == '\0')
306 break;
307 res += compress_char(vc->varname[i]) << (i * 6);
309 return res;
312 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
314 /* Due to the simplicity of struct variable_count, it makes no difference
315 * if you pass in objects or strings, the same operation applies. This is
316 * due to the fact that the hash occurs on the first element, which means
317 * the address of both the struct and the string are exactly the same. */
318 struct variable_count *vc = obj;
319 char *str = vstr;
320 return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
323 static char *xml_translate(char *in, struct ast_variable *vars)
325 struct ast_variable *v;
326 char *dest = NULL;
327 char *out, *tmp, *var, *val;
328 char *objtype = NULL;
329 int colons = 0;
330 int breaks = 0;
331 size_t len;
332 int count = 1;
333 int escaped = 0;
334 int inobj = 0;
335 int x;
336 struct variable_count *vc = NULL;
337 struct ao2_container *vco = NULL;
339 for (v = vars; v; v = v->next) {
340 if (!dest && !strcasecmp(v->name, "ajaxdest"))
341 dest = v->value;
342 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
343 objtype = v->value;
345 if (!dest)
346 dest = "unknown";
347 if (!objtype)
348 objtype = "generic";
349 for (x = 0; in[x]; x++) {
350 if (in[x] == ':')
351 colons++;
352 else if (in[x] == '\n')
353 breaks++;
354 else if (strchr("&\"<>\'", in[x]))
355 escaped++;
357 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
358 out = ast_malloc(len);
359 if (!out)
360 return 0;
361 tmp = out;
362 while (*in) {
363 var = in;
364 while (*in && (*in >= 32))
365 in++;
366 if (*in) {
367 if ((count > 3) && inobj) {
368 ast_build_string(&tmp, &len, " /></response>\n");
369 inobj = 0;
371 /* Entity is closed, so close out the name cache */
372 ao2_ref(vco, -1);
373 vco = NULL;
375 count = 0;
376 while (*in && (*in < 32)) {
377 *in = '\0';
378 in++;
379 count++;
381 val = strchr(var, ':');
382 if (val) {
383 *val = '\0';
384 val++;
385 if (*val == ' ')
386 val++;
387 if (!inobj) {
388 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
389 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
390 inobj = 1;
393 /* Check if the var has been used already */
394 if ((vc = ao2_find(vco, var, 0)))
395 vc->count++;
396 else {
397 /* Create a new entry for this one */
398 vc = ao2_alloc(sizeof(*vc), NULL);
399 vc->varname = var;
400 vc->count = 1;
401 ao2_link(vco, vc);
404 ast_build_string(&tmp, &len, " ");
405 xml_copy_escape(&tmp, &len, var, 1);
406 if (vc->count > 1)
407 ast_build_string(&tmp, &len, "-%d", vc->count);
408 ast_build_string(&tmp, &len, "='");
409 xml_copy_escape(&tmp, &len, val, 0);
410 ast_build_string(&tmp, &len, "'");
411 ao2_ref(vc, -1);
415 if (inobj)
416 ast_build_string(&tmp, &len, " /></response>\n");
417 if (vco)
418 ao2_ref(vco, -1);
419 return out;
422 static char *html_translate(char *in)
424 int x;
425 int colons = 0;
426 int breaks = 0;
427 size_t len;
428 int count = 1;
429 char *tmp, *var, *val, *out;
431 for (x=0; in[x]; x++) {
432 if (in[x] == ':')
433 colons++;
434 if (in[x] == '\n')
435 breaks++;
437 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
438 out = ast_malloc(len);
439 if (!out)
440 return 0;
441 tmp = out;
442 while (*in) {
443 var = in;
444 while (*in && (*in >= 32))
445 in++;
446 if (*in) {
447 if ((count % 4) == 0){
448 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
450 count = 0;
451 while (*in && (*in < 32)) {
452 *in = '\0';
453 in++;
454 count++;
456 val = strchr(var, ':');
457 if (val) {
458 *val = '\0';
459 val++;
460 if (*val == ' ')
461 val++;
462 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
466 return out;
471 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
473 struct ast_manager_user *user = NULL;
475 AST_LIST_TRAVERSE(&users, user, list)
476 if (!strcasecmp(user->username, name))
477 break;
478 return user;
481 void astman_append(struct mansession *s, const char *fmt, ...)
483 va_list ap;
484 struct ast_dynamic_str *buf;
486 ast_mutex_lock(&s->__lock);
488 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
489 ast_mutex_unlock(&s->__lock);
490 return;
493 va_start(ap, fmt);
494 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
495 va_end(ap);
497 if (s->fd > -1)
498 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
499 else {
500 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) {
501 ast_mutex_unlock(&s->__lock);
502 return;
505 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
508 ast_mutex_unlock(&s->__lock);
511 static int handle_showmancmd(int fd, int argc, char *argv[])
513 struct manager_action *cur;
514 char authority[80];
515 int num;
517 if (argc != 4)
518 return RESULT_SHOWUSAGE;
520 ast_rwlock_rdlock(&actionlock);
521 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
522 for (num = 3; num < argc; num++) {
523 if (!strcasecmp(cur->action, argv[num])) {
524 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 : "");
528 ast_rwlock_unlock(&actionlock);
530 return RESULT_SUCCESS;
533 static int handle_showmanager(int fd, int argc, char *argv[])
535 struct ast_manager_user *user = NULL;
537 if (argc != 4)
538 return RESULT_SHOWUSAGE;
540 AST_LIST_LOCK(&users);
542 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
543 ast_cli(fd, "There is no manager called %s\n", argv[3]);
544 AST_LIST_UNLOCK(&users);
545 return -1;
548 ast_cli(fd,"\n");
549 ast_cli(fd,
550 " username: %s\n"
551 " secret: %s\n"
552 " deny: %s\n"
553 " permit: %s\n"
554 " read: %s\n"
555 " write: %s\n"
556 "displayconnects: %s\n",
557 (user->username ? user->username : "(N/A)"),
558 (user->secret ? "<Set>" : "(N/A)"),
559 (user->deny ? user->deny : "(N/A)"),
560 (user->permit ? user->permit : "(N/A)"),
561 (user->read ? user->read : "(N/A)"),
562 (user->write ? user->write : "(N/A)"),
563 (user->displayconnects ? "yes" : "no"));
565 AST_LIST_UNLOCK(&users);
567 return RESULT_SUCCESS;
571 static int handle_showmanagers(int fd, int argc, char *argv[])
573 struct ast_manager_user *user = NULL;
574 int count_amu = 0;
576 if (argc != 3)
577 return RESULT_SHOWUSAGE;
579 AST_LIST_LOCK(&users);
581 /* If there are no users, print out something along those lines */
582 if (AST_LIST_EMPTY(&users)) {
583 ast_cli(fd, "There are no manager users.\n");
584 AST_LIST_UNLOCK(&users);
585 return RESULT_SUCCESS;
588 ast_cli(fd, "\nusername\n--------\n");
590 AST_LIST_TRAVERSE(&users, user, list) {
591 ast_cli(fd, "%s\n", user->username);
592 count_amu++;
595 AST_LIST_UNLOCK(&users);
597 ast_cli(fd,"-------------------\n");
598 ast_cli(fd,"%d manager users configured.\n", count_amu);
600 return RESULT_SUCCESS;
604 /*! \brief CLI command
605 Should change to "manager show commands" */
606 static int handle_showmancmds(int fd, int argc, char *argv[])
608 struct manager_action *cur;
609 char authority[80];
610 char *format = " %-15.15s %-15.15s %-55.55s\n";
612 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
613 ast_cli(fd, format, "------", "---------", "--------");
615 ast_rwlock_rdlock(&actionlock);
616 for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
617 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
618 ast_rwlock_unlock(&actionlock);
620 return RESULT_SUCCESS;
623 /*! \brief CLI command show manager connected */
624 /* Should change to "manager show connected" */
625 static int handle_showmanconn(int fd, int argc, char *argv[])
627 struct mansession *s;
628 char *format = " %-15.15s %-15.15s\n";
630 ast_cli(fd, format, "Username", "IP Address");
632 AST_LIST_LOCK(&sessions);
633 AST_LIST_TRAVERSE(&sessions, s, list)
634 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
635 AST_LIST_UNLOCK(&sessions);
637 return RESULT_SUCCESS;
640 /*! \brief CLI command show manager connected */
641 /* Should change to "manager show connected" */
642 static int handle_showmaneventq(int fd, int argc, char *argv[])
644 struct eventqent *s;
646 AST_LIST_LOCK(&sessions);
647 for (s = master_eventq; s; s = s->next) {
648 ast_cli(fd, "Usecount: %d\n",s->usecount);
649 ast_cli(fd, "Category: %d\n", s->category);
650 ast_cli(fd, "Event:\n%s", s->eventdata);
652 AST_LIST_UNLOCK(&sessions);
654 return RESULT_SUCCESS;
657 static char showmancmd_help[] =
658 "Usage: manager show command <actionname>\n"
659 " Shows the detailed description for a specific Asterisk manager interface command.\n";
661 static char showmancmds_help[] =
662 "Usage: manager show commands\n"
663 " Prints a listing of all the available Asterisk manager interface commands.\n";
665 static char showmanconn_help[] =
666 "Usage: manager show connected\n"
667 " Prints a listing of the users that are currently connected to the\n"
668 "Asterisk manager interface.\n";
670 static char showmaneventq_help[] =
671 "Usage: manager show eventq\n"
672 " Prints a listing of all events pending in the Asterisk manger\n"
673 "event queue.\n";
675 static char showmanagers_help[] =
676 "Usage: manager show users\n"
677 " Prints a listing of all managers that are currently configured on that\n"
678 " system.\n";
680 static char showmanager_help[] =
681 " Usage: manager show user <user>\n"
682 " Display all information related to the manager user specified.\n";
684 static struct ast_cli_entry cli_show_manager_command_deprecated = {
685 { "show", "manager", "command", NULL },
686 handle_showmancmd, NULL,
687 NULL, complete_show_mancmd };
689 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
690 { "show", "manager", "commands", NULL },
691 handle_showmancmds, NULL,
692 NULL };
694 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
695 { "show", "manager", "connected", NULL },
696 handle_showmanconn, NULL,
697 NULL };
699 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
700 { "show", "manager", "eventq", NULL },
701 handle_showmaneventq, NULL,
702 NULL };
704 static struct ast_cli_entry cli_manager[] = {
705 { { "manager", "show", "command", NULL },
706 handle_showmancmd, "Show a manager interface command",
707 showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
709 { { "manager", "show", "commands", NULL },
710 handle_showmancmds, "List manager interface commands",
711 showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
713 { { "manager", "show", "connected", NULL },
714 handle_showmanconn, "List connected manager interface users",
715 showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
717 { { "manager", "show", "eventq", NULL },
718 handle_showmaneventq, "List manager interface queued events",
719 showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
721 { { "manager", "show", "users", NULL },
722 handle_showmanagers, "List configured manager users",
723 showmanagers_help, NULL, NULL },
725 { { "manager", "show", "user", NULL },
726 handle_showmanager, "Display information on a specific manager user",
727 showmanager_help, NULL, NULL },
730 static void unuse_eventqent(struct eventqent *e)
732 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
733 pthread_kill(t, SIGURG);
736 static void free_session(struct mansession *s)
738 struct eventqent *eqe;
739 if (s->fd > -1)
740 close(s->fd);
741 if (s->outputstr)
742 free(s->outputstr);
743 ast_mutex_destroy(&s->__lock);
744 while (s->eventq) {
745 eqe = s->eventq;
746 s->eventq = s->eventq->next;
747 unuse_eventqent(eqe);
749 free(s);
752 static void destroy_session(struct mansession *s)
754 AST_LIST_LOCK(&sessions);
755 AST_LIST_REMOVE(&sessions, s, list);
756 num_sessions--;
757 free_session(s);
758 AST_LIST_UNLOCK(&sessions);
761 const char *astman_get_header(const struct message *m, char *var)
763 char cmp[80];
764 int x;
766 snprintf(cmp, sizeof(cmp), "%s: ", var);
768 for (x = 0; x < m->hdrcount; x++) {
769 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
770 return m->headers[x] + strlen(cmp);
773 return "";
776 struct ast_variable *astman_get_variables(const struct message *m)
778 int varlen, x, y;
779 struct ast_variable *head = NULL, *cur;
780 char *var, *val;
782 char *parse;
783 AST_DECLARE_APP_ARGS(args,
784 AST_APP_ARG(vars)[32];
787 varlen = strlen("Variable: ");
789 for (x = 0; x < m->hdrcount; x++) {
790 if (strncasecmp("Variable: ", m->headers[x], varlen))
791 continue;
793 parse = ast_strdupa(m->headers[x] + varlen);
795 AST_STANDARD_APP_ARGS(args, parse);
796 if (args.argc) {
797 for (y = 0; y < args.argc; y++) {
798 if (!args.vars[y])
799 continue;
800 var = val = ast_strdupa(args.vars[y]);
801 strsep(&val, "=");
802 if (!val || ast_strlen_zero(var))
803 continue;
804 cur = ast_variable_new(var, val);
805 if (head) {
806 cur->next = head;
807 head = cur;
808 } else
809 head = cur;
814 return head;
817 /*! \note NOTE:
818 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
819 hold the session lock _or_ be running in an action callback (in which case s->busy will
820 be non-zero). In either of these cases, there is no need to lock-protect the session's
821 fd, since no other output will be sent (events will be queued), and no input will
822 be read until either the current action finishes or get_input() obtains the session
823 lock.
825 void astman_send_error(struct mansession *s, const struct message *m, char *error)
827 const char *id = astman_get_header(m,"ActionID");
829 astman_append(s, "Response: Error\r\n");
830 if (!ast_strlen_zero(id))
831 astman_append(s, "ActionID: %s\r\n", id);
832 astman_append(s, "Message: %s\r\n\r\n", error);
835 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
837 const char *id = astman_get_header(m,"ActionID");
839 astman_append(s, "Response: %s\r\n", resp);
840 if (!ast_strlen_zero(id))
841 astman_append(s, "ActionID: %s\r\n", id);
842 if (msg)
843 astman_append(s, "Message: %s\r\n\r\n", msg);
844 else
845 astman_append(s, "\r\n");
848 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
850 astman_send_response(s, m, "Success", msg);
853 /*! Tells you if smallstr exists inside bigstr
854 which is delim by delim and uses no buf or stringsep
855 ast_instring("this|that|more","this",',') == 1;
857 feel free to move this to app.c -anthm */
858 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
860 const char *val = bigstr, *next;
862 do {
863 if ((next = strchr(val, delim))) {
864 if (!strncmp(val, smallstr, (next - val)))
865 return 1;
866 else
867 continue;
868 } else
869 return !strcmp(smallstr, val);
871 } while (*(val = (next + 1)));
873 return 0;
876 static int get_perm(const char *instr)
878 int x = 0, ret = 0;
880 if (!instr)
881 return 0;
883 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
884 if (ast_instring(instr, perms[x].label, ','))
885 ret |= perms[x].num;
888 return ret;
891 static int ast_is_number(const char *string)
893 int ret = 1, x = 0;
895 if (!string)
896 return 0;
898 for (x = 0; x < strlen(string); x++) {
899 if (!(string[x] >= 48 && string[x] <= 57)) {
900 ret = 0;
901 break;
905 return ret ? atoi(string) : 0;
908 static int strings_to_mask(const char *string)
910 int x, ret = -1;
912 x = ast_is_number(string);
914 if (x)
915 ret = x;
916 else if (ast_strlen_zero(string))
917 ret = -1;
918 else if (ast_false(string))
919 ret = 0;
920 else if (ast_true(string)) {
921 ret = 0;
922 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
923 ret |= perms[x].num;
924 } else {
925 ret = 0;
926 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
927 if (ast_instring(string, perms[x].label, ','))
928 ret |= perms[x].num;
932 return ret;
935 /*! \brief
936 Rather than braindead on,off this now can also accept a specific int mask value
937 or a ',' delim list of mask strings (the same as manager.conf) -anthm
939 static int set_eventmask(struct mansession *s, const char *eventmask)
941 int maskint = strings_to_mask(eventmask);
943 ast_mutex_lock(&s->__lock);
944 if (maskint >= 0)
945 s->send_events = maskint;
946 ast_mutex_unlock(&s->__lock);
948 return maskint;
951 static int authenticate(struct mansession *s, const struct message *m)
953 struct ast_config *cfg;
954 char *cat;
955 const char *user = astman_get_header(m, "Username");
956 const char *pass = astman_get_header(m, "Secret");
957 const char *authtype = astman_get_header(m, "AuthType");
958 const char *key = astman_get_header(m, "Key");
959 const char *events = astman_get_header(m, "Events");
961 cfg = ast_config_load("manager.conf");
962 if (!cfg)
963 return -1;
964 cat = ast_category_browse(cfg, NULL);
965 while (cat) {
966 if (strcasecmp(cat, "general")) {
967 /* This is a user */
968 if (!strcasecmp(cat, user)) {
969 struct ast_variable *v;
970 struct ast_ha *ha = NULL;
971 char *password = NULL;
973 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
974 if (!strcasecmp(v->name, "secret")) {
975 password = v->value;
976 } else if (!strcasecmp(v->name, "displaysystemname")) {
977 if (ast_true(v->value)) {
978 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
979 s->displaysystemname = 1;
980 } else {
981 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
984 } else if (!strcasecmp(v->name, "permit") ||
985 !strcasecmp(v->name, "deny")) {
986 ha = ast_append_ha(v->name, v->value, ha);
987 } else if (!strcasecmp(v->name, "writetimeout")) {
988 int val = atoi(v->value);
990 if (val < 100)
991 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
992 else
993 s->writetimeout = val;
997 if (ha && !ast_apply_ha(ha, &(s->sin))) {
998 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
999 ast_free_ha(ha);
1000 ast_config_destroy(cfg);
1001 return -1;
1002 } else if (ha)
1003 ast_free_ha(ha);
1004 if (!strcasecmp(authtype, "MD5")) {
1005 if (!ast_strlen_zero(key) &&
1006 !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
1007 int x;
1008 int len = 0;
1009 char md5key[256] = "";
1010 struct MD5Context md5;
1011 unsigned char digest[16];
1012 MD5Init(&md5);
1013 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1014 MD5Update(&md5, (unsigned char *) password, strlen(password));
1015 MD5Final(digest, &md5);
1016 for (x=0; x<16; x++)
1017 len += sprintf(md5key + len, "%2.2x", digest[x]);
1018 if (!strcmp(md5key, key))
1019 break;
1020 else {
1021 ast_config_destroy(cfg);
1022 return -1;
1024 } else {
1025 ast_log(LOG_DEBUG, "MD5 authentication is not possible. challenge: '%s'\n",
1026 S_OR(s->challenge, ""));
1027 ast_config_destroy(cfg);
1028 return -1;
1030 } else if (password && !strcmp(password, pass)) {
1031 break;
1032 } else {
1033 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1034 ast_config_destroy(cfg);
1035 return -1;
1039 cat = ast_category_browse(cfg, cat);
1041 if (cat) {
1042 ast_copy_string(s->username, cat, sizeof(s->username));
1043 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
1044 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
1045 ast_config_destroy(cfg);
1046 if (events)
1047 set_eventmask(s, events);
1048 return 0;
1050 ast_config_destroy(cfg);
1051 cfg = ast_config_load("users.conf");
1052 if (!cfg)
1053 return -1;
1054 cat = ast_category_browse(cfg, NULL);
1055 while (cat) {
1056 struct ast_variable *v;
1057 const char *password = NULL;
1058 int hasmanager = 0;
1059 const char *readperms = NULL;
1060 const char *writeperms = NULL;
1062 if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
1063 cat = ast_category_browse(cfg, cat);
1064 continue;
1066 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
1067 if (!strcasecmp(v->name, "secret"))
1068 password = v->value;
1069 else if (!strcasecmp(v->name, "hasmanager"))
1070 hasmanager = ast_true(v->value);
1071 else if (!strcasecmp(v->name, "managerread"))
1072 readperms = v->value;
1073 else if (!strcasecmp(v->name, "managerwrite"))
1074 writeperms = v->value;
1076 if (!hasmanager)
1077 break;
1078 if (!password || strcmp(password, pass)) {
1079 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1080 ast_config_destroy(cfg);
1081 return -1;
1083 ast_copy_string(s->username, cat, sizeof(s->username));
1084 s->readperm = readperms ? get_perm(readperms) : -1;
1085 s->writeperm = writeperms ? get_perm(writeperms) : -1;
1086 ast_config_destroy(cfg);
1087 if (events)
1088 set_eventmask(s, events);
1089 return 0;
1091 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1092 ast_config_destroy(cfg);
1093 return -1;
1096 /*! \brief Manager PING */
1097 static char mandescr_ping[] =
1098 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1099 " manager connection open.\n"
1100 "Variables: NONE\n";
1102 static int action_ping(struct mansession *s, const struct message *m)
1104 astman_send_response(s, m, "Pong", NULL);
1105 return 0;
1108 static char mandescr_getconfig[] =
1109 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1110 "file by category and contents.\n"
1111 "Variables:\n"
1112 " Filename: Configuration filename (e.g. foo.conf)\n";
1114 static int action_getconfig(struct mansession *s, const struct message *m)
1116 struct ast_config *cfg;
1117 const char *fn = astman_get_header(m, "Filename");
1118 int catcount = 0;
1119 int lineno = 0;
1120 char *category=NULL;
1121 struct ast_variable *v;
1122 char idText[256] = "";
1123 const char *id = astman_get_header(m, "ActionID");
1125 if (!ast_strlen_zero(id))
1126 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1128 if (ast_strlen_zero(fn)) {
1129 astman_send_error(s, m, "Filename not specified");
1130 return 0;
1132 if (!(cfg = ast_config_load_with_comments(fn))) {
1133 astman_send_error(s, m, "Config file not found");
1134 return 0;
1136 astman_append(s, "Response: Success\r\n%s", idText);
1137 while ((category = ast_category_browse(cfg, category))) {
1138 lineno = 0;
1139 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1140 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1141 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1142 catcount++;
1144 ast_config_destroy(cfg);
1145 astman_append(s, "\r\n");
1147 return 0;
1151 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
1153 int x;
1154 char hdr[40];
1155 const char *action, *cat, *var, *value, *match;
1156 struct ast_category *category;
1157 struct ast_variable *v;
1159 for (x=0;x<100000;x++) {
1160 unsigned int object = 0;
1162 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1163 action = astman_get_header(m, hdr);
1164 if (ast_strlen_zero(action))
1165 break;
1166 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1167 cat = astman_get_header(m, hdr);
1168 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1169 var = astman_get_header(m, hdr);
1170 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1171 value = astman_get_header(m, hdr);
1172 if (!ast_strlen_zero(value) && *value == '>') {
1173 object = 1;
1174 value++;
1176 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1177 match = astman_get_header(m, hdr);
1178 if (!strcasecmp(action, "newcat")) {
1179 if (!ast_strlen_zero(cat)) {
1180 category = ast_category_new(cat);
1181 if (category) {
1182 ast_category_append(cfg, category);
1185 } else if (!strcasecmp(action, "renamecat")) {
1186 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1187 category = ast_category_get(cfg, cat);
1188 if (category)
1189 ast_category_rename(category, value);
1191 } else if (!strcasecmp(action, "delcat")) {
1192 if (!ast_strlen_zero(cat))
1193 ast_category_delete(cfg, (char *) cat);
1194 } else if (!strcasecmp(action, "update")) {
1195 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1196 ast_variable_update(category, var, value, match, object);
1197 } else if (!strcasecmp(action, "delete")) {
1198 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1199 ast_variable_delete(category, (char *) var, (char *) match);
1200 } else if (!strcasecmp(action, "append")) {
1201 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1202 (category = ast_category_get(cfg, cat)) &&
1203 (v = ast_variable_new(var, value))){
1204 if (object || (match && !strcasecmp(match, "object")))
1205 v->object = 1;
1206 ast_variable_append(category, v);
1212 static char mandescr_updateconfig[] =
1213 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1214 "configuration elements in Asterisk configuration files.\n"
1215 "Variables (X's represent 6 digit number beginning with 000000):\n"
1216 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1217 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1218 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1219 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1220 " Cat-XXXXXX: Category to operate on\n"
1221 " Var-XXXXXX: Variable to work on\n"
1222 " Value-XXXXXX: Value to work on\n"
1223 " Match-XXXXXX: Extra match required to match line\n";
1225 static int action_updateconfig(struct mansession *s, const struct message *m)
1227 struct ast_config *cfg;
1228 const char *sfn = astman_get_header(m, "SrcFilename");
1229 const char *dfn = astman_get_header(m, "DstFilename");
1230 int res;
1231 char idText[256] = "";
1232 const char *id = astman_get_header(m, "ActionID");
1233 const char *rld = astman_get_header(m, "Reload");
1235 if (!ast_strlen_zero(id))
1236 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1238 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1239 astman_send_error(s, m, "Filename not specified");
1240 return 0;
1242 if (!(cfg = ast_config_load_with_comments(sfn))) {
1243 astman_send_error(s, m, "Config file not found");
1244 return 0;
1246 handle_updates(s, m, cfg);
1247 res = config_text_file_save(dfn, cfg, "Manager");
1248 ast_config_destroy(cfg);
1249 if (res) {
1250 astman_send_error(s, m, "Save of config failed");
1251 return 0;
1253 astman_append(s, "Response: Success\r\n%s\r\n", idText);
1254 if (!ast_strlen_zero(rld)) {
1255 if (ast_true(rld))
1256 rld = NULL;
1257 ast_module_reload(rld);
1259 return 0;
1262 /*! \brief Manager WAITEVENT */
1263 static char mandescr_waitevent[] =
1264 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1265 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1266 "session, events will be generated and queued.\n"
1267 "Variables: \n"
1268 " Timeout: Maximum time to wait for events\n";
1270 static int action_waitevent(struct mansession *s, const struct message *m)
1272 const char *timeouts = astman_get_header(m, "Timeout");
1273 int timeout = -1, max;
1274 int x;
1275 int needexit = 0;
1276 time_t now;
1277 struct eventqent *eqe;
1278 const char *id = astman_get_header(m,"ActionID");
1279 char idText[256] = "";
1281 if (!ast_strlen_zero(id))
1282 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1284 if (!ast_strlen_zero(timeouts)) {
1285 sscanf(timeouts, "%i", &timeout);
1288 ast_mutex_lock(&s->__lock);
1289 if (s->waiting_thread != AST_PTHREADT_NULL) {
1290 pthread_kill(s->waiting_thread, SIGURG);
1292 if (s->sessiontimeout) {
1293 time(&now);
1294 max = s->sessiontimeout - now - 10;
1295 if (max < 0)
1296 max = 0;
1297 if ((timeout < 0) || (timeout > max))
1298 timeout = max;
1299 if (!s->send_events)
1300 s->send_events = -1;
1301 /* Once waitevent is called, always queue events from now on */
1303 ast_mutex_unlock(&s->__lock);
1304 s->waiting_thread = pthread_self();
1305 if (option_debug)
1306 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1307 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1308 ast_mutex_lock(&s->__lock);
1309 if (s->eventq && s->eventq->next)
1310 needexit = 1;
1311 if (s->waiting_thread != pthread_self())
1312 needexit = 1;
1313 if (s->needdestroy)
1314 needexit = 1;
1315 ast_mutex_unlock(&s->__lock);
1316 if (needexit)
1317 break;
1318 if (s->fd > 0) {
1319 if (ast_wait_for_input(s->fd, 1000))
1320 break;
1321 } else {
1322 sleep(1);
1325 if (option_debug)
1326 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1327 ast_mutex_lock(&s->__lock);
1328 if (s->waiting_thread == pthread_self()) {
1329 astman_send_response(s, m, "Success", "Waiting for Event...");
1330 /* Only show events if we're the most recent waiter */
1331 while(s->eventq->next) {
1332 eqe = s->eventq->next;
1333 if (((s->readperm & eqe->category) == eqe->category) &&
1334 ((s->send_events & eqe->category) == eqe->category)) {
1335 astman_append(s, "%s", eqe->eventdata);
1337 unuse_eventqent(s->eventq);
1338 s->eventq = eqe;
1340 astman_append(s,
1341 "Event: WaitEventComplete\r\n"
1342 "%s"
1343 "\r\n", idText);
1344 s->waiting_thread = AST_PTHREADT_NULL;
1345 } else {
1346 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1348 ast_mutex_unlock(&s->__lock);
1349 return 0;
1352 static char mandescr_listcommands[] =
1353 "Description: Returns the action name and synopsis for every\n"
1354 " action that is available to the user\n"
1355 "Variables: NONE\n";
1357 /*! \note The actionlock is read-locked by the caller of this function */
1358 static int action_listcommands(struct mansession *s, const struct message *m)
1360 struct manager_action *cur;
1361 char idText[256] = "";
1362 char temp[BUFSIZ];
1363 const char *id = astman_get_header(m,"ActionID");
1365 if (!ast_strlen_zero(id))
1366 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1367 astman_append(s, "Response: Success\r\n%s", idText);
1368 for (cur = first_action; cur; cur = cur->next) {
1369 if ((s->writeperm & cur->authority) == cur->authority)
1370 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1372 astman_append(s, "\r\n");
1374 return 0;
1377 static char mandescr_events[] =
1378 "Description: Enable/Disable sending of events to this manager\n"
1379 " client.\n"
1380 "Variables:\n"
1381 " EventMask: 'on' if all events should be sent,\n"
1382 " 'off' if no events should be sent,\n"
1383 " 'system,call,log' to select which flags events should have to be sent.\n";
1385 static int action_events(struct mansession *s, const struct message *m)
1387 const char *mask = astman_get_header(m, "EventMask");
1388 int res;
1390 res = set_eventmask(s, mask);
1391 if (res > 0)
1392 astman_send_response(s, m, "Events On", NULL);
1393 else if (res == 0)
1394 astman_send_response(s, m, "Events Off", NULL);
1396 return 0;
1399 static char mandescr_logoff[] =
1400 "Description: Logoff this manager session\n"
1401 "Variables: NONE\n";
1403 static int action_logoff(struct mansession *s, const struct message *m)
1405 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1406 return -1;
1409 static char mandescr_hangup[] =
1410 "Description: Hangup a channel\n"
1411 "Variables: \n"
1412 " Channel: The channel name to be hungup\n";
1414 static int action_hangup(struct mansession *s, const struct message *m)
1416 struct ast_channel *c = NULL;
1417 const char *name = astman_get_header(m, "Channel");
1418 if (ast_strlen_zero(name)) {
1419 astman_send_error(s, m, "No channel specified");
1420 return 0;
1422 c = ast_get_channel_by_name_locked(name);
1423 if (!c) {
1424 astman_send_error(s, m, "No such channel");
1425 return 0;
1427 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1428 ast_channel_unlock(c);
1429 astman_send_ack(s, m, "Channel Hungup");
1430 return 0;
1433 static char mandescr_setvar[] =
1434 "Description: Set a global or local channel variable.\n"
1435 "Variables: (Names marked with * are required)\n"
1436 " Channel: Channel to set variable for\n"
1437 " *Variable: Variable name\n"
1438 " *Value: Value\n";
1440 static int action_setvar(struct mansession *s, const struct message *m)
1442 struct ast_channel *c = NULL;
1443 const char *name = astman_get_header(m, "Channel");
1444 const char *varname = astman_get_header(m, "Variable");
1445 const char *varval = astman_get_header(m, "Value");
1447 if (ast_strlen_zero(varname)) {
1448 astman_send_error(s, m, "No variable specified");
1449 return 0;
1452 if (!ast_strlen_zero(name)) {
1453 c = ast_get_channel_by_name_locked(name);
1454 if (!c) {
1455 astman_send_error(s, m, "No such channel");
1456 return 0;
1460 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1462 if (c)
1463 ast_channel_unlock(c);
1465 astman_send_ack(s, m, "Variable Set");
1467 return 0;
1470 static char mandescr_getvar[] =
1471 "Description: Get the value of a global or local channel variable.\n"
1472 "Variables: (Names marked with * are required)\n"
1473 " Channel: Channel to read variable from\n"
1474 " *Variable: Variable name\n"
1475 " ActionID: Optional Action id for message matching.\n";
1477 static int action_getvar(struct mansession *s, const struct message *m)
1479 struct ast_channel *c = NULL;
1480 const char *name = astman_get_header(m, "Channel");
1481 const char *varname = astman_get_header(m, "Variable");
1482 const char *id = astman_get_header(m,"ActionID");
1483 char *varval;
1484 char workspace[1024] = "";
1486 if (ast_strlen_zero(varname)) {
1487 astman_send_error(s, m, "No variable specified");
1488 return 0;
1491 if (!ast_strlen_zero(name)) {
1492 c = ast_get_channel_by_name_locked(name);
1493 if (!c) {
1494 astman_send_error(s, m, "No such channel");
1495 return 0;
1499 if (varname[strlen(varname) - 1] == ')') {
1500 char *copy = ast_strdupa(varname);
1502 ast_func_read(c, copy, workspace, sizeof(workspace));
1503 varval = workspace;
1504 } else {
1505 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1508 if (c)
1509 ast_channel_unlock(c);
1510 astman_append(s, "Response: Success\r\n"
1511 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1512 if (!ast_strlen_zero(id))
1513 astman_append(s, "ActionID: %s\r\n",id);
1514 astman_append(s, "\r\n");
1516 return 0;
1520 /*! \brief Manager "status" command to show channels */
1521 /* Needs documentation... */
1522 static int action_status(struct mansession *s, const struct message *m)
1524 const char *id = astman_get_header(m,"ActionID");
1525 const char *name = astman_get_header(m,"Channel");
1526 char idText[256] = "";
1527 struct ast_channel *c;
1528 char bridge[256];
1529 struct timeval now = ast_tvnow();
1530 long elapsed_seconds = 0;
1531 int all = ast_strlen_zero(name); /* set if we want all channels */
1533 if (!ast_strlen_zero(id))
1534 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1535 if (all)
1536 c = ast_channel_walk_locked(NULL);
1537 else {
1538 c = ast_get_channel_by_name_locked(name);
1539 if (!c) {
1540 astman_send_error(s, m, "No such channel");
1541 return 0;
1544 astman_send_ack(s, m, "Channel status will follow");
1545 /* if we look by name, we break after the first iteration */
1546 while (c) {
1547 if (c->_bridge)
1548 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1549 else
1550 bridge[0] = '\0';
1551 if (c->pbx) {
1552 if (c->cdr) {
1553 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1555 astman_append(s,
1556 "Event: Status\r\n"
1557 "Privilege: Call\r\n"
1558 "Channel: %s\r\n"
1559 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1560 "CallerIDNum: %s\r\n"
1561 "CallerIDName: %s\r\n"
1562 "Account: %s\r\n"
1563 "State: %s\r\n"
1564 "Context: %s\r\n"
1565 "Extension: %s\r\n"
1566 "Priority: %d\r\n"
1567 "Seconds: %ld\r\n"
1568 "%s"
1569 "Uniqueid: %s\r\n"
1570 "%s"
1571 "\r\n",
1572 c->name,
1573 S_OR(c->cid.cid_num, "<unknown>"),
1574 S_OR(c->cid.cid_num, "<unknown>"),
1575 S_OR(c->cid.cid_name, "<unknown>"),
1576 c->accountcode,
1577 ast_state2str(c->_state), c->context,
1578 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1579 } else {
1580 astman_append(s,
1581 "Event: Status\r\n"
1582 "Privilege: Call\r\n"
1583 "Channel: %s\r\n"
1584 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1585 "CallerIDNum: %s\r\n"
1586 "CallerIDName: %s\r\n"
1587 "Account: %s\r\n"
1588 "State: %s\r\n"
1589 "%s"
1590 "Uniqueid: %s\r\n"
1591 "%s"
1592 "\r\n",
1593 c->name,
1594 S_OR(c->cid.cid_num, "<unknown>"),
1595 S_OR(c->cid.cid_num, "<unknown>"),
1596 S_OR(c->cid.cid_name, "<unknown>"),
1597 c->accountcode,
1598 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1600 ast_channel_unlock(c);
1601 if (!all)
1602 break;
1603 c = ast_channel_walk_locked(c);
1605 astman_append(s,
1606 "Event: StatusComplete\r\n"
1607 "%s"
1608 "\r\n",idText);
1609 return 0;
1612 static char mandescr_redirect[] =
1613 "Description: Redirect (transfer) a call.\n"
1614 "Variables: (Names marked with * are required)\n"
1615 " *Channel: Channel to redirect\n"
1616 " ExtraChannel: Second call leg to transfer (optional)\n"
1617 " *Exten: Extension to transfer to\n"
1618 " *Context: Context to transfer to\n"
1619 " *Priority: Priority to transfer to\n"
1620 " ActionID: Optional Action id for message matching.\n";
1622 /*! \brief action_redirect: The redirect manager command */
1623 static int action_redirect(struct mansession *s, const struct message *m)
1625 const char *name = astman_get_header(m, "Channel");
1626 const char *name2 = astman_get_header(m, "ExtraChannel");
1627 const char *exten = astman_get_header(m, "Exten");
1628 const char *context = astman_get_header(m, "Context");
1629 const char *priority = astman_get_header(m, "Priority");
1630 struct ast_channel *chan, *chan2 = NULL;
1631 int pi = 0;
1632 int res;
1634 if (ast_strlen_zero(name)) {
1635 astman_send_error(s, m, "Channel not specified");
1636 return 0;
1638 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1639 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1640 astman_send_error(s, m, "Invalid priority");
1641 return 0;
1644 /* XXX watch out, possible deadlock!!! */
1645 chan = ast_get_channel_by_name_locked(name);
1646 if (!chan) {
1647 char buf[BUFSIZ];
1648 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1649 astman_send_error(s, m, buf);
1650 return 0;
1652 if (ast_check_hangup(chan)) {
1653 astman_send_error(s, m, "Redirect failed, channel not up.");
1654 ast_channel_unlock(chan);
1655 return 0;
1657 if (!ast_strlen_zero(name2))
1658 chan2 = ast_get_channel_by_name_locked(name2);
1659 if (chan2 && ast_check_hangup(chan2)) {
1660 astman_send_error(s, m, "Redirect failed, extra channel not up.");
1661 ast_channel_unlock(chan);
1662 ast_channel_unlock(chan2);
1663 return 0;
1665 res = ast_async_goto(chan, context, exten, pi);
1666 if (!res) {
1667 if (!ast_strlen_zero(name2)) {
1668 if (chan2)
1669 res = ast_async_goto(chan2, context, exten, pi);
1670 else
1671 res = -1;
1672 if (!res)
1673 astman_send_ack(s, m, "Dual Redirect successful");
1674 else
1675 astman_send_error(s, m, "Secondary redirect failed");
1676 } else
1677 astman_send_ack(s, m, "Redirect successful");
1678 } else
1679 astman_send_error(s, m, "Redirect failed");
1680 if (chan)
1681 ast_channel_unlock(chan);
1682 if (chan2)
1683 ast_channel_unlock(chan2);
1684 return 0;
1687 static int check_blacklist(const char *cmd)
1689 char *cmd_copy, *cur_cmd;
1690 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
1691 int i;
1693 cmd_copy = ast_strdupa(cmd);
1694 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
1695 cur_cmd = ast_strip(cur_cmd);
1696 if (ast_strlen_zero(cur_cmd)) {
1697 i--;
1698 continue;
1701 cmd_words[i] = cur_cmd;
1704 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
1705 int j, match = 1;
1707 for (j = 0; command_blacklist[i].words[j]; j++) {
1708 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
1709 match = 0;
1710 break;
1714 if (match) {
1715 return 1;
1719 return 0;
1722 static char mandescr_command[] =
1723 "Description: Run a CLI command.\n"
1724 "Variables: (Names marked with * are required)\n"
1725 " *Command: Asterisk CLI command to run\n"
1726 " ActionID: Optional Action id for message matching.\n";
1728 /*! \brief action_command: Manager command "command" - execute CLI command */
1729 static int action_command(struct mansession *s, const struct message *m)
1731 const char *cmd = astman_get_header(m, "Command");
1732 const char *id = astman_get_header(m, "ActionID");
1733 char *buf, *final_buf;
1734 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1735 int fd = mkstemp(template);
1736 off_t l;
1738 if (ast_strlen_zero(cmd)) {
1739 astman_send_error(s, m, "No command provided");
1740 return 0;
1743 if (check_blacklist(cmd)) {
1744 astman_send_error(s, m, "Command blacklisted");
1745 return 0;
1748 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1749 if (!ast_strlen_zero(id))
1750 astman_append(s, "ActionID: %s\r\n", id);
1751 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1752 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1753 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1755 /* This has a potential to overflow the stack. Hence, use the heap. */
1756 buf = ast_calloc(1, l + 1);
1757 final_buf = ast_calloc(1, l + 1);
1758 if (buf) {
1759 lseek(fd, 0, SEEK_SET);
1760 read(fd, buf, l);
1761 buf[l] = '\0';
1762 if (final_buf) {
1763 term_strip(final_buf, buf, l);
1764 final_buf[l] = '\0';
1766 astman_append(s, "%s", S_OR(final_buf, buf));
1767 ast_free(buf);
1769 close(fd);
1770 unlink(template);
1771 astman_append(s, "--END COMMAND--\r\n\r\n");
1772 if (final_buf)
1773 ast_free(final_buf);
1774 return 0;
1777 static void *fast_originate(void *data)
1779 struct fast_originate_helper *in = data;
1780 int res;
1781 int reason = 0;
1782 struct ast_channel *chan = NULL;
1783 char requested_channel[AST_CHANNEL_NAME];
1785 if (!ast_strlen_zero(in->app)) {
1786 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1787 S_OR(in->cid_num, NULL),
1788 S_OR(in->cid_name, NULL),
1789 in->vars, in->account, &chan);
1790 } else {
1791 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1792 S_OR(in->cid_num, NULL),
1793 S_OR(in->cid_name, NULL),
1794 in->vars, in->account, &chan);
1797 if (!chan)
1798 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1799 /* Tell the manager what happened with the channel */
1800 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1801 "%s"
1802 "Response: %s\r\n"
1803 "Channel: %s\r\n"
1804 "Context: %s\r\n"
1805 "Exten: %s\r\n"
1806 "Reason: %d\r\n"
1807 "Uniqueid: %s\r\n"
1808 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1809 "CallerIDNum: %s\r\n"
1810 "CallerIDName: %s\r\n",
1811 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1812 chan ? chan->uniqueid : "<null>",
1813 S_OR(in->cid_num, "<unknown>"),
1814 S_OR(in->cid_num, "<unknown>"),
1815 S_OR(in->cid_name, "<unknown>")
1818 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1819 if (chan)
1820 ast_channel_unlock(chan);
1821 free(in);
1822 return NULL;
1825 static char mandescr_originate[] =
1826 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1827 " Application/Data\n"
1828 "Variables: (Names marked with * are required)\n"
1829 " *Channel: Channel name to call\n"
1830 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1831 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1832 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1833 " Application: Application to use\n"
1834 " Data: Data to use (requires 'Application')\n"
1835 " Timeout: How long to wait for call to be answered (in ms)\n"
1836 " CallerID: Caller ID to be set on the outgoing channel\n"
1837 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1838 " Account: Account code\n"
1839 " Async: Set to 'true' for fast origination\n";
1841 static int action_originate(struct mansession *s, const struct message *m)
1843 const char *name = astman_get_header(m, "Channel");
1844 const char *exten = astman_get_header(m, "Exten");
1845 const char *context = astman_get_header(m, "Context");
1846 const char *priority = astman_get_header(m, "Priority");
1847 const char *timeout = astman_get_header(m, "Timeout");
1848 const char *callerid = astman_get_header(m, "CallerID");
1849 const char *account = astman_get_header(m, "Account");
1850 const char *app = astman_get_header(m, "Application");
1851 const char *appdata = astman_get_header(m, "Data");
1852 const char *async = astman_get_header(m, "Async");
1853 const char *id = astman_get_header(m, "ActionID");
1854 struct ast_variable *vars = astman_get_variables(m);
1855 char *tech, *data;
1856 char *l = NULL, *n = NULL;
1857 int pi = 0;
1858 int res;
1859 int to = 30000;
1860 int reason = 0;
1861 char tmp[256];
1862 char tmp2[256];
1864 pthread_t th;
1865 pthread_attr_t attr;
1866 if (!name) {
1867 astman_send_error(s, m, "Channel not specified");
1868 return 0;
1870 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1871 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1872 astman_send_error(s, m, "Invalid priority");
1873 return 0;
1876 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1877 astman_send_error(s, m, "Invalid timeout");
1878 return 0;
1880 ast_copy_string(tmp, name, sizeof(tmp));
1881 tech = tmp;
1882 data = strchr(tmp, '/');
1883 if (!data) {
1884 astman_send_error(s, m, "Invalid channel");
1885 return 0;
1887 *data++ = '\0';
1888 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1889 ast_callerid_parse(tmp2, &n, &l);
1890 if (n) {
1891 if (ast_strlen_zero(n))
1892 n = NULL;
1894 if (l) {
1895 ast_shrink_phone_number(l);
1896 if (ast_strlen_zero(l))
1897 l = NULL;
1899 if (ast_true(async)) {
1900 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1901 if (!fast) {
1902 res = -1;
1903 } else {
1904 if (!ast_strlen_zero(id))
1905 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1906 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1907 ast_copy_string(fast->data, data, sizeof(fast->data));
1908 ast_copy_string(fast->app, app, sizeof(fast->app));
1909 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1910 if (l)
1911 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1912 if (n)
1913 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1914 fast->vars = vars;
1915 ast_copy_string(fast->context, context, sizeof(fast->context));
1916 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1917 ast_copy_string(fast->account, account, sizeof(fast->account));
1918 fast->timeout = to;
1919 fast->priority = pi;
1920 pthread_attr_init(&attr);
1921 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1922 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1923 ast_free(fast);
1924 res = -1;
1925 } else {
1926 res = 0;
1928 pthread_attr_destroy(&attr);
1930 } else if (!ast_strlen_zero(app)) {
1931 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1932 } else {
1933 if (exten && context && pi)
1934 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1935 else {
1936 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1937 return 0;
1940 if (!res)
1941 astman_send_ack(s, m, "Originate successfully queued");
1942 else
1943 astman_send_error(s, m, "Originate failed");
1944 return 0;
1947 /*! \brief Help text for manager command mailboxstatus
1949 static char mandescr_mailboxstatus[] =
1950 "Description: Checks a voicemail account for status.\n"
1951 "Variables: (Names marked with * are required)\n"
1952 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1953 " ActionID: Optional ActionID for message matching.\n"
1954 "Returns number of messages.\n"
1955 " Message: Mailbox Status\n"
1956 " Mailbox: <mailboxid>\n"
1957 " Waiting: <count>\n"
1958 "\n";
1960 static int action_mailboxstatus(struct mansession *s, const struct message *m)
1962 const char *mailbox = astman_get_header(m, "Mailbox");
1963 const char *id = astman_get_header(m,"ActionID");
1964 char idText[256] = "";
1965 int ret;
1966 if (ast_strlen_zero(mailbox)) {
1967 astman_send_error(s, m, "Mailbox not specified");
1968 return 0;
1970 if (!ast_strlen_zero(id))
1971 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1972 ret = ast_app_has_voicemail(mailbox, NULL);
1973 astman_append(s, "Response: Success\r\n"
1974 "%s"
1975 "Message: Mailbox Status\r\n"
1976 "Mailbox: %s\r\n"
1977 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1978 return 0;
1981 static char mandescr_mailboxcount[] =
1982 "Description: Checks a voicemail account for new messages.\n"
1983 "Variables: (Names marked with * are required)\n"
1984 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1985 " ActionID: Optional ActionID for message matching.\n"
1986 "Returns number of new and old messages.\n"
1987 " Message: Mailbox Message Count\n"
1988 " Mailbox: <mailboxid>\n"
1989 " NewMessages: <count>\n"
1990 " OldMessages: <count>\n"
1991 "\n";
1992 static int action_mailboxcount(struct mansession *s, const struct message *m)
1994 const char *mailbox = astman_get_header(m, "Mailbox");
1995 const char *id = astman_get_header(m,"ActionID");
1996 char idText[256] = "";
1997 int newmsgs = 0, oldmsgs = 0;
1998 if (ast_strlen_zero(mailbox)) {
1999 astman_send_error(s, m, "Mailbox not specified");
2000 return 0;
2002 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2003 if (!ast_strlen_zero(id)) {
2004 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
2006 astman_append(s, "Response: Success\r\n"
2007 "%s"
2008 "Message: Mailbox Message Count\r\n"
2009 "Mailbox: %s\r\n"
2010 "NewMessages: %d\r\n"
2011 "OldMessages: %d\r\n"
2012 "\r\n",
2013 idText,mailbox, newmsgs, oldmsgs);
2014 return 0;
2017 static char mandescr_extensionstate[] =
2018 "Description: Report the extension state for given extension.\n"
2019 " If the extension has a hint, will use devicestate to check\n"
2020 " the status of the device connected to the extension.\n"
2021 "Variables: (Names marked with * are required)\n"
2022 " *Exten: Extension to check state on\n"
2023 " *Context: Context for extension\n"
2024 " ActionId: Optional ID for this transaction\n"
2025 "Will return an \"Extension Status\" message.\n"
2026 "The response will include the hint for the extension and the status.\n";
2028 static int action_extensionstate(struct mansession *s, const struct message *m)
2030 const char *exten = astman_get_header(m, "Exten");
2031 const char *context = astman_get_header(m, "Context");
2032 const char *id = astman_get_header(m,"ActionID");
2033 char idText[256] = "";
2034 char hint[256] = "";
2035 int status;
2036 if (ast_strlen_zero(exten)) {
2037 astman_send_error(s, m, "Extension not specified");
2038 return 0;
2040 if (ast_strlen_zero(context))
2041 context = "default";
2042 status = ast_extension_state(NULL, context, exten);
2043 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2044 if (!ast_strlen_zero(id)) {
2045 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2047 astman_append(s, "Response: Success\r\n"
2048 "%s"
2049 "Message: Extension Status\r\n"
2050 "Exten: %s\r\n"
2051 "Context: %s\r\n"
2052 "Hint: %s\r\n"
2053 "Status: %d\r\n\r\n",
2054 idText,exten, context, hint, status);
2055 return 0;
2058 static char mandescr_timeout[] =
2059 "Description: Hangup a channel after a certain time.\n"
2060 "Variables: (Names marked with * are required)\n"
2061 " *Channel: Channel name to hangup\n"
2062 " *Timeout: Maximum duration of the call (sec)\n"
2063 "Acknowledges set time with 'Timeout Set' message\n";
2065 static int action_timeout(struct mansession *s, const struct message *m)
2067 struct ast_channel *c = NULL;
2068 const char *name = astman_get_header(m, "Channel");
2069 int timeout = atoi(astman_get_header(m, "Timeout"));
2070 if (ast_strlen_zero(name)) {
2071 astman_send_error(s, m, "No channel specified");
2072 return 0;
2074 if (!timeout) {
2075 astman_send_error(s, m, "No timeout specified");
2076 return 0;
2078 c = ast_get_channel_by_name_locked(name);
2079 if (!c) {
2080 astman_send_error(s, m, "No such channel");
2081 return 0;
2083 ast_channel_setwhentohangup(c, timeout);
2084 ast_channel_unlock(c);
2085 astman_send_ack(s, m, "Timeout Set");
2086 return 0;
2089 static int process_events(struct mansession *s)
2091 struct eventqent *eqe;
2092 int ret = 0;
2093 ast_mutex_lock(&s->__lock);
2094 if (!s->eventq)
2095 s->eventq = master_eventq;
2096 while(s->eventq->next) {
2097 eqe = s->eventq->next;
2098 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
2099 ((s->send_events & eqe->category) == eqe->category)) {
2100 if (s->fd > -1) {
2101 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
2102 ret = -1;
2103 } else if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
2104 ret = -1;
2105 else
2106 ast_dynamic_str_append(&s->outputstr, 0, "%s", eqe->eventdata);
2108 unuse_eventqent(s->eventq);
2109 s->eventq = eqe;
2111 ast_mutex_unlock(&s->__lock);
2112 return ret;
2115 static char mandescr_userevent[] =
2116 "Description: Send an event to manager sessions.\n"
2117 "Variables: (Names marked with * are required)\n"
2118 " *UserEvent: EventStringToSend\n"
2119 " Header1: Content1\n"
2120 " HeaderN: ContentN\n";
2122 static int action_userevent(struct mansession *s, const struct message *m)
2124 const char *event = astman_get_header(m, "UserEvent");
2125 char body[2048] = "";
2126 int x, bodylen = 0;
2127 for (x = 0; x < m->hdrcount; x++) {
2128 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2129 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2130 bodylen += strlen(m->headers[x]);
2131 ast_copy_string(body + bodylen, "\r\n", 3);
2132 bodylen += 2;
2136 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2137 return 0;
2140 static int process_message(struct mansession *s, const struct message *m)
2142 char action[80] = "";
2143 struct manager_action *tmp;
2144 const char *id = astman_get_header(m,"ActionID");
2145 char idText[256] = "";
2146 int ret = 0;
2148 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2149 if (option_debug)
2150 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
2152 if (ast_strlen_zero(action)) {
2153 astman_send_error(s, m, "Missing action in request");
2154 return 0;
2156 if (!ast_strlen_zero(id)) {
2157 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2159 if (!s->authenticated) {
2160 if (!strcasecmp(action, "Challenge")) {
2161 const char *authtype = astman_get_header(m, "AuthType");
2163 if (!strcasecmp(authtype, "MD5")) {
2164 if (ast_strlen_zero(s->challenge))
2165 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
2166 astman_append(s, "Response: Success\r\n"
2167 "%s"
2168 "Challenge: %s\r\n\r\n",
2169 idText, s->challenge);
2170 return 0;
2171 } else {
2172 astman_send_error(s, m, "Must specify AuthType");
2173 return 0;
2175 } else if (!strcasecmp(action, "Login")) {
2176 if (authenticate(s, m)) {
2177 sleep(1);
2178 astman_send_error(s, m, "Authentication failed");
2179 return -1;
2180 } else {
2181 s->authenticated = 1;
2182 if (option_verbose > 1) {
2183 if (displayconnects) {
2184 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n",
2185 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2188 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n",
2189 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2190 astman_send_ack(s, m, "Authentication accepted");
2192 } else if (!strcasecmp(action, "Logoff")) {
2193 astman_send_ack(s, m, "See ya");
2194 return -1;
2195 } else
2196 astman_send_error(s, m, "Authentication Required");
2197 } else {
2198 if (!strcasecmp(action, "Login"))
2199 astman_send_ack(s, m, "Already logged in");
2200 else {
2201 ast_rwlock_rdlock(&actionlock);
2202 for (tmp = first_action; tmp; tmp = tmp->next) {
2203 if (strcasecmp(action, tmp->action))
2204 continue;
2205 if ((s->writeperm & tmp->authority) == tmp->authority) {
2206 if (tmp->func(s, m))
2207 ret = -1;
2208 } else
2209 astman_send_error(s, m, "Permission denied");
2210 break;
2212 ast_rwlock_unlock(&actionlock);
2213 if (!tmp)
2214 astman_send_error(s, m, "Invalid/unknown command");
2217 if (ret)
2218 return ret;
2219 return process_events(s);
2222 static int get_input(struct mansession *s, char *output)
2224 /* output must have at least sizeof(s->inbuf) space */
2225 int res;
2226 int x;
2227 struct pollfd fds[1];
2228 for (x = 1; x < s->inlen; x++) {
2229 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
2230 /* Copy output data up to and including \r\n */
2231 memcpy(output, s->inbuf, x + 1);
2232 /* Add trailing \0 */
2233 output[x+1] = '\0';
2234 /* Move remaining data back to the front */
2235 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2236 s->inlen -= (x + 1);
2237 return 1;
2240 if (s->inlen >= sizeof(s->inbuf) - 1) {
2241 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2242 s->inlen = 0;
2244 fds[0].fd = s->fd;
2245 fds[0].events = POLLIN;
2246 do {
2247 ast_mutex_lock(&s->__lock);
2248 if (s->pending_event) {
2249 s->pending_event = 0;
2250 ast_mutex_unlock(&s->__lock);
2251 return 0;
2253 s->waiting_thread = pthread_self();
2254 ast_mutex_unlock(&s->__lock);
2256 res = poll(fds, 1, -1);
2258 ast_mutex_lock(&s->__lock);
2259 s->waiting_thread = AST_PTHREADT_NULL;
2260 ast_mutex_unlock(&s->__lock);
2261 if (res < 0) {
2262 if (errno == EINTR || errno == EAGAIN) {
2263 return 0;
2265 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2266 return -1;
2267 } else if (res > 0) {
2268 ast_mutex_lock(&s->__lock);
2269 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2270 ast_mutex_unlock(&s->__lock);
2271 if (res < 1)
2272 return -1;
2273 break;
2275 } while(1);
2276 s->inlen += res;
2277 s->inbuf[s->inlen] = '\0';
2278 return 0;
2281 static int do_message(struct mansession *s)
2283 struct message m = { 0 };
2284 char header_buf[sizeof(s->inbuf)] = { '\0' };
2285 int res;
2287 for (;;) {
2288 /* Check if any events are pending and do them if needed */
2289 if (s->eventq->next) {
2290 if (process_events(s))
2291 return -1;
2293 res = get_input(s, header_buf);
2294 if (res == 0) {
2295 continue;
2296 } else if (res > 0) {
2297 /* Strip trailing \r\n */
2298 if (strlen(header_buf) < 2)
2299 continue;
2300 header_buf[strlen(header_buf) - 2] = '\0';
2301 if (ast_strlen_zero(header_buf))
2302 return process_message(s, &m) ? -1 : 0;
2303 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2304 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2305 } else {
2306 return res;
2311 static void *session_do(void *data)
2313 struct mansession *s = data;
2314 int res;
2316 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2317 for (;;) {
2318 if ((res = do_message(s)) < 0)
2319 break;
2321 if (s->authenticated) {
2322 if (option_verbose > 1) {
2323 if (displayconnects)
2324 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2326 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2327 } else {
2328 if (option_verbose > 1) {
2329 if (displayconnects)
2330 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2332 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2335 /* It is possible under certain circumstances for this session thread
2336 to complete its work and exit *before* the thread that created it
2337 has finished executing the ast_pthread_create_background() function.
2338 If this occurs, some versions of glibc appear to act in a buggy
2339 fashion and attempt to write data into memory that it thinks belongs
2340 to the thread but is in fact not owned by the thread (or may have
2341 been freed completely).
2343 Causing this thread to yield to other threads at least one time
2344 appears to work around this bug.
2346 usleep(1);
2348 destroy_session(s);
2349 return NULL;
2352 static void *accept_thread(void *ignore)
2354 int as;
2355 struct sockaddr_in sin;
2356 socklen_t sinlen;
2357 struct eventqent *eqe;
2358 struct mansession *s;
2359 struct protoent *p;
2360 int arg = 1;
2361 int flags;
2362 pthread_attr_t attr;
2363 time_t now;
2364 struct pollfd pfds[1];
2366 pthread_attr_init(&attr);
2367 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2369 for (;;) {
2370 time(&now);
2371 AST_LIST_LOCK(&sessions);
2372 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2373 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2374 AST_LIST_REMOVE_CURRENT(&sessions, list);
2375 num_sessions--;
2376 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2377 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2378 s->username, ast_inet_ntoa(s->sin.sin_addr));
2380 free_session(s);
2381 break;
2384 AST_LIST_TRAVERSE_SAFE_END
2385 /* Purge master event queue of old, unused events, but make sure we
2386 always keep at least one in the queue */
2387 eqe = master_eventq;
2388 while (master_eventq->next && !master_eventq->usecount) {
2389 eqe = master_eventq;
2390 master_eventq = master_eventq->next;
2391 free(eqe);
2393 AST_LIST_UNLOCK(&sessions);
2395 sinlen = sizeof(sin);
2396 pfds[0].fd = asock;
2397 pfds[0].events = POLLIN;
2398 /* Wait for something to happen, but timeout every few seconds so
2399 we can ditch any old manager sessions */
2400 if (poll(pfds, 1, 5000) < 1)
2401 continue;
2402 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2403 if (as < 0) {
2404 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2405 continue;
2407 p = getprotobyname("tcp");
2408 if (p) {
2409 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2410 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2413 if (!(s = ast_calloc(1, sizeof(*s))))
2414 continue;
2416 memcpy(&s->sin, &sin, sizeof(sin));
2417 s->writetimeout = 100;
2418 s->waiting_thread = AST_PTHREADT_NULL;
2420 if (!block_sockets) {
2421 /* For safety, make sure socket is non-blocking */
2422 flags = fcntl(as, F_GETFL);
2423 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2424 } else {
2425 flags = fcntl(as, F_GETFL);
2426 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2428 ast_mutex_init(&s->__lock);
2429 s->fd = as;
2430 s->send_events = -1;
2431 AST_LIST_LOCK(&sessions);
2432 AST_LIST_INSERT_HEAD(&sessions, s, list);
2433 num_sessions++;
2434 /* Find the last place in the master event queue and hook ourselves
2435 in there */
2436 s->eventq = master_eventq;
2437 while(s->eventq->next)
2438 s->eventq = s->eventq->next;
2439 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2440 AST_LIST_UNLOCK(&sessions);
2441 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2442 destroy_session(s);
2444 pthread_attr_destroy(&attr);
2445 return NULL;
2448 static int append_event(const char *str, int category)
2450 struct eventqent *tmp, *prev = NULL;
2451 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2453 if (!tmp)
2454 return -1;
2456 tmp->next = NULL;
2457 tmp->category = category;
2458 strcpy(tmp->eventdata, str);
2460 if (master_eventq) {
2461 prev = master_eventq;
2462 while (prev->next)
2463 prev = prev->next;
2464 prev->next = tmp;
2465 } else {
2466 master_eventq = tmp;
2469 tmp->usecount = num_sessions;
2471 return 0;
2474 /*! \brief manager_event: Send AMI event to client */
2475 int manager_event(int category, const char *event, const char *fmt, ...)
2477 struct mansession *s;
2478 char auth[80];
2479 va_list ap;
2480 struct timeval now;
2481 struct ast_dynamic_str *buf;
2483 /* Abort if there aren't any manager sessions */
2484 if (!num_sessions)
2485 return 0;
2487 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2488 return -1;
2490 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2491 "Event: %s\r\nPrivilege: %s\r\n",
2492 event, authority_to_str(category, auth, sizeof(auth)));
2494 if (timestampevents) {
2495 now = ast_tvnow();
2496 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2497 "Timestamp: %ld.%06lu\r\n",
2498 now.tv_sec, (unsigned long) now.tv_usec);
2501 va_start(ap, fmt);
2502 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2503 va_end(ap);
2505 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2507 /* Append event to master list and wake up any sleeping sessions */
2508 AST_LIST_LOCK(&sessions);
2509 append_event(buf->str, category);
2510 AST_LIST_TRAVERSE(&sessions, s, list) {
2511 ast_mutex_lock(&s->__lock);
2512 if (s->waiting_thread != AST_PTHREADT_NULL)
2513 pthread_kill(s->waiting_thread, SIGURG);
2514 else
2515 /* We have an event to process, but the mansession is
2516 * not waiting for it. We still need to indicate that there
2517 * is an event waiting so that get_input processes the pending
2518 * event instead of polling.
2520 s->pending_event = 1;
2521 ast_mutex_unlock(&s->__lock);
2523 AST_LIST_UNLOCK(&sessions);
2525 return 0;
2528 int ast_manager_unregister(char *action)
2530 struct manager_action *cur, *prev;
2532 ast_rwlock_wrlock(&actionlock);
2533 cur = prev = first_action;
2534 while (cur) {
2535 if (!strcasecmp(action, cur->action)) {
2536 prev->next = cur->next;
2537 free(cur);
2538 if (option_verbose > 1)
2539 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2540 ast_rwlock_unlock(&actionlock);
2541 return 0;
2543 prev = cur;
2544 cur = cur->next;
2546 ast_rwlock_unlock(&actionlock);
2547 return 0;
2550 static int manager_state_cb(char *context, char *exten, int state, void *data)
2552 /* Notify managers of change */
2553 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2554 return 0;
2557 static int ast_manager_register_struct(struct manager_action *act)
2559 struct manager_action *cur, *prev = NULL;
2560 int ret;
2562 ast_rwlock_wrlock(&actionlock);
2563 cur = first_action;
2564 while (cur) { /* Walk the list of actions */
2565 ret = strcasecmp(cur->action, act->action);
2566 if (ret == 0) {
2567 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2568 ast_rwlock_unlock(&actionlock);
2569 return -1;
2570 } else if (ret > 0) {
2571 /* Insert these alphabetically */
2572 if (prev) {
2573 act->next = prev->next;
2574 prev->next = act;
2575 } else {
2576 act->next = first_action;
2577 first_action = act;
2579 break;
2581 prev = cur;
2582 cur = cur->next;
2585 if (!cur) {
2586 if (prev)
2587 prev->next = act;
2588 else
2589 first_action = act;
2590 act->next = NULL;
2593 if (option_verbose > 1)
2594 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2595 ast_rwlock_unlock(&actionlock);
2596 return 0;
2599 /*! \brief register a new command with manager, including online help. This is
2600 the preferred way to register a manager command */
2601 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2603 struct manager_action *cur;
2605 cur = ast_malloc(sizeof(*cur));
2606 if (!cur)
2607 return -1;
2609 cur->action = action;
2610 cur->authority = auth;
2611 cur->func = func;
2612 cur->synopsis = synopsis;
2613 cur->description = description;
2614 cur->next = NULL;
2616 ast_manager_register_struct(cur);
2618 return 0;
2620 /*! @}
2621 END Doxygen group */
2623 static struct mansession *find_session(uint32_t ident)
2625 struct mansession *s;
2627 AST_LIST_LOCK(&sessions);
2628 AST_LIST_TRAVERSE(&sessions, s, list) {
2629 ast_mutex_lock(&s->__lock);
2630 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2631 s->inuse++;
2632 break;
2634 ast_mutex_unlock(&s->__lock);
2636 AST_LIST_UNLOCK(&sessions);
2638 return s;
2641 int astman_verify_session_readpermissions(uint32_t ident, int perm)
2643 int result = 0;
2644 struct mansession *s;
2646 AST_LIST_LOCK(&sessions);
2647 AST_LIST_TRAVERSE(&sessions, s, list) {
2648 ast_mutex_lock(&s->__lock);
2649 if ((s->managerid == ident) && (s->readperm & perm)) {
2650 result = 1;
2651 ast_mutex_unlock(&s->__lock);
2652 break;
2654 ast_mutex_unlock(&s->__lock);
2656 AST_LIST_UNLOCK(&sessions);
2657 return result;
2660 int astman_verify_session_writepermissions(uint32_t ident, int perm)
2662 int result = 0;
2663 struct mansession *s;
2665 AST_LIST_LOCK(&sessions);
2666 AST_LIST_TRAVERSE(&sessions, s, list) {
2667 ast_mutex_lock(&s->__lock);
2668 if ((s->managerid == ident) && (s->writeperm & perm)) {
2669 result = 1;
2670 ast_mutex_unlock(&s->__lock);
2671 break;
2673 ast_mutex_unlock(&s->__lock);
2675 AST_LIST_UNLOCK(&sessions);
2676 return result;
2679 enum {
2680 FORMAT_RAW,
2681 FORMAT_HTML,
2682 FORMAT_XML,
2684 static char *contenttype[] = { "plain", "html", "xml" };
2686 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2688 struct mansession *s = NULL;
2689 uint32_t ident = 0;
2690 char workspace[512];
2691 char cookie[128];
2692 size_t len = sizeof(workspace);
2693 int blastaway = 0;
2694 char *c = workspace;
2695 char *retval = NULL;
2696 struct ast_variable *v;
2698 for (v = params; v; v = v->next) {
2699 if (!strcasecmp(v->name, "mansession_id")) {
2700 sscanf(v->value, "%x", &ident);
2701 break;
2705 if (!(s = find_session(ident))) {
2706 /* Create new session */
2707 if (!(s = ast_calloc(1, sizeof(*s)))) {
2708 *status = 500;
2709 goto generic_callback_out;
2711 memcpy(&s->sin, requestor, sizeof(s->sin));
2712 s->fd = -1;
2713 s->waiting_thread = AST_PTHREADT_NULL;
2714 s->send_events = 0;
2715 ast_mutex_init(&s->__lock);
2716 ast_mutex_lock(&s->__lock);
2717 s->inuse = 1;
2718 /*!\note There is approximately a 1 in 1.8E19 chance that the following
2719 * calculation will produce 0, which is an invalid ID, but due to the
2720 * properties of the rand() function (and the constantcy of s), that
2721 * won't happen twice in a row.
2723 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
2724 AST_LIST_LOCK(&sessions);
2725 AST_LIST_INSERT_HEAD(&sessions, s, list);
2726 /* Hook into the last spot in the event queue */
2727 s->eventq = master_eventq;
2728 while (s->eventq->next)
2729 s->eventq = s->eventq->next;
2730 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2731 ast_atomic_fetchadd_int(&num_sessions, 1);
2732 AST_LIST_UNLOCK(&sessions);
2735 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2736 time(&s->sessiontimeout);
2737 if (!s->authenticated && (httptimeout > 5))
2738 s->sessiontimeout += 5;
2739 else
2740 s->sessiontimeout += httptimeout;
2741 ast_mutex_unlock(&s->__lock);
2743 if (s) {
2744 struct message m = { 0 };
2745 char tmp[80];
2746 unsigned int x;
2747 size_t hdrlen;
2749 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
2750 hdrlen = strlen(v->name) + strlen(v->value) + 3;
2751 m.headers[m.hdrcount] = alloca(hdrlen);
2752 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
2753 m.hdrcount = x + 1;
2756 if (process_message(s, &m)) {
2757 if (s->authenticated) {
2758 if (option_verbose > 1) {
2759 if (displayconnects)
2760 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2762 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2763 } else {
2764 if (option_verbose > 1) {
2765 if (displayconnects)
2766 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2768 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2770 s->needdestroy = 1;
2772 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2773 sprintf(tmp, "%08x", s->managerid);
2774 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2775 if (format == FORMAT_HTML)
2776 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
2777 if (format == FORMAT_XML) {
2778 ast_build_string(&c, &len, "<ajax-response>\n");
2779 } else if (format == FORMAT_HTML) {
2780 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2781 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2783 ast_mutex_lock(&s->__lock);
2784 if (s->outputstr) {
2785 char *tmp;
2786 if (format == FORMAT_XML)
2787 tmp = xml_translate(s->outputstr->str, params);
2788 else if (format == FORMAT_HTML)
2789 tmp = html_translate(s->outputstr->str);
2790 else
2791 tmp = s->outputstr->str;
2792 if (tmp) {
2793 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2794 if (retval) {
2795 strcpy(retval, workspace);
2796 strcpy(retval + strlen(retval), tmp);
2797 c = retval + strlen(retval);
2798 len = 120;
2801 if (tmp != s->outputstr->str)
2802 free(tmp);
2803 free(s->outputstr);
2804 s->outputstr = NULL;
2806 ast_mutex_unlock(&s->__lock);
2807 /* Still okay because c would safely be pointing to workspace even
2808 if retval failed to allocate above */
2809 if (format == FORMAT_XML) {
2810 ast_build_string(&c, &len, "</ajax-response>\n");
2811 } else if (format == FORMAT_HTML)
2812 ast_build_string(&c, &len, "</table></body>\r\n");
2813 } else {
2814 *status = 500;
2815 *title = strdup("Server Error");
2817 ast_mutex_lock(&s->__lock);
2818 if (s->needdestroy) {
2819 if (s->inuse == 1) {
2820 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2821 blastaway = 1;
2822 } else {
2823 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2824 if (s->waiting_thread != AST_PTHREADT_NULL)
2825 pthread_kill(s->waiting_thread, SIGURG);
2826 s->inuse--;
2828 } else
2829 s->inuse--;
2830 ast_mutex_unlock(&s->__lock);
2832 if (blastaway)
2833 destroy_session(s);
2834 generic_callback_out:
2835 if (*status != 200)
2836 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2837 return retval;
2840 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2842 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2845 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2847 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2850 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2852 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2855 struct ast_http_uri rawmanuri = {
2856 .description = "Raw HTTP Manager Event Interface",
2857 .uri = "rawman",
2858 .has_subtree = 0,
2859 .callback = rawman_http_callback,
2862 struct ast_http_uri manageruri = {
2863 .description = "HTML Manager Event Interface",
2864 .uri = "manager",
2865 .has_subtree = 0,
2866 .callback = manager_http_callback,
2869 struct ast_http_uri managerxmluri = {
2870 .description = "XML Manager Event Interface",
2871 .uri = "mxml",
2872 .has_subtree = 0,
2873 .callback = mxml_http_callback,
2876 static int registered = 0;
2877 static int webregged = 0;
2879 int init_manager(void)
2881 struct ast_config *cfg = NULL, *ucfg = NULL;
2882 const char *val;
2883 char *cat = NULL;
2884 int oldportno = portno;
2885 static struct sockaddr_in ba;
2886 int x = 1;
2887 int flags;
2888 int webenabled = 0;
2889 int newhttptimeout = 60;
2890 struct ast_manager_user *user = NULL;
2892 if (!registered) {
2893 /* Register default actions */
2894 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2895 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2896 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2897 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2898 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2899 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2900 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2901 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2902 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2903 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2904 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2905 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2906 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2907 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2908 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2909 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2910 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2911 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2912 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2914 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2915 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2916 registered = 1;
2917 /* Append placeholder event so master_eventq never runs dry */
2918 append_event("Event: Placeholder\r\n\r\n", 0);
2920 portno = DEFAULT_MANAGER_PORT;
2921 displayconnects = 1;
2922 cfg = ast_config_load("manager.conf");
2923 if (!cfg) {
2924 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2925 return 0;
2927 val = ast_variable_retrieve(cfg, "general", "enabled");
2928 if (val)
2929 enabled = ast_true(val);
2931 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2932 if (val)
2933 block_sockets = ast_true(val);
2935 val = ast_variable_retrieve(cfg, "general", "webenabled");
2936 if (val)
2937 webenabled = ast_true(val);
2939 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2940 if (sscanf(val, "%d", &portno) != 1) {
2941 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2942 portno = DEFAULT_MANAGER_PORT;
2946 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2947 displayconnects = ast_true(val);
2949 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2950 timestampevents = ast_true(val);
2952 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2953 newhttptimeout = atoi(val);
2955 memset(&ba, 0, sizeof(ba));
2956 ba.sin_family = AF_INET;
2957 ba.sin_port = htons(portno);
2959 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2960 if (!inet_aton(val, &ba.sin_addr)) {
2961 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2962 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2967 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2968 #if 0
2969 /* Can't be done yet */
2970 close(asock);
2971 asock = -1;
2972 #else
2973 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2974 #endif
2977 AST_LIST_LOCK(&users);
2979 if ((ucfg = ast_config_load("users.conf"))) {
2980 while ((cat = ast_category_browse(ucfg, cat))) {
2981 int hasmanager = 0;
2982 struct ast_variable *var = NULL;
2984 if (!strcasecmp(cat, "general")) {
2985 continue;
2988 if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
2989 continue;
2992 /* Look for an existing entry, if none found - create one and add it to the list */
2993 if (!(user = ast_get_manager_by_name_locked(cat))) {
2994 if (!(user = ast_calloc(1, sizeof(*user)))) {
2995 break;
2997 /* Copy name over */
2998 ast_copy_string(user->username, cat, sizeof(user->username));
2999 /* Insert into list */
3000 AST_LIST_INSERT_TAIL(&users, user, list);
3003 /* Make sure we keep this user and don't destroy it during cleanup */
3004 user->keep = 1;
3006 for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
3007 if (!strcasecmp(var->name, "secret")) {
3008 if (user->secret) {
3009 free(user->secret);
3011 user->secret = ast_strdup(var->value);
3012 } else if (!strcasecmp(var->name, "deny") ) {
3013 if (user->deny) {
3014 free(user->deny);
3016 user->deny = ast_strdup(var->value);
3017 } else if (!strcasecmp(var->name, "permit") ) {
3018 if (user->permit) {
3019 free(user->permit);
3021 user->permit = ast_strdup(var->value);
3022 } else if (!strcasecmp(var->name, "read") ) {
3023 if (user->read) {
3024 free(user->read);
3026 user->read = ast_strdup(var->value);
3027 } else if (!strcasecmp(var->name, "write") ) {
3028 if (user->write) {
3029 free(user->write);
3031 user->write = ast_strdup(var->value);
3032 } else if (!strcasecmp(var->name, "displayconnects") ) {
3033 user->displayconnects = ast_true(var->value);
3034 } else if (!strcasecmp(var->name, "hasmanager")) {
3035 /* already handled */
3036 } else {
3037 ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
3041 ast_config_destroy(ucfg);
3044 while ((cat = ast_category_browse(cfg, cat))) {
3045 struct ast_variable *var = NULL;
3047 if (!strcasecmp(cat, "general"))
3048 continue;
3050 /* Look for an existing entry, if none found - create one and add it to the list */
3051 if (!(user = ast_get_manager_by_name_locked(cat))) {
3052 if (!(user = ast_calloc(1, sizeof(*user))))
3053 break;
3054 /* Copy name over */
3055 ast_copy_string(user->username, cat, sizeof(user->username));
3056 /* Insert into list */
3057 AST_LIST_INSERT_TAIL(&users, user, list);
3060 /* Make sure we keep this user and don't destroy it during cleanup */
3061 user->keep = 1;
3063 var = ast_variable_browse(cfg, cat);
3064 while (var) {
3065 if (!strcasecmp(var->name, "secret")) {
3066 if (user->secret)
3067 free(user->secret);
3068 user->secret = ast_strdup(var->value);
3069 } else if (!strcasecmp(var->name, "deny") ) {
3070 if (user->deny)
3071 free(user->deny);
3072 user->deny = ast_strdup(var->value);
3073 } else if (!strcasecmp(var->name, "permit") ) {
3074 if (user->permit)
3075 free(user->permit);
3076 user->permit = ast_strdup(var->value);
3077 } else if (!strcasecmp(var->name, "read") ) {
3078 if (user->read)
3079 free(user->read);
3080 user->read = ast_strdup(var->value);
3081 } else if (!strcasecmp(var->name, "write") ) {
3082 if (user->write)
3083 free(user->write);
3084 user->write = ast_strdup(var->value);
3085 } else if (!strcasecmp(var->name, "displayconnects") )
3086 user->displayconnects = ast_true(var->value);
3087 else
3088 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
3089 var = var->next;
3093 /* Perform cleanup - essentially prune out old users that no longer exist */
3094 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
3095 if (user->keep) {
3096 user->keep = 0;
3097 continue;
3099 /* We do not need to keep this user so take them out of the list */
3100 AST_LIST_REMOVE_CURRENT(&users, list);
3101 /* Free their memory now */
3102 if (user->secret)
3103 free(user->secret);
3104 if (user->deny)
3105 free(user->deny);
3106 if (user->permit)
3107 free(user->permit);
3108 if (user->read)
3109 free(user->read);
3110 if (user->write)
3111 free(user->write);
3112 free(user);
3114 AST_LIST_TRAVERSE_SAFE_END
3116 AST_LIST_UNLOCK(&users);
3118 ast_config_destroy(cfg);
3120 if (webenabled && enabled) {
3121 if (!webregged) {
3122 ast_http_uri_link(&rawmanuri);
3123 ast_http_uri_link(&manageruri);
3124 ast_http_uri_link(&managerxmluri);
3125 webregged = 1;
3127 } else {
3128 if (webregged) {
3129 ast_http_uri_unlink(&rawmanuri);
3130 ast_http_uri_unlink(&manageruri);
3131 ast_http_uri_unlink(&managerxmluri);
3132 webregged = 0;
3136 if (newhttptimeout > 0)
3137 httptimeout = newhttptimeout;
3139 /* If not enabled, do nothing */
3140 if (!enabled)
3141 return 0;
3143 if (asock < 0) {
3144 asock = socket(AF_INET, SOCK_STREAM, 0);
3145 if (asock < 0) {
3146 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
3147 return -1;
3149 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
3150 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
3151 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
3152 close(asock);
3153 asock = -1;
3154 return -1;
3156 if (listen(asock, 2)) {
3157 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
3158 close(asock);
3159 asock = -1;
3160 return -1;
3162 flags = fcntl(asock, F_GETFL);
3163 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
3164 if (option_verbose)
3165 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
3166 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
3168 return 0;
3171 int reload_manager(void)
3173 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3174 return init_manager();