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