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