basically fix indentation of a large function after previous
[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         char *user = astman_get_header(m, "Username");
855         char *pass = astman_get_header(m, "Secret");
856         char *authtype = astman_get_header(m, "AuthType");
857         char *key = astman_get_header(m, "Key");
858         char *events = astman_get_header(m, "Events");
859         char *cat = NULL;
860         struct ast_config *cfg = ast_config_load("manager.conf");
861         int ret = -1;   /* default: error return */
862
863         if (!cfg)
864                 return -1;
865         while ( (cat = ast_category_browse(cfg, cat)) ) {
866                 struct ast_variable *v;
867                 struct ast_ha *ha = NULL;
868                 char *password = NULL;
869
870                 if (!strcasecmp(cat, "general") || strcasecmp(cat, user))
871                         continue;       /* skip 'general' and non-matching sections */
872
873                 /* collect parameters for the user's entry */
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) {
899                         if (!ast_apply_ha(ha, &(s->sin))) {
900                                 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
901                                 ast_free_ha(ha);
902                                 goto error;
903                         }
904                         ast_free_ha(ha);
905                 }
906                 if (!strcasecmp(authtype, "MD5")) {
907                         if (!ast_strlen_zero(key) && s->challenge) {
908                                 int x;
909                                 int len = 0;
910                                 char md5key[256] = "";
911                                 struct MD5Context md5;
912                                 unsigned char digest[16];
913                                 MD5Init(&md5);
914                                 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
915                                 MD5Update(&md5, (unsigned char *) password, strlen(password));
916                                 MD5Final(digest, &md5);
917                                 for (x=0; x<16; x++)
918                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
919                                 if (!strcmp(md5key, key))
920                                         break;
921                         }
922                 } else if (password) {
923                         if (!strcmp(password, pass))
924                                 break;
925                 }
926                 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
927                 goto error;
928         }
929         /* we get here with user not found (cat = NULL) or successful authentication */
930         if (cat) {
931                 ast_copy_string(s->username, cat, sizeof(s->username));
932                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
933                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
934                 if (events)
935                         set_eventmask(s, events);
936                 ret = 0;
937         } else {
938                 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
939         }
940 error:
941         ast_config_destroy(cfg);
942         return ret;
943 }
944
945 /*! \brief Manager PING */
946 static char mandescr_ping[] = 
947 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
948 "  manager connection open.\n"
949 "Variables: NONE\n";
950
951 static int action_ping(struct mansession *s, struct message *m)
952 {
953         astman_send_response(s, m, "Pong", NULL);
954         return 0;
955 }
956
957 static char mandescr_getconfig[] =
958 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
959 "file by category and contents.\n"
960 "Variables:\n"
961 "   Filename: Configuration filename (e.g. foo.conf)\n";
962
963 static int action_getconfig(struct mansession *s, struct message *m)
964 {
965         struct ast_config *cfg;
966         char *fn = astman_get_header(m, "Filename");
967         int catcount = 0;
968         int lineno = 0;
969         char *category=NULL;
970         struct ast_variable *v;
971         char idText[256] = "";
972         char *id = astman_get_header(m, "ActionID");
973
974         if (!ast_strlen_zero(id))
975                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
976
977         if (ast_strlen_zero(fn)) {
978                 astman_send_error(s, m, "Filename not specified");
979                 return 0;
980         }
981         if (!(cfg = ast_config_load(fn))) {
982                 astman_send_error(s, m, "Config file not found");
983                 return 0;
984         }
985         astman_append(s, "Response: Success\r\n%s", idText);
986         while ((category = ast_category_browse(cfg, category))) {
987                 lineno = 0;
988                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
989                 for (v = ast_variable_browse(cfg, category); v; v = v->next)
990                         astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
991                 catcount++;
992         }
993         ast_config_destroy(cfg);
994         astman_append(s, "\r\n");
995
996         return 0;
997 }
998
999
1000 static void handle_updates(struct mansession *s, struct message *m, struct ast_config *cfg)
1001 {
1002         int x;
1003         char hdr[40];
1004         char *action, *cat, *var, *value, *match;
1005         struct ast_category *category;
1006         struct ast_variable *v;
1007         
1008         for (x=0;x<100000;x++) {
1009                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1010                 action = astman_get_header(m, hdr);
1011                 if (ast_strlen_zero(action))
1012                         break;
1013                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1014                 cat = astman_get_header(m, hdr);
1015                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1016                 var = astman_get_header(m, hdr);
1017                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1018                 value = astman_get_header(m, hdr);
1019                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1020                 match = astman_get_header(m, hdr);
1021                 if (!strcasecmp(action, "newcat")) {
1022                         if (!ast_strlen_zero(cat)) {
1023                                 category = ast_category_new(cat);
1024                                 if (category) {
1025                                         ast_category_append(cfg, category);
1026                                 }
1027                         }
1028                 } else if (!strcasecmp(action, "renamecat")) {
1029                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1030                                 category = ast_category_get(cfg, cat);
1031                                 if (category) 
1032                                         ast_category_rename(category, value);
1033                         }
1034                 } else if (!strcasecmp(action, "delcat")) {
1035                         if (!ast_strlen_zero(cat))
1036                                 ast_category_delete(cfg, cat);
1037                 } else if (!strcasecmp(action, "update")) {
1038                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1039                                 ast_variable_update(category, var, value, match);
1040                 } else if (!strcasecmp(action, "delete")) {
1041                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1042                                 ast_variable_delete(category, var, match);
1043                 } else if (!strcasecmp(action, "append")) {
1044                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && 
1045                                 (category = ast_category_get(cfg, cat)) && 
1046                                 (v = ast_variable_new(var, value))){
1047                                 if (match && !strcasecmp(match, "object"))
1048                                         v->object = 1;
1049                                 ast_variable_append(category, v);
1050                         }
1051                 }
1052         }
1053 }
1054
1055 static char mandescr_updateconfig[] =
1056 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1057 "file by category and contents.\n"
1058 "Variables (X's represent 6 digit number beginning with 000000):\n"
1059 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1060 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1061 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1062 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1063 "   Cat-XXXXXX:    Category to operate on\n"
1064 "   Var-XXXXXX:    Variable to work on\n"
1065 "   Value-XXXXXX:  Value to work on\n"
1066 "   Match-XXXXXX:  Extra match required to match line\n";
1067
1068 static int action_updateconfig(struct mansession *s, struct message *m)
1069 {
1070         struct ast_config *cfg;
1071         char *sfn = astman_get_header(m, "SrcFilename");
1072         char *dfn = astman_get_header(m, "DstFilename");
1073         int res;
1074         char idText[256] = "";
1075         char *id = astman_get_header(m, "ActionID");
1076         char *rld = astman_get_header(m, "Reload");
1077
1078         if (!ast_strlen_zero(id))
1079                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1080
1081         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1082                 astman_send_error(s, m, "Filename not specified");
1083                 return 0;
1084         }
1085         if (!(cfg = ast_config_load(sfn))) {
1086                 astman_send_error(s, m, "Config file not found");
1087                 return 0;
1088         }
1089         handle_updates(s, m, cfg);
1090         res = config_text_file_save(dfn, cfg, "Manager");
1091         ast_config_destroy(cfg);
1092         if (res) {
1093                 astman_send_error(s, m, "Save of config failed");
1094                 return 0;
1095         }
1096         astman_append(s, "Response: Success\r\n%s\r\n", idText);
1097         if (!ast_strlen_zero(rld)) {
1098                 if (ast_true(rld))
1099                         rld = NULL;
1100                 ast_module_reload(rld); 
1101         }
1102         return 0;
1103 }
1104
1105 /*! \brief Manager WAITEVENT */
1106 static char mandescr_waitevent[] = 
1107 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1108 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1109 "session, events will be generated and queued.\n"
1110 "Variables: \n"
1111 "   Timeout: Maximum time to wait for events\n";
1112
1113 static int action_waitevent(struct mansession *s, struct message *m)
1114 {
1115         char *timeouts = astman_get_header(m, "Timeout");
1116         int timeout = -1, max;
1117         int x;
1118         int needexit = 0;
1119         time_t now;
1120         struct eventqent *eqe;
1121         char *id = astman_get_header(m,"ActionID");
1122         char idText[256] = "";
1123
1124         if (!ast_strlen_zero(id))
1125                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1126
1127         if (!ast_strlen_zero(timeouts)) {
1128                 sscanf(timeouts, "%i", &timeout);
1129         }
1130         
1131         ast_mutex_lock(&s->__lock);
1132         if (s->waiting_thread != AST_PTHREADT_NULL) {
1133                 pthread_kill(s->waiting_thread, SIGURG);
1134         }
1135         if (s->sessiontimeout) {
1136                 time(&now);
1137                 max = s->sessiontimeout - now - 10;
1138                 if (max < 0)
1139                         max = 0;
1140                 if ((timeout < 0) || (timeout > max))
1141                         timeout = max;
1142                 if (!s->send_events)
1143                         s->send_events = -1;
1144                 /* Once waitevent is called, always queue events from now on */
1145                 if (s->busy == 1)
1146                         s->busy = 2;
1147         }
1148         ast_mutex_unlock(&s->__lock);
1149         s->waiting_thread = pthread_self();
1150         if (option_debug)
1151                 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1152         for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1153                 ast_mutex_lock(&s->__lock);
1154                 if (s->eventq && s->eventq->next)
1155                         needexit = 1;
1156                 if (s->waiting_thread != pthread_self())
1157                         needexit = 1;
1158                 if (s->needdestroy)
1159                         needexit = 1;
1160                 ast_mutex_unlock(&s->__lock);
1161                 if (needexit)
1162                         break;
1163                 if (s->fd > 0) {
1164                         if (ast_wait_for_input(s->fd, 1000))
1165                                 break;
1166                 } else {
1167                         sleep(1);
1168                 }
1169         }
1170         if (option_debug)
1171                 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1172         ast_mutex_lock(&s->__lock);
1173         if (s->waiting_thread == pthread_self()) {
1174                 astman_send_response(s, m, "Success", "Waiting for Event...");
1175                 /* Only show events if we're the most recent waiter */
1176                 while(s->eventq->next) {
1177                         eqe = s->eventq->next;
1178                         if (((s->readperm & eqe->category) == eqe->category) &&
1179                             ((s->send_events & eqe->category) == eqe->category)) {
1180                                 astman_append(s, "%s", eqe->eventdata);
1181                         }
1182                         unuse_eventqent(s->eventq);
1183                         s->eventq = eqe;
1184                 }
1185                 astman_append(s,
1186                         "Event: WaitEventComplete\r\n"
1187                         "%s"
1188                         "\r\n", idText);
1189                 s->waiting_thread = AST_PTHREADT_NULL;
1190         } else {
1191                 if (option_debug)
1192                         ast_log(LOG_DEBUG, "Abandoning event request!\n");
1193         }
1194         ast_mutex_unlock(&s->__lock);
1195         return 0;
1196 }
1197
1198 static char mandescr_listcommands[] = 
1199 "Description: Returns the action name and synopsis for every\n"
1200 "  action that is available to the user\n"
1201 "Variables: NONE\n";
1202
1203 static int action_listcommands(struct mansession *s, struct message *m)
1204 {
1205         struct manager_action *cur = first_action;
1206         char idText[256] = "";
1207         char temp[BUFSIZ];
1208         char *id = astman_get_header(m,"ActionID");
1209
1210         if (!ast_strlen_zero(id))
1211                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1212         astman_append(s, "Response: Success\r\n%s", idText);
1213         ast_mutex_lock(&actionlock);
1214         while (cur) { /* Walk the list of actions */
1215                 if ((s->writeperm & cur->authority) == cur->authority)
1216                         astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1217                 cur = cur->next;
1218         }
1219         ast_mutex_unlock(&actionlock);
1220         astman_append(s, "\r\n");
1221
1222         return 0;
1223 }
1224
1225 static char mandescr_events[] = 
1226 "Description: Enable/Disable sending of events to this manager\n"
1227 "  client.\n"
1228 "Variables:\n"
1229 "       EventMask: 'on' if all events should be sent,\n"
1230 "               'off' if no events should be sent,\n"
1231 "               'system,call,log' to select which flags events should have to be sent.\n";
1232
1233 static int action_events(struct mansession *s, struct message *m)
1234 {
1235         char *mask = astman_get_header(m, "EventMask");
1236         int res;
1237
1238         res = set_eventmask(s, mask);
1239         if (res > 0)
1240                 astman_send_response(s, m, "Events On", NULL);
1241         else if (res == 0)
1242                 astman_send_response(s, m, "Events Off", NULL);
1243
1244         return 0;
1245 }
1246
1247 static char mandescr_logoff[] = 
1248 "Description: Logoff this manager session\n"
1249 "Variables: NONE\n";
1250
1251 static int action_logoff(struct mansession *s, struct message *m)
1252 {
1253         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1254         return -1;
1255 }
1256
1257 static char mandescr_hangup[] = 
1258 "Description: Hangup a channel\n"
1259 "Variables: \n"
1260 "       Channel: The channel name to be hungup\n";
1261
1262 static int action_hangup(struct mansession *s, struct message *m)
1263 {
1264         struct ast_channel *c = NULL;
1265         char *name = astman_get_header(m, "Channel");
1266         if (ast_strlen_zero(name)) {
1267                 astman_send_error(s, m, "No channel specified");
1268                 return 0;
1269         }
1270         c = ast_get_channel_by_name_locked(name);
1271         if (!c) {
1272                 astman_send_error(s, m, "No such channel");
1273                 return 0;
1274         }
1275         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1276         ast_channel_unlock(c);
1277         astman_send_ack(s, m, "Channel Hungup");
1278         return 0;
1279 }
1280
1281 static char mandescr_setvar[] = 
1282 "Description: Set a global or local channel variable.\n"
1283 "Variables: (Names marked with * are required)\n"
1284 "       Channel: Channel to set variable for\n"
1285 "       *Variable: Variable name\n"
1286 "       *Value: Value\n";
1287
1288 static int action_setvar(struct mansession *s, struct message *m)
1289 {
1290         struct ast_channel *c = NULL;
1291         char *name = astman_get_header(m, "Channel");
1292         char *varname = astman_get_header(m, "Variable");
1293         char *varval = astman_get_header(m, "Value");
1294         
1295         if (ast_strlen_zero(varname)) {
1296                 astman_send_error(s, m, "No variable specified");
1297                 return 0;
1298         }
1299         
1300         if (ast_strlen_zero(varval)) {
1301                 astman_send_error(s, m, "No value specified");
1302                 return 0;
1303         }
1304
1305         if (!ast_strlen_zero(name)) {
1306                 c = ast_get_channel_by_name_locked(name);
1307                 if (!c) {
1308                         astman_send_error(s, m, "No such channel");
1309                         return 0;
1310                 }
1311         }
1312         
1313         pbx_builtin_setvar_helper(c, varname, varval);
1314           
1315         if (c)
1316                 ast_channel_unlock(c);
1317
1318         astman_send_ack(s, m, "Variable Set");  
1319
1320         return 0;
1321 }
1322
1323 static char mandescr_getvar[] = 
1324 "Description: Get the value of a global or local channel variable.\n"
1325 "Variables: (Names marked with * are required)\n"
1326 "       Channel: Channel to read variable from\n"
1327 "       *Variable: Variable name\n"
1328 "       ActionID: Optional Action id for message matching.\n";
1329
1330 static int action_getvar(struct mansession *s, struct message *m)
1331 {
1332         struct ast_channel *c = NULL;
1333         char *name = astman_get_header(m, "Channel");
1334         char *varname = astman_get_header(m, "Variable");
1335         char *id = astman_get_header(m,"ActionID");
1336         char *varval;
1337         char workspace[1024];
1338
1339         if (ast_strlen_zero(varname)) {
1340                 astman_send_error(s, m, "No variable specified");
1341                 return 0;
1342         }
1343
1344         if (!ast_strlen_zero(name)) {
1345                 c = ast_get_channel_by_name_locked(name);
1346                 if (!c) {
1347                         astman_send_error(s, m, "No such channel");
1348                         return 0;
1349                 }
1350         }
1351
1352         if (varname[strlen(varname) - 1] == ')') {
1353                 ast_func_read(c, varname, workspace, sizeof(workspace));
1354         } else {
1355                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1356         }
1357
1358         if (c)
1359                 ast_channel_unlock(c);
1360         astman_append(s, "Response: Success\r\n"
1361                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1362         if (!ast_strlen_zero(id))
1363                 astman_append(s, "ActionID: %s\r\n",id);
1364         astman_append(s, "\r\n");
1365
1366         return 0;
1367 }
1368
1369
1370 /*! \brief Manager "status" command to show channels */
1371 /* Needs documentation... */
1372 static int action_status(struct mansession *s, struct message *m)
1373 {
1374         char *id = astman_get_header(m,"ActionID");
1375         char *name = astman_get_header(m,"Channel");
1376         char idText[256] = "";
1377         struct ast_channel *c;
1378         char bridge[256];
1379         struct timeval now = ast_tvnow();
1380         long elapsed_seconds = 0;
1381         int all = ast_strlen_zero(name); /* set if we want all channels */
1382
1383         astman_send_ack(s, m, "Channel status will follow");
1384         if (!ast_strlen_zero(id))
1385                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1386         if (all)
1387                 c = ast_channel_walk_locked(NULL);
1388         else {
1389                 c = ast_get_channel_by_name_locked(name);
1390                 if (!c) {
1391                         astman_send_error(s, m, "No such channel");
1392                         return 0;
1393                 }
1394         }
1395         /* if we look by name, we break after the first iteration */
1396         while (c) {
1397                 if (c->_bridge)
1398                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1399                 else
1400                         bridge[0] = '\0';
1401                 if (c->pbx) {
1402                         if (c->cdr) {
1403                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1404                         }
1405                         astman_append(s,
1406                         "Event: Status\r\n"
1407                         "Privilege: Call\r\n"
1408                         "Channel: %s\r\n"
1409                         "CallerIDNum: %s\r\n"
1410                         "CallerIDName: %s\r\n"
1411                         "Account: %s\r\n"
1412                         "State: %s\r\n"
1413                         "Context: %s\r\n"
1414                         "Extension: %s\r\n"
1415                         "Priority: %d\r\n"
1416                         "Seconds: %ld\r\n"
1417                         "%s"
1418                         "Uniqueid: %s\r\n"
1419                         "%s"
1420                         "\r\n",
1421                         c->name, 
1422                         S_OR(c->cid.cid_num, "<unknown>"), 
1423                         S_OR(c->cid.cid_name, "<unknown>"), 
1424                         c->accountcode,
1425                         ast_state2str(c->_state), c->context,
1426                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1427                 } else {
1428                         astman_append(s,
1429                         "Event: Status\r\n"
1430                         "Privilege: Call\r\n"
1431                         "Channel: %s\r\n"
1432                         "CallerIDNum: %s\r\n"
1433                         "CallerIDName: %s\r\n"
1434                         "Account: %s\r\n"
1435                         "State: %s\r\n"
1436                         "%s"
1437                         "Uniqueid: %s\r\n"
1438                         "%s"
1439                         "\r\n",
1440                         c->name, 
1441                         S_OR(c->cid.cid_num, "<unknown>"), 
1442                         S_OR(c->cid.cid_name, "<unknown>"), 
1443                         c->accountcode,
1444                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1445                 }
1446                 ast_channel_unlock(c);
1447                 if (!all)
1448                         break;
1449                 c = ast_channel_walk_locked(c);
1450         }
1451         astman_append(s,
1452         "Event: StatusComplete\r\n"
1453         "%s"
1454         "\r\n",idText);
1455         return 0;
1456 }
1457
1458 static char mandescr_redirect[] = 
1459 "Description: Redirect (transfer) a call.\n"
1460 "Variables: (Names marked with * are required)\n"
1461 "       *Channel: Channel to redirect\n"
1462 "       ExtraChannel: Second call leg to transfer (optional)\n"
1463 "       *Exten: Extension to transfer to\n"
1464 "       *Context: Context to transfer to\n"
1465 "       *Priority: Priority to transfer to\n"
1466 "       ActionID: Optional Action id for message matching.\n";
1467
1468 /*! \brief  action_redirect: The redirect manager command */
1469 static int action_redirect(struct mansession *s, struct message *m)
1470 {
1471         char *name = astman_get_header(m, "Channel");
1472         char *name2 = astman_get_header(m, "ExtraChannel");
1473         char *exten = astman_get_header(m, "Exten");
1474         char *context = astman_get_header(m, "Context");
1475         char *priority = astman_get_header(m, "Priority");
1476         struct ast_channel *chan, *chan2 = NULL;
1477         int pi = 0;
1478         int res;
1479
1480         if (ast_strlen_zero(name)) {
1481                 astman_send_error(s, m, "Channel not specified");
1482                 return 0;
1483         }
1484         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1485                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1486                         astman_send_error(s, m, "Invalid priority\n");
1487                         return 0;
1488                 }
1489         }
1490         /* XXX watch out, possible deadlock!!! */
1491         chan = ast_get_channel_by_name_locked(name);
1492         if (!chan) {
1493                 char buf[BUFSIZ];
1494                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1495                 astman_send_error(s, m, buf);
1496                 return 0;
1497         }
1498         if (!ast_strlen_zero(name2))
1499                 chan2 = ast_get_channel_by_name_locked(name2);
1500         res = ast_async_goto(chan, context, exten, pi);
1501         if (!res) {
1502                 if (!ast_strlen_zero(name2)) {
1503                         if (chan2)
1504                                 res = ast_async_goto(chan2, context, exten, pi);
1505                         else
1506                                 res = -1;
1507                         if (!res)
1508                                 astman_send_ack(s, m, "Dual Redirect successful");
1509                         else
1510                                 astman_send_error(s, m, "Secondary redirect failed");
1511                 } else
1512                         astman_send_ack(s, m, "Redirect successful");
1513         } else
1514                 astman_send_error(s, m, "Redirect failed");
1515         if (chan)
1516                 ast_channel_unlock(chan);
1517         if (chan2)
1518                 ast_channel_unlock(chan2);
1519         return 0;
1520 }
1521
1522 static char mandescr_command[] = 
1523 "Description: Run a CLI command.\n"
1524 "Variables: (Names marked with * are required)\n"
1525 "       *Command: Asterisk CLI command to run\n"
1526 "       ActionID: Optional Action id for message matching.\n";
1527
1528 /*! \brief  action_command: Manager command "command" - execute CLI command */
1529 static int action_command(struct mansession *s, struct message *m)
1530 {
1531         char *cmd = astman_get_header(m, "Command");
1532         char *id = astman_get_header(m, "ActionID");
1533         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1534         if (!ast_strlen_zero(id))
1535                 astman_append(s, "ActionID: %s\r\n", id);
1536         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1537         ast_cli_command(s->fd, cmd);
1538         astman_append(s, "--END COMMAND--\r\n\r\n");
1539         return 0;
1540 }
1541
1542 static void *fast_originate(void *data)
1543 {
1544         struct fast_originate_helper *in = data;
1545         int res;
1546         int reason = 0;
1547         struct ast_channel *chan = NULL;
1548
1549         if (!ast_strlen_zero(in->app)) {
1550                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
1551                         S_OR(in->cid_num, NULL), 
1552                         S_OR(in->cid_name, NULL),
1553                         in->vars, in->account, &chan);
1554         } else {
1555                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
1556                         S_OR(in->cid_num, NULL), 
1557                         S_OR(in->cid_name, NULL),
1558                         in->vars, in->account, &chan);
1559         }   
1560         
1561         /* Tell the manager what happened with the channel */
1562         manager_event(EVENT_FLAG_CALL,
1563                 res ? "OriginateFailure" : "OriginateSuccess",
1564                 "%s"
1565                 "Channel: %s/%s\r\n"
1566                 "Context: %s\r\n"
1567                 "Exten: %s\r\n"
1568                 "Reason: %d\r\n"
1569                 "Uniqueid: %s\r\n"
1570                 "CallerIDNum: %s\r\n"
1571                 "CallerIDName: %s\r\n",
1572                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
1573                 chan ? chan->uniqueid : "<null>",
1574                 S_OR(in->cid_num, "<unknown>"),
1575                 S_OR(in->cid_name, "<unknown>")
1576                 );
1577
1578         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1579         if (chan)
1580                 ast_channel_unlock(chan);
1581         free(in);
1582         return NULL;
1583 }
1584
1585 static char mandescr_originate[] = 
1586 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1587 "  Application/Data\n"
1588 "Variables: (Names marked with * are required)\n"
1589 "       *Channel: Channel name to call\n"
1590 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1591 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1592 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1593 "       Application: Application to use\n"
1594 "       Data: Data to use (requires 'Application')\n"
1595 "       Timeout: How long to wait for call to be answered (in ms)\n"
1596 "       CallerID: Caller ID to be set on the outgoing channel\n"
1597 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1598 "       Account: Account code\n"
1599 "       Async: Set to 'true' for fast origination\n";
1600
1601 static int action_originate(struct mansession *s, struct message *m)
1602 {
1603         char *name = astman_get_header(m, "Channel");
1604         char *exten = astman_get_header(m, "Exten");
1605         char *context = astman_get_header(m, "Context");
1606         char *priority = astman_get_header(m, "Priority");
1607         char *timeout = astman_get_header(m, "Timeout");
1608         char *callerid = astman_get_header(m, "CallerID");
1609         char *account = astman_get_header(m, "Account");
1610         char *app = astman_get_header(m, "Application");
1611         char *appdata = astman_get_header(m, "Data");
1612         char *async = astman_get_header(m, "Async");
1613         char *id = astman_get_header(m, "ActionID");
1614         struct ast_variable *vars = astman_get_variables(m);
1615         char *tech, *data;
1616         char *l = NULL, *n = NULL;
1617         int pi = 0;
1618         int res;
1619         int to = 30000;
1620         int reason = 0;
1621         char tmp[256];
1622         char tmp2[256];
1623         
1624         pthread_t th;
1625         pthread_attr_t attr;
1626         if (!name) {
1627                 astman_send_error(s, m, "Channel not specified");
1628                 return 0;
1629         }
1630         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1631                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1632                         astman_send_error(s, m, "Invalid priority\n");
1633                         return 0;
1634                 }
1635         }
1636         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1637                 astman_send_error(s, m, "Invalid timeout\n");
1638                 return 0;
1639         }
1640         ast_copy_string(tmp, name, sizeof(tmp));
1641         tech = tmp;
1642         data = strchr(tmp, '/');
1643         if (!data) {
1644                 astman_send_error(s, m, "Invalid channel\n");
1645                 return 0;
1646         }
1647         *data++ = '\0';
1648         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1649         ast_callerid_parse(tmp2, &n, &l);
1650         if (n) {
1651                 if (ast_strlen_zero(n))
1652                         n = NULL;
1653         }
1654         if (l) {
1655                 ast_shrink_phone_number(l);
1656                 if (ast_strlen_zero(l))
1657                         l = NULL;
1658         }
1659         if (ast_true(async)) {
1660                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1661                 if (!fast) {
1662                         res = -1;
1663                 } else {
1664                         if (!ast_strlen_zero(id))
1665                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1666                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1667                         ast_copy_string(fast->data, data, sizeof(fast->data));
1668                         ast_copy_string(fast->app, app, sizeof(fast->app));
1669                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1670                         if (l)
1671                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1672                         if (n)
1673                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1674                         fast->vars = vars;      
1675                         ast_copy_string(fast->context, context, sizeof(fast->context));
1676                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1677                         ast_copy_string(fast->account, account, sizeof(fast->account));
1678                         fast->timeout = to;
1679                         fast->priority = pi;
1680                         pthread_attr_init(&attr);
1681                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1682                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1683                                 res = -1;
1684                         } else {
1685                                 res = 0;
1686                         }
1687                 }
1688         } else if (!ast_strlen_zero(app)) {
1689                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1690         } else {
1691                 if (exten && context && pi)
1692                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1693                 else {
1694                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1695                         return 0;
1696                 }
1697         }   
1698         if (!res)
1699                 astman_send_ack(s, m, "Originate successfully queued");
1700         else
1701                 astman_send_error(s, m, "Originate failed");
1702         return 0;
1703 }
1704
1705 /*! \brief Help text for manager command mailboxstatus
1706  */
1707 static char mandescr_mailboxstatus[] = 
1708 "Description: Checks a voicemail account for status.\n"
1709 "Variables: (Names marked with * are required)\n"
1710 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1711 "       ActionID: Optional ActionID for message matching.\n"
1712 "Returns number of messages.\n"
1713 "       Message: Mailbox Status\n"
1714 "       Mailbox: <mailboxid>\n"
1715 "       Waiting: <count>\n"
1716 "\n";
1717
1718 static int action_mailboxstatus(struct mansession *s, struct message *m)
1719 {
1720         char *mailbox = astman_get_header(m, "Mailbox");
1721         char *id = astman_get_header(m,"ActionID");
1722         char idText[256] = "";
1723         int ret;
1724         if (ast_strlen_zero(mailbox)) {
1725                 astman_send_error(s, m, "Mailbox not specified");
1726                 return 0;
1727         }
1728         if (!ast_strlen_zero(id))
1729                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1730         ret = ast_app_has_voicemail(mailbox, NULL);
1731         astman_append(s, "Response: Success\r\n"
1732                                    "%s"
1733                                    "Message: Mailbox Status\r\n"
1734                                    "Mailbox: %s\r\n"
1735                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1736         return 0;
1737 }
1738
1739 static char mandescr_mailboxcount[] = 
1740 "Description: Checks a voicemail account for new messages.\n"
1741 "Variables: (Names marked with * are required)\n"
1742 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1743 "       ActionID: Optional ActionID for message matching.\n"
1744 "Returns number of new and old messages.\n"
1745 "       Message: Mailbox Message Count\n"
1746 "       Mailbox: <mailboxid>\n"
1747 "       NewMessages: <count>\n"
1748 "       OldMessages: <count>\n"
1749 "\n";
1750 static int action_mailboxcount(struct mansession *s, struct message *m)
1751 {
1752         char *mailbox = astman_get_header(m, "Mailbox");
1753         char *id = astman_get_header(m,"ActionID");
1754         char idText[256] = "";
1755         int newmsgs = 0, oldmsgs = 0;
1756         if (ast_strlen_zero(mailbox)) {
1757                 astman_send_error(s, m, "Mailbox not specified");
1758                 return 0;
1759         }
1760         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1761         if (!ast_strlen_zero(id)) {
1762                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1763         }
1764         astman_append(s, "Response: Success\r\n"
1765                                    "%s"
1766                                    "Message: Mailbox Message Count\r\n"
1767                                    "Mailbox: %s\r\n"
1768                                    "NewMessages: %d\r\n"
1769                                    "OldMessages: %d\r\n" 
1770                                    "\r\n",
1771                                     idText,mailbox, newmsgs, oldmsgs);
1772         return 0;
1773 }
1774
1775 static char mandescr_extensionstate[] = 
1776 "Description: Report the extension state for given extension.\n"
1777 "  If the extension has a hint, will use devicestate to check\n"
1778 "  the status of the device connected to the extension.\n"
1779 "Variables: (Names marked with * are required)\n"
1780 "       *Exten: Extension to check state on\n"
1781 "       *Context: Context for extension\n"
1782 "       ActionId: Optional ID for this transaction\n"
1783 "Will return an \"Extension Status\" message.\n"
1784 "The response will include the hint for the extension and the status.\n";
1785
1786 static int action_extensionstate(struct mansession *s, struct message *m)
1787 {
1788         char *exten = astman_get_header(m, "Exten");
1789         char *context = astman_get_header(m, "Context");
1790         char *id = astman_get_header(m,"ActionID");
1791         char idText[256] = "";
1792         char hint[256] = "";
1793         int status;
1794         if (ast_strlen_zero(exten)) {
1795                 astman_send_error(s, m, "Extension not specified");
1796                 return 0;
1797         }
1798         if (ast_strlen_zero(context))
1799                 context = "default";
1800         status = ast_extension_state(NULL, context, exten);
1801         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1802         if (!ast_strlen_zero(id)) {
1803                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1804         }
1805         astman_append(s, "Response: Success\r\n"
1806                                    "%s"
1807                                    "Message: Extension Status\r\n"
1808                                    "Exten: %s\r\n"
1809                                    "Context: %s\r\n"
1810                                    "Hint: %s\r\n"
1811                                    "Status: %d\r\n\r\n",
1812                                    idText,exten, context, hint, status);
1813         return 0;
1814 }
1815
1816 static char mandescr_timeout[] = 
1817 "Description: Hangup a channel after a certain time.\n"
1818 "Variables: (Names marked with * are required)\n"
1819 "       *Channel: Channel name to hangup\n"
1820 "       *Timeout: Maximum duration of the call (sec)\n"
1821 "Acknowledges set time with 'Timeout Set' message\n";
1822
1823 static int action_timeout(struct mansession *s, struct message *m)
1824 {
1825         struct ast_channel *c = NULL;
1826         char *name = astman_get_header(m, "Channel");
1827         int timeout = atoi(astman_get_header(m, "Timeout"));
1828         if (ast_strlen_zero(name)) {
1829                 astman_send_error(s, m, "No channel specified");
1830                 return 0;
1831         }
1832         if (!timeout) {
1833                 astman_send_error(s, m, "No timeout specified");
1834                 return 0;
1835         }
1836         c = ast_get_channel_by_name_locked(name);
1837         if (!c) {
1838                 astman_send_error(s, m, "No such channel");
1839                 return 0;
1840         }
1841         ast_channel_setwhentohangup(c, timeout);
1842         ast_channel_unlock(c);
1843         astman_send_ack(s, m, "Timeout Set");
1844         return 0;
1845 }
1846
1847 static int process_events(struct mansession *s)
1848 {
1849         struct eventqent *eqe;
1850         int ret = 0;
1851         ast_mutex_lock(&s->__lock);
1852         if (s->fd > -1) {
1853                 s->busy--;
1854                 if (!s->eventq)
1855                         s->eventq = master_eventq;
1856                 while(s->eventq->next) {
1857                         eqe = s->eventq->next;
1858                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1859                             ((s->send_events & eqe->category) == eqe->category)) {
1860                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1861                                         ret = -1;
1862                         }
1863                         unuse_eventqent(s->eventq);
1864                         s->eventq = eqe;
1865                 }
1866         }
1867         ast_mutex_unlock(&s->__lock);
1868         return ret;
1869 }
1870
1871 static char mandescr_userevent[] =
1872 "Description: Send an event to manager sessions.\n"
1873 "Variables: (Names marked with * are required)\n"
1874 "       *UserEvent: EventStringToSend\n"
1875 "       Header1: Content1\n"
1876 "       HeaderN: ContentN\n";
1877
1878 static int action_userevent(struct mansession *s, struct message *m)
1879 {
1880         char *event = astman_get_header(m, "UserEvent");
1881         char body[2048] = "";
1882         int x, bodylen = 0;
1883         for (x = 0; x < m->hdrcount; x++) {
1884                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1885                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1886                         bodylen += strlen(m->headers[x]);
1887                         ast_copy_string(body + bodylen, "\r\n", 3);
1888                         bodylen += 2;
1889                 }
1890         }
1891
1892         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1893         return 0;
1894 }
1895
1896 static int process_message(struct mansession *s, struct message *m)
1897 {
1898         char action[80] = "";
1899         struct manager_action *tmp = first_action;
1900         char *id = astman_get_header(m,"ActionID");
1901         char idText[256] = "";
1902         int ret = 0;
1903
1904         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1905         if (option_debug)
1906                 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
1907
1908         if (ast_strlen_zero(action)) {
1909                 astman_send_error(s, m, "Missing action in request");
1910                 return 0;
1911         }
1912         if (!ast_strlen_zero(id)) {
1913                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1914         }
1915         if (!s->authenticated) {
1916                 if (!strcasecmp(action, "Challenge")) {
1917                         char *authtype;
1918                         authtype = astman_get_header(m, "AuthType");
1919                         if (!strcasecmp(authtype, "MD5")) {
1920                                 if (ast_strlen_zero(s->challenge))
1921                                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1922                                 ast_mutex_lock(&s->__lock);
1923                                 astman_append(s, "Response: Success\r\n"
1924                                                 "%s"
1925                                                 "Challenge: %s\r\n\r\n",
1926                                                 idText, s->challenge);
1927                                 ast_mutex_unlock(&s->__lock);
1928                                 return 0;
1929                         } else {
1930                                 astman_send_error(s, m, "Must specify AuthType");
1931                                 return 0;
1932                         }
1933                 } else if (!strcasecmp(action, "Login")) {
1934                         if (authenticate(s, m)) {
1935                                 sleep(1);
1936                                 astman_send_error(s, m, "Authentication failed");
1937                                 return -1;
1938                         } else {
1939                                 s->authenticated = 1;
1940                                 if (option_verbose > 1) {
1941                                         if (displayconnects) {
1942                                                 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1943                                         }
1944                                 }
1945                                 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1946                                 astman_send_ack(s, m, "Authentication accepted");
1947                         }
1948                 } else if (!strcasecmp(action, "Logoff")) {
1949                         astman_send_ack(s, m, "See ya");
1950                         return -1;
1951                 } else
1952                         astman_send_error(s, m, "Authentication Required");
1953         } else {
1954                 ast_mutex_lock(&s->__lock);
1955                 s->busy++;
1956                 ast_mutex_unlock(&s->__lock);
1957                 while (tmp) {           
1958                         if (!strcasecmp(action, tmp->action)) {
1959                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1960                                         if (tmp->func(s, m))
1961                                                 ret = -1;
1962                                 } else {
1963                                         astman_send_error(s, m, "Permission denied");
1964                                 }
1965                                 break;
1966                         }
1967                         tmp = tmp->next;
1968                 }
1969                 if (!tmp)
1970                         astman_send_error(s, m, "Invalid/unknown command");
1971         }
1972         if (ret)
1973                 return ret;
1974         return process_events(s);
1975 }
1976
1977 static int get_input(struct mansession *s, char *output)
1978 {
1979         /* output must have at least sizeof(s->inbuf) space */
1980         int res;
1981         int x;
1982         struct pollfd fds[1];
1983         for (x = 1; x < s->inlen; x++) {
1984                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1985                         /* Copy output data up to and including \r\n */
1986                         memcpy(output, s->inbuf, x + 1);
1987                         /* Add trailing \0 */
1988                         output[x+1] = '\0';
1989                         /* Move remaining data back to the front */
1990                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1991                         s->inlen -= (x + 1);
1992                         return 1;
1993                 }
1994         } 
1995         if (s->inlen >= sizeof(s->inbuf) - 1) {
1996                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
1997                 s->inlen = 0;
1998         }
1999         fds[0].fd = s->fd;
2000         fds[0].events = POLLIN;
2001         do {
2002                 ast_mutex_lock(&s->__lock);
2003                 s->waiting_thread = pthread_self();
2004                 ast_mutex_unlock(&s->__lock);
2005
2006                 res = poll(fds, 1, -1);
2007
2008                 ast_mutex_lock(&s->__lock);
2009                 s->waiting_thread = AST_PTHREADT_NULL;
2010                 ast_mutex_unlock(&s->__lock);
2011                 if (res < 0) {
2012                         if (errno == EINTR) {
2013                                 if (s->dead)
2014                                         return -1;
2015                                 return 0;
2016                         }
2017                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2018                         return -1;
2019                 } else if (res > 0) {
2020                         ast_mutex_lock(&s->__lock);
2021                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2022                         ast_mutex_unlock(&s->__lock);
2023                         if (res < 1)
2024                                 return -1;
2025                         break;
2026                 }
2027         } while(1);
2028         s->inlen += res;
2029         s->inbuf[s->inlen] = '\0';
2030         return 0;
2031 }
2032
2033 static void *session_do(void *data)
2034 {
2035         struct mansession *s = data;
2036         struct message m;
2037         int res;
2038         
2039         ast_mutex_lock(&s->__lock);
2040         astman_append(s, "Asterisk Call Manager/1.0\r\n");
2041         ast_mutex_unlock(&s->__lock);
2042         memset(&m, 0, sizeof(m));
2043         for (;;) {
2044                 res = get_input(s, m.headers[m.hdrcount]);
2045                 if (res > 0) {
2046                         /* Strip trailing \r\n */
2047                         if (strlen(m.headers[m.hdrcount]) < 2)
2048                                 continue;
2049                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2050                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
2051                                 if (process_message(s, &m))
2052                                         break;
2053                                 memset(&m, 0, sizeof(m));
2054                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2055                                 m.hdrcount++;
2056                 } else if (res < 0) {
2057                         break;
2058                 } else if (s->eventq->next) {
2059                         if (process_events(s))
2060                                 break;
2061                 }
2062         }
2063         if (s->authenticated) {
2064                 if (option_verbose > 1) {
2065                         if (displayconnects) 
2066                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2067                 }
2068                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2069         } else {
2070                 if (option_verbose > 1) {
2071                         if (displayconnects)
2072                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2073                 }
2074                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2075         }
2076         destroy_session(s);
2077         return NULL;
2078 }
2079
2080 static void *accept_thread(void *ignore)
2081 {
2082         int as;
2083         struct sockaddr_in sin;
2084         socklen_t sinlen;
2085         struct eventqent *eqe;
2086         struct mansession *s;
2087         struct protoent *p;
2088         int arg = 1;
2089         int flags;
2090         pthread_attr_t attr;
2091         time_t now;
2092         struct pollfd pfds[1];
2093
2094         pthread_attr_init(&attr);
2095         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2096
2097         for (;;) {
2098                 time(&now);
2099                 AST_LIST_LOCK(&sessions);
2100                 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2101                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2102                                 AST_LIST_REMOVE_CURRENT(&sessions, list);
2103                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2104                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2105                                                 s->username, ast_inet_ntoa(s->sin.sin_addr));
2106                                 }
2107                                 free_session(s);
2108                                 break;  
2109                         }
2110                 }
2111                 AST_LIST_TRAVERSE_SAFE_END
2112                 /* Purge master event queue of old, unused events, but make sure we
2113                    always keep at least one in the queue */
2114                 eqe = master_eventq;
2115                 while (master_eventq->next && !master_eventq->usecount) {
2116                         eqe = master_eventq;
2117                         master_eventq = master_eventq->next;
2118                         free(eqe);
2119                 }
2120                 AST_LIST_UNLOCK(&sessions);
2121                 if (s)
2122                         ast_atomic_fetchadd_int(&num_sessions, -1);
2123
2124                 sinlen = sizeof(sin);
2125                 pfds[0].fd = asock;
2126                 pfds[0].events = POLLIN;
2127                 /* Wait for something to happen, but timeout every few seconds so
2128                    we can ditch any old manager sessions */
2129                 if (poll(pfds, 1, 5000) < 1)
2130                         continue;
2131                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2132                 if (as < 0) {
2133                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2134                         continue;
2135                 }
2136                 p = getprotobyname("tcp");
2137                 if (p) {
2138                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2139                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2140                         }
2141                 }
2142                 if (!(s = ast_calloc(1, sizeof(*s))))
2143                         continue;
2144
2145                 ast_atomic_fetchadd_int(&num_sessions, 1);
2146                 
2147                 memcpy(&s->sin, &sin, sizeof(sin));
2148                 s->writetimeout = 100;
2149                 s->waiting_thread = AST_PTHREADT_NULL;
2150
2151                 if (!block_sockets) {
2152                         /* For safety, make sure socket is non-blocking */
2153                         flags = fcntl(as, F_GETFL);
2154                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
2155                 } else {
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_background(&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         {
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         }
2495         ast_mutex_lock(&s->__lock);
2496         if (s->needdestroy) {
2497                 if (s->inuse == 1) {
2498                         if (option_debug)
2499                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2500                         blastaway = 1;
2501                 } else {
2502                         if (option_debug)
2503                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2504                         if (s->waiting_thread != AST_PTHREADT_NULL)
2505                                 pthread_kill(s->waiting_thread, SIGURG);
2506                         s->inuse--;
2507                 }
2508         } else
2509                 s->inuse--;
2510         ast_mutex_unlock(&s->__lock);
2511         
2512         if (blastaway)
2513                 destroy_session(s);
2514 generic_callback_out:
2515         if (*status != 200)
2516                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
2517         return retval;
2518 }
2519
2520 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2521 {
2522         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2523 }
2524
2525 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2526 {
2527         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2528 }
2529
2530 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2531 {
2532         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2533 }
2534
2535 struct ast_http_uri rawmanuri = {
2536         .description = "Raw HTTP Manager Event Interface",
2537         .uri = "rawman",
2538         .has_subtree = 0,
2539         .callback = rawman_http_callback,
2540 };
2541
2542 struct ast_http_uri manageruri = {
2543         .description = "HTML Manager Event Interface",
2544         .uri = "manager",
2545         .has_subtree = 0,
2546         .callback = manager_http_callback,
2547 };
2548
2549 struct ast_http_uri managerxmluri = {
2550         .description = "XML Manager Event Interface",
2551         .uri = "mxml",
2552         .has_subtree = 0,
2553         .callback = mxml_http_callback,
2554 };
2555
2556 static int registered = 0;
2557 static int webregged = 0;
2558
2559 int init_manager(void)
2560 {
2561         struct ast_config *cfg = NULL;
2562         const char *val;
2563         char *cat = NULL;
2564         int oldportno = portno;
2565         static struct sockaddr_in ba;
2566         int x = 1;
2567         int flags;
2568         int webenabled = 0;
2569         int newhttptimeout = 60;
2570         struct ast_manager_user *user = NULL;
2571
2572         if (!registered) {
2573                 /* Register default actions */
2574                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2575                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2576                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2577                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2578                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2579                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2580                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2581                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2582                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2583                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2584                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2585                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2586                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2587                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2588                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2589                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2590                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2591                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2592                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2593
2594                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2595                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2596                 registered = 1;
2597                 /* Append placeholder event so master_eventq never runs dry */
2598                 append_event("Event: Placeholder\r\n\r\n", 0);
2599         }
2600         portno = DEFAULT_MANAGER_PORT;
2601         displayconnects = 1;
2602         cfg = ast_config_load("manager.conf");
2603         if (!cfg) {
2604                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2605                 return 0;
2606         }
2607         val = ast_variable_retrieve(cfg, "general", "enabled");
2608         if (val)
2609                 enabled = ast_true(val);
2610
2611         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2612         if (val)
2613                 block_sockets = ast_true(val);
2614
2615         val = ast_variable_retrieve(cfg, "general", "webenabled");
2616         if (val)
2617                 webenabled = ast_true(val);
2618
2619         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2620                 if (sscanf(val, "%d", &portno) != 1) {
2621                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2622                         portno = DEFAULT_MANAGER_PORT;
2623                 }
2624         }
2625
2626         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2627                 displayconnects = ast_true(val);
2628
2629         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2630                 timestampevents = ast_true(val);
2631
2632         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2633                 newhttptimeout = atoi(val);
2634
2635         memset(&ba, 0, sizeof(ba));
2636         ba.sin_family = AF_INET;
2637         ba.sin_port = htons(portno);
2638
2639         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2640                 if (!inet_aton(val, &ba.sin_addr)) { 
2641                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2642                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2643                 }
2644         }
2645         
2646
2647         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2648 #if 0
2649                 /* Can't be done yet */
2650                 close(asock);
2651                 asock = -1;
2652 #else
2653                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2654 #endif
2655         }
2656
2657         AST_LIST_LOCK(&users);
2658
2659         while ((cat = ast_category_browse(cfg, cat))) {
2660                 struct ast_variable *var = NULL;
2661
2662                 if (!strcasecmp(cat, "general"))
2663                         continue;
2664
2665                 /* Look for an existing entry, if none found - create one and add it to the list */
2666                 if (!(user = ast_get_manager_by_name_locked(cat))) {
2667                         if (!(user = ast_calloc(1, sizeof(*user))))
2668                                 break;
2669                         /* Copy name over */
2670                         ast_copy_string(user->username, cat, sizeof(user->username));
2671                         /* Insert into list */
2672                         AST_LIST_INSERT_TAIL(&users, user, list);
2673                 }
2674
2675                 /* Make sure we keep this user and don't destroy it during cleanup */
2676                 user->keep = 1;
2677
2678                 var = ast_variable_browse(cfg, cat);
2679                 while (var) {
2680                         if (!strcasecmp(var->name, "secret")) {
2681                                 if (user->secret)
2682                                         free(user->secret);
2683                                 user->secret = ast_strdup(var->value);
2684                         } else if (!strcasecmp(var->name, "deny") ) {
2685                                 if (user->deny)
2686                                         free(user->deny);
2687                                 user->deny = ast_strdup(var->value);
2688                         } else if (!strcasecmp(var->name, "permit") ) {
2689                                 if (user->permit)
2690                                         free(user->permit);
2691                                 user->permit = ast_strdup(var->value);
2692                         }  else if (!strcasecmp(var->name, "read") ) {
2693                                 if (user->read)
2694                                         free(user->read);
2695                                 user->read = ast_strdup(var->value);
2696                         }  else if (!strcasecmp(var->name, "write") ) {
2697                                 if (user->write)
2698                                         free(user->write);
2699                                 user->write = ast_strdup(var->value);
2700                         }  else if (!strcasecmp(var->name, "displayconnects") )
2701                                 user->displayconnects = ast_true(var->value);
2702                         else {
2703                                 if (option_debug)
2704                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2705                         }
2706                         var = var->next;
2707                 }
2708         }
2709
2710         /* Perform cleanup - essentially prune out old users that no longer exist */
2711         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2712                 if (user->keep) {
2713                         user->keep = 0;
2714                         continue;
2715                 }
2716                 /* We do not need to keep this user so take them out of the list */
2717                 AST_LIST_REMOVE_CURRENT(&users, list);
2718                 /* Free their memory now */
2719                 if (user->secret)
2720                         free(user->secret);
2721                 if (user->deny)
2722                         free(user->deny);
2723                 if (user->permit)
2724                         free(user->permit);
2725                 if (user->read)
2726                         free(user->read);
2727                 if (user->write)
2728                         free(user->write);
2729                 free(user);
2730         }
2731         AST_LIST_TRAVERSE_SAFE_END
2732
2733         AST_LIST_UNLOCK(&users);
2734
2735         ast_config_destroy(cfg);
2736         
2737         if (webenabled && enabled) {
2738                 if (!webregged) {
2739                         ast_http_uri_link(&rawmanuri);
2740                         ast_http_uri_link(&manageruri);
2741                         ast_http_uri_link(&managerxmluri);
2742                         webregged = 1;
2743                 }
2744         } else {
2745                 if (webregged) {
2746                         ast_http_uri_unlink(&rawmanuri);
2747                         ast_http_uri_unlink(&manageruri);
2748                         ast_http_uri_unlink(&managerxmluri);
2749                         webregged = 0;
2750                 }
2751         }
2752
2753         if (newhttptimeout > 0)
2754                 httptimeout = newhttptimeout;
2755
2756         /* If not enabled, do nothing */
2757         if (!enabled)
2758                 return 0;
2759
2760         if (asock < 0) {
2761                 asock = socket(AF_INET, SOCK_STREAM, 0);
2762                 if (asock < 0) {
2763                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2764                         return -1;
2765                 }
2766                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2767                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2768                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2769                         close(asock);
2770                         asock = -1;
2771                         return -1;
2772                 }
2773                 if (listen(asock, 2)) {
2774                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2775                         close(asock);
2776                         asock = -1;
2777                         return -1;
2778                 }
2779                 flags = fcntl(asock, F_GETFL);
2780                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2781                 if (option_verbose)
2782                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2783                 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2784         }
2785         return 0;
2786 }
2787
2788 int reload_manager(void)
2789 {
2790         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2791         return init_manager();
2792 }