Let's also include aclocal.m4
[asterisk-bristuff.git] / main / manager.c
blob8b10712e8db9f2cba856ad0ff9a874570a556df3
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 int format;
79 char app[AST_MAX_APP];
80 char appdata[AST_MAX_EXTENSION];
81 char cid_name[AST_MAX_EXTENSION];
82 char cid_num[AST_MAX_EXTENSION];
83 char context[AST_MAX_CONTEXT];
84 char exten[AST_MAX_EXTENSION];
85 char idtext[AST_MAX_EXTENSION];
86 char account[AST_MAX_ACCOUNT_CODE];
87 int priority;
88 struct ast_variable *vars;
91 struct eventqent {
92 int usecount;
93 int category;
94 struct eventqent *next;
95 char eventdata[1];
98 static int enabled;
99 static int portno = DEFAULT_MANAGER_PORT;
100 static int asock = -1;
101 static int displayconnects = 1;
102 static int timestampevents;
103 static int httptimeout = 60;
105 static pthread_t t;
106 static int block_sockets;
107 static int num_sessions;
109 /* Protected by the sessions list lock */
110 struct eventqent *master_eventq = NULL;
112 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
113 #define MANAGER_EVENT_BUF_INITSIZE 256
115 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
116 #define ASTMAN_APPEND_BUF_INITSIZE 256
118 static struct permalias {
119 int num;
120 char *label;
121 } perms[] = {
122 { EVENT_FLAG_SYSTEM, "system" },
123 { EVENT_FLAG_CALL, "call" },
124 { EVENT_FLAG_LOG, "log" },
125 { EVENT_FLAG_VERBOSE, "verbose" },
126 { EVENT_FLAG_COMMAND, "command" },
127 { EVENT_FLAG_AGENT, "agent" },
128 { EVENT_FLAG_USER, "user" },
129 { EVENT_FLAG_CONFIG, "config" },
130 { -1, "all" },
131 { 0, "none" },
134 #define MAX_BLACKLIST_CMD_LEN 2
135 static struct {
136 char *words[AST_MAX_CMD_LEN];
137 } command_blacklist[] = {
138 {{ "module", "load", NULL }},
139 {{ "module", "unload", NULL }},
142 struct mansession {
143 /*! Execution thread */
144 pthread_t t;
145 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
146 ast_mutex_t __lock;
147 /*! socket address */
148 struct sockaddr_in sin;
149 /*! TCP socket */
150 int fd;
151 /*! Whether an HTTP manager is in use */
152 int inuse;
153 /*! Whether an HTTP session should be destroyed */
154 int needdestroy;
155 /*! Whether an HTTP session has someone waiting on events */
156 pthread_t waiting_thread;
157 /*! Unique manager identifer */
158 uint32_t managerid;
159 /*! Session timeout if HTTP */
160 time_t sessiontimeout;
161 /*! Output from manager interface */
162 struct ast_dynamic_str *outputstr;
163 /*! Logged in username */
164 char username[80];
165 /*! Authentication challenge */
166 char challenge[10];
167 /*! Authentication status */
168 int authenticated;
169 /*! Authorization for reading */
170 int readperm;
171 /*! Authorization for writing */
172 int writeperm;
173 /*! Buffer */
174 char inbuf[1024];
175 int inlen;
176 int send_events;
177 int displaysystemname; /*!< Add system name to manager responses and events */
178 /* Queued events that we've not had the ability to send yet */
179 struct eventqent *eventq;
180 /* Timeout for ast_carefulwrite() */
181 int writetimeout;
182 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
183 AST_LIST_ENTRY(mansession) list;
186 static AST_LIST_HEAD_STATIC(sessions, mansession);
188 struct ast_manager_user {
189 char username[80];
190 char *secret;
191 char *deny;
192 char *permit;
193 char *read;
194 char *write;
195 unsigned int displayconnects:1;
196 int keep;
197 AST_LIST_ENTRY(ast_manager_user) list;
200 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
202 static struct manager_action *first_action;
203 AST_RWLOCK_DEFINE_STATIC(actionlock);
205 /*! \brief Convert authority code to string with serveral options */
206 static char *authority_to_str(int authority, char *res, int reslen)
208 int running_total = 0, i;
210 memset(res, 0, reslen);
211 for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
212 if (authority & perms[i].num) {
213 if (*res) {
214 strncat(res, ",", (reslen > running_total) ? reslen - running_total - 1 : 0);
215 running_total++;
217 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total - 1 : 0);
218 running_total += strlen(perms[i].label);
222 if (ast_strlen_zero(res))
223 ast_copy_string(res, "<none>", reslen);
225 return res;
228 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
230 struct manager_action *cur;
231 int which = 0;
232 char *ret = NULL;
234 ast_rwlock_rdlock(&actionlock);
235 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
236 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
237 ret = ast_strdup(cur->action);
238 break; /* make sure we exit even if ast_strdup() returns NULL */
241 ast_rwlock_unlock(&actionlock);
243 return ret;
246 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
248 while (*src && (*maxlen > 6)) {
249 switch (*src) {
250 case '<':
251 strcpy(*dst, "&lt;");
252 (*dst) += 4;
253 *maxlen -= 4;
254 break;
255 case '>':
256 strcpy(*dst, "&gt;");
257 (*dst) += 4;
258 *maxlen -= 4;
259 break;
260 case '\"':
261 strcpy(*dst, "&quot;");
262 (*dst) += 6;
263 *maxlen -= 6;
264 break;
265 case '\'':
266 strcpy(*dst, "&apos;");
267 (*dst) += 6;
268 *maxlen -= 6;
269 break;
270 case '&':
271 strcpy(*dst, "&amp;");
272 (*dst) += 5;
273 *maxlen -= 5;
274 break;
275 default:
276 *(*dst)++ = lower ? tolower(*src) : *src;
277 (*maxlen)--;
279 src++;
283 struct variable_count {
284 char *varname;
285 int count;
288 static int compress_char(char c)
290 c &= 0x7f;
291 if (c < 32)
292 return 0;
293 else if (c >= 'a' && c <= 'z')
294 return c - 64;
295 else if (c > 'z')
296 return '_';
297 else
298 return c - 32;
301 static int variable_count_hash_fn(const void *vvc, const int flags)
303 const struct variable_count *vc = vvc;
304 int res = 0, i;
305 for (i = 0; i < 5; i++) {
306 if (vc->varname[i] == '\0')
307 break;
308 res += compress_char(vc->varname[i]) << (i * 6);
310 return res;
313 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
315 /* Due to the simplicity of struct variable_count, it makes no difference
316 * if you pass in objects or strings, the same operation applies. This is
317 * due to the fact that the hash occurs on the first element, which means
318 * the address of both the struct and the string are exactly the same. */
319 struct variable_count *vc = obj;
320 char *str = vstr;
321 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
324 static char *xml_translate(char *in, struct ast_variable *vars)
326 struct ast_variable *v;
327 char *dest = NULL;
328 char *out, *tmp, *var, *val;
329 char *objtype = NULL;
330 int colons = 0;
331 int breaks = 0;
332 size_t len;
333 int count = 1;
334 int escaped = 0;
335 int inobj = 0;
336 int x;
337 struct variable_count *vc = NULL;
338 struct ao2_container *vco = NULL;
340 for (v = vars; v; v = v->next) {
341 if (!dest && !strcasecmp(v->name, "ajaxdest"))
342 dest = v->value;
343 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
344 objtype = v->value;
346 if (!dest)
347 dest = "unknown";
348 if (!objtype)
349 objtype = "generic";
350 for (x = 0; in[x]; x++) {
351 if (in[x] == ':')
352 colons++;
353 else if (in[x] == '\n')
354 breaks++;
355 else if (strchr("&\"<>\'", in[x]))
356 escaped++;
358 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
359 out = ast_malloc(len);
360 if (!out)
361 return 0;
362 tmp = out;
363 while (*in) {
364 var = in;
365 while (*in && (*in >= 32))
366 in++;
367 if (*in) {
368 if ((count > 3) && inobj) {
369 ast_build_string(&tmp, &len, " /></response>\n");
370 inobj = 0;
372 /* Entity is closed, so close out the name cache */
373 ao2_ref(vco, -1);
374 vco = NULL;
376 count = 0;
377 while (*in && (*in < 32)) {
378 *in = '\0';
379 in++;
380 count++;
382 val = strchr(var, ':');
383 if (val) {
384 *val = '\0';
385 val++;
386 if (*val == ' ')
387 val++;
388 if (!inobj) {
389 vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
390 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
391 inobj = 1;
394 /* Check if the var has been used already */
395 if ((vc = ao2_find(vco, var, 0)))
396 vc->count++;
397 else {
398 /* Create a new entry for this one */
399 vc = ao2_alloc(sizeof(*vc), NULL);
400 vc->varname = var;
401 vc->count = 1;
402 ao2_link(vco, vc);
405 ast_build_string(&tmp, &len, " ");
406 xml_copy_escape(&tmp, &len, var, 1);
407 if (vc->count > 1)
408 ast_build_string(&tmp, &len, "-%d", vc->count);
409 ast_build_string(&tmp, &len, "='");
410 xml_copy_escape(&tmp, &len, val, 0);
411 ast_build_string(&tmp, &len, "'");
412 ao2_ref(vc, -1);
416 if (inobj)
417 ast_build_string(&tmp, &len, " /></response>\n");
418 if (vco)
419 ao2_ref(vco, -1);
420 return out;
423 static char *html_translate(char *in)
425 int x;
426 int colons = 0;
427 int breaks = 0;
428 size_t len;
429 int count = 1;
430 char *tmp, *var, *val, *out;
432 for (x=0; in[x]; x++) {
433 if (in[x] == ':')
434 colons++;
435 if (in[x] == '\n')
436 breaks++;
438 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
439 out = ast_malloc(len);
440 if (!out)
441 return 0;
442 tmp = out;
443 while (*in) {
444 var = in;
445 while (*in && (*in >= 32))
446 in++;
447 if (*in) {
448 if ((count % 4) == 0){
449 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
451 count = 0;
452 while (*in && (*in < 32)) {
453 *in = '\0';
454 in++;
455 count++;
457 val = strchr(var, ':');
458 if (val) {
459 *val = '\0';
460 val++;
461 if (*val == ' ')
462 val++;
463 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
467 return out;
472 static struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
474 struct ast_manager_user *user = NULL;
476 AST_LIST_TRAVERSE(&users, user, list)
477 if (!strcasecmp(user->username, name))
478 break;
479 return user;
482 void astman_append(struct mansession *s, const char *fmt, ...)
484 va_list ap;
485 struct ast_dynamic_str *buf;
487 ast_mutex_lock(&s->__lock);
489 if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
490 ast_mutex_unlock(&s->__lock);
491 return;
494 va_start(ap, fmt);
495 ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
496 va_end(ap);
498 if (s->fd > -1)
499 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
500 else {
501 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr)))) {
502 ast_mutex_unlock(&s->__lock);
503 return;
506 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
509 ast_mutex_unlock(&s->__lock);
512 static int handle_showmancmd(int fd, int argc, char *argv[])
514 struct manager_action *cur;
515 char authority[80];
516 int num;
518 if (argc != 4)
519 return RESULT_SHOWUSAGE;
521 ast_rwlock_rdlock(&actionlock);
522 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
523 for (num = 3; num < argc; num++) {
524 if (!strcasecmp(cur->action, argv[num])) {
525 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 : "");
529 ast_rwlock_unlock(&actionlock);
531 return RESULT_SUCCESS;
534 static int handle_showmanager(int fd, int argc, char *argv[])
536 struct ast_manager_user *user = NULL;
538 if (argc != 4)
539 return RESULT_SHOWUSAGE;
541 AST_LIST_LOCK(&users);
543 if (!(user = ast_get_manager_by_name_locked(argv[3]))) {
544 ast_cli(fd, "There is no manager called %s\n", argv[3]);
545 AST_LIST_UNLOCK(&users);
546 return -1;
549 ast_cli(fd,"\n");
550 ast_cli(fd,
551 " username: %s\n"
552 " secret: %s\n"
553 " deny: %s\n"
554 " permit: %s\n"
555 " read: %s\n"
556 " write: %s\n"
557 "displayconnects: %s\n",
558 (user->username ? user->username : "(N/A)"),
559 (user->secret ? "<Set>" : "(N/A)"),
560 (user->deny ? user->deny : "(N/A)"),
561 (user->permit ? user->permit : "(N/A)"),
562 (user->read ? user->read : "(N/A)"),
563 (user->write ? user->write : "(N/A)"),
564 (user->displayconnects ? "yes" : "no"));
566 AST_LIST_UNLOCK(&users);
568 return RESULT_SUCCESS;
572 static int handle_showmanagers(int fd, int argc, char *argv[])
574 struct ast_manager_user *user = NULL;
575 int count_amu = 0;
577 if (argc != 3)
578 return RESULT_SHOWUSAGE;
580 AST_LIST_LOCK(&users);
582 /* If there are no users, print out something along those lines */
583 if (AST_LIST_EMPTY(&users)) {
584 ast_cli(fd, "There are no manager users.\n");
585 AST_LIST_UNLOCK(&users);
586 return RESULT_SUCCESS;
589 ast_cli(fd, "\nusername\n--------\n");
591 AST_LIST_TRAVERSE(&users, user, list) {
592 ast_cli(fd, "%s\n", user->username);
593 count_amu++;
596 AST_LIST_UNLOCK(&users);
598 ast_cli(fd,"-------------------\n");
599 ast_cli(fd,"%d manager users configured.\n", count_amu);
601 return RESULT_SUCCESS;
605 /*! \brief CLI command
606 Should change to "manager show commands" */
607 static int handle_showmancmds(int fd, int argc, char *argv[])
609 struct manager_action *cur;
610 char authority[80];
611 char *format = " %-15.15s %-15.15s %-55.55s\n";
613 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
614 ast_cli(fd, format, "------", "---------", "--------");
616 ast_rwlock_rdlock(&actionlock);
617 for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
618 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
619 ast_rwlock_unlock(&actionlock);
621 return RESULT_SUCCESS;
624 /*! \brief CLI command show manager connected */
625 /* Should change to "manager show connected" */
626 static int handle_showmanconn(int fd, int argc, char *argv[])
628 struct mansession *s;
629 char *format = " %-15.15s %-15.15s\n";
631 ast_cli(fd, format, "Username", "IP Address");
633 AST_LIST_LOCK(&sessions);
634 AST_LIST_TRAVERSE(&sessions, s, list)
635 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
636 AST_LIST_UNLOCK(&sessions);
638 return RESULT_SUCCESS;
641 /*! \brief CLI command show manager connected */
642 /* Should change to "manager show connected" */
643 static int handle_showmaneventq(int fd, int argc, char *argv[])
645 struct eventqent *s;
647 AST_LIST_LOCK(&sessions);
648 for (s = master_eventq; s; s = s->next) {
649 ast_cli(fd, "Usecount: %d\n",s->usecount);
650 ast_cli(fd, "Category: %d\n", s->category);
651 ast_cli(fd, "Event:\n%s", s->eventdata);
653 AST_LIST_UNLOCK(&sessions);
655 return RESULT_SUCCESS;
658 static char showmancmd_help[] =
659 "Usage: manager show command <actionname>\n"
660 " Shows the detailed description for a specific Asterisk manager interface command.\n";
662 static char showmancmds_help[] =
663 "Usage: manager show commands\n"
664 " Prints a listing of all the available Asterisk manager interface commands.\n";
666 static char showmanconn_help[] =
667 "Usage: manager show connected\n"
668 " Prints a listing of the users that are currently connected to the\n"
669 "Asterisk manager interface.\n";
671 static char showmaneventq_help[] =
672 "Usage: manager show eventq\n"
673 " Prints a listing of all events pending in the Asterisk manger\n"
674 "event queue.\n";
676 static char showmanagers_help[] =
677 "Usage: manager show users\n"
678 " Prints a listing of all managers that are currently configured on that\n"
679 " system.\n";
681 static char showmanager_help[] =
682 " Usage: manager show user <user>\n"
683 " Display all information related to the manager user specified.\n";
685 static struct ast_cli_entry cli_show_manager_command_deprecated = {
686 { "show", "manager", "command", NULL },
687 handle_showmancmd, NULL,
688 NULL, complete_show_mancmd };
690 static struct ast_cli_entry cli_show_manager_commands_deprecated = {
691 { "show", "manager", "commands", NULL },
692 handle_showmancmds, NULL,
693 NULL };
695 static struct ast_cli_entry cli_show_manager_connected_deprecated = {
696 { "show", "manager", "connected", NULL },
697 handle_showmanconn, NULL,
698 NULL };
700 static struct ast_cli_entry cli_show_manager_eventq_deprecated = {
701 { "show", "manager", "eventq", NULL },
702 handle_showmaneventq, NULL,
703 NULL };
705 static struct ast_cli_entry cli_manager[] = {
706 { { "manager", "show", "command", NULL },
707 handle_showmancmd, "Show a manager interface command",
708 showmancmd_help, complete_show_mancmd, &cli_show_manager_command_deprecated },
710 { { "manager", "show", "commands", NULL },
711 handle_showmancmds, "List manager interface commands",
712 showmancmds_help, NULL, &cli_show_manager_commands_deprecated },
714 { { "manager", "show", "connected", NULL },
715 handle_showmanconn, "List connected manager interface users",
716 showmanconn_help, NULL, &cli_show_manager_connected_deprecated },
718 { { "manager", "show", "eventq", NULL },
719 handle_showmaneventq, "List manager interface queued events",
720 showmaneventq_help, NULL, &cli_show_manager_eventq_deprecated },
722 { { "manager", "show", "users", NULL },
723 handle_showmanagers, "List configured manager users",
724 showmanagers_help, NULL, NULL },
726 { { "manager", "show", "user", NULL },
727 handle_showmanager, "Display information on a specific manager user",
728 showmanager_help, NULL, NULL },
731 static void unuse_eventqent(struct eventqent *e)
733 if (ast_atomic_dec_and_test(&e->usecount) && e->next)
734 pthread_kill(t, SIGURG);
737 static void free_session(struct mansession *s)
739 struct eventqent *eqe;
740 if (s->fd > -1)
741 close(s->fd);
742 if (s->outputstr)
743 free(s->outputstr);
744 ast_mutex_destroy(&s->__lock);
745 while (s->eventq) {
746 eqe = s->eventq;
747 s->eventq = s->eventq->next;
748 unuse_eventqent(eqe);
750 free(s);
753 static void destroy_session(struct mansession *s)
755 AST_LIST_LOCK(&sessions);
756 AST_LIST_REMOVE(&sessions, s, list);
757 num_sessions--;
758 free_session(s);
759 AST_LIST_UNLOCK(&sessions);
762 const char *astman_get_header(const struct message *m, char *var)
764 char cmp[80];
765 int x;
767 snprintf(cmp, sizeof(cmp), "%s: ", var);
769 for (x = 0; x < m->hdrcount; x++) {
770 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
771 return m->headers[x] + strlen(cmp);
774 return "";
777 struct ast_variable *astman_get_variables(const struct message *m)
779 int varlen, x, y;
780 struct ast_variable *head = NULL, *cur;
781 char *var, *val;
783 char *parse;
784 AST_DECLARE_APP_ARGS(args,
785 AST_APP_ARG(vars)[32];
788 varlen = strlen("Variable: ");
790 for (x = 0; x < m->hdrcount; x++) {
791 if (strncasecmp("Variable: ", m->headers[x], varlen))
792 continue;
794 parse = ast_strdupa(m->headers[x] + varlen);
796 AST_STANDARD_APP_ARGS(args, parse);
797 if (args.argc) {
798 for (y = 0; y < args.argc; y++) {
799 if (!args.vars[y])
800 continue;
801 var = val = ast_strdupa(args.vars[y]);
802 strsep(&val, "=");
803 if (!val || ast_strlen_zero(var))
804 continue;
805 cur = ast_variable_new(var, val);
806 if (head) {
807 cur->next = head;
808 head = cur;
809 } else
810 head = cur;
815 return head;
818 /*! \note NOTE:
819 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
820 hold the session lock _or_ be running in an action callback (in which case s->busy will
821 be non-zero). In either of these cases, there is no need to lock-protect the session's
822 fd, since no other output will be sent (events will be queued), and no input will
823 be read until either the current action finishes or get_input() obtains the session
824 lock.
826 void astman_send_error(struct mansession *s, const struct message *m, char *error)
828 const char *id = astman_get_header(m,"ActionID");
830 astman_append(s, "Response: Error\r\n");
831 if (!ast_strlen_zero(id))
832 astman_append(s, "ActionID: %s\r\n", id);
833 astman_append(s, "Message: %s\r\n\r\n", error);
836 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
838 const char *id = astman_get_header(m,"ActionID");
840 astman_append(s, "Response: %s\r\n", resp);
841 if (!ast_strlen_zero(id))
842 astman_append(s, "ActionID: %s\r\n", id);
843 if (msg)
844 astman_append(s, "Message: %s\r\n\r\n", msg);
845 else
846 astman_append(s, "\r\n");
849 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
851 astman_send_response(s, m, "Success", msg);
854 /*! Tells you if smallstr exists inside bigstr
855 which is delim by delim and uses no buf or stringsep
856 ast_instring("this|that|more","this",',') == 1;
858 feel free to move this to app.c -anthm */
859 static int ast_instring(const char *bigstr, const char *smallstr, char delim)
861 const char *val = bigstr, *next;
863 do {
864 if ((next = strchr(val, delim))) {
865 if (!strncmp(val, smallstr, (next - val)))
866 return 1;
867 else
868 continue;
869 } else
870 return !strcmp(smallstr, val);
872 } while (*(val = (next + 1)));
874 return 0;
877 static int get_perm(const char *instr)
879 int x = 0, ret = 0;
881 if (!instr)
882 return 0;
884 for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
885 if (ast_instring(instr, perms[x].label, ','))
886 ret |= perms[x].num;
889 return ret;
892 static int ast_is_number(const char *string)
894 int ret = 1, x = 0;
896 if (!string)
897 return 0;
899 for (x = 0; x < strlen(string); x++) {
900 if (!(string[x] >= 48 && string[x] <= 57)) {
901 ret = 0;
902 break;
906 return ret ? atoi(string) : 0;
909 static int strings_to_mask(const char *string)
911 int x, ret = -1;
913 x = ast_is_number(string);
915 if (x)
916 ret = x;
917 else if (ast_strlen_zero(string))
918 ret = -1;
919 else if (ast_false(string))
920 ret = 0;
921 else if (ast_true(string)) {
922 ret = 0;
923 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
924 ret |= perms[x].num;
925 } else {
926 ret = 0;
927 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
928 if (ast_instring(string, perms[x].label, ','))
929 ret |= perms[x].num;
933 return ret;
936 /*! \brief
937 Rather than braindead on,off this now can also accept a specific int mask value
938 or a ',' delim list of mask strings (the same as manager.conf) -anthm
940 static int set_eventmask(struct mansession *s, const char *eventmask)
942 int maskint = strings_to_mask(eventmask);
944 ast_mutex_lock(&s->__lock);
945 if (maskint >= 0)
946 s->send_events = maskint;
947 ast_mutex_unlock(&s->__lock);
949 return maskint;
952 static int authenticate(struct mansession *s, const struct message *m)
954 struct ast_config *cfg;
955 char *cat;
956 const char *user = astman_get_header(m, "Username");
957 const char *pass = astman_get_header(m, "Secret");
958 const char *authtype = astman_get_header(m, "AuthType");
959 const char *key = astman_get_header(m, "Key");
960 const char *events = astman_get_header(m, "Events");
962 cfg = ast_config_load("manager.conf");
963 if (!cfg)
964 return -1;
965 cat = ast_category_browse(cfg, NULL);
966 while (cat) {
967 if (strcasecmp(cat, "general")) {
968 /* This is a user */
969 if (!strcasecmp(cat, user)) {
970 struct ast_variable *v;
971 struct ast_ha *ha = NULL;
972 char *password = NULL;
974 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
975 if (!strcasecmp(v->name, "secret")) {
976 password = v->value;
977 } else if (!strcasecmp(v->name, "displaysystemname")) {
978 if (ast_true(v->value)) {
979 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
980 s->displaysystemname = 1;
981 } else {
982 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
985 } else if (!strcasecmp(v->name, "permit") ||
986 !strcasecmp(v->name, "deny")) {
987 ha = ast_append_ha(v->name, v->value, ha);
988 } else if (!strcasecmp(v->name, "writetimeout")) {
989 int val = atoi(v->value);
991 if (val < 100)
992 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
993 else
994 s->writetimeout = val;
998 if (ha && !ast_apply_ha(ha, &(s->sin))) {
999 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1000 ast_free_ha(ha);
1001 ast_config_destroy(cfg);
1002 return -1;
1003 } else if (ha)
1004 ast_free_ha(ha);
1005 if (!strcasecmp(authtype, "MD5")) {
1006 if (!ast_strlen_zero(key) &&
1007 !ast_strlen_zero(s->challenge) && !ast_strlen_zero(password)) {
1008 int x;
1009 int len = 0;
1010 char md5key[256] = "";
1011 struct MD5Context md5;
1012 unsigned char digest[16];
1013 MD5Init(&md5);
1014 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1015 MD5Update(&md5, (unsigned char *) password, strlen(password));
1016 MD5Final(digest, &md5);
1017 for (x=0; x<16; x++)
1018 len += sprintf(md5key + len, "%2.2x", digest[x]);
1019 if (!strcmp(md5key, key))
1020 break;
1021 else {
1022 ast_config_destroy(cfg);
1023 return -1;
1025 } else {
1026 ast_log(LOG_DEBUG, "MD5 authentication is not possible. challenge: '%s'\n",
1027 S_OR(s->challenge, ""));
1028 ast_config_destroy(cfg);
1029 return -1;
1031 } else if (password && !strcmp(password, pass)) {
1032 break;
1033 } else {
1034 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1035 ast_config_destroy(cfg);
1036 return -1;
1040 cat = ast_category_browse(cfg, cat);
1042 if (cat) {
1043 ast_copy_string(s->username, cat, sizeof(s->username));
1044 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
1045 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
1046 ast_config_destroy(cfg);
1047 if (events)
1048 set_eventmask(s, events);
1049 return 0;
1051 ast_config_destroy(cfg);
1052 cfg = ast_config_load("users.conf");
1053 if (!cfg)
1054 return -1;
1055 cat = ast_category_browse(cfg, NULL);
1056 while (cat) {
1057 struct ast_variable *v;
1058 const char *password = NULL;
1059 int hasmanager = 0;
1060 const char *readperms = NULL;
1061 const char *writeperms = NULL;
1063 if (strcasecmp(cat, user) || !strcasecmp(cat, "general")) {
1064 cat = ast_category_browse(cfg, cat);
1065 continue;
1067 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
1068 if (!strcasecmp(v->name, "secret"))
1069 password = v->value;
1070 else if (!strcasecmp(v->name, "hasmanager"))
1071 hasmanager = ast_true(v->value);
1072 else if (!strcasecmp(v->name, "managerread"))
1073 readperms = v->value;
1074 else if (!strcasecmp(v->name, "managerwrite"))
1075 writeperms = v->value;
1077 if (!hasmanager)
1078 break;
1079 if (!password || strcmp(password, pass)) {
1080 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1081 ast_config_destroy(cfg);
1082 return -1;
1084 ast_copy_string(s->username, cat, sizeof(s->username));
1085 s->readperm = readperms ? get_perm(readperms) : -1;
1086 s->writeperm = writeperms ? get_perm(writeperms) : -1;
1087 ast_config_destroy(cfg);
1088 if (events)
1089 set_eventmask(s, events);
1090 return 0;
1092 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1093 ast_config_destroy(cfg);
1094 return -1;
1097 /*! \brief Manager PING */
1098 static char mandescr_ping[] =
1099 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
1100 " manager connection open.\n"
1101 "Variables: NONE\n";
1103 static int action_ping(struct mansession *s, const struct message *m)
1105 astman_send_response(s, m, "Pong", NULL);
1106 return 0;
1109 static char mandescr_getconfig[] =
1110 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1111 "file by category and contents.\n"
1112 "Variables:\n"
1113 " Filename: Configuration filename (e.g. foo.conf)\n";
1115 static int action_getconfig(struct mansession *s, const struct message *m)
1117 struct ast_config *cfg;
1118 const char *fn = astman_get_header(m, "Filename");
1119 int catcount = 0;
1120 int lineno = 0;
1121 char *category=NULL;
1122 struct ast_variable *v;
1123 char idText[256] = "";
1124 const char *id = astman_get_header(m, "ActionID");
1126 if (!ast_strlen_zero(id))
1127 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1129 if (ast_strlen_zero(fn)) {
1130 astman_send_error(s, m, "Filename not specified");
1131 return 0;
1133 if (!(cfg = ast_config_load_with_comments(fn))) {
1134 astman_send_error(s, m, "Config file not found");
1135 return 0;
1137 astman_append(s, "Response: Success\r\n%s", idText);
1138 while ((category = ast_category_browse(cfg, category))) {
1139 lineno = 0;
1140 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1141 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1142 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1143 catcount++;
1145 ast_config_destroy(cfg);
1146 astman_append(s, "\r\n");
1148 return 0;
1152 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
1154 int x;
1155 char hdr[40];
1156 const char *action, *cat, *var, *value, *match;
1157 struct ast_category *category;
1158 struct ast_variable *v;
1160 for (x=0;x<100000;x++) {
1161 unsigned int object = 0;
1163 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1164 action = astman_get_header(m, hdr);
1165 if (ast_strlen_zero(action))
1166 break;
1167 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1168 cat = astman_get_header(m, hdr);
1169 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1170 var = astman_get_header(m, hdr);
1171 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1172 value = astman_get_header(m, hdr);
1173 if (!ast_strlen_zero(value) && *value == '>') {
1174 object = 1;
1175 value++;
1177 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1178 match = astman_get_header(m, hdr);
1179 if (!strcasecmp(action, "newcat")) {
1180 if (!ast_strlen_zero(cat)) {
1181 category = ast_category_new(cat);
1182 if (category) {
1183 ast_category_append(cfg, category);
1186 } else if (!strcasecmp(action, "renamecat")) {
1187 if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1188 category = ast_category_get(cfg, cat);
1189 if (category)
1190 ast_category_rename(category, value);
1192 } else if (!strcasecmp(action, "delcat")) {
1193 if (!ast_strlen_zero(cat))
1194 ast_category_delete(cfg, (char *) cat);
1195 } else if (!strcasecmp(action, "update")) {
1196 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1197 ast_variable_update(category, var, value, match, object);
1198 } else if (!strcasecmp(action, "delete")) {
1199 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1200 ast_variable_delete(category, (char *) var, (char *) match);
1201 } else if (!strcasecmp(action, "append")) {
1202 if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1203 (category = ast_category_get(cfg, cat)) &&
1204 (v = ast_variable_new(var, value))){
1205 if (object || (match && !strcasecmp(match, "object")))
1206 v->object = 1;
1207 ast_variable_append(category, v);
1213 static char mandescr_updateconfig[] =
1214 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1215 "configuration elements in Asterisk configuration files.\n"
1216 "Variables (X's represent 6 digit number beginning with 000000):\n"
1217 " SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
1218 " DstFilename: Configuration filename to write(e.g. foo.conf)\n"
1219 " Reload: Whether or not a reload should take place (or name of specific module)\n"
1220 " Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1221 " Cat-XXXXXX: Category to operate on\n"
1222 " Var-XXXXXX: Variable to work on\n"
1223 " Value-XXXXXX: Value to work on\n"
1224 " Match-XXXXXX: Extra match required to match line\n";
1226 static int action_updateconfig(struct mansession *s, const struct message *m)
1228 struct ast_config *cfg;
1229 const char *sfn = astman_get_header(m, "SrcFilename");
1230 const char *dfn = astman_get_header(m, "DstFilename");
1231 int res;
1232 char idText[256] = "";
1233 const char *id = astman_get_header(m, "ActionID");
1234 const char *rld = astman_get_header(m, "Reload");
1236 if (!ast_strlen_zero(id))
1237 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1239 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1240 astman_send_error(s, m, "Filename not specified");
1241 return 0;
1243 if (!(cfg = ast_config_load_with_comments(sfn))) {
1244 astman_send_error(s, m, "Config file not found");
1245 return 0;
1247 handle_updates(s, m, cfg);
1248 res = config_text_file_save(dfn, cfg, "Manager");
1249 ast_config_destroy(cfg);
1250 if (res) {
1251 astman_send_error(s, m, "Save of config failed");
1252 return 0;
1254 astman_append(s, "Response: Success\r\n%s\r\n", idText);
1255 if (!ast_strlen_zero(rld)) {
1256 if (ast_true(rld))
1257 rld = NULL;
1258 ast_module_reload(rld);
1260 return 0;
1263 /*! \brief Manager WAITEVENT */
1264 static char mandescr_waitevent[] =
1265 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
1266 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
1267 "session, events will be generated and queued.\n"
1268 "Variables: \n"
1269 " Timeout: Maximum time to wait for events\n";
1271 static int action_waitevent(struct mansession *s, const struct message *m)
1273 const char *timeouts = astman_get_header(m, "Timeout");
1274 int timeout = -1, max;
1275 int x;
1276 int needexit = 0;
1277 time_t now;
1278 struct eventqent *eqe;
1279 const char *id = astman_get_header(m,"ActionID");
1280 char idText[256] = "";
1282 if (!ast_strlen_zero(id))
1283 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1285 if (!ast_strlen_zero(timeouts)) {
1286 sscanf(timeouts, "%i", &timeout);
1289 ast_mutex_lock(&s->__lock);
1290 if (s->waiting_thread != AST_PTHREADT_NULL) {
1291 pthread_kill(s->waiting_thread, SIGURG);
1293 if (s->sessiontimeout) {
1294 time(&now);
1295 max = s->sessiontimeout - now - 10;
1296 if (max < 0)
1297 max = 0;
1298 if ((timeout < 0) || (timeout > max))
1299 timeout = max;
1300 if (!s->send_events)
1301 s->send_events = -1;
1302 /* Once waitevent is called, always queue events from now on */
1304 ast_mutex_unlock(&s->__lock);
1305 s->waiting_thread = pthread_self();
1306 if (option_debug)
1307 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1308 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1309 ast_mutex_lock(&s->__lock);
1310 if (s->eventq && s->eventq->next)
1311 needexit = 1;
1312 if (s->waiting_thread != pthread_self())
1313 needexit = 1;
1314 if (s->needdestroy)
1315 needexit = 1;
1316 ast_mutex_unlock(&s->__lock);
1317 if (needexit)
1318 break;
1319 if (s->fd > 0) {
1320 if (ast_wait_for_input(s->fd, 1000))
1321 break;
1322 } else {
1323 sleep(1);
1326 if (option_debug)
1327 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1328 ast_mutex_lock(&s->__lock);
1329 if (s->waiting_thread == pthread_self()) {
1330 astman_send_response(s, m, "Success", "Waiting for Event...");
1331 /* Only show events if we're the most recent waiter */
1332 while(s->eventq->next) {
1333 eqe = s->eventq->next;
1334 if (((s->readperm & eqe->category) == eqe->category) &&
1335 ((s->send_events & eqe->category) == eqe->category)) {
1336 astman_append(s, "%s", eqe->eventdata);
1338 unuse_eventqent(s->eventq);
1339 s->eventq = eqe;
1341 astman_append(s,
1342 "Event: WaitEventComplete\r\n"
1343 "%s"
1344 "\r\n", idText);
1345 s->waiting_thread = AST_PTHREADT_NULL;
1346 } else {
1347 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1349 ast_mutex_unlock(&s->__lock);
1350 return 0;
1353 static char mandescr_listcommands[] =
1354 "Description: Returns the action name and synopsis for every\n"
1355 " action that is available to the user\n"
1356 "Variables: NONE\n";
1358 /*! \note The actionlock is read-locked by the caller of this function */
1359 static int action_listcommands(struct mansession *s, const struct message *m)
1361 struct manager_action *cur;
1362 char idText[256] = "";
1363 char temp[BUFSIZ];
1364 const char *id = astman_get_header(m,"ActionID");
1366 if (!ast_strlen_zero(id))
1367 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1368 astman_append(s, "Response: Success\r\n%s", idText);
1369 for (cur = first_action; cur; cur = cur->next) {
1370 if ((s->writeperm & cur->authority) == cur->authority)
1371 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1373 astman_append(s, "\r\n");
1375 return 0;
1378 static char mandescr_events[] =
1379 "Description: Enable/Disable sending of events to this manager\n"
1380 " client.\n"
1381 "Variables:\n"
1382 " EventMask: 'on' if all events should be sent,\n"
1383 " 'off' if no events should be sent,\n"
1384 " 'system,call,log' to select which flags events should have to be sent.\n";
1386 static int action_events(struct mansession *s, const struct message *m)
1388 const char *mask = astman_get_header(m, "EventMask");
1389 int res;
1391 res = set_eventmask(s, mask);
1392 if (res > 0)
1393 astman_send_response(s, m, "Events On", NULL);
1394 else if (res == 0)
1395 astman_send_response(s, m, "Events Off", NULL);
1397 return 0;
1400 static char mandescr_logoff[] =
1401 "Description: Logoff this manager session\n"
1402 "Variables: NONE\n";
1404 static int action_logoff(struct mansession *s, const struct message *m)
1406 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1407 return -1;
1410 static char mandescr_hangup[] =
1411 "Description: Hangup a channel\n"
1412 "Variables: \n"
1413 " Channel: The channel name to be hungup\n";
1415 static int action_hangup(struct mansession *s, const struct message *m)
1417 struct ast_channel *c = NULL;
1418 const char *name = astman_get_header(m, "Channel");
1419 if (ast_strlen_zero(name)) {
1420 astman_send_error(s, m, "No channel specified");
1421 return 0;
1423 c = ast_get_channel_by_name_locked(name);
1424 if (!c) {
1425 astman_send_error(s, m, "No such channel");
1426 return 0;
1428 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1429 ast_channel_unlock(c);
1430 astman_send_ack(s, m, "Channel Hungup");
1431 return 0;
1434 static char mandescr_setvar[] =
1435 "Description: Set a global or local channel variable.\n"
1436 "Variables: (Names marked with * are required)\n"
1437 " Channel: Channel to set variable for\n"
1438 " *Variable: Variable name\n"
1439 " *Value: Value\n";
1441 static int action_setvar(struct mansession *s, const struct message *m)
1443 struct ast_channel *c = NULL;
1444 const char *name = astman_get_header(m, "Channel");
1445 const char *varname = astman_get_header(m, "Variable");
1446 const char *varval = astman_get_header(m, "Value");
1448 if (ast_strlen_zero(varname)) {
1449 astman_send_error(s, m, "No variable specified");
1450 return 0;
1453 if (!ast_strlen_zero(name)) {
1454 c = ast_get_channel_by_name_locked(name);
1455 if (!c) {
1456 astman_send_error(s, m, "No such channel");
1457 return 0;
1461 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1463 if (c)
1464 ast_channel_unlock(c);
1466 astman_send_ack(s, m, "Variable Set");
1468 return 0;
1471 static char mandescr_getvar[] =
1472 "Description: Get the value of a global or local channel variable.\n"
1473 "Variables: (Names marked with * are required)\n"
1474 " Channel: Channel to read variable from\n"
1475 " *Variable: Variable name\n"
1476 " ActionID: Optional Action id for message matching.\n";
1478 static int action_getvar(struct mansession *s, const struct message *m)
1480 struct ast_channel *c = NULL;
1481 const char *name = astman_get_header(m, "Channel");
1482 const char *varname = astman_get_header(m, "Variable");
1483 const char *id = astman_get_header(m,"ActionID");
1484 char *varval;
1485 char workspace[1024] = "";
1487 if (ast_strlen_zero(varname)) {
1488 astman_send_error(s, m, "No variable specified");
1489 return 0;
1492 if (!ast_strlen_zero(name)) {
1493 c = ast_get_channel_by_name_locked(name);
1494 if (!c) {
1495 astman_send_error(s, m, "No such channel");
1496 return 0;
1500 if (varname[strlen(varname) - 1] == ')') {
1501 char *copy = ast_strdupa(varname);
1503 ast_func_read(c, copy, workspace, sizeof(workspace));
1504 varval = workspace;
1505 } else {
1506 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1509 if (c)
1510 ast_channel_unlock(c);
1511 astman_append(s, "Response: Success\r\n"
1512 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1513 if (!ast_strlen_zero(id))
1514 astman_append(s, "ActionID: %s\r\n",id);
1515 astman_append(s, "\r\n");
1517 return 0;
1521 /*! \brief Manager "status" command to show channels */
1522 /* Needs documentation... */
1523 static int action_status(struct mansession *s, const struct message *m)
1525 const char *id = astman_get_header(m,"ActionID");
1526 const char *name = astman_get_header(m,"Channel");
1527 char idText[256] = "";
1528 struct ast_channel *c;
1529 char bridge[256];
1530 struct timeval now = ast_tvnow();
1531 long elapsed_seconds = 0;
1532 int all = ast_strlen_zero(name); /* set if we want all channels */
1534 if (!ast_strlen_zero(id))
1535 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1536 if (all)
1537 c = ast_channel_walk_locked(NULL);
1538 else {
1539 c = ast_get_channel_by_name_locked(name);
1540 if (!c) {
1541 astman_send_error(s, m, "No such channel");
1542 return 0;
1545 astman_send_ack(s, m, "Channel status will follow");
1546 /* if we look by name, we break after the first iteration */
1547 while (c) {
1548 if (c->_bridge)
1549 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1550 else
1551 bridge[0] = '\0';
1552 if (c->pbx) {
1553 if (c->cdr) {
1554 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1556 astman_append(s,
1557 "Event: Status\r\n"
1558 "Privilege: Call\r\n"
1559 "Channel: %s\r\n"
1560 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1561 "CallerIDNum: %s\r\n"
1562 "CallerIDName: %s\r\n"
1563 "Account: %s\r\n"
1564 "State: %s\r\n"
1565 "Context: %s\r\n"
1566 "Extension: %s\r\n"
1567 "Priority: %d\r\n"
1568 "Seconds: %ld\r\n"
1569 "%s"
1570 "Uniqueid: %s\r\n"
1571 "%s"
1572 "\r\n",
1573 c->name,
1574 S_OR(c->cid.cid_num, "<unknown>"),
1575 S_OR(c->cid.cid_num, "<unknown>"),
1576 S_OR(c->cid.cid_name, "<unknown>"),
1577 c->accountcode,
1578 ast_state2str(c->_state), c->context,
1579 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1580 } else {
1581 astman_append(s,
1582 "Event: Status\r\n"
1583 "Privilege: Call\r\n"
1584 "Channel: %s\r\n"
1585 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1586 "CallerIDNum: %s\r\n"
1587 "CallerIDName: %s\r\n"
1588 "Account: %s\r\n"
1589 "State: %s\r\n"
1590 "%s"
1591 "Uniqueid: %s\r\n"
1592 "%s"
1593 "\r\n",
1594 c->name,
1595 S_OR(c->cid.cid_num, "<unknown>"),
1596 S_OR(c->cid.cid_num, "<unknown>"),
1597 S_OR(c->cid.cid_name, "<unknown>"),
1598 c->accountcode,
1599 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1601 ast_channel_unlock(c);
1602 if (!all)
1603 break;
1604 c = ast_channel_walk_locked(c);
1606 astman_append(s,
1607 "Event: StatusComplete\r\n"
1608 "%s"
1609 "\r\n",idText);
1610 return 0;
1613 static char mandescr_redirect[] =
1614 "Description: Redirect (transfer) a call.\n"
1615 "Variables: (Names marked with * are required)\n"
1616 " *Channel: Channel to redirect\n"
1617 " ExtraChannel: Second call leg to transfer (optional)\n"
1618 " *Exten: Extension to transfer to\n"
1619 " *Context: Context to transfer to\n"
1620 " *Priority: Priority to transfer to\n"
1621 " ActionID: Optional Action id for message matching.\n";
1623 /*! \brief action_redirect: The redirect manager command */
1624 static int action_redirect(struct mansession *s, const struct message *m)
1626 const char *name = astman_get_header(m, "Channel");
1627 const char *name2 = astman_get_header(m, "ExtraChannel");
1628 const char *exten = astman_get_header(m, "Exten");
1629 const char *context = astman_get_header(m, "Context");
1630 const char *priority = astman_get_header(m, "Priority");
1631 struct ast_channel *chan, *chan2 = NULL;
1632 int pi = 0;
1633 int res;
1635 if (ast_strlen_zero(name)) {
1636 astman_send_error(s, m, "Channel not specified");
1637 return 0;
1639 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1640 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1641 astman_send_error(s, m, "Invalid priority");
1642 return 0;
1645 /* XXX watch out, possible deadlock!!! */
1646 chan = ast_get_channel_by_name_locked(name);
1647 if (!chan) {
1648 char buf[BUFSIZ];
1649 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1650 astman_send_error(s, m, buf);
1651 return 0;
1653 if (ast_check_hangup(chan)) {
1654 astman_send_error(s, m, "Redirect failed, channel not up.");
1655 ast_channel_unlock(chan);
1656 return 0;
1658 if (!ast_strlen_zero(name2))
1659 chan2 = ast_get_channel_by_name_locked(name2);
1660 if (chan2 && ast_check_hangup(chan2)) {
1661 astman_send_error(s, m, "Redirect failed, extra channel not up.");
1662 ast_channel_unlock(chan);
1663 ast_channel_unlock(chan2);
1664 return 0;
1666 res = ast_async_goto(chan, context, exten, pi);
1667 if (!res) {
1668 if (!ast_strlen_zero(name2)) {
1669 if (chan2)
1670 res = ast_async_goto(chan2, context, exten, pi);
1671 else
1672 res = -1;
1673 if (!res)
1674 astman_send_ack(s, m, "Dual Redirect successful");
1675 else
1676 astman_send_error(s, m, "Secondary redirect failed");
1677 } else
1678 astman_send_ack(s, m, "Redirect successful");
1679 } else
1680 astman_send_error(s, m, "Redirect failed");
1681 if (chan)
1682 ast_channel_unlock(chan);
1683 if (chan2)
1684 ast_channel_unlock(chan2);
1685 return 0;
1688 static int check_blacklist(const char *cmd)
1690 char *cmd_copy, *cur_cmd;
1691 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
1692 int i;
1694 cmd_copy = ast_strdupa(cmd);
1695 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
1696 cur_cmd = ast_strip(cur_cmd);
1697 if (ast_strlen_zero(cur_cmd)) {
1698 i--;
1699 continue;
1702 cmd_words[i] = cur_cmd;
1705 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
1706 int j, match = 1;
1708 for (j = 0; command_blacklist[i].words[j]; j++) {
1709 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
1710 match = 0;
1711 break;
1715 if (match) {
1716 return 1;
1720 return 0;
1723 static char mandescr_command[] =
1724 "Description: Run a CLI command.\n"
1725 "Variables: (Names marked with * are required)\n"
1726 " *Command: Asterisk CLI command to run\n"
1727 " ActionID: Optional Action id for message matching.\n";
1729 /*! \brief action_command: Manager command "command" - execute CLI command */
1730 static int action_command(struct mansession *s, const struct message *m)
1732 const char *cmd = astman_get_header(m, "Command");
1733 const char *id = astman_get_header(m, "ActionID");
1734 char *buf, *final_buf;
1735 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
1736 int fd = mkstemp(template);
1737 off_t l;
1739 if (ast_strlen_zero(cmd)) {
1740 astman_send_error(s, m, "No command provided");
1741 return 0;
1744 if (check_blacklist(cmd)) {
1745 astman_send_error(s, m, "Command blacklisted");
1746 return 0;
1749 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1750 if (!ast_strlen_zero(id))
1751 astman_append(s, "ActionID: %s\r\n", id);
1752 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1753 ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
1754 l = lseek(fd, 0, SEEK_END); /* how many chars available */
1756 /* This has a potential to overflow the stack. Hence, use the heap. */
1757 buf = ast_calloc(1, l + 1);
1758 final_buf = ast_calloc(1, l + 1);
1759 if (buf) {
1760 lseek(fd, 0, SEEK_SET);
1761 read(fd, buf, l);
1762 buf[l] = '\0';
1763 if (final_buf) {
1764 term_strip(final_buf, buf, l);
1765 final_buf[l] = '\0';
1767 astman_append(s, "%s", S_OR(final_buf, buf));
1768 ast_free(buf);
1770 close(fd);
1771 unlink(template);
1772 astman_append(s, "--END COMMAND--\r\n\r\n");
1773 if (final_buf)
1774 ast_free(final_buf);
1775 return 0;
1778 static void *fast_originate(void *data)
1780 struct fast_originate_helper *in = data;
1781 int res;
1782 int reason = 0;
1783 struct ast_channel *chan = NULL;
1784 char requested_channel[AST_CHANNEL_NAME];
1786 if (!ast_strlen_zero(in->app)) {
1787 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1788 S_OR(in->cid_num, NULL),
1789 S_OR(in->cid_name, NULL),
1790 in->vars, in->account, &chan);
1791 } else {
1792 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1793 S_OR(in->cid_num, NULL),
1794 S_OR(in->cid_name, NULL),
1795 in->vars, in->account, &chan);
1798 if (!chan)
1799 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
1800 /* Tell the manager what happened with the channel */
1801 manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1802 "%s"
1803 "Response: %s\r\n"
1804 "Channel: %s\r\n"
1805 "Context: %s\r\n"
1806 "Exten: %s\r\n"
1807 "Reason: %d\r\n"
1808 "Uniqueid: %s\r\n"
1809 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1810 "CallerIDNum: %s\r\n"
1811 "CallerIDName: %s\r\n",
1812 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
1813 chan ? chan->uniqueid : "<null>",
1814 S_OR(in->cid_num, "<unknown>"),
1815 S_OR(in->cid_num, "<unknown>"),
1816 S_OR(in->cid_name, "<unknown>")
1819 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1820 if (chan)
1821 ast_channel_unlock(chan);
1822 free(in);
1823 return NULL;
1826 static char mandescr_originate[] =
1827 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1828 " Application/Data\n"
1829 "Variables: (Names marked with * are required)\n"
1830 " *Channel: Channel name to call\n"
1831 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1832 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1833 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1834 " Application: Application to use\n"
1835 " Data: Data to use (requires 'Application')\n"
1836 " Timeout: How long to wait for call to be answered (in ms)\n"
1837 " CallerID: Caller ID to be set on the outgoing channel\n"
1838 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1839 " Account: Account code\n"
1840 " Async: Set to 'true' for fast origination\n";
1842 static int action_originate(struct mansession *s, const struct message *m)
1844 const char *name = astman_get_header(m, "Channel");
1845 const char *exten = astman_get_header(m, "Exten");
1846 const char *context = astman_get_header(m, "Context");
1847 const char *priority = astman_get_header(m, "Priority");
1848 const char *timeout = astman_get_header(m, "Timeout");
1849 const char *callerid = astman_get_header(m, "CallerID");
1850 const char *account = astman_get_header(m, "Account");
1851 const char *app = astman_get_header(m, "Application");
1852 const char *appdata = astman_get_header(m, "Data");
1853 const char *async = astman_get_header(m, "Async");
1854 const char *id = astman_get_header(m, "ActionID");
1855 const char *codecs = astman_get_header(m, "Codecs");
1856 struct ast_variable *vars = astman_get_variables(m);
1857 char *tech, *data;
1858 char *l = NULL, *n = NULL;
1859 int pi = 0;
1860 int res;
1861 int to = 30000;
1862 int reason = 0;
1863 char tmp[256];
1864 char tmp2[256];
1865 int format = AST_FORMAT_SLINEAR;
1867 pthread_t th;
1868 pthread_attr_t attr;
1869 if (ast_strlen_zero(name)) {
1870 astman_send_error(s, m, "Channel not specified");
1871 return 0;
1873 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1874 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1875 astman_send_error(s, m, "Invalid priority");
1876 return 0;
1879 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1880 astman_send_error(s, m, "Invalid timeout");
1881 return 0;
1883 ast_copy_string(tmp, name, sizeof(tmp));
1884 tech = tmp;
1885 data = strchr(tmp, '/');
1886 if (!data) {
1887 astman_send_error(s, m, "Invalid channel");
1888 return 0;
1890 *data++ = '\0';
1891 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1892 ast_callerid_parse(tmp2, &n, &l);
1893 if (n) {
1894 if (ast_strlen_zero(n))
1895 n = NULL;
1897 if (l) {
1898 ast_shrink_phone_number(l);
1899 if (ast_strlen_zero(l))
1900 l = NULL;
1902 if (!ast_strlen_zero(codecs)) {
1903 format = 0;
1904 ast_parse_allow_disallow(NULL, &format, codecs, 1);
1906 if (ast_true(async)) {
1907 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1908 if (!fast) {
1909 res = -1;
1910 } else {
1911 if (!ast_strlen_zero(id))
1912 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1913 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1914 ast_copy_string(fast->data, data, sizeof(fast->data));
1915 ast_copy_string(fast->app, app, sizeof(fast->app));
1916 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1917 if (l)
1918 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1919 if (n)
1920 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1921 fast->vars = vars;
1922 ast_copy_string(fast->context, context, sizeof(fast->context));
1923 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1924 ast_copy_string(fast->account, account, sizeof(fast->account));
1925 fast->format = format;
1926 fast->timeout = to;
1927 fast->priority = pi;
1928 pthread_attr_init(&attr);
1929 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1930 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1931 ast_free(fast);
1932 res = -1;
1933 } else {
1934 res = 0;
1936 pthread_attr_destroy(&attr);
1938 } else if (!ast_strlen_zero(app)) {
1939 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1940 } else {
1941 if (exten && context && pi)
1942 res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1943 else {
1944 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1945 return 0;
1948 if (!res)
1949 astman_send_ack(s, m, "Originate successfully queued");
1950 else
1951 astman_send_error(s, m, "Originate failed");
1952 return 0;
1955 /*! \brief Help text for manager command mailboxstatus
1957 static char mandescr_mailboxstatus[] =
1958 "Description: Checks a voicemail account for status.\n"
1959 "Variables: (Names marked with * are required)\n"
1960 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1961 " ActionID: Optional ActionID for message matching.\n"
1962 "Returns number of messages.\n"
1963 " Message: Mailbox Status\n"
1964 " Mailbox: <mailboxid>\n"
1965 " Waiting: <count>\n"
1966 "\n";
1968 static int action_mailboxstatus(struct mansession *s, const struct message *m)
1970 const char *mailbox = astman_get_header(m, "Mailbox");
1971 const char *id = astman_get_header(m,"ActionID");
1972 char idText[256] = "";
1973 int ret;
1974 if (ast_strlen_zero(mailbox)) {
1975 astman_send_error(s, m, "Mailbox not specified");
1976 return 0;
1978 if (!ast_strlen_zero(id))
1979 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1980 ret = ast_app_has_voicemail(mailbox, NULL);
1981 astman_append(s, "Response: Success\r\n"
1982 "%s"
1983 "Message: Mailbox Status\r\n"
1984 "Mailbox: %s\r\n"
1985 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1986 return 0;
1989 static char mandescr_mailboxcount[] =
1990 "Description: Checks a voicemail account for new messages.\n"
1991 "Variables: (Names marked with * are required)\n"
1992 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1993 " ActionID: Optional ActionID for message matching.\n"
1994 "Returns number of new and old messages.\n"
1995 " Message: Mailbox Message Count\n"
1996 " Mailbox: <mailboxid>\n"
1997 " NewMessages: <count>\n"
1998 " OldMessages: <count>\n"
1999 "\n";
2000 static int action_mailboxcount(struct mansession *s, const struct message *m)
2002 const char *mailbox = astman_get_header(m, "Mailbox");
2003 const char *id = astman_get_header(m,"ActionID");
2004 char idText[256] = "";
2005 int newmsgs = 0, oldmsgs = 0;
2006 if (ast_strlen_zero(mailbox)) {
2007 astman_send_error(s, m, "Mailbox not specified");
2008 return 0;
2010 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2011 if (!ast_strlen_zero(id)) {
2012 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
2014 astman_append(s, "Response: Success\r\n"
2015 "%s"
2016 "Message: Mailbox Message Count\r\n"
2017 "Mailbox: %s\r\n"
2018 "NewMessages: %d\r\n"
2019 "OldMessages: %d\r\n"
2020 "\r\n",
2021 idText,mailbox, newmsgs, oldmsgs);
2022 return 0;
2025 static char mandescr_extensionstate[] =
2026 "Description: Report the extension state for given extension.\n"
2027 " If the extension has a hint, will use devicestate to check\n"
2028 " the status of the device connected to the extension.\n"
2029 "Variables: (Names marked with * are required)\n"
2030 " *Exten: Extension to check state on\n"
2031 " *Context: Context for extension\n"
2032 " ActionId: Optional ID for this transaction\n"
2033 "Will return an \"Extension Status\" message.\n"
2034 "The response will include the hint for the extension and the status.\n";
2036 static int action_extensionstate(struct mansession *s, const struct message *m)
2038 const char *exten = astman_get_header(m, "Exten");
2039 const char *context = astman_get_header(m, "Context");
2040 const char *id = astman_get_header(m,"ActionID");
2041 char idText[256] = "";
2042 char hint[256] = "";
2043 int status;
2044 if (ast_strlen_zero(exten)) {
2045 astman_send_error(s, m, "Extension not specified");
2046 return 0;
2048 if (ast_strlen_zero(context))
2049 context = "default";
2050 status = ast_extension_state(NULL, context, exten);
2051 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2052 if (!ast_strlen_zero(id)) {
2053 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2055 astman_append(s, "Response: Success\r\n"
2056 "%s"
2057 "Message: Extension Status\r\n"
2058 "Exten: %s\r\n"
2059 "Context: %s\r\n"
2060 "Hint: %s\r\n"
2061 "Status: %d\r\n\r\n",
2062 idText,exten, context, hint, status);
2063 return 0;
2066 static char mandescr_timeout[] =
2067 "Description: Hangup a channel after a certain time.\n"
2068 "Variables: (Names marked with * are required)\n"
2069 " *Channel: Channel name to hangup\n"
2070 " *Timeout: Maximum duration of the call (sec)\n"
2071 "Acknowledges set time with 'Timeout Set' message\n";
2073 static int action_timeout(struct mansession *s, const struct message *m)
2075 struct ast_channel *c = NULL;
2076 const char *name = astman_get_header(m, "Channel");
2077 int timeout = atoi(astman_get_header(m, "Timeout"));
2078 if (ast_strlen_zero(name)) {
2079 astman_send_error(s, m, "No channel specified");
2080 return 0;
2082 if (!timeout) {
2083 astman_send_error(s, m, "No timeout specified");
2084 return 0;
2086 c = ast_get_channel_by_name_locked(name);
2087 if (!c) {
2088 astman_send_error(s, m, "No such channel");
2089 return 0;
2091 ast_channel_setwhentohangup(c, timeout);
2092 ast_channel_unlock(c);
2093 astman_send_ack(s, m, "Timeout Set");
2094 return 0;
2097 static int process_events(struct mansession *s)
2099 struct eventqent *eqe;
2100 int ret = 0;
2101 ast_mutex_lock(&s->__lock);
2102 if (!s->eventq)
2103 s->eventq = master_eventq;
2104 while(s->eventq->next) {
2105 eqe = s->eventq->next;
2106 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
2107 ((s->send_events & eqe->category) == eqe->category)) {
2108 if (s->fd > -1) {
2109 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
2110 ret = -1;
2111 } else if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
2112 ret = -1;
2113 else
2114 ast_dynamic_str_append(&s->outputstr, 0, "%s", eqe->eventdata);
2116 unuse_eventqent(s->eventq);
2117 s->eventq = eqe;
2119 ast_mutex_unlock(&s->__lock);
2120 return ret;
2123 static char mandescr_userevent[] =
2124 "Description: Send an event to manager sessions.\n"
2125 "Variables: (Names marked with * are required)\n"
2126 " *UserEvent: EventStringToSend\n"
2127 " Header1: Content1\n"
2128 " HeaderN: ContentN\n";
2130 static int action_userevent(struct mansession *s, const struct message *m)
2132 const char *event = astman_get_header(m, "UserEvent");
2133 char body[2048] = "";
2134 int x, bodylen = 0;
2135 for (x = 0; x < m->hdrcount; x++) {
2136 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2137 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2138 bodylen += strlen(m->headers[x]);
2139 ast_copy_string(body + bodylen, "\r\n", 3);
2140 bodylen += 2;
2144 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2145 return 0;
2148 static int process_message(struct mansession *s, const struct message *m)
2150 char action[80] = "";
2151 struct manager_action *tmp;
2152 const char *id = astman_get_header(m,"ActionID");
2153 char idText[256] = "";
2154 int ret = 0;
2156 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2157 if (option_debug)
2158 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
2160 if (ast_strlen_zero(action)) {
2161 astman_send_error(s, m, "Missing action in request");
2162 return 0;
2164 if (!ast_strlen_zero(id)) {
2165 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2167 if (!s->authenticated) {
2168 if (!strcasecmp(action, "Challenge")) {
2169 const char *authtype = astman_get_header(m, "AuthType");
2171 if (!strcasecmp(authtype, "MD5")) {
2172 if (ast_strlen_zero(s->challenge))
2173 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
2174 astman_append(s, "Response: Success\r\n"
2175 "%s"
2176 "Challenge: %s\r\n\r\n",
2177 idText, s->challenge);
2178 return 0;
2179 } else {
2180 astman_send_error(s, m, "Must specify AuthType");
2181 return 0;
2183 } else if (!strcasecmp(action, "Login")) {
2184 if (authenticate(s, m)) {
2185 sleep(1);
2186 astman_send_error(s, m, "Authentication failed");
2187 return -1;
2188 } else {
2189 s->authenticated = 1;
2190 if (option_verbose > 1) {
2191 if (displayconnects) {
2192 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n",
2193 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2196 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n",
2197 (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
2198 astman_send_ack(s, m, "Authentication accepted");
2200 } else if (!strcasecmp(action, "Logoff")) {
2201 astman_send_ack(s, m, "See ya");
2202 return -1;
2203 } else
2204 astman_send_error(s, m, "Authentication Required");
2205 } else {
2206 if (!strcasecmp(action, "Login"))
2207 astman_send_ack(s, m, "Already logged in");
2208 else {
2209 ast_rwlock_rdlock(&actionlock);
2210 for (tmp = first_action; tmp; tmp = tmp->next) {
2211 if (strcasecmp(action, tmp->action))
2212 continue;
2213 if ((s->writeperm & tmp->authority) == tmp->authority) {
2214 if (tmp->func(s, m))
2215 ret = -1;
2216 } else
2217 astman_send_error(s, m, "Permission denied");
2218 break;
2220 ast_rwlock_unlock(&actionlock);
2221 if (!tmp)
2222 astman_send_error(s, m, "Invalid/unknown command");
2225 if (ret)
2226 return ret;
2227 return process_events(s);
2230 static int get_input(struct mansession *s, char *output)
2232 /* output must have at least sizeof(s->inbuf) space */
2233 int res;
2234 int x;
2235 struct pollfd fds[1];
2236 for (x = 1; x < s->inlen; x++) {
2237 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
2238 /* Copy output data up to and including \r\n */
2239 memcpy(output, s->inbuf, x + 1);
2240 /* Add trailing \0 */
2241 output[x+1] = '\0';
2242 /* Move remaining data back to the front */
2243 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2244 s->inlen -= (x + 1);
2245 return 1;
2248 if (s->inlen >= sizeof(s->inbuf) - 1) {
2249 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2250 s->inlen = 0;
2252 fds[0].fd = s->fd;
2253 fds[0].events = POLLIN;
2254 do {
2255 ast_mutex_lock(&s->__lock);
2256 if (s->pending_event) {
2257 s->pending_event = 0;
2258 ast_mutex_unlock(&s->__lock);
2259 return 0;
2261 s->waiting_thread = pthread_self();
2262 ast_mutex_unlock(&s->__lock);
2264 res = poll(fds, 1, -1);
2266 ast_mutex_lock(&s->__lock);
2267 s->waiting_thread = AST_PTHREADT_NULL;
2268 ast_mutex_unlock(&s->__lock);
2269 if (res < 0) {
2270 if (errno == EINTR || errno == EAGAIN) {
2271 return 0;
2273 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2274 return -1;
2275 } else if (res > 0) {
2276 ast_mutex_lock(&s->__lock);
2277 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2278 ast_mutex_unlock(&s->__lock);
2279 if (res < 1)
2280 return -1;
2281 break;
2283 } while(1);
2284 s->inlen += res;
2285 s->inbuf[s->inlen] = '\0';
2286 return 0;
2289 static int do_message(struct mansession *s)
2291 struct message m = { 0 };
2292 char header_buf[sizeof(s->inbuf)] = { '\0' };
2293 int res;
2295 for (;;) {
2296 /* Check if any events are pending and do them if needed */
2297 if (s->eventq->next) {
2298 if (process_events(s))
2299 return -1;
2301 res = get_input(s, header_buf);
2302 if (res == 0) {
2303 continue;
2304 } else if (res > 0) {
2305 /* Strip trailing \r\n */
2306 if (strlen(header_buf) < 2)
2307 continue;
2308 header_buf[strlen(header_buf) - 2] = '\0';
2309 if (ast_strlen_zero(header_buf))
2310 return process_message(s, &m) ? -1 : 0;
2311 else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2312 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2313 } else {
2314 return res;
2319 static void *session_do(void *data)
2321 struct mansession *s = data;
2322 int res;
2324 astman_append(s, "Asterisk Call Manager/1.0\r\n");
2325 for (;;) {
2326 if ((res = do_message(s)) < 0)
2327 break;
2329 if (s->authenticated) {
2330 if (option_verbose > 1) {
2331 if (displayconnects)
2332 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2334 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2335 } else {
2336 if (option_verbose > 1) {
2337 if (displayconnects)
2338 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2340 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2343 /* It is possible under certain circumstances for this session thread
2344 to complete its work and exit *before* the thread that created it
2345 has finished executing the ast_pthread_create_background() function.
2346 If this occurs, some versions of glibc appear to act in a buggy
2347 fashion and attempt to write data into memory that it thinks belongs
2348 to the thread but is in fact not owned by the thread (or may have
2349 been freed completely).
2351 Causing this thread to yield to other threads at least one time
2352 appears to work around this bug.
2354 usleep(1);
2356 destroy_session(s);
2357 return NULL;
2360 static void *accept_thread(void *ignore)
2362 int as;
2363 struct sockaddr_in sin;
2364 socklen_t sinlen;
2365 struct eventqent *eqe;
2366 struct mansession *s;
2367 struct protoent *p;
2368 int arg = 1;
2369 int flags;
2370 pthread_attr_t attr;
2371 time_t now;
2372 struct pollfd pfds[1];
2374 pthread_attr_init(&attr);
2375 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2377 for (;;) {
2378 time(&now);
2379 AST_LIST_LOCK(&sessions);
2380 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2381 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2382 AST_LIST_REMOVE_CURRENT(&sessions, list);
2383 num_sessions--;
2384 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2385 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2386 s->username, ast_inet_ntoa(s->sin.sin_addr));
2388 free_session(s);
2389 break;
2392 AST_LIST_TRAVERSE_SAFE_END
2393 /* Purge master event queue of old, unused events, but make sure we
2394 always keep at least one in the queue */
2395 eqe = master_eventq;
2396 while (master_eventq->next && !master_eventq->usecount) {
2397 eqe = master_eventq;
2398 master_eventq = master_eventq->next;
2399 free(eqe);
2401 AST_LIST_UNLOCK(&sessions);
2403 sinlen = sizeof(sin);
2404 pfds[0].fd = asock;
2405 pfds[0].events = POLLIN;
2406 /* Wait for something to happen, but timeout every few seconds so
2407 we can ditch any old manager sessions */
2408 if (poll(pfds, 1, 5000) < 1)
2409 continue;
2410 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2411 if (as < 0) {
2412 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2413 continue;
2415 p = getprotobyname("tcp");
2416 if (p) {
2417 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2418 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2421 if (!(s = ast_calloc(1, sizeof(*s))))
2422 continue;
2424 memcpy(&s->sin, &sin, sizeof(sin));
2425 s->writetimeout = 100;
2426 s->waiting_thread = AST_PTHREADT_NULL;
2428 if (!block_sockets) {
2429 /* For safety, make sure socket is non-blocking */
2430 flags = fcntl(as, F_GETFL);
2431 fcntl(as, F_SETFL, flags | O_NONBLOCK);
2432 } else {
2433 flags = fcntl(as, F_GETFL);
2434 fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2436 ast_mutex_init(&s->__lock);
2437 s->fd = as;
2438 s->send_events = -1;
2439 AST_LIST_LOCK(&sessions);
2440 AST_LIST_INSERT_HEAD(&sessions, s, list);
2441 num_sessions++;
2442 /* Find the last place in the master event queue and hook ourselves
2443 in there */
2444 s->eventq = master_eventq;
2445 while(s->eventq->next)
2446 s->eventq = s->eventq->next;
2447 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2448 AST_LIST_UNLOCK(&sessions);
2449 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2450 destroy_session(s);
2452 pthread_attr_destroy(&attr);
2453 return NULL;
2456 static int append_event(const char *str, int category)
2458 struct eventqent *tmp, *prev = NULL;
2459 tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2461 if (!tmp)
2462 return -1;
2464 tmp->next = NULL;
2465 tmp->category = category;
2466 strcpy(tmp->eventdata, str);
2468 if (master_eventq) {
2469 prev = master_eventq;
2470 while (prev->next)
2471 prev = prev->next;
2472 prev->next = tmp;
2473 } else {
2474 master_eventq = tmp;
2477 tmp->usecount = num_sessions;
2479 return 0;
2482 /*! \brief manager_event: Send AMI event to client */
2483 int manager_event(int category, const char *event, const char *fmt, ...)
2485 struct mansession *s;
2486 char auth[80];
2487 va_list ap;
2488 struct timeval now;
2489 struct ast_dynamic_str *buf;
2491 /* Abort if there aren't any manager sessions */
2492 if (!num_sessions)
2493 return 0;
2495 if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2496 return -1;
2498 ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2499 "Event: %s\r\nPrivilege: %s\r\n",
2500 event, authority_to_str(category, auth, sizeof(auth)));
2502 if (timestampevents) {
2503 now = ast_tvnow();
2504 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2505 "Timestamp: %ld.%06lu\r\n",
2506 now.tv_sec, (unsigned long) now.tv_usec);
2509 va_start(ap, fmt);
2510 ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2511 va_end(ap);
2513 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2515 /* Append event to master list and wake up any sleeping sessions */
2516 AST_LIST_LOCK(&sessions);
2517 append_event(buf->str, category);
2518 AST_LIST_TRAVERSE(&sessions, s, list) {
2519 ast_mutex_lock(&s->__lock);
2520 if (s->waiting_thread != AST_PTHREADT_NULL)
2521 pthread_kill(s->waiting_thread, SIGURG);
2522 else
2523 /* We have an event to process, but the mansession is
2524 * not waiting for it. We still need to indicate that there
2525 * is an event waiting so that get_input processes the pending
2526 * event instead of polling.
2528 s->pending_event = 1;
2529 ast_mutex_unlock(&s->__lock);
2531 AST_LIST_UNLOCK(&sessions);
2533 return 0;
2536 int ast_manager_unregister(char *action)
2538 struct manager_action *cur, *prev;
2540 ast_rwlock_wrlock(&actionlock);
2541 cur = prev = first_action;
2542 while (cur) {
2543 if (!strcasecmp(action, cur->action)) {
2544 prev->next = cur->next;
2545 free(cur);
2546 if (option_verbose > 1)
2547 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2548 ast_rwlock_unlock(&actionlock);
2549 return 0;
2551 prev = cur;
2552 cur = cur->next;
2554 ast_rwlock_unlock(&actionlock);
2555 return 0;
2558 static int manager_state_cb(char *context, char *exten, int state, void *data)
2560 /* Notify managers of change */
2561 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2562 return 0;
2565 static int ast_manager_register_struct(struct manager_action *act)
2567 struct manager_action *cur, *prev = NULL;
2568 int ret;
2570 ast_rwlock_wrlock(&actionlock);
2571 cur = first_action;
2572 while (cur) { /* Walk the list of actions */
2573 ret = strcasecmp(cur->action, act->action);
2574 if (ret == 0) {
2575 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2576 ast_rwlock_unlock(&actionlock);
2577 return -1;
2578 } else if (ret > 0) {
2579 /* Insert these alphabetically */
2580 if (prev) {
2581 act->next = prev->next;
2582 prev->next = act;
2583 } else {
2584 act->next = first_action;
2585 first_action = act;
2587 break;
2589 prev = cur;
2590 cur = cur->next;
2593 if (!cur) {
2594 if (prev)
2595 prev->next = act;
2596 else
2597 first_action = act;
2598 act->next = NULL;
2601 if (option_verbose > 1)
2602 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2603 ast_rwlock_unlock(&actionlock);
2604 return 0;
2607 /*! \brief register a new command with manager, including online help. This is
2608 the preferred way to register a manager command */
2609 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2611 struct manager_action *cur;
2613 cur = ast_malloc(sizeof(*cur));
2614 if (!cur)
2615 return -1;
2617 cur->action = action;
2618 cur->authority = auth;
2619 cur->func = func;
2620 cur->synopsis = synopsis;
2621 cur->description = description;
2622 cur->next = NULL;
2624 ast_manager_register_struct(cur);
2626 return 0;
2628 /*! @}
2629 END Doxygen group */
2631 static struct mansession *find_session(uint32_t ident)
2633 struct mansession *s;
2635 AST_LIST_LOCK(&sessions);
2636 AST_LIST_TRAVERSE(&sessions, s, list) {
2637 ast_mutex_lock(&s->__lock);
2638 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2639 s->inuse++;
2640 break;
2642 ast_mutex_unlock(&s->__lock);
2644 AST_LIST_UNLOCK(&sessions);
2646 return s;
2649 int astman_verify_session_readpermissions(uint32_t ident, int perm)
2651 int result = 0;
2652 struct mansession *s;
2654 AST_LIST_LOCK(&sessions);
2655 AST_LIST_TRAVERSE(&sessions, s, list) {
2656 ast_mutex_lock(&s->__lock);
2657 if ((s->managerid == ident) && (s->readperm & perm)) {
2658 result = 1;
2659 ast_mutex_unlock(&s->__lock);
2660 break;
2662 ast_mutex_unlock(&s->__lock);
2664 AST_LIST_UNLOCK(&sessions);
2665 return result;
2668 int astman_verify_session_writepermissions(uint32_t ident, int perm)
2670 int result = 0;
2671 struct mansession *s;
2673 AST_LIST_LOCK(&sessions);
2674 AST_LIST_TRAVERSE(&sessions, s, list) {
2675 ast_mutex_lock(&s->__lock);
2676 if ((s->managerid == ident) && (s->writeperm & perm)) {
2677 result = 1;
2678 ast_mutex_unlock(&s->__lock);
2679 break;
2681 ast_mutex_unlock(&s->__lock);
2683 AST_LIST_UNLOCK(&sessions);
2684 return result;
2687 enum {
2688 FORMAT_RAW,
2689 FORMAT_HTML,
2690 FORMAT_XML,
2692 static char *contenttype[] = { "plain", "html", "xml" };
2694 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2696 struct mansession *s = NULL;
2697 uint32_t ident = 0;
2698 char workspace[512];
2699 char cookie[128];
2700 size_t len = sizeof(workspace);
2701 int blastaway = 0;
2702 char *c = workspace;
2703 char *retval = NULL;
2704 struct ast_variable *v;
2706 for (v = params; v; v = v->next) {
2707 if (!strcasecmp(v->name, "mansession_id")) {
2708 sscanf(v->value, "%x", &ident);
2709 break;
2713 if (!(s = find_session(ident))) {
2714 /* Create new session */
2715 if (!(s = ast_calloc(1, sizeof(*s)))) {
2716 *status = 500;
2717 goto generic_callback_out;
2719 memcpy(&s->sin, requestor, sizeof(s->sin));
2720 s->fd = -1;
2721 s->waiting_thread = AST_PTHREADT_NULL;
2722 s->send_events = 0;
2723 ast_mutex_init(&s->__lock);
2724 ast_mutex_lock(&s->__lock);
2725 s->inuse = 1;
2726 /*!\note There is approximately a 1 in 1.8E19 chance that the following
2727 * calculation will produce 0, which is an invalid ID, but due to the
2728 * properties of the rand() function (and the constantcy of s), that
2729 * won't happen twice in a row.
2731 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
2732 AST_LIST_LOCK(&sessions);
2733 AST_LIST_INSERT_HEAD(&sessions, s, list);
2734 /* Hook into the last spot in the event queue */
2735 s->eventq = master_eventq;
2736 while (s->eventq->next)
2737 s->eventq = s->eventq->next;
2738 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2739 ast_atomic_fetchadd_int(&num_sessions, 1);
2740 AST_LIST_UNLOCK(&sessions);
2743 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2744 time(&s->sessiontimeout);
2745 if (!s->authenticated && (httptimeout > 5))
2746 s->sessiontimeout += 5;
2747 else
2748 s->sessiontimeout += httptimeout;
2749 ast_mutex_unlock(&s->__lock);
2751 if (s) {
2752 struct message m = { 0 };
2753 char tmp[80];
2754 unsigned int x;
2755 size_t hdrlen;
2757 for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
2758 hdrlen = strlen(v->name) + strlen(v->value) + 3;
2759 m.headers[m.hdrcount] = alloca(hdrlen);
2760 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
2761 m.hdrcount = x + 1;
2764 if (process_message(s, &m)) {
2765 if (s->authenticated) {
2766 if (option_verbose > 1) {
2767 if (displayconnects)
2768 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2770 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2771 } else {
2772 if (option_verbose > 1) {
2773 if (displayconnects)
2774 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2776 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2778 s->needdestroy = 1;
2780 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2781 sprintf(tmp, "%08x", s->managerid);
2782 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2783 if (format == FORMAT_HTML)
2784 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Interface</title>");
2785 if (format == FORMAT_XML) {
2786 ast_build_string(&c, &len, "<ajax-response>\n");
2787 } else if (format == FORMAT_HTML) {
2788 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2789 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2791 ast_mutex_lock(&s->__lock);
2792 if (s->outputstr) {
2793 char *tmp;
2794 if (format == FORMAT_XML)
2795 tmp = xml_translate(s->outputstr->str, params);
2796 else if (format == FORMAT_HTML)
2797 tmp = html_translate(s->outputstr->str);
2798 else
2799 tmp = s->outputstr->str;
2800 if (tmp) {
2801 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2802 if (retval) {
2803 strcpy(retval, workspace);
2804 strcpy(retval + strlen(retval), tmp);
2805 c = retval + strlen(retval);
2806 len = 120;
2809 if (tmp != s->outputstr->str)
2810 free(tmp);
2811 free(s->outputstr);
2812 s->outputstr = NULL;
2814 ast_mutex_unlock(&s->__lock);
2815 /* Still okay because c would safely be pointing to workspace even
2816 if retval failed to allocate above */
2817 if (format == FORMAT_XML) {
2818 ast_build_string(&c, &len, "</ajax-response>\n");
2819 } else if (format == FORMAT_HTML)
2820 ast_build_string(&c, &len, "</table></body>\r\n");
2821 } else {
2822 *status = 500;
2823 *title = strdup("Server Error");
2825 ast_mutex_lock(&s->__lock);
2826 if (s->needdestroy) {
2827 if (s->inuse == 1) {
2828 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2829 blastaway = 1;
2830 } else {
2831 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2832 if (s->waiting_thread != AST_PTHREADT_NULL)
2833 pthread_kill(s->waiting_thread, SIGURG);
2834 s->inuse--;
2836 } else
2837 s->inuse--;
2838 ast_mutex_unlock(&s->__lock);
2840 if (blastaway)
2841 destroy_session(s);
2842 generic_callback_out:
2843 if (*status != 200)
2844 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2845 return retval;
2848 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2850 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2853 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2855 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2858 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2860 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2863 struct ast_http_uri rawmanuri = {
2864 .description = "Raw HTTP Manager Event Interface",
2865 .uri = "rawman",
2866 .has_subtree = 0,
2867 .callback = rawman_http_callback,
2870 struct ast_http_uri manageruri = {
2871 .description = "HTML Manager Event Interface",
2872 .uri = "manager",
2873 .has_subtree = 0,
2874 .callback = manager_http_callback,
2877 struct ast_http_uri managerxmluri = {
2878 .description = "XML Manager Event Interface",
2879 .uri = "mxml",
2880 .has_subtree = 0,
2881 .callback = mxml_http_callback,
2884 static int registered = 0;
2885 static int webregged = 0;
2887 int init_manager(void)
2889 struct ast_config *cfg = NULL, *ucfg = NULL;
2890 const char *val;
2891 char *cat = NULL;
2892 int oldportno = portno;
2893 static struct sockaddr_in ba;
2894 int x = 1;
2895 int flags;
2896 int webenabled = 0;
2897 int newhttptimeout = 60;
2898 struct ast_manager_user *user = NULL;
2900 if (!registered) {
2901 /* Register default actions */
2902 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2903 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2904 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2905 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2906 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2907 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2908 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2909 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2910 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2911 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2912 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2913 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2914 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2915 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2916 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2917 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2918 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2919 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2920 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2922 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2923 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2924 registered = 1;
2925 /* Append placeholder event so master_eventq never runs dry */
2926 append_event("Event: Placeholder\r\n\r\n", 0);
2928 portno = DEFAULT_MANAGER_PORT;
2929 displayconnects = 1;
2930 cfg = ast_config_load("manager.conf");
2931 if (!cfg) {
2932 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2933 return 0;
2935 val = ast_variable_retrieve(cfg, "general", "enabled");
2936 if (val)
2937 enabled = ast_true(val);
2939 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2940 if (val)
2941 block_sockets = ast_true(val);
2943 val = ast_variable_retrieve(cfg, "general", "webenabled");
2944 if (val)
2945 webenabled = ast_true(val);
2947 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2948 if (sscanf(val, "%d", &portno) != 1) {
2949 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2950 portno = DEFAULT_MANAGER_PORT;
2954 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2955 displayconnects = ast_true(val);
2957 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2958 timestampevents = ast_true(val);
2960 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2961 newhttptimeout = atoi(val);
2963 memset(&ba, 0, sizeof(ba));
2964 ba.sin_family = AF_INET;
2965 ba.sin_port = htons(portno);
2967 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2968 if (!inet_aton(val, &ba.sin_addr)) {
2969 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2970 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2975 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2976 #if 0
2977 /* Can't be done yet */
2978 close(asock);
2979 asock = -1;
2980 #else
2981 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2982 #endif
2985 AST_LIST_LOCK(&users);
2987 if ((ucfg = ast_config_load("users.conf"))) {
2988 while ((cat = ast_category_browse(ucfg, cat))) {
2989 int hasmanager = 0;
2990 struct ast_variable *var = NULL;
2992 if (!strcasecmp(cat, "general")) {
2993 continue;
2996 if (!(hasmanager = ast_true(ast_variable_retrieve(ucfg, cat, "hasmanager")))) {
2997 continue;
3000 /* Look for an existing entry, if none found - create one and add it to the list */
3001 if (!(user = ast_get_manager_by_name_locked(cat))) {
3002 if (!(user = ast_calloc(1, sizeof(*user)))) {
3003 break;
3005 /* Copy name over */
3006 ast_copy_string(user->username, cat, sizeof(user->username));
3007 /* Insert into list */
3008 AST_LIST_INSERT_TAIL(&users, user, list);
3011 /* Make sure we keep this user and don't destroy it during cleanup */
3012 user->keep = 1;
3014 for (var = ast_variable_browse(ucfg, cat); var; var = var->next) {
3015 if (!strcasecmp(var->name, "secret")) {
3016 if (user->secret) {
3017 free(user->secret);
3019 user->secret = ast_strdup(var->value);
3020 } else if (!strcasecmp(var->name, "deny") ) {
3021 if (user->deny) {
3022 free(user->deny);
3024 user->deny = ast_strdup(var->value);
3025 } else if (!strcasecmp(var->name, "permit") ) {
3026 if (user->permit) {
3027 free(user->permit);
3029 user->permit = ast_strdup(var->value);
3030 } else if (!strcasecmp(var->name, "read") ) {
3031 if (user->read) {
3032 free(user->read);
3034 user->read = ast_strdup(var->value);
3035 } else if (!strcasecmp(var->name, "write") ) {
3036 if (user->write) {
3037 free(user->write);
3039 user->write = ast_strdup(var->value);
3040 } else if (!strcasecmp(var->name, "displayconnects") ) {
3041 user->displayconnects = ast_true(var->value);
3042 } else if (!strcasecmp(var->name, "hasmanager")) {
3043 /* already handled */
3044 } else {
3045 ast_log(LOG_DEBUG, "%s is an unknown option (to the manager module).\n", var->name);
3049 ast_config_destroy(ucfg);
3052 while ((cat = ast_category_browse(cfg, cat))) {
3053 struct ast_variable *var = NULL;
3055 if (!strcasecmp(cat, "general"))
3056 continue;
3058 /* Look for an existing entry, if none found - create one and add it to the list */
3059 if (!(user = ast_get_manager_by_name_locked(cat))) {
3060 if (!(user = ast_calloc(1, sizeof(*user))))
3061 break;
3062 /* Copy name over */
3063 ast_copy_string(user->username, cat, sizeof(user->username));
3064 /* Insert into list */
3065 AST_LIST_INSERT_TAIL(&users, user, list);
3068 /* Make sure we keep this user and don't destroy it during cleanup */
3069 user->keep = 1;
3071 var = ast_variable_browse(cfg, cat);
3072 while (var) {
3073 if (!strcasecmp(var->name, "secret")) {
3074 if (user->secret)
3075 free(user->secret);
3076 user->secret = ast_strdup(var->value);
3077 } else if (!strcasecmp(var->name, "deny") ) {
3078 if (user->deny)
3079 free(user->deny);
3080 user->deny = ast_strdup(var->value);
3081 } else if (!strcasecmp(var->name, "permit") ) {
3082 if (user->permit)
3083 free(user->permit);
3084 user->permit = ast_strdup(var->value);
3085 } else if (!strcasecmp(var->name, "read") ) {
3086 if (user->read)
3087 free(user->read);
3088 user->read = ast_strdup(var->value);
3089 } else if (!strcasecmp(var->name, "write") ) {
3090 if (user->write)
3091 free(user->write);
3092 user->write = ast_strdup(var->value);
3093 } else if (!strcasecmp(var->name, "displayconnects") )
3094 user->displayconnects = ast_true(var->value);
3095 else
3096 ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
3097 var = var->next;
3101 /* Perform cleanup - essentially prune out old users that no longer exist */
3102 AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
3103 if (user->keep) {
3104 user->keep = 0;
3105 continue;
3107 /* We do not need to keep this user so take them out of the list */
3108 AST_LIST_REMOVE_CURRENT(&users, list);
3109 /* Free their memory now */
3110 if (user->secret)
3111 free(user->secret);
3112 if (user->deny)
3113 free(user->deny);
3114 if (user->permit)
3115 free(user->permit);
3116 if (user->read)
3117 free(user->read);
3118 if (user->write)
3119 free(user->write);
3120 free(user);
3122 AST_LIST_TRAVERSE_SAFE_END
3124 AST_LIST_UNLOCK(&users);
3126 ast_config_destroy(cfg);
3128 if (webenabled && enabled) {
3129 if (!webregged) {
3130 ast_http_uri_link(&rawmanuri);
3131 ast_http_uri_link(&manageruri);
3132 ast_http_uri_link(&managerxmluri);
3133 webregged = 1;
3135 } else {
3136 if (webregged) {
3137 ast_http_uri_unlink(&rawmanuri);
3138 ast_http_uri_unlink(&manageruri);
3139 ast_http_uri_unlink(&managerxmluri);
3140 webregged = 0;
3144 if (newhttptimeout > 0)
3145 httptimeout = newhttptimeout;
3147 /* If not enabled, do nothing */
3148 if (!enabled)
3149 return 0;
3151 if (asock < 0) {
3152 asock = socket(AF_INET, SOCK_STREAM, 0);
3153 if (asock < 0) {
3154 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
3155 return -1;
3157 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
3158 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
3159 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
3160 close(asock);
3161 asock = -1;
3162 return -1;
3164 if (listen(asock, 2)) {
3165 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
3166 close(asock);
3167 asock = -1;
3168 return -1;
3170 flags = fcntl(asock, F_GETFL);
3171 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
3172 if (option_verbose)
3173 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
3174 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
3176 return 0;
3179 int reload_manager(void)
3181 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3182 return init_manager();