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