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