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