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