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