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