593fc736cbc373d79a92a240827b0f6e96dc7ed7
[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         }
913
914         ast_config_destroy(cfg);
915         if (!cat) {
916                 /* Didn't find the user in manager.conf, check users.conf */
917                 int hasmanager = 0;
918                 cfg = ast_config_load("users.conf");
919                 if (!cfg)
920                         return -1;
921                 while ( (cat = ast_category_browse(cfg, cat)) ) {
922                         if (!strcasecmp(cat, user) && strcasecmp(cat, "general"))
923                                 break;
924                 }
925                 if (!cat) {
926                         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
927                         ast_config_destroy(cfg);
928                         return -1;
929                 }
930                 /* collect parameters for the user's entry from users.conf */
931                 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
932                         if (!strcasecmp(v->name, "secret"))
933                                 password = ast_strdupa(v->value);
934                         else if (!strcasecmp(v->name, "read"))
935                                 readperm = get_perm(v->value);
936                         else if (!strcasecmp(v->name, "write"))
937                                 writeperm = get_perm(v->value);
938                         else if (!strcasecmp(v->name, "permit") ||
939                                    !strcasecmp(v->name, "deny")) {
940                                 ha = ast_append_ha(v->name, v->value, ha, NULL);
941                         } else if (!strcasecmp(v->name, "writetimeout")) {
942                                 int val = atoi(v->value);
943         
944                                 if (val < 100)
945                                         ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
946                                 else
947                                         s->writetimeout = val;
948                         } else if (!strcasecmp(v->name, "hasmanager")) {
949                                 hasmanager = ast_true(v->value);
950                         }
951                 }
952                 ast_config_destroy(cfg);
953                 if (!hasmanager) {
954                         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
955                         return -1;
956                 }
957         }
958
959         }
960
961         if (ha) {
962                 int good = ast_apply_ha(ha, &(s->sin));
963                 ast_free_ha(ha);
964                 if (!good) {
965                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
966                         return -1;
967                 }
968         }
969         if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
970                 const char *key = astman_get_header(m, "Key");
971                 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge)) {
972                         int x;
973                         int len = 0;
974                         char md5key[256] = "";
975                         struct MD5Context md5;
976                         unsigned char digest[16];
977
978                         MD5Init(&md5);
979                         MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
980                         MD5Update(&md5, (unsigned char *) password, strlen(password));
981                         MD5Final(digest, &md5);
982                         for (x=0; x<16; x++)
983                                 len += sprintf(md5key + len, "%2.2x", digest[x]);
984                         if (!strcmp(md5key, key))
985                                 error = 0;
986                 }
987         } else if (password) {
988                 const char *pass = astman_get_header(m, "Secret");
989                 if (!strcmp(password, pass))
990                         error = 0;
991         }
992         if (error) {
993                 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
994                 return -1;
995         }
996         ast_copy_string(s->username, user, sizeof(s->username));
997         s->readperm = readperm;
998         s->writeperm = writeperm;
999         set_eventmask(s, astman_get_header(m, "Events"));
1000         return 0;
1001 }
1002
1003 /*! \brief Manager PING */
1004 static char mandescr_ping[] =
1005 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
1006 "  manager connection open.\n"
1007 "Variables: NONE\n";
1008
1009 static int action_ping(struct mansession *s, const struct message *m)
1010 {
1011         astman_send_response(s, m, "Pong", NULL);
1012         return 0;
1013 }
1014
1015 static char mandescr_getconfig[] =
1016 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1017 "file by category and contents.\n"
1018 "Variables:\n"
1019 "   Filename: Configuration filename (e.g. foo.conf)\n";
1020
1021 static int action_getconfig(struct mansession *s, const struct message *m)
1022 {
1023         struct ast_config *cfg;
1024         const char *fn = astman_get_header(m, "Filename");
1025         int catcount = 0;
1026         int lineno = 0;
1027         char *category=NULL;
1028         struct ast_variable *v;
1029
1030         if (ast_strlen_zero(fn)) {
1031                 astman_send_error(s, m, "Filename not specified");
1032                 return 0;
1033         }
1034         if (!(cfg = ast_config_load_with_comments(fn))) {
1035                 astman_send_error(s, m, "Config file not found");
1036                 return 0;
1037         }
1038         astman_start_ack(s, m);
1039         while ((category = ast_category_browse(cfg, category))) {
1040                 lineno = 0;
1041                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1042                 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1043                         astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1044                 catcount++;
1045         }
1046         ast_config_destroy(cfg);
1047         astman_append(s, "\r\n");
1048
1049         return 0;
1050 }
1051
1052 /* helper function for action_updateconfig */
1053 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
1054 {
1055         int x;
1056         char hdr[40];
1057         const char *action, *cat, *var, *value, *match;
1058         struct ast_category *category;
1059         struct ast_variable *v;
1060
1061         for (x=0;x<100000;x++) {
1062                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1063                 action = astman_get_header(m, hdr);
1064                 if (ast_strlen_zero(action))
1065                         break;
1066                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1067                 cat = astman_get_header(m, hdr);
1068                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1069                 var = astman_get_header(m, hdr);
1070                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1071                 value = astman_get_header(m, hdr);
1072                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1073                 match = astman_get_header(m, hdr);
1074                 if (!strcasecmp(action, "newcat")) {
1075                         if (!ast_strlen_zero(cat)) {
1076                                 category = ast_category_new(cat);
1077                                 if (category) {
1078                                         ast_category_append(cfg, category);
1079                                 }
1080                         }
1081                 } else if (!strcasecmp(action, "renamecat")) {
1082                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1083                                 category = ast_category_get(cfg, cat);
1084                                 if (category)
1085                                         ast_category_rename(category, value);
1086                         }
1087                 } else if (!strcasecmp(action, "delcat")) {
1088                         if (!ast_strlen_zero(cat))
1089                                 ast_category_delete(cfg, cat);
1090                 } else if (!strcasecmp(action, "update")) {
1091                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1092                                 ast_variable_update(category, var, value, match);
1093                 } else if (!strcasecmp(action, "delete")) {
1094                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1095                                 ast_variable_delete(category, var, match);
1096                 } else if (!strcasecmp(action, "append")) {
1097                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1098                                 (category = ast_category_get(cfg, cat)) &&
1099                                 (v = ast_variable_new(var, value))){
1100                                 if (match && !strcasecmp(match, "object"))
1101                                         v->object = 1;
1102                                 ast_variable_append(category, v);
1103                         }
1104                 }
1105         }
1106 }
1107
1108 static char mandescr_updateconfig[] =
1109 "Description: A 'UpdateConfig' action will dump the contents of a configuration\n"
1110 "file by category and contents.\n"
1111 "Variables (X's represent 6 digit number beginning with 000000):\n"
1112 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1113 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1114 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1115 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1116 "   Cat-XXXXXX:    Category to operate on\n"
1117 "   Var-XXXXXX:    Variable to work on\n"
1118 "   Value-XXXXXX:  Value to work on\n"
1119 "   Match-XXXXXX:  Extra match required to match line\n";
1120
1121 static int action_updateconfig(struct mansession *s, const struct message *m)
1122 {
1123         struct ast_config *cfg;
1124         const char *sfn = astman_get_header(m, "SrcFilename");
1125         const char *dfn = astman_get_header(m, "DstFilename");
1126         int res;
1127         const char *rld = astman_get_header(m, "Reload");
1128
1129         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1130                 astman_send_error(s, m, "Filename not specified");
1131                 return 0;
1132         }
1133         if (!(cfg = ast_config_load_with_comments(sfn))) {
1134                 astman_send_error(s, m, "Config file not found");
1135                 return 0;
1136         }
1137         handle_updates(s, m, cfg);
1138         res = config_text_file_save(dfn, cfg, "Manager");
1139         ast_config_destroy(cfg);
1140         if (res) {
1141                 astman_send_error(s, m, "Save of config failed");
1142                 return 0;
1143         }
1144         astman_send_ack(s, m, NULL);
1145         if (!ast_strlen_zero(rld)) {
1146                 if (ast_true(rld))
1147                         rld = NULL;
1148                 ast_module_reload(rld);
1149         }
1150         return 0;
1151 }
1152
1153 /*! \brief Manager WAITEVENT */
1154 static char mandescr_waitevent[] =
1155 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1156 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1157 "session, events will be generated and queued.\n"
1158 "Variables: \n"
1159 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1160
1161 static int action_waitevent(struct mansession *s, const struct message *m)
1162 {
1163         const char *timeouts = astman_get_header(m, "Timeout");
1164         int timeout = -1;
1165         int x;
1166         int needexit = 0;
1167         const char *id = astman_get_header(m,"ActionID");
1168         char idText[256] = "";
1169
1170         if (!ast_strlen_zero(id))
1171                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1172
1173         if (!ast_strlen_zero(timeouts)) {
1174                 sscanf(timeouts, "%i", &timeout);
1175                 if (timeout < -1)
1176                         timeout = -1;
1177                 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1178         }
1179
1180         ast_mutex_lock(&s->__lock);
1181         if (s->waiting_thread != AST_PTHREADT_NULL)
1182                 pthread_kill(s->waiting_thread, SIGURG);
1183
1184         if (s->managerid) { /* AMI-over-HTTP session */
1185                 /*
1186                  * Make sure the timeout is within the expire time of the session,
1187                  * as the client will likely abort the request if it does not see
1188                  * data coming after some amount of time.
1189                  */
1190                 time_t now = time(NULL);
1191                 int max = s->sessiontimeout - now - 10;
1192
1193                 if (max < 0)    /* We are already late. Strange but possible. */
1194                         max = 0;
1195                 if (timeout < 0 || timeout > max)
1196                         timeout = max;
1197                 if (!s->send_events)    /* make sure we record events */
1198                         s->send_events = -1;
1199         }
1200         ast_mutex_unlock(&s->__lock);
1201
1202         /* XXX should this go inside the lock ? */
1203         s->waiting_thread = pthread_self();     /* let new events wake up this thread */
1204         if (option_debug)
1205                 ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
1206
1207         for (x=0; x < timeout || timeout < 0; x++) {
1208                 ast_mutex_lock(&s->__lock);
1209                 if (NEW_EVENT(s))
1210                         needexit = 1;
1211                 /* We can have multiple HTTP session point to the same mansession entry.
1212                  * The way we deal with it is not very nice: newcomers kick out the previous
1213                  * HTTP session. XXX this needs to be improved.
1214                  */
1215                 if (s->waiting_thread != pthread_self())
1216                         needexit = 1;
1217                 if (s->needdestroy)
1218                         needexit = 1;
1219                 ast_mutex_unlock(&s->__lock);
1220                 if (needexit)
1221                         break;
1222                 if (s->managerid == 0) {        /* AMI session */
1223                         if (ast_wait_for_input(s->fd, 1000))
1224                                 break;
1225                 } else {        /* HTTP session */
1226                         sleep(1);
1227                 }
1228         }
1229         if (option_debug)
1230                 ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
1231         ast_mutex_lock(&s->__lock);
1232         if (s->waiting_thread == pthread_self()) {
1233                 struct eventqent *eqe;
1234                 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1235                 while ( (eqe = NEW_EVENT(s)) ) {
1236                         ref_event(eqe);
1237                         if (((s->readperm & eqe->category) == eqe->category) &&
1238                             ((s->send_events & eqe->category) == eqe->category)) {
1239                                 astman_append(s, "%s", eqe->eventdata);
1240                         }
1241                         s->last_ev = unref_event(s->last_ev);
1242                 }
1243                 astman_append(s,
1244                         "Event: WaitEventComplete\r\n"
1245                         "%s"
1246                         "\r\n", idText);
1247                 s->waiting_thread = AST_PTHREADT_NULL;
1248         } else {
1249                 if (option_debug)
1250                         ast_log(LOG_DEBUG, "Abandoning event request!\n");
1251         }
1252         ast_mutex_unlock(&s->__lock);
1253         return 0;
1254 }
1255
1256 static char mandescr_listcommands[] =
1257 "Description: Returns the action name and synopsis for every\n"
1258 "  action that is available to the user\n"
1259 "Variables: NONE\n";
1260
1261 static int action_listcommands(struct mansession *s, const struct message *m)
1262 {
1263         struct manager_action *cur;
1264         struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1265
1266         astman_start_ack(s, m);
1267         ast_mutex_lock(&actionlock);
1268         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
1269                 if ((s->writeperm & cur->authority) == cur->authority)
1270                         astman_append(s, "%s: %s (Priv: %s)\r\n",
1271                                 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1272         }
1273         ast_mutex_unlock(&actionlock);
1274         astman_append(s, "\r\n");
1275
1276         return 0;
1277 }
1278
1279 static char mandescr_events[] =
1280 "Description: Enable/Disable sending of events to this manager\n"
1281 "  client.\n"
1282 "Variables:\n"
1283 "       EventMask: 'on' if all events should be sent,\n"
1284 "               'off' if no events should be sent,\n"
1285 "               'system,call,log' to select which flags events should have to be sent.\n";
1286
1287 static int action_events(struct mansession *s, const struct message *m)
1288 {
1289         const char *mask = astman_get_header(m, "EventMask");
1290         int res;
1291
1292         res = set_eventmask(s, mask);
1293         if (res > 0)
1294                 astman_send_response(s, m, "Events On", NULL);
1295         else if (res == 0)
1296                 astman_send_response(s, m, "Events Off", NULL);
1297
1298         return 0;
1299 }
1300
1301 static char mandescr_logoff[] =
1302 "Description: Logoff this manager session\n"
1303 "Variables: NONE\n";
1304
1305 static int action_logoff(struct mansession *s, const struct message *m)
1306 {
1307         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1308         return -1;
1309 }
1310
1311 static int action_login(struct mansession *s, const struct message *m)
1312 {
1313         if (authenticate(s, m)) {
1314                 sleep(1);
1315                 astman_send_error(s, m, "Authentication failed");
1316                 return -1;
1317         }
1318         s->authenticated = 1;
1319         if (option_verbose > 1) {
1320                 if (displayconnects) {
1321                         ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1322                 }
1323         }
1324         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1325         astman_send_ack(s, m, "Authentication accepted");
1326         return 0;
1327 }
1328
1329 static int action_challenge(struct mansession *s, const struct message *m)
1330 {
1331         const char *authtype = astman_get_header(m, "AuthType");
1332
1333         if (!strcasecmp(authtype, "MD5")) {
1334                 if (ast_strlen_zero(s->challenge))
1335                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1336                 ast_mutex_lock(&s->__lock);
1337                 astman_start_ack(s, m);
1338                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1339                 ast_mutex_unlock(&s->__lock);
1340         } else {
1341                 astman_send_error(s, m, "Must specify AuthType");
1342         }
1343         return 0;
1344 }
1345
1346 static char mandescr_hangup[] =
1347 "Description: Hangup a channel\n"
1348 "Variables: \n"
1349 "       Channel: The channel name to be hungup\n";
1350
1351 static int action_hangup(struct mansession *s, const struct message *m)
1352 {
1353         struct ast_channel *c = NULL;
1354         const char *name = astman_get_header(m, "Channel");
1355         if (ast_strlen_zero(name)) {
1356                 astman_send_error(s, m, "No channel specified");
1357                 return 0;
1358         }
1359         c = ast_get_channel_by_name_locked(name);
1360         if (!c) {
1361                 astman_send_error(s, m, "No such channel");
1362                 return 0;
1363         }
1364         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1365         ast_channel_unlock(c);
1366         astman_send_ack(s, m, "Channel Hungup");
1367         return 0;
1368 }
1369
1370 static char mandescr_setvar[] =
1371 "Description: Set a global or local channel variable.\n"
1372 "Variables: (Names marked with * are required)\n"
1373 "       Channel: Channel to set variable for\n"
1374 "       *Variable: Variable name\n"
1375 "       *Value: Value\n";
1376
1377 static int action_setvar(struct mansession *s, const struct message *m)
1378 {
1379         struct ast_channel *c = NULL;
1380         const char *name = astman_get_header(m, "Channel");
1381         const char *varname = astman_get_header(m, "Variable");
1382         const char *varval = astman_get_header(m, "Value");
1383
1384         if (ast_strlen_zero(varname)) {
1385                 astman_send_error(s, m, "No variable specified");
1386                 return 0;
1387         }
1388
1389         if (ast_strlen_zero(varval)) {
1390                 astman_send_error(s, m, "No value specified");
1391                 return 0;
1392         }
1393
1394         if (!ast_strlen_zero(name)) {
1395                 c = ast_get_channel_by_name_locked(name);
1396                 if (!c) {
1397                         astman_send_error(s, m, "No such channel");
1398                         return 0;
1399                 }
1400         }
1401
1402         pbx_builtin_setvar_helper(c, varname, varval);
1403
1404         if (c)
1405                 ast_channel_unlock(c);
1406
1407         astman_send_ack(s, m, "Variable Set");
1408
1409         return 0;
1410 }
1411
1412 static char mandescr_getvar[] =
1413 "Description: Get the value of a global or local channel variable.\n"
1414 "Variables: (Names marked with * are required)\n"
1415 "       Channel: Channel to read variable from\n"
1416 "       *Variable: Variable name\n"
1417 "       ActionID: Optional Action id for message matching.\n";
1418
1419 static int action_getvar(struct mansession *s, const struct message *m)
1420 {
1421         struct ast_channel *c = NULL;
1422         const char *name = astman_get_header(m, "Channel");
1423         const char *varname = astman_get_header(m, "Variable");
1424         char *varval;
1425         char workspace[1024];
1426
1427         if (ast_strlen_zero(varname)) {
1428                 astman_send_error(s, m, "No variable specified");
1429                 return 0;
1430         }
1431
1432         if (!ast_strlen_zero(name)) {
1433                 c = ast_get_channel_by_name_locked(name);
1434                 if (!c) {
1435                         astman_send_error(s, m, "No such channel");
1436                         return 0;
1437                 }
1438         }
1439
1440         if (varname[strlen(varname) - 1] == ')') {
1441                 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1442         } else {
1443                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1444         }
1445
1446         if (c)
1447                 ast_channel_unlock(c);
1448         astman_start_ack(s, m);
1449         astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1450
1451         return 0;
1452 }
1453
1454
1455 /*! \brief Manager "status" command to show channels */
1456 /* Needs documentation... */
1457 static int action_status(struct mansession *s, const struct message *m)
1458 {
1459         const char *name = astman_get_header(m,"Channel");
1460         struct ast_channel *c;
1461         char bridge[256];
1462         struct timeval now = ast_tvnow();
1463         long elapsed_seconds = 0;
1464         int all = ast_strlen_zero(name); /* set if we want all channels */
1465         const char *id = astman_get_header(m,"ActionID");
1466         char idText[256] = "";
1467
1468         if (!ast_strlen_zero(id))
1469                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1470
1471         astman_send_ack(s, m, "Channel status will follow");
1472         if (all)
1473                 c = ast_channel_walk_locked(NULL);
1474         else {
1475                 c = ast_get_channel_by_name_locked(name);
1476                 if (!c) {
1477                         astman_send_error(s, m, "No such channel");
1478                         return 0;
1479                 }
1480         }
1481         /* if we look by name, we break after the first iteration */
1482         while (c) {
1483                 if (c->_bridge)
1484                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1485                 else
1486                         bridge[0] = '\0';
1487                 if (c->pbx) {
1488                         if (c->cdr) {
1489                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1490                         }
1491                         astman_append(s,
1492                         "Event: Status\r\n"
1493                         "Privilege: Call\r\n"
1494                         "Channel: %s\r\n"
1495                         "CallerIDNum: %s\r\n"
1496                         "CallerIDName: %s\r\n"
1497                         "Account: %s\r\n"
1498                         "State: %s\r\n"
1499                         "Context: %s\r\n"
1500                         "Extension: %s\r\n"
1501                         "Priority: %d\r\n"
1502                         "Seconds: %ld\r\n"
1503                         "%s"
1504                         "Uniqueid: %s\r\n"
1505                         "%s"
1506                         "\r\n",
1507                         c->name,
1508                         S_OR(c->cid.cid_num, "<unknown>"),
1509                         S_OR(c->cid.cid_name, "<unknown>"),
1510                         c->accountcode,
1511                         ast_state2str(c->_state), c->context,
1512                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1513                 } else {
1514                         astman_append(s,
1515                         "Event: Status\r\n"
1516                         "Privilege: Call\r\n"
1517                         "Channel: %s\r\n"
1518                         "CallerIDNum: %s\r\n"
1519                         "CallerIDName: %s\r\n"
1520                         "Account: %s\r\n"
1521                         "State: %s\r\n"
1522                         "%s"
1523                         "Uniqueid: %s\r\n"
1524                         "%s"
1525                         "\r\n",
1526                         c->name,
1527                         S_OR(c->cid.cid_num, "<unknown>"),
1528                         S_OR(c->cid.cid_name, "<unknown>"),
1529                         c->accountcode,
1530                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1531                 }
1532                 ast_channel_unlock(c);
1533                 if (!all)
1534                         break;
1535                 c = ast_channel_walk_locked(c);
1536         }
1537         astman_append(s,
1538         "Event: StatusComplete\r\n"
1539         "%s"
1540         "\r\n",idText);
1541         return 0;
1542 }
1543
1544 static char mandescr_sendtext[] =
1545 "Description: Sends A Text Message while in a call.\n"
1546 "Variables: (Names marked with * are required)\n"
1547 "       *Channel: Channel to send message to\n"
1548 "       *Message: Message to send\n"
1549 "       ActionID: Optional Action id for message matching.\n";
1550
1551 static int action_sendtext(struct mansession *s, const struct message *m)
1552 {
1553         struct ast_channel *c = NULL;
1554         const char *name = astman_get_header(m, "Channel");
1555         const char *textmsg = astman_get_header(m, "Message");
1556         int res = 0;
1557
1558         if (ast_strlen_zero(name)) {
1559                 astman_send_error(s, m, "No channel specified");
1560                 return 0;
1561         }
1562
1563         if (ast_strlen_zero(textmsg)) {
1564                 astman_send_error(s, m, "No Message specified");
1565                 return 0;
1566         }
1567
1568         c = ast_get_channel_by_name_locked(name);
1569         if (!c) {
1570                 astman_send_error(s, m, "No such channel");
1571                 return 0;
1572         }
1573
1574         res = ast_sendtext(c, textmsg);
1575         ast_mutex_unlock(&c->lock);
1576         
1577         if (res > 0)
1578                 astman_send_ack(s, m, "Success");
1579         else
1580                 astman_send_error(s, m, "Failure");
1581         
1582         return res;
1583 }
1584
1585 static char mandescr_redirect[] =
1586 "Description: Redirect (transfer) a call.\n"
1587 "Variables: (Names marked with * are required)\n"
1588 "       *Channel: Channel to redirect\n"
1589 "       ExtraChannel: Second call leg to transfer (optional)\n"
1590 "       *Exten: Extension to transfer to\n"
1591 "       *Context: Context to transfer to\n"
1592 "       *Priority: Priority to transfer to\n"
1593 "       ActionID: Optional Action id for message matching.\n";
1594
1595 /*! \brief  action_redirect: The redirect manager command */
1596 static int action_redirect(struct mansession *s, const struct message *m)
1597 {
1598         const char *name = astman_get_header(m, "Channel");
1599         const char *name2 = astman_get_header(m, "ExtraChannel");
1600         const char *exten = astman_get_header(m, "Exten");
1601         const char *context = astman_get_header(m, "Context");
1602         const char *priority = astman_get_header(m, "Priority");
1603         struct ast_channel *chan, *chan2 = NULL;
1604         int pi = 0;
1605         int res;
1606
1607         if (ast_strlen_zero(name)) {
1608                 astman_send_error(s, m, "Channel not specified");
1609                 return 0;
1610         }
1611         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1612                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1613                         astman_send_error(s, m, "Invalid priority\n");
1614                         return 0;
1615                 }
1616         }
1617         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1618         chan = ast_get_channel_by_name_locked(name);
1619         if (!chan) {
1620                 char buf[BUFSIZ];
1621                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1622                 astman_send_error(s, m, buf);
1623                 return 0;
1624         }
1625         if (!ast_strlen_zero(name2))
1626                 chan2 = ast_get_channel_by_name_locked(name2);
1627         res = ast_async_goto(chan, context, exten, pi);
1628         if (!res) {
1629                 if (!ast_strlen_zero(name2)) {
1630                         if (chan2)
1631                                 res = ast_async_goto(chan2, context, exten, pi);
1632                         else
1633                                 res = -1;
1634                         if (!res)
1635                                 astman_send_ack(s, m, "Dual Redirect successful");
1636                         else
1637                                 astman_send_error(s, m, "Secondary redirect failed");
1638                 } else
1639                         astman_send_ack(s, m, "Redirect successful");
1640         } else
1641                 astman_send_error(s, m, "Redirect failed");
1642         if (chan)
1643                 ast_channel_unlock(chan);
1644         if (chan2)
1645                 ast_channel_unlock(chan2);
1646         return 0;
1647 }
1648
1649 static char mandescr_command[] =
1650 "Description: Run a CLI command.\n"
1651 "Variables: (Names marked with * are required)\n"
1652 "       *Command: Asterisk CLI command to run\n"
1653 "       ActionID: Optional Action id for message matching.\n";
1654
1655 /*! \brief  Manager command "command" - execute CLI command */
1656 static int action_command(struct mansession *s, const struct message *m)
1657 {
1658         const char *cmd = astman_get_header(m, "Command");
1659         const char *id = astman_get_header(m, "ActionID");
1660         char *buf;
1661         char template[] = "/tmp/ast-ami-XXXXXX";        /* template for temporary file */
1662         int fd = mkstemp(template);
1663         off_t l;
1664
1665         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1666         if (!ast_strlen_zero(id))
1667                 astman_append(s, "ActionID: %s\r\n", id);
1668         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1669         ast_cli_command(fd, cmd);       /* XXX need to change this to use a FILE * */
1670         l = lseek(fd, 0, SEEK_END);     /* how many chars available */
1671         buf = alloca(l+1);
1672         lseek(fd, 0, SEEK_SET);
1673         read(fd, buf, l);
1674         buf[l] = '\0';
1675         close(fd);
1676         unlink(template);
1677         astman_append(s, buf);
1678         astman_append(s, "--END COMMAND--\r\n\r\n");
1679         return 0;
1680 }
1681
1682 /* helper function for originate */
1683 struct fast_originate_helper {
1684         char tech[AST_MAX_EXTENSION];
1685         char data[AST_MAX_EXTENSION];
1686         int timeout;
1687         char app[AST_MAX_APP];
1688         char appdata[AST_MAX_EXTENSION];
1689         char cid_name[AST_MAX_EXTENSION];
1690         char cid_num[AST_MAX_EXTENSION];
1691         char context[AST_MAX_CONTEXT];
1692         char exten[AST_MAX_EXTENSION];
1693         char idtext[AST_MAX_EXTENSION];
1694         char account[AST_MAX_ACCOUNT_CODE];
1695         int priority;
1696         struct ast_variable *vars;
1697 };
1698
1699 static void *fast_originate(void *data)
1700 {
1701         struct fast_originate_helper *in = data;
1702         int res;
1703         int reason = 0;
1704         struct ast_channel *chan = NULL;
1705         char requested_channel[AST_CHANNEL_NAME];
1706
1707         if (!ast_strlen_zero(in->app)) {
1708                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1709                         S_OR(in->cid_num, NULL),
1710                         S_OR(in->cid_name, NULL),
1711                         in->vars, in->account, &chan);
1712         } else {
1713                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1714                         S_OR(in->cid_num, NULL),
1715                         S_OR(in->cid_name, NULL),
1716                         in->vars, in->account, &chan);
1717         }
1718
1719         if (!chan)
1720                 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);     
1721         /* Tell the manager what happened with the channel */
1722         manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1723                 "%s"
1724                 "Response: %s\r\n"
1725                 "Channel: %s\r\n"
1726                 "Context: %s\r\n"
1727                 "Exten: %s\r\n"
1728                 "Reason: %d\r\n"
1729                 "Uniqueid: %s\r\n"
1730                 "CallerIDNum: %s\r\n"
1731                 "CallerIDName: %s\r\n",
1732                 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
1733                 chan ? chan->uniqueid : "<null>",
1734                 S_OR(in->cid_num, "<unknown>"),
1735                 S_OR(in->cid_name, "<unknown>")
1736                 );
1737
1738         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1739         if (chan)
1740                 ast_channel_unlock(chan);
1741         free(in);
1742         return NULL;
1743 }
1744
1745 static char mandescr_originate[] =
1746 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1747 "  Application/Data\n"
1748 "Variables: (Names marked with * are required)\n"
1749 "       *Channel: Channel name to call\n"
1750 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1751 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1752 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1753 "       Application: Application to use\n"
1754 "       Data: Data to use (requires 'Application')\n"
1755 "       Timeout: How long to wait for call to be answered (in ms)\n"
1756 "       CallerID: Caller ID to be set on the outgoing channel\n"
1757 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1758 "       Account: Account code\n"
1759 "       Async: Set to 'true' for fast origination\n";
1760
1761 static int action_originate(struct mansession *s, const struct message *m)
1762 {
1763         const char *name = astman_get_header(m, "Channel");
1764         const char *exten = astman_get_header(m, "Exten");
1765         const char *context = astman_get_header(m, "Context");
1766         const char *priority = astman_get_header(m, "Priority");
1767         const char *timeout = astman_get_header(m, "Timeout");
1768         const char *callerid = astman_get_header(m, "CallerID");
1769         const char *account = astman_get_header(m, "Account");
1770         const char *app = astman_get_header(m, "Application");
1771         const char *appdata = astman_get_header(m, "Data");
1772         const char *async = astman_get_header(m, "Async");
1773         const char *id = astman_get_header(m, "ActionID");
1774         struct ast_variable *vars = astman_get_variables(m);
1775         char *tech, *data;
1776         char *l = NULL, *n = NULL;
1777         int pi = 0;
1778         int res;
1779         int to = 30000;
1780         int reason = 0;
1781         char tmp[256];
1782         char tmp2[256];
1783
1784         pthread_t th;
1785         pthread_attr_t attr;
1786         if (!name) {
1787                 astman_send_error(s, m, "Channel not specified");
1788                 return 0;
1789         }
1790         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1791                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1792                         astman_send_error(s, m, "Invalid priority\n");
1793                         return 0;
1794                 }
1795         }
1796         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1797                 astman_send_error(s, m, "Invalid timeout\n");
1798                 return 0;
1799         }
1800         ast_copy_string(tmp, name, sizeof(tmp));
1801         tech = tmp;
1802         data = strchr(tmp, '/');
1803         if (!data) {
1804                 astman_send_error(s, m, "Invalid channel\n");
1805                 return 0;
1806         }
1807         *data++ = '\0';
1808         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1809         ast_callerid_parse(tmp2, &n, &l);
1810         if (n) {
1811                 if (ast_strlen_zero(n))
1812                         n = NULL;
1813         }
1814         if (l) {
1815                 ast_shrink_phone_number(l);
1816                 if (ast_strlen_zero(l))
1817                         l = NULL;
1818         }
1819         if (ast_true(async)) {
1820                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1821                 if (!fast) {
1822                         res = -1;
1823                 } else {
1824                         if (!ast_strlen_zero(id))
1825                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1826                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1827                         ast_copy_string(fast->data, data, sizeof(fast->data));
1828                         ast_copy_string(fast->app, app, sizeof(fast->app));
1829                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1830                         if (l)
1831                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1832                         if (n)
1833                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1834                         fast->vars = vars;
1835                         ast_copy_string(fast->context, context, sizeof(fast->context));
1836                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1837                         ast_copy_string(fast->account, account, sizeof(fast->account));
1838                         fast->timeout = to;
1839                         fast->priority = pi;
1840                         pthread_attr_init(&attr);
1841                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1842                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1843                                 res = -1;
1844                         } else {
1845                                 res = 0;
1846                         }
1847                 }
1848         } else if (!ast_strlen_zero(app)) {
1849                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1850         } else {
1851                 if (exten && context && pi)
1852                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1853                 else {
1854                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1855                         return 0;
1856                 }
1857         }
1858         if (!res)
1859                 astman_send_ack(s, m, "Originate successfully queued");
1860         else
1861                 astman_send_error(s, m, "Originate failed");
1862         return 0;
1863 }
1864
1865 /*! \brief Help text for manager command mailboxstatus
1866  */
1867 static char mandescr_mailboxstatus[] =
1868 "Description: Checks a voicemail account for status.\n"
1869 "Variables: (Names marked with * are required)\n"
1870 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1871 "       ActionID: Optional ActionID for message matching.\n"
1872 "Returns number of messages.\n"
1873 "       Message: Mailbox Status\n"
1874 "       Mailbox: <mailboxid>\n"
1875 "       Waiting: <count>\n"
1876 "\n";
1877
1878 static int action_mailboxstatus(struct mansession *s, const struct message *m)
1879 {
1880         const char *mailbox = astman_get_header(m, "Mailbox");
1881         int ret;
1882
1883         if (ast_strlen_zero(mailbox)) {
1884                 astman_send_error(s, m, "Mailbox not specified");
1885                 return 0;
1886         }
1887         ret = ast_app_has_voicemail(mailbox, NULL);
1888         astman_start_ack(s, m);
1889         astman_append(s, "Message: Mailbox Status\r\n"
1890                          "Mailbox: %s\r\n"
1891                          "Waiting: %d\r\n\r\n", mailbox, ret);
1892         return 0;
1893 }
1894
1895 static char mandescr_mailboxcount[] =
1896 "Description: Checks a voicemail account for new messages.\n"
1897 "Variables: (Names marked with * are required)\n"
1898 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1899 "       ActionID: Optional ActionID for message matching.\n"
1900 "Returns number of new and old messages.\n"
1901 "       Message: Mailbox Message Count\n"
1902 "       Mailbox: <mailboxid>\n"
1903 "       NewMessages: <count>\n"
1904 "       OldMessages: <count>\n"
1905 "\n";
1906 static int action_mailboxcount(struct mansession *s, const struct message *m)
1907 {
1908         const char *mailbox = astman_get_header(m, "Mailbox");
1909         int newmsgs = 0, oldmsgs = 0;
1910
1911         if (ast_strlen_zero(mailbox)) {
1912                 astman_send_error(s, m, "Mailbox not specified");
1913                 return 0;
1914         }
1915         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1916         astman_start_ack(s, m);
1917         astman_append(s,   "Message: Mailbox Message Count\r\n"
1918                            "Mailbox: %s\r\n"
1919                            "NewMessages: %d\r\n"
1920                            "OldMessages: %d\r\n"
1921                            "\r\n",
1922                            mailbox, newmsgs, oldmsgs);
1923         return 0;
1924 }
1925
1926 static char mandescr_extensionstate[] =
1927 "Description: Report the extension state for given extension.\n"
1928 "  If the extension has a hint, will use devicestate to check\n"
1929 "  the status of the device connected to the extension.\n"
1930 "Variables: (Names marked with * are required)\n"
1931 "       *Exten: Extension to check state on\n"
1932 "       *Context: Context for extension\n"
1933 "       ActionId: Optional ID for this transaction\n"
1934 "Will return an \"Extension Status\" message.\n"
1935 "The response will include the hint for the extension and the status.\n";
1936
1937 static int action_extensionstate(struct mansession *s, const struct message *m)
1938 {
1939         const char *exten = astman_get_header(m, "Exten");
1940         const char *context = astman_get_header(m, "Context");
1941         char hint[256] = "";
1942         int status;
1943         if (ast_strlen_zero(exten)) {
1944                 astman_send_error(s, m, "Extension not specified");
1945                 return 0;
1946         }
1947         if (ast_strlen_zero(context))
1948                 context = "default";
1949         status = ast_extension_state(NULL, context, exten);
1950         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1951         astman_start_ack(s, m);
1952         astman_append(s,   "Message: Extension Status\r\n"
1953                            "Exten: %s\r\n"
1954                            "Context: %s\r\n"
1955                            "Hint: %s\r\n"
1956                            "Status: %d\r\n\r\n",
1957                            exten, context, hint, status);
1958         return 0;
1959 }
1960
1961 static char mandescr_timeout[] =
1962 "Description: Hangup a channel after a certain time.\n"
1963 "Variables: (Names marked with * are required)\n"
1964 "       *Channel: Channel name to hangup\n"
1965 "       *Timeout: Maximum duration of the call (sec)\n"
1966 "Acknowledges set time with 'Timeout Set' message\n";
1967
1968 static int action_timeout(struct mansession *s, const struct message *m)
1969 {
1970         struct ast_channel *c;
1971         const char *name = astman_get_header(m, "Channel");
1972         int timeout = atoi(astman_get_header(m, "Timeout"));
1973
1974         if (ast_strlen_zero(name)) {
1975                 astman_send_error(s, m, "No channel specified");
1976                 return 0;
1977         }
1978         if (!timeout) {
1979                 astman_send_error(s, m, "No timeout specified");
1980                 return 0;
1981         }
1982         c = ast_get_channel_by_name_locked(name);
1983         if (!c) {
1984                 astman_send_error(s, m, "No such channel");
1985                 return 0;
1986         }
1987         ast_channel_setwhentohangup(c, timeout);
1988         ast_channel_unlock(c);
1989         astman_send_ack(s, m, "Timeout Set");
1990         return 0;
1991 }
1992
1993 /*!
1994  * Send any applicable events to the client listening on this socket.
1995  * Wait only for a finite time on each event, and drop all events whether
1996  * they are successfully sent or not.
1997  */
1998 static int process_events(struct mansession *s)
1999 {
2000         int ret = 0;
2001
2002         ast_mutex_lock(&s->__lock);
2003         if (s->f != NULL) {
2004                 struct eventqent *eqe;
2005
2006                 while ( (eqe = NEW_EVENT(s)) ) {
2007                         ref_event(eqe);
2008                         if (!ret && s->authenticated &&
2009                             (s->readperm & eqe->category) == eqe->category &&
2010                             (s->send_events & eqe->category) == eqe->category) {
2011                                 if (send_string(s, eqe->eventdata) < 0)
2012                                         ret = -1;       /* don't send more */
2013                         }
2014                         s->last_ev = unref_event(s->last_ev);
2015                 }
2016         }
2017         ast_mutex_unlock(&s->__lock);
2018         return ret;
2019 }
2020
2021 static char mandescr_userevent[] =
2022 "Description: Send an event to manager sessions.\n"
2023 "Variables: (Names marked with * are required)\n"
2024 "       *UserEvent: EventStringToSend\n"
2025 "       Header1: Content1\n"
2026 "       HeaderN: ContentN\n";
2027
2028 static int action_userevent(struct mansession *s, const struct message *m)
2029 {
2030         const char *event = astman_get_header(m, "UserEvent");
2031         char body[2048] = "";
2032         int x, bodylen = 0;
2033         for (x = 0; x < m->hdrcount; x++) {
2034                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2035                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2036                         bodylen += strlen(m->headers[x]);
2037                         ast_copy_string(body + bodylen, "\r\n", 3);
2038                         bodylen += 2;
2039                 }
2040         }
2041
2042         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2043         return 0;
2044 }
2045
2046 /*
2047  * Done with the action handlers here, we start with the code in charge
2048  * of accepting connections and serving them.
2049  * accept_thread() forks a new thread for each connection, session_do(),
2050  * which in turn calls get_input() repeatedly until a full message has
2051  * been accumulated, and then invokes process_message() to pass it to
2052  * the appropriate handler.
2053  */
2054
2055 /*
2056  * Process an AMI message, performing desired action.
2057  * Return 0 on success, -1 on error that require the session to be destroyed.
2058  */
2059 static int process_message(struct mansession *s, const struct message *m)
2060 {
2061         char action[80] = "";
2062         int ret = 0;
2063         struct manager_action *tmp;
2064
2065         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2066         if (option_debug)
2067                 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
2068
2069         if (ast_strlen_zero(action)) {
2070                 astman_send_error(s, m, "Missing action in request");
2071                 return 0;
2072         }
2073
2074         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2075                 astman_send_error(s, m, "Permission denied");
2076                 return 0;
2077         }
2078         /* XXX should we protect the list navigation ? */
2079         for (tmp = first_action ; tmp; tmp = tmp->next) {
2080                 if (!strcasecmp(action, tmp->action)) {
2081                         if ((s->writeperm & tmp->authority) == tmp->authority) {
2082                                 if (tmp->func(s, m))    /* error */
2083                                         return -1;
2084                         } else {
2085                                 astman_send_error(s, m, "Permission denied");
2086                         }
2087                         break;
2088                 }
2089         }
2090         if (!tmp)
2091                 astman_send_error(s, m, "Invalid/unknown command. Use Action: ListCommands to show available commands.");
2092         if (ret)
2093                 return ret;
2094         /* Once done with our message, deliver any pending events */
2095         return process_events(s);
2096 }
2097
2098 /*!
2099  * Read one full line (including crlf) from the manager socket.
2100  * \r\n is the only valid terminator for the line.
2101  * (Note that, later, '\0' will be considered as the end-of-line marker,
2102  * so everything between the '\0' and the '\r\n' will not be used).
2103  * Also note that we assume output to have at least "maxlen" space.
2104  */
2105 static int get_input(struct mansession *s, char *output)
2106 {
2107         int res, x;
2108         int maxlen = sizeof(s->inbuf) - 1;
2109         char *src = s->inbuf;
2110
2111         /*
2112          * Look for \r\n within the buffer. If found, copy to the output
2113          * buffer and return, trimming the \r\n (not used afterwards).
2114          */
2115         for (x = 0; x < s->inlen; x++) {
2116                 int cr; /* set if we have \r */
2117                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2118                         cr = 2; /* Found. Update length to include \r\n */
2119                 else if (src[x] == '\n')
2120                         cr = 1; /* also accept \n only */
2121                 else
2122                         continue;
2123                 memmove(output, src, x);        /*... but trim \r\n */
2124                 output[x] = '\0';               /* terminate the string */
2125                 x += cr;                        /* number of bytes used */
2126                 s->inlen -= x;                  /* remaining size */
2127                 memmove(src, src + x, s->inlen); /* remove used bytes */
2128                 return 1;
2129         }
2130         if (s->inlen >= maxlen) {
2131                 /* no crlf found, and buffer full - sorry, too long for us */
2132                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2133                 s->inlen = 0;
2134         }
2135         res = 0;
2136         while (res == 0) {
2137                 /* XXX do we really need this locking ? */
2138                 ast_mutex_lock(&s->__lock);
2139                 s->waiting_thread = pthread_self();
2140                 ast_mutex_unlock(&s->__lock);
2141
2142                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
2143
2144                 ast_mutex_lock(&s->__lock);
2145                 s->waiting_thread = AST_PTHREADT_NULL;
2146                 ast_mutex_unlock(&s->__lock);
2147         }
2148         if (res < 0) {
2149                 /* If we get a signal from some other thread (typically because
2150                  * there are new events queued), return 0 to notify the caller.
2151                  */
2152                 if (errno == EINTR)
2153                         return 0;
2154                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2155                 return -1;
2156         }
2157         ast_mutex_lock(&s->__lock);
2158         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2159         if (res < 1)
2160                 res = -1;       /* error return */
2161         else {
2162                 s->inlen += res;
2163                 src[s->inlen] = '\0';
2164                 res = 0;
2165         }
2166         ast_mutex_unlock(&s->__lock);
2167         return res;
2168 }
2169
2170 static int do_message(struct mansession *s)
2171 {
2172         struct message m = { 0 };
2173         char header_buf[sizeof(s->inbuf)] = { '\0' };
2174         int res;
2175
2176         for (;;) {
2177                 /* Check if any events are pending and do them if needed */
2178                 if (process_events(s))
2179                         return -1;
2180                 res = get_input(s, header_buf);
2181                 if (res == 0) {
2182                         continue;
2183                 } else if (res > 0) {
2184                         if (ast_strlen_zero(header_buf))
2185                                 return process_message(s, &m) ? -1 : 0;
2186                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2187                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2188                 } else {
2189                         return res;
2190                 }
2191         }
2192 }
2193
2194 /*! \brief The body of the individual manager session.
2195  * Call get_input() to read one line at a time
2196  * (or be woken up on new events), collect the lines in a
2197  * message until found an empty line, and execute the request.
2198  * In any case, deliver events asynchronously through process_events()
2199  * (called from here if no line is available, or at the end of
2200  * process_message(). )
2201  */
2202 static void *session_do(void *data)
2203 {
2204         struct server_instance *ser = data;
2205         struct mansession *s = ast_calloc(1, sizeof(*s));
2206         int flags;
2207         int res;
2208
2209         if (s == NULL)
2210                 goto done;
2211
2212         s->writetimeout = 100;
2213         s->waiting_thread = AST_PTHREADT_NULL;
2214
2215         flags = fcntl(ser->fd, F_GETFL);
2216         if (!block_sockets) /* make sure socket is non-blocking */
2217                 flags |= O_NONBLOCK;
2218         else
2219                 flags &= ~O_NONBLOCK;
2220         fcntl(ser->fd, F_SETFL, flags);
2221
2222         ast_mutex_init(&s->__lock);
2223         s->send_events = -1;
2224         /* these fields duplicate those in the 'ser' structure */
2225         s->fd = ser->fd;
2226         s->f = ser->f;
2227         s->sin = ser->requestor;
2228
2229         ast_atomic_fetchadd_int(&num_sessions, 1);
2230         AST_LIST_LOCK(&sessions);
2231         AST_LIST_INSERT_HEAD(&sessions, s, list);
2232         AST_LIST_UNLOCK(&sessions);
2233         /* Hook to the tail of the event queue */
2234         s->last_ev = grab_last();
2235         ast_mutex_lock(&s->__lock);
2236         s->f = ser->f;
2237         astman_append(s, "Asterisk Call Manager/1.0\r\n");      /* welcome prompt */
2238         ast_mutex_unlock(&s->__lock);
2239         for (;;) {
2240                 if ((res = do_message(s)) < 0)
2241                         break;
2242         }
2243         /* session is over, explain why and terminate */
2244         if (s->authenticated) {
2245                 if (option_verbose > 1) {
2246                         if (displayconnects)
2247                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2248                 }
2249                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2250         } else {
2251                 if (option_verbose > 1) {
2252                         if (displayconnects)
2253                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2254                 }
2255                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2256         }
2257         destroy_session(s);
2258
2259 done:
2260         free(ser);
2261         return NULL;
2262 }
2263
2264 /*! \brief remove at most n_max stale session from the list. */
2265 static void purge_sessions(int n_max)
2266 {
2267         struct mansession *s;
2268         time_t now = time(NULL);
2269
2270         AST_LIST_LOCK(&sessions);
2271         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2272                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2273                         AST_LIST_REMOVE_CURRENT(&sessions, list);
2274                         ast_atomic_fetchadd_int(&num_sessions, -1);
2275                         if (s->authenticated && (option_verbose > 1) && displayconnects) {
2276                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2277                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2278                         }
2279                         free_session(s);        /* XXX outside ? */
2280                         if (--n_max <= 0)
2281                                 break;
2282                 }
2283         }
2284         AST_LIST_TRAVERSE_SAFE_END
2285         AST_LIST_UNLOCK(&sessions);
2286 }
2287
2288 /*
2289  * events are appended to a queue from where they
2290  * can be dispatched to clients.
2291  */
2292 static int append_event(const char *str, int category)
2293 {
2294         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2295         static int seq; /* sequence number */
2296
2297         if (!tmp)
2298                 return -1;
2299
2300         /* need to init all fields, because ast_malloc() does not */
2301         tmp->usecount = 0;
2302         tmp->category = category;
2303         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2304         AST_LIST_NEXT(tmp, eq_next) = NULL;
2305         strcpy(tmp->eventdata, str);
2306
2307         AST_LIST_LOCK(&all_events);
2308         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2309         AST_LIST_UNLOCK(&all_events);
2310
2311         return 0;
2312 }
2313
2314 /* XXX see if can be moved inside the function */
2315 AST_THREADSTORAGE(manager_event_buf);
2316 #define MANAGER_EVENT_BUF_INITSIZE   256
2317
2318 /*! \brief  manager_event: Send AMI event to client */
2319 int __manager_event(int category, const char *event,
2320         const char *file, int line, const char *func, const char *fmt, ...)
2321 {
2322         struct mansession *s;
2323         struct manager_custom_hook *hook;
2324         struct ast_str *auth = ast_str_alloca(80);
2325         const char *cat_str;
2326         va_list ap;
2327         struct timeval now;
2328         struct ast_str *buf;
2329
2330         /* Abort if there aren't any manager sessions */
2331         if (!num_sessions)
2332                 return 0;
2333
2334         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2335                 return -1;
2336
2337         cat_str = authority_to_str(category, &auth);
2338         ast_str_set(&buf, 0,
2339                         "Event: %s\r\nPrivilege: %s\r\n",
2340                          event, cat_str);
2341
2342         if (timestampevents) {
2343                 now = ast_tvnow();
2344                 ast_str_append(&buf, 0,
2345                                 "Timestamp: %ld.%06lu\r\n",
2346                                  now.tv_sec, (unsigned long) now.tv_usec);
2347         }
2348         if (manager_debug) {
2349                 static int seq;
2350                 ast_str_append(&buf, 0,
2351                                 "SequenceNumber: %d\r\n",
2352                                  ast_atomic_fetchadd_int(&seq, 1));
2353                 ast_str_append(&buf, 0,
2354                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2355         }
2356
2357         va_start(ap, fmt);
2358         ast_str_append_va(&buf, 0, fmt, ap);
2359         va_end(ap);
2360
2361         ast_str_append(&buf, 0, "\r\n");
2362
2363         append_event(buf->str, category);
2364
2365         /* Wake up any sleeping sessions */
2366         AST_LIST_LOCK(&sessions);
2367         AST_LIST_TRAVERSE(&sessions, s, list) {
2368                 ast_mutex_lock(&s->__lock);
2369                 if (s->waiting_thread != AST_PTHREADT_NULL)
2370                         pthread_kill(s->waiting_thread, SIGURG);
2371                 ast_mutex_unlock(&s->__lock);
2372         }
2373         AST_LIST_UNLOCK(&sessions);
2374
2375         AST_RWLIST_RDLOCK(&manager_hooks);
2376         if (!AST_RWLIST_EMPTY(&manager_hooks)) {
2377                 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2378                         hook->helper(category, event, buf->str);
2379                 }
2380         }
2381         AST_RWLIST_UNLOCK(&manager_hooks);
2382
2383         return 0;
2384 }
2385
2386 /*
2387  * support functions to register/unregister AMI action handlers,
2388  */
2389 int ast_manager_unregister(char *action)
2390 {
2391         struct manager_action *cur = first_action, *prev = first_action;
2392
2393         ast_mutex_lock(&actionlock);
2394         for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2395                 if (!strcasecmp(action, cur->action)) {
2396                         if (prev)
2397                                 prev->next = cur->next;
2398                         else
2399                                 first_action = cur->next;
2400                         free(cur);
2401                         if (option_verbose > 1)
2402                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2403                         break;
2404                 }
2405         }
2406         ast_mutex_unlock(&actionlock);
2407         return 0;
2408 }
2409
2410 static int manager_state_cb(char *context, char *exten, int state, void *data)
2411 {
2412         /* Notify managers of change */
2413         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2414         return 0;
2415 }
2416
2417 static int ast_manager_register_struct(struct manager_action *act)
2418 {
2419         struct manager_action *cur, *prev = NULL;
2420         int ret;
2421
2422         ast_mutex_lock(&actionlock);
2423         for (cur = first_action; cur; prev = cur, cur = cur->next) {
2424                 ret = strcasecmp(cur->action, act->action);
2425                 if (ret == 0) {
2426                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2427                         ast_mutex_unlock(&actionlock);
2428                         return -1;
2429                 }
2430                 if (ret > 0)    /* Insert these alphabetically */
2431                         break;
2432         }
2433         if (prev)
2434                 prev->next = act;
2435         else
2436                 first_action = act;
2437         act->next = cur;
2438
2439         if (option_verbose > 1)
2440                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2441         ast_mutex_unlock(&actionlock);
2442         return 0;
2443 }
2444
2445 /*! \brief register a new command with manager, including online help. This is
2446         the preferred way to register a manager command */
2447 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2448 {
2449         struct manager_action *cur;
2450
2451         cur = ast_malloc(sizeof(*cur));
2452         if (!cur)
2453                 return -1;
2454
2455         cur->action = action;
2456         cur->authority = auth;
2457         cur->func = func;
2458         cur->synopsis = synopsis;
2459         cur->description = description;
2460         cur->next = NULL;
2461
2462         ast_manager_register_struct(cur);
2463
2464         return 0;
2465 }
2466 /*! @}
2467  END Doxygen group */
2468
2469 /*
2470  * The following are support functions for AMI-over-http.
2471  * The common entry point is generic_http_callback(),
2472  * which extracts HTTP header and URI fields and reformats
2473  * them into AMI messages, locates a proper session
2474  * (using the mansession_id Cookie or GET variable),
2475  * and calls process_message() as for regular AMI clients.
2476  * When done, the output (which goes to a temporary file)
2477  * is read back into a buffer and reformatted as desired,
2478  * then fed back to the client over the original socket.
2479  */
2480
2481 enum output_format {
2482         FORMAT_RAW,
2483         FORMAT_HTML,
2484         FORMAT_XML,
2485 };
2486
2487 static char *contenttype[] = {
2488         [FORMAT_RAW] = "plain",
2489         [FORMAT_HTML] = "html",
2490         [FORMAT_XML] =  "xml",
2491 };
2492
2493 /*!
2494  * locate an http session in the list. The search key (ident) is
2495  * the value of the mansession_id cookie (0 is not valid and means
2496  * a session on the AMI socket).
2497  */
2498 static struct mansession *find_session(unsigned long ident)
2499 {
2500         struct mansession *s;
2501
2502         if (ident == 0)
2503                 return NULL;
2504
2505         AST_LIST_LOCK(&sessions);
2506         AST_LIST_TRAVERSE(&sessions, s, list) {
2507                 ast_mutex_lock(&s->__lock);
2508                 if (s->managerid == ident && !s->needdestroy) {
2509                         ast_atomic_fetchadd_int(&s->inuse, 1);
2510                         break;
2511                 }
2512                 ast_mutex_unlock(&s->__lock);
2513         }
2514         AST_LIST_UNLOCK(&sessions);
2515
2516         return s;
2517 }
2518
2519 /*
2520  * convert to xml with various conversion:
2521  * mode & 1     -> lowercase;
2522  * mode & 2     -> replace non-alphanumeric chars with underscore
2523  */
2524 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
2525 {
2526         /* store in a local buffer to avoid calling ast_str_append too often */
2527         char buf[256];
2528         char *dst = buf;
2529         int space = sizeof(buf);
2530         /* repeat until done and nothing to flush */
2531         for ( ; *src || dst != buf ; src++) {
2532                 if (*src == '\0' || space < 10) {       /* flush */
2533                         *dst++ = '\0';
2534                         ast_str_append(out, 0, "%s", buf);
2535                         dst = buf;
2536                         space = sizeof(buf);
2537                         if (*src == '\0')
2538                                 break;
2539                 }
2540                         
2541                 if ( (mode & 2) && !isalnum(*src)) {
2542                         *dst++ = '_';
2543                         space--;
2544                         continue;
2545                 }
2546                 switch (*src) {
2547                 case '<':
2548                         strcpy(dst, "&lt;");
2549                         dst += 4;
2550                         space -= 4;
2551                         break;
2552                 case '>':
2553                         strcpy(dst, "&gt;");
2554                         dst += 4;
2555                         space -= 4;
2556                         break;
2557                 case '\"':
2558                         strcpy(dst, "&quot;");
2559                         dst += 6;
2560                         space -= 6;
2561                         break;
2562                 case '\'':
2563                         strcpy(dst, "&apos;");
2564                         dst += 6;
2565                         space -= 6;
2566                         break;
2567                 case '&':
2568                         strcpy(dst, "&amp;");
2569                         dst += 5;
2570                         space -= 5;
2571                         break;
2572
2573                 default:
2574                         *dst++ = mode ? tolower(*src) : *src;
2575                         space--;
2576                 }
2577         }
2578 }
2579
2580 /*! \brief Convert the input into XML or HTML.
2581  * The input is supposed to be a sequence of lines of the form
2582  *      Name: value
2583  * optionally followed by a blob of unformatted text.
2584  * A blank line is a section separator. Basically, this is a
2585  * mixture of the format of Manager Interface and CLI commands.
2586  * The unformatted text is considered as a single value of a field
2587  * named 'Opaque-data'.
2588  *
2589  * At the moment the output format is the following (but it may
2590  * change depending on future requirements so don't count too
2591  * much on it when writing applications):
2592  *
2593  * General: the unformatted text is used as a value of
2594  * XML output:  to be completed
2595  *   Each section is within <response type="object" id="xxx">
2596  *   where xxx is taken from ajaxdest variable or defaults to unknown
2597  *   Each row is reported as an attribute Name="value" of an XML
2598  *   entity named from the variable ajaxobjtype, default to "generic"
2599  *
2600  * HTML output:
2601  *   each Name-value pair is output as a single row of a two-column table.
2602  *   Sections (blank lines in the input) are separated by a <HR>
2603  *
2604  */
2605 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
2606 {
2607         struct ast_variable *v;
2608         char *dest = NULL;
2609         char *var, *val;
2610         char *objtype = NULL;
2611         int in_data = 0;        /* parsing data */
2612         int inobj = 0;
2613         int xml = (format == FORMAT_XML);
2614
2615         for (v = vars; v; v = v->next) {
2616                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
2617                         dest = v->value;
2618                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
2619                         objtype = v->value;
2620         }
2621         if (!dest)
2622                 dest = "unknown";
2623         if (!objtype)
2624                 objtype = "generic";
2625 #if 0
2626         /* determine how large is the response.
2627          * This is a heuristic - counting colons (for headers),
2628          * newlines (for extra arguments), and escaped chars.
2629          * XXX needs to be checked carefully for overflows.
2630          * Even better, use some code that allows extensible strings.
2631          */
2632         for (x = 0; in[x]; x++) {
2633                 if (in[x] == ':')
2634                         colons++;
2635                 else if (in[x] == '\n')
2636                         breaks++;
2637                 else if (strchr("&\"<>", in[x]))
2638                         escaped++;
2639         }
2640         len = (size_t) (1 + strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
2641         out = ast_malloc(len);
2642         if (!out)
2643                 return NULL;
2644         tmp = out;
2645         *tmp = '\0';
2646 #endif
2647         /* we want to stop when we find an empty line */
2648         while (in && *in) {
2649                 val = strsep(&in, "\r\n");      /* mark start and end of line */
2650                 if (in && *in == '\n')          /* remove trailing \n if any */
2651                         in++;
2652                 ast_trim_blanks(val);
2653                 if (0)
2654                         ast_verbose("inobj %d in_data %d line <%s>\n", inobj, in_data, val);
2655                 if (ast_strlen_zero(val)) {
2656                         if (in_data) { /* close data */
2657                                 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
2658                                 in_data = 0;
2659                         }
2660                         ast_str_append(out, 0, xml ? " /></response>\n" :
2661                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2662                         inobj = 0;
2663                         continue;
2664                 }
2665                 /* we expect Name: value lines */
2666                 if (in_data) {
2667                         var = NULL;
2668                 } else {
2669                         var = strsep(&val, ":");
2670                         if (val) {      /* found the field name */
2671                                 val = ast_skip_blanks(val);
2672                                 ast_trim_blanks(var);
2673                         } else {                /* field name not found, move to opaque mode */
2674                                 val = var;
2675                                 var = "Opaque-data";
2676                         }
2677                 }
2678                 if (!inobj) {
2679                         if (xml)
2680                                 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
2681                         else
2682                                 ast_str_append(out, 0, "<body>\n");
2683                         inobj = 1;
2684                 }
2685                 if (!in_data) { /* build appropriate line start */
2686                         ast_str_append(out, 0, xml ? " " : "<tr><td>");
2687                         xml_copy_escape(out, var, xml ? 1 | 2 : 0);
2688                         ast_str_append(out, 0, xml ? "='" : "</td><td>");
2689                         if (!strcmp(var, "Opaque-data"))
2690                                 in_data = 1;
2691                 }
2692                 xml_copy_escape(out, val, 0);   /* data field */
2693                 if (!in_data)
2694                         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
2695                 else
2696                         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
2697         }
2698         if (inobj)
2699                 ast_str_append(out, 0, xml ? " /></response>\n" :
2700                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2701 }
2702
2703 static struct ast_str *generic_http_callback(enum output_format format,
2704                                              struct sockaddr_in *requestor, const char *uri,
2705                                              struct ast_variable *params, int *status,
2706                                              char **title, int *contentlength)
2707 {
2708         struct mansession *s = NULL;
2709         unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
2710         int blastaway = 0;
2711         struct ast_variable *v;
2712         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
2713         struct ast_str *out = NULL;
2714         struct message m = { 0 };
2715         unsigned int x;
2716         size_t hdrlen;
2717
2718         for (v = params; v; v = v->next) {
2719                 if (!strcasecmp(v->name, "mansession_id")) {
2720                         sscanf(v->value, "%lx", &ident);
2721                         break;
2722                 }
2723         }
2724
2725         if (!(s = find_session(ident))) {
2726                 /* Create new session.
2727                  * While it is not in the list we don't need any locking
2728                  */
2729                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2730                         *status = 500;
2731                         goto generic_callback_out;
2732                 }
2733                 s->sin = *requestor;
2734                 s->fd = -1;
2735                 s->waiting_thread = AST_PTHREADT_NULL;
2736                 s->send_events = 0;
2737                 ast_mutex_init(&s->__lock);
2738                 ast_mutex_lock(&s->__lock);
2739                 s->inuse = 1;
2740                 s->managerid = rand() | 1;      /* make sure it is non-zero */
2741                 s->last_ev = grab_last();
2742                 AST_LIST_LOCK(&sessions);
2743                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2744                 AST_LIST_UNLOCK(&sessions);
2745                 ast_atomic_fetchadd_int(&num_sessions, 1);
2746         }
2747
2748         ast_mutex_unlock(&s->__lock);
2749
2750         if (!(out = ast_str_create(1024))) {
2751                 *status = 500;
2752                 goto generic_callback_out;
2753         }
2754
2755         s->fd = mkstemp(template);      /* create a temporary file for command output */
2756         unlink(template);
2757         s->f = fdopen(s->fd, "w+");
2758
2759         for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
2760                 hdrlen = strlen(v->name) + strlen(v->value) + 3;
2761                 m.headers[m.hdrcount] = alloca(hdrlen);
2762                 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
2763                 m.hdrcount = x + 1;
2764         }
2765
2766         if (process_message(s, &m)) {
2767                 if (s->authenticated) {
2768                         if (option_verbose > 1) {
2769                                 if (displayconnects)
2770                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2771                         }
2772                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2773                 } else {
2774                         if (option_verbose > 1) {
2775                                 if (displayconnects)
2776                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2777                         }
2778                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2779                 }
2780                 s->needdestroy = 1;
2781         }
2782
2783         ast_str_append(&out, 0,
2784                        "Content-type: text/%s\r\n"
2785                        "Cache-Control: no-cache;\r\n"
2786                        "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
2787                        "\r\n",
2788                         contenttype[format],
2789                         s->managerid, httptimeout);
2790
2791         if (format == FORMAT_XML) {
2792                 ast_str_append(&out, 0, "<ajax-response>\n");
2793         } else if (format == FORMAT_HTML) {
2794
2795 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
2796 #define TEST_STRING \
2797         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
2798         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
2799         <input type=\"submit\"></form>"
2800
2801                 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
2802                 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2803                 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
2804                 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
2805         }
2806
2807         if (s->f != NULL) {     /* have temporary output */
2808                 char *buf;
2809                 size_t l = ftell(s->f);
2810
2811                 fclose(s->f);
2812                 if (format == FORMAT_XML || format == FORMAT_HTML) {
2813                         if (l) {
2814                                 if ((buf = mmap(NULL, l, PROT_READ, MAP_SHARED, s->fd, 0))) {
2815                                         xml_translate(&out, buf, params, format);
2816                                         munmap(buf, l);
2817                                 }
2818                         } else {
2819                                 xml_translate(&out, "", params, format);
2820                         }
2821                 }
2822                 s->f = NULL;
2823                 s->fd = -1;
2824         }
2825
2826         if (format == FORMAT_XML) {
2827                 ast_str_append(&out, 0, "</ajax-response>\n");
2828         } else if (format == FORMAT_HTML)
2829                 ast_str_append(&out, 0, "</table></body>\r\n");
2830
2831         ast_mutex_lock(&s->__lock);
2832         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
2833         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
2834
2835         if (s->needdestroy) {
2836                 if (s->inuse == 1) {
2837                         if (option_debug)
2838                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2839                         blastaway = 1;
2840                 } else {
2841                         if (option_debug)
2842                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2843                         if (s->waiting_thread != AST_PTHREADT_NULL)
2844                                 pthread_kill(s->waiting_thread, SIGURG);
2845                         s->inuse--;
2846                 }
2847         } else
2848                 s->inuse--;
2849         ast_mutex_unlock(&s->__lock);
2850
2851         if (blastaway)
2852                 destroy_session(s);
2853 generic_callback_out:
2854         if (*status != 200)
2855                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2856         return out;
2857 }
2858
2859 static struct ast_str *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2860 {
2861         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2862 }
2863
2864 static struct ast_str *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2865 {
2866         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2867 }
2868
2869 static struct ast_str *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2870 {
2871         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2872 }
2873
2874 struct ast_http_uri rawmanuri = {
2875         .description = "Raw HTTP Manager Event Interface",
2876         .uri = "rawman",
2877         .has_subtree = 0,
2878         .callback = rawman_http_callback,
2879 };
2880
2881 struct ast_http_uri manageruri = {
2882         .description = "HTML Manager Event Interface",
2883         .uri = "manager",
2884         .has_subtree = 0,
2885         .callback = manager_http_callback,
2886 };
2887
2888 struct ast_http_uri managerxmluri = {
2889         .description = "XML Manager Event Interface",
2890         .uri = "mxml",
2891         .has_subtree = 0,
2892         .callback = mxml_http_callback,
2893 };
2894
2895 static int registered = 0;
2896 static int webregged = 0;
2897
2898 /*! \brief cleanup code called at each iteration of server_root,
2899  * guaranteed to happen every 5 seconds at most
2900  */
2901 static void purge_old_stuff(void *data)
2902 {
2903         purge_sessions(1);
2904         purge_events();
2905 }
2906
2907 struct tls_config ami_tls_cfg;
2908 static struct server_args ami_desc = {
2909         .accept_fd = -1,
2910         .master = AST_PTHREADT_NULL,
2911         .tls_cfg = NULL, 
2912         .poll_timeout = 5000,   /* wake up every 5 seconds */
2913         .periodic_fn = purge_old_stuff,
2914         .name = "AMI server",
2915         .accept_fn = server_root,       /* thread doing the accept() */
2916         .worker_fn = session_do,        /* thread handling the session */
2917 };
2918
2919 static struct server_args amis_desc = {
2920         .accept_fd = -1,
2921         .master = AST_PTHREADT_NULL,
2922         .tls_cfg = &ami_tls_cfg, 
2923         .poll_timeout = -1,     /* the other does the periodic cleanup */
2924         .name = "AMI TLS server",
2925         .accept_fn = server_root,       /* thread doing the accept() */
2926         .worker_fn = session_do,        /* thread handling the session */
2927 };
2928
2929 int init_manager(void)
2930 {
2931         struct ast_config *cfg = NULL;
2932         const char *val;
2933         char *cat = NULL;
2934         int webenabled = 0;
2935         int enabled = 0;
2936         int newhttptimeout = 60;
2937         int have_sslbindaddr = 0;
2938         struct hostent *hp;
2939         struct ast_hostent ahp;
2940         struct ast_manager_user *user = NULL;
2941         struct ast_variable *var;
2942
2943         if (!registered) {
2944                 /* Register default actions */
2945                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2946                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2947                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2948                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
2949                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
2950                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2951                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2952                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2953                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2954                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2955                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2956                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2957                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2958                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2959                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2960                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2961                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2962                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2963                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2964                 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
2965                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2966                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2967
2968                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2969                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2970                 registered = 1;
2971                 /* Append placeholder event so master_eventq never runs dry */
2972                 append_event("Event: Placeholder\r\n\r\n", 0);
2973         }
2974         displayconnects = 1;
2975         cfg = ast_config_load("manager.conf");
2976         if (!cfg) {
2977                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2978                 return 0;
2979         }
2980
2981         /* default values */
2982         memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
2983         memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
2984         amis_desc.sin.sin_port = htons(5039);
2985         ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
2986
2987         ami_tls_cfg.enabled = 0;
2988         if (ami_tls_cfg.certfile)
2989                 free(ami_tls_cfg.certfile);
2990         ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
2991         if (ami_tls_cfg.cipher)
2992                 free(ami_tls_cfg.cipher);
2993         ami_tls_cfg.cipher = ast_strdup("");
2994
2995         for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
2996                 val = var->value;
2997                 if (!strcasecmp(var->name, "ssenable"))
2998                         ami_tls_cfg.enabled = ast_true(val);
2999                 else if (!strcasecmp(var->name, "ssbindport"))
3000                         amis_desc.sin.sin_port = htons(atoi(val));
3001                 else if (!strcasecmp(var->name, "ssbindaddr")) {
3002                         if ((hp = ast_gethostbyname(val, &ahp))) {
3003                                 memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
3004                                 have_sslbindaddr = 1;
3005                         } else {
3006                                 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
3007                         }
3008                 } else if (!strcasecmp(var->name, "sslcert")) {
3009                         free(ami_tls_cfg.certfile);
3010                         ami_tls_cfg.certfile = ast_strdup(val);
3011                 } else if (!strcasecmp(var->name, "sslcipher")) {
3012                         free(ami_tls_cfg.cipher);
3013                         ami_tls_cfg.cipher = ast_strdup(val);
3014                 } else if (!strcasecmp(var->name, "enabled")) {
3015                         enabled = ast_true(val);
3016                 } else if (!strcasecmp(var->name, "block-sockets")) {
3017                         block_sockets = ast_true(val);
3018                 } else if (!strcasecmp(var->name, "webenabled")) {
3019                         webenabled = ast_true(val);
3020                 } else if (!strcasecmp(var->name, "port")) {
3021                         ami_desc.sin.sin_port = htons(atoi(val));
3022                 } else if (!strcasecmp(var->name, "bindaddr")) {
3023                         if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
3024                                 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
3025                                 memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
3026                         }
3027                 } else if (!strcasecmp(var->name, "displayconnects")) {
3028                         displayconnects = ast_true(val);
3029                 } else if (!strcasecmp(var->name, "timestampevents")) {
3030                         timestampevents = ast_true(val);
3031                 } else if (!strcasecmp(var->name, "debug")) {
3032                         manager_debug = ast_true(val);
3033                 } else if (!strcasecmp(var->name, "httptimeout")) {
3034                         newhttptimeout = atoi(val);
3035                 } else {
3036                         ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
3037                                 var->name, val);
3038                 }       
3039         }
3040
3041         if (enabled)
3042                 ami_desc.sin.sin_family = AF_INET;
3043         if (!have_sslbindaddr)
3044                 amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
3045         if (ami_tls_cfg.enabled)
3046                 amis_desc.sin.sin_family = AF_INET;
3047
3048         
3049         AST_LIST_LOCK(&users);
3050
3051         while ((cat = ast_category_browse(cfg, cat))) {
3052
3053                 if (!strcasecmp(cat, "general"))
3054                         continue;
3055
3056                 /* Look for an existing entry, if none found - create one and add it to the list */
3057                 if (!(user = get_manager_by_name_locked(cat))) {
3058                         if (!(user = ast_calloc(1, sizeof(*user))))
3059                                 break;
3060                         /* Copy name over */
3061                         ast_copy_string(user->username, cat, sizeof(user->username));
3062                         /* Insert into list */
3063                         AST_LIST_INSERT_TAIL(&users, user, list);
3064                 }
3065
3066                 /* Make sure we keep this user and don't destroy it during cleanup */
3067                 user->keep = 1;
3068
3069                 var = ast_variable_browse(cfg, cat);
3070                 while (var) {
3071                         if (!strcasecmp(var->name, "secret")) {
3072                                 if (user->secret)
3073                                         free(user->secret);
3074                                 user->secret = ast_strdup(var->value);
3075                         } else if (!strcasecmp(var->name, "deny") ) {
3076                                 if (user->deny)
3077                                         free(user->deny);
3078                                 user->deny = ast_strdup(var->value);
3079                         } else if (!strcasecmp(var->name, "permit") ) {
3080                                 if (user->permit)
3081                                         free(user->permit);
3082                                 user->permit = ast_strdup(var->value);
3083                         }  else if (!strcasecmp(var->name, "read") ) {
3084                                 if (user->read)
3085                                         free(user->read);
3086                                 user->read = ast_strdup(var->value);
3087                         }  else if (!strcasecmp(var->name, "write") ) {
3088                                 if (user->write)
3089                                         free(user->write);
3090                                 user->write = ast_strdup(var->value);
3091                         }  else if (!strcasecmp(var->name, "displayconnects") )
3092                                 user->displayconnects = ast_true(var->value);
3093                         else {
3094                                 if (option_debug)
3095                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
3096                         }
3097                         var = var->next;
3098                 }
3099         }
3100
3101         /* Perform cleanup - essentially prune out old users that no longer exist */
3102         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
3103                 if (user->keep) {       /* valid record. clear flag for the next round */
3104                         user->keep = 0;
3105                         continue;
3106                 }
3107                 /* We do not need to keep this user so take them out of the list */
3108                 AST_LIST_REMOVE_CURRENT(&users, list);
3109                 /* Free their memory now */
3110                 if (user->secret)
3111                         free(user->secret);
3112                 if (user->deny)
3113                         free(user->deny);
3114                 if (user->permit)
3115                         free(user->permit);
3116                 if (user->read)
3117                         free(user->read);
3118                 if (user->write)
3119                         free(user->write);
3120                 free(user);
3121         }
3122         AST_LIST_TRAVERSE_SAFE_END
3123
3124         AST_LIST_UNLOCK(&users);
3125
3126         ast_config_destroy(cfg);
3127
3128         if (webenabled && enabled) {
3129                 if (!webregged) {
3130                         ast_http_uri_link(&rawmanuri);
3131                         ast_http_uri_link(&manageruri);
3132                         ast_http_uri_link(&managerxmluri);
3133                         webregged = 1;
3134                 }
3135         } else {
3136                 if (webregged) {
3137                         ast_http_uri_unlink(&rawmanuri);
3138                         ast_http_uri_unlink(&manageruri);
3139                         ast_http_uri_unlink(&managerxmluri);
3140                         webregged = 0;
3141                 }
3142         }
3143
3144         if (newhttptimeout > 0)
3145                 httptimeout = newhttptimeout;
3146
3147         server_start(&ami_desc);
3148         if (ssl_setup(amis_desc.tls_cfg))
3149                 server_start(&amis_desc);
3150         return 0;
3151 }
3152
3153 int reload_manager(void)
3154 {
3155         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3156         return init_manager();
3157 }