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