Merged revisions 30874 via svnmerge from
[asterisk-bristuff.git] / manager.c
blob103231aff67befdec77c0bf95750ea555f1f7426
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 <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <netdb.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <arpa/inet.h>
46 #include <signal.h>
47 #include <errno.h>
48 #include <unistd.h>
50 #include "asterisk.h"
52 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
70 struct fast_originate_helper {
71 char tech[AST_MAX_MANHEADER_LEN];
72 char data[AST_MAX_MANHEADER_LEN];
73 int timeout;
74 char app[AST_MAX_APP];
75 char appdata[AST_MAX_MANHEADER_LEN];
76 char cid_name[AST_MAX_MANHEADER_LEN];
77 char cid_num[AST_MAX_MANHEADER_LEN];
78 char context[AST_MAX_CONTEXT];
79 char exten[AST_MAX_EXTENSION];
80 char idtext[AST_MAX_MANHEADER_LEN];
81 char account[AST_MAX_ACCOUNT_CODE];
82 int priority;
83 struct ast_variable *vars;
86 struct eventqent {
87 int usecount;
88 int category;
89 ast_mutex_t lock;
90 struct eventqent *next;
91 char eventdata[1];
94 static int enabled = 0;
95 static int portno = DEFAULT_MANAGER_PORT;
96 static int asock = -1;
97 static int displayconnects = 1;
98 static int timestampevents = 0;
99 static int httptimeout = 60;
101 static pthread_t t;
102 AST_MUTEX_DEFINE_STATIC(sessionlock);
103 static int block_sockets = 0;
104 static int num_sessions = 0;
105 struct eventqent *master_eventq = NULL;
107 static struct permalias {
108 int num;
109 char *label;
110 } perms[] = {
111 { EVENT_FLAG_SYSTEM, "system" },
112 { EVENT_FLAG_CALL, "call" },
113 { EVENT_FLAG_LOG, "log" },
114 { EVENT_FLAG_VERBOSE, "verbose" },
115 { EVENT_FLAG_COMMAND, "command" },
116 { EVENT_FLAG_AGENT, "agent" },
117 { EVENT_FLAG_USER, "user" },
118 { -1, "all" },
119 { 0, "none" },
122 static struct mansession {
123 /*! Execution thread */
124 pthread_t t;
125 /*! Thread lock -- don't use in action callbacks, it's already taken care of */
126 ast_mutex_t __lock;
127 /*! socket address */
128 struct sockaddr_in sin;
129 /*! TCP socket */
130 int fd;
131 /*! Whether or not we're busy doing an action */
132 int busy;
133 /*! Whether or not we're "dead" */
134 int dead;
135 /*! Whether an HTTP manager is in use */
136 int inuse;
137 /*! Whether an HTTP session should be destroyed */
138 int needdestroy;
139 /*! Whether an HTTP session has someone waiting on events */
140 pthread_t waiting_thread;
141 /*! Unique manager identifer */
142 unsigned long managerid;
143 /*! Session timeout if HTTP */
144 time_t sessiontimeout;
145 /*! Output from manager interface */
146 char *outputstr;
147 /*! Logged in username */
148 char username[80];
149 /*! Authentication challenge */
150 char challenge[10];
151 /*! Authentication status */
152 int authenticated;
153 /*! Authorization for reading */
154 int readperm;
155 /*! Authorization for writing */
156 int writeperm;
157 /*! Buffer */
158 char inbuf[AST_MAX_MANHEADER_LEN];
159 int inlen;
160 int send_events;
161 int displaysystemname; /*!< Add system name to manager responses and events */
162 /* Queued events that we've not had the ability to send yet */
163 struct eventqent *eventq;
164 /* Timeout for ast_carefulwrite() */
165 int writetimeout;
166 struct mansession *next;
167 } *sessions = NULL;
169 static struct manager_action *first_action = NULL;
170 AST_MUTEX_DEFINE_STATIC(actionlock);
172 /*! authority_to_str: Convert authority code to string with serveral options */
173 static char *authority_to_str(int authority, char *res, int reslen)
175 int running_total = 0, i;
176 memset(res, 0, reslen);
177 for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
178 if (authority & perms[i].num) {
179 if (*res) {
180 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
181 running_total++;
183 strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
184 running_total += strlen(perms[i].label);
187 if (ast_strlen_zero(res)) {
188 ast_copy_string(res, "<none>", reslen);
190 return res;
193 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
195 struct manager_action *cur;
196 int which = 0;
197 char *ret = NULL;
199 ast_mutex_lock(&actionlock);
200 for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
201 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
202 ret = ast_strdup(cur->action);
203 break; /* make sure we exit even if ast_strdup() returns NULL */
206 ast_mutex_unlock(&actionlock);
207 return ret;
210 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
212 while (*src && (*maxlen > 6)) {
213 switch (*src) {
214 case '<':
215 strcpy(*dst, "&lt;");
216 (*dst) += 4;
217 *maxlen -= 4;
218 break;
219 case '>':
220 strcpy(*dst, "&gt;");
221 (*dst) += 4;
222 *maxlen -= 4;
223 break;
224 case '\"':
225 strcpy(*dst, "&quot;");
226 (*dst) += 6;
227 *maxlen -= 6;
228 break;
229 case '\'':
230 strcpy(*dst, "&apos;");
231 (*dst) += 6;
232 *maxlen -= 6;
233 break;
234 case '&':
235 strcpy(*dst, "&amp;");
236 (*dst) += 4;
237 *maxlen -= 4;
238 break;
239 default:
240 *(*dst)++ = lower ? tolower(*src) : *src;
241 (*maxlen)--;
243 src++;
247 static char *xml_translate(char *in, struct ast_variable *vars)
249 struct ast_variable *v;
250 char *dest = NULL;
251 char *out, *tmp, *var, *val;
252 char *objtype = NULL;
253 int colons = 0;
254 int breaks = 0;
255 size_t len;
256 int count = 1;
257 int escaped = 0;
258 int inobj = 0;
259 int x;
260 v = vars;
262 while (v) {
263 if (!dest && !strcasecmp(v->name, "ajaxdest"))
264 dest = v->value;
265 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
266 objtype = v->value;
267 v = v->next;
269 if (!dest)
270 dest = "unknown";
271 if (!objtype)
272 objtype = "generic";
273 for (x=0; in[x]; x++) {
274 if (in[x] == ':')
275 colons++;
276 else if (in[x] == '\n')
277 breaks++;
278 else if (strchr("&\"<>", in[x]))
279 escaped++;
281 len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
282 out = malloc(len);
283 if (!out)
284 return 0;
285 tmp = out;
286 while (*in) {
287 var = in;
288 while (*in && (*in >= 32))
289 in++;
290 if (*in) {
291 if ((count > 3) && inobj) {
292 ast_build_string(&tmp, &len, " /></response>\n");
293 inobj = 0;
295 count = 0;
296 while (*in && (*in < 32)) {
297 *in = '\0';
298 in++;
299 count++;
301 val = strchr(var, ':');
302 if (val) {
303 *val = '\0';
304 val++;
305 if (*val == ' ')
306 val++;
307 if (!inobj) {
308 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
309 inobj = 1;
311 ast_build_string(&tmp, &len, " ");
312 xml_copy_escape(&tmp, &len, var, 1);
313 ast_build_string(&tmp, &len, "='");
314 xml_copy_escape(&tmp, &len, val, 0);
315 ast_build_string(&tmp, &len, "'");
319 if (inobj)
320 ast_build_string(&tmp, &len, " /></response>\n");
321 return out;
324 static char *html_translate(char *in)
326 int x;
327 int colons = 0;
328 int breaks = 0;
329 size_t len;
330 int count = 1;
331 char *tmp, *var, *val, *out;
333 for (x=0; in[x]; x++) {
334 if (in[x] == ':')
335 colons++;
336 if (in[x] == '\n')
337 breaks++;
339 len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
340 out = malloc(len);
341 if (!out)
342 return 0;
343 tmp = out;
344 while (*in) {
345 var = in;
346 while (*in && (*in >= 32))
347 in++;
348 if (*in) {
349 if ((count % 4) == 0){
350 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
352 count = 0;
353 while (*in && (*in < 32)) {
354 *in = '\0';
355 in++;
356 count++;
358 val = strchr(var, ':');
359 if (val) {
360 *val = '\0';
361 val++;
362 if (*val == ' ')
363 val++;
364 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
368 return out;
371 void astman_append(struct mansession *s, const char *fmt, ...)
373 char *stuff;
374 int res;
375 va_list ap;
376 char *tmp;
378 va_start(ap, fmt);
379 res = vasprintf(&stuff, fmt, ap);
380 va_end(ap);
381 if (res == -1) {
382 ast_log(LOG_ERROR, "Memory allocation failure\n");
383 return;
385 if (s->fd > -1)
386 ast_carefulwrite(s->fd, stuff, strlen(stuff), s->writetimeout);
387 else {
388 tmp = realloc(s->outputstr, (s->outputstr ? strlen(s->outputstr) : 0) + strlen(stuff) + 1);
389 if (tmp) {
390 if (!s->outputstr)
391 tmp[0] = '\0';
392 s->outputstr = tmp;
393 strcat(s->outputstr, stuff);
396 free(stuff);
399 static int handle_showmancmd(int fd, int argc, char *argv[])
401 struct manager_action *cur = first_action;
402 char authority[80];
403 int num;
405 if (argc != 4)
406 return RESULT_SHOWUSAGE;
407 ast_mutex_lock(&actionlock);
408 while (cur) { /* Walk the list of actions */
409 for (num = 3; num < argc; num++) {
410 if (!strcasecmp(cur->action, argv[num])) {
411 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 : "");
414 cur = cur->next;
417 ast_mutex_unlock(&actionlock);
418 return RESULT_SUCCESS;
421 /*! \brief handle_showmancmds: CLI command */
422 /* Should change to "manager show commands" */
423 static int handle_showmancmds(int fd, int argc, char *argv[])
425 struct manager_action *cur = first_action;
426 char authority[80];
427 char *format = " %-15.15s %-15.15s %-55.55s\n";
429 ast_mutex_lock(&actionlock);
430 ast_cli(fd, format, "Action", "Privilege", "Synopsis");
431 ast_cli(fd, format, "------", "---------", "--------");
432 while (cur) { /* Walk the list of actions */
433 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
434 cur = cur->next;
437 ast_mutex_unlock(&actionlock);
438 return RESULT_SUCCESS;
441 /*! \brief handle_showmanconn: CLI command show manager connected */
442 /* Should change to "manager show connected" */
443 static int handle_showmanconn(int fd, int argc, char *argv[])
445 struct mansession *s;
446 char iabuf[INET_ADDRSTRLEN];
447 char *format = " %-15.15s %-15.15s\n";
448 ast_mutex_lock(&sessionlock);
449 s = sessions;
450 ast_cli(fd, format, "Username", "IP Address");
451 while (s) {
452 ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
453 s = s->next;
456 ast_mutex_unlock(&sessionlock);
457 return RESULT_SUCCESS;
460 /*! \brief handle_showmanconn: CLI command show manager connected */
461 /* Should change to "manager show connected" */
462 static int handle_showmaneventq(int fd, int argc, char *argv[])
464 struct eventqent *s;
465 ast_mutex_lock(&sessionlock);
466 s = master_eventq;
467 while (s) {
468 ast_cli(fd, "Usecount: %d\n",s->usecount);
469 ast_cli(fd, "Category: %d\n", s->category);
470 ast_cli(fd, "Event:\n%s", s->eventdata);
471 s = s->next;
473 ast_mutex_unlock(&sessionlock);
474 return RESULT_SUCCESS;
477 static char showmancmd_help[] =
478 "Usage: show manager command <actionname>\n"
479 " Shows the detailed description for a specific Asterisk manager interface command.\n";
481 static char showmancmds_help[] =
482 "Usage: show manager commands\n"
483 " Prints a listing of all the available Asterisk manager interface commands.\n";
485 static char showmanconn_help[] =
486 "Usage: show manager connected\n"
487 " Prints a listing of the users that are currently connected to the\n"
488 "Asterisk manager interface.\n";
490 static char showmaneventq_help[] =
491 "Usage: show manager eventq\n"
492 " Prints a listing of all events pending in the Asterisk manger\n"
493 "event queue.\n";
495 static struct ast_cli_entry show_mancmd_cli =
496 { { "show", "manager", "command", NULL },
497 handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
499 static struct ast_cli_entry show_mancmds_cli =
500 { { "show", "manager", "commands", NULL },
501 handle_showmancmds, "List manager interface commands", showmancmds_help };
503 static struct ast_cli_entry show_manconn_cli =
504 { { "show", "manager", "connected", NULL },
505 handle_showmanconn, "Show connected manager interface users", showmanconn_help };
507 static struct ast_cli_entry show_maneventq_cli =
508 { { "show", "manager", "eventq", NULL },
509 handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
511 static void unuse_eventqent(struct eventqent *e)
513 /* XXX Need to atomically decrement the users. Change this to atomic_dec
514 one day when we have such a beast XXX */
515 int val;
516 ast_mutex_lock(&e->lock);
517 e->usecount--;
518 val = !e->usecount && e->next;
519 ast_mutex_unlock(&e->lock);
520 /* Wake up sleeping beauty */
521 if (val)
522 pthread_kill(t, SIGURG);
525 static void free_session(struct mansession *s)
527 struct eventqent *eqe;
528 if (s->fd > -1)
529 close(s->fd);
530 if (s->outputstr)
531 free(s->outputstr);
532 ast_mutex_destroy(&s->__lock);
533 while (s->eventq) {
534 eqe = s->eventq;
535 s->eventq = s->eventq->next;
536 unuse_eventqent(eqe);
538 free(s);
541 static void destroy_session(struct mansession *s)
543 struct mansession *cur, *prev = NULL;
544 ast_mutex_lock(&sessionlock);
545 cur = sessions;
546 while (cur) {
547 if (cur == s)
548 break;
549 prev = cur;
550 cur = cur->next;
552 if (cur) {
553 if (prev)
554 prev->next = cur->next;
555 else
556 sessions = cur->next;
557 free_session(s);
558 num_sessions--;
559 } else
560 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
561 ast_mutex_unlock(&sessionlock);
564 char *astman_get_header(struct message *m, char *var)
566 char cmp[80];
567 int x;
568 snprintf(cmp, sizeof(cmp), "%s: ", var);
569 for (x=0; x<m->hdrcount; x++)
570 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
571 return m->headers[x] + strlen(cmp);
572 return "";
575 struct ast_variable *astman_get_variables(struct message *m)
577 int varlen, x, y;
578 struct ast_variable *head = NULL, *cur;
579 char *var, *val;
581 char *parse;
582 AST_DECLARE_APP_ARGS(args,
583 AST_APP_ARG(vars)[32];
586 varlen = strlen("Variable: ");
588 for (x = 0; x < m->hdrcount; x++) {
589 if (strncasecmp("Variable: ", m->headers[x], varlen))
590 continue;
592 parse = ast_strdupa(m->headers[x] + varlen);
594 AST_STANDARD_APP_ARGS(args, parse);
595 if (args.argc) {
596 for (y = 0; y < args.argc; y++) {
597 if (!args.vars[y])
598 continue;
599 var = val = ast_strdupa(args.vars[y]);
600 strsep(&val, "=");
601 if (!val || ast_strlen_zero(var))
602 continue;
603 cur = ast_variable_new(var, val);
604 if (head) {
605 cur->next = head;
606 head = cur;
607 } else
608 head = cur;
613 return head;
616 /*! NOTE:
617 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
618 hold the session lock _or_ be running in an action callback (in which case s->busy will
619 be non-zero). In either of these cases, there is no need to lock-protect the session's
620 fd, since no other output will be sent (events will be queued), and no input will
621 be read until either the current action finishes or get_input() obtains the session
622 lock.
624 void astman_send_error(struct mansession *s, struct message *m, char *error)
626 char *id = astman_get_header(m,"ActionID");
628 astman_append(s, "Response: Error\r\n");
629 if (!ast_strlen_zero(id))
630 astman_append(s, "ActionID: %s\r\n", id);
631 astman_append(s, "Message: %s\r\n\r\n", error);
634 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
636 char *id = astman_get_header(m,"ActionID");
638 astman_append(s, "Response: %s\r\n", resp);
639 if (!ast_strlen_zero(id))
640 astman_append(s, "ActionID: %s\r\n", id);
641 if (msg)
642 astman_append(s, "Message: %s\r\n\r\n", msg);
643 else
644 astman_append(s, "\r\n");
647 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
649 astman_send_response(s, m, "Success", msg);
652 /*! Tells you if smallstr exists inside bigstr
653 which is delim by delim and uses no buf or stringsep
654 ast_instring("this|that|more","this",',') == 1;
656 feel free to move this to app.c -anthm */
657 static int ast_instring(char *bigstr, char *smallstr, char delim)
659 char *val = bigstr, *next;
661 do {
662 if ((next = strchr(val, delim))) {
663 if (!strncmp(val, smallstr, (next - val)))
664 return 1;
665 else
666 continue;
667 } else
668 return !strcmp(smallstr, val);
670 } while (*(val = (next + 1)));
672 return 0;
675 static int get_perm(char *instr)
677 int x = 0, ret = 0;
679 if (!instr)
680 return 0;
682 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
683 if (ast_instring(instr, perms[x].label, ','))
684 ret |= perms[x].num;
686 return ret;
689 static int ast_is_number(char *string)
691 int ret = 1, x = 0;
693 if (!string)
694 return 0;
696 for (x=0; x < strlen(string); x++) {
697 if (!(string[x] >= 48 && string[x] <= 57)) {
698 ret = 0;
699 break;
703 return ret ? atoi(string) : 0;
706 static int ast_strings_to_mask(char *string)
708 int x, ret = -1;
710 x = ast_is_number(string);
712 if (x) {
713 ret = x;
714 } else if (ast_strlen_zero(string)) {
715 ret = -1;
716 } else if (ast_false(string)) {
717 ret = 0;
718 } else if (ast_true(string)) {
719 ret = 0;
720 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
721 ret |= perms[x].num;
722 } else {
723 ret = 0;
724 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
725 if (ast_instring(string, perms[x].label, ','))
726 ret |= perms[x].num;
730 return ret;
733 /*!
734 Rather than braindead on,off this now can also accept a specific int mask value
735 or a ',' delim list of mask strings (the same as manager.conf) -anthm
738 static int set_eventmask(struct mansession *s, char *eventmask)
740 int maskint = ast_strings_to_mask(eventmask);
742 ast_mutex_lock(&s->__lock);
743 if (maskint >= 0)
744 s->send_events = maskint;
745 ast_mutex_unlock(&s->__lock);
747 return maskint;
750 static int authenticate(struct mansession *s, struct message *m)
752 struct ast_config *cfg;
753 char iabuf[INET_ADDRSTRLEN];
754 char *cat;
755 char *user = astman_get_header(m, "Username");
756 char *pass = astman_get_header(m, "Secret");
757 char *authtype = astman_get_header(m, "AuthType");
758 char *key = astman_get_header(m, "Key");
759 char *events = astman_get_header(m, "Events");
761 cfg = ast_config_load("manager.conf");
762 if (!cfg)
763 return -1;
764 cat = ast_category_browse(cfg, NULL);
765 while (cat) {
766 if (strcasecmp(cat, "general")) {
767 /* This is a user */
768 if (!strcasecmp(cat, user)) {
769 struct ast_variable *v;
770 struct ast_ha *ha = NULL;
771 char *password = NULL;
772 v = ast_variable_browse(cfg, cat);
773 while (v) {
774 if (!strcasecmp(v->name, "secret")) {
775 password = v->value;
776 } else if (!strcasecmp(v->name, "displaysystemname")) {
777 if (ast_true(v->value)) {
778 if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
779 s->displaysystemname = 1;
780 } else {
781 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
784 } else if (!strcasecmp(v->name, "permit") ||
785 !strcasecmp(v->name, "deny")) {
786 ha = ast_append_ha(v->name, v->value, ha);
787 } else if (!strcasecmp(v->name, "writetimeout")) {
788 int val = atoi(v->value);
790 if (val < 100)
791 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
792 else
793 s->writetimeout = val;
796 v = v->next;
798 if (ha && !ast_apply_ha(ha, &(s->sin))) {
799 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
800 ast_free_ha(ha);
801 ast_config_destroy(cfg);
802 return -1;
803 } else if (ha)
804 ast_free_ha(ha);
805 if (!strcasecmp(authtype, "MD5")) {
806 if (!ast_strlen_zero(key) && s->challenge) {
807 int x;
808 int len = 0;
809 char md5key[256] = "";
810 struct MD5Context md5;
811 unsigned char digest[16];
812 MD5Init(&md5);
813 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
814 MD5Update(&md5, (unsigned char *) password, strlen(password));
815 MD5Final(digest, &md5);
816 for (x=0; x<16; x++)
817 len += sprintf(md5key + len, "%2.2x", digest[x]);
818 if (!strcmp(md5key, key))
819 break;
820 else {
821 ast_config_destroy(cfg);
822 return -1;
825 } else if (password && !strcmp(password, pass)) {
826 break;
827 } else {
828 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
829 ast_config_destroy(cfg);
830 return -1;
834 cat = ast_category_browse(cfg, cat);
836 if (cat) {
837 ast_copy_string(s->username, cat, sizeof(s->username));
838 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
839 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
840 ast_config_destroy(cfg);
841 if (events)
842 set_eventmask(s, events);
843 return 0;
845 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
846 ast_config_destroy(cfg);
847 return -1;
850 /*! \brief PING: Manager PING */
851 static char mandescr_ping[] =
852 "Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
853 " manager connection open.\n"
854 "Variables: NONE\n";
856 static int action_ping(struct mansession *s, struct message *m)
858 astman_send_response(s, m, "Pong", NULL);
859 return 0;
862 /*! \brief WAITEVENT: Manager WAITEVENT */
863 static char mandescr_waitevent[] =
864 "Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
865 "a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
866 "session, events will be generated and queued.\n"
867 "Variables: \n"
868 " Timeout: Maximum time to wait for events\n";
870 static int action_waitevent(struct mansession *s, struct message *m)
872 char *timeouts = astman_get_header(m, "Timeout");
873 int timeout = -1, max;
874 int x;
875 int needexit = 0;
876 time_t now;
877 struct eventqent *eqe;
878 char *id = astman_get_header(m,"ActionID");
879 char idText[256] = "";
881 if (!ast_strlen_zero(id))
882 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
884 if (!ast_strlen_zero(timeouts)) {
885 sscanf(timeouts, "%i", &timeout);
888 ast_mutex_lock(&s->__lock);
889 if (s->waiting_thread != AST_PTHREADT_NULL) {
890 pthread_kill(s->waiting_thread, SIGURG);
892 if (s->sessiontimeout) {
893 time(&now);
894 max = s->sessiontimeout - now - 10;
895 if (max < 0)
896 max = 0;
897 if ((timeout < 0) || (timeout > max))
898 timeout = max;
899 if (!s->send_events)
900 s->send_events = -1;
901 /* Once waitevent is called, always queue events from now on */
902 if (s->busy == 1)
903 s->busy = 2;
905 ast_mutex_unlock(&s->__lock);
906 s->waiting_thread = pthread_self();
907 if (option_debug)
908 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
909 for (x=0; ((x < timeout) || (timeout < 0)); x++) {
910 ast_mutex_lock(&s->__lock);
911 if (s->eventq && s->eventq->next)
912 needexit = 1;
913 if (s->waiting_thread != pthread_self())
914 needexit = 1;
915 if (s->needdestroy)
916 needexit = 1;
917 ast_mutex_unlock(&s->__lock);
918 if (needexit)
919 break;
920 if (s->fd > 0) {
921 if (ast_wait_for_input(s->fd, 1000))
922 break;
923 } else {
924 sleep(1);
927 if (option_debug)
928 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
929 ast_mutex_lock(&s->__lock);
930 if (s->waiting_thread == pthread_self()) {
931 astman_send_response(s, m, "Success", "Waiting for Event...");
932 /* Only show events if we're the most recent waiter */
933 while(s->eventq->next) {
934 eqe = s->eventq->next;
935 if (((s->readperm & eqe->category) == eqe->category) &&
936 ((s->send_events & eqe->category) == eqe->category)) {
937 astman_append(s, "%s", eqe->eventdata);
939 unuse_eventqent(s->eventq);
940 s->eventq = eqe;
942 astman_append(s,
943 "Event: WaitEventComplete\r\n"
944 "%s"
945 "\r\n", idText);
946 s->waiting_thread = AST_PTHREADT_NULL;
947 } else {
948 ast_log(LOG_DEBUG, "Abandoning event request!\n");
950 ast_mutex_unlock(&s->__lock);
951 return 0;
954 static char mandescr_listcommands[] =
955 "Description: Returns the action name and synopsis for every\n"
956 " action that is available to the user\n"
957 "Variables: NONE\n";
959 static int action_listcommands(struct mansession *s, struct message *m)
961 struct manager_action *cur = first_action;
962 char idText[256] = "";
963 char temp[BUFSIZ];
964 char *id = astman_get_header(m,"ActionID");
966 if (!ast_strlen_zero(id))
967 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
968 astman_append(s, "Response: Success\r\n%s", idText);
969 ast_mutex_lock(&actionlock);
970 while (cur) { /* Walk the list of actions */
971 if ((s->writeperm & cur->authority) == cur->authority)
972 astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
973 cur = cur->next;
975 ast_mutex_unlock(&actionlock);
976 astman_append(s, "\r\n");
978 return 0;
981 static char mandescr_events[] =
982 "Description: Enable/Disable sending of events to this manager\n"
983 " client.\n"
984 "Variables:\n"
985 " EventMask: 'on' if all events should be sent,\n"
986 " 'off' if no events should be sent,\n"
987 " 'system,call,log' to select which flags events should have to be sent.\n";
989 static int action_events(struct mansession *s, struct message *m)
991 char *mask = astman_get_header(m, "EventMask");
992 int res;
994 res = set_eventmask(s, mask);
995 if (res > 0)
996 astman_send_response(s, m, "Events On", NULL);
997 else if (res == 0)
998 astman_send_response(s, m, "Events Off", NULL);
1000 return 0;
1003 static char mandescr_logoff[] =
1004 "Description: Logoff this manager session\n"
1005 "Variables: NONE\n";
1007 static int action_logoff(struct mansession *s, struct message *m)
1009 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1010 return -1;
1013 static char mandescr_hangup[] =
1014 "Description: Hangup a channel\n"
1015 "Variables: \n"
1016 " Channel: The channel name to be hungup\n";
1018 static int action_hangup(struct mansession *s, struct message *m)
1020 struct ast_channel *c = NULL;
1021 char *name = astman_get_header(m, "Channel");
1022 if (ast_strlen_zero(name)) {
1023 astman_send_error(s, m, "No channel specified");
1024 return 0;
1026 c = ast_get_channel_by_name_locked(name);
1027 if (!c) {
1028 astman_send_error(s, m, "No such channel");
1029 return 0;
1031 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1032 ast_channel_unlock(c);
1033 astman_send_ack(s, m, "Channel Hungup");
1034 return 0;
1037 static char mandescr_setvar[] =
1038 "Description: Set a global or local channel variable.\n"
1039 "Variables: (Names marked with * are required)\n"
1040 " Channel: Channel to set variable for\n"
1041 " *Variable: Variable name\n"
1042 " *Value: Value\n";
1044 static int action_setvar(struct mansession *s, struct message *m)
1046 struct ast_channel *c = NULL;
1047 char *name = astman_get_header(m, "Channel");
1048 char *varname = astman_get_header(m, "Variable");
1049 char *varval = astman_get_header(m, "Value");
1051 if (ast_strlen_zero(varname)) {
1052 astman_send_error(s, m, "No variable specified");
1053 return 0;
1056 if (ast_strlen_zero(varval)) {
1057 astman_send_error(s, m, "No value specified");
1058 return 0;
1061 if (!ast_strlen_zero(name)) {
1062 c = ast_get_channel_by_name_locked(name);
1063 if (!c) {
1064 astman_send_error(s, m, "No such channel");
1065 return 0;
1069 pbx_builtin_setvar_helper(c, varname, varval);
1071 if (c)
1072 ast_channel_unlock(c);
1074 astman_send_ack(s, m, "Variable Set");
1076 return 0;
1079 static char mandescr_getvar[] =
1080 "Description: Get the value of a global or local channel variable.\n"
1081 "Variables: (Names marked with * are required)\n"
1082 " Channel: Channel to read variable from\n"
1083 " *Variable: Variable name\n"
1084 " ActionID: Optional Action id for message matching.\n";
1086 static int action_getvar(struct mansession *s, struct message *m)
1088 struct ast_channel *c = NULL;
1089 char *name = astman_get_header(m, "Channel");
1090 char *varname = astman_get_header(m, "Variable");
1091 char *id = astman_get_header(m,"ActionID");
1092 char *varval;
1093 char workspace[1024];
1095 if (ast_strlen_zero(varname)) {
1096 astman_send_error(s, m, "No variable specified");
1097 return 0;
1100 if (!ast_strlen_zero(name)) {
1101 c = ast_get_channel_by_name_locked(name);
1102 if (!c) {
1103 astman_send_error(s, m, "No such channel");
1104 return 0;
1108 if (varname[strlen(varname) - 1] == ')') {
1109 ast_func_read(c, varname, workspace, sizeof(workspace));
1110 } else {
1111 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1114 if (c)
1115 ast_channel_unlock(c);
1116 astman_append(s, "Response: Success\r\n"
1117 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1118 if (!ast_strlen_zero(id))
1119 astman_append(s, "ActionID: %s\r\n",id);
1120 astman_append(s, "\r\n");
1122 return 0;
1126 /*! \brief action_status: Manager "status" command to show channels */
1127 /* Needs documentation... */
1128 static int action_status(struct mansession *s, struct message *m)
1130 char *id = astman_get_header(m,"ActionID");
1131 char *name = astman_get_header(m,"Channel");
1132 char idText[256] = "";
1133 struct ast_channel *c;
1134 char bridge[256];
1135 struct timeval now = ast_tvnow();
1136 long elapsed_seconds = 0;
1137 int all = ast_strlen_zero(name); /* set if we want all channels */
1139 astman_send_ack(s, m, "Channel status will follow");
1140 if (!ast_strlen_zero(id))
1141 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1142 if (all)
1143 c = ast_channel_walk_locked(NULL);
1144 else {
1145 c = ast_get_channel_by_name_locked(name);
1146 if (!c) {
1147 astman_send_error(s, m, "No such channel");
1148 return 0;
1151 /* if we look by name, we break after the first iteration */
1152 while (c) {
1153 if (c->_bridge)
1154 snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1155 else
1156 bridge[0] = '\0';
1157 if (c->pbx) {
1158 if (c->cdr) {
1159 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1161 astman_append(s,
1162 "Event: Status\r\n"
1163 "Privilege: Call\r\n"
1164 "Channel: %s\r\n"
1165 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1166 "CallerIDNum: %s\r\n"
1167 "CallerIDName: %s\r\n"
1168 "Account: %s\r\n"
1169 "State: %s\r\n"
1170 "Context: %s\r\n"
1171 "Extension: %s\r\n"
1172 "Priority: %d\r\n"
1173 "Seconds: %ld\r\n"
1174 "%s"
1175 "Uniqueid: %s\r\n"
1176 "%s"
1177 "\r\n",
1178 c->name,
1179 S_OR(c->cid.cid_num, "<unknown>"),
1180 S_OR(c->cid.cid_num, "<unknown>"),
1181 S_OR(c->cid.cid_name, "<unknown>"),
1182 c->accountcode,
1183 ast_state2str(c->_state), c->context,
1184 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1185 } else {
1186 astman_append(s,
1187 "Event: Status\r\n"
1188 "Privilege: Call\r\n"
1189 "Channel: %s\r\n"
1190 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1191 "CallerIDNum: %s\r\n"
1192 "CallerIDName: %s\r\n"
1193 "Account: %s\r\n"
1194 "State: %s\r\n"
1195 "%s"
1196 "Uniqueid: %s\r\n"
1197 "%s"
1198 "\r\n",
1199 c->name,
1200 S_OR(c->cid.cid_num, "<unknown>"),
1201 S_OR(c->cid.cid_num, "<unknown>"),
1202 S_OR(c->cid.cid_name, "<unknown>"),
1203 c->accountcode,
1204 ast_state2str(c->_state), bridge, c->uniqueid, idText);
1206 ast_channel_unlock(c);
1207 if (!all)
1208 break;
1209 c = ast_channel_walk_locked(c);
1211 astman_append(s,
1212 "Event: StatusComplete\r\n"
1213 "%s"
1214 "\r\n",idText);
1215 return 0;
1218 static char mandescr_redirect[] =
1219 "Description: Redirect (transfer) a call.\n"
1220 "Variables: (Names marked with * are required)\n"
1221 " *Channel: Channel to redirect\n"
1222 " ExtraChannel: Second call leg to transfer (optional)\n"
1223 " *Exten: Extension to transfer to\n"
1224 " *Context: Context to transfer to\n"
1225 " *Priority: Priority to transfer to\n"
1226 " ActionID: Optional Action id for message matching.\n";
1228 /*! \brief action_redirect: The redirect manager command */
1229 static int action_redirect(struct mansession *s, struct message *m)
1231 char *name = astman_get_header(m, "Channel");
1232 char *name2 = astman_get_header(m, "ExtraChannel");
1233 char *exten = astman_get_header(m, "Exten");
1234 char *context = astman_get_header(m, "Context");
1235 char *priority = astman_get_header(m, "Priority");
1236 struct ast_channel *chan, *chan2 = NULL;
1237 int pi = 0;
1238 int res;
1240 if (ast_strlen_zero(name)) {
1241 astman_send_error(s, m, "Channel not specified");
1242 return 0;
1244 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1245 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1246 astman_send_error(s, m, "Invalid priority\n");
1247 return 0;
1250 /* XXX watch out, possible deadlock!!! */
1251 chan = ast_get_channel_by_name_locked(name);
1252 if (!chan) {
1253 char buf[BUFSIZ];
1254 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1255 astman_send_error(s, m, buf);
1256 return 0;
1258 if (!ast_strlen_zero(name2))
1259 chan2 = ast_get_channel_by_name_locked(name2);
1260 res = ast_async_goto(chan, context, exten, pi);
1261 if (!res) {
1262 if (!ast_strlen_zero(name2)) {
1263 if (chan2)
1264 res = ast_async_goto(chan2, context, exten, pi);
1265 else
1266 res = -1;
1267 if (!res)
1268 astman_send_ack(s, m, "Dual Redirect successful");
1269 else
1270 astman_send_error(s, m, "Secondary redirect failed");
1271 } else
1272 astman_send_ack(s, m, "Redirect successful");
1273 } else
1274 astman_send_error(s, m, "Redirect failed");
1275 if (chan)
1276 ast_channel_unlock(chan);
1277 if (chan2)
1278 ast_channel_unlock(chan2);
1279 return 0;
1282 static char mandescr_command[] =
1283 "Description: Run a CLI command.\n"
1284 "Variables: (Names marked with * are required)\n"
1285 " *Command: Asterisk CLI command to run\n"
1286 " ActionID: Optional Action id for message matching.\n";
1288 /*! \brief action_command: Manager command "command" - execute CLI command */
1289 static int action_command(struct mansession *s, struct message *m)
1291 char *cmd = astman_get_header(m, "Command");
1292 char *id = astman_get_header(m, "ActionID");
1293 astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1294 if (!ast_strlen_zero(id))
1295 astman_append(s, "ActionID: %s\r\n", id);
1296 /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1297 ast_cli_command(s->fd, cmd);
1298 astman_append(s, "--END COMMAND--\r\n\r\n");
1299 return 0;
1302 static void *fast_originate(void *data)
1304 struct fast_originate_helper *in = data;
1305 int res;
1306 int reason = 0;
1307 struct ast_channel *chan = NULL;
1309 if (!ast_strlen_zero(in->app)) {
1310 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1311 S_OR(in->cid_num, NULL),
1312 S_OR(in->cid_name, NULL),
1313 in->vars, in->account, &chan);
1314 } else {
1315 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1316 S_OR(in->cid_num, NULL),
1317 S_OR(in->cid_name, NULL),
1318 in->vars, in->account, &chan);
1321 /* Tell the manager what happened with the channel */
1322 manager_event(EVENT_FLAG_CALL,
1323 res ? "OriginateFailure" : "OriginateSuccess",
1324 "%s"
1325 "Channel: %s/%s\r\n"
1326 "Context: %s\r\n"
1327 "Exten: %s\r\n"
1328 "Reason: %d\r\n"
1329 "Uniqueid: %s\r\n"
1330 "CallerID: %s\r\n" /* This parameter is deprecated and will be removed post-1.4 */
1331 "CallerIDNum: %s\r\n"
1332 "CallerIDName: %s\r\n",
1333 in->idtext, in->tech, in->data, in->context, in->exten, reason,
1334 chan ? chan->uniqueid : "<null>",
1335 S_OR(in->cid_num, "<unknown>"),
1336 S_OR(in->cid_num, "<unknown>"),
1337 S_OR(in->cid_name, "<unknown>")
1340 /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1341 if (chan)
1342 ast_channel_unlock(chan);
1343 free(in);
1344 return NULL;
1347 static char mandescr_originate[] =
1348 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1349 " Application/Data\n"
1350 "Variables: (Names marked with * are required)\n"
1351 " *Channel: Channel name to call\n"
1352 " Exten: Extension to use (requires 'Context' and 'Priority')\n"
1353 " Context: Context to use (requires 'Exten' and 'Priority')\n"
1354 " Priority: Priority to use (requires 'Exten' and 'Context')\n"
1355 " Application: Application to use\n"
1356 " Data: Data to use (requires 'Application')\n"
1357 " Timeout: How long to wait for call to be answered (in ms)\n"
1358 " CallerID: Caller ID to be set on the outgoing channel\n"
1359 " Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1360 " Account: Account code\n"
1361 " Async: Set to 'true' for fast origination\n";
1363 static int action_originate(struct mansession *s, struct message *m)
1365 char *name = astman_get_header(m, "Channel");
1366 char *exten = astman_get_header(m, "Exten");
1367 char *context = astman_get_header(m, "Context");
1368 char *priority = astman_get_header(m, "Priority");
1369 char *timeout = astman_get_header(m, "Timeout");
1370 char *callerid = astman_get_header(m, "CallerID");
1371 char *account = astman_get_header(m, "Account");
1372 char *app = astman_get_header(m, "Application");
1373 char *appdata = astman_get_header(m, "Data");
1374 char *async = astman_get_header(m, "Async");
1375 char *id = astman_get_header(m, "ActionID");
1376 struct ast_variable *vars = astman_get_variables(m);
1377 char *tech, *data;
1378 char *l = NULL, *n = NULL;
1379 int pi = 0;
1380 int res;
1381 int to = 30000;
1382 int reason = 0;
1383 char tmp[256];
1384 char tmp2[256];
1386 pthread_t th;
1387 pthread_attr_t attr;
1388 if (!name) {
1389 astman_send_error(s, m, "Channel not specified");
1390 return 0;
1392 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1393 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1394 astman_send_error(s, m, "Invalid priority\n");
1395 return 0;
1398 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1399 astman_send_error(s, m, "Invalid timeout\n");
1400 return 0;
1402 ast_copy_string(tmp, name, sizeof(tmp));
1403 tech = tmp;
1404 data = strchr(tmp, '/');
1405 if (!data) {
1406 astman_send_error(s, m, "Invalid channel\n");
1407 return 0;
1409 *data++ = '\0';
1410 ast_copy_string(tmp2, callerid, sizeof(tmp2));
1411 ast_callerid_parse(tmp2, &n, &l);
1412 if (n) {
1413 if (ast_strlen_zero(n))
1414 n = NULL;
1416 if (l) {
1417 ast_shrink_phone_number(l);
1418 if (ast_strlen_zero(l))
1419 l = NULL;
1421 if (ast_true(async)) {
1422 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1423 if (!fast) {
1424 res = -1;
1425 } else {
1426 memset(fast, 0, sizeof(struct fast_originate_helper));
1427 if (!ast_strlen_zero(id))
1428 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1429 ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1430 ast_copy_string(fast->data, data, sizeof(fast->data));
1431 ast_copy_string(fast->app, app, sizeof(fast->app));
1432 ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1433 if (l)
1434 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1435 if (n)
1436 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1437 fast->vars = vars;
1438 ast_copy_string(fast->context, context, sizeof(fast->context));
1439 ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1440 ast_copy_string(fast->account, account, sizeof(fast->account));
1441 fast->timeout = to;
1442 fast->priority = pi;
1443 pthread_attr_init(&attr);
1444 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1445 if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1446 res = -1;
1447 } else {
1448 res = 0;
1451 } else if (!ast_strlen_zero(app)) {
1452 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1453 } else {
1454 if (exten && context && pi)
1455 res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1456 else {
1457 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1458 return 0;
1461 if (!res)
1462 astman_send_ack(s, m, "Originate successfully queued");
1463 else
1464 astman_send_error(s, m, "Originate failed");
1465 return 0;
1468 /*! \brief Help text for manager command mailboxstatus
1470 static char mandescr_mailboxstatus[] =
1471 "Description: Checks a voicemail account for status.\n"
1472 "Variables: (Names marked with * are required)\n"
1473 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1474 " ActionID: Optional ActionID for message matching.\n"
1475 "Returns number of messages.\n"
1476 " Message: Mailbox Status\n"
1477 " Mailbox: <mailboxid>\n"
1478 " Waiting: <count>\n"
1479 "\n";
1481 static int action_mailboxstatus(struct mansession *s, struct message *m)
1483 char *mailbox = astman_get_header(m, "Mailbox");
1484 char *id = astman_get_header(m,"ActionID");
1485 char idText[256] = "";
1486 int ret;
1487 if (ast_strlen_zero(mailbox)) {
1488 astman_send_error(s, m, "Mailbox not specified");
1489 return 0;
1491 if (!ast_strlen_zero(id))
1492 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1493 ret = ast_app_has_voicemail(mailbox, NULL);
1494 astman_append(s, "Response: Success\r\n"
1495 "%s"
1496 "Message: Mailbox Status\r\n"
1497 "Mailbox: %s\r\n"
1498 "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1499 return 0;
1502 static char mandescr_mailboxcount[] =
1503 "Description: Checks a voicemail account for new messages.\n"
1504 "Variables: (Names marked with * are required)\n"
1505 " *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1506 " ActionID: Optional ActionID for message matching.\n"
1507 "Returns number of new and old messages.\n"
1508 " Message: Mailbox Message Count\n"
1509 " Mailbox: <mailboxid>\n"
1510 " NewMessages: <count>\n"
1511 " OldMessages: <count>\n"
1512 "\n";
1513 static int action_mailboxcount(struct mansession *s, struct message *m)
1515 char *mailbox = astman_get_header(m, "Mailbox");
1516 char *id = astman_get_header(m,"ActionID");
1517 char idText[256] = "";
1518 int newmsgs = 0, oldmsgs = 0;
1519 if (ast_strlen_zero(mailbox)) {
1520 astman_send_error(s, m, "Mailbox not specified");
1521 return 0;
1523 ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1524 if (!ast_strlen_zero(id)) {
1525 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1527 astman_append(s, "Response: Success\r\n"
1528 "%s"
1529 "Message: Mailbox Message Count\r\n"
1530 "Mailbox: %s\r\n"
1531 "NewMessages: %d\r\n"
1532 "OldMessages: %d\r\n"
1533 "\r\n",
1534 idText,mailbox, newmsgs, oldmsgs);
1535 return 0;
1538 static char mandescr_extensionstate[] =
1539 "Description: Report the extension state for given extension.\n"
1540 " If the extension has a hint, will use devicestate to check\n"
1541 " the status of the device connected to the extension.\n"
1542 "Variables: (Names marked with * are required)\n"
1543 " *Exten: Extension to check state on\n"
1544 " *Context: Context for extension\n"
1545 " ActionId: Optional ID for this transaction\n"
1546 "Will return an \"Extension Status\" message.\n"
1547 "The response will include the hint for the extension and the status.\n";
1549 static int action_extensionstate(struct mansession *s, struct message *m)
1551 char *exten = astman_get_header(m, "Exten");
1552 char *context = astman_get_header(m, "Context");
1553 char *id = astman_get_header(m,"ActionID");
1554 char idText[256] = "";
1555 char hint[256] = "";
1556 int status;
1557 if (ast_strlen_zero(exten)) {
1558 astman_send_error(s, m, "Extension not specified");
1559 return 0;
1561 if (ast_strlen_zero(context))
1562 context = "default";
1563 status = ast_extension_state(NULL, context, exten);
1564 ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1565 if (!ast_strlen_zero(id)) {
1566 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1568 astman_append(s, "Response: Success\r\n"
1569 "%s"
1570 "Message: Extension Status\r\n"
1571 "Exten: %s\r\n"
1572 "Context: %s\r\n"
1573 "Hint: %s\r\n"
1574 "Status: %d\r\n\r\n",
1575 idText,exten, context, hint, status);
1576 return 0;
1579 static char mandescr_timeout[] =
1580 "Description: Hangup a channel after a certain time.\n"
1581 "Variables: (Names marked with * are required)\n"
1582 " *Channel: Channel name to hangup\n"
1583 " *Timeout: Maximum duration of the call (sec)\n"
1584 "Acknowledges set time with 'Timeout Set' message\n";
1586 static int action_timeout(struct mansession *s, struct message *m)
1588 struct ast_channel *c = NULL;
1589 char *name = astman_get_header(m, "Channel");
1590 int timeout = atoi(astman_get_header(m, "Timeout"));
1591 if (ast_strlen_zero(name)) {
1592 astman_send_error(s, m, "No channel specified");
1593 return 0;
1595 if (!timeout) {
1596 astman_send_error(s, m, "No timeout specified");
1597 return 0;
1599 c = ast_get_channel_by_name_locked(name);
1600 if (!c) {
1601 astman_send_error(s, m, "No such channel");
1602 return 0;
1604 ast_channel_setwhentohangup(c, timeout);
1605 ast_channel_unlock(c);
1606 astman_send_ack(s, m, "Timeout Set");
1607 return 0;
1610 static int process_events(struct mansession *s)
1612 struct eventqent *eqe;
1613 int ret = 0;
1614 ast_mutex_lock(&s->__lock);
1615 if (s->fd > -1) {
1616 s->busy--;
1617 if (!s->eventq)
1618 s->eventq = master_eventq;
1619 while(s->eventq->next) {
1620 eqe = s->eventq->next;
1621 if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1622 ((s->send_events & eqe->category) == eqe->category)) {
1623 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1624 ret = -1;
1626 unuse_eventqent(s->eventq);
1627 s->eventq = eqe;
1630 ast_mutex_unlock(&s->__lock);
1631 return ret;
1634 static char mandescr_userevent[] =
1635 "Description: Send an event to manager sessions.\n"
1636 "Variables: (Names marked with * are required)\n"
1637 " *UserEvent: EventStringToSend\n"
1638 " Header1: Content1\n"
1639 " HeaderN: ContentN\n";
1641 static int action_userevent(struct mansession *s, struct message *m)
1643 char *event = astman_get_header(m, "UserEvent");
1644 char body[2048] = "";
1645 int x, bodylen = 0;
1646 for (x = 0; x < m->hdrcount; x++) {
1647 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1648 ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1649 bodylen += strlen(m->headers[x]);
1650 ast_copy_string(body + bodylen, "\r\n", 3);
1651 bodylen += 2;
1655 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1656 return 0;
1659 static int process_message(struct mansession *s, struct message *m)
1661 char action[80] = "";
1662 struct manager_action *tmp = first_action;
1663 char *id = astman_get_header(m,"ActionID");
1664 char idText[256] = "";
1665 char iabuf[INET_ADDRSTRLEN];
1666 int ret = 0;
1668 ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1669 ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1671 if (ast_strlen_zero(action)) {
1672 astman_send_error(s, m, "Missing action in request");
1673 return 0;
1675 if (!ast_strlen_zero(id)) {
1676 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1678 if (!s->authenticated) {
1679 if (!strcasecmp(action, "Challenge")) {
1680 char *authtype;
1681 authtype = astman_get_header(m, "AuthType");
1682 if (!strcasecmp(authtype, "MD5")) {
1683 if (ast_strlen_zero(s->challenge))
1684 snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1685 ast_mutex_lock(&s->__lock);
1686 astman_append(s, "Response: Success\r\n"
1687 "%s"
1688 "Challenge: %s\r\n\r\n",
1689 idText, s->challenge);
1690 ast_mutex_unlock(&s->__lock);
1691 return 0;
1692 } else {
1693 astman_send_error(s, m, "Must specify AuthType");
1694 return 0;
1696 } else if (!strcasecmp(action, "Login")) {
1697 if (authenticate(s, m)) {
1698 sleep(1);
1699 astman_send_error(s, m, "Authentication failed");
1700 return -1;
1701 } else {
1702 s->authenticated = 1;
1703 if (option_verbose > 1) {
1704 if (displayconnects) {
1705 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1708 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1709 astman_send_ack(s, m, "Authentication accepted");
1711 } else if (!strcasecmp(action, "Logoff")) {
1712 astman_send_ack(s, m, "See ya");
1713 return -1;
1714 } else
1715 astman_send_error(s, m, "Authentication Required");
1716 } else {
1717 ast_mutex_lock(&s->__lock);
1718 s->busy++;
1719 ast_mutex_unlock(&s->__lock);
1720 while (tmp) {
1721 if (!strcasecmp(action, tmp->action)) {
1722 if ((s->writeperm & tmp->authority) == tmp->authority) {
1723 if (tmp->func(s, m))
1724 ret = -1;
1725 } else {
1726 astman_send_error(s, m, "Permission denied");
1728 break;
1730 tmp = tmp->next;
1732 if (!tmp)
1733 astman_send_error(s, m, "Invalid/unknown command");
1735 if (ret)
1736 return ret;
1737 return process_events(s);
1740 static int get_input(struct mansession *s, char *output)
1742 /* output must have at least sizeof(s->inbuf) space */
1743 int res;
1744 int x;
1745 struct pollfd fds[1];
1746 char iabuf[INET_ADDRSTRLEN];
1747 for (x = 1; x < s->inlen; x++) {
1748 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1749 /* Copy output data up to and including \r\n */
1750 memcpy(output, s->inbuf, x + 1);
1751 /* Add trailing \0 */
1752 output[x+1] = '\0';
1753 /* Move remaining data back to the front */
1754 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1755 s->inlen -= (x + 1);
1756 return 1;
1759 if (s->inlen >= sizeof(s->inbuf) - 1) {
1760 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
1761 s->inlen = 0;
1763 fds[0].fd = s->fd;
1764 fds[0].events = POLLIN;
1765 do {
1766 ast_mutex_lock(&s->__lock);
1767 s->waiting_thread = pthread_self();
1768 ast_mutex_unlock(&s->__lock);
1770 res = poll(fds, 1, -1);
1772 ast_mutex_lock(&s->__lock);
1773 s->waiting_thread = AST_PTHREADT_NULL;
1774 ast_mutex_unlock(&s->__lock);
1775 if (res < 0) {
1776 if (errno == EINTR) {
1777 if (s->dead)
1778 return -1;
1779 return 0;
1781 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1782 return -1;
1783 } else if (res > 0) {
1784 ast_mutex_lock(&s->__lock);
1785 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1786 ast_mutex_unlock(&s->__lock);
1787 if (res < 1)
1788 return -1;
1789 break;
1791 } while(1);
1792 s->inlen += res;
1793 s->inbuf[s->inlen] = '\0';
1794 return 0;
1797 static void *session_do(void *data)
1799 struct mansession *s = data;
1800 struct message m;
1801 char iabuf[INET_ADDRSTRLEN];
1802 int res;
1804 ast_mutex_lock(&s->__lock);
1805 astman_append(s, "Asterisk Call Manager/1.0\r\n");
1806 ast_mutex_unlock(&s->__lock);
1807 memset(&m, 0, sizeof(m));
1808 for (;;) {
1809 res = get_input(s, m.headers[m.hdrcount]);
1810 if (res > 0) {
1811 /* Strip trailing \r\n */
1812 if (strlen(m.headers[m.hdrcount]) < 2)
1813 continue;
1814 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1815 if (ast_strlen_zero(m.headers[m.hdrcount])) {
1816 if (process_message(s, &m))
1817 break;
1818 memset(&m, 0, sizeof(m));
1819 } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
1820 m.hdrcount++;
1821 } else if (res < 0) {
1822 break;
1823 } else if (s->eventq->next) {
1824 if (process_events(s))
1825 break;
1828 if (s->authenticated) {
1829 if (option_verbose > 1) {
1830 if (displayconnects)
1831 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1833 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1834 } else {
1835 if (option_verbose > 1) {
1836 if (displayconnects)
1837 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1839 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1841 destroy_session(s);
1842 return NULL;
1845 static void *accept_thread(void *ignore)
1847 int as;
1848 struct sockaddr_in sin;
1849 socklen_t sinlen;
1850 struct eventqent *eqe;
1851 struct mansession *s, *prev = NULL, *next;
1852 struct protoent *p;
1853 int arg = 1;
1854 int flags;
1855 pthread_attr_t attr;
1856 time_t now;
1857 struct pollfd pfds[1];
1858 char iabuf[INET_ADDRSTRLEN];
1860 pthread_attr_init(&attr);
1861 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1863 for (;;) {
1864 time(&now);
1865 ast_mutex_lock(&sessionlock);
1866 prev = NULL;
1867 s = sessions;
1868 while (s) {
1869 next = s->next;
1870 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
1871 num_sessions--;
1872 if (prev)
1873 prev->next = next;
1874 else
1875 sessions = next;
1876 if (s->authenticated && (option_verbose > 1) && displayconnects) {
1877 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
1878 s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1880 free_session(s);
1881 } else
1882 prev = s;
1883 s = next;
1885 /* Purge master event queue of old, unused events, but make sure we
1886 always keep at least one in the queue */
1887 eqe = master_eventq;
1888 while (master_eventq->next && !master_eventq->usecount) {
1889 eqe = master_eventq;
1890 master_eventq = master_eventq->next;
1891 free(eqe);
1893 ast_mutex_unlock(&sessionlock);
1895 sinlen = sizeof(sin);
1896 pfds[0].fd = asock;
1897 pfds[0].events = POLLIN;
1898 /* Wait for something to happen, but timeout every few seconds so
1899 we can ditch any old manager sessions */
1900 if (poll(pfds, 1, 5000) < 1)
1901 continue;
1902 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1903 if (as < 0) {
1904 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1905 continue;
1907 p = getprotobyname("tcp");
1908 if (p) {
1909 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1910 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1913 s = malloc(sizeof(struct mansession));
1914 if (!s) {
1915 ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1916 continue;
1918 memset(s, 0, sizeof(struct mansession));
1919 memcpy(&s->sin, &sin, sizeof(sin));
1920 s->writetimeout = 100;
1921 s->waiting_thread = AST_PTHREADT_NULL;
1923 if (!block_sockets) {
1924 /* For safety, make sure socket is non-blocking */
1925 flags = fcntl(as, F_GETFL);
1926 fcntl(as, F_SETFL, flags | O_NONBLOCK);
1928 ast_mutex_init(&s->__lock);
1929 s->fd = as;
1930 s->send_events = -1;
1931 ast_mutex_lock(&sessionlock);
1932 num_sessions++;
1933 s->next = sessions;
1934 sessions = s;
1935 /* Find the last place in the master event queue and hook ourselves
1936 in there */
1937 s->eventq = master_eventq;
1938 while(s->eventq->next)
1939 s->eventq = s->eventq->next;
1940 ast_mutex_lock(&s->eventq->lock);
1941 s->eventq->usecount++;
1942 ast_mutex_unlock(&s->eventq->lock);
1943 ast_mutex_unlock(&sessionlock);
1944 if (ast_pthread_create(&s->t, &attr, session_do, s))
1945 destroy_session(s);
1947 pthread_attr_destroy(&attr);
1948 return NULL;
1951 static int append_event(const char *str, int category)
1953 struct eventqent *tmp, *prev = NULL;
1954 tmp = malloc(sizeof(struct eventqent) + strlen(str));
1955 if (tmp) {
1956 ast_mutex_init(&tmp->lock);
1957 tmp->next = NULL;
1958 tmp->category = category;
1959 strcpy(tmp->eventdata, str);
1960 if (master_eventq) {
1961 prev = master_eventq;
1962 while (prev->next)
1963 prev = prev->next;
1964 prev->next = tmp;
1965 } else {
1966 master_eventq = tmp;
1968 tmp->usecount = num_sessions;
1969 return 0;
1971 return -1;
1974 /*! \brief manager_event: Send AMI event to client */
1975 int manager_event(int category, const char *event, const char *fmt, ...)
1977 struct mansession *s;
1978 char auth[80];
1979 char tmp[4096] = "";
1980 char *tmp_next = tmp;
1981 size_t tmp_left = sizeof(tmp) - 2;
1982 va_list ap;
1983 struct timeval now;
1985 /* Abort if there aren't any manager sessions */
1986 if (!num_sessions)
1987 return 0;
1989 ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1990 event, authority_to_str(category, auth, sizeof(auth)));
1991 if (timestampevents) {
1992 now = ast_tvnow();
1993 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1994 now.tv_sec, (unsigned long) now.tv_usec);
1996 va_start(ap, fmt);
1997 ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1998 va_end(ap);
1999 *tmp_next++ = '\r';
2000 *tmp_next++ = '\n';
2001 *tmp_next = '\0';
2003 ast_mutex_lock(&sessionlock);
2004 /* Append even to master list and wake up any sleeping sessions */
2005 append_event(tmp, category);
2006 for (s = sessions; s; s = s->next) {
2007 ast_mutex_lock(&s->__lock);
2008 if (s->waiting_thread != AST_PTHREADT_NULL)
2009 pthread_kill(s->waiting_thread, SIGURG);
2010 ast_mutex_unlock(&s->__lock);
2012 ast_mutex_unlock(&sessionlock);
2014 return 0;
2017 int ast_manager_unregister( char *action )
2019 struct manager_action *cur = first_action, *prev = first_action;
2021 ast_mutex_lock(&actionlock);
2022 while (cur) {
2023 if (!strcasecmp(action, cur->action)) {
2024 prev->next = cur->next;
2025 free(cur);
2026 if (option_verbose > 1)
2027 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2028 ast_mutex_unlock(&actionlock);
2029 return 0;
2031 prev = cur;
2032 cur = cur->next;
2034 ast_mutex_unlock(&actionlock);
2035 return 0;
2038 static int manager_state_cb(char *context, char *exten, int state, void *data)
2040 /* Notify managers of change */
2041 manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2042 return 0;
2045 static int ast_manager_register_struct(struct manager_action *act)
2047 struct manager_action *cur = first_action, *prev = NULL;
2048 int ret;
2050 ast_mutex_lock(&actionlock);
2051 while (cur) { /* Walk the list of actions */
2052 ret = strcasecmp(cur->action, act->action);
2053 if (ret == 0) {
2054 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2055 ast_mutex_unlock(&actionlock);
2056 return -1;
2057 } else if (ret > 0) {
2058 /* Insert these alphabetically */
2059 if (prev) {
2060 act->next = prev->next;
2061 prev->next = act;
2062 } else {
2063 act->next = first_action;
2064 first_action = act;
2066 break;
2068 prev = cur;
2069 cur = cur->next;
2072 if (!cur) {
2073 if (prev)
2074 prev->next = act;
2075 else
2076 first_action = act;
2077 act->next = NULL;
2080 if (option_verbose > 1)
2081 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2082 ast_mutex_unlock(&actionlock);
2083 return 0;
2086 /*! \brief register a new command with manager, including online help. This is
2087 the preferred way to register a manager command */
2088 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2090 struct manager_action *cur;
2092 cur = malloc(sizeof(struct manager_action));
2093 if (!cur) {
2094 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
2095 return -1;
2097 cur->action = action;
2098 cur->authority = auth;
2099 cur->func = func;
2100 cur->synopsis = synopsis;
2101 cur->description = description;
2102 cur->next = NULL;
2104 ast_manager_register_struct(cur);
2106 return 0;
2108 /*! @}
2109 END Doxygen group */
2111 static struct mansession *find_session(unsigned long ident)
2113 struct mansession *s;
2114 ast_mutex_lock(&sessionlock);
2115 s = sessions;
2116 while (s) {
2117 ast_mutex_lock(&s->__lock);
2118 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2119 s->inuse++;
2120 break;
2122 ast_mutex_unlock(&s->__lock);
2123 s = s->next;
2125 ast_mutex_unlock(&sessionlock);
2126 return s;
2130 static void vars2msg(struct message *m, struct ast_variable *vars)
2132 int x;
2133 for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2134 if (!vars)
2135 break;
2136 m->hdrcount = x + 1;
2137 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2141 #define FORMAT_RAW 0
2142 #define FORMAT_HTML 1
2143 #define FORMAT_XML 2
2145 static char *contenttype[] = { "plain", "html", "xml" };
2147 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2149 struct mansession *s = NULL;
2150 unsigned long ident = 0;
2151 char workspace[256];
2152 char cookie[128];
2153 char iabuf[INET_ADDRSTRLEN];
2154 size_t len = sizeof(workspace);
2155 int blastaway = 0;
2156 char *c = workspace;
2157 char *retval = NULL;
2158 struct message m;
2159 struct ast_variable *v;
2161 v = params;
2162 while (v) {
2163 if (!strcasecmp(v->name, "mansession_id")) {
2164 sscanf(v->value, "%lx", &ident);
2165 break;
2167 v = v->next;
2169 s = find_session(ident);
2171 if (!s) {
2172 /* Create new session */
2173 s = calloc(1, sizeof(struct mansession));
2174 memcpy(&s->sin, requestor, sizeof(s->sin));
2175 s->fd = -1;
2176 s->waiting_thread = AST_PTHREADT_NULL;
2177 s->send_events = 0;
2178 ast_mutex_init(&s->__lock);
2179 ast_mutex_lock(&s->__lock);
2180 ast_mutex_lock(&sessionlock);
2181 s->inuse = 1;
2182 s->managerid = rand() | (unsigned long)s;
2183 s->next = sessions;
2184 sessions = s;
2185 num_sessions++;
2186 /* Hook into the last spot in the event queue */
2187 s->eventq = master_eventq;
2188 while (s->eventq->next)
2189 s->eventq = s->eventq->next;
2190 ast_mutex_lock(&s->eventq->lock);
2191 s->eventq->usecount++;
2192 ast_mutex_unlock(&s->eventq->lock);
2193 ast_mutex_unlock(&sessionlock);
2196 /* Reset HTTP timeout. If we're not yet authenticated, keep it extremely short */
2197 time(&s->sessiontimeout);
2198 if (!s->authenticated && (httptimeout > 5))
2199 s->sessiontimeout += 5;
2200 else
2201 s->sessiontimeout += httptimeout;
2202 ast_mutex_unlock(&s->__lock);
2204 memset(&m, 0, sizeof(m));
2205 if (s) {
2206 char tmp[80];
2207 ast_build_string(&c, &len, "Content-type: text/%s\n", contenttype[format]);
2208 sprintf(tmp, "%08lx", s->managerid);
2209 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2210 if (format == FORMAT_HTML)
2211 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2212 vars2msg(&m, params);
2213 if (format == FORMAT_XML) {
2214 ast_build_string(&c, &len, "<ajax-response>\n");
2215 } else if (format == FORMAT_HTML) {
2216 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2217 ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2219 if (process_message(s, &m)) {
2220 if (s->authenticated) {
2221 if (option_verbose > 1) {
2222 if (displayconnects)
2223 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2225 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2226 } else {
2227 if (option_verbose > 1) {
2228 if (displayconnects)
2229 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2231 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2233 s->needdestroy = 1;
2235 if (s->outputstr) {
2236 char *tmp;
2237 if (format == FORMAT_XML)
2238 tmp = xml_translate(s->outputstr, params);
2239 else if (format == FORMAT_HTML)
2240 tmp = html_translate(s->outputstr);
2241 else
2242 tmp = s->outputstr;
2243 if (tmp) {
2244 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2245 if (retval) {
2246 strcpy(retval, workspace);
2247 strcpy(retval + strlen(retval), tmp);
2248 c = retval + strlen(retval);
2249 len = 120;
2251 free(tmp);
2253 if (tmp != s->outputstr)
2254 free(s->outputstr);
2255 s->outputstr = NULL;
2257 /* Still okay because c would safely be pointing to workspace even
2258 if retval failed to allocate above */
2259 if (format == FORMAT_XML) {
2260 ast_build_string(&c, &len, "</ajax-response>\n");
2261 } else if (format == FORMAT_HTML)
2262 ast_build_string(&c, &len, "</table></body>\r\n");
2263 } else {
2264 *status = 500;
2265 *title = strdup("Server Error");
2267 ast_mutex_lock(&s->__lock);
2268 if (s->needdestroy) {
2269 if (s->inuse == 1) {
2270 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2271 blastaway = 1;
2272 } else {
2273 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2274 if (s->waiting_thread != AST_PTHREADT_NULL)
2275 pthread_kill(s->waiting_thread, SIGURG);
2276 s->inuse--;
2278 } else
2279 s->inuse--;
2280 ast_mutex_unlock(&s->__lock);
2282 if (blastaway)
2283 destroy_session(s);
2284 if (*status != 200)
2285 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2286 return retval;
2289 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2291 return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2294 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2296 return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2299 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2301 return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2304 struct ast_http_uri rawmanuri = {
2305 .description = "Raw HTTP Manager Event Interface",
2306 .uri = "rawman",
2307 .has_subtree = 0,
2308 .callback = rawman_http_callback,
2311 struct ast_http_uri manageruri = {
2312 .description = "HTML Manager Event Interface",
2313 .uri = "manager",
2314 .has_subtree = 0,
2315 .callback = manager_http_callback,
2318 struct ast_http_uri managerxmluri = {
2319 .description = "XML Manager Event Interface",
2320 .uri = "mxml",
2321 .has_subtree = 0,
2322 .callback = mxml_http_callback,
2325 static int registered = 0;
2326 static int webregged = 0;
2328 int init_manager(void)
2330 struct ast_config *cfg;
2331 char *val;
2332 int oldportno = portno;
2333 static struct sockaddr_in ba;
2334 int x = 1;
2335 int flags;
2336 int webenabled = 0;
2337 int newhttptimeout = 60;
2338 if (!registered) {
2339 /* Register default actions */
2340 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2341 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2342 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2343 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2344 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2345 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2346 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2347 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2348 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2349 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2350 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2351 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2352 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2353 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2354 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2355 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2356 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2358 ast_cli_register(&show_mancmd_cli);
2359 ast_cli_register(&show_mancmds_cli);
2360 ast_cli_register(&show_manconn_cli);
2361 ast_cli_register(&show_maneventq_cli);
2362 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2363 registered = 1;
2364 /* Append placeholder event so master_eventq never runs dry */
2365 append_event("Event: Placeholder\r\n\r\n", 0);
2367 portno = DEFAULT_MANAGER_PORT;
2368 displayconnects = 1;
2369 cfg = ast_config_load("manager.conf");
2370 if (!cfg) {
2371 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf. Call management disabled.\n");
2372 return 0;
2374 val = ast_variable_retrieve(cfg, "general", "enabled");
2375 if (val)
2376 enabled = ast_true(val);
2378 val = ast_variable_retrieve(cfg, "general", "block-sockets");
2379 if (val)
2380 block_sockets = ast_true(val);
2382 val = ast_variable_retrieve(cfg, "general", "webenabled");
2383 if (val)
2384 webenabled = ast_true(val);
2386 if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2387 if (sscanf(val, "%d", &portno) != 1) {
2388 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2389 portno = DEFAULT_MANAGER_PORT;
2393 if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2394 displayconnects = ast_true(val);
2396 if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2397 timestampevents = ast_true(val);
2399 if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2400 newhttptimeout = atoi(val);
2402 memset(&ba, 0, sizeof(ba));
2403 ba.sin_family = AF_INET;
2404 ba.sin_port = htons(portno);
2406 if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2407 if (!inet_aton(val, &ba.sin_addr)) {
2408 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2409 memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2414 if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2415 #if 0
2416 /* Can't be done yet */
2417 close(asock);
2418 asock = -1;
2419 #else
2420 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2421 #endif
2423 ast_config_destroy(cfg);
2425 if (webenabled && enabled) {
2426 if (!webregged) {
2427 ast_http_uri_link(&rawmanuri);
2428 ast_http_uri_link(&manageruri);
2429 ast_http_uri_link(&managerxmluri);
2430 webregged = 1;
2432 } else {
2433 if (webregged) {
2434 ast_http_uri_unlink(&rawmanuri);
2435 ast_http_uri_unlink(&manageruri);
2436 ast_http_uri_unlink(&managerxmluri);
2437 webregged = 0;
2441 if (newhttptimeout > 0)
2442 httptimeout = newhttptimeout;
2444 /* If not enabled, do nothing */
2445 if (!enabled) {
2446 return 0;
2449 if (asock < 0) {
2450 asock = socket(AF_INET, SOCK_STREAM, 0);
2451 if (asock < 0) {
2452 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2453 return -1;
2455 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2456 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2457 ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2458 close(asock);
2459 asock = -1;
2460 return -1;
2462 if (listen(asock, 2)) {
2463 ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2464 close(asock);
2465 asock = -1;
2466 return -1;
2468 flags = fcntl(asock, F_GETFL);
2469 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2470 if (option_verbose)
2471 ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2472 ast_pthread_create(&t, NULL, accept_thread, NULL);
2474 return 0;
2477 int reload_manager(void)
2479 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2480 return init_manager();