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