Merged revisions 89536 via svnmerge from
[asterisk/asterisk.git] / main / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief The Asterisk Management Interface - AMI
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref OpenSSL http://www.openssl.org - for AMI/SSL 
26  *
27  * At the moment this file contains a number of functions, namely:
28  *
29  * - data structures storing AMI state
30  * - AMI-related API functions, used by internal asterisk components
31  * - handlers for AMI-related CLI functions
32  * - handlers for AMI functions (available through the AMI socket)
33  * - the code for the main AMI listener thread and individual session threads
34  * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
35  *
36  * \ref amiconf
37  */
38
39 /*! \addtogroup Group_AMI AMI functions
40 */
41 /*! @{
42  Doxygen group */
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include "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                         }
1043                 }
1044                 ast_config_destroy(cfg);
1045                 if (!hasmanager) {
1046                         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1047                         return -1;
1048                 }
1049         }
1050
1051         }
1052
1053         if (ha) {
1054                 int good = ast_apply_ha(ha, &(s->sin));
1055                 ast_free_ha(ha);
1056                 if (!good) {
1057                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1058                         return -1;
1059                 }
1060         }
1061         if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1062                 const char *key = astman_get_header(m, "Key");
1063                 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) &&
1064                     !ast_strlen_zero(password)) {
1065                         int x;
1066                         int len = 0;
1067                         char md5key[256] = "";
1068                         struct MD5Context md5;
1069                         unsigned char digest[16];
1070
1071                         MD5Init(&md5);
1072                         MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1073                         MD5Update(&md5, (unsigned char *) password, strlen(password));
1074                         MD5Final(digest, &md5);
1075                         for (x=0; x<16; x++)
1076                                 len += sprintf(md5key + len, "%2.2x", digest[x]);
1077                         if (!strcmp(md5key, key))
1078                                 error = 0;
1079                 } else {
1080                         ast_debug(1, "MD5 authentication is not possible.  challenge: '%s'\n", 
1081                                 S_OR(s->challenge, ""));
1082                         return -1;
1083                 }
1084         } else if (password) {
1085                 const char *pass = astman_get_header(m, "Secret");
1086                 if (!strcmp(password, pass))
1087                         error = 0;
1088         }
1089         if (error) {
1090                 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user);
1091                 return -1;
1092         }
1093         ast_copy_string(s->username, user, sizeof(s->username));
1094         s->readperm = readperm;
1095         s->writeperm = writeperm;
1096         s->sessionstart = time(NULL);
1097         set_eventmask(s, astman_get_header(m, "Events"));
1098         return 0;
1099 }
1100
1101 /*! \brief Manager PING */
1102 static char mandescr_ping[] =
1103 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
1104 "  manager connection open.\n"
1105 "Variables: NONE\n";
1106
1107 static int action_ping(struct mansession *s, const struct message *m)
1108 {
1109         astman_send_response(s, m, "Pong", NULL);
1110         return 0;
1111 }
1112
1113 static char mandescr_getconfig[] =
1114 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1115 "file by category and contents.\n"
1116 "Variables:\n"
1117 "   Filename: Configuration filename (e.g. foo.conf)\n";
1118
1119 static int action_getconfig(struct mansession *s, const struct message *m)
1120 {
1121         struct ast_config *cfg;
1122         const char *fn = astman_get_header(m, "Filename");
1123         int catcount = 0;
1124         int lineno = 0;
1125         char *category=NULL;
1126         struct ast_variable *v;
1127         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1128
1129         if (ast_strlen_zero(fn)) {
1130                 astman_send_error(s, m, "Filename not specified");
1131                 return 0;
1132         }
1133         if (!(cfg = ast_config_load(fn, config_flags))) {
1134                 astman_send_error(s, m, "Config file not found");
1135                 return 0;
1136         }
1137         astman_start_ack(s, m);
1138         while ((category = ast_category_browse(cfg, category))) {
1139                 lineno = 0;
1140                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1141                 for (v = ast_variable_browse(cfg, category); v; v = v->next)
1142                         astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1143                 catcount++;
1144         }
1145         ast_config_destroy(cfg);
1146         astman_append(s, "\r\n");
1147
1148         return 0;
1149 }
1150
1151 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1152 static void json_escape(char *out, const char *in)
1153 {
1154         for (; *in; in++) {
1155                 if (*in == '\\' || *in == '\"')
1156                         *out++ = '\\';
1157                 *out++ = *in;
1158         }
1159         *out = '\0';
1160 }
1161
1162 static char mandescr_getconfigjson[] =
1163 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1164 "file by category and contents in JSON format.  This only makes sense to be used\n"
1165 "using rawman over the HTTP interface.\n"
1166 "Variables:\n"
1167 "   Filename: Configuration filename (e.g. foo.conf)\n";
1168
1169 static int action_getconfigjson(struct mansession *s, const struct message *m)
1170 {
1171         struct ast_config *cfg;
1172         const char *fn = astman_get_header(m, "Filename");
1173         char *category = NULL;
1174         struct ast_variable *v;
1175         int comma1 = 0;
1176         char *buf = NULL;
1177         unsigned int buf_len = 0;
1178         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1179
1180         if (ast_strlen_zero(fn)) {
1181                 astman_send_error(s, m, "Filename not specified");
1182                 return 0;
1183         }
1184
1185         if (!(cfg = ast_config_load(fn, config_flags))) {
1186                 astman_send_error(s, m, "Config file not found");
1187                 return 0;
1188         }
1189
1190         buf_len = 512;
1191         buf = alloca(buf_len);
1192
1193         astman_start_ack(s, m);
1194         astman_append(s, "JSON: {");
1195         while ((category = ast_category_browse(cfg, category))) {
1196                 int comma2 = 0;
1197                 if (buf_len < 2 * strlen(category) + 1) {
1198                         buf_len *= 2;
1199                         buf = alloca(buf_len);
1200                 }
1201                 json_escape(buf, category);
1202                 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1203                 if (!comma1)
1204                         comma1 = 1;
1205                 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1206                         if (comma2)
1207                                 astman_append(s, ",");
1208                         if (buf_len < 2 * strlen(v->name) + 1) {
1209                                 buf_len *= 2;
1210                                 buf = alloca(buf_len);
1211                         }
1212                         json_escape(buf, v->name);
1213                         astman_append(s, "\"%s", buf);
1214                         if (buf_len < 2 * strlen(v->value) + 1) {
1215                                 buf_len *= 2;
1216                                 buf = alloca(buf_len);
1217                         }
1218                         json_escape(buf, v->value);
1219                         astman_append(s, "%s\"", buf);
1220                         if (!comma2)
1221                                 comma2 = 1;
1222                 }
1223                 astman_append(s, "]");
1224         }
1225         astman_append(s, "}\r\n\r\n");
1226
1227         ast_config_destroy(cfg);
1228
1229         return 0;
1230 }
1231
1232 /* helper function for action_updateconfig */
1233 static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1234 {
1235         int x;
1236         char hdr[40];
1237         const char *action, *cat, *var, *value, *match;
1238         struct ast_category *category;
1239         struct ast_variable *v;
1240
1241         for (x = 0; x < 100000; x++) {
1242                 unsigned int object = 0;
1243
1244                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1245                 action = astman_get_header(m, hdr);
1246                 if (ast_strlen_zero(action))
1247                         break;
1248                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1249                 cat = astman_get_header(m, hdr);
1250                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1251                 var = astman_get_header(m, hdr);
1252                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1253                 value = astman_get_header(m, hdr);
1254                 if (!ast_strlen_zero(value) && *value == '>') {
1255                         object = 1;
1256                         value++;
1257                 }
1258                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1259                 match = astman_get_header(m, hdr);
1260                 if (!strcasecmp(action, "newcat")) {
1261                         if (!ast_strlen_zero(cat)) {
1262                                 category = ast_category_new(cat, dfn, 99999);
1263                                 if (category) {
1264                                         ast_category_append(cfg, category);
1265                                 }
1266                         }
1267                 } else if (!strcasecmp(action, "renamecat")) {
1268                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
1269                                 category = ast_category_get(cfg, cat);
1270                                 if (category)
1271                                         ast_category_rename(category, value);
1272                         }
1273                 } else if (!strcasecmp(action, "delcat")) {
1274                         if (!ast_strlen_zero(cat))
1275                                 ast_category_delete(cfg, cat);
1276                 } else if (!strcasecmp(action, "update")) {
1277                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1278                                 ast_variable_update(category, var, value, match, object);
1279                 } else if (!strcasecmp(action, "delete")) {
1280                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
1281                                 ast_variable_delete(category, var, match);
1282                 } else if (!strcasecmp(action, "append")) {
1283                         if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
1284                                 (category = ast_category_get(cfg, cat)) &&
1285                                 (v = ast_variable_new(var, value, dfn))){
1286                                 if (object || (match && !strcasecmp(match, "object")))
1287                                         v->object = 1;
1288                                 ast_variable_append(category, v);
1289                         }
1290                 }
1291         }
1292 }
1293
1294 static char mandescr_updateconfig[] =
1295 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1296 "configuration elements in Asterisk configuration files.\n"
1297 "Variables (X's represent 6 digit number beginning with 000000):\n"
1298 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1299 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1300 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1301 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
1302 "   Cat-XXXXXX:    Category to operate on\n"
1303 "   Var-XXXXXX:    Variable to work on\n"
1304 "   Value-XXXXXX:  Value to work on\n"
1305 "   Match-XXXXXX:  Extra match required to match line\n";
1306
1307 static int action_updateconfig(struct mansession *s, const struct message *m)
1308 {
1309         struct ast_config *cfg;
1310         const char *sfn = astman_get_header(m, "SrcFilename");
1311         const char *dfn = astman_get_header(m, "DstFilename");
1312         int res;
1313         const char *rld = astman_get_header(m, "Reload");
1314         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1315
1316         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1317                 astman_send_error(s, m, "Filename not specified");
1318                 return 0;
1319         }
1320         if (!(cfg = ast_config_load(sfn, config_flags))) {
1321                 astman_send_error(s, m, "Config file not found");
1322                 return 0;
1323         }
1324         handle_updates(s, m, cfg, dfn);
1325         ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1326         res = config_text_file_save(dfn, cfg, "Manager");
1327         ast_config_destroy(cfg);
1328         if (res) {
1329                 astman_send_error(s, m, "Save of config failed");
1330                 return 0;
1331         }
1332         astman_send_ack(s, m, NULL);
1333         if (!ast_strlen_zero(rld)) {
1334                 if (ast_true(rld))
1335                         rld = NULL;
1336                 ast_module_reload(rld);
1337         }
1338         return 0;
1339 }
1340
1341 /*! \brief Manager WAITEVENT */
1342 static char mandescr_waitevent[] =
1343 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1344 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1345 "session, events will be generated and queued.\n"
1346 "Variables: \n"
1347 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1348
1349 static int action_waitevent(struct mansession *s, const struct message *m)
1350 {
1351         const char *timeouts = astman_get_header(m, "Timeout");
1352         int timeout = -1;
1353         int x;
1354         int needexit = 0;
1355         const char *id = astman_get_header(m,"ActionID");
1356         char idText[256] = "";
1357
1358         if (!ast_strlen_zero(id))
1359                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1360
1361         if (!ast_strlen_zero(timeouts)) {
1362                 sscanf(timeouts, "%i", &timeout);
1363                 if (timeout < -1)
1364                         timeout = -1;
1365                 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1366         }
1367
1368         ast_mutex_lock(&s->__lock);
1369         if (s->waiting_thread != AST_PTHREADT_NULL)
1370                 pthread_kill(s->waiting_thread, SIGURG);
1371
1372         if (s->managerid) { /* AMI-over-HTTP session */
1373                 /*
1374                  * Make sure the timeout is within the expire time of the session,
1375                  * as the client will likely abort the request if it does not see
1376                  * data coming after some amount of time.
1377                  */
1378                 time_t now = time(NULL);
1379                 int max = s->sessiontimeout - now - 10;
1380
1381                 if (max < 0)    /* We are already late. Strange but possible. */
1382                         max = 0;
1383                 if (timeout < 0 || timeout > max)
1384                         timeout = max;
1385                 if (!s->send_events)    /* make sure we record events */
1386                         s->send_events = -1;
1387         }
1388         ast_mutex_unlock(&s->__lock);
1389
1390         /* XXX should this go inside the lock ? */
1391         s->waiting_thread = pthread_self();     /* let new events wake up this thread */
1392         ast_debug(1, "Starting waiting for an event!\n");
1393
1394         for (x=0; x < timeout || timeout < 0; x++) {
1395                 ast_mutex_lock(&s->__lock);
1396                 if (NEW_EVENT(s))
1397                         needexit = 1;
1398                 /* We can have multiple HTTP session point to the same mansession entry.
1399                  * The way we deal with it is not very nice: newcomers kick out the previous
1400                  * HTTP session. XXX this needs to be improved.
1401                  */
1402                 if (s->waiting_thread != pthread_self())
1403                         needexit = 1;
1404                 if (s->needdestroy)
1405                         needexit = 1;
1406                 ast_mutex_unlock(&s->__lock);
1407                 if (needexit)
1408                         break;
1409                 if (s->managerid == 0) {        /* AMI session */
1410                         if (ast_wait_for_input(s->fd, 1000))
1411                                 break;
1412                 } else {        /* HTTP session */
1413                         sleep(1);
1414                 }
1415         }
1416         ast_debug(1, "Finished waiting for an event!\n");
1417         ast_mutex_lock(&s->__lock);
1418         if (s->waiting_thread == pthread_self()) {
1419                 struct eventqent *eqe;
1420                 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1421                 while ( (eqe = NEW_EVENT(s)) ) {
1422                         ref_event(eqe);
1423                         if (((s->readperm & eqe->category) == eqe->category) &&
1424                             ((s->send_events & eqe->category) == eqe->category)) {
1425                                 astman_append(s, "%s", eqe->eventdata);
1426                         }
1427                         s->last_ev = unref_event(s->last_ev);
1428                 }
1429                 astman_append(s,
1430                         "Event: WaitEventComplete\r\n"
1431                         "%s"
1432                         "\r\n", idText);
1433                 s->waiting_thread = AST_PTHREADT_NULL;
1434         } else {
1435                 ast_debug(1, "Abandoning event request!\n");
1436         }
1437         ast_mutex_unlock(&s->__lock);
1438         return 0;
1439 }
1440
1441 static char mandescr_listcommands[] =
1442 "Description: Returns the action name and synopsis for every\n"
1443 "  action that is available to the user\n"
1444 "Variables: NONE\n";
1445
1446 /*! \note The actionlock is read-locked by the caller of this function */
1447 static int action_listcommands(struct mansession *s, const struct message *m)
1448 {
1449         struct manager_action *cur;
1450         struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1451
1452         astman_start_ack(s, m);
1453         AST_RWLIST_TRAVERSE(&actions, cur, list) {
1454                 if ((s->writeperm & cur->authority) == cur->authority)
1455                         astman_append(s, "%s: %s (Priv: %s)\r\n",
1456                                 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1457         }
1458         astman_append(s, "\r\n");
1459
1460         return 0;
1461 }
1462
1463 static char mandescr_events[] =
1464 "Description: Enable/Disable sending of events to this manager\n"
1465 "  client.\n"
1466 "Variables:\n"
1467 "       EventMask: 'on' if all events should be sent,\n"
1468 "               'off' if no events should be sent,\n"
1469 "               'system,call,log' to select which flags events should have to be sent.\n";
1470
1471 static int action_events(struct mansession *s, const struct message *m)
1472 {
1473         const char *mask = astman_get_header(m, "EventMask");
1474         int res;
1475
1476         res = set_eventmask(s, mask);
1477         if (res > 0)
1478                 astman_send_response(s, m, "Events On", NULL);
1479         else if (res == 0)
1480                 astman_send_response(s, m, "Events Off", NULL);
1481
1482         return 0;
1483 }
1484
1485 static char mandescr_logoff[] =
1486 "Description: Logoff this manager session\n"
1487 "Variables: NONE\n";
1488
1489 static int action_logoff(struct mansession *s, const struct message *m)
1490 {
1491         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1492         return -1;
1493 }
1494
1495 static int action_login(struct mansession *s, const struct message *m)
1496 {
1497         if (authenticate(s, m)) {
1498                 sleep(1);
1499                 astman_send_error(s, m, "Authentication failed");
1500                 return -1;
1501         }
1502         s->authenticated = 1;
1503         if (manager_displayconnects(s))
1504                 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1505         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1506         astman_send_ack(s, m, "Authentication accepted");
1507         return 0;
1508 }
1509
1510 static int action_challenge(struct mansession *s, const struct message *m)
1511 {
1512         const char *authtype = astman_get_header(m, "AuthType");
1513
1514         if (!strcasecmp(authtype, "MD5")) {
1515                 if (ast_strlen_zero(s->challenge))
1516                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1517                 ast_mutex_lock(&s->__lock);
1518                 astman_start_ack(s, m);
1519                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1520                 ast_mutex_unlock(&s->__lock);
1521         } else {
1522                 astman_send_error(s, m, "Must specify AuthType");
1523         }
1524         return 0;
1525 }
1526
1527 static char mandescr_hangup[] =
1528 "Description: Hangup a channel\n"
1529 "Variables: \n"
1530 "       Channel: The channel name to be hungup\n";
1531
1532 static int action_hangup(struct mansession *s, const struct message *m)
1533 {
1534         struct ast_channel *c = NULL;
1535         const char *name = astman_get_header(m, "Channel");
1536         if (ast_strlen_zero(name)) {
1537                 astman_send_error(s, m, "No channel specified");
1538                 return 0;
1539         }
1540         c = ast_get_channel_by_name_locked(name);
1541         if (!c) {
1542                 astman_send_error(s, m, "No such channel");
1543                 return 0;
1544         }
1545         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1546         ast_channel_unlock(c);
1547         astman_send_ack(s, m, "Channel Hungup");
1548         return 0;
1549 }
1550
1551 static char mandescr_setvar[] =
1552 "Description: Set a global or local channel variable.\n"
1553 "Variables: (Names marked with * are required)\n"
1554 "       Channel: Channel to set variable for\n"
1555 "       *Variable: Variable name\n"
1556 "       *Value: Value\n";
1557
1558 static int action_setvar(struct mansession *s, const struct message *m)
1559 {
1560         struct ast_channel *c = NULL;
1561         const char *name = astman_get_header(m, "Channel");
1562         const char *varname = astman_get_header(m, "Variable");
1563         const char *varval = astman_get_header(m, "Value");
1564
1565         if (ast_strlen_zero(varname)) {
1566                 astman_send_error(s, m, "No variable specified");
1567                 return 0;
1568         }
1569
1570         if (!ast_strlen_zero(name)) {
1571                 c = ast_get_channel_by_name_locked(name);
1572                 if (!c) {
1573                         astman_send_error(s, m, "No such channel");
1574                         return 0;
1575                 }
1576         }
1577
1578         pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1579
1580         if (c)
1581                 ast_channel_unlock(c);
1582
1583         astman_send_ack(s, m, "Variable Set");
1584
1585         return 0;
1586 }
1587
1588 static char mandescr_getvar[] =
1589 "Description: Get the value of a global or local channel variable.\n"
1590 "Variables: (Names marked with * are required)\n"
1591 "       Channel: Channel to read variable from\n"
1592 "       *Variable: Variable name\n"
1593 "       ActionID: Optional Action id for message matching.\n";
1594
1595 static int action_getvar(struct mansession *s, const struct message *m)
1596 {
1597         struct ast_channel *c = NULL;
1598         const char *name = astman_get_header(m, "Channel");
1599         const char *varname = astman_get_header(m, "Variable");
1600         char *varval;
1601         char workspace[1024] = "";
1602
1603         if (ast_strlen_zero(varname)) {
1604                 astman_send_error(s, m, "No variable specified");
1605                 return 0;
1606         }
1607
1608         if (!ast_strlen_zero(name)) {
1609                 c = ast_get_channel_by_name_locked(name);
1610                 if (!c) {
1611                         astman_send_error(s, m, "No such channel");
1612                         return 0;
1613                 }
1614         }
1615
1616         if (varname[strlen(varname) - 1] == ')') {
1617                 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1618                 varval = workspace;
1619         } else {
1620                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1621         }
1622
1623         if (c)
1624                 ast_channel_unlock(c);
1625         astman_start_ack(s, m);
1626         astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1627
1628         return 0;
1629 }
1630
1631
1632 /*! \brief Manager "status" command to show channels */
1633 /* Needs documentation... */
1634 static int action_status(struct mansession *s, const struct message *m)
1635 {
1636         const char *name = astman_get_header(m,"Channel");
1637         struct ast_channel *c;
1638         char bridge[256];
1639         struct timeval now = ast_tvnow();
1640         long elapsed_seconds = 0;
1641         int all = ast_strlen_zero(name); /* set if we want all channels */
1642         const char *id = astman_get_header(m,"ActionID");
1643         char idText[256] = "";
1644
1645         if (!ast_strlen_zero(id))
1646                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1647
1648         if (all)
1649                 c = ast_channel_walk_locked(NULL);
1650         else {
1651                 c = ast_get_channel_by_name_locked(name);
1652                 if (!c) {
1653                         astman_send_error(s, m, "No such channel");
1654                         return 0;
1655                 }
1656         }
1657         astman_send_ack(s, m, "Channel status will follow");
1658         /* if we look by name, we break after the first iteration */
1659         while (c) {
1660                 if (c->_bridge)
1661                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
1662                 else
1663                         bridge[0] = '\0';
1664                 if (c->pbx) {
1665                         if (c->cdr) {
1666                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1667                         }
1668                         astman_append(s,
1669                         "Event: Status\r\n"
1670                         "Privilege: Call\r\n"
1671                         "Channel: %s\r\n"
1672                         "CallerIDNum: %s\r\n"
1673                         "CallerIDName: %s\r\n"
1674                         "Account: %s\r\n"
1675                         "State: %s\r\n"
1676                         "Context: %s\r\n"
1677                         "Extension: %s\r\n"
1678                         "Priority: %d\r\n"
1679                         "Seconds: %ld\r\n"
1680                         "%s"
1681                         "Uniqueid: %s\r\n"
1682                         "%s"
1683                         "\r\n",
1684                         c->name,
1685                         S_OR(c->cid.cid_num, "<unknown>"),
1686                         S_OR(c->cid.cid_name, "<unknown>"),
1687                         c->accountcode,
1688                         ast_state2str(c->_state), c->context,
1689                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1690                 } else {
1691                         astman_append(s,
1692                         "Event: Status\r\n"
1693                         "Privilege: Call\r\n"
1694                         "Channel: %s\r\n"
1695                         "CallerIDNum: %s\r\n"
1696                         "CallerIDName: %s\r\n"
1697                         "Account: %s\r\n"
1698                         "State: %s\r\n"
1699                         "%s"
1700                         "Uniqueid: %s\r\n"
1701                         "%s"
1702                         "\r\n",
1703                         c->name,
1704                         S_OR(c->cid.cid_num, "<unknown>"),
1705                         S_OR(c->cid.cid_name, "<unknown>"),
1706                         c->accountcode,
1707                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1708                 }
1709                 ast_channel_unlock(c);
1710                 if (!all)
1711                         break;
1712                 c = ast_channel_walk_locked(c);
1713         }
1714         astman_append(s,
1715         "Event: StatusComplete\r\n"
1716         "%s"
1717         "\r\n",idText);
1718         return 0;
1719 }
1720
1721 static char mandescr_sendtext[] =
1722 "Description: Sends A Text Message while in a call.\n"
1723 "Variables: (Names marked with * are required)\n"
1724 "       *Channel: Channel to send message to\n"
1725 "       *Message: Message to send\n"
1726 "       ActionID: Optional Action id for message matching.\n";
1727
1728 static int action_sendtext(struct mansession *s, const struct message *m)
1729 {
1730         struct ast_channel *c = NULL;
1731         const char *name = astman_get_header(m, "Channel");
1732         const char *textmsg = astman_get_header(m, "Message");
1733         int res = 0;
1734
1735         if (ast_strlen_zero(name)) {
1736                 astman_send_error(s, m, "No channel specified");
1737                 return 0;
1738         }
1739
1740         if (ast_strlen_zero(textmsg)) {
1741                 astman_send_error(s, m, "No Message specified");
1742                 return 0;
1743         }
1744
1745         c = ast_get_channel_by_name_locked(name);
1746         if (!c) {
1747                 astman_send_error(s, m, "No such channel");
1748                 return 0;
1749         }
1750
1751         res = ast_sendtext(c, textmsg);
1752         ast_channel_unlock(c);
1753         
1754         if (res > 0)
1755                 astman_send_ack(s, m, "Success");
1756         else
1757                 astman_send_error(s, m, "Failure");
1758         
1759         return res;
1760 }
1761
1762 static char mandescr_redirect[] =
1763 "Description: Redirect (transfer) a call.\n"
1764 "Variables: (Names marked with * are required)\n"
1765 "       *Channel: Channel to redirect\n"
1766 "       ExtraChannel: Second call leg to transfer (optional)\n"
1767 "       *Exten: Extension to transfer to\n"
1768 "       *Context: Context to transfer to\n"
1769 "       *Priority: Priority to transfer to\n"
1770 "       ActionID: Optional Action id for message matching.\n";
1771
1772 /*! \brief  action_redirect: The redirect manager command */
1773 static int action_redirect(struct mansession *s, const struct message *m)
1774 {
1775         const char *name = astman_get_header(m, "Channel");
1776         const char *name2 = astman_get_header(m, "ExtraChannel");
1777         const char *exten = astman_get_header(m, "Exten");
1778         const char *context = astman_get_header(m, "Context");
1779         const char *priority = astman_get_header(m, "Priority");
1780         struct ast_channel *chan, *chan2 = NULL;
1781         int pi = 0;
1782         int res;
1783
1784         if (ast_strlen_zero(name)) {
1785                 astman_send_error(s, m, "Channel not specified");
1786                 return 0;
1787         }
1788         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1789                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1790                         astman_send_error(s, m, "Invalid priority\n");
1791                         return 0;
1792                 }
1793         }
1794         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1795         chan = ast_get_channel_by_name_locked(name);
1796         if (!chan) {
1797                 char buf[BUFSIZ];
1798                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1799                 astman_send_error(s, m, buf);
1800                 return 0;
1801         }
1802         if (ast_check_hangup(chan)) {
1803                 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1804                 ast_channel_unlock(chan);
1805                 return 0;
1806         }
1807         if (!ast_strlen_zero(name2))
1808                 chan2 = ast_get_channel_by_name_locked(name2);
1809         if (chan2 && ast_check_hangup(chan2)) {
1810                 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1811                 ast_channel_unlock(chan);
1812                 ast_channel_unlock(chan2);
1813                 return 0;
1814         }
1815         res = ast_async_goto(chan, context, exten, pi);
1816         if (!res) {
1817                 if (!ast_strlen_zero(name2)) {
1818                         if (chan2)
1819                                 res = ast_async_goto(chan2, context, exten, pi);
1820                         else
1821                                 res = -1;
1822                         if (!res)
1823                                 astman_send_ack(s, m, "Dual Redirect successful");
1824                         else
1825                                 astman_send_error(s, m, "Secondary redirect failed");
1826                 } else
1827                         astman_send_ack(s, m, "Redirect successful");
1828         } else
1829                 astman_send_error(s, m, "Redirect failed");
1830         if (chan)
1831                 ast_channel_unlock(chan);
1832         if (chan2)
1833                 ast_channel_unlock(chan2);
1834         return 0;
1835 }
1836
1837 static char mandescr_command[] =
1838 "Description: Run a CLI command.\n"
1839 "Variables: (Names marked with * are required)\n"
1840 "       *Command: Asterisk CLI command to run\n"
1841 "       ActionID: Optional Action id for message matching.\n";
1842
1843 /*! \brief  Manager command "command" - execute CLI command */
1844 static int action_command(struct mansession *s, const struct message *m)
1845 {
1846         const char *cmd = astman_get_header(m, "Command");
1847         const char *id = astman_get_header(m, "ActionID");
1848         char *buf, *final_buf;
1849         char template[] = "/tmp/ast-ami-XXXXXX";        /* template for temporary file */
1850         int fd = mkstemp(template), i = 0;
1851         off_t l;
1852
1853         for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
1854                 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
1855                         astman_send_error(s, m, "Command blacklisted");
1856                         return 0;
1857                 }
1858         }
1859
1860         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1861         if (!ast_strlen_zero(id))
1862                 astman_append(s, "ActionID: %s\r\n", id);
1863         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1864         ast_cli_command(fd, cmd);       /* XXX need to change this to use a FILE * */
1865         l = lseek(fd, 0, SEEK_END);     /* how many chars available */
1866
1867         /* This has a potential to overflow the stack.  Hence, use the heap. */
1868         buf = ast_calloc(1, l + 1);
1869         final_buf = ast_calloc(1, l + 1);
1870         if (buf) {
1871                 lseek(fd, 0, SEEK_SET);
1872                 read(fd, buf, l);
1873                 buf[l] = '\0';
1874                 if (final_buf) {
1875                         term_strip(final_buf, buf, l);
1876                         final_buf[l] = '\0';
1877                 }
1878                 astman_append(s, S_OR(final_buf, buf));
1879                 ast_free(buf);
1880         }
1881         close(fd);
1882         unlink(template);
1883         astman_append(s, "--END COMMAND--\r\n\r\n");
1884         if (final_buf)
1885                 ast_free(final_buf);
1886         return 0;
1887 }
1888
1889 /* helper function for originate */
1890 struct fast_originate_helper {
1891         char tech[AST_MAX_EXTENSION];
1892         char data[AST_MAX_EXTENSION];
1893         int timeout;
1894         char app[AST_MAX_APP];
1895         char appdata[AST_MAX_EXTENSION];
1896         char cid_name[AST_MAX_EXTENSION];
1897         char cid_num[AST_MAX_EXTENSION];
1898         char context[AST_MAX_CONTEXT];
1899         char exten[AST_MAX_EXTENSION];
1900         char idtext[AST_MAX_EXTENSION];
1901         char account[AST_MAX_ACCOUNT_CODE];
1902         int priority;
1903         struct ast_variable *vars;
1904 };
1905
1906 static void *fast_originate(void *data)
1907 {
1908         struct fast_originate_helper *in = data;
1909         int res;
1910         int reason = 0;
1911         struct ast_channel *chan = NULL;
1912         char requested_channel[AST_CHANNEL_NAME];
1913
1914         if (!ast_strlen_zero(in->app)) {
1915                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1916                         S_OR(in->cid_num, NULL),
1917                         S_OR(in->cid_name, NULL),
1918                         in->vars, in->account, &chan);
1919         } else {
1920                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1921                         S_OR(in->cid_num, NULL),
1922                         S_OR(in->cid_name, NULL),
1923                         in->vars, in->account, &chan);
1924         }
1925
1926         if (!chan)
1927                 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);     
1928         /* Tell the manager what happened with the channel */
1929         manager_event(EVENT_FLAG_CALL, "OriginateResponse",
1930                 "%s"
1931                 "Response: %s\r\n"
1932                 "Channel: %s\r\n"
1933                 "Context: %s\r\n"
1934                 "Exten: %s\r\n"
1935                 "Reason: %d\r\n"
1936                 "Uniqueid: %s\r\n"
1937                 "CallerIDNum: %s\r\n"
1938                 "CallerIDName: %s\r\n",
1939                 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
1940                 chan ? chan->uniqueid : "<null>",
1941                 S_OR(in->cid_num, "<unknown>"),
1942                 S_OR(in->cid_name, "<unknown>")
1943                 );
1944
1945         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1946         if (chan)
1947                 ast_channel_unlock(chan);
1948         ast_free(in);
1949         return NULL;
1950 }
1951
1952 static char mandescr_originate[] =
1953 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1954 "  Application/Data\n"
1955 "Variables: (Names marked with * are required)\n"
1956 "       *Channel: Channel name to call\n"
1957 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1958 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1959 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1960 "       Application: Application to use\n"
1961 "       Data: Data to use (requires 'Application')\n"
1962 "       Timeout: How long to wait for call to be answered (in ms)\n"
1963 "       CallerID: Caller ID to be set on the outgoing channel\n"
1964 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1965 "       Account: Account code\n"
1966 "       Async: Set to 'true' for fast origination\n";
1967
1968 static int action_originate(struct mansession *s, const struct message *m)
1969 {
1970         const char *name = astman_get_header(m, "Channel");
1971         const char *exten = astman_get_header(m, "Exten");
1972         const char *context = astman_get_header(m, "Context");
1973         const char *priority = astman_get_header(m, "Priority");
1974         const char *timeout = astman_get_header(m, "Timeout");
1975         const char *callerid = astman_get_header(m, "CallerID");
1976         const char *account = astman_get_header(m, "Account");
1977         const char *app = astman_get_header(m, "Application");
1978         const char *appdata = astman_get_header(m, "Data");
1979         const char *async = astman_get_header(m, "Async");
1980         const char *id = astman_get_header(m, "ActionID");
1981         struct ast_variable *vars = astman_get_variables(m);
1982         char *tech, *data;
1983         char *l = NULL, *n = NULL;
1984         int pi = 0;
1985         int res;
1986         int to = 30000;
1987         int reason = 0;
1988         char tmp[256];
1989         char tmp2[256];
1990
1991         pthread_t th;
1992         if (!name) {
1993                 astman_send_error(s, m, "Channel not specified");
1994                 return 0;
1995         }
1996         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1997                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1998                         astman_send_error(s, m, "Invalid priority\n");
1999                         return 0;
2000                 }
2001         }
2002         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2003                 astman_send_error(s, m, "Invalid timeout\n");
2004                 return 0;
2005         }
2006         ast_copy_string(tmp, name, sizeof(tmp));
2007         tech = tmp;
2008         data = strchr(tmp, '/');
2009         if (!data) {
2010                 astman_send_error(s, m, "Invalid channel\n");
2011                 return 0;
2012         }
2013         *data++ = '\0';
2014         ast_copy_string(tmp2, callerid, sizeof(tmp2));
2015         ast_callerid_parse(tmp2, &n, &l);
2016         if (n) {
2017                 if (ast_strlen_zero(n))
2018                         n = NULL;
2019         }
2020         if (l) {
2021                 ast_shrink_phone_number(l);
2022                 if (ast_strlen_zero(l))
2023                         l = NULL;
2024         }
2025         if (ast_true(async)) {
2026                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2027                 if (!fast) {
2028                         res = -1;
2029                 } else {
2030                         if (!ast_strlen_zero(id))
2031                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2032                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2033                         ast_copy_string(fast->data, data, sizeof(fast->data));
2034                         ast_copy_string(fast->app, app, sizeof(fast->app));
2035                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2036                         if (l)
2037                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2038                         if (n)
2039                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2040                         fast->vars = vars;
2041                         ast_copy_string(fast->context, context, sizeof(fast->context));
2042                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2043                         ast_copy_string(fast->account, account, sizeof(fast->account));
2044                         fast->timeout = to;
2045                         fast->priority = pi;
2046                         if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2047                                 res = -1;
2048                         } else {
2049                                 res = 0;
2050                         }
2051                 }
2052         } else if (!ast_strlen_zero(app)) {
2053                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2054         } else {
2055                 if (exten && context && pi)
2056                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2057                 else {
2058                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2059                         return 0;
2060                 }
2061         }
2062         if (!res)
2063                 astman_send_ack(s, m, "Originate successfully queued");
2064         else
2065                 astman_send_error(s, m, "Originate failed");
2066         return 0;
2067 }
2068
2069 /*! \brief Help text for manager command mailboxstatus
2070  */
2071 static char mandescr_mailboxstatus[] =
2072 "Description: Checks a voicemail account for status.\n"
2073 "Variables: (Names marked with * are required)\n"
2074 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2075 "       ActionID: Optional ActionID for message matching.\n"
2076 "Returns number of messages.\n"
2077 "       Message: Mailbox Status\n"
2078 "       Mailbox: <mailboxid>\n"
2079 "       Waiting: <count>\n"
2080 "\n";
2081
2082 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2083 {
2084         const char *mailbox = astman_get_header(m, "Mailbox");
2085         int ret;
2086
2087         if (ast_strlen_zero(mailbox)) {
2088                 astman_send_error(s, m, "Mailbox not specified");
2089                 return 0;
2090         }
2091         ret = ast_app_has_voicemail(mailbox, NULL);
2092         astman_start_ack(s, m);
2093         astman_append(s, "Message: Mailbox Status\r\n"
2094                          "Mailbox: %s\r\n"
2095                          "Waiting: %d\r\n\r\n", mailbox, ret);
2096         return 0;
2097 }
2098
2099 static char mandescr_mailboxcount[] =
2100 "Description: Checks a voicemail account for new messages.\n"
2101 "Variables: (Names marked with * are required)\n"
2102 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2103 "       ActionID: Optional ActionID for message matching.\n"
2104 "Returns number of new and old messages.\n"
2105 "       Message: Mailbox Message Count\n"
2106 "       Mailbox: <mailboxid>\n"
2107 "       NewMessages: <count>\n"
2108 "       OldMessages: <count>\n"
2109 "\n";
2110 static int action_mailboxcount(struct mansession *s, const struct message *m)
2111 {
2112         const char *mailbox = astman_get_header(m, "Mailbox");
2113         int newmsgs = 0, oldmsgs = 0;
2114
2115         if (ast_strlen_zero(mailbox)) {
2116                 astman_send_error(s, m, "Mailbox not specified");
2117                 return 0;
2118         }
2119         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2120         astman_start_ack(s, m);
2121         astman_append(s,   "Message: Mailbox Message Count\r\n"
2122                            "Mailbox: %s\r\n"
2123                            "NewMessages: %d\r\n"
2124                            "OldMessages: %d\r\n"
2125                            "\r\n",
2126                            mailbox, newmsgs, oldmsgs);
2127         return 0;
2128 }
2129
2130 static char mandescr_extensionstate[] =
2131 "Description: Report the extension state for given extension.\n"
2132 "  If the extension has a hint, will use devicestate to check\n"
2133 "  the status of the device connected to the extension.\n"
2134 "Variables: (Names marked with * are required)\n"
2135 "       *Exten: Extension to check state on\n"
2136 "       *Context: Context for extension\n"
2137 "       ActionId: Optional ID for this transaction\n"
2138 "Will return an \"Extension Status\" message.\n"
2139 "The response will include the hint for the extension and the status.\n";
2140
2141 static int action_extensionstate(struct mansession *s, const struct message *m)
2142 {
2143         const char *exten = astman_get_header(m, "Exten");
2144         const char *context = astman_get_header(m, "Context");
2145         char hint[256] = "";
2146         int status;
2147         if (ast_strlen_zero(exten)) {
2148                 astman_send_error(s, m, "Extension not specified");
2149                 return 0;
2150         }
2151         if (ast_strlen_zero(context))
2152                 context = "default";
2153         status = ast_extension_state(NULL, context, exten);
2154         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2155         astman_start_ack(s, m);
2156         astman_append(s,   "Message: Extension Status\r\n"
2157                            "Exten: %s\r\n"
2158                            "Context: %s\r\n"
2159                            "Hint: %s\r\n"
2160                            "Status: %d\r\n\r\n",
2161                            exten, context, hint, status);
2162         return 0;
2163 }
2164
2165 static char mandescr_timeout[] =
2166 "Description: Hangup a channel after a certain time.\n"
2167 "Variables: (Names marked with * are required)\n"
2168 "       *Channel: Channel name to hangup\n"
2169 "       *Timeout: Maximum duration of the call (sec)\n"
2170 "Acknowledges set time with 'Timeout Set' message\n";
2171
2172 static int action_timeout(struct mansession *s, const struct message *m)
2173 {
2174         struct ast_channel *c;
2175         const char *name = astman_get_header(m, "Channel");
2176         int timeout = atoi(astman_get_header(m, "Timeout"));
2177
2178         if (ast_strlen_zero(name)) {
2179                 astman_send_error(s, m, "No channel specified");
2180                 return 0;
2181         }
2182         if (!timeout) {
2183                 astman_send_error(s, m, "No timeout specified");
2184                 return 0;
2185         }
2186         c = ast_get_channel_by_name_locked(name);
2187         if (!c) {
2188                 astman_send_error(s, m, "No such channel");
2189                 return 0;
2190         }
2191         ast_channel_setwhentohangup(c, timeout);
2192         ast_channel_unlock(c);
2193         astman_send_ack(s, m, "Timeout Set");
2194         return 0;
2195 }
2196
2197 /*!
2198  * Send any applicable events to the client listening on this socket.
2199  * Wait only for a finite time on each event, and drop all events whether
2200  * they are successfully sent or not.
2201  */
2202 static int process_events(struct mansession *s)
2203 {
2204         int ret = 0;
2205
2206         ast_mutex_lock(&s->__lock);
2207         if (s->f != NULL) {
2208                 struct eventqent *eqe;
2209
2210                 while ( (eqe = NEW_EVENT(s)) ) {
2211                         ref_event(eqe);
2212                         if (!ret && s->authenticated &&
2213                             (s->readperm & eqe->category) == eqe->category &&
2214                             (s->send_events & eqe->category) == eqe->category) {
2215                                 if (send_string(s, eqe->eventdata) < 0)
2216                                         ret = -1;       /* don't send more */
2217                         }
2218                         s->last_ev = unref_event(s->last_ev);
2219                 }
2220         }
2221         ast_mutex_unlock(&s->__lock);
2222         return ret;
2223 }
2224
2225 static char mandescr_userevent[] =
2226 "Description: Send an event to manager sessions.\n"
2227 "Variables: (Names marked with * are required)\n"
2228 "       *UserEvent: EventStringToSend\n"
2229 "       Header1: Content1\n"
2230 "       HeaderN: ContentN\n";
2231
2232 static int action_userevent(struct mansession *s, const struct message *m)
2233 {
2234         const char *event = astman_get_header(m, "UserEvent");
2235         char body[2048] = "";
2236         int x, bodylen = 0;
2237         for (x = 0; x < m->hdrcount; x++) {
2238                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2239                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2240                         bodylen += strlen(m->headers[x]);
2241                         ast_copy_string(body + bodylen, "\r\n", 3);
2242                         bodylen += 2;
2243                 }
2244         }
2245
2246         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2247         return 0;
2248 }
2249
2250 static char mandescr_coresettings[] =
2251 "Description: Query for Core PBX settings.\n"
2252 "Variables: (Names marked with * are optional)\n"
2253 "       *ActionID: ActionID of this transaction\n";
2254
2255 /*! \brief Show PBX core settings information */
2256 static int action_coresettings(struct mansession *s, const struct message *m)
2257 {
2258         const char *actionid = astman_get_header(m, "ActionID");
2259         char idText[150] = "";
2260
2261         if (!ast_strlen_zero(actionid))
2262                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2263
2264         astman_append(s, "Response: Success\r\n"
2265                         "%s"
2266                         "AMIversion: %s\r\n"
2267                         "AsteriskVersion: %s\r\n"
2268                         "SystemName: %s\r\n"
2269                         "CoreMaxCalls: %d\r\n"
2270                         "CoreMaxLoadAvg: %f\r\n"
2271                         "CoreRunUser: %s\r\n"
2272                         "CoreRunGroup: %s\r\n"
2273                         "CoreMaxFilehandles: %d\r\n" 
2274                         "CoreRealTimeEnabled: %s\r\n"
2275                         "CoreCDRenabled: %s\r\n"
2276                         "CoreHTTPenabled: %s\r\n"
2277                         ,
2278                         idText,
2279                         AMI_VERSION,
2280                         ASTERISK_VERSION, 
2281                         ast_config_AST_SYSTEM_NAME,
2282                         option_maxcalls,
2283                         option_maxload,
2284                         ast_config_AST_RUN_USER,
2285                         ast_config_AST_RUN_GROUP,
2286                         option_maxfiles,
2287                         ast_realtime_enabled() ? "Yes" : "No",
2288                         check_cdr_enabled() ? "Yes" : "No",
2289                         check_webmanager_enabled() ? "Yes" : "No"
2290                         );
2291         return 0;
2292 }
2293
2294 static char mandescr_corestatus[] =
2295 "Description: Query for Core PBX status.\n"
2296 "Variables: (Names marked with * are optional)\n"
2297 "       *ActionID: ActionID of this transaction\n";
2298
2299 /*! \brief Show PBX core status information */
2300 static int action_corestatus(struct mansession *s, const struct message *m)
2301 {
2302         const char *actionid = astman_get_header(m, "ActionID");
2303         char idText[150];
2304         char startuptime[150];
2305         char reloadtime[150];
2306         struct ast_tm tm;
2307
2308         if (!ast_strlen_zero(actionid))
2309                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2310
2311         ast_localtime(&ast_startuptime, &tm, NULL);
2312         ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2313         ast_localtime(&ast_lastreloadtime, &tm, NULL);
2314         ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2315
2316         astman_append(s, "Response: Success\r\n"
2317                         "%s"
2318                         "CoreStartupTime: %s\r\n"
2319                         "CoreReloadTime: %s\r\n"
2320                         "CoreCurrentCalls: %d\r\n"
2321                         "",
2322                         idText,
2323                         startuptime,
2324                         reloadtime,
2325                         ast_active_channels()
2326                         );
2327         return 0;
2328 }
2329
2330 static char mandescr_reload[] =
2331 "Description: Send a reload event.\n"
2332 "Variables: (Names marked with * are optional)\n"
2333 "       *ActionID: ActionID of this transaction\n"
2334 "       *Module: Name of the module to reload\n";
2335
2336 /*! \brief Send a reload event */
2337 static int action_reload(struct mansession *s, const struct message *m)
2338 {
2339         const char *actionid = astman_get_header(m, "ActionID");
2340         const char *module = astman_get_header(m, "Module");
2341         int res = ast_module_reload(S_OR(module, NULL));
2342         char idText[80] = "";
2343
2344         if (!ast_strlen_zero(actionid))
2345                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2346         if (res == 2)
2347                 astman_append(s, "Response: Success\r\n%s", idText);
2348         else
2349                 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2350         return 0;
2351 }
2352
2353 /*
2354  * Done with the action handlers here, we start with the code in charge
2355  * of accepting connections and serving them.
2356  * accept_thread() forks a new thread for each connection, session_do(),
2357  * which in turn calls get_input() repeatedly until a full message has
2358  * been accumulated, and then invokes process_message() to pass it to
2359  * the appropriate handler.
2360  */
2361
2362 /*
2363  * Process an AMI message, performing desired action.
2364  * Return 0 on success, -1 on error that require the session to be destroyed.
2365  */
2366 static int process_message(struct mansession *s, const struct message *m)
2367 {
2368         char action[80] = "";
2369         int ret = 0;
2370         struct manager_action *tmp;
2371         const char *user = astman_get_header(m, "Username");
2372
2373         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2374         ast_debug(1, "Manager received command '%s'\n", action);
2375
2376         if (ast_strlen_zero(action)) {
2377                 ast_mutex_lock(&s->__lock);
2378                 astman_send_error(s, m, "Missing action in request");
2379                 ast_mutex_unlock(&s->__lock);
2380                 return 0;
2381         }
2382
2383         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2384                 ast_mutex_lock(&s->__lock);
2385                 astman_send_error(s, m, "Permission denied");
2386                 ast_mutex_unlock(&s->__lock);
2387                 return 0;
2388         }
2389
2390         if (!allowmultiplelogin && !s->authenticated && user &&
2391                 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2392                 if (check_manager_session_inuse(user)) {
2393                         sleep(1);
2394                         ast_mutex_lock(&s->__lock);
2395                         astman_send_error(s, m, "Login Already In Use");
2396                         ast_mutex_unlock(&s->__lock);
2397                         return -1;
2398                 }
2399         }
2400
2401         AST_RWLIST_RDLOCK(&actions);
2402         AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2403                 if (strcasecmp(action, tmp->action))
2404                         continue;
2405                 if ((s->writeperm & tmp->authority) == tmp->authority)
2406                         ret = tmp->func(s, m);
2407                 else
2408                         astman_send_error(s, m, "Permission denied");
2409                 break;
2410         }
2411         AST_RWLIST_UNLOCK(&actions);
2412
2413         if (!tmp) {
2414                 ast_mutex_lock(&s->__lock);
2415                 astman_send_error(s, m, "Invalid/unknown command. Use Action: ListCommands to show available commands.");
2416                 ast_mutex_unlock(&s->__lock);
2417         }
2418         if (ret)
2419                 return ret;
2420         /* Once done with our message, deliver any pending events */
2421         return process_events(s);
2422 }
2423
2424 /*!
2425  * Read one full line (including crlf) from the manager socket.
2426  * \note \verbatim
2427  * \r\n is the only valid terminator for the line.
2428  * (Note that, later, '\0' will be considered as the end-of-line marker,
2429  * so everything between the '\0' and the '\r\n' will not be used).
2430  * Also note that we assume output to have at least "maxlen" space.
2431  * \endverbatim
2432  */
2433 static int get_input(struct mansession *s, char *output)
2434 {
2435         int res, x;
2436         int maxlen = sizeof(s->inbuf) - 1;
2437         char *src = s->inbuf;
2438
2439         /*
2440          * Look for \r\n within the buffer. If found, copy to the output
2441          * buffer and return, trimming the \r\n (not used afterwards).
2442          */
2443         for (x = 0; x < s->inlen; x++) {
2444                 int cr; /* set if we have \r */
2445                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2446                         cr = 2; /* Found. Update length to include \r\n */
2447                 else if (src[x] == '\n')
2448                         cr = 1; /* also accept \n only */
2449                 else
2450                         continue;
2451                 memmove(output, src, x);        /*... but trim \r\n */
2452                 output[x] = '\0';               /* terminate the string */
2453                 x += cr;                        /* number of bytes used */
2454                 s->inlen -= x;                  /* remaining size */
2455                 memmove(src, src + x, s->inlen); /* remove used bytes */
2456                 return 1;
2457         }
2458         if (s->inlen >= maxlen) {
2459                 /* no crlf found, and buffer full - sorry, too long for us */
2460                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2461                 s->inlen = 0;
2462         }
2463         res = 0;
2464         while (res == 0) {
2465                 /* XXX do we really need this locking ? */
2466                 ast_mutex_lock(&s->__lock);
2467                 s->waiting_thread = pthread_self();
2468                 ast_mutex_unlock(&s->__lock);
2469
2470                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
2471
2472                 ast_mutex_lock(&s->__lock);
2473                 s->waiting_thread = AST_PTHREADT_NULL;
2474                 ast_mutex_unlock(&s->__lock);
2475         }
2476         if (res < 0) {
2477                 /* If we get a signal from some other thread (typically because
2478                  * there are new events queued), return 0 to notify the caller.
2479                  */
2480                 if (errno == EINTR)
2481                         return 0;
2482                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2483                 return -1;
2484         }
2485         ast_mutex_lock(&s->__lock);
2486         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2487         if (res < 1)
2488                 res = -1;       /* error return */
2489         else {
2490                 s->inlen += res;
2491                 src[s->inlen] = '\0';
2492                 res = 0;
2493         }
2494         ast_mutex_unlock(&s->__lock);
2495         return res;
2496 }
2497
2498 static int do_message(struct mansession *s)
2499 {
2500         struct message m = { 0 };
2501         char header_buf[sizeof(s->inbuf)] = { '\0' };
2502         int res;
2503
2504         for (;;) {
2505                 /* Check if any events are pending and do them if needed */
2506                 if (process_events(s))
2507                         return -1;
2508                 res = get_input(s, header_buf);
2509                 if (res == 0) {
2510                         continue;
2511                 } else if (res > 0) {
2512                         if (ast_strlen_zero(header_buf))
2513                                 return process_message(s, &m) ? -1 : 0;
2514                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2515                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2516                 } else {
2517                         return res;
2518                 }
2519         }
2520 }
2521
2522 /*! \brief The body of the individual manager session.
2523  * Call get_input() to read one line at a time
2524  * (or be woken up on new events), collect the lines in a
2525  * message until found an empty line, and execute the request.
2526  * In any case, deliver events asynchronously through process_events()
2527  * (called from here if no line is available, or at the end of
2528  * process_message(). )
2529  */
2530 static void *session_do(void *data)
2531 {
2532         struct server_instance *ser = data;
2533         struct mansession *s = ast_calloc(1, sizeof(*s));
2534         int flags;
2535         int res;
2536
2537         if (s == NULL)
2538                 goto done;
2539
2540         s->writetimeout = 100;
2541         s->waiting_thread = AST_PTHREADT_NULL;
2542
2543         flags = fcntl(ser->fd, F_GETFL);
2544         if (!block_sockets) /* make sure socket is non-blocking */
2545                 flags |= O_NONBLOCK;
2546         else
2547                 flags &= ~O_NONBLOCK;
2548         fcntl(ser->fd, F_SETFL, flags);
2549
2550         ast_mutex_init(&s->__lock);
2551         s->send_events = -1;
2552         /* these fields duplicate those in the 'ser' structure */
2553         s->fd = ser->fd;
2554         s->f = ser->f;
2555         s->sin = ser->requestor;
2556
2557         AST_LIST_LOCK(&sessions);
2558         AST_LIST_INSERT_HEAD(&sessions, s, list);
2559         ast_atomic_fetchadd_int(&num_sessions, 1);
2560         AST_LIST_UNLOCK(&sessions);
2561         /* Hook to the tail of the event queue */
2562         s->last_ev = grab_last();
2563         s->f = ser->f;
2564         astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);  /* welcome prompt */
2565         for (;;) {
2566                 if ((res = do_message(s)) < 0)
2567                         break;
2568         }
2569         /* session is over, explain why and terminate */
2570         if (s->authenticated) {
2571                         if (manager_displayconnects(s))
2572                         ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2573                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2574         } else {
2575                         if (displayconnects)
2576                         ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2577                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2578         }
2579         destroy_session(s);
2580
2581 done:
2582         ast_free(ser);
2583         return NULL;
2584 }
2585
2586 /*! \brief remove at most n_max stale session from the list. */
2587 static void purge_sessions(int n_max)
2588 {
2589         struct mansession *s;
2590         time_t now = time(NULL);
2591
2592         AST_LIST_LOCK(&sessions);
2593         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2594                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2595                         AST_LIST_REMOVE_CURRENT(list);
2596                         ast_atomic_fetchadd_int(&num_sessions, -1);
2597                         if (s->authenticated && (option_verbose > 1) && manager_displayconnects(s)) {
2598                                 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2599                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2600                         }
2601                         free_session(s);        /* XXX outside ? */
2602                         if (--n_max <= 0)
2603                                 break;
2604                 }
2605         }
2606         AST_LIST_TRAVERSE_SAFE_END;
2607         AST_LIST_UNLOCK(&sessions);
2608 }
2609
2610 /*
2611  * events are appended to a queue from where they
2612  * can be dispatched to clients.
2613  */
2614 static int append_event(const char *str, int category)
2615 {
2616         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2617         static int seq; /* sequence number */
2618
2619         if (!tmp)
2620                 return -1;
2621
2622         /* need to init all fields, because ast_malloc() does not */
2623         tmp->usecount = 0;
2624         tmp->category = category;
2625         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2626         AST_LIST_NEXT(tmp, eq_next) = NULL;
2627         strcpy(tmp->eventdata, str);
2628
2629         AST_LIST_LOCK(&all_events);
2630         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2631         AST_LIST_UNLOCK(&all_events);
2632
2633         return 0;
2634 }
2635
2636 /* XXX see if can be moved inside the function */
2637 AST_THREADSTORAGE(manager_event_buf);
2638 #define MANAGER_EVENT_BUF_INITSIZE   256
2639
2640 /*! \brief  manager_event: Send AMI event to client */
2641 int __manager_event(int category, const char *event,
2642         const char *file, int line, const char *func, const char *fmt, ...)
2643 {
2644         struct mansession *s;
2645         struct manager_custom_hook *hook;
2646         struct ast_str *auth = ast_str_alloca(80);
2647         const char *cat_str;
2648         va_list ap;
2649         struct timeval now;
2650         struct ast_str *buf;
2651
2652         /* Abort if there aren't any manager sessions */
2653         if (!num_sessions)
2654                 return 0;
2655
2656         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2657                 return -1;
2658
2659         cat_str = authority_to_str(category, &auth);
2660         ast_str_set(&buf, 0,
2661                         "Event: %s\r\nPrivilege: %s\r\n",
2662                          event, cat_str);
2663
2664         if (timestampevents) {
2665                 now = ast_tvnow();
2666                 ast_str_append(&buf, 0,
2667                                 "Timestamp: %ld.%06lu\r\n",
2668                                  now.tv_sec, (unsigned long) now.tv_usec);
2669         }
2670         if (manager_debug) {
2671                 static int seq;
2672                 ast_str_append(&buf, 0,
2673                                 "SequenceNumber: %d\r\n",
2674                                  ast_atomic_fetchadd_int(&seq, 1));
2675                 ast_str_append(&buf, 0,
2676                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
2677         }
2678
2679         va_start(ap, fmt);
2680         ast_str_append_va(&buf, 0, fmt, ap);
2681         va_end(ap);
2682
2683         ast_str_append(&buf, 0, "\r\n");
2684
2685         append_event(buf->str, category);
2686
2687         /* Wake up any sleeping sessions */
2688         AST_LIST_LOCK(&sessions);
2689         AST_LIST_TRAVERSE(&sessions, s, list) {
2690                 ast_mutex_lock(&s->__lock);
2691                 if (s->waiting_thread != AST_PTHREADT_NULL)
2692                         pthread_kill(s->waiting_thread, SIGURG);
2693                 ast_mutex_unlock(&s->__lock);
2694         }
2695         AST_LIST_UNLOCK(&sessions);
2696
2697         AST_RWLIST_RDLOCK(&manager_hooks);
2698         AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
2699                 hook->helper(category, event, buf->str);
2700         }
2701         AST_RWLIST_UNLOCK(&manager_hooks);
2702
2703         return 0;
2704 }
2705
2706 /*
2707  * support functions to register/unregister AMI action handlers,
2708  */
2709 int ast_manager_unregister(char *action)
2710 {
2711         struct manager_action *cur;
2712
2713         AST_RWLIST_WRLOCK(&actions);
2714         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
2715                 if (!strcasecmp(action, cur->action)) {
2716                         AST_RWLIST_REMOVE_CURRENT(list);
2717                         ast_free(cur);
2718                         ast_verb(2, "Manager unregistered action %s\n", action);
2719                         break;
2720                 }
2721         }
2722         AST_RWLIST_TRAVERSE_SAFE_END;
2723         AST_RWLIST_UNLOCK(&actions);
2724
2725         return 0;
2726 }
2727
2728 static int manager_state_cb(char *context, char *exten, int state, void *data)
2729 {
2730         /* Notify managers of change */
2731         char hint[BUFSIZ];
2732         ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
2733
2734         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
2735         return 0;
2736 }
2737
2738 static int ast_manager_register_struct(struct manager_action *act)
2739 {
2740         struct manager_action *cur, *prev = NULL;
2741
2742         AST_RWLIST_WRLOCK(&actions);
2743         AST_RWLIST_TRAVERSE(&actions, cur, list) {
2744                 int ret = strcasecmp(cur->action, act->action);
2745                 if (ret == 0) {
2746                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2747                         AST_RWLIST_UNLOCK(&actions);
2748                         return -1;
2749                 }
2750                 if (ret > 0) { /* Insert these alphabetically */
2751                         prev = cur;
2752                         break;
2753                 }
2754         }
2755         
2756         if (prev)       
2757                 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
2758         else
2759                 AST_RWLIST_INSERT_HEAD(&actions, act, list);
2760
2761         ast_verb(2, "Manager registered action %s\n", act->action);
2762
2763         AST_RWLIST_UNLOCK(&actions);
2764
2765         return 0;
2766 }
2767
2768 /*! \brief register a new command with manager, including online help. This is
2769         the preferred way to register a manager command */
2770 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
2771 {
2772         struct manager_action *cur = NULL;
2773
2774         if (!(cur = ast_calloc(1, sizeof(*cur))))
2775                 return -1;
2776
2777         cur->action = action;
2778         cur->authority = auth;
2779         cur->func = func;
2780         cur->synopsis = synopsis;
2781         cur->description = description;
2782
2783         ast_manager_register_struct(cur);
2784
2785         return 0;
2786 }
2787 /*! @}
2788  END Doxygen group */
2789
2790 /*
2791  * The following are support functions for AMI-over-http.
2792  * The common entry point is generic_http_callback(),
2793  * which extracts HTTP header and URI fields and reformats
2794  * them into AMI messages, locates a proper session
2795  * (using the mansession_id Cookie or GET variable),
2796  * and calls process_message() as for regular AMI clients.
2797  * When done, the output (which goes to a temporary file)
2798  * is read back into a buffer and reformatted as desired,
2799  * then fed back to the client over the original socket.
2800  */
2801
2802 enum output_format {
2803         FORMAT_RAW,
2804         FORMAT_HTML,
2805         FORMAT_XML,
2806 };
2807
2808 static char *contenttype[] = {
2809         [FORMAT_RAW] = "plain",
2810         [FORMAT_HTML] = "html",
2811         [FORMAT_XML] =  "xml",
2812 };
2813
2814 /*!
2815  * locate an http session in the list. The search key (ident) is
2816  * the value of the mansession_id cookie (0 is not valid and means
2817  * a session on the AMI socket).
2818  */
2819 static struct mansession *find_session(unsigned long ident)
2820 {
2821         struct mansession *s;
2822
2823         if (ident == 0)
2824                 return NULL;
2825
2826         AST_LIST_LOCK(&sessions);
2827         AST_LIST_TRAVERSE(&sessions, s, list) {
2828                 ast_mutex_lock(&s->__lock);
2829                 if (s->managerid == ident && !s->needdestroy) {
2830                         ast_atomic_fetchadd_int(&s->inuse, 1);
2831                         break;
2832                 }
2833                 ast_mutex_unlock(&s->__lock);
2834         }
2835         AST_LIST_UNLOCK(&sessions);
2836
2837         return s;
2838 }
2839
2840 int astman_verify_session_readpermissions(unsigned long ident, int perm)
2841 {
2842         int result = 0;
2843         struct mansession *s;
2844
2845         AST_LIST_LOCK(&sessions);
2846         AST_LIST_TRAVERSE(&sessions, s, list) {
2847                 ast_mutex_lock(&s->__lock);
2848                 if ((s->managerid == ident) && (s->readperm & perm)) {
2849                         result = 1;
2850                         ast_mutex_unlock(&s->__lock);
2851                         break;
2852                 }
2853                 ast_mutex_unlock(&s->__lock);
2854         }
2855         AST_LIST_UNLOCK(&sessions);
2856         return result;
2857 }
2858
2859 int astman_verify_session_writepermissions(unsigned long ident, int perm)
2860 {
2861         int result = 0;
2862         struct mansession *s;
2863
2864         AST_LIST_LOCK(&sessions);
2865         AST_LIST_TRAVERSE(&sessions, s, list) {
2866                 ast_mutex_lock(&s->__lock);
2867                 if ((s->managerid == ident) && (s->writeperm & perm)) {
2868                         result = 1;
2869                         ast_mutex_unlock(&s->__lock);
2870                         break;
2871                 }
2872                 ast_mutex_unlock(&s->__lock);
2873         }
2874         AST_LIST_UNLOCK(&sessions);
2875         return result;
2876 }
2877
2878 /*
2879  * convert to xml with various conversion:
2880  * mode & 1     -> lowercase;
2881  * mode & 2     -> replace non-alphanumeric chars with underscore
2882  */
2883 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
2884 {
2885         /* store in a local buffer to avoid calling ast_str_append too often */
2886         char buf[256];
2887         char *dst = buf;
2888         int space = sizeof(buf);
2889         /* repeat until done and nothing to flush */
2890         for ( ; *src || dst != buf ; src++) {
2891                 if (*src == '\0' || space < 10) {       /* flush */
2892                         *dst++ = '\0';
2893                         ast_str_append(out, 0, "%s", buf);
2894                         dst = buf;
2895                         space = sizeof(buf);
2896                         if (*src == '\0')
2897                                 break;
2898                 }
2899                         
2900                 if ( (mode & 2) && !isalnum(*src)) {
2901                         *dst++ = '_';
2902                         space--;
2903                         continue;
2904                 }
2905                 switch (*src) {
2906                 case '<':
2907                         strcpy(dst, "&lt;");
2908                         dst += 4;
2909                         space -= 4;
2910                         break;
2911                 case '>':
2912                         strcpy(dst, "&gt;");
2913                         dst += 4;
2914                         space -= 4;
2915                         break;
2916                 case '\"':
2917                         strcpy(dst, "&quot;");
2918                         dst += 6;
2919                         space -= 6;
2920                         break;
2921                 case '\'':
2922                         strcpy(dst, "&apos;");
2923                         dst += 6;
2924                         space -= 6;
2925                         break;
2926                 case '&':
2927                         strcpy(dst, "&amp;");
2928                         dst += 5;
2929                         space -= 5;
2930                         break;
2931
2932                 default:
2933                         *dst++ = mode ? tolower(*src) : *src;
2934                         space--;
2935                 }
2936         }
2937 }
2938
2939 struct variable_count {
2940         char *varname;
2941         int count;
2942 };
2943
2944 static int compress_char(char c)
2945 {
2946         c &= 0x7f;
2947         if (c < 32)
2948                 return 0;
2949         else if (c >= 'a' && c <= 'z')
2950                 return c - 64;
2951         else if (c > 'z')
2952                 return '_';
2953         else
2954                 return c - 32;
2955 }
2956
2957 static int variable_count_hash_fn(const void *vvc, const int flags)
2958 {
2959         const struct variable_count *vc = vvc;
2960         int res = 0, i;
2961         for (i = 0; i < 5; i++) {
2962                 if (vc->varname[i] == '\0')
2963                         break;
2964                 res += compress_char(vc->varname[i]) << (i * 6);
2965         }
2966         return res;
2967 }
2968
2969 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
2970 {
2971         /* Due to the simplicity of struct variable_count, it makes no difference
2972          * if you pass in objects or strings, the same operation applies. This is
2973          * due to the fact that the hash occurs on the first element, which means
2974          * the address of both the struct and the string are exactly the same. */
2975         struct variable_count *vc = obj;
2976         char *str = vstr;
2977         return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
2978 }
2979
2980 /*! \brief Convert the input into XML or HTML.
2981  * The input is supposed to be a sequence of lines of the form
2982  *      Name: value
2983  * optionally followed by a blob of unformatted text.
2984  * A blank line is a section separator. Basically, this is a
2985  * mixture of the format of Manager Interface and CLI commands.
2986  * The unformatted text is considered as a single value of a field
2987  * named 'Opaque-data'.
2988  *
2989  * At the moment the output format is the following (but it may
2990  * change depending on future requirements so don't count too
2991  * much on it when writing applications):
2992  *
2993  * General: the unformatted text is used as a value of
2994  * XML output:  to be completed
2995  * 
2996  * \verbatim
2997  *   Each section is within <response type="object" id="xxx">
2998  *   where xxx is taken from ajaxdest variable or defaults to unknown
2999  *   Each row is reported as an attribute Name="value" of an XML
3000  *   entity named from the variable ajaxobjtype, default to "generic"
3001  * \endverbatim
3002  *
3003  * HTML output:
3004  *   each Name-value pair is output as a single row of a two-column table.
3005  *   Sections (blank lines in the input) are separated by a <HR>
3006  *
3007  */
3008 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3009 {
3010         struct ast_variable *v;
3011         const char *dest = NULL;
3012         char *var, *val;
3013         const char *objtype = NULL;
3014         int in_data = 0;        /* parsing data */
3015         int inobj = 0;
3016         int xml = (format == FORMAT_XML);
3017         struct variable_count *vc = NULL;
3018         struct ao2_container *vco = NULL;
3019
3020         for (v = vars; v; v = v->next) {
3021                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3022                         dest = v->value;
3023                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3024                         objtype = v->value;
3025         }
3026         if (!dest)
3027                 dest = "unknown";
3028         if (!objtype)
3029                 objtype = "generic";
3030
3031         /* we want to stop when we find an empty line */
3032         while (in && *in) {
3033                 val = strsep(&in, "\r\n");      /* mark start and end of line */
3034                 if (in && *in == '\n')          /* remove trailing \n if any */
3035                         in++;
3036                 ast_trim_blanks(val);
3037                 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3038                 if (ast_strlen_zero(val)) {
3039                         if (in_data) { /* close data */
3040                                 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3041                                 in_data = 0;
3042                         }
3043                         ast_str_append(out, 0, xml ? " /></response>\n" :
3044                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3045                         inobj = 0;
3046                         ao2_ref(vco, -1);
3047                         vco = NULL;
3048                         continue;
3049                 }
3050
3051                 /* we expect Name: value lines */
3052                 if (in_data) {
3053                         var = NULL;
3054                 } else {
3055                         var = strsep(&val, ":");
3056                         if (val) {      /* found the field name */
3057                                 val = ast_skip_blanks(val);
3058                                 ast_trim_blanks(var);
3059                         } else {                /* field name not found, move to opaque mode */
3060                                 val = var;
3061                                 var = "Opaque-data";
3062                         }
3063                 }
3064
3065                 if (!inobj) {
3066                         if (xml)
3067                                 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3068                         else
3069                                 ast_str_append(out, 0, "<body>\n");
3070                         vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3071                         inobj = 1;
3072                 }
3073
3074                 if (!in_data) { /* build appropriate line start */
3075                         ast_str_append(out, 0, xml ? " " : "<tr><td>");
3076                         if ((vc = ao2_find(vco, var, 0)))
3077                                 vc->count++;
3078                         else {
3079                                 /* Create a new entry for this one */
3080                                 vc = ao2_alloc(sizeof(*vc), NULL);
3081                                 vc->varname = var;
3082                                 vc->count = 1;
3083                                 /* Increment refcount, because we're going to deref once later */
3084                                 ao2_ref(vc, 1);
3085                                 ao2_link(vco, vc);
3086                         }
3087                         xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3088                         if (vc->count > 1)
3089                                 ast_str_append(out, 0, "-%d", vc->count);
3090                         ao2_ref(vc, -1);
3091                         ast_str_append(out, 0, xml ? "='" : "</td><td>");
3092                         if (!strcmp(var, "Opaque-data"))
3093                                 in_data = 1;
3094                 }
3095                 xml_copy_escape(out, val, 0);   /* data field */
3096                 if (!in_data)
3097                         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3098                 else
3099                         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3100         }
3101         if (inobj) {
3102                 ast_str_append(out, 0, xml ? " /></response>\n" :
3103                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3104                 ao2_ref(vco, -1);
3105         }
3106 }
3107
3108 static struct ast_str *generic_http_callback(enum output_format format,
3109                                              struct sockaddr_in *requestor, const char *uri,
3110                                              struct ast_variable *params, int *status,
3111                                              char **title, int *contentlength)
3112 {
3113         struct mansession *s = NULL;
3114         unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3115         int blastaway = 0;
3116         struct ast_variable *v;
3117         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
3118         struct ast_str *out = NULL;
3119         struct message m = { 0 };
3120         unsigned int x;
3121         size_t hdrlen;
3122
3123         for (v = params; v; v = v->next) {
3124                 if (!strcasecmp(v->name, "mansession_id")) {
3125                         sscanf(v->value, "%lx", &ident);
3126                         break;
3127                 }
3128         }
3129
3130         if (!(s = find_session(ident))) {
3131                 /* Create new session.
3132                  * While it is not in the list we don't need any locking
3133                  */
3134                 if (!(s = ast_calloc(1, sizeof(*s)))) {
3135                         *status = 500;
3136                         goto generic_callback_out;
3137                 }
3138                 s->sin = *requestor;
3139                 s->fd = -1;
3140                 s->waiting_thread = AST_PTHREADT_NULL;
3141                 s->send_events = 0;
3142                 ast_mutex_init(&s->__lock);
3143                 ast_mutex_lock(&s->__lock);
3144                 s->inuse = 1;
3145                 s->managerid = rand() | 1;      /* make sure it is non-zero */
3146                 s->last_ev = grab_last();
3147                 AST_LIST_LOCK(&sessions);
3148                 AST_LIST_INSERT_HEAD(&sessions, s, list);
3149                 ast_atomic_fetchadd_int(&num_sessions, 1);
3150                 AST_LIST_UNLOCK(&sessions);
3151         }
3152
3153         ast_mutex_unlock(&s->__lock);
3154
3155         if (!(out = ast_str_create(1024))) {
3156                 *status = 500;
3157                 goto generic_callback_out;
3158         }
3159
3160         s->fd = mkstemp(template);      /* create a temporary file for command output */
3161         unlink(template);
3162         s->f = fdopen(s->fd, "w+");
3163
3164         for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3165                 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3166                 m.headers[m.hdrcount] = alloca(hdrlen);
3167                 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3168                 m.hdrcount = x + 1;
3169         }
3170
3171         if (process_message(s, &m)) {
3172                 if (s->authenticated) {
3173                                 if (manager_displayconnects(s))
3174                                 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3175                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3176                 } else {
3177                                 if (displayconnects)
3178                                 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3179                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3180                 }
3181                 s->needdestroy = 1;
3182         }
3183
3184         ast_str_append(&out, 0,
3185                        "Content-type: text/%s\r\n"
3186                        "Cache-Control: no-cache;\r\n"
3187                        "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
3188                        "\r\n",
3189                         contenttype[format],
3190                         s->managerid, httptimeout);
3191
3192         if (format == FORMAT_XML) {
3193                 ast_str_append(&out, 0, "<ajax-response>\n");
3194         } else if (format == FORMAT_HTML) {
3195
3196 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3197 #define TEST_STRING \
3198         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3199         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3200         <input type=\"submit\"></form>"
3201
3202                 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
3203                 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3204                 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3205                 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3206         }
3207
3208         if (s->f != NULL) {     /* have temporary output */
3209                 char *buf;
3210                 size_t l = ftell(s->f);
3211                 
3212                 if (l) {
3213                         if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3214                                 if (format == FORMAT_XML || format == FORMAT_HTML)
3215                                         xml_translate(&out, buf, params, format);
3216                                 else
3217                                         ast_str_append(&out, 0, buf);
3218                                 munmap(buf, l);
3219                         }
3220                 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3221                         xml_translate(&out, "", params, format);
3222                 }
3223                 fclose(s->f);
3224                 s->f = NULL;
3225                 s->fd = -1;
3226         }
3227
3228         if (format == FORMAT_XML) {
3229                 ast_str_append(&out, 0, "</ajax-response>\n");
3230         } else if (format == FORMAT_HTML)
3231                 ast_str_append(&out, 0, "</table></body>\r\n");
3232
3233         ast_mutex_lock(&s->__lock);
3234         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
3235         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3236
3237         if (s->needdestroy) {
3238                 if (s->inuse == 1) {
3239                         ast_debug(1, "Need destroy, doing it now!\n");
3240                         blastaway = 1;
3241                 } else {
3242                         ast_debug(1, "Need destroy, but can't do it yet!\n");