5240 update
[asterisk/asterisk.git] / main / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief The Asterisk Management Interface - AMI
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Channel Management and more
26  *
27  * \ref amiconf
28  */
29
30 /*! \addtogroup Group_AMI AMI functions
31 */
32 /*! @{
33  Doxygen group */
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
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>
53
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
72 struct fast_originate_helper {
73         char tech[AST_MAX_MANHEADER_LEN];
74         char data[AST_MAX_MANHEADER_LEN];
75         int timeout;
76         char app[AST_MAX_APP];
77         char appdata[AST_MAX_MANHEADER_LEN];
78         char cid_name[AST_MAX_MANHEADER_LEN];
79         char cid_num[AST_MAX_MANHEADER_LEN];
80         char context[AST_MAX_CONTEXT];
81         char exten[AST_MAX_EXTENSION];
82         char idtext[AST_MAX_MANHEADER_LEN];
83         char account[AST_MAX_ACCOUNT_CODE];
84         int priority;
85         struct ast_variable *vars;
86 };
87
88 struct eventqent {
89         int usecount;
90         int category;
91         struct eventqent *next;
92         char eventdata[1];
93 };
94
95 static int enabled = 0;
96 static int portno = DEFAULT_MANAGER_PORT;
97 static int asock = -1;
98 static int displayconnects = 1;
99 static int timestampevents = 0;
100 static int httptimeout = 60;
101
102 static pthread_t t;
103 static int block_sockets = 0;
104 static int num_sessions = 0;
105
106 static struct ast_manager_user *amus =NULL;
107
108 /* Protected by the sessions list lock */
109 struct eventqent *master_eventq = NULL;
110
111 AST_MUTEX_DEFINE_STATIC(amulock);
112 AST_THREADSTORAGE(manager_event_buf, manager_event_buf_init);
113 #define MANAGER_EVENT_BUF_INITSIZE   256
114
115 AST_THREADSTORAGE(astman_append_buf, astman_append_buf_init);
116 #define ASTMAN_APPEND_BUF_INITSIZE   256
117
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" },
132 };
133
134 struct mansession {
135         /*! Execution thread */
136         pthread_t t;
137         /*! Thread lock -- don't use in action callbacks, it's already taken care of  */
138         ast_mutex_t __lock;
139         /*! socket address */
140         struct sockaddr_in sin;
141         /*! TCP socket */
142         int fd;
143         /*! Whether or not we're busy doing an action */
144         int busy;
145         /*! Whether or not we're "dead" */
146         int dead;
147         /*! Whether an HTTP manager is in use */
148         int inuse;
149         /*! Whether an HTTP session should be destroyed */
150         int needdestroy;
151         /*! Whether an HTTP session has someone waiting on events */
152         pthread_t waiting_thread;
153         /*! Unique manager identifer */
154         unsigned long managerid;
155         /*! Session timeout if HTTP */
156         time_t sessiontimeout;
157         /*! Output from manager interface */
158         struct ast_dynamic_str *outputstr;
159         /*! Logged in username */
160         char username[80];
161         /*! Authentication challenge */
162         char challenge[10];
163         /*! Authentication status */
164         int authenticated;
165         /*! Authorization for reading */
166         int readperm;
167         /*! Authorization for writing */
168         int writeperm;
169         /*! Buffer */
170         char inbuf[AST_MAX_MANHEADER_LEN];
171         int inlen;
172         int send_events;
173         int displaysystemname;          /*!< Add system name to manager responses and events */
174         /* Queued events that we've not had the ability to send yet */
175         struct eventqent *eventq;
176         /* Timeout for ast_carefulwrite() */
177         int writetimeout;
178         AST_LIST_ENTRY(mansession) list;
179 };
180
181 struct ast_manager_user {
182         char username[80];
183         char *secret;
184         char *deny;
185         char *permit;
186         char *read;
187         char *write;
188         unsigned int displayconnects:1;
189         struct ast_manager_user *next;
190 };
191
192 static AST_LIST_HEAD_STATIC(sessions, mansession);
193
194 static struct manager_action *first_action = NULL;
195 AST_MUTEX_DEFINE_STATIC(actionlock);
196
197 /*! \brief Convert authority code to string with serveral options */
198 static char *authority_to_str(int authority, char *res, int reslen)
199 {
200         int running_total = 0, i;
201
202         memset(res, 0, reslen);
203         for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
204                 if (authority & perms[i].num) {
205                         if (*res) {
206                                 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
207                                 running_total++;
208                         }
209                         strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
210                         running_total += strlen(perms[i].label);
211                 }
212         }
213
214         if (ast_strlen_zero(res))
215                 ast_copy_string(res, "<none>", reslen);
216
217         return res;
218 }
219
220 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
221 {
222         struct manager_action *cur;
223         int which = 0;
224         char *ret = NULL;
225
226         ast_mutex_lock(&actionlock);
227         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
228                 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
229                         ret = ast_strdup(cur->action);
230                         break;  /* make sure we exit even if ast_strdup() returns NULL */
231                 }
232         }
233         ast_mutex_unlock(&actionlock);
234
235         return ret;
236 }
237
238 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
239 {
240         while (*src && (*maxlen > 6)) {
241                 switch (*src) {
242                 case '<':
243                         strcpy(*dst, "&lt;");
244                         (*dst) += 4;
245                         *maxlen -= 4;
246                         break;
247                 case '>':
248                         strcpy(*dst, "&gt;");
249                         (*dst) += 4;
250                         *maxlen -= 4;
251                         break;
252                 case '\"':
253                         strcpy(*dst, "&quot;");
254                         (*dst) += 6;
255                         *maxlen -= 6;
256                         break;
257                 case '\'':
258                         strcpy(*dst, "&apos;");
259                         (*dst) += 6;
260                         *maxlen -= 6;
261                         break;
262                 case '&':
263                         strcpy(*dst, "&amp;");
264                         (*dst) += 5;
265                         *maxlen -= 5;
266                         break;
267                 default:
268                         *(*dst)++ = lower ? tolower(*src) : *src;
269                         (*maxlen)--;
270                 }
271                 src++;
272         }
273 }
274
275 static char *xml_translate(char *in, struct ast_variable *vars)
276 {
277         struct ast_variable *v;
278         char *dest = NULL;
279         char *out, *tmp, *var, *val;
280         char *objtype = NULL;
281         int colons = 0;
282         int breaks = 0;
283         size_t len;
284         int count = 1;
285         int escaped = 0;
286         int inobj = 0;
287         int x;
288
289         for (v = vars; v; v = v->next) {
290                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
291                         dest = v->value;
292                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
293                         objtype = v->value;
294         }
295         if (!dest)
296                 dest = "unknown";
297         if (!objtype)
298                 objtype = "generic";
299         for (x = 0; in[x]; x++) {
300                 if (in[x] == ':')
301                         colons++;
302                 else if (in[x] == '\n')
303                         breaks++;
304                 else if (strchr("&\"<>", in[x]))
305                         escaped++;
306         }
307         len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
308         out = ast_malloc(len);
309         if (!out)
310                 return 0;
311         tmp = out;
312         while (*in) {
313                 var = in;
314                 while (*in && (*in >= 32))
315                         in++;
316                 if (*in) {
317                         if ((count > 3) && inobj) {
318                                 ast_build_string(&tmp, &len, " /></response>\n");
319                                 inobj = 0;
320                         }
321                         count = 0;
322                         while (*in && (*in < 32)) {
323                                 *in = '\0';
324                                 in++;
325                                 count++;
326                         }
327                         val = strchr(var, ':');
328                         if (val) {
329                                 *val = '\0';
330                                 val++;
331                                 if (*val == ' ')
332                                         val++;
333                                 if (!inobj) {
334                                         ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
335                                         inobj = 1;
336                                 }
337                                 ast_build_string(&tmp, &len, " ");
338                                 xml_copy_escape(&tmp, &len, var, 1);
339                                 ast_build_string(&tmp, &len, "='");
340                                 xml_copy_escape(&tmp, &len, val, 0);
341                                 ast_build_string(&tmp, &len, "'");
342                         }
343                 }
344         }
345         if (inobj)
346                 ast_build_string(&tmp, &len, " /></response>\n");
347         return out;
348 }
349
350 static char *html_translate(char *in)
351 {
352         int x;
353         int colons = 0;
354         int breaks = 0;
355         size_t len;
356         int count = 1;
357         char *tmp, *var, *val, *out;
358
359         for (x=0; in[x]; x++) {
360                 if (in[x] == ':')
361                         colons++;
362                 if (in[x] == '\n')
363                         breaks++;
364         }
365         len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
366         out = ast_malloc(len);
367         if (!out)
368                 return 0;
369         tmp = out;
370         while (*in) {
371                 var = in;
372                 while (*in && (*in >= 32))
373                         in++;
374                 if (*in) {
375                         if ((count % 4) == 0){
376                                 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
377                         }
378                         count = 0;
379                         while (*in && (*in < 32)) {
380                                 *in = '\0';
381                                 in++;
382                                 count++;
383                         }
384                         val = strchr(var, ':');
385                         if (val) {
386                                 *val = '\0';
387                                 val++;
388                                 if (*val == ' ')
389                                         val++;
390                                 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
391                         }
392                 }
393         }
394         return out;
395 }
396
397
398
399 struct ast_manager_user *ast_get_manager_by_name_locked(const char *name)
400 {
401         struct ast_manager_user *tmp = NULL;
402         tmp=amus;
403         while (tmp) {
404                 if (!strcasecmp(tmp->username, name))
405                         break;
406                 tmp = tmp->next;
407         }
408         if ( tmp)
409                 return tmp;
410         return NULL;
411 }
412
413
414
415 int *ast_manager_user_add(struct ast_manager_user *amu) {
416         if (!amu) {
417                 ast_log(LOG_DEBUG, "You cant pass NULL to that function");
418                 return NULL;
419         }
420         ast_mutex_lock(&amulock);
421         amu->next = amus;
422         amus = amu;
423         ast_mutex_unlock(&amulock);
424         return NULL;
425 }
426
427 void astman_append(struct mansession *s, const char *fmt, ...)
428 {
429         va_list ap;
430         struct ast_dynamic_str *buf;
431
432         if (!(buf = ast_dynamic_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
433                 return;
434
435         va_start(ap, fmt);
436         ast_dynamic_str_thread_set_va(&buf, 0, &astman_append_buf, fmt, ap);
437         va_end(ap);
438
439         if (s->fd > -1)
440                 ast_carefulwrite(s->fd, buf->str, strlen(buf->str), s->writetimeout);
441         else {
442                 if (!s->outputstr && !(s->outputstr = ast_calloc(1, sizeof(*s->outputstr))))
443                         return;
444
445                 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf->str);
446         }
447 }
448
449 static int handle_showmancmd(int fd, int argc, char *argv[])
450 {
451         struct manager_action *cur = first_action;
452         char authority[80];
453         int num;
454
455         if (argc != 4)
456                 return RESULT_SHOWUSAGE;
457
458         ast_mutex_lock(&actionlock);
459         for (; cur; cur = cur->next) { /* Walk the list of actions */
460                 for (num = 3; num < argc; num++) {
461                         if (!strcasecmp(cur->action, argv[num])) {
462                                 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 : "");
463                         }
464                 }
465         }
466         ast_mutex_unlock(&actionlock);
467
468         return RESULT_SUCCESS;
469 }
470
471 static int handle_showmanager(int fd, int argc, char *argv[])
472 {
473         struct ast_manager_user *tmp;
474         if (argc != 4 )
475                 return RESULT_SHOWUSAGE;
476
477         /* try to lock manager_user list ... */
478         if (ast_mutex_lock(&amulock)) {
479                 ast_log(LOG_ERROR, "Unable to lock manager_user list\n");
480                 return -1;
481         }
482
483         tmp = ast_get_manager_by_name_locked(argv[3]);
484
485         if (!tmp) {
486                 ast_cli(fd, "There are no manager called %s\n",argv[3]);
487                 ast_mutex_unlock(&amulock);
488                 return -1;
489         }
490         ast_cli(fd,"\n");
491         ast_cli(fd,
492                 "       username: %s\n"
493                 "         secret: %s\n"
494                 "           deny: %s\n"
495                 "         permit: %s\n"
496                 "           read: %s\n"
497                 "          write: %s\n"
498                 "displayconnects: %s\n",
499                 (tmp->username ? tmp->username : "(N/A)"),
500                 (tmp->secret ? tmp->secret : "(N/A)"),
501                 (tmp->deny ? tmp->deny : "(N/A)"),
502                 (tmp->permit ? tmp->permit : "(N/A)"),
503                 (tmp->read ? tmp->read : "(N/A)"),
504                 (tmp->write ? tmp->write : "(N/A)"),
505                 (tmp->displayconnects ? "yes" : "no"));
506         ast_mutex_unlock(&amulock);
507
508         return RESULT_SUCCESS;
509 }
510
511
512 static int handle_showmanagers(int fd, int argc, char *argv[]) {
513         struct ast_manager_user *tmp;
514         int count_amu = 0;
515
516         if ( argc > 4 )
517                 return RESULT_SHOWUSAGE;
518
519         /* try to lock manager_user list ... */
520         if (ast_mutex_lock(&amulock)) {
521                 ast_log(LOG_ERROR, "Unable to lock manager_user list\n");
522                 return -1;
523         }
524
525         tmp=amus;
526         if (!tmp) {
527                 ast_cli(fd, "There are no manager user.\n");
528                 ast_mutex_unlock(&amulock);
529                 return -1;
530         }
531         ast_cli(fd, "\nusername\n--------\n");
532         while (tmp) {
533                 ast_cli(fd, "%s\n", tmp->username);
534                 tmp = tmp->next;
535                 count_amu++;
536         }
537         ast_mutex_unlock(&amulock);
538         ast_cli(fd,"-------------------\n");
539         ast_cli(fd,"%d manager users configured.\n", count_amu);
540         return RESULT_SUCCESS;
541 }
542
543
544 /*! \brief  CLI command
545         Should change to "manager show commands" */
546 static int handle_showmancmds(int fd, int argc, char *argv[])
547 {
548         struct manager_action *cur = first_action;
549         char authority[80];
550         char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
551
552         ast_cli(fd, format, "Action", "Privilege", "Synopsis");
553         ast_cli(fd, format, "------", "---------", "--------");
554
555         ast_mutex_lock(&actionlock);
556         for (; cur; cur = cur->next) /* Walk the list of actions */
557                 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
558         ast_mutex_unlock(&actionlock);
559
560         return RESULT_SUCCESS;
561 }
562
563 /*! \brief CLI command show manager connected */
564 /* Should change to "manager show connected" */
565 static int handle_showmanconn(int fd, int argc, char *argv[])
566 {
567         struct mansession *s;
568         char *format = "  %-15.15s  %-15.15s\n";
569
570         ast_cli(fd, format, "Username", "IP Address");
571
572         AST_LIST_LOCK(&sessions);
573         AST_LIST_TRAVERSE(&sessions, s, list)
574                 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
575         AST_LIST_UNLOCK(&sessions);
576
577         return RESULT_SUCCESS;
578 }
579
580 /*! \brief CLI command show manager connected */
581 /* Should change to "manager show connected" */
582 static int handle_showmaneventq(int fd, int argc, char *argv[])
583 {
584         struct eventqent *s;
585
586         AST_LIST_LOCK(&sessions);
587         for (s = master_eventq; s; s = s->next) {
588                 ast_cli(fd, "Usecount: %d\n",s->usecount);
589                 ast_cli(fd, "Category: %d\n", s->category);
590                 ast_cli(fd, "Event:\n%s", s->eventdata);
591         }
592         AST_LIST_UNLOCK(&sessions);
593
594         return RESULT_SUCCESS;
595 }
596
597 static char showmancmd_help[] =
598 "Usage: show manager command <actionname>\n"
599 "       Shows the detailed description for a specific Asterisk manager interface command.\n";
600
601 static char showmancmds_help[] =
602 "Usage: show manager commands\n"
603 "       Prints a listing of all the available Asterisk manager interface commands.\n";
604
605 static char showmanconn_help[] =
606 "Usage: show manager connected\n"
607 "       Prints a listing of the users that are currently connected to the\n"
608 "Asterisk manager interface.\n";
609
610 static char showmaneventq_help[] =
611 "Usage: show manager eventq\n"
612 "       Prints a listing of all events pending in the Asterisk manger\n"
613 "event queue.\n";
614
615 static char showmanagers_help[] =
616 "Usage: show managers\n"
617 "       Prints a listing of all managers that are currently configured on that\n"
618 " system.\n";
619
620 static char showmanager_help[] =
621 " Usage: show manager foobar\n"
622 " Display all the infos related to the manager foobar.\n";
623
624 static struct ast_cli_entry show_managers_cli =
625         { { "manager", "show", "users" },
626         handle_showmanagers, "Show all managers users (connected or not)", showmanagers_help };
627
628 static struct ast_cli_entry show_manager_cli =
629 { { "manager", "show", "user" }, handle_showmanager, "Display information on a specific manager", showmanager_help};
630
631
632 static struct ast_cli_entry show_mancmd_cli =
633         { { "show", "manager", "command", NULL },
634         handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
635
636 static struct ast_cli_entry show_mancmds_cli =
637         { { "show", "manager", "commands", NULL },
638         handle_showmancmds, "List manager interface commands", showmancmds_help };
639
640 static struct ast_cli_entry show_manconn_cli =
641         { { "show", "manager", "connected", NULL },
642         handle_showmanconn, "Show connected manager interface users", showmanconn_help };
643
644 static struct ast_cli_entry show_maneventq_cli =
645         { { "show", "manager", "eventq", NULL },
646         handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
647
648 static void unuse_eventqent(struct eventqent *e)
649 {
650         if (ast_atomic_dec_and_test(&e->usecount) && e->next)
651                 pthread_kill(t, SIGURG);
652 }
653
654 static void free_session(struct mansession *s)
655 {
656         struct eventqent *eqe;
657         if (s->fd > -1)
658                 close(s->fd);
659         if (s->outputstr)
660                 free(s->outputstr);
661         ast_mutex_destroy(&s->__lock);
662         while (s->eventq) {
663                 eqe = s->eventq;
664                 s->eventq = s->eventq->next;
665                 unuse_eventqent(eqe);
666         }
667         free(s);
668 }
669
670 static void destroy_session(struct mansession *s)
671 {
672         AST_LIST_LOCK(&sessions);
673         AST_LIST_REMOVE(&sessions, s, list);
674         AST_LIST_UNLOCK(&sessions);
675
676         ast_atomic_fetchadd_int(&num_sessions, -1);
677         free_session(s);
678 }
679
680 char *astman_get_header(struct message *m, char *var)
681 {
682         char cmp[80];
683         int x;
684
685         snprintf(cmp, sizeof(cmp), "%s: ", var);
686
687         for (x = 0; x < m->hdrcount; x++) {
688                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
689                         return m->headers[x] + strlen(cmp);
690         }
691
692         return "";
693 }
694
695 struct ast_variable *astman_get_variables(struct message *m)
696 {
697         int varlen, x, y;
698         struct ast_variable *head = NULL, *cur;
699         char *var, *val;
700
701         char *parse;
702         AST_DECLARE_APP_ARGS(args,
703                 AST_APP_ARG(vars)[32];
704         );
705
706         varlen = strlen("Variable: ");
707
708         for (x = 0; x < m->hdrcount; x++) {
709                 if (strncasecmp("Variable: ", m->headers[x], varlen))
710                         continue;
711
712                 parse = ast_strdupa(m->headers[x] + varlen);
713
714                 AST_STANDARD_APP_ARGS(args, parse);
715                 if (args.argc) {
716                         for (y = 0; y < args.argc; y++) {
717                                 if (!args.vars[y])
718                                         continue;
719                                 var = val = ast_strdupa(args.vars[y]);
720                                 strsep(&val, "=");
721                                 if (!val || ast_strlen_zero(var))
722                                         continue;
723                                 cur = ast_variable_new(var, val);
724                                 if (head) {
725                                         cur->next = head;
726                                         head = cur;
727                                 } else
728                                         head = cur;
729                         }
730                 }
731         }
732
733         return head;
734 }
735
736 /*! \note NOTE:
737    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
738    hold the session lock _or_ be running in an action callback (in which case s->busy will
739    be non-zero). In either of these cases, there is no need to lock-protect the session's
740    fd, since no other output will be sent (events will be queued), and no input will
741    be read until either the current action finishes or get_input() obtains the session
742    lock.
743  */
744 void astman_send_error(struct mansession *s, struct message *m, char *error)
745 {
746         char *id = astman_get_header(m,"ActionID");
747
748         astman_append(s, "Response: Error\r\n");
749         if (!ast_strlen_zero(id))
750                 astman_append(s, "ActionID: %s\r\n", id);
751         astman_append(s, "Message: %s\r\n\r\n", error);
752 }
753
754 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
755 {
756         char *id = astman_get_header(m,"ActionID");
757
758         astman_append(s, "Response: %s\r\n", resp);
759         if (!ast_strlen_zero(id))
760                 astman_append(s, "ActionID: %s\r\n", id);
761         if (msg)
762                 astman_append(s, "Message: %s\r\n\r\n", msg);
763         else
764                 astman_append(s, "\r\n");
765 }
766
767 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
768 {
769         astman_send_response(s, m, "Success", msg);
770 }
771
772 /*! Tells you if smallstr exists inside bigstr
773    which is delim by delim and uses no buf or stringsep
774    ast_instring("this|that|more","this",',') == 1;
775
776    feel free to move this to app.c -anthm */
777 static int ast_instring(char *bigstr, char *smallstr, char delim)
778 {
779         char *val = bigstr, *next;
780
781         do {
782                 if ((next = strchr(val, delim))) {
783                         if (!strncmp(val, smallstr, (next - val)))
784                                 return 1;
785                         else
786                                 continue;
787                 } else
788                         return !strcmp(smallstr, val);
789
790         } while (*(val = (next + 1)));
791
792         return 0;
793 }
794
795 static int get_perm(char *instr)
796 {
797         int x = 0, ret = 0;
798
799         if (!instr)
800                 return 0;
801
802         for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
803                 if (ast_instring(instr, perms[x].label, ','))
804                         ret |= perms[x].num;
805         }
806
807         return ret;
808 }
809
810 static int ast_is_number(char *string)
811 {
812         int ret = 1, x = 0;
813
814         if (!string)
815                 return 0;
816
817         for (x = 0; x < strlen(string); x++) {
818                 if (!(string[x] >= 48 && string[x] <= 57)) {
819                         ret = 0;
820                         break;
821                 }
822         }
823
824         return ret ? atoi(string) : 0;
825 }
826
827 static int ast_strings_to_mask(char *string)
828 {
829         int x, ret = -1;
830
831         x = ast_is_number(string);
832
833         if (x)
834                 ret = x;
835         else if (ast_strlen_zero(string))
836                 ret = -1;
837         else if (ast_false(string))
838                 ret = 0;
839         else if (ast_true(string)) {
840                 ret = 0;
841                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
842                         ret |= perms[x].num;
843         } else {
844                 ret = 0;
845                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
846                         if (ast_instring(string, perms[x].label, ','))
847                                 ret |= perms[x].num;
848                 }
849         }
850
851         return ret;
852 }
853
854 /*! \brief
855    Rather than braindead on,off this now can also accept a specific int mask value
856    or a ',' delim list of mask strings (the same as manager.conf) -anthm
857 */
858 static int set_eventmask(struct mansession *s, char *eventmask)
859 {
860         int maskint = ast_strings_to_mask(eventmask);
861
862         ast_mutex_lock(&s->__lock);
863         if (maskint >= 0)
864                 s->send_events = maskint;
865         ast_mutex_unlock(&s->__lock);
866
867         return maskint;
868 }
869
870 static int authenticate(struct mansession *s, struct message *m)
871 {
872         struct ast_config *cfg;
873         char *cat;
874         char *user = astman_get_header(m, "Username");
875         char *pass = astman_get_header(m, "Secret");
876         char *authtype = astman_get_header(m, "AuthType");
877         char *key = astman_get_header(m, "Key");
878         char *events = astman_get_header(m, "Events");
879
880         cfg = ast_config_load("manager.conf");
881         if (!cfg)
882                 return -1;
883         cat = ast_category_browse(cfg, NULL);
884         while (cat) {
885                 if (strcasecmp(cat, "general")) {
886                         /* This is a user */
887                         if (!strcasecmp(cat, user)) {
888                                 struct ast_variable *v;
889                                 struct ast_ha *ha = NULL;
890                                 char *password = NULL;
891
892                                 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
893                                         if (!strcasecmp(v->name, "secret")) {
894                                                 password = v->value;
895                                         } else if (!strcasecmp(v->name, "displaysystemname")) {
896                                                 if (ast_true(v->value)) {
897                                                         if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
898                                                                 s->displaysystemname = 1;
899                                                         } else {
900                                                                 ast_log(LOG_ERROR, "Can't enable displaysystemname in manager.conf - no system name configured in asterisk.conf\n");
901                                                         }
902                                                 }
903                                         } else if (!strcasecmp(v->name, "permit") ||
904                                                    !strcasecmp(v->name, "deny")) {
905                                                 ha = ast_append_ha(v->name, v->value, ha);
906                                         } else if (!strcasecmp(v->name, "writetimeout")) {
907                                                 int val = atoi(v->value);
908
909                                                 if (val < 100)
910                                                         ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
911                                                 else
912                                                         s->writetimeout = val;
913                                         }
914
915                                 }
916                                 if (ha && !ast_apply_ha(ha, &(s->sin))) {
917                                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
918                                         ast_free_ha(ha);
919                                         ast_config_destroy(cfg);
920                                         return -1;
921                                 } else if (ha)
922                                         ast_free_ha(ha);
923                                 if (!strcasecmp(authtype, "MD5")) {
924                                         if (!ast_strlen_zero(key) && s->challenge) {
925                                                 int x;
926                                                 int len = 0;
927                                                 char md5key[256] = "";
928                                                 struct MD5Context md5;
929                                                 unsigned char digest[16];
930                                                 MD5Init(&md5);
931                                                 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
932                                                 MD5Update(&md5, (unsigned char *) password, strlen(password));
933                                                 MD5Final(digest, &md5);
934                                                 for (x=0; x<16; x++)
935                                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
936                                                 if (!strcmp(md5key, key))
937                                                         break;
938                                                 else {
939                                                         ast_config_destroy(cfg);
940                                                         return -1;
941                                                 }
942                                         }
943                                 } else if (password && !strcmp(password, pass)) {
944                                         break;
945                                 } else {
946                                         ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
947                                         ast_config_destroy(cfg);
948                                         return -1;
949                                 }
950                         }
951                 }
952                 cat = ast_category_browse(cfg, cat);
953         }
954         if (cat) {
955                 ast_copy_string(s->username, cat, sizeof(s->username));
956                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
957                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
958                 ast_config_destroy(cfg);
959                 if (events)
960                         set_eventmask(s, events);
961                 return 0;
962         }
963         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
964         ast_config_destroy(cfg);
965         return -1;
966 }
967
968 /*! \brief Manager PING */
969 static char mandescr_ping[] =
970 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
971 "  manager connection open.\n"
972 "Variables: NONE\n";
973
974 static int action_ping(struct mansession *s, struct message *m)
975 {
976         astman_send_response(s, m, "Pong", NULL);
977         return 0;
978 }
979
980 static char mandescr_getconfig[] =
981 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
982 "file by category and contents.\n"
983 "Variables:\n"
984 "   Filename: Configuration filename (e.g. foo.conf)\n";
985
986 static int action_getconfig(struct mansession *s, struct message *m)
987 {
988         struct ast_config *cfg;
989         char *fn = astman_get_header(m, "Filename");
990         int catcount = 0;
991         int lineno = 0;
992         char *category=NULL;
993         struct ast_variable *v;
994         char idText[256] = "";
995         char *id = astman_get_header(m, "ActionID");
996
997         if (!ast_strlen_zero(id))
998                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
999
1000         if (ast_strlen_zero(fn)) {
1001                 astman_send_error(s, m, "Filename not specified");
1002                 return 0;
1003         }
1004         if (!(cfg = ast_config_load(fn))) {
1005                 astman_send_error(s, m, "Config file not found");
1006                 return 0;
1007         }
1008         astman_append(s, "Response: Success\r\n%s", idText);
1009         while ((category = ast_category_browse(cfg, category))) {
1010                 lineno = 0;
1011                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1012                 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1013                         astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1014                 catcount++;
1015         }
1016         ast_config_destroy(cfg);
1017         astman_append(s, "\r\n");
1018
1019         return 0;
1020 }
1021
1022
1023 static void handle_updates(struct mansession *s, struct message *m, struct ast_config *cfg)
1024 {
1025         int x;
1026         char hdr[40];
1027         char *action, *cat, *var, *value, *match;
1028         struct ast_category *category;
1029         struct ast_variable *v;
1030
1031         for (x=0;x<100000;x++) {
1032                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1033                 action = astman_get_header(m, hdr);
1034                 if (ast_strlen_zero(action))
1035                         break;
1036                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1037                 cat = astman_get_header(m, hdr);
1038                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1039                 var = astman_get_header(m, hdr);
1040                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1041                 value = astman_get_header(m, hdr);
1042                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1043                 match = astman_get_header(m, hdr);
1044                 if (!strcasecmp(action, "newcat")) {
1045                         if (!ast_strlen_zero(cat)) {
1046                                 category = ast_category_new(cat);
1047                                 if (category) {
1048                                         ast_category_append(cfg, category);
1049                                 }
1050                         }
1051                 } else if (!strcasecmp(action, "renamecat")) {
1052                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1053                                 category = ast_category_get(cfg, cat);
1054                                 if (category)
1055                                         ast_category_rename(category, value);
1056                         }
1057                 } else if (!strcasecmp(action, "delcat")) {
1058                         if (!ast_strlen_zero(cat))
1059                                 ast_category_delete(cfg, cat);
1060                 } else if (!strcasecmp(action, "update")) {
1061                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1062                                 ast_variable_update(category, var, value, match);
1063                 } else if (!strcasecmp(action, "delete")) {
1064                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1065                                 ast_variable_delete(category, var, match);
1066                 } else if (!strcasecmp(action, "append")) {
1067                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1068                                 (category = ast_category_get(cfg, cat)) &&
1069                                 (v = ast_variable_new(var, value))){
1070                                 if (match && !strcasecmp(match, "object"))
1071                                         v->object = 1;
1072                                 ast_variable_append(category, v);
1073                         }
1074                 }
1075         }
1076 }
1077
1078 static char mandescr_updateconfig[] =
1079 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1080 "file by category and contents.\n"
1081 "Variables (X's represent 6 digit number beginning with 000000):\n"
1082 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1083 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1084 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1085 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1086 "   Cat-XXXXXX:    Category to operate on\n"
1087 "   Var-XXXXXX:    Variable to work on\n"
1088 "   Value-XXXXXX:  Value to work on\n"
1089 "   Match-XXXXXX:  Extra match required to match line\n";
1090
1091 static int action_updateconfig(struct mansession *s, struct message *m)
1092 {
1093         struct ast_config *cfg;
1094         char *sfn = astman_get_header(m, "SrcFilename");
1095         char *dfn = astman_get_header(m, "DstFilename");
1096         int res;
1097         char idText[256] = "";
1098         char *id = astman_get_header(m, "ActionID");
1099         char *rld = astman_get_header(m, "Reload");
1100
1101         if (!ast_strlen_zero(id))
1102                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1103
1104         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1105                 astman_send_error(s, m, "Filename not specified");
1106                 return 0;
1107         }
1108         if (!(cfg = ast_config_load(sfn))) {
1109                 astman_send_error(s, m, "Config file not found");
1110                 return 0;
1111         }
1112         handle_updates(s, m, cfg);
1113         res = config_text_file_save(dfn, cfg, "Manager");
1114         ast_config_destroy(cfg);
1115         if (res) {
1116                 astman_send_error(s, m, "Save of config failed");
1117                 return 0;
1118         }
1119         astman_append(s, "Response: Success\r\n%s\r\n", idText);
1120         if (!ast_strlen_zero(rld)) {
1121                 if (ast_true(rld))
1122                         rld = NULL;
1123                 ast_module_reload(rld);
1124         }
1125         return 0;
1126 }
1127
1128 /*! \brief Manager WAITEVENT */
1129 static char mandescr_waitevent[] =
1130 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1131 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1132 "session, events will be generated and queued.\n"
1133 "Variables: \n"
1134 "   Timeout: Maximum time to wait for events\n";
1135
1136 static int action_waitevent(struct mansession *s, struct message *m)
1137 {
1138         char *timeouts = astman_get_header(m, "Timeout");
1139         int timeout = -1, max;
1140         int x;
1141         int needexit = 0;
1142         time_t now;
1143         struct eventqent *eqe;
1144         char *id = astman_get_header(m,"ActionID");
1145         char idText[256] = "";
1146
1147         if (!ast_strlen_zero(id))
1148                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1149
1150         if (!ast_strlen_zero(timeouts)) {
1151                 sscanf(timeouts, "%i", &timeout);
1152         }
1153
1154         ast_mutex_lock(&s->__lock);
1155         if (s->waiting_thread != AST_PTHREADT_NULL) {
1156                 pthread_kill(s->waiting_thread, SIGURG);
1157         }
1158         if (s->sessiontimeout) {
1159                 time(&now);
1160                 max = s->sessiontimeout - now - 10;
1161                 if (max < 0)
1162                         max = 0;
1163                 if ((timeout < 0) || (timeout > max))
1164                         timeout = max;
1165                 if (!s->send_events)
1166                         s->send_events = -1;
1167                 /* Once waitevent is called, always queue events from now on */
1168                 if (s->busy == 1)
1169                         s->busy = 2;
1170         }
1171         ast_mutex_unlock(&s->__lock);
1172         s->waiting_thread = pthread_self();
1173         if (option_debug)
1174                 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1175         for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1176                 ast_mutex_lock(&s->__lock);
1177                 if (s->eventq && s->eventq->next)
1178                         needexit = 1;
1179                 if (s->waiting_thread != pthread_self())
1180                         needexit = 1;
1181                 if (s->needdestroy)
1182                         needexit = 1;
1183                 ast_mutex_unlock(&s->__lock);
1184                 if (needexit)
1185                         break;
1186                 if (s->fd > 0) {
1187                         if (ast_wait_for_input(s->fd, 1000))
1188                                 break;
1189                 } else {
1190                         sleep(1);
1191                 }
1192         }
1193         if (option_debug)
1194                 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1195         ast_mutex_lock(&s->__lock);
1196         if (s->waiting_thread == pthread_self()) {
1197                 astman_send_response(s, m, "Success", "Waiting for Event...");
1198                 /* Only show events if we're the most recent waiter */
1199                 while(s->eventq->next) {
1200                         eqe = s->eventq->next;
1201                         if (((s->readperm & eqe->category) == eqe->category) &&
1202                             ((s->send_events & eqe->category) == eqe->category)) {
1203                                 astman_append(s, "%s", eqe->eventdata);
1204                         }
1205                         unuse_eventqent(s->eventq);
1206                         s->eventq = eqe;
1207                 }
1208                 astman_append(s,
1209                         "Event: WaitEventComplete\r\n"
1210                         "%s"
1211                         "\r\n", idText);
1212                 s->waiting_thread = AST_PTHREADT_NULL;
1213         } else {
1214                 ast_log(LOG_DEBUG, "Abandoning event request!\n");
1215         }
1216         ast_mutex_unlock(&s->__lock);
1217         return 0;
1218 }
1219
1220 static char mandescr_listcommands[] =
1221 "Description: Returns the action name and synopsis for every\n"
1222 "  action that is available to the user\n"
1223 "Variables: NONE\n";
1224
1225 static int action_listcommands(struct mansession *s, struct message *m)
1226 {
1227         struct manager_action *cur = first_action;
1228         char idText[256] = "";
1229         char temp[BUFSIZ];
1230         char *id = astman_get_header(m,"ActionID");
1231
1232         if (!ast_strlen_zero(id))
1233                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1234         astman_append(s, "Response: Success\r\n%s", idText);
1235         ast_mutex_lock(&actionlock);
1236         while (cur) { /* Walk the list of actions */
1237                 if ((s->writeperm & cur->authority) == cur->authority)
1238                         astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1239                 cur = cur->next;
1240         }
1241         ast_mutex_unlock(&actionlock);
1242         astman_append(s, "\r\n");
1243
1244         return 0;
1245 }
1246
1247 static char mandescr_events[] =
1248 "Description: Enable/Disable sending of events to this manager\n"
1249 "  client.\n"
1250 "Variables:\n"
1251 "       EventMask: 'on' if all events should be sent,\n"
1252 "               'off' if no events should be sent,\n"
1253 "               'system,call,log' to select which flags events should have to be sent.\n";
1254
1255 static int action_events(struct mansession *s, struct message *m)
1256 {
1257         char *mask = astman_get_header(m, "EventMask");
1258         int res;
1259
1260         res = set_eventmask(s, mask);
1261         if (res > 0)
1262                 astman_send_response(s, m, "Events On", NULL);
1263         else if (res == 0)
1264                 astman_send_response(s, m, "Events Off", NULL);
1265
1266         return 0;
1267 }
1268
1269 static char mandescr_logoff[] =
1270 "Description: Logoff this manager session\n"
1271 "Variables: NONE\n";
1272
1273 static int action_logoff(struct mansession *s, struct message *m)
1274 {
1275         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1276         return -1;
1277 }
1278
1279 static char mandescr_hangup[] =
1280 "Description: Hangup a channel\n"
1281 "Variables: \n"
1282 "       Channel: The channel name to be hungup\n";
1283
1284 static int action_hangup(struct mansession *s, struct message *m)
1285 {
1286         struct ast_channel *c = NULL;
1287         char *name = astman_get_header(m, "Channel");
1288         if (ast_strlen_zero(name)) {
1289                 astman_send_error(s, m, "No channel specified");
1290                 return 0;
1291         }
1292         c = ast_get_channel_by_name_locked(name);
1293         if (!c) {
1294                 astman_send_error(s, m, "No such channel");
1295                 return 0;
1296         }
1297         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1298         ast_channel_unlock(c);
1299         astman_send_ack(s, m, "Channel Hungup");
1300         return 0;
1301 }
1302
1303 static char mandescr_setvar[] =
1304 "Description: Set a global or local channel variable.\n"
1305 "Variables: (Names marked with * are required)\n"
1306 "       Channel: Channel to set variable for\n"
1307 "       *Variable: Variable name\n"
1308 "       *Value: Value\n";
1309
1310 static int action_setvar(struct mansession *s, struct message *m)
1311 {
1312         struct ast_channel *c = NULL;
1313         char *name = astman_get_header(m, "Channel");
1314         char *varname = astman_get_header(m, "Variable");
1315         char *varval = astman_get_header(m, "Value");
1316
1317         if (ast_strlen_zero(varname)) {
1318                 astman_send_error(s, m, "No variable specified");
1319                 return 0;
1320         }
1321
1322         if (ast_strlen_zero(varval)) {
1323                 astman_send_error(s, m, "No value specified");
1324                 return 0;
1325         }
1326
1327         if (!ast_strlen_zero(name)) {
1328                 c = ast_get_channel_by_name_locked(name);
1329                 if (!c) {
1330                         astman_send_error(s, m, "No such channel");
1331                         return 0;
1332                 }
1333         }
1334
1335         pbx_builtin_setvar_helper(c, varname, varval);
1336
1337         if (c)
1338                 ast_channel_unlock(c);
1339
1340         astman_send_ack(s, m, "Variable Set");
1341
1342         return 0;
1343 }
1344
1345 static char mandescr_getvar[] =
1346 "Description: Get the value of a global or local channel variable.\n"
1347 "Variables: (Names marked with * are required)\n"
1348 "       Channel: Channel to read variable from\n"
1349 "       *Variable: Variable name\n"
1350 "       ActionID: Optional Action id for message matching.\n";
1351
1352 static int action_getvar(struct mansession *s, struct message *m)
1353 {
1354         struct ast_channel *c = NULL;
1355         char *name = astman_get_header(m, "Channel");
1356         char *varname = astman_get_header(m, "Variable");
1357         char *id = astman_get_header(m,"ActionID");
1358         char *varval;
1359         char workspace[1024];
1360
1361         if (ast_strlen_zero(varname)) {
1362                 astman_send_error(s, m, "No variable specified");
1363                 return 0;
1364         }
1365
1366         if (!ast_strlen_zero(name)) {
1367                 c = ast_get_channel_by_name_locked(name);
1368                 if (!c) {
1369                         astman_send_error(s, m, "No such channel");
1370                         return 0;
1371                 }
1372         }
1373
1374         if (varname[strlen(varname) - 1] == ')') {
1375                 ast_func_read(c, varname, workspace, sizeof(workspace));
1376         } else {
1377                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1378         }
1379
1380         if (c)
1381                 ast_channel_unlock(c);
1382         astman_append(s, "Response: Success\r\n"
1383                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1384         if (!ast_strlen_zero(id))
1385                 astman_append(s, "ActionID: %s\r\n",id);
1386         astman_append(s, "\r\n");
1387
1388         return 0;
1389 }
1390
1391
1392 /*! \brief Manager "status" command to show channels */
1393 /* Needs documentation... */
1394 static int action_status(struct mansession *s, struct message *m)
1395 {
1396         char *id = astman_get_header(m,"ActionID");
1397         char *name = astman_get_header(m,"Channel");
1398         char idText[256] = "";
1399         struct ast_channel *c;
1400         char bridge[256];
1401         struct timeval now = ast_tvnow();
1402         long elapsed_seconds = 0;
1403         int all = ast_strlen_zero(name); /* set if we want all channels */
1404
1405         astman_send_ack(s, m, "Channel status will follow");
1406         if (!ast_strlen_zero(id))
1407                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1408         if (all)
1409                 c = ast_channel_walk_locked(NULL);
1410         else {
1411                 c = ast_get_channel_by_name_locked(name);
1412                 if (!c) {
1413                         astman_send_error(s, m, "No such channel");
1414                         return 0;
1415                 }
1416         }
1417         /* if we look by name, we break after the first iteration */
1418         while (c) {
1419                 if (c->_bridge)
1420                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1421                 else
1422                         bridge[0] = '\0';
1423                 if (c->pbx) {
1424                         if (c->cdr) {
1425                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1426                         }
1427                         astman_append(s,
1428                         "Event: Status\r\n"
1429                         "Privilege: Call\r\n"
1430                         "Channel: %s\r\n"
1431                         "CallerID: %s\r\n"              /* This parameter is deprecated and will be removed post-1.4 */
1432                         "CallerIDNum: %s\r\n"
1433                         "CallerIDName: %s\r\n"
1434                         "Account: %s\r\n"
1435                         "State: %s\r\n"
1436                         "Context: %s\r\n"
1437                         "Extension: %s\r\n"
1438                         "Priority: %d\r\n"
1439                         "Seconds: %ld\r\n"
1440                         "%s"
1441                         "Uniqueid: %s\r\n"
1442                         "%s"
1443                         "\r\n",
1444                         c->name,
1445                         S_OR(c->cid.cid_num, "<unknown>"),
1446                         S_OR(c->cid.cid_num, "<unknown>"),
1447                         S_OR(c->cid.cid_name, "<unknown>"),
1448                         c->accountcode,
1449                         ast_state2str(c->_state), c->context,
1450                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1451                 } else {
1452                         astman_append(s,
1453                         "Event: Status\r\n"
1454                         "Privilege: Call\r\n"
1455                         "Channel: %s\r\n"
1456                         "CallerID: %s\r\n"              /* This parameter is deprecated and will be removed post-1.4 */
1457                         "CallerIDNum: %s\r\n"
1458                         "CallerIDName: %s\r\n"
1459                         "Account: %s\r\n"
1460                         "State: %s\r\n"
1461                         "%s"
1462                         "Uniqueid: %s\r\n"
1463                         "%s"
1464                         "\r\n",
1465                         c->name,
1466                         S_OR(c->cid.cid_num, "<unknown>"),
1467                         S_OR(c->cid.cid_num, "<unknown>"),
1468                         S_OR(c->cid.cid_name, "<unknown>"),
1469                         c->accountcode,
1470                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1471                 }
1472                 ast_channel_unlock(c);
1473                 if (!all)
1474                         break;
1475                 c = ast_channel_walk_locked(c);
1476         }
1477         astman_append(s,
1478         "Event: StatusComplete\r\n"
1479         "%s"
1480         "\r\n",idText);
1481         return 0;
1482 }
1483
1484 static char mandescr_redirect[] =
1485 "Description: Redirect (transfer) a call.\n"
1486 "Variables: (Names marked with * are required)\n"
1487 "       *Channel: Channel to redirect\n"
1488 "       ExtraChannel: Second call leg to transfer (optional)\n"
1489 "       *Exten: Extension to transfer to\n"
1490 "       *Context: Context to transfer to\n"
1491 "       *Priority: Priority to transfer to\n"
1492 "       ActionID: Optional Action id for message matching.\n";
1493
1494 /*! \brief  action_redirect: The redirect manager command */
1495 static int action_redirect(struct mansession *s, struct message *m)
1496 {
1497         char *name = astman_get_header(m, "Channel");
1498         char *name2 = astman_get_header(m, "ExtraChannel");
1499         char *exten = astman_get_header(m, "Exten");
1500         char *context = astman_get_header(m, "Context");
1501         char *priority = astman_get_header(m, "Priority");
1502         struct ast_channel *chan, *chan2 = NULL;
1503         int pi = 0;
1504         int res;
1505
1506         if (ast_strlen_zero(name)) {
1507                 astman_send_error(s, m, "Channel not specified");
1508                 return 0;
1509         }
1510         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1511                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1512                         astman_send_error(s, m, "Invalid priority\n");
1513                         return 0;
1514                 }
1515         }
1516         /* XXX watch out, possible deadlock!!! */
1517         chan = ast_get_channel_by_name_locked(name);
1518         if (!chan) {
1519                 char buf[BUFSIZ];
1520                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1521                 astman_send_error(s, m, buf);
1522                 return 0;
1523         }
1524         if (!ast_strlen_zero(name2))
1525                 chan2 = ast_get_channel_by_name_locked(name2);
1526         res = ast_async_goto(chan, context, exten, pi);
1527         if (!res) {
1528                 if (!ast_strlen_zero(name2)) {
1529                         if (chan2)
1530                                 res = ast_async_goto(chan2, context, exten, pi);
1531                         else
1532                                 res = -1;
1533                         if (!res)
1534                                 astman_send_ack(s, m, "Dual Redirect successful");
1535                         else
1536                                 astman_send_error(s, m, "Secondary redirect failed");
1537                 } else
1538                         astman_send_ack(s, m, "Redirect successful");
1539         } else
1540                 astman_send_error(s, m, "Redirect failed");
1541         if (chan)
1542                 ast_channel_unlock(chan);
1543         if (chan2)
1544                 ast_channel_unlock(chan2);
1545         return 0;
1546 }
1547
1548 static char mandescr_command[] =
1549 "Description: Run a CLI command.\n"
1550 "Variables: (Names marked with * are required)\n"
1551 "       *Command: Asterisk CLI command to run\n"
1552 "       ActionID: Optional Action id for message matching.\n";
1553
1554 /*! \brief  action_command: Manager command "command" - execute CLI command */
1555 static int action_command(struct mansession *s, struct message *m)
1556 {
1557         char *cmd = astman_get_header(m, "Command");
1558         char *id = astman_get_header(m, "ActionID");
1559         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1560         if (!ast_strlen_zero(id))
1561                 astman_append(s, "ActionID: %s\r\n", id);
1562         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1563         ast_cli_command(s->fd, cmd);
1564         astman_append(s, "--END COMMAND--\r\n\r\n");
1565         return 0;
1566 }
1567
1568 static void *fast_originate(void *data)
1569 {
1570         struct fast_originate_helper *in = data;
1571         int res;
1572         int reason = 0;
1573         struct ast_channel *chan = NULL;
1574
1575         if (!ast_strlen_zero(in->app)) {
1576                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1577                         S_OR(in->cid_num, NULL),
1578                         S_OR(in->cid_name, NULL),
1579                         in->vars, in->account, &chan);
1580         } else {
1581                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1582                         S_OR(in->cid_num, NULL),
1583                         S_OR(in->cid_name, NULL),
1584                         in->vars, in->account, &chan);
1585         }
1586
1587         /* Tell the manager what happened with the channel */
1588         manager_event(EVENT_FLAG_CALL,
1589                 res ? "OriginateFailure" : "OriginateSuccess",
1590                 "%s"
1591                 "Channel: %s/%s\r\n"
1592                 "Context: %s\r\n"
1593                 "Exten: %s\r\n"
1594                 "Reason: %d\r\n"
1595                 "Uniqueid: %s\r\n"
1596                 "CallerID: %s\r\n"              /* This parameter is deprecated and will be removed post-1.4 */
1597                 "CallerIDNum: %s\r\n"
1598                 "CallerIDName: %s\r\n",
1599                 in->idtext, in->tech, in->data, in->context, in->exten, reason,
1600                 chan ? chan->uniqueid : "<null>",
1601                 S_OR(in->cid_num, "<unknown>"),
1602                 S_OR(in->cid_num, "<unknown>"),
1603                 S_OR(in->cid_name, "<unknown>")
1604                 );
1605
1606         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1607         if (chan)
1608                 ast_channel_unlock(chan);
1609         free(in);
1610         return NULL;
1611 }
1612
1613 static char mandescr_originate[] =
1614 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1615 "  Application/Data\n"
1616 "Variables: (Names marked with * are required)\n"
1617 "       *Channel: Channel name to call\n"
1618 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1619 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1620 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1621 "       Application: Application to use\n"
1622 "       Data: Data to use (requires 'Application')\n"
1623 "       Timeout: How long to wait for call to be answered (in ms)\n"
1624 "       CallerID: Caller ID to be set on the outgoing channel\n"
1625 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1626 "       Account: Account code\n"
1627 "       Async: Set to 'true' for fast origination\n";
1628
1629 static int action_originate(struct mansession *s, struct message *m)
1630 {
1631         char *name = astman_get_header(m, "Channel");
1632         char *exten = astman_get_header(m, "Exten");
1633         char *context = astman_get_header(m, "Context");
1634         char *priority = astman_get_header(m, "Priority");
1635         char *timeout = astman_get_header(m, "Timeout");
1636         char *callerid = astman_get_header(m, "CallerID");
1637         char *account = astman_get_header(m, "Account");
1638         char *app = astman_get_header(m, "Application");
1639         char *appdata = astman_get_header(m, "Data");
1640         char *async = astman_get_header(m, "Async");
1641         char *id = astman_get_header(m, "ActionID");
1642         struct ast_variable *vars = astman_get_variables(m);
1643         char *tech, *data;
1644         char *l = NULL, *n = NULL;
1645         int pi = 0;
1646         int res;
1647         int to = 30000;
1648         int reason = 0;
1649         char tmp[256];
1650         char tmp2[256];
1651
1652         pthread_t th;
1653         pthread_attr_t attr;
1654         if (!name) {
1655                 astman_send_error(s, m, "Channel not specified");
1656                 return 0;
1657         }
1658         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1659                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1660                         astman_send_error(s, m, "Invalid priority\n");
1661                         return 0;
1662                 }
1663         }
1664         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1665                 astman_send_error(s, m, "Invalid timeout\n");
1666                 return 0;
1667         }
1668         ast_copy_string(tmp, name, sizeof(tmp));
1669         tech = tmp;
1670         data = strchr(tmp, '/');
1671         if (!data) {
1672                 astman_send_error(s, m, "Invalid channel\n");
1673                 return 0;
1674         }
1675         *data++ = '\0';
1676         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1677         ast_callerid_parse(tmp2, &n, &l);
1678         if (n) {
1679                 if (ast_strlen_zero(n))
1680                         n = NULL;
1681         }
1682         if (l) {
1683                 ast_shrink_phone_number(l);
1684                 if (ast_strlen_zero(l))
1685                         l = NULL;
1686         }
1687         if (ast_true(async)) {
1688                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1689                 if (!fast) {
1690                         res = -1;
1691                 } else {
1692                         if (!ast_strlen_zero(id))
1693                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1694                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1695                         ast_copy_string(fast->data, data, sizeof(fast->data));
1696                         ast_copy_string(fast->app, app, sizeof(fast->app));
1697                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1698                         if (l)
1699                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1700                         if (n)
1701                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1702                         fast->vars = vars;
1703                         ast_copy_string(fast->context, context, sizeof(fast->context));
1704                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1705                         ast_copy_string(fast->account, account, sizeof(fast->account));
1706                         fast->timeout = to;
1707                         fast->priority = pi;
1708                         pthread_attr_init(&attr);
1709                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1710                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1711                                 res = -1;
1712                         } else {
1713                                 res = 0;
1714                         }
1715                 }
1716         } else if (!ast_strlen_zero(app)) {
1717                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1718         } else {
1719                 if (exten && context && pi)
1720                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1721                 else {
1722                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1723                         return 0;
1724                 }
1725         }
1726         if (!res)
1727                 astman_send_ack(s, m, "Originate successfully queued");
1728         else
1729                 astman_send_error(s, m, "Originate failed");
1730         return 0;
1731 }
1732
1733 /*! \brief Help text for manager command mailboxstatus
1734  */
1735 static char mandescr_mailboxstatus[] =
1736 "Description: Checks a voicemail account for status.\n"
1737 "Variables: (Names marked with * are required)\n"
1738 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1739 "       ActionID: Optional ActionID for message matching.\n"
1740 "Returns number of messages.\n"
1741 "       Message: Mailbox Status\n"
1742 "       Mailbox: <mailboxid>\n"
1743 "       Waiting: <count>\n"
1744 "\n";
1745
1746 static int action_mailboxstatus(struct mansession *s, struct message *m)
1747 {
1748         char *mailbox = astman_get_header(m, "Mailbox");
1749         char *id = astman_get_header(m,"ActionID");
1750         char idText[256] = "";
1751         int ret;
1752         if (ast_strlen_zero(mailbox)) {
1753                 astman_send_error(s, m, "Mailbox not specified");
1754                 return 0;
1755         }
1756         if (!ast_strlen_zero(id))
1757                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1758         ret = ast_app_has_voicemail(mailbox, NULL);
1759         astman_append(s, "Response: Success\r\n"
1760                                    "%s"
1761                                    "Message: Mailbox Status\r\n"
1762                                    "Mailbox: %s\r\n"
1763                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1764         return 0;
1765 }
1766
1767 static char mandescr_mailboxcount[] =
1768 "Description: Checks a voicemail account for new messages.\n"
1769 "Variables: (Names marked with * are required)\n"
1770 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1771 "       ActionID: Optional ActionID for message matching.\n"
1772 "Returns number of new and old messages.\n"
1773 "       Message: Mailbox Message Count\n"
1774 "       Mailbox: <mailboxid>\n"
1775 "       NewMessages: <count>\n"
1776 "       OldMessages: <count>\n"
1777 "\n";
1778 static int action_mailboxcount(struct mansession *s, struct message *m)
1779 {
1780         char *mailbox = astman_get_header(m, "Mailbox");
1781         char *id = astman_get_header(m,"ActionID");
1782         char idText[256] = "";
1783         int newmsgs = 0, oldmsgs = 0;
1784         if (ast_strlen_zero(mailbox)) {
1785                 astman_send_error(s, m, "Mailbox not specified");
1786                 return 0;
1787         }
1788         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1789         if (!ast_strlen_zero(id)) {
1790                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1791         }
1792         astman_append(s, "Response: Success\r\n"
1793                                    "%s"
1794                                    "Message: Mailbox Message Count\r\n"
1795                                    "Mailbox: %s\r\n"
1796                                    "NewMessages: %d\r\n"
1797                                    "OldMessages: %d\r\n"
1798                                    "\r\n",
1799                                     idText,mailbox, newmsgs, oldmsgs);
1800         return 0;
1801 }
1802
1803 static char mandescr_extensionstate[] =
1804 "Description: Report the extension state for given extension.\n"
1805 "  If the extension has a hint, will use devicestate to check\n"
1806 "  the status of the device connected to the extension.\n"
1807 "Variables: (Names marked with * are required)\n"
1808 "       *Exten: Extension to check state on\n"
1809 "       *Context: Context for extension\n"
1810 "       ActionId: Optional ID for this transaction\n"
1811 "Will return an \"Extension Status\" message.\n"
1812 "The response will include the hint for the extension and the status.\n";
1813
1814 static int action_extensionstate(struct mansession *s, struct message *m)
1815 {
1816         char *exten = astman_get_header(m, "Exten");
1817         char *context = astman_get_header(m, "Context");
1818         char *id = astman_get_header(m,"ActionID");
1819         char idText[256] = "";
1820         char hint[256] = "";
1821         int status;
1822         if (ast_strlen_zero(exten)) {
1823                 astman_send_error(s, m, "Extension not specified");
1824                 return 0;
1825         }
1826         if (ast_strlen_zero(context))
1827                 context = "default";
1828         status = ast_extension_state(NULL, context, exten);
1829         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1830         if (!ast_strlen_zero(id)) {
1831                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1832         }
1833         astman_append(s, "Response: Success\r\n"
1834                                    "%s"
1835                                    "Message: Extension Status\r\n"
1836                                    "Exten: %s\r\n"
1837                                    "Context: %s\r\n"
1838                                    "Hint: %s\r\n"
1839                                    "Status: %d\r\n\r\n",
1840                                    idText,exten, context, hint, status);
1841         return 0;
1842 }
1843
1844 static char mandescr_timeout[] =
1845 "Description: Hangup a channel after a certain time.\n"
1846 "Variables: (Names marked with * are required)\n"
1847 "       *Channel: Channel name to hangup\n"
1848 "       *Timeout: Maximum duration of the call (sec)\n"
1849 "Acknowledges set time with 'Timeout Set' message\n";
1850
1851 static int action_timeout(struct mansession *s, struct message *m)
1852 {
1853         struct ast_channel *c = NULL;
1854         char *name = astman_get_header(m, "Channel");
1855         int timeout = atoi(astman_get_header(m, "Timeout"));
1856         if (ast_strlen_zero(name)) {
1857                 astman_send_error(s, m, "No channel specified");
1858                 return 0;
1859         }
1860         if (!timeout) {
1861                 astman_send_error(s, m, "No timeout specified");
1862                 return 0;
1863         }
1864         c = ast_get_channel_by_name_locked(name);
1865         if (!c) {
1866                 astman_send_error(s, m, "No such channel");
1867                 return 0;
1868         }
1869         ast_channel_setwhentohangup(c, timeout);
1870         ast_channel_unlock(c);
1871         astman_send_ack(s, m, "Timeout Set");
1872         return 0;
1873 }
1874
1875 static int process_events(struct mansession *s)
1876 {
1877         struct eventqent *eqe;
1878         int ret = 0;
1879         ast_mutex_lock(&s->__lock);
1880         if (s->fd > -1) {
1881                 s->busy--;
1882                 if (!s->eventq)
1883                         s->eventq = master_eventq;
1884                 while(s->eventq->next) {
1885                         eqe = s->eventq->next;
1886                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1887                             ((s->send_events & eqe->category) == eqe->category)) {
1888                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1889                                         ret = -1;
1890                         }
1891                         unuse_eventqent(s->eventq);
1892                         s->eventq = eqe;
1893                 }
1894         }
1895         ast_mutex_unlock(&s->__lock);
1896         return ret;
1897 }
1898
1899 static char mandescr_userevent[] =
1900 "Description: Send an event to manager sessions.\n"
1901 "Variables: (Names marked with * are required)\n"
1902 "       *UserEvent: EventStringToSend\n"
1903 "       Header1: Content1\n"
1904 "       HeaderN: ContentN\n";
1905
1906 static int action_userevent(struct mansession *s, struct message *m)
1907 {
1908         char *event = astman_get_header(m, "UserEvent");
1909         char body[2048] = "";
1910         int x, bodylen = 0;
1911         for (x = 0; x < m->hdrcount; x++) {
1912                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1913                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1914                         bodylen += strlen(m->headers[x]);
1915                         ast_copy_string(body + bodylen, "\r\n", 3);
1916                         bodylen += 2;
1917                 }
1918         }
1919
1920         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1921         return 0;
1922 }
1923
1924 static int process_message(struct mansession *s, struct message *m)
1925 {
1926         char action[80] = "";
1927         struct manager_action *tmp = first_action;
1928         char *id = astman_get_header(m,"ActionID");
1929         char idText[256] = "";
1930         int ret = 0;
1931
1932         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1933         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1934
1935         if (ast_strlen_zero(action)) {
1936                 astman_send_error(s, m, "Missing action in request");
1937                 return 0;
1938         }
1939         if (!ast_strlen_zero(id)) {
1940                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1941         }
1942         if (!s->authenticated) {
1943                 if (!strcasecmp(action, "Challenge")) {
1944                         char *authtype;
1945                         authtype = astman_get_header(m, "AuthType");
1946                         if (!strcasecmp(authtype, "MD5")) {
1947                                 if (ast_strlen_zero(s->challenge))
1948                                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1949                                 ast_mutex_lock(&s->__lock);
1950                                 astman_append(s, "Response: Success\r\n"
1951                                                 "%s"
1952                                                 "Challenge: %s\r\n\r\n",
1953                                                 idText, s->challenge);
1954                                 ast_mutex_unlock(&s->__lock);
1955                                 return 0;
1956                         } else {
1957                                 astman_send_error(s, m, "Must specify AuthType");
1958                                 return 0;
1959                         }
1960                 } else if (!strcasecmp(action, "Login")) {
1961                         if (authenticate(s, m)) {
1962                                 sleep(1);
1963                                 astman_send_error(s, m, "Authentication failed");
1964                                 return -1;
1965                         } else {
1966                                 s->authenticated = 1;
1967                                 if (option_verbose > 1) {
1968                                         if (displayconnects) {
1969                                                 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1970                                         }
1971                                 }
1972                                 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1973                                 astman_send_ack(s, m, "Authentication accepted");
1974                         }
1975                 } else if (!strcasecmp(action, "Logoff")) {
1976                         astman_send_ack(s, m, "See ya");
1977                         return -1;
1978                 } else
1979                         astman_send_error(s, m, "Authentication Required");
1980         } else {
1981                 ast_mutex_lock(&s->__lock);
1982                 s->busy++;
1983                 ast_mutex_unlock(&s->__lock);
1984                 while (tmp) {
1985                         if (!strcasecmp(action, tmp->action)) {
1986                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1987                                         if (tmp->func(s, m))
1988                                                 ret = -1;
1989                                 } else {
1990                                         astman_send_error(s, m, "Permission denied");
1991                                 }
1992                                 break;
1993                         }
1994                         tmp = tmp->next;
1995                 }
1996                 if (!tmp)
1997                         astman_send_error(s, m, "Invalid/unknown command");
1998         }
1999         if (ret)
2000                 return ret;
2001         return process_events(s);
2002 }
2003
2004 static int get_input(struct mansession *s, char *output)
2005 {
2006         /* output must have at least sizeof(s->inbuf) space */
2007         int res;
2008         int x;
2009         struct pollfd fds[1];
2010         for (x = 1; x < s->inlen; x++) {
2011                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
2012                         /* Copy output data up to and including \r\n */
2013                         memcpy(output, s->inbuf, x + 1);
2014                         /* Add trailing \0 */
2015                         output[x+1] = '\0';
2016                         /* Move remaining data back to the front */
2017                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
2018                         s->inlen -= (x + 1);
2019                         return 1;
2020                 }
2021         }
2022         if (s->inlen >= sizeof(s->inbuf) - 1) {
2023                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
2024                 s->inlen = 0;
2025         }
2026         fds[0].fd = s->fd;
2027         fds[0].events = POLLIN;
2028         do {
2029                 ast_mutex_lock(&s->__lock);
2030                 s->waiting_thread = pthread_self();
2031                 ast_mutex_unlock(&s->__lock);
2032
2033                 res = poll(fds, 1, -1);
2034
2035                 ast_mutex_lock(&s->__lock);
2036                 s->waiting_thread = AST_PTHREADT_NULL;
2037                 ast_mutex_unlock(&s->__lock);
2038                 if (res < 0) {
2039                         if (errno == EINTR) {
2040                                 if (s->dead)
2041                                         return -1;
2042                                 return 0;
2043                         }
2044                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2045                         return -1;
2046                 } else if (res > 0) {
2047                         ast_mutex_lock(&s->__lock);
2048                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2049                         ast_mutex_unlock(&s->__lock);
2050                         if (res < 1)
2051                                 return -1;
2052                         break;
2053                 }
2054         } while(1);
2055         s->inlen += res;
2056         s->inbuf[s->inlen] = '\0';
2057         return 0;
2058 }
2059
2060 static void *session_do(void *data)
2061 {
2062         struct mansession *s = data;
2063         struct message m;
2064         int res;
2065
2066         ast_mutex_lock(&s->__lock);
2067         astman_append(s, "Asterisk Call Manager/1.0\r\n");
2068         ast_mutex_unlock(&s->__lock);
2069         memset(&m, 0, sizeof(m));
2070         for (;;) {
2071                 res = get_input(s, m.headers[m.hdrcount]);
2072                 if (res > 0) {
2073                         /* Strip trailing \r\n */
2074                         if (strlen(m.headers[m.hdrcount]) < 2)
2075                                 continue;
2076                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2077                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
2078                                 if (process_message(s, &m))
2079                                         break;
2080                                 memset(&m, 0, sizeof(m));
2081                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2082                                 m.hdrcount++;
2083                 } else if (res < 0) {
2084                         break;
2085                 } else if (s->eventq->next) {
2086                         if (process_events(s))
2087                                 break;
2088                 }
2089         }
2090         if (s->authenticated) {
2091                 if (option_verbose > 1) {
2092                         if (displayconnects)
2093                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2094                 }
2095                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2096         } else {
2097                 if (option_verbose > 1) {
2098                         if (displayconnects)
2099                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2100                 }
2101                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2102         }
2103         destroy_session(s);
2104         return NULL;
2105 }
2106
2107 static void *accept_thread(void *ignore)
2108 {
2109         int as;
2110         struct sockaddr_in sin;
2111         socklen_t sinlen;
2112         struct eventqent *eqe;
2113         struct mansession *s;
2114         struct protoent *p;
2115         int arg = 1;
2116         int flags;
2117         pthread_attr_t attr;
2118         time_t now;
2119         struct pollfd pfds[1];
2120
2121         pthread_attr_init(&attr);
2122         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2123
2124         for (;;) {
2125                 time(&now);
2126                 AST_LIST_LOCK(&sessions);
2127                 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2128                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2129                                 AST_LIST_REMOVE_CURRENT(&sessions, list);
2130                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2131                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2132                                                 s->username, ast_inet_ntoa(s->sin.sin_addr));
2133                                 }
2134                                 free_session(s);
2135                                 break;
2136                         }
2137                 }
2138                 AST_LIST_TRAVERSE_SAFE_END
2139                 /* Purge master event queue of old, unused events, but make sure we
2140                    always keep at least one in the queue */
2141                 eqe = master_eventq;
2142                 while (master_eventq->next && !master_eventq->usecount) {
2143                         eqe = master_eventq;
2144                         master_eventq = master_eventq->next;
2145                         free(eqe);
2146                 }
2147                 AST_LIST_UNLOCK(&sessions);
2148                 if (s)
2149                         ast_atomic_fetchadd_int(&num_sessions, -1);
2150
2151                 sinlen = sizeof(sin);
2152                 pfds[0].fd = asock;
2153                 pfds[0].events = POLLIN;
2154                 /* Wait for something to happen, but timeout every few seconds so
2155                    we can ditch any old manager sessions */
2156                 if (poll(pfds, 1, 5000) < 1)
2157                         continue;
2158                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2159                 if (as < 0) {
2160                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2161                         continue;
2162                 }
2163                 p = getprotobyname("tcp");
2164                 if (p) {
2165                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2166                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2167                         }
2168                 }
2169                 if (!(s = ast_calloc(1, sizeof(*s))))
2170                         continue;
2171
2172                 ast_atomic_fetchadd_int(&num_sessions, 1);
2173
2174                 memcpy(&s->sin, &sin, sizeof(sin));
2175                 s->writetimeout = 100;
2176                 s->waiting_thread = AST_PTHREADT_NULL;
2177
2178                 if (!block_sockets) {
2179                         /* For safety, make sure socket is non-blocking */
2180                         flags = fcntl(as, F_GETFL);
2181                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
2182                 }
2183                 ast_mutex_init(&s->__lock);
2184                 s->fd = as;
2185                 s->send_events = -1;
2186                 AST_LIST_LOCK(&sessions);
2187                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2188                 /* Find the last place in the master event queue and hook ourselves
2189                    in there */
2190                 s->eventq = master_eventq;
2191                 while(s->eventq->next)
2192                         s->eventq = s->eventq->next;
2193                 AST_LIST_UNLOCK(&sessions);
2194                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2195                 if (ast_pthread_create(&s->t, &attr, session_do, s))
2196                         destroy_session(s);
2197         }
2198         pthread_attr_destroy(&attr);
2199         return NULL;
2200 }
2201
2202 static int append_event(const char *str, int category)
2203 {
2204         struct eventqent *tmp, *prev = NULL;
2205         tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2206
2207         if (!tmp)
2208                 return -1;
2209
2210         tmp->next = NULL;
2211         tmp->category = category;
2212         strcpy(tmp->eventdata, str);
2213
2214         if (master_eventq) {
2215                 prev = master_eventq;
2216                 while (prev->next)
2217                         prev = prev->next;
2218                 prev->next = tmp;
2219         } else {
2220                 master_eventq = tmp;
2221         }
2222
2223         tmp->usecount = num_sessions;
2224
2225         return 0;
2226 }
2227
2228 /*! \brief  manager_event: Send AMI event to client */
2229 int manager_event(int category, const char *event, const char *fmt, ...)
2230 {
2231         struct mansession *s;
2232         char auth[80];
2233         va_list ap;
2234         struct timeval now;
2235         struct ast_dynamic_str *buf;
2236
2237         /* Abort if there aren't any manager sessions */
2238         if (!num_sessions)
2239                 return 0;
2240
2241         if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2242                 return -1;
2243
2244         ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2245                         "Event: %s\r\nPrivilege: %s\r\n",
2246                          event, authority_to_str(category, auth, sizeof(auth)));
2247
2248         if (timestampevents) {
2249                 now = ast_tvnow();
2250                 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2251                                 "Timestamp: %ld.%06lu\r\n",
2252                                  now.tv_sec, (unsigned long) now.tv_usec);
2253         }
2254
2255         va_start(ap, fmt);
2256         ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2257         va_end(ap);
2258
2259         ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2260
2261         append_event(buf->str, category);
2262
2263         /* Append even to master list and wake up any sleeping sessions */
2264         AST_LIST_LOCK(&sessions);
2265         AST_LIST_TRAVERSE(&sessions, s, list) {
2266                 ast_mutex_lock(&s->__lock);
2267                 if (s->waiting_thread != AST_PTHREADT_NULL)
2268                         pthread_kill(s->waiting_thread, SIGURG);
2269                 ast_mutex_unlock(&s->__lock);
2270         }
2271         AST_LIST_UNLOCK(&sessions);
2272
2273         return 0;
2274 }
2275
2276 int ast_manager_unregister(char *action)
2277 {
2278         struct manager_action *cur = first_action, *prev = first_action;
2279
2280         ast_mutex_lock(&actionlock);
2281         while (cur) {
2282                 if (!strcasecmp(action, cur->action)) {
2283                         prev->next = cur->next;
2284                         free(cur);
2285                         if (option_verbose > 1)
2286                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2287                         ast_mutex_unlock(&actionlock);
2288                         return 0;
2289                 }
2290                 prev = cur;
2291                 cur = cur->next;
2292         }
2293         ast_mutex_unlock(&actionlock);
2294         return 0;
2295 }
2296
2297 static int manager_state_cb(char *context, char *exten, int state, void *data)
2298 {
2299         /* Notify managers of change */
2300         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2301         return 0;
2302 }
2303
2304 static int ast_manager_register_struct(struct manager_action *act)
2305 {
2306         struct manager_action *cur = first_action, *prev = NULL;
2307         int ret;
2308
2309         ast_mutex_lock(&actionlock);
2310         while (cur) { /* Walk the list of actions */
2311                 ret = strcasecmp(cur->action, act->action);
2312                 if (ret == 0) {
2313                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2314                         ast_mutex_unlock(&actionlock);
2315                         return -1;
2316                 } else if (ret > 0) {
2317                         /* Insert these alphabetically */
2318                         if (prev) {
2319                                 act->next = prev->next;
2320                                 prev->next = act;
2321                         } else {
2322                                 act->next = first_action;
2323                                 first_action = act;
2324                         }
2325                         break;
2326                 }
2327                 prev = cur;
2328                 cur = cur->next;
2329         }
2330
2331         if (!cur) {
2332                 if (prev)
2333                         prev->next = act;
2334                 else
2335                         first_action = act;
2336                 act->next = NULL;
2337         }
2338
2339         if (option_verbose > 1)
2340                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2341         ast_mutex_unlock(&actionlock);
2342         return 0;
2343 }
2344
2345 /*! \brief register a new command with manager, including online help. This is
2346         the preferred way to register a manager command */
2347 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2348 {
2349         struct manager_action *cur;
2350
2351         cur = ast_malloc(sizeof(*cur));
2352         if (!cur)
2353                 return -1;
2354
2355         cur->action = action;
2356         cur->authority = auth;
2357         cur->func = func;
2358         cur->synopsis = synopsis;
2359         cur->description = description;
2360         cur->next = NULL;
2361
2362         ast_manager_register_struct(cur);
2363
2364         return 0;
2365 }
2366 /*! @}
2367  END Doxygen group */
2368
2369 static struct mansession *find_session(unsigned long ident)
2370 {
2371         struct mansession *s;
2372
2373         AST_LIST_LOCK(&sessions);
2374         AST_LIST_TRAVERSE(&sessions, s, list) {
2375                 ast_mutex_lock(&s->__lock);
2376                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2377                         s->inuse++;
2378                         break;
2379                 }
2380                 ast_mutex_unlock(&s->__lock);
2381         }
2382         AST_LIST_UNLOCK(&sessions);
2383
2384         return s;
2385 }
2386
2387
2388 static void vars2msg(struct message *m, struct ast_variable *vars)
2389 {
2390         int x;
2391         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2392                 if (!vars)
2393                         break;
2394                 m->hdrcount = x + 1;
2395                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2396         }
2397 }
2398
2399 enum {
2400         FORMAT_RAW,
2401         FORMAT_HTML,
2402         FORMAT_XML,
2403 };
2404 static char *contenttype[] = { "plain", "html", "xml" };
2405
2406 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2407 {
2408         struct mansession *s = NULL;
2409         unsigned long ident = 0;
2410         char workspace[512];
2411         char cookie[128];
2412         size_t len = sizeof(workspace);
2413         int blastaway = 0;
2414         char *c = workspace;
2415         char *retval = NULL;
2416         struct message m;
2417         struct ast_variable *v;
2418
2419         for (v = params; v; v = v->next) {
2420                 if (!strcasecmp(v->name, "mansession_id")) {
2421                         sscanf(v->value, "%lx", &ident);
2422                         break;
2423                 }
2424         }
2425
2426         if (!(s = find_session(ident))) {
2427                 /* Create new session */
2428                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2429                         *status = 500;
2430                         goto generic_callback_out;
2431                 }
2432                 memcpy(&s->sin, requestor, sizeof(s->sin));
2433                 s->fd = -1;
2434                 s->waiting_thread = AST_PTHREADT_NULL;
2435                 s->send_events = 0;
2436                 ast_mutex_init(&s->__lock);
2437                 ast_mutex_lock(&s->__lock);
2438                 s->inuse = 1;
2439                 s->managerid = rand() | (unsigned long)s;
2440                 AST_LIST_LOCK(&sessions);
2441                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2442                 /* Hook into the last spot in the event queue */
2443                 s->eventq = master_eventq;
2444                 while (s->eventq->next)
2445                         s->eventq = s->eventq->next;
2446                 AST_LIST_UNLOCK(&sessions);
2447                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2448                 ast_atomic_fetchadd_int(&num_sessions, 1);
2449         }
2450
2451         /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
2452         time(&s->sessiontimeout);
2453         if (!s->authenticated && (httptimeout > 5))
2454                 s->sessiontimeout += 5;
2455         else
2456                 s->sessiontimeout += httptimeout;
2457         ast_mutex_unlock(&s->__lock);
2458
2459         memset(&m, 0, sizeof(m));
2460         if (s) {
2461                 char tmp[80];
2462                 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2463                 sprintf(tmp, "%08lx", s->managerid);
2464                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2465                 if (format == FORMAT_HTML)
2466                         ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2467                 vars2msg(&m, params);
2468                 if (format == FORMAT_XML) {
2469                         ast_build_string(&c, &len, "<ajax-response>\n");
2470                 } else if (format == FORMAT_HTML) {
2471                         ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2472                         ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2473                 }
2474                 if (process_message(s, &m)) {
2475                         if (s->authenticated) {
2476                                 if (option_verbose > 1) {
2477                                         if (displayconnects)
2478                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2479                                 }
2480                                 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2481                         } else {
2482                                 if (option_verbose > 1) {
2483                                         if (displayconnects)
2484                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2485                                 }
2486                                 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2487                         }
2488                         s->needdestroy = 1;
2489                 }
2490                 if (s->outputstr) {
2491                         char *tmp;
2492                         if (format == FORMAT_XML)
2493                                 tmp = xml_translate(s->outputstr->str, params);
2494                         else if (format == FORMAT_HTML)
2495                                 tmp = html_translate(s->outputstr->str);
2496                         else
2497                                 tmp = s->outputstr->str;
2498                         if (tmp) {
2499                                 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2500                                 if (retval) {
2501                                         strcpy(retval, workspace);
2502                                         strcpy(retval + strlen(retval), tmp);
2503                                         c = retval + strlen(retval);
2504                                         len = 120;
2505                                 }
2506                         }
2507                         if (tmp != s->outputstr->str)
2508                                 free(tmp);
2509                         free(s->outputstr);
2510                         s->outputstr = NULL;
2511                 }
2512                 /* Still okay because c would safely be pointing to workspace even
2513                    if retval failed to allocate above */
2514                 if (format == FORMAT_XML) {
2515                         ast_build_string(&c, &len, "</ajax-response>\n");
2516                 } else if (format == FORMAT_HTML)
2517                         ast_build_string(&c, &len, "</table></body>\r\n");
2518         } else {
2519                 *status = 500;
2520                 *title = strdup("Server Error");
2521         }
2522         ast_mutex_lock(&s->__lock);
2523         if (s->needdestroy) {
2524                 if (s->inuse == 1) {
2525                         ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2526                         blastaway = 1;
2527                 } else {
2528                         ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2529                         if (s->waiting_thread != AST_PTHREADT_NULL)
2530                                 pthread_kill(s->waiting_thread, SIGURG);
2531                         s->inuse--;
2532                 }
2533         } else
2534                 s->inuse--;
2535         ast_mutex_unlock(&s->__lock);
2536
2537         if (blastaway)
2538                 destroy_session(s);
2539 generic_callback_out:
2540         if (*status != 200)
2541                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2542         return retval;
2543 }
2544
2545 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2546 {
2547         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2548 }
2549
2550 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2551 {
2552         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2553 }
2554
2555 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2556 {
2557         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2558 }
2559
2560 struct ast_http_uri rawmanuri = {
2561         .description = "Raw HTTP Manager Event Interface",
2562         .uri = "rawman",
2563         .has_subtree = 0,
2564         .callback = rawman_http_callback,
2565 };
2566
2567 struct ast_http_uri manageruri = {
2568         .description = "HTML Manager Event Interface",
2569         .uri = "manager",
2570         .has_subtree = 0,
2571         .callback = manager_http_callback,
2572 };
2573
2574 struct ast_http_uri managerxmluri = {
2575         .description = "XML Manager Event Interface",
2576         .uri = "mxml",
2577         .has_subtree = 0,
2578         .callback = mxml_http_callback,
2579 };
2580
2581 static int registered = 0;
2582 static int webregged = 0;
2583
2584 int init_manager(void)
2585 {
2586         struct ast_config *cfg;
2587         char *val,*cat;
2588         int oldportno = portno;
2589         static struct sockaddr_in ba;
2590         int x = 1;
2591         int flags;
2592         int webenabled = 0;
2593         int newhttptimeout = 60;
2594         amus = NULL;
2595         if (!registered) {
2596                 /* Register default actions */
2597                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2598                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2599                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2600                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2601                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2602                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2603                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2604                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2605                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2606                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2607                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2608                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2609                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2610                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2611                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2612                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2613                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2614                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2615                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2616
2617                 ast_cli_register(&show_mancmd_cli);
2618                 ast_cli_register(&show_mancmds_cli);
2619                 ast_cli_register(&show_manconn_cli);
2620                 ast_cli_register(&show_maneventq_cli);
2621                 ast_cli_register(&show_managers_cli);
2622                 ast_cli_register(&show_manager_cli);
2623
2624                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2625                 registered = 1;
2626                 /* Append placeholder event so master_eventq never runs dry */
2627                 append_event("Event: Placeholder\r\n\r\n", 0);
2628         }
2629         portno = DEFAULT_MANAGER_PORT;
2630         displayconnects = 1;
2631         cfg = ast_config_load("manager.conf");
2632         if (!cfg) {
2633                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2634                 return 0;
2635         }
2636         val = ast_variable_retrieve(cfg, "general", "enabled");
2637         if (val)
2638                 enabled = ast_true(val);
2639
2640         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2641         if (val)
2642                 block_sockets = ast_true(val);
2643
2644         val = ast_variable_retrieve(cfg, "general", "webenabled");
2645         if (val)
2646                 webenabled = ast_true(val);
2647
2648         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2649                 if (sscanf(val, "%d", &portno) != 1) {
2650                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2651                         portno = DEFAULT_MANAGER_PORT;
2652                 }
2653         }
2654
2655         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2656                 displayconnects = ast_true(val);
2657
2658         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2659                 timestampevents = ast_true(val);
2660
2661         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2662                 newhttptimeout = atoi(val);
2663
2664         memset(&ba, 0, sizeof(ba));
2665         ba.sin_family = AF_INET;
2666         ba.sin_port = htons(portno);
2667
2668         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2669                 if (!inet_aton(val, &ba.sin_addr)) {
2670                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2671                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2672                 }
2673         }
2674
2675
2676         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2677 #if 0
2678                 /* Can't be done yet */
2679                 close(asock);
2680                 asock = -1;
2681 #else
2682                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2683 #endif
2684         }
2685
2686
2687
2688                 cat = ast_category_browse(cfg, NULL);
2689                 amus=NULL; /* Resetting the boss */
2690                 while(cat) {
2691                         if (!strcasecmp(cat, "general")) {
2692                                 ast_log(LOG_NOTICE, "ignoring the cat general\n");
2693                                 cat = ast_category_browse(cfg, cat);
2694                                 continue;
2695                         }
2696
2697                         struct ast_manager_user *amu = malloc(sizeof(struct ast_manager_user));
2698                         memset(amu, 0, sizeof(struct ast_manager_user));
2699                         struct ast_variable *var;
2700                         var = ast_variable_browse(cfg, cat);
2701
2702                         while (var) {
2703                                 if (!strcasecmp(var->name, "secret")) {
2704                                         if (amu->secret)
2705                                                 free(amu->secret);
2706                                         amu->secret=strdup(var->value);
2707                                 } else if (!strcasecmp(var->name, "deny") ) {
2708                                         if (amu->deny)
2709                                                 free(amu->deny);
2710                                         amu->deny=strdup(var->value);
2711                                 } else if (!strcasecmp(var->name, "permit") ) {
2712                                         if (amu->permit)
2713                                                 free(amu->permit);
2714                                         amu->permit=strdup(var->value);
2715                                 }  else if (!strcasecmp(var->name, "read") ) {
2716                                         if (amu->read)
2717                                                 free(amu->read);
2718                                         amu->read=strdup(var->value);
2719                                 }  else if (!strcasecmp(var->name, "write") ) {
2720                                         if (amu->write)
2721                                                 free(amu->write);
2722                                         amu->write=strdup(var->value);
2723                                 }  else if (!strcasecmp(var->name, "displayconnects") ) {
2724                                         amu->displayconnects=ast_true(var->value);
2725                                 } else {
2726                                         ast_log(LOG_DEBUG, "%s is unknown.\n",var->name);
2727                                 }
2728                                 var = var->next;
2729                         }
2730
2731                         ast_copy_string(amu->username,cat,sizeof(amu->username));
2732
2733                         ast_log(LOG_DEBUG, "Adding %s\n",amu->username);
2734                         ast_manager_user_add(amu);
2735                         amu=NULL;
2736                         cat = ast_category_browse(cfg, cat);
2737                 }
2738
2739         ast_config_destroy(cfg);
2740
2741         if (webenabled && enabled) {
2742                 if (!webregged) {
2743                         ast_http_uri_link(&rawmanuri);
2744                         ast_http_uri_link(&manageruri);
2745                         ast_http_uri_link(&managerxmluri);
2746                         webregged = 1;
2747                 }
2748         } else {
2749                 if (webregged) {
2750                         ast_http_uri_unlink(&rawmanuri);
2751                         ast_http_uri_unlink(&manageruri);
2752                         ast_http_uri_unlink(&managerxmluri);
2753                         webregged = 0;
2754                 }
2755         }
2756
2757         if (newhttptimeout > 0)
2758                 httptimeout = newhttptimeout;
2759
2760         /* If not enabled, do nothing */
2761         if (!enabled)
2762                 return 0;
2763
2764         if (asock < 0) {
2765                 asock = socket(AF_INET, SOCK_STREAM, 0);
2766                 if (asock < 0) {
2767                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2768                         return -1;
2769                 }
2770                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2771                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2772                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2773                         close(asock);
2774                         asock = -1;
2775                         return -1;
2776                 }
2777                 if (listen(asock, 2)) {
2778                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2779                         close(asock);
2780                         asock = -1;
2781                         return -1;
2782                 }
2783                 flags = fcntl(asock, F_GETFL);
2784                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2785                 if (option_verbose)
2786                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2787                 ast_pthread_create(&t, NULL, accept_thread, NULL);
2788         }
2789         return 0;
2790 }
2791
2792 int reload_manager(void)
2793 {
2794         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2795         return init_manager();
2796 }
2797