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