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