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