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