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