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