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