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