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