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