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