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