protect access to first_action with actionlock.
[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 l = strlen(word), 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, l) && ++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;
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 = first_action; 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;
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 = first_action; 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;
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         for (cur = first_action; cur; cur = cur->next) { /* 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         }
1218         ast_mutex_unlock(&actionlock);
1219         astman_append(s, "\r\n");
1220
1221         return 0;
1222 }
1223
1224 static char mandescr_events[] = 
1225 "Description: Enable/Disable sending of events to this manager\n"
1226 "  client.\n"
1227 "Variables:\n"
1228 "       EventMask: 'on' if all events should be sent,\n"
1229 "               'off' if no events should be sent,\n"
1230 "               'system,call,log' to select which flags events should have to be sent.\n";
1231
1232 static int action_events(struct mansession *s, struct message *m)
1233 {
1234         char *mask = astman_get_header(m, "EventMask");
1235         int res;
1236
1237         res = set_eventmask(s, mask);
1238         if (res > 0)
1239                 astman_send_response(s, m, "Events On", NULL);
1240         else if (res == 0)
1241                 astman_send_response(s, m, "Events Off", NULL);
1242
1243         return 0;
1244 }
1245
1246 static char mandescr_logoff[] = 
1247 "Description: Logoff this manager session\n"
1248 "Variables: NONE\n";
1249
1250 static int action_logoff(struct mansession *s, struct message *m)
1251 {
1252         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1253         return -1;
1254 }
1255
1256 static char mandescr_hangup[] = 
1257 "Description: Hangup a channel\n"
1258 "Variables: \n"
1259 "       Channel: The channel name to be hungup\n";
1260
1261 static int action_hangup(struct mansession *s, struct message *m)
1262 {
1263         struct ast_channel *c = NULL;
1264         char *name = astman_get_header(m, "Channel");
1265         if (ast_strlen_zero(name)) {
1266                 astman_send_error(s, m, "No channel specified");
1267                 return 0;
1268         }
1269         c = ast_get_channel_by_name_locked(name);
1270         if (!c) {
1271                 astman_send_error(s, m, "No such channel");
1272                 return 0;
1273         }
1274         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1275         ast_channel_unlock(c);
1276         astman_send_ack(s, m, "Channel Hungup");
1277         return 0;
1278 }
1279
1280 static char mandescr_setvar[] = 
1281 "Description: Set a global or local channel variable.\n"
1282 "Variables: (Names marked with * are required)\n"
1283 "       Channel: Channel to set variable for\n"
1284 "       *Variable: Variable name\n"
1285 "       *Value: Value\n";
1286
1287 static int action_setvar(struct mansession *s, struct message *m)
1288 {
1289         struct ast_channel *c = NULL;
1290         char *name = astman_get_header(m, "Channel");
1291         char *varname = astman_get_header(m, "Variable");
1292         char *varval = astman_get_header(m, "Value");
1293         
1294         if (ast_strlen_zero(varname)) {
1295                 astman_send_error(s, m, "No variable specified");
1296                 return 0;
1297         }
1298         
1299         if (ast_strlen_zero(varval)) {
1300                 astman_send_error(s, m, "No value specified");
1301                 return 0;
1302         }
1303
1304         if (!ast_strlen_zero(name)) {
1305                 c = ast_get_channel_by_name_locked(name);
1306                 if (!c) {
1307                         astman_send_error(s, m, "No such channel");
1308                         return 0;
1309                 }
1310         }
1311         
1312         pbx_builtin_setvar_helper(c, varname, varval);
1313           
1314         if (c)
1315                 ast_channel_unlock(c);
1316
1317         astman_send_ack(s, m, "Variable Set");  
1318
1319         return 0;
1320 }
1321
1322 static char mandescr_getvar[] = 
1323 "Description: Get the value of a global or local channel variable.\n"
1324 "Variables: (Names marked with * are required)\n"
1325 "       Channel: Channel to read variable from\n"
1326 "       *Variable: Variable name\n"
1327 "       ActionID: Optional Action id for message matching.\n";
1328
1329 static int action_getvar(struct mansession *s, struct message *m)
1330 {
1331         struct ast_channel *c = NULL;
1332         char *name = astman_get_header(m, "Channel");
1333         char *varname = astman_get_header(m, "Variable");
1334         char *id = astman_get_header(m,"ActionID");
1335         char *varval;
1336         char workspace[1024];
1337
1338         if (ast_strlen_zero(varname)) {
1339                 astman_send_error(s, m, "No variable specified");
1340                 return 0;
1341         }
1342
1343         if (!ast_strlen_zero(name)) {
1344                 c = ast_get_channel_by_name_locked(name);
1345                 if (!c) {
1346                         astman_send_error(s, m, "No such channel");
1347                         return 0;
1348                 }
1349         }
1350
1351         if (varname[strlen(varname) - 1] == ')') {
1352                 ast_func_read(c, varname, workspace, sizeof(workspace));
1353         } else {
1354                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1355         }
1356
1357         if (c)
1358                 ast_channel_unlock(c);
1359         astman_append(s, "Response: Success\r\n"
1360                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1361         if (!ast_strlen_zero(id))
1362                 astman_append(s, "ActionID: %s\r\n",id);
1363         astman_append(s, "\r\n");
1364
1365         return 0;
1366 }
1367
1368
1369 /*! \brief Manager "status" command to show channels */
1370 /* Needs documentation... */
1371 static int action_status(struct mansession *s, struct message *m)
1372 {
1373         char *id = astman_get_header(m,"ActionID");
1374         char *name = astman_get_header(m,"Channel");
1375         char idText[256] = "";
1376         struct ast_channel *c;
1377         char bridge[256];
1378         struct timeval now = ast_tvnow();
1379         long elapsed_seconds = 0;
1380         int all = ast_strlen_zero(name); /* set if we want all channels */
1381
1382         astman_send_ack(s, m, "Channel status will follow");
1383         if (!ast_strlen_zero(id))
1384                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1385         if (all)
1386                 c = ast_channel_walk_locked(NULL);
1387         else {
1388                 c = ast_get_channel_by_name_locked(name);
1389                 if (!c) {
1390                         astman_send_error(s, m, "No such channel");
1391                         return 0;
1392                 }
1393         }
1394         /* if we look by name, we break after the first iteration */
1395         while (c) {
1396                 if (c->_bridge)
1397                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1398                 else
1399                         bridge[0] = '\0';
1400                 if (c->pbx) {
1401                         if (c->cdr) {
1402                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1403                         }
1404                         astman_append(s,
1405                         "Event: Status\r\n"
1406                         "Privilege: Call\r\n"
1407                         "Channel: %s\r\n"
1408                         "CallerIDNum: %s\r\n"
1409                         "CallerIDName: %s\r\n"
1410                         "Account: %s\r\n"
1411                         "State: %s\r\n"
1412                         "Context: %s\r\n"
1413                         "Extension: %s\r\n"
1414                         "Priority: %d\r\n"
1415                         "Seconds: %ld\r\n"
1416                         "%s"
1417                         "Uniqueid: %s\r\n"
1418                         "%s"
1419                         "\r\n",
1420                         c->name, 
1421                         S_OR(c->cid.cid_num, "<unknown>"), 
1422                         S_OR(c->cid.cid_name, "<unknown>"), 
1423                         c->accountcode,
1424                         ast_state2str(c->_state), c->context,
1425                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1426                 } else {
1427                         astman_append(s,
1428                         "Event: Status\r\n"
1429                         "Privilege: Call\r\n"
1430                         "Channel: %s\r\n"
1431                         "CallerIDNum: %s\r\n"
1432                         "CallerIDName: %s\r\n"
1433                         "Account: %s\r\n"
1434                         "State: %s\r\n"
1435                         "%s"
1436                         "Uniqueid: %s\r\n"
1437                         "%s"
1438                         "\r\n",
1439                         c->name, 
1440                         S_OR(c->cid.cid_num, "<unknown>"), 
1441                         S_OR(c->cid.cid_name, "<unknown>"), 
1442                         c->accountcode,
1443                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1444                 }
1445                 ast_channel_unlock(c);
1446                 if (!all)
1447                         break;
1448                 c = ast_channel_walk_locked(c);
1449         }
1450         astman_append(s,
1451         "Event: StatusComplete\r\n"
1452         "%s"
1453         "\r\n",idText);
1454         return 0;
1455 }
1456
1457 static char mandescr_redirect[] = 
1458 "Description: Redirect (transfer) a call.\n"
1459 "Variables: (Names marked with * are required)\n"
1460 "       *Channel: Channel to redirect\n"
1461 "       ExtraChannel: Second call leg to transfer (optional)\n"
1462 "       *Exten: Extension to transfer to\n"
1463 "       *Context: Context to transfer to\n"
1464 "       *Priority: Priority to transfer to\n"
1465 "       ActionID: Optional Action id for message matching.\n";
1466
1467 /*! \brief  action_redirect: The redirect manager command */
1468 static int action_redirect(struct mansession *s, struct message *m)
1469 {
1470         char *name = astman_get_header(m, "Channel");
1471         char *name2 = astman_get_header(m, "ExtraChannel");
1472         char *exten = astman_get_header(m, "Exten");
1473         char *context = astman_get_header(m, "Context");
1474         char *priority = astman_get_header(m, "Priority");
1475         struct ast_channel *chan, *chan2 = NULL;
1476         int pi = 0;
1477         int res;
1478
1479         if (ast_strlen_zero(name)) {
1480                 astman_send_error(s, m, "Channel not specified");
1481                 return 0;
1482         }
1483         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1484                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1485                         astman_send_error(s, m, "Invalid priority\n");
1486                         return 0;
1487                 }
1488         }
1489         /* XXX watch out, possible deadlock!!! */
1490         chan = ast_get_channel_by_name_locked(name);
1491         if (!chan) {
1492                 char buf[BUFSIZ];
1493                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1494                 astman_send_error(s, m, buf);
1495                 return 0;
1496         }
1497         if (!ast_strlen_zero(name2))
1498                 chan2 = ast_get_channel_by_name_locked(name2);
1499         res = ast_async_goto(chan, context, exten, pi);
1500         if (!res) {
1501                 if (!ast_strlen_zero(name2)) {
1502                         if (chan2)
1503                                 res = ast_async_goto(chan2, context, exten, pi);
1504                         else
1505                                 res = -1;
1506                         if (!res)
1507                                 astman_send_ack(s, m, "Dual Redirect successful");
1508                         else
1509                                 astman_send_error(s, m, "Secondary redirect failed");
1510                 } else
1511                         astman_send_ack(s, m, "Redirect successful");
1512         } else
1513                 astman_send_error(s, m, "Redirect failed");
1514         if (chan)
1515                 ast_channel_unlock(chan);
1516         if (chan2)
1517                 ast_channel_unlock(chan2);
1518         return 0;
1519 }
1520
1521 static char mandescr_command[] = 
1522 "Description: Run a CLI command.\n"
1523 "Variables: (Names marked with * are required)\n"
1524 "       *Command: Asterisk CLI command to run\n"
1525 "       ActionID: Optional Action id for message matching.\n";
1526
1527 /*! \brief  action_command: Manager command "command" - execute CLI command */
1528 static int action_command(struct mansession *s, struct message *m)
1529 {
1530         char *cmd = astman_get_header(m, "Command");
1531         char *id = astman_get_header(m, "ActionID");
1532         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1533         if (!ast_strlen_zero(id))
1534                 astman_append(s, "ActionID: %s\r\n", id);
1535         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1536         ast_cli_command(s->fd, cmd);
1537         astman_append(s, "--END COMMAND--\r\n\r\n");
1538         return 0;
1539 }
1540
1541 static void *fast_originate(void *data)
1542 {
1543         struct fast_originate_helper *in = data;
1544         int res;
1545         int reason = 0;
1546         struct ast_channel *chan = NULL;
1547
1548         if (!ast_strlen_zero(in->app)) {
1549                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
1550                         S_OR(in->cid_num, NULL), 
1551                         S_OR(in->cid_name, NULL),
1552                         in->vars, in->account, &chan);
1553         } else {
1554                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
1555                         S_OR(in->cid_num, NULL), 
1556                         S_OR(in->cid_name, NULL),
1557                         in->vars, in->account, &chan);
1558         }   
1559         
1560         /* Tell the manager what happened with the channel */
1561         manager_event(EVENT_FLAG_CALL,
1562                 res ? "OriginateFailure" : "OriginateSuccess",
1563                 "%s"
1564                 "Channel: %s/%s\r\n"
1565                 "Context: %s\r\n"
1566                 "Exten: %s\r\n"
1567                 "Reason: %d\r\n"
1568                 "Uniqueid: %s\r\n"
1569                 "CallerIDNum: %s\r\n"
1570                 "CallerIDName: %s\r\n",
1571                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
1572                 chan ? chan->uniqueid : "<null>",
1573                 S_OR(in->cid_num, "<unknown>"),
1574                 S_OR(in->cid_name, "<unknown>")
1575                 );
1576
1577         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1578         if (chan)
1579                 ast_channel_unlock(chan);
1580         free(in);
1581         return NULL;
1582 }
1583
1584 static char mandescr_originate[] = 
1585 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1586 "  Application/Data\n"
1587 "Variables: (Names marked with * are required)\n"
1588 "       *Channel: Channel name to call\n"
1589 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1590 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1591 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1592 "       Application: Application to use\n"
1593 "       Data: Data to use (requires 'Application')\n"
1594 "       Timeout: How long to wait for call to be answered (in ms)\n"
1595 "       CallerID: Caller ID to be set on the outgoing channel\n"
1596 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1597 "       Account: Account code\n"
1598 "       Async: Set to 'true' for fast origination\n";
1599
1600 static int action_originate(struct mansession *s, struct message *m)
1601 {
1602         char *name = astman_get_header(m, "Channel");
1603         char *exten = astman_get_header(m, "Exten");
1604         char *context = astman_get_header(m, "Context");
1605         char *priority = astman_get_header(m, "Priority");
1606         char *timeout = astman_get_header(m, "Timeout");
1607         char *callerid = astman_get_header(m, "CallerID");
1608         char *account = astman_get_header(m, "Account");
1609         char *app = astman_get_header(m, "Application");
1610         char *appdata = astman_get_header(m, "Data");
1611         char *async = astman_get_header(m, "Async");
1612         char *id = astman_get_header(m, "ActionID");
1613         struct ast_variable *vars = astman_get_variables(m);
1614         char *tech, *data;
1615         char *l = NULL, *n = NULL;
1616         int pi = 0;
1617         int res;
1618         int to = 30000;
1619         int reason = 0;
1620         char tmp[256];
1621         char tmp2[256];
1622         
1623         pthread_t th;
1624         pthread_attr_t attr;
1625         if (!name) {
1626                 astman_send_error(s, m, "Channel not specified");
1627                 return 0;
1628         }
1629         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1630                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1631                         astman_send_error(s, m, "Invalid priority\n");
1632                         return 0;
1633                 }
1634         }
1635         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1636                 astman_send_error(s, m, "Invalid timeout\n");
1637                 return 0;
1638         }
1639         ast_copy_string(tmp, name, sizeof(tmp));
1640         tech = tmp;
1641         data = strchr(tmp, '/');
1642         if (!data) {
1643                 astman_send_error(s, m, "Invalid channel\n");
1644                 return 0;
1645         }
1646         *data++ = '\0';
1647         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1648         ast_callerid_parse(tmp2, &n, &l);
1649         if (n) {
1650                 if (ast_strlen_zero(n))
1651                         n = NULL;
1652         }
1653         if (l) {
1654                 ast_shrink_phone_number(l);
1655                 if (ast_strlen_zero(l))
1656                         l = NULL;
1657         }
1658         if (ast_true(async)) {
1659                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1660                 if (!fast) {
1661                         res = -1;
1662                 } else {
1663                         if (!ast_strlen_zero(id))
1664                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1665                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1666                         ast_copy_string(fast->data, data, sizeof(fast->data));
1667                         ast_copy_string(fast->app, app, sizeof(fast->app));
1668                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1669                         if (l)
1670                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1671                         if (n)
1672                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1673                         fast->vars = vars;      
1674                         ast_copy_string(fast->context, context, sizeof(fast->context));
1675                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1676                         ast_copy_string(fast->account, account, sizeof(fast->account));
1677                         fast->timeout = to;
1678                         fast->priority = pi;
1679                         pthread_attr_init(&attr);
1680                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1681                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1682                                 res = -1;
1683                         } else {
1684                                 res = 0;
1685                         }
1686                 }
1687         } else if (!ast_strlen_zero(app)) {
1688                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1689         } else {
1690                 if (exten && context && pi)
1691                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1692                 else {
1693                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1694                         return 0;
1695                 }
1696         }   
1697         if (!res)
1698                 astman_send_ack(s, m, "Originate successfully queued");
1699         else
1700                 astman_send_error(s, m, "Originate failed");
1701         return 0;
1702 }
1703
1704 /*! \brief Help text for manager command mailboxstatus
1705  */
1706 static char mandescr_mailboxstatus[] = 
1707 "Description: Checks a voicemail account for status.\n"
1708 "Variables: (Names marked with * are required)\n"
1709 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1710 "       ActionID: Optional ActionID for message matching.\n"
1711 "Returns number of messages.\n"
1712 "       Message: Mailbox Status\n"
1713 "       Mailbox: <mailboxid>\n"
1714 "       Waiting: <count>\n"
1715 "\n";
1716
1717 static int action_mailboxstatus(struct mansession *s, struct message *m)
1718 {
1719         char *mailbox = astman_get_header(m, "Mailbox");
1720         char *id = astman_get_header(m,"ActionID");
1721         char idText[256] = "";
1722         int ret;
1723         if (ast_strlen_zero(mailbox)) {
1724                 astman_send_error(s, m, "Mailbox not specified");
1725                 return 0;
1726         }
1727         if (!ast_strlen_zero(id))
1728                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1729         ret = ast_app_has_voicemail(mailbox, NULL);
1730         astman_append(s, "Response: Success\r\n"
1731                                    "%s"
1732                                    "Message: Mailbox Status\r\n"
1733                                    "Mailbox: %s\r\n"
1734                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1735         return 0;
1736 }
1737
1738 static char mandescr_mailboxcount[] = 
1739 "Description: Checks a voicemail account for new messages.\n"
1740 "Variables: (Names marked with * are required)\n"
1741 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1742 "       ActionID: Optional ActionID for message matching.\n"
1743 "Returns number of new and old messages.\n"
1744 "       Message: Mailbox Message Count\n"
1745 "       Mailbox: <mailboxid>\n"
1746 "       NewMessages: <count>\n"
1747 "       OldMessages: <count>\n"
1748 "\n";
1749 static int action_mailboxcount(struct mansession *s, struct message *m)
1750 {
1751         char *mailbox = astman_get_header(m, "Mailbox");
1752         char *id = astman_get_header(m,"ActionID");
1753         char idText[256] = "";
1754         int newmsgs = 0, oldmsgs = 0;
1755         if (ast_strlen_zero(mailbox)) {
1756                 astman_send_error(s, m, "Mailbox not specified");
1757                 return 0;
1758         }
1759         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1760         if (!ast_strlen_zero(id)) {
1761                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1762         }
1763         astman_append(s, "Response: Success\r\n"
1764                                    "%s"
1765                                    "Message: Mailbox Message Count\r\n"
1766                                    "Mailbox: %s\r\n"
1767                                    "NewMessages: %d\r\n"
1768                                    "OldMessages: %d\r\n" 
1769                                    "\r\n",
1770                                     idText,mailbox, newmsgs, oldmsgs);
1771         return 0;
1772 }
1773
1774 static char mandescr_extensionstate[] = 
1775 "Description: Report the extension state for given extension.\n"
1776 "  If the extension has a hint, will use devicestate to check\n"
1777 "  the status of the device connected to the extension.\n"
1778 "Variables: (Names marked with * are required)\n"
1779 "       *Exten: Extension to check state on\n"
1780 "       *Context: Context for extension\n"
1781 "       ActionId: Optional ID for this transaction\n"
1782 "Will return an \"Extension Status\" message.\n"
1783 "The response will include the hint for the extension and the status.\n";
1784
1785 static int action_extensionstate(struct mansession *s, struct message *m)
1786 {
1787         char *exten = astman_get_header(m, "Exten");
1788         char *context = astman_get_header(m, "Context");
1789         char *id = astman_get_header(m,"ActionID");
1790         char idText[256] = "";
1791         char hint[256] = "";
1792         int status;
1793         if (ast_strlen_zero(exten)) {
1794                 astman_send_error(s, m, "Extension not specified");
1795                 return 0;
1796         }
1797         if (ast_strlen_zero(context))
1798                 context = "default";
1799         status = ast_extension_state(NULL, context, exten);
1800         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1801         if (!ast_strlen_zero(id)) {
1802                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1803         }
1804         astman_append(s, "Response: Success\r\n"
1805                                    "%s"
1806                                    "Message: Extension Status\r\n"
1807                                    "Exten: %s\r\n"
1808                                    "Context: %s\r\n"
1809                                    "Hint: %s\r\n"
1810                                    "Status: %d\r\n\r\n",
1811                                    idText,exten, context, hint, status);
1812         return 0;
1813 }
1814
1815 static char mandescr_timeout[] = 
1816 "Description: Hangup a channel after a certain time.\n"
1817 "Variables: (Names marked with * are required)\n"
1818 "       *Channel: Channel name to hangup\n"
1819 "       *Timeout: Maximum duration of the call (sec)\n"
1820 "Acknowledges set time with 'Timeout Set' message\n";
1821
1822 static int action_timeout(struct mansession *s, struct message *m)
1823 {
1824         struct ast_channel *c = NULL;
1825         char *name = astman_get_header(m, "Channel");
1826         int timeout = atoi(astman_get_header(m, "Timeout"));
1827         if (ast_strlen_zero(name)) {
1828                 astman_send_error(s, m, "No channel specified");
1829                 return 0;
1830         }
1831         if (!timeout) {
1832                 astman_send_error(s, m, "No timeout specified");
1833                 return 0;
1834         }
1835         c = ast_get_channel_by_name_locked(name);
1836         if (!c) {
1837                 astman_send_error(s, m, "No such channel");
1838                 return 0;
1839         }
1840         ast_channel_setwhentohangup(c, timeout);
1841         ast_channel_unlock(c);
1842         astman_send_ack(s, m, "Timeout Set");
1843         return 0;
1844 }
1845
1846 static int process_events(struct mansession *s)
1847 {
1848         struct eventqent *eqe;
1849         int ret = 0;
1850         ast_mutex_lock(&s->__lock);
1851         if (s->fd > -1) {
1852                 s->busy--;
1853                 if (!s->eventq)
1854                         s->eventq = master_eventq;
1855                 while(s->eventq->next) {
1856                         eqe = s->eventq->next;
1857                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1858                             ((s->send_events & eqe->category) == eqe->category)) {
1859                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1860                                         ret = -1;
1861                         }
1862                         unuse_eventqent(s->eventq);
1863                         s->eventq = eqe;
1864                 }
1865         }
1866         ast_mutex_unlock(&s->__lock);
1867         return ret;
1868 }
1869
1870 static char mandescr_userevent[] =
1871 "Description: Send an event to manager sessions.\n"
1872 "Variables: (Names marked with * are required)\n"
1873 "       *UserEvent: EventStringToSend\n"
1874 "       Header1: Content1\n"
1875 "       HeaderN: ContentN\n";
1876
1877 static int action_userevent(struct mansession *s, struct message *m)
1878 {
1879         char *event = astman_get_header(m, "UserEvent");
1880         char body[2048] = "";
1881         int x, bodylen = 0;
1882         for (x = 0; x < m->hdrcount; x++) {
1883                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1884                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1885                         bodylen += strlen(m->headers[x]);
1886                         ast_copy_string(body + bodylen, "\r\n", 3);
1887                         bodylen += 2;
1888                 }
1889         }
1890
1891         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1892         return 0;
1893 }
1894
1895 static int process_message(struct mansession *s, struct message *m)
1896 {
1897         char action[80] = "";
1898         char *id = astman_get_header(m,"ActionID");
1899         char idText[256] = "";
1900         int ret = 0;
1901
1902         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1903         if (option_debug)
1904                 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
1905
1906         if (ast_strlen_zero(action)) {
1907                 astman_send_error(s, m, "Missing action in request");
1908                 return 0;
1909         }
1910         if (!ast_strlen_zero(id)) {
1911                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1912         }
1913         if (!s->authenticated) {
1914                 if (!strcasecmp(action, "Challenge")) {
1915                         char *authtype;
1916                         authtype = astman_get_header(m, "AuthType");
1917                         if (!strcasecmp(authtype, "MD5")) {
1918                                 if (ast_strlen_zero(s->challenge))
1919                                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1920                                 ast_mutex_lock(&s->__lock);
1921                                 astman_append(s, "Response: Success\r\n"
1922                                                 "%s"
1923                                                 "Challenge: %s\r\n\r\n",
1924                                                 idText, s->challenge);
1925                                 ast_mutex_unlock(&s->__lock);
1926                                 return 0;
1927                         } else {
1928                                 astman_send_error(s, m, "Must specify AuthType");
1929                                 return 0;
1930                         }
1931                 } else if (!strcasecmp(action, "Login")) {
1932                         if (authenticate(s, m)) {
1933                                 sleep(1);
1934                                 astman_send_error(s, m, "Authentication failed");
1935                                 return -1;
1936                         } else {
1937                                 s->authenticated = 1;
1938                                 if (option_verbose > 1) {
1939                                         if (displayconnects) {
1940                                                 ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1941                                         }
1942                                 }
1943                                 ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1944                                 astman_send_ack(s, m, "Authentication accepted");
1945                         }
1946                 } else if (!strcasecmp(action, "Logoff")) {
1947                         astman_send_ack(s, m, "See ya");
1948                         return -1;
1949                 } else
1950                         astman_send_error(s, m, "Authentication Required");
1951         } else {
1952                 struct manager_action *tmp;
1953                 ast_mutex_lock(&s->__lock);
1954                 s->busy++;
1955                 ast_mutex_unlock(&s->__lock);
1956                 /* XXX should we protect the list navigation ? */
1957                 for (tmp = first_action ; tmp; tmp = tmp->next) {               
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                 }
1968                 if (!tmp)
1969                         astman_send_error(s, m, "Invalid/unknown command");
1970         }
1971         if (ret)
1972                 return ret;
1973         return process_events(s);
1974 }
1975
1976 static int get_input(struct mansession *s, char *output)
1977 {
1978         /* output must have at least sizeof(s->inbuf) space */
1979         int res;
1980         int x;
1981         struct pollfd fds[1];
1982         for (x = 1; x < s->inlen; x++) {
1983                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1984                         /* Copy output data up to and including \r\n */
1985                         memcpy(output, s->inbuf, x + 1);
1986                         /* Add trailing \0 */
1987                         output[x+1] = '\0';
1988                         /* Move remaining data back to the front */
1989                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1990                         s->inlen -= (x + 1);
1991                         return 1;
1992                 }
1993         } 
1994         if (s->inlen >= sizeof(s->inbuf) - 1) {
1995                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
1996                 s->inlen = 0;
1997         }
1998         fds[0].fd = s->fd;
1999         fds[0].events = POLLIN;
2000         do {
2001                 ast_mutex_lock(&s->__lock);
2002                 s->waiting_thread = pthread_self();
2003                 ast_mutex_unlock(&s->__lock);
2004
2005                 res = poll(fds, 1, -1);
2006
2007                 ast_mutex_lock(&s->__lock);
2008                 s->waiting_thread = AST_PTHREADT_NULL;
2009                 ast_mutex_unlock(&s->__lock);
2010                 if (res < 0) {
2011                         if (errno == EINTR) {
2012                                 if (s->dead)
2013                                         return -1;
2014                                 return 0;
2015                         }
2016                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2017                         return -1;
2018                 } else if (res > 0) {
2019                         ast_mutex_lock(&s->__lock);
2020                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2021                         ast_mutex_unlock(&s->__lock);
2022                         if (res < 1)
2023                                 return -1;
2024                         break;
2025                 }
2026         } while(1);
2027         s->inlen += res;
2028         s->inbuf[s->inlen] = '\0';
2029         return 0;
2030 }
2031
2032 static void *session_do(void *data)
2033 {
2034         struct mansession *s = data;
2035         struct message m;
2036         int res;
2037         
2038         ast_mutex_lock(&s->__lock);
2039         astman_append(s, "Asterisk Call Manager/1.0\r\n");
2040         ast_mutex_unlock(&s->__lock);
2041         memset(&m, 0, sizeof(m));
2042         for (;;) {
2043                 res = get_input(s, m.headers[m.hdrcount]);
2044                 if (res > 0) {
2045                         /* Strip trailing \r\n */
2046                         if (strlen(m.headers[m.hdrcount]) < 2)
2047                                 continue;
2048                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2049                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
2050                                 if (process_message(s, &m))
2051                                         break;
2052                                 memset(&m, 0, sizeof(m));
2053                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2054                                 m.hdrcount++;
2055                 } else if (res < 0) {
2056                         break;
2057                 } else if (s->eventq->next) {
2058                         if (process_events(s))
2059                                 break;
2060                 }
2061         }
2062         if (s->authenticated) {
2063                 if (option_verbose > 1) {
2064                         if (displayconnects) 
2065                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2066                 }
2067                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2068         } else {
2069                 if (option_verbose > 1) {
2070                         if (displayconnects)
2071                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2072                 }
2073                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2074         }
2075         destroy_session(s);
2076         return NULL;
2077 }
2078
2079 static void *accept_thread(void *ignore)
2080 {
2081         int as;
2082         struct sockaddr_in sin;
2083         socklen_t sinlen;
2084         struct eventqent *eqe;
2085         struct mansession *s;
2086         struct protoent *p;
2087         int arg = 1;
2088         int flags;
2089         pthread_attr_t attr;
2090         time_t now;
2091         struct pollfd pfds[1];
2092
2093         pthread_attr_init(&attr);
2094         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2095
2096         for (;;) {
2097                 time(&now);
2098                 AST_LIST_LOCK(&sessions);
2099                 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2100                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2101                                 AST_LIST_REMOVE_CURRENT(&sessions, list);
2102                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2103                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2104                                                 s->username, ast_inet_ntoa(s->sin.sin_addr));
2105                                 }
2106                                 free_session(s);
2107                                 break;  
2108                         }
2109                 }
2110                 AST_LIST_TRAVERSE_SAFE_END
2111                 /* Purge master event queue of old, unused events, but make sure we
2112                    always keep at least one in the queue */
2113                 eqe = master_eventq;
2114                 while (master_eventq->next && !master_eventq->usecount) {
2115                         eqe = master_eventq;
2116                         master_eventq = master_eventq->next;
2117                         free(eqe);
2118                 }
2119                 AST_LIST_UNLOCK(&sessions);
2120                 if (s)
2121                         ast_atomic_fetchadd_int(&num_sessions, -1);
2122
2123                 sinlen = sizeof(sin);
2124                 pfds[0].fd = asock;
2125                 pfds[0].events = POLLIN;
2126                 /* Wait for something to happen, but timeout every few seconds so
2127                    we can ditch any old manager sessions */
2128                 if (poll(pfds, 1, 5000) < 1)
2129                         continue;
2130                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2131                 if (as < 0) {
2132                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2133                         continue;
2134                 }
2135                 p = getprotobyname("tcp");
2136                 if (p) {
2137                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2138                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2139                         }
2140                 }
2141                 if (!(s = ast_calloc(1, sizeof(*s))))
2142                         continue;
2143
2144                 ast_atomic_fetchadd_int(&num_sessions, 1);
2145                 
2146                 memcpy(&s->sin, &sin, sizeof(sin));
2147                 s->writetimeout = 100;
2148                 s->waiting_thread = AST_PTHREADT_NULL;
2149
2150                 if (!block_sockets) {
2151                         /* For safety, make sure socket is non-blocking */
2152                         flags = fcntl(as, F_GETFL);
2153                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
2154                 } else {
2155                         flags = fcntl(as, F_GETFL);
2156                         fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2157                 }
2158                 ast_mutex_init(&s->__lock);
2159                 s->fd = as;
2160                 s->send_events = -1;
2161                 AST_LIST_LOCK(&sessions);
2162                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2163                 /* Find the last place in the master event queue and hook ourselves
2164                    in there */
2165                 s->eventq = master_eventq;
2166                 while(s->eventq->next)
2167                         s->eventq = s->eventq->next;
2168                 AST_LIST_UNLOCK(&sessions);
2169                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2170                 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2171                         destroy_session(s);
2172         }
2173         pthread_attr_destroy(&attr);
2174         return NULL;
2175 }
2176
2177 static int append_event(const char *str, int category)
2178 {
2179         struct eventqent *tmp, *prev = NULL;
2180         tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2181
2182         if (!tmp)
2183                 return -1;
2184
2185         tmp->next = NULL;
2186         tmp->category = category;
2187         strcpy(tmp->eventdata, str);
2188         
2189         if (master_eventq) {
2190                 prev = master_eventq;
2191                 while (prev->next) 
2192                         prev = prev->next;
2193                 prev->next = tmp;
2194         } else {
2195                 master_eventq = tmp;
2196         }
2197         
2198         tmp->usecount = num_sessions;
2199         
2200         return 0;
2201 }
2202
2203 /*! \brief  manager_event: Send AMI event to client */
2204 int manager_event(int category, const char *event, const char *fmt, ...)
2205 {
2206         struct mansession *s;
2207         char auth[80];
2208         va_list ap;
2209         struct timeval now;
2210         struct ast_dynamic_str *buf;
2211
2212         /* Abort if there aren't any manager sessions */
2213         if (!num_sessions)
2214                 return 0;
2215
2216         if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2217                 return -1;
2218
2219         ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2220                         "Event: %s\r\nPrivilege: %s\r\n",
2221                          event, authority_to_str(category, auth, sizeof(auth)));
2222
2223         if (timestampevents) {
2224                 now = ast_tvnow();
2225                 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2226                                 "Timestamp: %ld.%06lu\r\n",
2227                                  now.tv_sec, (unsigned long) now.tv_usec);
2228         }
2229
2230         va_start(ap, fmt);
2231         ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2232         va_end(ap);
2233         
2234         ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");     
2235         
2236         /* Append event to master list and wake up any sleeping sessions */
2237         AST_LIST_LOCK(&sessions);
2238         append_event(buf->str, category);
2239         AST_LIST_TRAVERSE(&sessions, s, list) {
2240                 ast_mutex_lock(&s->__lock);
2241                 if (s->waiting_thread != AST_PTHREADT_NULL)
2242                         pthread_kill(s->waiting_thread, SIGURG);
2243                 ast_mutex_unlock(&s->__lock);
2244         }
2245         AST_LIST_UNLOCK(&sessions);
2246
2247         return 0;
2248 }
2249
2250 int ast_manager_unregister(char *action) 
2251 {
2252         struct manager_action *cur = first_action, *prev = first_action;
2253
2254         ast_mutex_lock(&actionlock);
2255         for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2256                 if (!strcasecmp(action, cur->action)) {
2257                         if (prev)
2258                                 prev->next = cur->next;
2259                         else
2260                                 first_action = cur->next;
2261                         free(cur);
2262                         if (option_verbose > 1) 
2263                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2264                         break;
2265                 }
2266         }
2267         ast_mutex_unlock(&actionlock);
2268         return 0;
2269 }
2270
2271 static int manager_state_cb(char *context, char *exten, int state, void *data)
2272 {
2273         /* Notify managers of change */
2274         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2275         return 0;
2276 }
2277
2278 static int ast_manager_register_struct(struct manager_action *act)
2279 {
2280         struct manager_action *cur, *prev = NULL;
2281         int ret;
2282
2283         ast_mutex_lock(&actionlock);
2284         for (cur = first_action; cur; prev = cur, cur = cur->next) {
2285                 ret = strcasecmp(cur->action, act->action);
2286                 if (ret == 0) {
2287                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2288                         ast_mutex_unlock(&actionlock);
2289                         return -1;
2290                 }
2291                 if (ret > 0)    /* Insert these alphabetically */
2292                         break;
2293         }
2294         if (prev)
2295                 prev->next = act;
2296         else
2297                 first_action = act;
2298         act->next = cur;
2299
2300         if (option_verbose > 1) 
2301                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2302         ast_mutex_unlock(&actionlock);
2303         return 0;
2304 }
2305
2306 /*! \brief register a new command with manager, including online help. This is 
2307         the preferred way to register a manager command */
2308 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2309 {
2310         struct manager_action *cur;
2311
2312         cur = ast_malloc(sizeof(*cur));
2313         if (!cur)
2314                 return -1;
2315         
2316         cur->action = action;
2317         cur->authority = auth;
2318         cur->func = func;
2319         cur->synopsis = synopsis;
2320         cur->description = description;
2321         cur->next = NULL;
2322
2323         ast_manager_register_struct(cur);
2324
2325         return 0;
2326 }
2327 /*! @}
2328  END Doxygen group */
2329
2330 static struct mansession *find_session(unsigned long ident)
2331 {
2332         struct mansession *s;
2333
2334         AST_LIST_LOCK(&sessions);
2335         AST_LIST_TRAVERSE(&sessions, s, list) {
2336                 ast_mutex_lock(&s->__lock);
2337                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2338                         s->inuse++;
2339                         break;
2340                 }
2341                 ast_mutex_unlock(&s->__lock);
2342         }
2343         AST_LIST_UNLOCK(&sessions);
2344
2345         return s;
2346 }
2347
2348
2349 static void vars2msg(struct message *m, struct ast_variable *vars)
2350 {
2351         int x;
2352         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2353                 if (!vars)
2354                         break;
2355                 m->hdrcount = x + 1;
2356                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2357         }
2358 }
2359
2360 enum {
2361         FORMAT_RAW,
2362         FORMAT_HTML,
2363         FORMAT_XML,
2364 };
2365 static char *contenttype[] = { "plain", "html", "xml" };
2366
2367 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2368 {
2369         struct mansession *s = NULL;
2370         unsigned long ident = 0;
2371         char workspace[512];
2372         char cookie[128];
2373         size_t len = sizeof(workspace);
2374         int blastaway = 0;
2375         char *c = workspace;
2376         char *retval = NULL;
2377         struct message m;
2378         struct ast_variable *v;
2379
2380         for (v = params; v; v = v->next) {
2381                 if (!strcasecmp(v->name, "mansession_id")) {
2382                         sscanf(v->value, "%lx", &ident);
2383                         ast_verbose("session is <%lx>\n", ident);
2384                         break;
2385                 }
2386         }
2387         
2388         if (!(s = find_session(ident))) {
2389                 /* Create new session */
2390                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2391                         *status = 500;
2392                         goto generic_callback_out;
2393                 }
2394                 memcpy(&s->sin, requestor, sizeof(s->sin));
2395                 s->fd = -1;
2396                 s->waiting_thread = AST_PTHREADT_NULL;
2397                 s->send_events = 0;
2398                 ast_mutex_init(&s->__lock);
2399                 ast_mutex_lock(&s->__lock);
2400                 s->inuse = 1;
2401                 s->managerid = rand() | (unsigned long)s;
2402                 AST_LIST_LOCK(&sessions);
2403                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2404                 /* Hook into the last spot in the event queue */
2405                 s->eventq = master_eventq;
2406                 while (s->eventq->next)
2407                         s->eventq = s->eventq->next;
2408                 AST_LIST_UNLOCK(&sessions);
2409                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2410                 ast_atomic_fetchadd_int(&num_sessions, 1);
2411         }
2412
2413         /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
2414         time(&s->sessiontimeout);
2415         if (!s->authenticated && (httptimeout > 5))
2416                 s->sessiontimeout += 5;
2417         else
2418                 s->sessiontimeout += httptimeout;
2419         ast_mutex_unlock(&s->__lock);
2420         
2421         memset(&m, 0, sizeof(m));
2422         {
2423                 char tmp[80];
2424                 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2425                 sprintf(tmp, "%08lx", s->managerid);
2426                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2427                 if (format == FORMAT_HTML)
2428                         ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2429                 vars2msg(&m, params);
2430                 if (format == FORMAT_XML) {
2431                         ast_build_string(&c, &len, "<ajax-response>\n");
2432                 } else if (format == FORMAT_HTML) {
2433                         ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2434                         ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2435                 }
2436                 if (process_message(s, &m)) {
2437                         if (s->authenticated) {
2438                                 if (option_verbose > 1) {
2439                                         if (displayconnects) 
2440                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
2441                                 }
2442                                 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2443                         } else {
2444                                 if (option_verbose > 1) {
2445                                         if (displayconnects)
2446                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2447                                 }
2448                                 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2449                         }
2450                         s->needdestroy = 1;
2451                 }
2452                 if (s->outputstr) {
2453                         char *tmp;
2454                         if (format == FORMAT_XML)
2455                                 tmp = xml_translate(s->outputstr->str, params);
2456                         else if (format == FORMAT_HTML)
2457                                 tmp = html_translate(s->outputstr->str);
2458                         else
2459                                 tmp = s->outputstr->str;
2460                         if (tmp) {
2461                                 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2462                                 if (retval) {
2463                                         strcpy(retval, workspace);
2464                                         strcpy(retval + strlen(retval), tmp);
2465                                         c = retval + strlen(retval);
2466                                         len = 120;
2467                                 }
2468                         }
2469                         if (tmp != s->outputstr->str)
2470                                 free(tmp);
2471                         free(s->outputstr);
2472                         s->outputstr = NULL;
2473                 }
2474                 /* Still okay because c would safely be pointing to workspace even
2475                    if retval failed to allocate above */
2476                 if (format == FORMAT_XML) {
2477                         ast_build_string(&c, &len, "</ajax-response>\n");
2478                 } else if (format == FORMAT_HTML)
2479                         ast_build_string(&c, &len, "</table></body>\r\n");
2480         }
2481         ast_mutex_lock(&s->__lock);
2482         if (s->needdestroy) {
2483                 if (s->inuse == 1) {
2484                         if (option_debug)
2485                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2486                         blastaway = 1;
2487                 } else {
2488                         if (option_debug)
2489                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2490                         if (s->waiting_thread != AST_PTHREADT_NULL)
2491                                 pthread_kill(s->waiting_thread, SIGURG);
2492                         s->inuse--;
2493                 }
2494         } else
2495                 s->inuse--;
2496         ast_mutex_unlock(&s->__lock);
2497         
2498         if (blastaway)
2499                 destroy_session(s);
2500 generic_callback_out:
2501         if (*status != 200)
2502                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
2503         return retval;
2504 }
2505
2506 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2507 {
2508         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2509 }
2510
2511 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2512 {
2513         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2514 }
2515
2516 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2517 {
2518         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2519 }
2520
2521 struct ast_http_uri rawmanuri = {
2522         .description = "Raw HTTP Manager Event Interface",
2523         .uri = "rawman",
2524         .has_subtree = 0,
2525         .callback = rawman_http_callback,
2526 };
2527
2528 struct ast_http_uri manageruri = {
2529         .description = "HTML Manager Event Interface",
2530         .uri = "manager",
2531         .has_subtree = 0,
2532         .callback = manager_http_callback,
2533 };
2534
2535 struct ast_http_uri managerxmluri = {
2536         .description = "XML Manager Event Interface",
2537         .uri = "mxml",
2538         .has_subtree = 0,
2539         .callback = mxml_http_callback,
2540 };
2541
2542 static int registered = 0;
2543 static int webregged = 0;
2544
2545 int init_manager(void)
2546 {
2547         struct ast_config *cfg = NULL;
2548         const char *val;
2549         char *cat = NULL;
2550         int oldportno = portno;
2551         static struct sockaddr_in ba;
2552         int x = 1;
2553         int flags;
2554         int webenabled = 0;
2555         int newhttptimeout = 60;
2556         struct ast_manager_user *user = NULL;
2557
2558         if (!registered) {
2559                 /* Register default actions */
2560                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2561                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2562                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2563                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2564                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2565                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2566                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2567                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2568                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2569                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2570                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2571                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2572                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2573                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2574                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2575                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2576                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2577                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2578                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2579
2580                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2581                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2582                 registered = 1;
2583                 /* Append placeholder event so master_eventq never runs dry */
2584                 append_event("Event: Placeholder\r\n\r\n", 0);
2585         }
2586         portno = DEFAULT_MANAGER_PORT;
2587         displayconnects = 1;
2588         cfg = ast_config_load("manager.conf");
2589         if (!cfg) {
2590                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2591                 return 0;
2592         }
2593         val = ast_variable_retrieve(cfg, "general", "enabled");
2594         if (val)
2595                 enabled = ast_true(val);
2596
2597         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2598         if (val)
2599                 block_sockets = ast_true(val);
2600
2601         val = ast_variable_retrieve(cfg, "general", "webenabled");
2602         if (val)
2603                 webenabled = ast_true(val);
2604
2605         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2606                 if (sscanf(val, "%d", &portno) != 1) {
2607                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2608                         portno = DEFAULT_MANAGER_PORT;
2609                 }
2610         }
2611
2612         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2613                 displayconnects = ast_true(val);
2614
2615         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2616                 timestampevents = ast_true(val);
2617
2618         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2619                 newhttptimeout = atoi(val);
2620
2621         memset(&ba, 0, sizeof(ba));
2622         ba.sin_family = AF_INET;
2623         ba.sin_port = htons(portno);
2624
2625         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2626                 if (!inet_aton(val, &ba.sin_addr)) { 
2627                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2628                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2629                 }
2630         }
2631         
2632
2633         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2634 #if 0
2635                 /* Can't be done yet */
2636                 close(asock);
2637                 asock = -1;
2638 #else
2639                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2640 #endif
2641         }
2642
2643         AST_LIST_LOCK(&users);
2644
2645         while ((cat = ast_category_browse(cfg, cat))) {
2646                 struct ast_variable *var = NULL;
2647
2648                 if (!strcasecmp(cat, "general"))
2649                         continue;
2650
2651                 /* Look for an existing entry, if none found - create one and add it to the list */
2652                 if (!(user = ast_get_manager_by_name_locked(cat))) {
2653                         if (!(user = ast_calloc(1, sizeof(*user))))
2654                                 break;
2655                         /* Copy name over */
2656                         ast_copy_string(user->username, cat, sizeof(user->username));
2657                         /* Insert into list */
2658                         AST_LIST_INSERT_TAIL(&users, user, list);
2659                 }
2660
2661                 /* Make sure we keep this user and don't destroy it during cleanup */
2662                 user->keep = 1;
2663
2664                 var = ast_variable_browse(cfg, cat);
2665                 while (var) {
2666                         if (!strcasecmp(var->name, "secret")) {
2667                                 if (user->secret)
2668                                         free(user->secret);
2669                                 user->secret = ast_strdup(var->value);
2670                         } else if (!strcasecmp(var->name, "deny") ) {
2671                                 if (user->deny)
2672                                         free(user->deny);
2673                                 user->deny = ast_strdup(var->value);
2674                         } else if (!strcasecmp(var->name, "permit") ) {
2675                                 if (user->permit)
2676                                         free(user->permit);
2677                                 user->permit = ast_strdup(var->value);
2678                         }  else if (!strcasecmp(var->name, "read") ) {
2679                                 if (user->read)
2680                                         free(user->read);
2681                                 user->read = ast_strdup(var->value);
2682                         }  else if (!strcasecmp(var->name, "write") ) {
2683                                 if (user->write)
2684                                         free(user->write);
2685                                 user->write = ast_strdup(var->value);
2686                         }  else if (!strcasecmp(var->name, "displayconnects") )
2687                                 user->displayconnects = ast_true(var->value);
2688                         else {
2689                                 if (option_debug)
2690                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2691                         }
2692                         var = var->next;
2693                 }
2694         }
2695
2696         /* Perform cleanup - essentially prune out old users that no longer exist */
2697         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2698                 if (user->keep) {
2699                         user->keep = 0;
2700                         continue;
2701                 }
2702                 /* We do not need to keep this user so take them out of the list */
2703                 AST_LIST_REMOVE_CURRENT(&users, list);
2704                 /* Free their memory now */
2705                 if (user->secret)
2706                         free(user->secret);
2707                 if (user->deny)
2708                         free(user->deny);
2709                 if (user->permit)
2710                         free(user->permit);
2711                 if (user->read)
2712                         free(user->read);
2713                 if (user->write)
2714                         free(user->write);
2715                 free(user);
2716         }
2717         AST_LIST_TRAVERSE_SAFE_END
2718
2719         AST_LIST_UNLOCK(&users);
2720
2721         ast_config_destroy(cfg);
2722         
2723         if (webenabled && enabled) {
2724                 if (!webregged) {
2725                         ast_http_uri_link(&rawmanuri);
2726                         ast_http_uri_link(&manageruri);
2727                         ast_http_uri_link(&managerxmluri);
2728                         webregged = 1;
2729                 }
2730         } else {
2731                 if (webregged) {
2732                         ast_http_uri_unlink(&rawmanuri);
2733                         ast_http_uri_unlink(&manageruri);
2734                         ast_http_uri_unlink(&managerxmluri);
2735                         webregged = 0;
2736                 }
2737         }
2738
2739         if (newhttptimeout > 0)
2740                 httptimeout = newhttptimeout;
2741
2742         /* If not enabled, do nothing */
2743         if (!enabled)
2744                 return 0;
2745
2746         if (asock < 0) {
2747                 asock = socket(AF_INET, SOCK_STREAM, 0);
2748                 if (asock < 0) {
2749                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2750                         return -1;
2751                 }
2752                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2753                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2754                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2755                         close(asock);
2756                         asock = -1;
2757                         return -1;
2758                 }
2759                 if (listen(asock, 2)) {
2760                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2761                         close(asock);
2762                         asock = -1;
2763                         return -1;
2764                 }
2765                 flags = fcntl(asock, F_GETFL);
2766                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2767                 if (option_verbose)
2768                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2769                 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2770         }
2771         return 0;
2772 }
2773
2774 int reload_manager(void)
2775 {
2776         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2777         return init_manager();
2778 }