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