128f9d92da4fb23ff37fa693a4987eee684f0351
[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         int error = -1;
856         struct ast_ha *ha = NULL;
857         char *password = NULL;
858         int readperm = 0, writeperm = 0;
859
860         if (ast_strlen_zero(user))      /* missing username */
861                 return -1;
862
863     {
864         /*
865          * XXX there should be no need to scan the config file again here,
866          * suffices to call ast_get_manager_by_name_locked() to fetch
867          * the user's entry.
868          */
869         struct ast_config *cfg = ast_config_load("manager.conf");
870         char *cat = NULL;
871         struct ast_variable *v;
872
873         if (!cfg)
874                 return -1;
875         while ( (cat = ast_category_browse(cfg, cat)) ) {
876                 /* "general" is not a valid user */
877                 if (!strcasecmp(cat, user) && strcasecmp(cat, "general"))
878                         break;
879         }
880         if (!cat) {
881                 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
882                 ast_config_destroy(cfg);
883                 return -1;
884         }
885
886         /* collect parameters for the user's entry */
887         for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
888                 if (!strcasecmp(v->name, "secret"))
889                         password = ast_strdupa(v->value);
890                 else if (!strcasecmp(v->name, "read"))
891                         readperm = get_perm(v->value);
892                 else if (!strcasecmp(v->name, "write"))
893                         writeperm = get_perm(v->value);
894                 else if (!strcasecmp(v->name, "permit") ||
895                            !strcasecmp(v->name, "deny")) {
896                         ha = ast_append_ha(v->name, v->value, ha);
897                 } else if (!strcasecmp(v->name, "writetimeout")) {
898                         int val = atoi(v->value);
899
900                         if (val < 100)
901                                 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
902                         else
903                                 s->writetimeout = val;
904                 }
905         }
906         ast_config_destroy(cfg);
907     }
908
909         if (ha) {
910                 int good = ast_apply_ha(ha, &(s->sin));
911                 ast_free_ha(ha);
912                 if (!good) {
913                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
914                         return -1;
915                 }
916         }
917         if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
918                 char *key = astman_get_header(m, "Key");
919                 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge)) {
920                         int x;
921                         int len = 0;
922                         char md5key[256] = "";
923                         struct MD5Context md5;
924                         unsigned char digest[16];
925
926                         MD5Init(&md5);
927                         MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
928                         MD5Update(&md5, (unsigned char *) password, strlen(password));
929                         MD5Final(digest, &md5);
930                         for (x=0; x<16; x++)
931                                 len += sprintf(md5key + len, "%2.2x", digest[x]);
932                         if (!strcmp(md5key, key))
933                                 error = 0;
934                 }
935         } else if (password) {
936                 char *pass = astman_get_header(m, "Secret");
937                 if (!strcmp(password, pass))
938                         error = 0;
939         }
940         if (error) {
941                 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
942                 return -1;
943         }
944         ast_copy_string(s->username, user, sizeof(s->username));
945         s->readperm = readperm;
946         s->writeperm = writeperm;
947         set_eventmask(s, astman_get_header(m, "Events"));
948         return 0;
949 }
950
951 /*! \brief Manager PING */
952 static char mandescr_ping[] = 
953 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
954 "  manager connection open.\n"
955 "Variables: NONE\n";
956
957 static int action_ping(struct mansession *s, struct message *m)
958 {
959         astman_send_response(s, m, "Pong", NULL);
960         return 0;
961 }
962
963 static char mandescr_getconfig[] =
964 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
965 "file by category and contents.\n"
966 "Variables:\n"
967 "   Filename: Configuration filename (e.g. foo.conf)\n";
968
969 static int action_getconfig(struct mansession *s, struct message *m)
970 {
971         struct ast_config *cfg;
972         char *fn = astman_get_header(m, "Filename");
973         int catcount = 0;
974         int lineno = 0;
975         char *category=NULL;
976         struct ast_variable *v;
977         char idText[256] = "";
978         char *id = astman_get_header(m, "ActionID");
979
980         if (!ast_strlen_zero(id))
981                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
982
983         if (ast_strlen_zero(fn)) {
984                 astman_send_error(s, m, "Filename not specified");
985                 return 0;
986         }
987         if (!(cfg = ast_config_load(fn))) {
988                 astman_send_error(s, m, "Config file not found");
989                 return 0;
990         }
991         astman_append(s, "Response: Success\r\n%s", idText);
992         while ((category = ast_category_browse(cfg, category))) {
993                 lineno = 0;
994                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
995                 for (v = ast_variable_browse(cfg, category); v; v = v->next)
996                         astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
997                 catcount++;
998         }
999         ast_config_destroy(cfg);
1000         astman_append(s, "\r\n");
1001
1002         return 0;
1003 }
1004
1005
1006 static void handle_updates(struct mansession *s, struct message *m, struct ast_config *cfg)
1007 {
1008         int x;
1009         char hdr[40];
1010         char *action, *cat, *var, *value, *match;
1011         struct ast_category *category;
1012         struct ast_variable *v;
1013         
1014         for (x=0;x<100000;x++) {
1015                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1016                 action = astman_get_header(m, hdr);
1017                 if (ast_strlen_zero(action))
1018                         break;
1019                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1020                 cat = astman_get_header(m, hdr);
1021                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1022                 var = astman_get_header(m, hdr);
1023                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1024                 value = astman_get_header(m, hdr);
1025                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1026                 match = astman_get_header(m, hdr);
1027                 if (!strcasecmp(action, "newcat")) {
1028                         if (!ast_strlen_zero(cat)) {
1029                                 category = ast_category_new(cat);
1030                                 if (category) {
1031                                         ast_category_append(cfg, category);
1032                                 }
1033                         }
1034                 } else if (!strcasecmp(action, "renamecat")) {
1035                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1036                                 category = ast_category_get(cfg, cat);
1037                                 if (category) 
1038                                         ast_category_rename(category, value);
1039                         }
1040                 } else if (!strcasecmp(action, "delcat")) {
1041                         if (!ast_strlen_zero(cat))
1042                                 ast_category_delete(cfg, cat);
1043                 } else if (!strcasecmp(action, "update")) {
1044                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1045                                 ast_variable_update(category, var, value, match);
1046                 } else if (!strcasecmp(action, "delete")) {
1047                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1048                                 ast_variable_delete(category, var, match);
1049                 } else if (!strcasecmp(action, "append")) {
1050                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && 
1051                                 (category = ast_category_get(cfg, cat)) && 
1052                                 (v = ast_variable_new(var, value))){
1053                                 if (match && !strcasecmp(match, "object"))
1054                                         v->object = 1;
1055                                 ast_variable_append(category, v);
1056                         }
1057                 }
1058         }
1059 }
1060
1061 static char mandescr_updateconfig[] =
1062 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1063 "file by category and contents.\n"
1064 "Variables (X's represent 6 digit number beginning with 000000):\n"
1065 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1066 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1067 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1068 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1069 "   Cat-XXXXXX:    Category to operate on\n"
1070 "   Var-XXXXXX:    Variable to work on\n"
1071 "   Value-XXXXXX:  Value to work on\n"
1072 "   Match-XXXXXX:  Extra match required to match line\n";
1073
1074 static int action_updateconfig(struct mansession *s, struct message *m)
1075 {
1076         struct ast_config *cfg;
1077         char *sfn = astman_get_header(m, "SrcFilename");
1078         char *dfn = astman_get_header(m, "DstFilename");
1079         int res;
1080         char idText[256] = "";
1081         char *id = astman_get_header(m, "ActionID");
1082         char *rld = astman_get_header(m, "Reload");
1083
1084         if (!ast_strlen_zero(id))
1085                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1086
1087         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1088                 astman_send_error(s, m, "Filename not specified");
1089                 return 0;
1090         }
1091         if (!(cfg = ast_config_load(sfn))) {
1092                 astman_send_error(s, m, "Config file not found");
1093                 return 0;
1094         }
1095         handle_updates(s, m, cfg);
1096         res = config_text_file_save(dfn, cfg, "Manager");
1097         ast_config_destroy(cfg);
1098         if (res) {
1099                 astman_send_error(s, m, "Save of config failed");
1100                 return 0;
1101         }
1102         astman_append(s, "Response: Success\r\n%s\r\n", idText);
1103         if (!ast_strlen_zero(rld)) {
1104                 if (ast_true(rld))
1105                         rld = NULL;
1106                 ast_module_reload(rld); 
1107         }
1108         return 0;
1109 }
1110
1111 /*! \brief Manager WAITEVENT */
1112 static char mandescr_waitevent[] = 
1113 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1114 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1115 "session, events will be generated and queued.\n"
1116 "Variables: \n"
1117 "   Timeout: Maximum time to wait for events\n";
1118
1119 static int action_waitevent(struct mansession *s, struct message *m)
1120 {
1121         char *timeouts = astman_get_header(m, "Timeout");
1122         int timeout = -1, max;
1123         int x;
1124         int needexit = 0;
1125         time_t now;
1126         struct eventqent *eqe;
1127         char *id = astman_get_header(m,"ActionID");
1128         char idText[256] = "";
1129
1130         if (!ast_strlen_zero(id))
1131                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1132
1133         if (!ast_strlen_zero(timeouts)) {
1134                 sscanf(timeouts, "%i", &timeout);
1135         }
1136         
1137         ast_mutex_lock(&s->__lock);
1138         if (s->waiting_thread != AST_PTHREADT_NULL) {
1139                 pthread_kill(s->waiting_thread, SIGURG);
1140         }
1141         if (s->sessiontimeout) {
1142                 time(&now);
1143                 max = s->sessiontimeout - now - 10;
1144                 if (max < 0)
1145                         max = 0;
1146                 if ((timeout < 0) || (timeout > max))
1147                         timeout = max;
1148                 if (!s->send_events)
1149                         s->send_events = -1;
1150                 /* Once waitevent is called, always queue events from now on */
1151                 if (s->busy == 1)
1152                         s->busy = 2;
1153         }
1154         ast_mutex_unlock(&s->__lock);
1155         s->waiting_thread = pthread_self();
1156         if (option_debug)
1157                 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1158         for (x=0; ((x < timeout) || (timeout < 0)); x++) {
1159                 ast_mutex_lock(&s->__lock);
1160                 if (s->eventq && s->eventq->next)
1161                         needexit = 1;
1162                 if (s->waiting_thread != pthread_self())
1163                         needexit = 1;
1164                 if (s->needdestroy)
1165                         needexit = 1;
1166                 ast_mutex_unlock(&s->__lock);
1167                 if (needexit)
1168                         break;
1169                 if (!s->inuse && s->fd > 0) {
1170                         if (ast_wait_for_input(s->fd, 1000))
1171                                 break;
1172                 } else {
1173                         sleep(1);
1174                 }
1175         }
1176         if (option_debug)
1177                 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1178         ast_mutex_lock(&s->__lock);
1179         if (s->waiting_thread == pthread_self()) {
1180                 astman_send_response(s, m, "Success", "Waiting for Event...");
1181                 /* Only show events if we're the most recent waiter */
1182                 while(s->eventq->next) {
1183                         eqe = s->eventq->next;
1184                         if (((s->readperm & eqe->category) == eqe->category) &&
1185                             ((s->send_events & eqe->category) == eqe->category)) {
1186                                 astman_append(s, "%s", eqe->eventdata);
1187                         }
1188                         unuse_eventqent(s->eventq);
1189                         s->eventq = eqe;
1190                 }
1191                 astman_append(s,
1192                         "Event: WaitEventComplete\r\n"
1193                         "%s"
1194                         "\r\n", idText);
1195                 s->waiting_thread = AST_PTHREADT_NULL;
1196         } else {
1197                 if (option_debug)
1198                         ast_log(LOG_DEBUG, "Abandoning event request!\n");
1199         }
1200         ast_mutex_unlock(&s->__lock);
1201         return 0;
1202 }
1203
1204 static char mandescr_listcommands[] = 
1205 "Description: Returns the action name and synopsis for every\n"
1206 "  action that is available to the user\n"
1207 "Variables: NONE\n";
1208
1209 static int action_listcommands(struct mansession *s, struct message *m)
1210 {
1211         struct manager_action *cur;
1212         char idText[256] = "";
1213         char temp[BUFSIZ];
1214         char *id = astman_get_header(m,"ActionID");
1215
1216         if (!ast_strlen_zero(id))
1217                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1218         astman_append(s, "Response: Success\r\n%s", idText);
1219         ast_mutex_lock(&actionlock);
1220         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
1221                 if ((s->writeperm & cur->authority) == cur->authority)
1222                         astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)));
1223         }
1224         ast_mutex_unlock(&actionlock);
1225         astman_append(s, "\r\n");
1226
1227         return 0;
1228 }
1229
1230 static char mandescr_events[] = 
1231 "Description: Enable/Disable sending of events to this manager\n"
1232 "  client.\n"
1233 "Variables:\n"
1234 "       EventMask: 'on' if all events should be sent,\n"
1235 "               'off' if no events should be sent,\n"
1236 "               'system,call,log' to select which flags events should have to be sent.\n";
1237
1238 static int action_events(struct mansession *s, struct message *m)
1239 {
1240         char *mask = astman_get_header(m, "EventMask");
1241         int res;
1242
1243         res = set_eventmask(s, mask);
1244         if (res > 0)
1245                 astman_send_response(s, m, "Events On", NULL);
1246         else if (res == 0)
1247                 astman_send_response(s, m, "Events Off", NULL);
1248
1249         return 0;
1250 }
1251
1252 static char mandescr_logoff[] = 
1253 "Description: Logoff this manager session\n"
1254 "Variables: NONE\n";
1255
1256 static int action_logoff(struct mansession *s, struct message *m)
1257 {
1258         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1259         return -1;
1260 }
1261
1262 static int action_login(struct mansession *s, struct message *m)
1263 {
1264         if (authenticate(s, m)) {
1265                 sleep(1);
1266                 astman_send_error(s, m, "Authentication failed");
1267                 return -1;
1268         }
1269         s->authenticated = 1;
1270         if (option_verbose > 1) {
1271                 if (displayconnects) {
1272                         ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1273                 }
1274         }
1275         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1276         astman_send_ack(s, m, "Authentication accepted");
1277         return 0;
1278 }
1279
1280 static int action_challenge(struct mansession *s, struct message *m)
1281 {
1282         char *authtype = astman_get_header(m, "AuthType");
1283
1284         if (!strcasecmp(authtype, "MD5")) {
1285                 char *id = astman_get_header(m,"ActionID");
1286                 if (ast_strlen_zero(s->challenge))
1287                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1288                 ast_mutex_lock(&s->__lock);
1289                 astman_append(s, "Response: Success\r\n");
1290                 if (!ast_strlen_zero(id))
1291                         astman_append(s, "ActionID: %s\r\n", id);
1292                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1293                 ast_mutex_unlock(&s->__lock);
1294         } else {
1295                 astman_send_error(s, m, "Must specify AuthType");
1296         }
1297         return 0;
1298 }
1299
1300 static char mandescr_hangup[] = 
1301 "Description: Hangup a channel\n"
1302 "Variables: \n"
1303 "       Channel: The channel name to be hungup\n";
1304
1305 static int action_hangup(struct mansession *s, struct message *m)
1306 {
1307         struct ast_channel *c = NULL;
1308         char *name = astman_get_header(m, "Channel");
1309         if (ast_strlen_zero(name)) {
1310                 astman_send_error(s, m, "No channel specified");
1311                 return 0;
1312         }
1313         c = ast_get_channel_by_name_locked(name);
1314         if (!c) {
1315                 astman_send_error(s, m, "No such channel");
1316                 return 0;
1317         }
1318         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1319         ast_channel_unlock(c);
1320         astman_send_ack(s, m, "Channel Hungup");
1321         return 0;
1322 }
1323
1324 static char mandescr_setvar[] = 
1325 "Description: Set a global or local channel variable.\n"
1326 "Variables: (Names marked with * are required)\n"
1327 "       Channel: Channel to set variable for\n"
1328 "       *Variable: Variable name\n"
1329 "       *Value: Value\n";
1330
1331 static int action_setvar(struct mansession *s, struct message *m)
1332 {
1333         struct ast_channel *c = NULL;
1334         char *name = astman_get_header(m, "Channel");
1335         char *varname = astman_get_header(m, "Variable");
1336         char *varval = astman_get_header(m, "Value");
1337         
1338         if (ast_strlen_zero(varname)) {
1339                 astman_send_error(s, m, "No variable specified");
1340                 return 0;
1341         }
1342         
1343         if (ast_strlen_zero(varval)) {
1344                 astman_send_error(s, m, "No value specified");
1345                 return 0;
1346         }
1347
1348         if (!ast_strlen_zero(name)) {
1349                 c = ast_get_channel_by_name_locked(name);
1350                 if (!c) {
1351                         astman_send_error(s, m, "No such channel");
1352                         return 0;
1353                 }
1354         }
1355         
1356         pbx_builtin_setvar_helper(c, varname, varval);
1357           
1358         if (c)
1359                 ast_channel_unlock(c);
1360
1361         astman_send_ack(s, m, "Variable Set");  
1362
1363         return 0;
1364 }
1365
1366 static char mandescr_getvar[] = 
1367 "Description: Get the value of a global or local channel variable.\n"
1368 "Variables: (Names marked with * are required)\n"
1369 "       Channel: Channel to read variable from\n"
1370 "       *Variable: Variable name\n"
1371 "       ActionID: Optional Action id for message matching.\n";
1372
1373 static int action_getvar(struct mansession *s, struct message *m)
1374 {
1375         struct ast_channel *c = NULL;
1376         char *name = astman_get_header(m, "Channel");
1377         char *varname = astman_get_header(m, "Variable");
1378         char *id = astman_get_header(m,"ActionID");
1379         char *varval;
1380         char workspace[1024];
1381
1382         if (ast_strlen_zero(varname)) {
1383                 astman_send_error(s, m, "No variable specified");
1384                 return 0;
1385         }
1386
1387         if (!ast_strlen_zero(name)) {
1388                 c = ast_get_channel_by_name_locked(name);
1389                 if (!c) {
1390                         astman_send_error(s, m, "No such channel");
1391                         return 0;
1392                 }
1393         }
1394
1395         if (varname[strlen(varname) - 1] == ')') {
1396                 ast_func_read(c, varname, workspace, sizeof(workspace));
1397         } else {
1398                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1399         }
1400
1401         if (c)
1402                 ast_channel_unlock(c);
1403         astman_append(s, "Response: Success\r\n"
1404                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
1405         if (!ast_strlen_zero(id))
1406                 astman_append(s, "ActionID: %s\r\n",id);
1407         astman_append(s, "\r\n");
1408
1409         return 0;
1410 }
1411
1412
1413 /*! \brief Manager "status" command to show channels */
1414 /* Needs documentation... */
1415 static int action_status(struct mansession *s, struct message *m)
1416 {
1417         char *id = astman_get_header(m,"ActionID");
1418         char *name = astman_get_header(m,"Channel");
1419         char idText[256] = "";
1420         struct ast_channel *c;
1421         char bridge[256];
1422         struct timeval now = ast_tvnow();
1423         long elapsed_seconds = 0;
1424         int all = ast_strlen_zero(name); /* set if we want all channels */
1425
1426         astman_send_ack(s, m, "Channel status will follow");
1427         if (!ast_strlen_zero(id))
1428                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1429         if (all)
1430                 c = ast_channel_walk_locked(NULL);
1431         else {
1432                 c = ast_get_channel_by_name_locked(name);
1433                 if (!c) {
1434                         astman_send_error(s, m, "No such channel");
1435                         return 0;
1436                 }
1437         }
1438         /* if we look by name, we break after the first iteration */
1439         while (c) {
1440                 if (c->_bridge)
1441                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1442                 else
1443                         bridge[0] = '\0';
1444                 if (c->pbx) {
1445                         if (c->cdr) {
1446                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1447                         }
1448                         astman_append(s,
1449                         "Event: Status\r\n"
1450                         "Privilege: Call\r\n"
1451                         "Channel: %s\r\n"
1452                         "CallerIDNum: %s\r\n"
1453                         "CallerIDName: %s\r\n"
1454                         "Account: %s\r\n"
1455                         "State: %s\r\n"
1456                         "Context: %s\r\n"
1457                         "Extension: %s\r\n"
1458                         "Priority: %d\r\n"
1459                         "Seconds: %ld\r\n"
1460                         "%s"
1461                         "Uniqueid: %s\r\n"
1462                         "%s"
1463                         "\r\n",
1464                         c->name, 
1465                         S_OR(c->cid.cid_num, "<unknown>"), 
1466                         S_OR(c->cid.cid_name, "<unknown>"), 
1467                         c->accountcode,
1468                         ast_state2str(c->_state), c->context,
1469                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1470                 } else {
1471                         astman_append(s,
1472                         "Event: Status\r\n"
1473                         "Privilege: Call\r\n"
1474                         "Channel: %s\r\n"
1475                         "CallerIDNum: %s\r\n"
1476                         "CallerIDName: %s\r\n"
1477                         "Account: %s\r\n"
1478                         "State: %s\r\n"
1479                         "%s"
1480                         "Uniqueid: %s\r\n"
1481                         "%s"
1482                         "\r\n",
1483                         c->name, 
1484                         S_OR(c->cid.cid_num, "<unknown>"), 
1485                         S_OR(c->cid.cid_name, "<unknown>"), 
1486                         c->accountcode,
1487                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1488                 }
1489                 ast_channel_unlock(c);
1490                 if (!all)
1491                         break;
1492                 c = ast_channel_walk_locked(c);
1493         }
1494         astman_append(s,
1495         "Event: StatusComplete\r\n"
1496         "%s"
1497         "\r\n",idText);
1498         return 0;
1499 }
1500
1501 static char mandescr_redirect[] = 
1502 "Description: Redirect (transfer) a call.\n"
1503 "Variables: (Names marked with * are required)\n"
1504 "       *Channel: Channel to redirect\n"
1505 "       ExtraChannel: Second call leg to transfer (optional)\n"
1506 "       *Exten: Extension to transfer to\n"
1507 "       *Context: Context to transfer to\n"
1508 "       *Priority: Priority to transfer to\n"
1509 "       ActionID: Optional Action id for message matching.\n";
1510
1511 /*! \brief  action_redirect: The redirect manager command */
1512 static int action_redirect(struct mansession *s, struct message *m)
1513 {
1514         char *name = astman_get_header(m, "Channel");
1515         char *name2 = astman_get_header(m, "ExtraChannel");
1516         char *exten = astman_get_header(m, "Exten");
1517         char *context = astman_get_header(m, "Context");
1518         char *priority = astman_get_header(m, "Priority");
1519         struct ast_channel *chan, *chan2 = NULL;
1520         int pi = 0;
1521         int res;
1522
1523         if (ast_strlen_zero(name)) {
1524                 astman_send_error(s, m, "Channel not specified");
1525                 return 0;
1526         }
1527         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1528                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1529                         astman_send_error(s, m, "Invalid priority\n");
1530                         return 0;
1531                 }
1532         }
1533         /* XXX watch out, possible deadlock!!! */
1534         chan = ast_get_channel_by_name_locked(name);
1535         if (!chan) {
1536                 char buf[BUFSIZ];
1537                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1538                 astman_send_error(s, m, buf);
1539                 return 0;
1540         }
1541         if (!ast_strlen_zero(name2))
1542                 chan2 = ast_get_channel_by_name_locked(name2);
1543         res = ast_async_goto(chan, context, exten, pi);
1544         if (!res) {
1545                 if (!ast_strlen_zero(name2)) {
1546                         if (chan2)
1547                                 res = ast_async_goto(chan2, context, exten, pi);
1548                         else
1549                                 res = -1;
1550                         if (!res)
1551                                 astman_send_ack(s, m, "Dual Redirect successful");
1552                         else
1553                                 astman_send_error(s, m, "Secondary redirect failed");
1554                 } else
1555                         astman_send_ack(s, m, "Redirect successful");
1556         } else
1557                 astman_send_error(s, m, "Redirect failed");
1558         if (chan)
1559                 ast_channel_unlock(chan);
1560         if (chan2)
1561                 ast_channel_unlock(chan2);
1562         return 0;
1563 }
1564
1565 static char mandescr_command[] = 
1566 "Description: Run a CLI command.\n"
1567 "Variables: (Names marked with * are required)\n"
1568 "       *Command: Asterisk CLI command to run\n"
1569 "       ActionID: Optional Action id for message matching.\n";
1570
1571 /*! \brief  action_command: Manager command "command" - execute CLI command */
1572 static int action_command(struct mansession *s, struct message *m)
1573 {
1574         char *cmd = astman_get_header(m, "Command");
1575         char *id = astman_get_header(m, "ActionID");
1576         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1577         if (!ast_strlen_zero(id))
1578                 astman_append(s, "ActionID: %s\r\n", id);
1579         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1580         ast_cli_command(s->fd, cmd);
1581         astman_append(s, "--END COMMAND--\r\n\r\n");
1582         return 0;
1583 }
1584
1585 static void *fast_originate(void *data)
1586 {
1587         struct fast_originate_helper *in = data;
1588         int res;
1589         int reason = 0;
1590         struct ast_channel *chan = NULL;
1591
1592         if (!ast_strlen_zero(in->app)) {
1593                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
1594                         S_OR(in->cid_num, NULL), 
1595                         S_OR(in->cid_name, NULL),
1596                         in->vars, in->account, &chan);
1597         } else {
1598                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
1599                         S_OR(in->cid_num, NULL), 
1600                         S_OR(in->cid_name, NULL),
1601                         in->vars, in->account, &chan);
1602         }   
1603         
1604         /* Tell the manager what happened with the channel */
1605         manager_event(EVENT_FLAG_CALL,
1606                 res ? "OriginateFailure" : "OriginateSuccess",
1607                 "%s"
1608                 "Channel: %s/%s\r\n"
1609                 "Context: %s\r\n"
1610                 "Exten: %s\r\n"
1611                 "Reason: %d\r\n"
1612                 "Uniqueid: %s\r\n"
1613                 "CallerIDNum: %s\r\n"
1614                 "CallerIDName: %s\r\n",
1615                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
1616                 chan ? chan->uniqueid : "<null>",
1617                 S_OR(in->cid_num, "<unknown>"),
1618                 S_OR(in->cid_name, "<unknown>")
1619                 );
1620
1621         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1622         if (chan)
1623                 ast_channel_unlock(chan);
1624         free(in);
1625         return NULL;
1626 }
1627
1628 static char mandescr_originate[] = 
1629 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1630 "  Application/Data\n"
1631 "Variables: (Names marked with * are required)\n"
1632 "       *Channel: Channel name to call\n"
1633 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1634 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1635 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1636 "       Application: Application to use\n"
1637 "       Data: Data to use (requires 'Application')\n"
1638 "       Timeout: How long to wait for call to be answered (in ms)\n"
1639 "       CallerID: Caller ID to be set on the outgoing channel\n"
1640 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1641 "       Account: Account code\n"
1642 "       Async: Set to 'true' for fast origination\n";
1643
1644 static int action_originate(struct mansession *s, struct message *m)
1645 {
1646         char *name = astman_get_header(m, "Channel");
1647         char *exten = astman_get_header(m, "Exten");
1648         char *context = astman_get_header(m, "Context");
1649         char *priority = astman_get_header(m, "Priority");
1650         char *timeout = astman_get_header(m, "Timeout");
1651         char *callerid = astman_get_header(m, "CallerID");
1652         char *account = astman_get_header(m, "Account");
1653         char *app = astman_get_header(m, "Application");
1654         char *appdata = astman_get_header(m, "Data");
1655         char *async = astman_get_header(m, "Async");
1656         char *id = astman_get_header(m, "ActionID");
1657         struct ast_variable *vars = astman_get_variables(m);
1658         char *tech, *data;
1659         char *l = NULL, *n = NULL;
1660         int pi = 0;
1661         int res;
1662         int to = 30000;
1663         int reason = 0;
1664         char tmp[256];
1665         char tmp2[256];
1666         
1667         pthread_t th;
1668         pthread_attr_t attr;
1669         if (!name) {
1670                 astman_send_error(s, m, "Channel not specified");
1671                 return 0;
1672         }
1673         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1674                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1675                         astman_send_error(s, m, "Invalid priority\n");
1676                         return 0;
1677                 }
1678         }
1679         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1680                 astman_send_error(s, m, "Invalid timeout\n");
1681                 return 0;
1682         }
1683         ast_copy_string(tmp, name, sizeof(tmp));
1684         tech = tmp;
1685         data = strchr(tmp, '/');
1686         if (!data) {
1687                 astman_send_error(s, m, "Invalid channel\n");
1688                 return 0;
1689         }
1690         *data++ = '\0';
1691         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1692         ast_callerid_parse(tmp2, &n, &l);
1693         if (n) {
1694                 if (ast_strlen_zero(n))
1695                         n = NULL;
1696         }
1697         if (l) {
1698                 ast_shrink_phone_number(l);
1699                 if (ast_strlen_zero(l))
1700                         l = NULL;
1701         }
1702         if (ast_true(async)) {
1703                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1704                 if (!fast) {
1705                         res = -1;
1706                 } else {
1707                         if (!ast_strlen_zero(id))
1708                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1709                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1710                         ast_copy_string(fast->data, data, sizeof(fast->data));
1711                         ast_copy_string(fast->app, app, sizeof(fast->app));
1712                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1713                         if (l)
1714                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1715                         if (n)
1716                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1717                         fast->vars = vars;      
1718                         ast_copy_string(fast->context, context, sizeof(fast->context));
1719                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1720                         ast_copy_string(fast->account, account, sizeof(fast->account));
1721                         fast->timeout = to;
1722                         fast->priority = pi;
1723                         pthread_attr_init(&attr);
1724                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1725                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1726                                 res = -1;
1727                         } else {
1728                                 res = 0;
1729                         }
1730                 }
1731         } else if (!ast_strlen_zero(app)) {
1732                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1733         } else {
1734                 if (exten && context && pi)
1735                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1736                 else {
1737                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1738                         return 0;
1739                 }
1740         }   
1741         if (!res)
1742                 astman_send_ack(s, m, "Originate successfully queued");
1743         else
1744                 astman_send_error(s, m, "Originate failed");
1745         return 0;
1746 }
1747
1748 /*! \brief Help text for manager command mailboxstatus
1749  */
1750 static char mandescr_mailboxstatus[] = 
1751 "Description: Checks a voicemail account for status.\n"
1752 "Variables: (Names marked with * are required)\n"
1753 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1754 "       ActionID: Optional ActionID for message matching.\n"
1755 "Returns number of messages.\n"
1756 "       Message: Mailbox Status\n"
1757 "       Mailbox: <mailboxid>\n"
1758 "       Waiting: <count>\n"
1759 "\n";
1760
1761 static int action_mailboxstatus(struct mansession *s, struct message *m)
1762 {
1763         char *mailbox = astman_get_header(m, "Mailbox");
1764         char *id = astman_get_header(m,"ActionID");
1765         char idText[256] = "";
1766         int ret;
1767         if (ast_strlen_zero(mailbox)) {
1768                 astman_send_error(s, m, "Mailbox not specified");
1769                 return 0;
1770         }
1771         if (!ast_strlen_zero(id))
1772                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1773         ret = ast_app_has_voicemail(mailbox, NULL);
1774         astman_append(s, "Response: Success\r\n"
1775                                    "%s"
1776                                    "Message: Mailbox Status\r\n"
1777                                    "Mailbox: %s\r\n"
1778                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1779         return 0;
1780 }
1781
1782 static char mandescr_mailboxcount[] = 
1783 "Description: Checks a voicemail account for new messages.\n"
1784 "Variables: (Names marked with * are required)\n"
1785 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1786 "       ActionID: Optional ActionID for message matching.\n"
1787 "Returns number of new and old messages.\n"
1788 "       Message: Mailbox Message Count\n"
1789 "       Mailbox: <mailboxid>\n"
1790 "       NewMessages: <count>\n"
1791 "       OldMessages: <count>\n"
1792 "\n";
1793 static int action_mailboxcount(struct mansession *s, struct message *m)
1794 {
1795         char *mailbox = astman_get_header(m, "Mailbox");
1796         char *id = astman_get_header(m,"ActionID");
1797         char idText[256] = "";
1798         int newmsgs = 0, oldmsgs = 0;
1799         if (ast_strlen_zero(mailbox)) {
1800                 astman_send_error(s, m, "Mailbox not specified");
1801                 return 0;
1802         }
1803         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1804         if (!ast_strlen_zero(id)) {
1805                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1806         }
1807         astman_append(s, "Response: Success\r\n"
1808                                    "%s"
1809                                    "Message: Mailbox Message Count\r\n"
1810                                    "Mailbox: %s\r\n"
1811                                    "NewMessages: %d\r\n"
1812                                    "OldMessages: %d\r\n" 
1813                                    "\r\n",
1814                                     idText,mailbox, newmsgs, oldmsgs);
1815         return 0;
1816 }
1817
1818 static char mandescr_extensionstate[] = 
1819 "Description: Report the extension state for given extension.\n"
1820 "  If the extension has a hint, will use devicestate to check\n"
1821 "  the status of the device connected to the extension.\n"
1822 "Variables: (Names marked with * are required)\n"
1823 "       *Exten: Extension to check state on\n"
1824 "       *Context: Context for extension\n"
1825 "       ActionId: Optional ID for this transaction\n"
1826 "Will return an \"Extension Status\" message.\n"
1827 "The response will include the hint for the extension and the status.\n";
1828
1829 static int action_extensionstate(struct mansession *s, struct message *m)
1830 {
1831         char *exten = astman_get_header(m, "Exten");
1832         char *context = astman_get_header(m, "Context");
1833         char *id = astman_get_header(m,"ActionID");
1834         char idText[256] = "";
1835         char hint[256] = "";
1836         int status;
1837         if (ast_strlen_zero(exten)) {
1838                 astman_send_error(s, m, "Extension not specified");
1839                 return 0;
1840         }
1841         if (ast_strlen_zero(context))
1842                 context = "default";
1843         status = ast_extension_state(NULL, context, exten);
1844         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1845         if (!ast_strlen_zero(id)) {
1846                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1847         }
1848         astman_append(s, "Response: Success\r\n"
1849                                    "%s"
1850                                    "Message: Extension Status\r\n"
1851                                    "Exten: %s\r\n"
1852                                    "Context: %s\r\n"
1853                                    "Hint: %s\r\n"
1854                                    "Status: %d\r\n\r\n",
1855                                    idText,exten, context, hint, status);
1856         return 0;
1857 }
1858
1859 static char mandescr_timeout[] = 
1860 "Description: Hangup a channel after a certain time.\n"
1861 "Variables: (Names marked with * are required)\n"
1862 "       *Channel: Channel name to hangup\n"
1863 "       *Timeout: Maximum duration of the call (sec)\n"
1864 "Acknowledges set time with 'Timeout Set' message\n";
1865
1866 static int action_timeout(struct mansession *s, struct message *m)
1867 {
1868         struct ast_channel *c = NULL;
1869         char *name = astman_get_header(m, "Channel");
1870         int timeout = atoi(astman_get_header(m, "Timeout"));
1871         if (ast_strlen_zero(name)) {
1872                 astman_send_error(s, m, "No channel specified");
1873                 return 0;
1874         }
1875         if (!timeout) {
1876                 astman_send_error(s, m, "No timeout specified");
1877                 return 0;
1878         }
1879         c = ast_get_channel_by_name_locked(name);
1880         if (!c) {
1881                 astman_send_error(s, m, "No such channel");
1882                 return 0;
1883         }
1884         ast_channel_setwhentohangup(c, timeout);
1885         ast_channel_unlock(c);
1886         astman_send_ack(s, m, "Timeout Set");
1887         return 0;
1888 }
1889
1890 static int process_events(struct mansession *s)
1891 {
1892         int ret = 0;
1893
1894         ast_mutex_lock(&s->__lock);
1895         if (s->fd > -1) {
1896                 struct eventqent *eqe;
1897
1898                 s->busy--;
1899                 if (!s->eventq)
1900                         s->eventq = master_eventq;
1901                 while( (eqe = s->eventq->next) ) {
1902                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1903                             ((s->send_events & eqe->category) == eqe->category)) {
1904                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1905                                         ret = -1;
1906                         }
1907                         unuse_eventqent(s->eventq);
1908                         s->eventq = eqe;
1909                 }
1910         }
1911         ast_mutex_unlock(&s->__lock);
1912         return ret;
1913 }
1914
1915 static char mandescr_userevent[] =
1916 "Description: Send an event to manager sessions.\n"
1917 "Variables: (Names marked with * are required)\n"
1918 "       *UserEvent: EventStringToSend\n"
1919 "       Header1: Content1\n"
1920 "       HeaderN: ContentN\n";
1921
1922 static int action_userevent(struct mansession *s, struct message *m)
1923 {
1924         char *event = astman_get_header(m, "UserEvent");
1925         char body[2048] = "";
1926         int x, bodylen = 0;
1927         for (x = 0; x < m->hdrcount; x++) {
1928                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1929                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1930                         bodylen += strlen(m->headers[x]);
1931                         ast_copy_string(body + bodylen, "\r\n", 3);
1932                         bodylen += 2;
1933                 }
1934         }
1935
1936         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1937         return 0;
1938 }
1939
1940 /*
1941  * Process the message, performing desired action.
1942  * Return 0 on success, -1 on error that require the session to be destroyed.
1943  */
1944 static int process_message(struct mansession *s, struct message *m)
1945 {
1946         char action[80] = "";
1947         int ret = 0;
1948         struct manager_action *tmp;
1949
1950         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1951         if (option_debug)
1952                 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
1953
1954         if (ast_strlen_zero(action)) {
1955                 astman_send_error(s, m, "Missing action in request");
1956                 return 0;
1957         }
1958
1959         ast_atomic_fetchadd_int(&s->busy, 1);   /* XXX what's for ? */
1960         /* XXX should we protect the list navigation ? */
1961         for (tmp = first_action ; tmp; tmp = tmp->next) {               
1962                 if (!strcasecmp(action, tmp->action)) {
1963                         if ((s->writeperm & tmp->authority) == tmp->authority) {
1964                                 if (tmp->func(s, m))    /* error */
1965                                         return -1;
1966                         } else {
1967                                 astman_send_error(s, m, "Permission denied");
1968                         }
1969                         break;
1970                 }
1971         }
1972         if (!tmp)
1973                 astman_send_error(s, m, "Invalid/unknown command");
1974         if (ret)
1975                 return ret;
1976         return process_events(s);
1977 }
1978
1979 static int get_input(struct mansession *s, char *output)
1980 {
1981         /* output must have at least sizeof(s->inbuf) space */
1982         int res;
1983         int x;
1984         struct pollfd fds[1];
1985         for (x = 1; x < s->inlen; x++) {
1986                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1987                         /* Copy output data up to and including \r\n */
1988                         memcpy(output, s->inbuf, x + 1);
1989                         /* Add trailing \0 */
1990                         output[x+1] = '\0';
1991                         /* Move remaining data back to the front */
1992                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1993                         s->inlen -= (x + 1);
1994                         return 1;
1995                 }
1996         } 
1997         if (s->inlen >= sizeof(s->inbuf) - 1) {
1998                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), s->inbuf);
1999                 s->inlen = 0;
2000         }
2001         fds[0].fd = s->fd;
2002         fds[0].events = POLLIN;
2003         do {
2004                 ast_mutex_lock(&s->__lock);
2005                 s->waiting_thread = pthread_self();
2006                 ast_mutex_unlock(&s->__lock);
2007
2008                 res = poll(fds, 1, -1);
2009
2010                 ast_mutex_lock(&s->__lock);
2011                 s->waiting_thread = AST_PTHREADT_NULL;
2012                 ast_mutex_unlock(&s->__lock);
2013                 if (res < 0) {
2014                         if (errno == EINTR) {
2015                                 return 0;
2016                         }
2017                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2018                         return -1;
2019                 } else if (res > 0) {
2020                         ast_mutex_lock(&s->__lock);
2021                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
2022                         ast_mutex_unlock(&s->__lock);
2023                         if (res < 1)
2024                                 return -1;
2025                         break;
2026                 }
2027         } while(1);
2028         s->inlen += res;
2029         s->inbuf[s->inlen] = '\0';
2030         return 0;
2031 }
2032
2033 static void *session_do(void *data)
2034 {
2035         struct mansession *s = data;
2036         struct message m;
2037         int res;
2038         
2039         ast_mutex_lock(&s->__lock);
2040         astman_append(s, "Asterisk Call Manager/1.0\r\n");
2041         ast_mutex_unlock(&s->__lock);
2042         memset(&m, 0, sizeof(m));
2043         for (;;) {
2044                 res = get_input(s, m.headers[m.hdrcount]);
2045                 if (res > 0) {
2046                         /* Strip trailing \r\n */
2047                         if (strlen(m.headers[m.hdrcount]) < 2)
2048                                 continue;
2049                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
2050                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
2051                                 if (process_message(s, &m))
2052                                         break;
2053                                 memset(&m, 0, sizeof(m));
2054                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2055                                 m.hdrcount++;
2056                 } else if (res < 0) {
2057                         break;
2058                 } else if (s->eventq->next) {
2059                         if (process_events(s))
2060                                 break;
2061                 }
2062         }
2063         if (s->authenticated) {
2064                 if (option_verbose > 1) {
2065                         if (displayconnects) 
2066                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2067                 }
2068                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2069         } else {
2070                 if (option_verbose > 1) {
2071                         if (displayconnects)
2072                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2073                 }
2074                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2075         }
2076         destroy_session(s);
2077         return NULL;
2078 }
2079
2080 static void *accept_thread(void *ignore)
2081 {
2082         int as;
2083         struct sockaddr_in sin;
2084         socklen_t sinlen;
2085         struct eventqent *eqe;
2086         struct mansession *s;
2087         struct protoent *p;
2088         int arg = 1;
2089         int flags;
2090         pthread_attr_t attr;
2091         time_t now;
2092         struct pollfd pfds[1];
2093
2094         pthread_attr_init(&attr);
2095         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2096
2097         for (;;) {
2098                 time(&now);
2099                 AST_LIST_LOCK(&sessions);
2100                 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2101                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2102                                 AST_LIST_REMOVE_CURRENT(&sessions, list);
2103                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2104                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2105                                                 s->username, ast_inet_ntoa(s->sin.sin_addr));
2106                                 }
2107                                 free_session(s);
2108                                 break;  
2109                         }
2110                 }
2111                 AST_LIST_TRAVERSE_SAFE_END
2112                 /* Purge master event queue of old, unused events, but make sure we
2113                    always keep at least one in the queue */
2114                 /* XXX why do we need one entry in the queue ? */
2115                 while (master_eventq->next && !master_eventq->usecount) {
2116                         eqe = master_eventq;
2117                         master_eventq = master_eventq->next;
2118                         free(eqe);
2119                 }
2120                 AST_LIST_UNLOCK(&sessions);
2121                 if (s)
2122                         ast_atomic_fetchadd_int(&num_sessions, -1);
2123
2124                 sinlen = sizeof(sin);
2125                 pfds[0].fd = asock;
2126                 pfds[0].events = POLLIN;
2127                 /* Wait for something to happen, but timeout every few seconds so
2128                    we can ditch any old manager sessions */
2129                 if (poll(pfds, 1, 5000) < 1)
2130                         continue;
2131                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2132                 if (as < 0) {
2133                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2134                         continue;
2135                 }
2136                 p = getprotobyname("tcp");
2137                 if (p) {
2138                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2139                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2140                         }
2141                 }
2142                 if (!(s = ast_calloc(1, sizeof(*s))))
2143                         continue;
2144
2145                 ast_atomic_fetchadd_int(&num_sessions, 1);
2146                 
2147                 memcpy(&s->sin, &sin, sizeof(sin));
2148                 s->writetimeout = 100;
2149                 s->waiting_thread = AST_PTHREADT_NULL;
2150
2151                 if (!block_sockets) {
2152                         /* For safety, make sure socket is non-blocking */
2153                         flags = fcntl(as, F_GETFL);
2154                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
2155                 } else {
2156                         flags = fcntl(as, F_GETFL);
2157                         fcntl(as, F_SETFL, flags & ~O_NONBLOCK);
2158                 }
2159                 ast_mutex_init(&s->__lock);
2160                 s->fd = as;
2161                 s->send_events = -1;
2162                 AST_LIST_LOCK(&sessions);
2163                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2164                 /* Find the last place in the master event queue and hook ourselves
2165                    in there */
2166                 s->eventq = master_eventq;
2167                 while(s->eventq->next)
2168                         s->eventq = s->eventq->next;
2169                 AST_LIST_UNLOCK(&sessions);
2170                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2171                 if (ast_pthread_create_background(&s->t, &attr, session_do, s))
2172                         destroy_session(s);
2173         }
2174         pthread_attr_destroy(&attr);
2175         return NULL;
2176 }
2177
2178 static int append_event(const char *str, int category)
2179 {
2180         struct eventqent *tmp, *prev = NULL;
2181         tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2182
2183         if (!tmp)
2184                 return -1;
2185
2186         tmp->next = NULL;
2187         tmp->category = category;
2188         strcpy(tmp->eventdata, str);
2189         
2190         if (master_eventq) {
2191                 prev = master_eventq;
2192                 while (prev->next) 
2193                         prev = prev->next;
2194                 prev->next = tmp;
2195         } else {
2196                 master_eventq = tmp;
2197         }
2198         
2199         tmp->usecount = num_sessions;
2200         
2201         return 0;
2202 }
2203
2204 /*! \brief  manager_event: Send AMI event to client */
2205 int manager_event(int category, const char *event, const char *fmt, ...)
2206 {
2207         struct mansession *s;
2208         char auth[80];
2209         va_list ap;
2210         struct timeval now;
2211         struct ast_dynamic_str *buf;
2212
2213         /* Abort if there aren't any manager sessions */
2214         if (!num_sessions)
2215                 return 0;
2216
2217         if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2218                 return -1;
2219
2220         ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2221                         "Event: %s\r\nPrivilege: %s\r\n",
2222                          event, authority_to_str(category, auth, sizeof(auth)));
2223
2224         if (timestampevents) {
2225                 now = ast_tvnow();
2226                 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2227                                 "Timestamp: %ld.%06lu\r\n",
2228                                  now.tv_sec, (unsigned long) now.tv_usec);
2229         }
2230
2231         va_start(ap, fmt);
2232         ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2233         va_end(ap);
2234         
2235         ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");     
2236         
2237         /* Append event to master list and wake up any sleeping sessions */
2238         AST_LIST_LOCK(&sessions);
2239         append_event(buf->str, category);
2240         AST_LIST_TRAVERSE(&sessions, s, list) {
2241                 ast_mutex_lock(&s->__lock);
2242                 if (s->waiting_thread != AST_PTHREADT_NULL)
2243                         pthread_kill(s->waiting_thread, SIGURG);
2244                 ast_mutex_unlock(&s->__lock);
2245         }
2246         AST_LIST_UNLOCK(&sessions);
2247
2248         return 0;
2249 }
2250
2251 int ast_manager_unregister(char *action) 
2252 {
2253         struct manager_action *cur = first_action, *prev = first_action;
2254
2255         ast_mutex_lock(&actionlock);
2256         for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2257                 if (!strcasecmp(action, cur->action)) {
2258                         if (prev)
2259                                 prev->next = cur->next;
2260                         else
2261                                 first_action = cur->next;
2262                         free(cur);
2263                         if (option_verbose > 1) 
2264                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2265                         break;
2266                 }
2267         }
2268         ast_mutex_unlock(&actionlock);
2269         return 0;
2270 }
2271
2272 static int manager_state_cb(char *context, char *exten, int state, void *data)
2273 {
2274         /* Notify managers of change */
2275         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2276         return 0;
2277 }
2278
2279 static int ast_manager_register_struct(struct manager_action *act)
2280 {
2281         struct manager_action *cur, *prev = NULL;
2282         int ret;
2283
2284         ast_mutex_lock(&actionlock);
2285         for (cur = first_action; cur; prev = cur, cur = cur->next) {
2286                 ret = strcasecmp(cur->action, act->action);
2287                 if (ret == 0) {
2288                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2289                         ast_mutex_unlock(&actionlock);
2290                         return -1;
2291                 }
2292                 if (ret > 0)    /* Insert these alphabetically */
2293                         break;
2294         }
2295         if (prev)
2296                 prev->next = act;
2297         else
2298                 first_action = act;
2299         act->next = cur;
2300
2301         if (option_verbose > 1) 
2302                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2303         ast_mutex_unlock(&actionlock);
2304         return 0;
2305 }
2306
2307 /*! \brief register a new command with manager, including online help. This is 
2308         the preferred way to register a manager command */
2309 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2310 {
2311         struct manager_action *cur;
2312
2313         cur = ast_malloc(sizeof(*cur));
2314         if (!cur)
2315                 return -1;
2316         
2317         cur->action = action;
2318         cur->authority = auth;
2319         cur->func = func;
2320         cur->synopsis = synopsis;
2321         cur->description = description;
2322         cur->next = NULL;
2323
2324         ast_manager_register_struct(cur);
2325
2326         return 0;
2327 }
2328 /*! @}
2329  END Doxygen group */
2330
2331 static struct mansession *find_session(unsigned long ident)
2332 {
2333         struct mansession *s;
2334
2335         AST_LIST_LOCK(&sessions);
2336         AST_LIST_TRAVERSE(&sessions, s, list) {
2337                 ast_mutex_lock(&s->__lock);
2338                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2339                         s->inuse++;
2340                         break;
2341                 }
2342                 ast_mutex_unlock(&s->__lock);
2343         }
2344         AST_LIST_UNLOCK(&sessions);
2345
2346         return s;
2347 }
2348
2349
2350 static void vars2msg(struct message *m, struct ast_variable *vars)
2351 {
2352         int x;
2353         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2354                 if (!vars)
2355                         break;
2356                 m->hdrcount = x + 1;
2357                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2358         }
2359 }
2360
2361
2362 static char *generic_http_callback(enum output_format format,
2363         struct sockaddr_in *requestor, const char *uri,
2364         struct ast_variable *params, int *status,
2365         char **title, int *contentlength)
2366 {
2367         struct mansession *s = NULL;
2368         unsigned long ident = 0;
2369         char workspace[1024];
2370         char cookie[128];
2371         size_t len = sizeof(workspace);
2372         int blastaway = 0;
2373         char *c = workspace;
2374         char *retval = NULL;
2375         struct message m;
2376         struct ast_variable *v;
2377
2378         for (v = params; v; v = v->next) {
2379                 if (!strcasecmp(v->name, "mansession_id")) {
2380                         sscanf(v->value, "%lx", &ident);
2381                         ast_verbose("session is <%lx>\n", ident);
2382                         break;
2383                 }
2384         }
2385         
2386         if (!(s = find_session(ident))) {
2387                 /* Create new session */
2388                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2389                         *status = 500;
2390                         goto generic_callback_out;
2391                 }
2392                 memcpy(&s->sin, requestor, sizeof(s->sin));
2393                 s->fd = -1;
2394                 s->waiting_thread = AST_PTHREADT_NULL;
2395                 s->send_events = 0;
2396                 ast_mutex_init(&s->__lock);
2397                 ast_mutex_lock(&s->__lock);
2398                 s->inuse = 1;
2399                 s->managerid = rand() | (unsigned long)s;
2400                 AST_LIST_LOCK(&sessions);
2401                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2402                 /* Hook into the last spot in the event queue */
2403                 s->eventq = master_eventq;
2404                 while (s->eventq->next)
2405                         s->eventq = s->eventq->next;
2406                 AST_LIST_UNLOCK(&sessions);
2407                 ast_atomic_fetchadd_int(&s->eventq->usecount, 1);
2408                 ast_atomic_fetchadd_int(&num_sessions, 1);
2409         }
2410
2411         /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
2412         time(&s->sessiontimeout);
2413         if (!s->authenticated && (httptimeout > 5))
2414                 s->sessiontimeout += 5;
2415         else
2416                 s->sessiontimeout += httptimeout;
2417         ast_mutex_unlock(&s->__lock);
2418         
2419         memset(&m, 0, sizeof(m));
2420         {
2421                 char tmp[80];
2422                 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2423                 sprintf(tmp, "%08lx", s->managerid);
2424                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2425                 if (format == FORMAT_HTML)
2426                         ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2427                 vars2msg(&m, params);
2428                 if (format == FORMAT_XML) {
2429                         ast_build_string(&c, &len, "<ajax-response>\n");
2430                 } else if (format == FORMAT_HTML) {
2431 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
2432 #define TEST_STRING \
2433         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br>\
2434         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>
2435         <input type=\"submit\"></form>"
2436                         ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2437                         ast_build_string(&c, &len, ROW_FMT, "<h1>&nbsp;&nbsp;Manager Tester</h1>");
2438                         ast_build_string(&c, &len, ROW_FMT, TEST_STRING);
2439                 }
2440                 {
2441                         char template[32];
2442                         ast_copy_string(template, "/tmp/ast-http-XXXXXX", sizeof(template));
2443                         s->fd = mkstemp(template);
2444                 }
2445                 if (process_message(s, &m)) {
2446                         if (s->authenticated) {
2447                                 if (option_verbose > 1) {
2448                                         if (displayconnects) 
2449                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));    
2450                                 }
2451                                 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2452                         } else {
2453                                 if (option_verbose > 1) {
2454                                         if (displayconnects)
2455                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2456                                 }
2457                                 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2458                         }
2459                         s->needdestroy = 1;
2460                 }
2461                 if (s->fd > -1) {       /* have temporary output */
2462                         char *buf;
2463                         off_t len = lseek(s->fd, 0, SEEK_END);  /* how many chars available */
2464
2465                         if (len > 0 && (buf = ast_calloc(1, len+1))) {
2466                                 if (!s->outputstr)
2467                                         s->outputstr = ast_calloc(1, sizeof(*s->outputstr));
2468                                 if (s->outputstr) {
2469                                         lseek(s->fd, 0, SEEK_SET);
2470                                         read(s->fd, buf, len);
2471                                         ast_verbose("--- fd %d has %d bytes ---\n%s\n---\n", s->fd, (int)len, buf);
2472                                         ast_dynamic_str_append(&s->outputstr, 0, "%s", buf);
2473                                 }
2474                                 free(buf);
2475                         }
2476                         close(s->fd);
2477                         s->fd = -1;
2478                 }
2479
2480                 if (s->outputstr) {
2481                         char *tmp;
2482                         if (format == FORMAT_XML || format == FORMAT_HTML)
2483                                 tmp = xml_translate(s->outputstr->str, params, format);
2484                         else
2485                                 tmp = s->outputstr->str;
2486                         if (tmp) {
2487                                 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2488                                 if (retval) {
2489                                         strcpy(retval, workspace);
2490                                         strcpy(retval + strlen(retval), tmp);
2491                                         c = retval + strlen(retval);
2492                                         len = 120;
2493                                 }
2494                         }
2495                         if (tmp != s->outputstr->str)
2496                                 free(tmp);
2497                         free(s->outputstr);
2498                         s->outputstr = NULL;
2499                 }
2500                 /* Still okay because c would safely be pointing to workspace even
2501                    if retval failed to allocate above */
2502                 if (format == FORMAT_XML) {
2503                         ast_build_string(&c, &len, "</ajax-response>\n");
2504                 } else if (format == FORMAT_HTML)
2505                         ast_build_string(&c, &len, "</table></body>\r\n");
2506         }
2507         ast_mutex_lock(&s->__lock);
2508         if (s->needdestroy) {
2509                 if (s->inuse == 1) {
2510                         if (option_debug)
2511                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2512                         blastaway = 1;
2513                 } else {
2514                         if (option_debug)
2515                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2516                         if (s->waiting_thread != AST_PTHREADT_NULL)
2517                                 pthread_kill(s->waiting_thread, SIGURG);
2518                         s->inuse--;
2519                 }
2520         } else
2521                 s->inuse--;
2522         ast_mutex_unlock(&s->__lock);
2523         
2524         if (blastaway)
2525                 destroy_session(s);
2526 generic_callback_out:
2527         if (*status != 200)
2528                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
2529         return retval;
2530 }
2531
2532 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2533 {
2534         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2535 }
2536
2537 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2538 {
2539         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2540 }
2541
2542 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2543 {
2544         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2545 }
2546
2547 struct ast_http_uri rawmanuri = {
2548         .description = "Raw HTTP Manager Event Interface",
2549         .uri = "rawman",
2550         .has_subtree = 0,
2551         .callback = rawman_http_callback,
2552 };
2553
2554 struct ast_http_uri manageruri = {
2555         .description = "HTML Manager Event Interface",
2556         .uri = "manager",
2557         .has_subtree = 0,
2558         .callback = manager_http_callback,
2559 };
2560
2561 struct ast_http_uri managerxmluri = {
2562         .description = "XML Manager Event Interface",
2563         .uri = "mxml",
2564         .has_subtree = 0,
2565         .callback = mxml_http_callback,
2566 };
2567
2568 static int registered = 0;
2569 static int webregged = 0;
2570
2571 int init_manager(void)
2572 {
2573         struct ast_config *cfg = NULL;
2574         const char *val;
2575         char *cat = NULL;
2576         int oldportno = portno;
2577         static struct sockaddr_in ba;
2578         int x = 1;
2579         int flags;
2580         int webenabled = 0;
2581         int newhttptimeout = 60;
2582         struct ast_manager_user *user = NULL;
2583
2584         if (!registered) {
2585                 /* Register default actions */
2586                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2587                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2588                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2589                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
2590                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
2591                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2592                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2593                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2594                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2595                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2596                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2597                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2598                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2599                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2600                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2601                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2602                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2603                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2604                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2605                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2606                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2607
2608                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2609                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2610                 registered = 1;
2611                 /* Append placeholder event so master_eventq never runs dry */
2612                 append_event("Event: Placeholder\r\n\r\n", 0);
2613         }
2614         portno = DEFAULT_MANAGER_PORT;
2615         displayconnects = 1;
2616         cfg = ast_config_load("manager.conf");
2617         if (!cfg) {
2618                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2619                 return 0;
2620         }
2621         val = ast_variable_retrieve(cfg, "general", "enabled");
2622         if (val)
2623                 enabled = ast_true(val);
2624
2625         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2626         if (val)
2627                 block_sockets = ast_true(val);
2628
2629         val = ast_variable_retrieve(cfg, "general", "webenabled");
2630         if (val)
2631                 webenabled = ast_true(val);
2632
2633         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2634                 if (sscanf(val, "%d", &portno) != 1) {
2635                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2636                         portno = DEFAULT_MANAGER_PORT;
2637                 }
2638         }
2639
2640         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2641                 displayconnects = ast_true(val);
2642
2643         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2644                 timestampevents = ast_true(val);
2645
2646         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2647                 newhttptimeout = atoi(val);
2648
2649         memset(&ba, 0, sizeof(ba));
2650         ba.sin_family = AF_INET;
2651         ba.sin_port = htons(portno);
2652
2653         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2654                 if (!inet_aton(val, &ba.sin_addr)) { 
2655                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2656                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2657                 }
2658         }
2659         
2660
2661         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2662 #if 0
2663                 /* Can't be done yet */
2664                 close(asock);
2665                 asock = -1;
2666 #else
2667                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2668 #endif
2669         }
2670
2671         AST_LIST_LOCK(&users);
2672
2673         while ((cat = ast_category_browse(cfg, cat))) {
2674                 struct ast_variable *var = NULL;
2675
2676                 if (!strcasecmp(cat, "general"))
2677                         continue;
2678
2679                 /* Look for an existing entry, if none found - create one and add it to the list */
2680                 if (!(user = ast_get_manager_by_name_locked(cat))) {
2681                         if (!(user = ast_calloc(1, sizeof(*user))))
2682                                 break;
2683                         /* Copy name over */
2684                         ast_copy_string(user->username, cat, sizeof(user->username));
2685                         /* Insert into list */
2686                         AST_LIST_INSERT_TAIL(&users, user, list);
2687                 }
2688
2689                 /* Make sure we keep this user and don't destroy it during cleanup */
2690                 user->keep = 1;
2691
2692                 var = ast_variable_browse(cfg, cat);
2693                 while (var) {
2694                         if (!strcasecmp(var->name, "secret")) {
2695                                 if (user->secret)
2696                                         free(user->secret);
2697                                 user->secret = ast_strdup(var->value);
2698                         } else if (!strcasecmp(var->name, "deny") ) {
2699                                 if (user->deny)
2700                                         free(user->deny);
2701                                 user->deny = ast_strdup(var->value);
2702                         } else if (!strcasecmp(var->name, "permit") ) {
2703                                 if (user->permit)
2704                                         free(user->permit);
2705                                 user->permit = ast_strdup(var->value);
2706                         }  else if (!strcasecmp(var->name, "read") ) {
2707                                 if (user->read)
2708                                         free(user->read);
2709                                 user->read = ast_strdup(var->value);
2710                         }  else if (!strcasecmp(var->name, "write") ) {
2711                                 if (user->write)
2712                                         free(user->write);
2713                                 user->write = ast_strdup(var->value);
2714                         }  else if (!strcasecmp(var->name, "displayconnects") )
2715                                 user->displayconnects = ast_true(var->value);
2716                         else {
2717                                 if (option_debug)
2718                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2719                         }
2720                         var = var->next;
2721                 }
2722         }
2723
2724         /* Perform cleanup - essentially prune out old users that no longer exist */
2725         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2726                 if (user->keep) {
2727                         user->keep = 0;
2728                         continue;
2729                 }
2730                 /* We do not need to keep this user so take them out of the list */
2731                 AST_LIST_REMOVE_CURRENT(&users, list);
2732                 /* Free their memory now */
2733                 if (user->secret)
2734                         free(user->secret);
2735                 if (user->deny)
2736                         free(user->deny);
2737                 if (user->permit)
2738                         free(user->permit);
2739                 if (user->read)
2740                         free(user->read);
2741                 if (user->write)
2742                         free(user->write);
2743                 free(user);
2744         }
2745         AST_LIST_TRAVERSE_SAFE_END
2746
2747         AST_LIST_UNLOCK(&users);
2748
2749         ast_config_destroy(cfg);
2750         
2751         if (webenabled && enabled) {
2752                 if (!webregged) {
2753                         ast_http_uri_link(&rawmanuri);
2754                         ast_http_uri_link(&manageruri);
2755                         ast_http_uri_link(&managerxmluri);
2756                         webregged = 1;
2757                 }
2758         } else {
2759                 if (webregged) {
2760                         ast_http_uri_unlink(&rawmanuri);
2761                         ast_http_uri_unlink(&manageruri);
2762                         ast_http_uri_unlink(&managerxmluri);
2763                         webregged = 0;
2764                 }
2765         }
2766
2767         if (newhttptimeout > 0)
2768                 httptimeout = newhttptimeout;
2769
2770         /* If not enabled, do nothing */
2771         if (!enabled)
2772                 return 0;
2773
2774         if (asock < 0) {
2775                 asock = socket(AF_INET, SOCK_STREAM, 0);
2776                 if (asock < 0) {
2777                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2778                         return -1;
2779                 }
2780                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2781                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2782                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2783                         close(asock);
2784                         asock = -1;
2785                         return -1;
2786                 }
2787                 if (listen(asock, 2)) {
2788                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2789                         close(asock);
2790                         asock = -1;
2791                         return -1;
2792                 }
2793                 flags = fcntl(asock, F_GETFL);
2794                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2795                 if (option_verbose)
2796                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2797                 ast_pthread_create_background(&t, NULL, accept_thread, NULL);
2798         }
2799         return 0;
2800 }
2801
2802 int reload_manager(void)
2803 {
2804         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2805         return init_manager();
2806 }