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