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