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