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