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