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