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