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