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