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