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