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