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