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