8f6ad5d7e4239b3945da7d6a8b7cab6fe3c9685c
[asterisk/asterisk.git] / main / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief The Asterisk Management Interface - AMI
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * At the moment this file contains a number of functions, namely:
26  *
27  * - data structures storing AMI state
28  * - AMI-related API functions, used by internal asterisk components
29  * - handlers for AMI-related CLI functions
30  * - handlers for AMI functions (available through the AMI socket)
31  * - the code for the main AMI listener thread and individual session threads
32  * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
33  *
34  * \ref amiconf
35  */
36
37 /*! \addtogroup Group_AMI AMI functions
38 */
39 /*! @{
40  Doxygen group */
41
42 #include "asterisk.h"
43
44 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <sys/time.h>
51 #include <sys/types.h>
52 #include <netdb.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <netinet/tcp.h>
56 #include <arpa/inet.h>
57 #include <signal.h>
58 #include <errno.h>
59 #include <unistd.h>
60
61 #include "asterisk/channel.h"
62 #include "asterisk/file.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/config.h"
65 #include "asterisk/callerid.h"
66 #include "asterisk/lock.h"
67 #include "asterisk/logger.h"
68 #include "asterisk/options.h"
69 #include "asterisk/cli.h"
70 #include "asterisk/app.h"
71 #include "asterisk/pbx.h"
72 #include "asterisk/md5.h"
73 #include "asterisk/acl.h"
74 #include "asterisk/utils.h"
75 #include "asterisk/http.h"
76 #include "asterisk/threadstorage.h"
77 #include "asterisk/linkedlists.h"
78
79 /*!
80  * Linked list of events.
81  * Global events are appended to the list by append_event().
82  * The usecount is the number of stored pointers to the element,
83  * excluding the list pointers. So an element that is only in
84  * the list has a usecount of 0, not 1.
85  *
86  * Clients have a pointer to the last event processed, and for each
87  * of these clients we track the usecount of the elements.
88  * If we have a pointer to an entry in the list, it is safe to navigate
89  * it forward because elements will not be deleted, but only appended.
90  * The worst that can happen is seeing the pointer still NULL.
91  *
92  * When the usecount of an element drops to 0, and the element is the
93  * first in the list, we can remove it. Removal is done within the
94  * main thread, which is woken up for the purpose.
95  *
96  * For simplicity of implementation, we make sure the list is never empty.
97  */
98 struct eventqent {
99         int usecount;           /*!< # of clients who still need the event */
100         int category;
101         unsigned int seq;       /*!< sequence number */
102         AST_LIST_ENTRY(eventqent) eq_next;
103         char eventdata[1];      /*!< really variable size, allocated by append_event() */
104 };
105
106 static AST_LIST_HEAD_STATIC(all_events, eventqent);
107
108 static int displayconnects = 1;
109 static int timestampevents;
110 static int httptimeout = 60;
111
112 static int block_sockets;
113 static int num_sessions;
114
115 static int manager_debug;       /*!< enable some debugging code in the manager */
116
117 /*!
118  * Descriptor for a manager session, either on the AMI socket or over HTTP.
119  * AMI session have managerid == 0; the entry is created upon a connect,
120  * and destroyed with the socket.
121  * HTTP sessions have managerid != 0, the value is used as a search key
122  * to lookup sessions (using the mansession_id cookie).
123  */
124 struct mansession {
125         pthread_t ms_t;         /*!< Execution thread, basically useless */
126         ast_mutex_t __lock;     /*!< Thread lock -- don't use in action callbacks, it's already taken care of  */
127                                 /* XXX need to document which fields it is protecting */
128         struct sockaddr_in sin; /*!< address we are connecting from */
129         FILE *f;                /*!< fdopen() on the underlying fd */
130         int fd;                 /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
131         int inuse;              /*!< number of HTTP sessions using this entry */
132         int needdestroy;        /*!< Whether an HTTP session should be destroyed */
133         pthread_t waiting_thread;       /*!< Sleeping thread using this descriptor */
134         unsigned long managerid;        /*!< Unique manager identifier, 0 for AMI sessions */
135         time_t sessiontimeout;  /*!< Session timeout if HTTP */
136         char username[80];      /*!< Logged in username */
137         char challenge[10];     /*!< Authentication challenge */
138         int authenticated;      /*!< Authentication status */
139         int readperm;           /*!< Authorization for reading */
140         int writeperm;          /*!< Authorization for writing */
141         char inbuf[AST_MAX_MANHEADER_LEN+1];    /*!< Buffer */
142                 /* we use the extra byte to add a '\0' and simplify parsing */
143         int inlen;              /*!< number of buffered bytes */
144         int send_events;        /*!<  XXX what ? */
145         struct eventqent *last_ev;      /*!< last event processed. */
146         int writetimeout;       /*!< Timeout for ast_carefulwrite() */
147         AST_LIST_ENTRY(mansession) list;
148 };
149
150 #define NEW_EVENT(m)    (AST_LIST_NEXT(m->last_ev, eq_next))
151
152 static AST_LIST_HEAD_STATIC(sessions, mansession);
153
154 /*! \brief user descriptor, as read from the config file.
155  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
156  * lines which are not supported here, and readperm/writeperm/writetimeout
157  * are not stored.
158  */
159 struct ast_manager_user {
160         char username[80];
161         char *secret;
162         char *deny;
163         char *permit;
164         char *read;
165         char *write;
166         int displayconnects;    /*!< XXX unused */
167         int keep;       /*!< mark entries created on a reload */
168         AST_LIST_ENTRY(ast_manager_user) list;
169 };
170
171 /*! \brief list of users found in the config file */
172 static AST_LIST_HEAD_STATIC(users, ast_manager_user);
173
174 /*! \brief list of actions registered */
175 static struct manager_action *first_action;
176 AST_MUTEX_DEFINE_STATIC(actionlock);
177
178 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
179
180 /*! \brief Add a custom hook to be called when an event is fired */
181 void ast_manager_register_hook(struct manager_custom_hook *hook)
182 {
183         AST_RWLIST_WRLOCK(&manager_hooks);
184         AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
185         AST_RWLIST_UNLOCK(&manager_hooks);
186         return;
187 }
188
189 /*! \brief Delete a custom hook to be called when an event is fired */
190 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
191 {
192         AST_RWLIST_WRLOCK(&manager_hooks);
193         AST_RWLIST_REMOVE(&manager_hooks, hook, list);
194         AST_RWLIST_UNLOCK(&manager_hooks);
195         return;
196 }
197
198 /*! \brief
199  * Event list management functions.
200  * We assume that the event list always has at least one element,
201  * and the delete code will not remove the last entry even if the
202  * 
203  */
204 #if 0
205 static time_t __deb(time_t start, const char *msg)
206 {
207         time_t now = time(NULL);
208         ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
209         if (start != 0 && now - start > 5)
210                 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
211         return now;
212 }
213
214 static void LOCK_EVENTS(void)
215 {
216         time_t start = __deb(0, "about to lock events");
217         AST_LIST_LOCK(&all_events);
218         __deb(start, "done lock events");
219 }
220
221 static void UNLOCK_EVENTS(void)
222 {
223         __deb(0, "about to unlock events");
224         AST_LIST_UNLOCK(&all_events);
225 }
226
227 static void LOCK_SESS(void)
228 {
229         time_t start = __deb(0, "about to lock sessions");
230         AST_LIST_LOCK(&sessions);
231         __deb(start, "done lock sessions");
232 }
233
234 static void UNLOCK_SESS(void)
235 {
236         __deb(0, "about to unlock sessions");
237         AST_LIST_UNLOCK(&sessions);
238 }
239 #endif
240
241 /*!
242  * Grab a reference to the last event, update usecount as needed.
243  * Can handle a NULL pointer.
244  */
245 static struct eventqent *grab_last(void)
246 {
247         struct eventqent *ret;
248
249         AST_LIST_LOCK(&all_events);
250         ret = AST_LIST_LAST(&all_events);
251         /* the list is never empty now, but may become so when
252          * we optimize it in the future, so be prepared.
253          */
254         if (ret)
255                 ast_atomic_fetchadd_int(&ret->usecount, 1);
256         AST_LIST_UNLOCK(&all_events);
257         return ret;
258 }
259
260 /*!
261  * Purge unused events. Remove elements from the head
262  * as long as their usecount is 0 and there is a next element.
263  */
264 static void purge_events(void)
265 {
266         struct eventqent *ev;
267
268         AST_LIST_LOCK(&all_events);
269         while ( (ev = AST_LIST_FIRST(&all_events)) &&
270             ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
271                 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
272                 free(ev);
273         }
274         AST_LIST_UNLOCK(&all_events);
275 }
276
277 /*!
278  * helper functions to convert back and forth between
279  * string and numeric representation of set of flags
280  */
281 static struct permalias {
282         int num;
283         char *label;
284 } perms[] = {
285         { EVENT_FLAG_SYSTEM, "system" },
286         { EVENT_FLAG_CALL, "call" },
287         { EVENT_FLAG_LOG, "log" },
288         { EVENT_FLAG_VERBOSE, "verbose" },
289         { EVENT_FLAG_COMMAND, "command" },
290         { EVENT_FLAG_AGENT, "agent" },
291         { EVENT_FLAG_USER, "user" },
292         { EVENT_FLAG_CONFIG, "config" },
293         { -1, "all" },
294         { 0, "none" },
295 };
296
297 /*! \brief Convert authority code to a list of options */
298 static char *authority_to_str(int authority, struct ast_str **res)
299 {
300         int i;
301         char *sep = "";
302
303         (*res)->used = 0;
304         for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
305                 if (authority & perms[i].num) {
306                         ast_str_append(res, 0, "%s%s", sep, perms[i].label);
307                         sep = ",";
308                 }
309         }
310
311         if ((*res)->used == 0)  /* replace empty string with something sensible */
312                 ast_str_append(res, 0, "<none>");
313
314         return (*res)->str;
315 }
316
317 /*! Tells you if smallstr exists inside bigstr
318    which is delim by delim and uses no buf or stringsep
319    ast_instring("this|that|more","this",'|') == 1;
320
321    feel free to move this to app.c -anthm */
322 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
323 {
324         const char *val = bigstr, *next;
325
326         do {
327                 if ((next = strchr(val, delim))) {
328                         if (!strncmp(val, smallstr, (next - val)))
329                                 return 1;
330                         else
331                                 continue;
332                 } else
333                         return !strcmp(smallstr, val);
334
335         } while (*(val = (next + 1)));
336
337         return 0;
338 }
339
340 static int get_perm(const char *instr)
341 {
342         int x = 0, ret = 0;
343
344         if (!instr)
345                 return 0;
346
347         for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
348                 if (ast_instring(instr, perms[x].label, ','))
349                         ret |= perms[x].num;
350         }
351
352         return ret;
353 }
354
355 /*!
356  * A number returns itself, false returns 0, true returns all flags,
357  * other strings return the flags that are set.
358  */
359 static int ast_strings_to_mask(const char *string)
360 {
361         const char *p;
362
363         if (ast_strlen_zero(string))
364                 return -1;
365
366         for (p = string; *p; p++)
367                 if (*p < '0' || *p > '9')
368                         break;
369         if (!p) /* all digits */
370                 return atoi(string);
371         if (ast_false(string))
372                 return 0;
373         if (ast_true(string)) { /* all permissions */
374                 int x, ret = 0;
375                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
376                         ret |= perms[x].num;
377                 return ret;
378         }
379         return get_perm(string);
380 }
381
382 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
383 {
384         struct manager_action *cur;
385         int l = strlen(word), which = 0;
386         char *ret = NULL;
387
388         ast_mutex_lock(&actionlock);
389         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
390                 if (!strncasecmp(word, cur->action, l) && ++which > state) {
391                         ret = ast_strdup(cur->action);
392                         break;  /* make sure we exit even if ast_strdup() returns NULL */
393                 }
394         }
395         ast_mutex_unlock(&actionlock);
396
397         return ret;
398 }
399
400 /*!
401  * lookup an entry in the list of registered users.
402  * must be called with the list lock held.
403  */
404 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
405 {
406         struct ast_manager_user *user = NULL;
407
408         AST_LIST_TRAVERSE(&users, user, list)
409                 if (!strcasecmp(user->username, name))
410                         break;
411         return user;
412 }
413
414
415 static int handle_showmancmd(int fd, int argc, char *argv[])
416 {
417         struct manager_action *cur;
418         struct ast_str *authority = ast_str_alloca(80);
419         int num;
420
421         if (argc != 4)
422                 return RESULT_SHOWUSAGE;
423
424         ast_mutex_lock(&actionlock);
425         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
426                 for (num = 3; num < argc; num++) {
427                         if (!strcasecmp(cur->action, argv[num])) {
428                                 ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
429                                         cur->action, cur->synopsis,
430                                         authority_to_str(cur->authority, &authority),
431                                         S_OR(cur->description, "") );
432                         }
433                 }
434         }
435         ast_mutex_unlock(&actionlock);
436
437         return RESULT_SUCCESS;
438 }
439
440 static int handle_mandebug(int fd, int argc, char *argv[])
441 {
442         if (argc == 2)
443                 ast_cli(fd, "manager debug is %s\n", manager_debug? "on" : "off");
444         else if (argc == 3) {
445                 if (!strcasecmp(argv[2], "on"))
446                         manager_debug = 1;
447                 else if (!strcasecmp(argv[2], "off"))
448                         manager_debug = 0;
449                 else
450                         return RESULT_SHOWUSAGE;
451         }
452         return RESULT_SUCCESS;
453 }
454
455 static int handle_showmanager(int fd, int argc, char *argv[])
456 {
457         struct ast_manager_user *user = NULL;
458
459         if (argc != 4)
460                 return RESULT_SHOWUSAGE;
461
462         AST_LIST_LOCK(&users);
463
464         if (!(user = get_manager_by_name_locked(argv[3]))) {
465                 ast_cli(fd, "There is no manager called %s\n", argv[3]);
466                 AST_LIST_UNLOCK(&users);
467                 return -1;
468         }
469
470         ast_cli(fd,"\n");
471         ast_cli(fd,
472                 "       username: %s\n"
473                 "         secret: %s\n"
474                 "           deny: %s\n"
475                 "         permit: %s\n"
476                 "           read: %s\n"
477                 "          write: %s\n"
478                 "displayconnects: %s\n",
479                 (user->username ? user->username : "(N/A)"),
480                 (user->secret ? user->secret : "(N/A)"),
481                 (user->deny ? user->deny : "(N/A)"),
482                 (user->permit ? user->permit : "(N/A)"),
483                 (user->read ? user->read : "(N/A)"),
484                 (user->write ? user->write : "(N/A)"),
485                 (user->displayconnects ? "yes" : "no"));
486
487         AST_LIST_UNLOCK(&users);
488
489         return RESULT_SUCCESS;
490 }
491
492
493 static int handle_showmanagers(int fd, int argc, char *argv[])
494 {
495         struct ast_manager_user *user = NULL;
496         int count_amu = 0;
497
498         if (argc != 3)
499                 return RESULT_SHOWUSAGE;
500
501         AST_LIST_LOCK(&users);
502
503         /* If there are no users, print out something along those lines */
504         if (AST_LIST_EMPTY(&users)) {
505                 ast_cli(fd, "There are no manager users.\n");
506                 AST_LIST_UNLOCK(&users);
507                 return RESULT_SUCCESS;
508         }
509
510         ast_cli(fd, "\nusername\n--------\n");
511
512         AST_LIST_TRAVERSE(&users, user, list) {
513                 ast_cli(fd, "%s\n", user->username);
514                 count_amu++;
515         }
516
517         AST_LIST_UNLOCK(&users);
518
519         ast_cli(fd,"-------------------\n");
520         ast_cli(fd,"%d manager users configured.\n", count_amu);
521
522         return RESULT_SUCCESS;
523 }
524
525
526 /*! \brief  CLI command  manager list commands */
527 static int handle_showmancmds(int fd, int argc, char *argv[])
528 {
529         struct manager_action *cur;
530         struct ast_str *authority = ast_str_alloca(80);
531         char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
532
533         ast_cli(fd, format, "Action", "Privilege", "Synopsis");
534         ast_cli(fd, format, "------", "---------", "--------");
535
536         ast_mutex_lock(&actionlock);
537         for (cur = first_action; cur; cur = cur->next) /* Walk the list of actions */
538                 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
539         ast_mutex_unlock(&actionlock);
540
541         return RESULT_SUCCESS;
542 }
543
544 /*! \brief CLI command manager list connected */
545 static int handle_showmanconn(int fd, int argc, char *argv[])
546 {
547         struct mansession *s;
548         char *format = "  %-15.15s  %-15.15s\n";
549
550         ast_cli(fd, format, "Username", "IP Address");
551
552         AST_LIST_LOCK(&sessions);
553         AST_LIST_TRAVERSE(&sessions, s, list)
554                 ast_cli(fd, format,s->username, ast_inet_ntoa(s->sin.sin_addr));
555         AST_LIST_UNLOCK(&sessions);
556
557         return RESULT_SUCCESS;
558 }
559
560 /*! \brief CLI command manager list eventq */
561 /* Should change to "manager show connected" */
562 static int handle_showmaneventq(int fd, int argc, char *argv[])
563 {
564         struct eventqent *s;
565
566         AST_LIST_LOCK(&all_events);
567         AST_LIST_TRAVERSE(&all_events, s, eq_next) {
568                 ast_cli(fd, "Usecount: %d\n",s->usecount);
569                 ast_cli(fd, "Category: %d\n", s->category);
570                 ast_cli(fd, "Event:\n%s", s->eventdata);
571         }
572         AST_LIST_UNLOCK(&all_events);
573
574         return RESULT_SUCCESS;
575 }
576
577 static char showmancmd_help[] =
578 "Usage: manager show command <actionname>\n"
579 "       Shows the detailed description for a specific Asterisk manager interface command.\n";
580
581 static char showmancmds_help[] =
582 "Usage: manager show commands\n"
583 "       Prints a listing of all the available Asterisk manager interface commands.\n";
584
585 static char showmanconn_help[] =
586 "Usage: manager show connected\n"
587 "       Prints a listing of the users that are currently connected to the\n"
588 "Asterisk manager interface.\n";
589
590 static char showmaneventq_help[] =
591 "Usage: manager show eventq\n"
592 "       Prints a listing of all events pending in the Asterisk manger\n"
593 "event queue.\n";
594
595 static char showmanagers_help[] =
596 "Usage: manager show users\n"
597 "       Prints a listing of all managers that are currently configured on that\n"
598 " system.\n";
599
600 static char showmanager_help[] =
601 " Usage: manager show user <user>\n"
602 "        Display all information related to the manager user specified.\n";
603
604 static struct ast_cli_entry cli_manager[] = {
605         { { "manager", "show", "command", NULL },
606         handle_showmancmd, "Show a manager interface command",
607         showmancmd_help, complete_show_mancmd },
608
609         { { "manager", "show", "commands", NULL },
610         handle_showmancmds, "List manager interface commands",
611         showmancmds_help },
612
613         { { "manager", "show", "connected", NULL },
614         handle_showmanconn, "List connected manager interface users",
615         showmanconn_help },
616
617         { { "manager", "show", "eventq", NULL },
618         handle_showmaneventq, "List manager interface queued events",
619         showmaneventq_help },
620
621         { { "manager", "show", "users", NULL },
622         handle_showmanagers, "List configured manager users",
623         showmanagers_help, NULL, NULL },
624
625         { { "manager", "show", "user", NULL },
626         handle_showmanager, "Display information on a specific manager user",
627         showmanager_help, NULL, NULL },
628
629         { { "manager", "debug", NULL },
630         handle_mandebug, "Show, enable, disable debugging of the manager code",
631         "Usage: manager debug [on|off]\n        Show, enable, disable debugging of the manager code.\n", NULL, NULL },
632 };
633
634 /*
635  * Decrement the usecount for the event; if it goes to zero,
636  * (why check for e->next ?) wakeup the
637  * main thread, which is in charge of freeing the record.
638  * Returns the next record.
639  */
640 static struct eventqent *unref_event(struct eventqent *e)
641 {
642         ast_atomic_fetchadd_int(&e->usecount, -1);
643         return AST_LIST_NEXT(e, eq_next);
644 }
645
646 static void ref_event(struct eventqent *e)
647 {
648         ast_atomic_fetchadd_int(&e->usecount, 1);
649 }
650
651 /*
652  * destroy a session, leaving the usecount
653  */
654 static void free_session(struct mansession *s)
655 {
656         struct eventqent *eqe = s->last_ev;
657         if (s->f != NULL)
658                 fclose(s->f);
659         ast_mutex_destroy(&s->__lock);
660         free(s);
661         unref_event(eqe);
662 }
663
664 static void destroy_session(struct mansession *s)
665 {
666         AST_LIST_LOCK(&sessions);
667         AST_LIST_REMOVE(&sessions, s, list);
668         AST_LIST_UNLOCK(&sessions);
669
670         ast_atomic_fetchadd_int(&num_sessions, -1);
671         free_session(s);
672 }
673
674 char *astman_get_header(struct message *m, char *var)
675 {
676         int x, l = strlen(var);
677
678         for (x = 0; x < m->hdrcount; x++) {
679                 char *h = m->headers[x];
680                 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
681                         return h + l + 2;
682         }
683
684         return "";
685 }
686
687 struct ast_variable *astman_get_variables(struct message *m)
688 {
689         int varlen, x, y;
690         struct ast_variable *head = NULL, *cur;
691
692         AST_DECLARE_APP_ARGS(args,
693                 AST_APP_ARG(vars)[32];
694         );
695
696         varlen = strlen("Variable: ");
697
698         for (x = 0; x < m->hdrcount; x++) {
699                 char *parse, *var, *val;
700
701                 if (strncasecmp("Variable: ", m->headers[x], varlen))
702                         continue;
703                 parse = ast_strdupa(m->headers[x] + varlen);
704
705                 AST_STANDARD_APP_ARGS(args, parse);
706                 if (!args.argc)
707                         continue;
708                 for (y = 0; y < args.argc; y++) {
709                         if (!args.vars[y])
710                                 continue;
711                         var = val = ast_strdupa(args.vars[y]);
712                         strsep(&val, "=");
713                         if (!val || ast_strlen_zero(var))
714                                 continue;
715                         cur = ast_variable_new(var, val);
716                         cur->next = head;
717                         head = cur;
718                 }
719         }
720
721         return head;
722 }
723
724 /*!
725  * helper function to send a string to the socket.
726  * Return -1 on error (e.g. buffer full).
727  */
728 static int send_string(struct mansession *s, char *string)
729 {
730         int len = strlen(string);       /* residual length */
731         char *src = string;
732         struct timeval start = ast_tvnow();
733         int n = 0;
734
735         for (;;) {
736                 int elapsed;
737                 struct pollfd fd;
738                 n = fwrite(src, 1, len, s->f);  /* try to write the string, non blocking */
739                 if (n == len /* ok */ || n < 0 /* error */)
740                         break;
741                 len -= n;       /* skip already written data */
742                 src += n;
743                 fd.fd = s->fd;
744                 fd.events = POLLOUT;
745                 n = -1;         /* error marker */
746                 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
747                 if (elapsed > s->writetimeout)
748                         break;
749                 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
750                         break;
751         }
752         fflush(s->f);
753         return n < 0 ? -1 : 0;
754 }
755
756 /* XXX see if it can be moved inside the function */
757 AST_THREADSTORAGE(astman_append_buf);
758 #define ASTMAN_APPEND_BUF_INITSIZE   256
759
760 /*!
761  * utility functions for creating AMI replies
762  */
763 void astman_append(struct mansession *s, const char *fmt, ...)
764 {
765         va_list ap;
766         struct ast_str *buf;
767
768         if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
769                 return;
770
771         va_start(ap, fmt);
772         ast_str_set_va(&buf, 0, fmt, ap);
773         va_end(ap);
774
775         if (s->f != NULL)
776                 send_string(s, buf->str);
777         else
778                 ast_verbose("fd == -1 in astman_append, should not happen\n");
779 }
780
781 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
782    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
783    hold the session lock _or_ be running in an action callback (in which case s->busy will
784    be non-zero). In either of these cases, there is no need to lock-protect the session's
785    fd, since no other output will be sent (events will be queued), and no input will
786    be read until either the current action finishes or get_input() obtains the session
787    lock.
788  */
789
790 /*! \brief send a response with an optional message,
791  * and terminate it with an empty line.
792  * m is used only to grab the 'ActionID' field.
793  *
794  * Use the explicit constant MSG_MOREDATA to remove the empty line.
795  * XXX MSG_MOREDATA should go to a header file.
796  */
797 #define MSG_MOREDATA    ((char *)astman_send_response)
798 static void astman_send_response_full(struct mansession *s, struct message *m, char *resp, char *msg, char *listflag)
799 {
800         char *id = astman_get_header(m,"ActionID");
801
802         astman_append(s, "Response: %s\r\n", resp);
803         if (!ast_strlen_zero(id))
804                 astman_append(s, "ActionID: %s\r\n", id);
805         if (listflag)
806                 astman_append(s, "Eventlist: %s\r\n", listflag);        /* Start, complete, cancelled */
807         if (msg == MSG_MOREDATA)
808                 return;
809         else if (msg)
810                 astman_append(s, "Message: %s\r\n\r\n", msg);
811         else
812                 astman_append(s, "\r\n");
813 }
814
815 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
816 {
817         astman_send_response_full(s, m, resp, msg, NULL);
818 }
819
820 void astman_send_error(struct mansession *s, struct message *m, char *error)
821 {
822         astman_send_response_full(s, m, "Error", error, NULL);
823 }
824
825 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
826 {
827         astman_send_response_full(s, m, "Success", msg, NULL);
828 }
829
830 static void astman_start_ack(struct mansession *s, struct message *m)
831 {
832         astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
833 }
834
835 void astman_send_listack(struct mansession *s, struct message *m, char *msg, char *listflag)
836 {
837         astman_send_response_full(s, m, "Success", msg, listflag);
838 }
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, char *eventmask)
847 {
848         int maskint = ast_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, struct message *m)
866 {
867         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                 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                 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, 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, struct message *m)
983 {
984         struct ast_config *cfg;
985         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, struct message *m, struct ast_config *cfg)
1015 {
1016         int x;
1017         char hdr[40];
1018         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, 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, var, value, 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, var, 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, struct message *m)
1083 {
1084         struct ast_config *cfg;
1085         char *sfn = astman_get_header(m, "SrcFilename");
1086         char *dfn = astman_get_header(m, "DstFilename");
1087         int res;
1088         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, struct message *m)
1123 {
1124         char *timeouts = astman_get_header(m, "Timeout");
1125         int timeout = -1;
1126         int x;
1127         int needexit = 0;
1128         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, 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, struct message *m)
1249 {
1250         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, 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, 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, struct message *m)
1291 {
1292         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, struct message *m)
1313 {
1314         struct ast_channel *c = NULL;
1315         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, struct message *m)
1339 {
1340         struct ast_channel *c = NULL;
1341         char *name = astman_get_header(m, "Channel");
1342         char *varname = astman_get_header(m, "Variable");
1343         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, struct message *m)
1381 {
1382         struct ast_channel *c = NULL;
1383         char *name = astman_get_header(m, "Channel");
1384         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, 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, struct message *m)
1419 {
1420         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         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, struct message *m)
1513 {
1514         struct ast_channel *c = NULL;
1515         char *name = astman_get_header(m, "Channel");
1516         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, struct message *m)
1558 {
1559         char *name = astman_get_header(m, "Channel");
1560         char *name2 = astman_get_header(m, "ExtraChannel");
1561         char *exten = astman_get_header(m, "Exten");
1562         char *context = astman_get_header(m, "Context");
1563         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, struct message *m)
1618 {
1619         char *cmd = astman_get_header(m, "Command");
1620         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_MANHEADER_LEN];
1646         char data[AST_MAX_MANHEADER_LEN];
1647         int timeout;
1648         char app[AST_MAX_APP];
1649         char appdata[AST_MAX_MANHEADER_LEN];
1650         char cid_name[AST_MAX_MANHEADER_LEN];
1651         char cid_num[AST_MAX_MANHEADER_LEN];
1652         char context[AST_MAX_CONTEXT];
1653         char exten[AST_MAX_EXTENSION];
1654         char idtext[AST_MAX_MANHEADER_LEN];
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, struct message *m)
1723 {
1724         char *name = astman_get_header(m, "Channel");
1725         char *exten = astman_get_header(m, "Exten");
1726         char *context = astman_get_header(m, "Context");
1727         char *priority = astman_get_header(m, "Priority");
1728         char *timeout = astman_get_header(m, "Timeout");
1729         char *callerid = astman_get_header(m, "CallerID");
1730         char *account = astman_get_header(m, "Account");
1731         char *app = astman_get_header(m, "Application");
1732         char *appdata = astman_get_header(m, "Data");
1733         char *async = astman_get_header(m, "Async");
1734         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, struct message *m)
1840 {
1841         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, struct message *m)
1868 {
1869         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, struct message *m)
1899 {
1900         char *exten = astman_get_header(m, "Exten");
1901         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, struct message *m)
1930 {
1931         struct ast_channel *c;
1932         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, struct message *m)
1990 {
1991         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, 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 /*! \brief The body of the individual manager session.
2132  * Call get_input() to read one line at a time
2133  * (or be woken up on new events), collect the lines in a
2134  * message until found an empty line, and execute the request.
2135  * In any case, deliver events asynchronously through process_events()
2136  * (called from here if no line is available, or at the end of
2137  * process_message(). )
2138  */
2139 static void *session_do(void *data)
2140 {
2141         struct message m;       /* XXX watch out, this is 20k of memory! */
2142         struct server_instance *ser = data;
2143         struct mansession *s = ast_calloc(1, sizeof(*s));
2144         int flags;
2145
2146         if (s == NULL)
2147                 goto done;
2148
2149         s->writetimeout = 100;
2150         s->waiting_thread = AST_PTHREADT_NULL;
2151
2152         flags = fcntl(ser->fd, F_GETFL);
2153         if (!block_sockets) /* make sure socket is non-blocking */
2154                 flags |= O_NONBLOCK;
2155         else
2156                 flags &= ~O_NONBLOCK;
2157         fcntl(ser->fd, F_SETFL, flags);
2158
2159         ast_mutex_init(&s->__lock);
2160         s->send_events = -1;
2161         /* these fields duplicate those in the 'ser' structure */
2162         s->fd = ser->fd;
2163         s->f = ser->f;
2164         s->sin = ser->requestor;
2165
2166         ast_atomic_fetchadd_int(&num_sessions, 1);
2167         AST_LIST_LOCK(&sessions);
2168         AST_LIST_INSERT_HEAD(&sessions, s, list);
2169         AST_LIST_UNLOCK(&sessions);
2170         /* Hook to the tail of the event queue */
2171         s->last_ev = grab_last();
2172         ast_mutex_lock(&s->__lock);
2173         s->f = ser->f;
2174         astman_append(s, "Asterisk Call Manager/1.0\r\n");      /* welcome prompt */
2175         ast_mutex_unlock(&s->__lock);
2176         memset(&m, 0, sizeof(m));
2177         for (;;) {
2178                 char *buf = m.headers[m.hdrcount];
2179                 int res = get_input(s, buf);
2180                 if (res < 0)    /* error */
2181                         break;
2182                 if (res > 0) {  /* got one line */
2183                         if (ast_strlen_zero(buf)) {     /* empty line, terminator */
2184                                 if (process_message(s, &m))
2185                                         break;
2186                                 memset(&m, 0, sizeof(m));
2187                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2188                                 m.hdrcount++;
2189                 } else if (process_events(s))
2190                         break;
2191         }
2192         /* session is over, explain why and terminate */
2193         if (s->authenticated) {
2194                 if (option_verbose > 1) {
2195                         if (displayconnects)
2196                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2197                 }
2198                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2199         } else {
2200                 if (option_verbose > 1) {
2201                         if (displayconnects)
2202                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2203                 }
2204                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2205         }
2206         destroy_session(s);
2207
2208 done:
2209         free(ser);
2210         return NULL;
2211 }
2212
2213 /*! \brief remove at most n_max stale session from the list. */
2214 static void purge_sessions(int n_max)
2215 {
2216         struct mansession *s;
2217         time_t now = time(NULL);
2218
2219         AST_LIST_LOCK(&sessions);
2220         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2221                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2222                         AST_LIST_REMOVE_CURRENT(&sessions, list);
2223                         ast_atomic_fetchadd_int(&num_sessions, -1);
2224                         if (s->authenticated && (option_verbose > 1) && displayconnects) {
2225                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2226                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2227                         }
2228                         free_session(s);        /* XXX outside ? */
2229                         if (--n_max <= 0)
2230                                 break;
2231                 }
2232         }
2233         AST_LIST_TRAVERSE_SAFE_END
2234         AST_LIST_UNLOCK(&sessions);
2235 }
2236
2237 /*
2238  * events are appended to a queue from where they
2239  * can be dispatched to clients.
2240  */
2241 static int append_event(const char *str, int category)
2242 {
2243         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2244         static int seq; /* sequence number */
2245
2246         if (!tmp)
2247                 return -1;
2248
2249         /* need to init all fields, because ast_malloc() does not */
2250         tmp->usecount = 0;
2251         tmp->category = category;
2252         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2253         AST_LIST_NEXT(tmp, eq_next) = NULL;
2254         strcpy(tmp->eventdata, str);
2255
2256         AST_LIST_LOCK(&all_events);
2257         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2258         AST_LIST_UNLOCK(&all_events);
2259
2260         return 0;
2261 }
2262
2263 /* XXX see if can be moved inside the function */
2264 AST_THREADSTORAGE(manager_event_buf);
2265 #define MANAGER_EVENT_BUF_INITSIZE   256
2266
2267 /*! \brief  manager_event: Send AMI event to client */
2268 int __manager_event(int category, const char *event,
2269         const char *file, int line, const char *func, const char *fmt, ...)
2270 {
2271         struct mansession *s;
2272         struct manager_custom_hook *hook;
2273         struct ast_str *auth = ast_str_alloca(80);
2274         const char *cat_str;
2275         va_list ap;
2276         struct timeval now;
2277         struct ast_str *buf;
2278
2279         /* Abort if there aren't any manager sessions */
2280         if (!num_sessions)
2281                 return 0;
2282
2283         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2284                 return -1;
2285
2286         cat_str = authority_to_str(category, &auth);
2287         ast_str_set(&buf, 0,
2288                         "Event: %s\r\nPrivilege: %s\r\n",
2289                          event, cat_str);
2290
2291         if (timestampevents) {
2292                 now = ast_tvnow();
2293                 ast_str_append(&buf, 0,
2294                                 "Timestamp: %ld.%06lu\r\n",
2295                                  now.tv_sec, (unsigned long) now.tv_usec);
2296         }
2297         if (manager_debug) {
2298                 static int seq;
2299                 ast_str_append(&buf, 0,
2300                                 "SequenceNumber: %d\r\n",
2301                                  ast_atomic_fetchadd_int(&seq, 1));
2302                 ast_str_append(&buf, 0,
2303                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2304         }
2305
2306         va_start(ap, fmt);
2307         ast_str_append_va(&buf, 0, fmt, ap);
2308         va_end(ap);
2309
2310         ast_str_append(&buf, 0, "\r\n");
2311
2312         append_event(buf->str, category);
2313
2314         /* Wake up any sleeping sessions */
2315         AST_LIST_LOCK(&sessions);
2316         AST_LIST_TRAVERSE(&sessions, s, list) {
2317                 ast_mutex_lock(&s->__lock);
2318                 if (s->waiting_thread != AST_PTHREADT_NULL)
2319                         pthread_kill(s->waiting_thread, SIGURG);
2320                 ast_mutex_unlock(&s->__lock);
2321         }
2322         AST_LIST_UNLOCK(&sessions);
2323
2324         AST_RWLIST_RDLOCK(&manager_hooks);
2325         if (!AST_RWLIST_EMPTY(&manager_hooks)) {
2326                 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2327                         hook->helper(category, event, buf->str);
2328                 }
2329         }
2330         AST_RWLIST_UNLOCK(&manager_hooks);
2331
2332         return 0;
2333 }
2334
2335 /*
2336  * support functions to register/unregister AMI action handlers,
2337  */
2338 int ast_manager_unregister(char *action)
2339 {
2340         struct manager_action *cur = first_action, *prev = first_action;
2341
2342         ast_mutex_lock(&actionlock);
2343         for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2344                 if (!strcasecmp(action, cur->action)) {
2345                         if (prev)
2346                                 prev->next = cur->next;
2347                         else
2348                                 first_action = cur->next;
2349                         free(cur);
2350                         if (option_verbose > 1)
2351                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2352                         break;
2353                 }
2354         }
2355         ast_mutex_unlock(&actionlock);
2356         return 0;
2357 }
2358
2359 static int manager_state_cb(char *context, char *exten, int state, void *data)
2360 {
2361         /* Notify managers of change */
2362         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2363         return 0;
2364 }
2365
2366 static int ast_manager_register_struct(struct manager_action *act)
2367 {
2368         struct manager_action *cur, *prev = NULL;
2369         int ret;
2370
2371         ast_mutex_lock(&actionlock);
2372         for (cur = first_action; cur; prev = cur, cur = cur->next) {
2373                 ret = strcasecmp(cur->action, act->action);
2374                 if (ret == 0) {
2375                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2376                         ast_mutex_unlock(&actionlock);
2377                         return -1;
2378                 }
2379                 if (ret > 0)    /* Insert these alphabetically */
2380                         break;
2381         }
2382         if (prev)
2383                 prev->next = act;
2384         else
2385                 first_action = act;
2386         act->next = cur;
2387
2388         if (option_verbose > 1)
2389                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2390         ast_mutex_unlock(&actionlock);
2391         return 0;
2392 }
2393
2394 /*! \brief register a new command with manager, including online help. This is
2395         the preferred way to register a manager command */
2396 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2397 {
2398         struct manager_action *cur;
2399
2400         cur = ast_malloc(sizeof(*cur));
2401         if (!cur)
2402                 return -1;
2403
2404         cur->action = action;
2405         cur->authority = auth;
2406         cur->func = func;
2407         cur->synopsis = synopsis;
2408         cur->description = description;
2409         cur->next = NULL;
2410
2411         ast_manager_register_struct(cur);
2412
2413         return 0;
2414 }
2415 /*! @}
2416  END Doxygen group */
2417
2418 /*
2419  * The following are support functions for AMI-over-http.
2420  * The common entry point is generic_http_callback(),
2421  * which extracts HTTP header and URI fields and reformats
2422  * them into AMI messages, locates a proper session
2423  * (using the mansession_id Cookie or GET variable),
2424  * and calls process_message() as for regular AMI clients.
2425  * When done, the output (which goes to a temporary file)
2426  * is read back into a buffer and reformatted as desired,
2427  * then fed back to the client over the original socket.
2428  */
2429
2430 enum output_format {
2431         FORMAT_RAW,
2432         FORMAT_HTML,
2433         FORMAT_XML,
2434 };
2435
2436 static char *contenttype[] = {
2437         [FORMAT_RAW] = "plain",
2438         [FORMAT_HTML] = "html",
2439         [FORMAT_XML] =  "xml",
2440 };
2441
2442 /*!
2443  * locate an http session in the list. The search key (ident) is
2444  * the value of the mansession_id cookie (0 is not valid and means
2445  * a session on the AMI socket).
2446  */
2447 static struct mansession *find_session(unsigned long ident)
2448 {
2449         struct mansession *s;
2450
2451         if (ident == 0)
2452                 return NULL;
2453
2454         AST_LIST_LOCK(&sessions);
2455         AST_LIST_TRAVERSE(&sessions, s, list) {
2456                 ast_mutex_lock(&s->__lock);
2457                 if (s->managerid == ident && !s->needdestroy) {
2458                         ast_atomic_fetchadd_int(&s->inuse, 1);
2459                         break;
2460                 }
2461                 ast_mutex_unlock(&s->__lock);
2462         }
2463         AST_LIST_UNLOCK(&sessions);
2464
2465         return s;
2466 }
2467
2468 static void vars2msg(struct message *m, struct ast_variable *vars)
2469 {
2470         int x;
2471         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2472                 if (!vars)
2473                         break;
2474                 m->hdrcount = x + 1;
2475                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2476         }
2477 }
2478
2479 /*
2480  * convert to xml with various conversion:
2481  * mode & 1     -> lowercase;
2482  * mode & 2     -> replace non-alphanumeric chars with underscore
2483  */
2484 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
2485 {
2486         /* store in a local buffer to avoid calling ast_str_append too often */
2487         char buf[256];
2488         char *dst = buf;
2489         int space = sizeof(buf);
2490         /* repeat until done and nothing to flush */
2491         for ( ; *src || dst != buf ; src++) {
2492                 if (*src == '\0' || space < 10) {       /* flush */
2493                         *dst++ = '\0';
2494                         ast_str_append(out, 0, "%s", buf);
2495                         dst = buf;
2496                         space = sizeof(buf);
2497                         if (*src == '\0')
2498                                 break;
2499                 }
2500                         
2501                 if ( (mode & 2) && !isalnum(*src)) {
2502                         *dst++ = '_';
2503                         space--;
2504                         continue;
2505                 }
2506                 switch (*src) {
2507                 case '<':
2508                         strcpy(dst, "&lt;");
2509                         dst += 4;
2510                         space -= 4;
2511                         break;
2512                 case '>':
2513                         strcpy(dst, "&gt;");
2514                         dst += 4;
2515                         space -= 4;
2516                         break;
2517                 case '\"':
2518                         strcpy(dst, "&quot;");
2519                         dst += 6;
2520                         space -= 6;
2521                         break;
2522                 case '\'':
2523                         strcpy(dst, "&apos;");
2524                         dst += 6;
2525                         space -= 6;
2526                         break;
2527                 case '&':
2528                         strcpy(dst, "&amp;");
2529                         dst += 5;
2530                         space -= 5;
2531                         break;
2532
2533                 default:
2534                         *dst++ = mode ? tolower(*src) : *src;
2535                         space--;
2536                 }
2537         }
2538 }
2539
2540 /*! \brief Convert the input into XML or HTML.
2541  * The input is supposed to be a sequence of lines of the form
2542  *      Name: value
2543  * optionally followed by a blob of unformatted text.
2544  * A blank line is a section separator. Basically, this is a
2545  * mixture of the format of Manager Interface and CLI commands.
2546  * The unformatted text is considered as a single value of a field
2547  * named 'Opaque-data'.
2548  *
2549  * At the moment the output format is the following (but it may
2550  * change depending on future requirements so don't count too
2551  * much on it when writing applications):
2552  *
2553  * General: the unformatted text is used as a value of
2554  * XML output:  to be completed
2555  *   Each section is within <response type="object" id="xxx">
2556  *   where xxx is taken from ajaxdest variable or defaults to unknown
2557  *   Each row is reported as an attribute Name="value" of an XML
2558  *   entity named from the variable ajaxobjtype, default to "generic"
2559  *
2560  * HTML output:
2561  *   each Name-value pair is output as a single row of a two-column table.
2562  *   Sections (blank lines in the input) are separated by a <HR>
2563  *
2564  */
2565 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
2566 {
2567         struct ast_variable *v;
2568         char *dest = NULL;
2569         char *var, *val;
2570         char *objtype = NULL;
2571         int in_data = 0;        /* parsing data */
2572         int inobj = 0;
2573         int xml = (format == FORMAT_XML);
2574
2575         for (v = vars; v; v = v->next) {
2576                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
2577                         dest = v->value;
2578                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
2579                         objtype = v->value;
2580         }
2581         if (!dest)
2582                 dest = "unknown";
2583         if (!objtype)
2584                 objtype = "generic";
2585 #if 0
2586         /* determine how large is the response.
2587          * This is a heuristic - counting colons (for headers),
2588          * newlines (for extra arguments), and escaped chars.
2589          * XXX needs to be checked carefully for overflows.
2590          * Even better, use some code that allows extensible strings.
2591          */
2592         for (x = 0; in[x]; x++) {
2593                 if (in[x] == ':')
2594                         colons++;
2595                 else if (in[x] == '\n')
2596                         breaks++;
2597                 else if (strchr("&\"<>", in[x]))
2598                         escaped++;
2599         }
2600         len = (size_t) (1 + strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
2601         out = ast_malloc(len);
2602         if (!out)
2603                 return NULL;
2604         tmp = out;
2605         *tmp = '\0';
2606 #endif
2607         /* we want to stop when we find an empty line */
2608         while (in && *in) {
2609                 val = strsep(&in, "\r\n");      /* mark start and end of line */
2610                 if (in && *in == '\n')          /* remove trailing \n if any */
2611                         in++;
2612                 ast_trim_blanks(val);
2613                 if (0)
2614                         ast_verbose("inobj %d in_data %d line <%s>\n", inobj, in_data, val);
2615                 if (ast_strlen_zero(val)) {
2616                         if (in_data) { /* close data */
2617                                 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
2618                                 in_data = 0;
2619                         }
2620                         ast_str_append(out, 0, xml ? " /></response>\n" :
2621                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2622                         inobj = 0;
2623                         continue;
2624                 }
2625                 /* we expect Name: value lines */
2626                 if (in_data) {
2627                         var = NULL;
2628                 } else {
2629                         var = strsep(&val, ":");
2630                         if (val) {      /* found the field name */
2631                                 val = ast_skip_blanks(val);
2632                                 ast_trim_blanks(var);
2633                         } else {                /* field name not found, move to opaque mode */
2634                                 val = var;
2635                                 var = "Opaque-data";
2636                         }
2637                 }
2638                 if (!inobj) {
2639                         if (xml)
2640                                 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
2641                         else
2642                                 ast_str_append(out, 0, "<body>\n");
2643                         inobj = 1;
2644                 }
2645                 if (!in_data) { /* build appropriate line start */
2646                         ast_str_append(out, 0, xml ? " " : "<tr><td>");
2647                         xml_copy_escape(out, var, xml ? 1 | 2 : 0);
2648                         ast_str_append(out, 0, xml ? "='" : "</td><td>");
2649                         if (!strcmp(var, "Opaque-data"))
2650                                 in_data = 1;
2651                 }
2652                 xml_copy_escape(out, val, 0);   /* data field */
2653                 if (!in_data)
2654                         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
2655                 else
2656                         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
2657         }
2658         if (inobj)
2659                 ast_str_append(out, 0, xml ? " /></response>\n" :
2660                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2661 }
2662
2663 static struct ast_str *generic_http_callback(enum output_format format,
2664         struct sockaddr_in *requestor, const char *uri,
2665         struct ast_variable *params, int *status,
2666         char **title, int *contentlength)
2667 {
2668         struct mansession *s = NULL;
2669         unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
2670         int blastaway = 0;
2671         struct message m;
2672         struct ast_variable *v;
2673         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
2674         struct ast_str *out = NULL;
2675
2676         for (v = params; v; v = v->next) {
2677                 if (!strcasecmp(v->name, "mansession_id")) {
2678                         sscanf(v->value, "%lx", &ident);
2679                         break;
2680                 }
2681         }
2682
2683         if (!(s = find_session(ident))) {
2684                 /* Create new session.
2685                  * While it is not in the list we don't need any locking
2686                  */
2687                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2688                         *status = 500;
2689                         goto generic_callback_out;
2690                 }
2691                 s->sin = *requestor;
2692                 s->fd = -1;
2693                 s->waiting_thread = AST_PTHREADT_NULL;
2694                 s->send_events = 0;
2695                 ast_mutex_init(&s->__lock);
2696                 ast_mutex_lock(&s->__lock);
2697                 s->inuse = 1;
2698                 s->managerid = rand() | 1;      /* make sure it is non-zero */
2699                 s->last_ev = grab_last();
2700                 AST_LIST_LOCK(&sessions);
2701                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2702                 AST_LIST_UNLOCK(&sessions);
2703                 ast_atomic_fetchadd_int(&num_sessions, 1);
2704         }
2705
2706         ast_mutex_unlock(&s->__lock);
2707
2708         out = ast_str_create(1024);
2709         if (out == NULL) {
2710                 *status = 500;
2711                 goto generic_callback_out;
2712         }
2713         memset(&m, 0, sizeof(m));
2714         ast_str_append(&out, 0,
2715                 "Content-type: text/%s\r\n"
2716                 "Cache-Control: no-cache;\r\n"
2717                 "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
2718                 "\r\n",
2719                         contenttype[format],
2720                         s->managerid, httptimeout);
2721
2722         if (format == FORMAT_HTML)
2723                 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Test Interface</title>");
2724         vars2msg(&m, params);
2725
2726         if (format == FORMAT_XML) {
2727                 ast_str_append(&out, 0, "<ajax-response>\n");
2728         } else if (format == FORMAT_HTML) {
2729
2730 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
2731 #define TEST_STRING \
2732         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
2733         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
2734         <input type=\"submit\"></form>"
2735
2736                 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2737                 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
2738                 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
2739         }
2740
2741         s->fd = mkstemp(template);      /* create a temporary file for command output */
2742         s->f = fdopen(s->fd, "w+");
2743
2744         if (process_message(s, &m)) {
2745                 if (s->authenticated) {
2746                         if (option_verbose > 1) {
2747                                 if (displayconnects)
2748                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2749                         }
2750                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2751                 } else {
2752                         if (option_verbose > 1) {
2753                                 if (displayconnects)
2754                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2755                         }
2756                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2757                 }
2758                 s->needdestroy = 1;
2759         }
2760         if (s->f != NULL) {     /* have temporary output */
2761                 char *buf;
2762                 int l = ftell(s->f);
2763
2764                 /* always return something even if len == 0 */
2765                 if ((buf = ast_calloc(1, l+1))) {
2766                         if (l > 0) {
2767                                 fseek(s->f, 0, SEEK_SET);
2768                                 fread(buf, 1, l, s->f);
2769                         }
2770                         if (format == FORMAT_XML || format == FORMAT_HTML)
2771                                 xml_translate(&out, buf, params, format);
2772                         free(buf);
2773                 }
2774                 fclose(s->f);
2775                 s->f = NULL;
2776                 s->fd = -1;
2777                 unlink(template);
2778         }
2779
2780         /* Still okay because c would safely be pointing to workspace even
2781            if retval failed to allocate above */
2782         if (format == FORMAT_XML) {
2783                 ast_str_append(&out, 0, "</ajax-response>\n");
2784         } else if (format == FORMAT_HTML)
2785                 ast_str_append(&out, 0, "</table></body>\r\n");
2786
2787         ast_mutex_lock(&s->__lock);
2788         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
2789         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
2790         if (0)
2791                 ast_verbose("die in %d seconds\n",
2792                         (int)(s->sessiontimeout - time(NULL)) );
2793         if (s->needdestroy) {
2794                 if (s->inuse == 1) {
2795                         if (option_debug)
2796                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2797                         blastaway = 1;
2798                 } else {
2799                         if (option_debug)
2800                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2801                         if (s->waiting_thread != AST_PTHREADT_NULL)
2802                                 pthread_kill(s->waiting_thread, SIGURG);
2803                         s->inuse--;
2804                 }
2805         } else
2806                 s->inuse--;
2807         ast_mutex_unlock(&s->__lock);
2808
2809         if (blastaway)
2810                 destroy_session(s);
2811 generic_callback_out:
2812         if (*status != 200)
2813                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2814         return out;
2815 }
2816
2817 static struct ast_str *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2818 {
2819         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2820 }
2821
2822 static struct ast_str *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2823 {
2824         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2825 }
2826
2827 static struct ast_str *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2828 {
2829         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2830 }
2831
2832 struct ast_http_uri rawmanuri = {
2833         .description = "Raw HTTP Manager Event Interface",
2834         .uri = "rawman",
2835         .has_subtree = 0,
2836         .callback = rawman_http_callback,
2837 };
2838
2839 struct ast_http_uri manageruri = {
2840         .description = "HTML Manager Event Interface",
2841         .uri = "manager",
2842         .has_subtree = 0,
2843         .callback = manager_http_callback,
2844 };
2845
2846 struct ast_http_uri managerxmluri = {
2847         .description = "XML Manager Event Interface",
2848         .uri = "mxml",
2849         .has_subtree = 0,
2850         .callback = mxml_http_callback,
2851 };
2852
2853 static int registered = 0;
2854 static int webregged = 0;
2855
2856 /*! \brief cleanup code called at each iteration of server_root,
2857  * guaranteed to happen every 5 seconds at most
2858  */
2859 static void purge_old_stuff(void *data)
2860 {
2861         purge_sessions(1);
2862         purge_events();
2863 }
2864
2865 struct tls_config ami_tls_cfg;
2866 static struct server_args ami_desc = {
2867         .accept_fd = -1,
2868         .master = AST_PTHREADT_NULL,
2869         .tls_cfg = NULL, 
2870         .poll_timeout = 5000,   /* wake up every 5 seconds */
2871         .periodic_fn = purge_old_stuff,
2872         .name = "AMI server",
2873         .accept_fn = server_root,       /* thread doing the accept() */
2874         .worker_fn = session_do,        /* thread handling the session */
2875 };
2876
2877 static struct server_args amis_desc = {
2878         .accept_fd = -1,
2879         .master = AST_PTHREADT_NULL,
2880         .tls_cfg = &ami_tls_cfg, 
2881         .poll_timeout = -1,     /* the other does the periodic cleanup */
2882         .name = "AMI TLS server",
2883         .accept_fn = server_root,       /* thread doing the accept() */
2884         .worker_fn = session_do,        /* thread handling the session */
2885 };
2886
2887 int init_manager(void)
2888 {
2889         struct ast_config *cfg = NULL;
2890         const char *val;
2891         char *cat = NULL;
2892         int webenabled = 0;
2893         int enabled = 0;
2894         int newhttptimeout = 60;
2895         int have_sslbindaddr = 0;
2896         struct hostent *hp;
2897         struct ast_hostent ahp;
2898         struct ast_manager_user *user = NULL;
2899         struct ast_variable *var;
2900
2901         if (!registered) {
2902                 /* Register default actions */
2903                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2904                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2905                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2906                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
2907                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
2908                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2909                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2910                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2911                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2912                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2913                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2914                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2915                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2916                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2917                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2918                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2919                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2920                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2921                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2922                 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
2923                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2924                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2925
2926                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2927                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2928                 registered = 1;
2929                 /* Append placeholder event so master_eventq never runs dry */
2930                 append_event("Event: Placeholder\r\n\r\n", 0);
2931         }
2932         displayconnects = 1;
2933         cfg = ast_config_load("manager.conf");
2934         if (!cfg) {
2935                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2936                 return 0;
2937         }
2938
2939         /* default values */
2940         memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
2941         memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
2942         amis_desc.sin.sin_port = htons(5039);
2943         ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
2944
2945         ami_tls_cfg.enabled = 0;
2946         if (ami_tls_cfg.certfile)
2947                 free(ami_tls_cfg.certfile);
2948         ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
2949         if (ami_tls_cfg.cipher)
2950                 free(ami_tls_cfg.cipher);
2951         ami_tls_cfg.cipher = ast_strdup("");
2952
2953         for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
2954                 val = var->value;
2955                 if (!strcasecmp(var->name, "ssenable"))
2956                         ami_tls_cfg.enabled = ast_true(val);
2957                 else if (!strcasecmp(var->name, "ssbindport"))
2958                         amis_desc.sin.sin_port = htons(atoi(val));
2959                 else if (!strcasecmp(var->name, "ssbindaddr")) {
2960                         if ((hp = ast_gethostbyname(val, &ahp))) {
2961                                 memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
2962                                 have_sslbindaddr = 1;
2963                         } else {
2964                                 ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
2965                         }
2966                 } else if (!strcasecmp(var->name, "sslcert")) {
2967                         free(ami_tls_cfg.certfile);
2968                         ami_tls_cfg.certfile = ast_strdup(val);
2969                 } else if (!strcasecmp(var->name, "sslcipher")) {
2970                         free(ami_tls_cfg.cipher);
2971                         ami_tls_cfg.cipher = ast_strdup(val);
2972                 } else if (!strcasecmp(var->name, "enabled")) {
2973                         enabled = ast_true(val);
2974                 } else if (!strcasecmp(var->name, "block-sockets")) {
2975                         block_sockets = ast_true(val);
2976                 } else if (!strcasecmp(var->name, "webenabled")) {
2977                         webenabled = ast_true(val);
2978                 } else if (!strcasecmp(var->name, "port")) {
2979                         ami_desc.sin.sin_port = htons(atoi(val));
2980                 } else if (!strcasecmp(var->name, "bindaddr")) {
2981                         if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
2982                                 ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2983                                 memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
2984                         }
2985                 } else if (!strcasecmp(var->name, "displayconnects")) {
2986                         displayconnects = ast_true(val);
2987                 } else if (!strcasecmp(var->name, "timestampevents")) {
2988                         timestampevents = ast_true(val);
2989                 } else if (!strcasecmp(var->name, "debug")) {
2990                         manager_debug = ast_true(val);
2991                 } else if (!strcasecmp(var->name, "httptimeout")) {
2992                         newhttptimeout = atoi(val);
2993                 } else {
2994                         ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
2995                                 var->name, val);
2996                 }       
2997         }
2998
2999         if (enabled)
3000                 ami_desc.sin.sin_family = AF_INET;
3001         if (!have_sslbindaddr)
3002                 amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
3003         if (ami_tls_cfg.enabled)
3004                 amis_desc.sin.sin_family = AF_INET;
3005
3006         
3007         AST_LIST_LOCK(&users);
3008
3009         while ((cat = ast_category_browse(cfg, cat))) {
3010
3011                 if (!strcasecmp(cat, "general"))
3012                         continue;
3013
3014                 /* Look for an existing entry, if none found - create one and add it to the list */
3015                 if (!(user = get_manager_by_name_locked(cat))) {
3016                         if (!(user = ast_calloc(1, sizeof(*user))))
3017                                 break;
3018                         /* Copy name over */
3019                         ast_copy_string(user->username, cat, sizeof(user->username));
3020                         /* Insert into list */
3021                         AST_LIST_INSERT_TAIL(&users, user, list);
3022                 }
3023
3024                 /* Make sure we keep this user and don't destroy it during cleanup */
3025                 user->keep = 1;
3026
3027                 var = ast_variable_browse(cfg, cat);
3028                 while (var) {
3029                         if (!strcasecmp(var->name, "secret")) {
3030                                 if (user->secret)
3031                                         free(user->secret);
3032                                 user->secret = ast_strdup(var->value);
3033                         } else if (!strcasecmp(var->name, "deny") ) {
3034                                 if (user->deny)
3035                                         free(user->deny);
3036                                 user->deny = ast_strdup(var->value);
3037                         } else if (!strcasecmp(var->name, "permit") ) {
3038                                 if (user->permit)
3039                                         free(user->permit);
3040                                 user->permit = ast_strdup(var->value);
3041                         }  else if (!strcasecmp(var->name, "read") ) {
3042                                 if (user->read)
3043                                         free(user->read);
3044                                 user->read = ast_strdup(var->value);
3045                         }  else if (!strcasecmp(var->name, "write") ) {
3046                                 if (user->write)
3047                                         free(user->write);
3048                                 user->write = ast_strdup(var->value);
3049                         }  else if (!strcasecmp(var->name, "displayconnects") )
3050                                 user->displayconnects = ast_true(var->value);
3051                         else {
3052                                 if (option_debug)
3053                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
3054                         }
3055                         var = var->next;
3056                 }
3057         }
3058
3059         /* Perform cleanup - essentially prune out old users that no longer exist */
3060         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
3061                 if (user->keep) {       /* valid record. clear flag for the next round */
3062                         user->keep = 0;
3063                         continue;
3064                 }
3065                 /* We do not need to keep this user so take them out of the list */
3066                 AST_LIST_REMOVE_CURRENT(&users, list);
3067                 /* Free their memory now */
3068                 if (user->secret)
3069                         free(user->secret);
3070                 if (user->deny)
3071                         free(user->deny);
3072                 if (user->permit)
3073                         free(user->permit);
3074                 if (user->read)
3075                         free(user->read);
3076                 if (user->write)
3077                         free(user->write);
3078                 free(user);
3079         }
3080         AST_LIST_TRAVERSE_SAFE_END
3081
3082         AST_LIST_UNLOCK(&users);
3083
3084         ast_config_destroy(cfg);
3085
3086         if (webenabled && enabled) {
3087                 if (!webregged) {
3088                         ast_http_uri_link(&rawmanuri);
3089                         ast_http_uri_link(&manageruri);
3090                         ast_http_uri_link(&managerxmluri);
3091                         webregged = 1;
3092                 }
3093         } else {
3094                 if (webregged) {
3095                         ast_http_uri_unlink(&rawmanuri);
3096                         ast_http_uri_unlink(&manageruri);
3097                         ast_http_uri_unlink(&managerxmluri);
3098                         webregged = 0;
3099                 }
3100         }
3101
3102         if (newhttptimeout > 0)
3103                 httptimeout = newhttptimeout;
3104
3105         server_start(&ami_desc);
3106         if (ssl_setup(amis_desc.tls_cfg))
3107                 server_start(&amis_desc);
3108         return 0;
3109 }
3110
3111 int reload_manager(void)
3112 {
3113         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3114         return init_manager();
3115 }