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