ce4d26221f100ddb897e0b5f1f519fdccbe9d25e
[asterisk/asterisk.git] / 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  * Channel Management and more
26  * 
27  * \ref amiconf
28  */
29
30 /*! \addtogroup Group_AMI AMI functions 
31 */
32 /*! @{ 
33  Doxygen group */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <netdb.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
45 #include <signal.h>
46 #include <errno.h>
47 #include <unistd.h>
48
49 #include "asterisk.h"
50
51 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
52
53 #include "asterisk/channel.h"
54 #include "asterisk/file.h"
55 #include "asterisk/manager.h"
56 #include "asterisk/config.h"
57 #include "asterisk/callerid.h"
58 #include "asterisk/lock.h"
59 #include "asterisk/logger.h"
60 #include "asterisk/options.h"
61 #include "asterisk/cli.h"
62 #include "asterisk/app.h"
63 #include "asterisk/pbx.h"
64 #include "asterisk/md5.h"
65 #include "asterisk/acl.h"
66 #include "asterisk/utils.h"
67
68 struct fast_originate_helper {
69         char tech[256];
70         char data[256];
71         int timeout;
72         char app[256];
73         char appdata[256];
74         char cid_name[256];
75         char cid_num[256];
76         char context[256];
77         char exten[256];
78         char idtext[256];
79         int priority;
80         struct ast_variable *vars;
81 };
82
83 static int enabled = 0;
84 static int portno = DEFAULT_MANAGER_PORT;
85 static int asock = -1;
86 static int displayconnects = 1;
87 static int timestampevents = 0;
88
89 static pthread_t t;
90 AST_MUTEX_DEFINE_STATIC(sessionlock);
91 static int block_sockets = 0;
92
93 static struct permalias {
94         int num;
95         char *label;
96 } perms[] = {
97         { EVENT_FLAG_SYSTEM, "system" },
98         { EVENT_FLAG_CALL, "call" },
99         { EVENT_FLAG_LOG, "log" },
100         { EVENT_FLAG_VERBOSE, "verbose" },
101         { EVENT_FLAG_COMMAND, "command" },
102         { EVENT_FLAG_AGENT, "agent" },
103         { EVENT_FLAG_USER, "user" },
104         { -1, "all" },
105         { 0, "none" },
106 };
107
108 static struct mansession *sessions = NULL;
109 static struct manager_action *first_action = NULL;
110 AST_MUTEX_DEFINE_STATIC(actionlock);
111
112 /*! If you are calling ast_carefulwrite, it is assumed that you are calling
113    it on a file descriptor that _DOES_ have NONBLOCK set.  This way,
114    there is only one system call made to do a write, unless we actually
115    have a need to wait.  This way, we get better performance. */
116 int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
117 {
118         /* Try to write string, but wait no more than ms milliseconds
119            before timing out */
120         int res=0;
121         struct pollfd fds[1];
122         while(len) {
123                 res = write(fd, s, len);
124                 if ((res < 0) && (errno != EAGAIN)) {
125                         return -1;
126                 }
127                 if (res < 0) res = 0;
128                 len -= res;
129                 s += res;
130                 res = 0;
131                 if (len) {
132                         fds[0].fd = fd;
133                         fds[0].events = POLLOUT;
134                         /* Wait until writable again */
135                         res = poll(fds, 1, timeoutms);
136                         if (res < 1)
137                                 return -1;
138                 }
139         }
140         return res;
141 }
142
143 /*! authority_to_str: Convert authority code to string with serveral options */
144 static char *authority_to_str(int authority, char *res, int reslen)
145 {
146         int running_total = 0, i;
147         memset(res, 0, reslen);
148         for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
149                 if (authority & perms[i].num) {
150                         if (*res) {
151                                 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
152                                 running_total++;
153                         }
154                         strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
155                         running_total += strlen(perms[i].label);
156                 }
157         }
158         if (ast_strlen_zero(res)) {
159                 ast_copy_string(res, "<none>", reslen);
160         }
161         return res;
162 }
163
164 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
165 {
166         struct manager_action *cur = first_action;
167         int which = 0;
168
169         ast_mutex_lock(&actionlock);
170         while (cur) { /* Walk the list of actions */
171                 if (!strncasecmp(word, cur->action, strlen(word))) {
172                         if (++which > state) {
173                                 char *ret = strdup(cur->action);
174                                 ast_mutex_unlock(&actionlock);
175                                 return ret;
176                         }
177                 }
178                 cur = cur->next;
179         }
180         ast_mutex_unlock(&actionlock);
181         return NULL;
182 }
183
184 static int handle_showmancmd(int fd, int argc, char *argv[])
185 {
186         struct manager_action *cur = first_action;
187         char authority[80];
188         int num;
189
190         if (argc != 4)
191                 return RESULT_SHOWUSAGE;
192         ast_mutex_lock(&actionlock);
193         while (cur) { /* Walk the list of actions */
194                 for (num = 3; num < argc; num++) {
195                         if (!strcasecmp(cur->action, argv[num])) {
196                                 ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
197                         }
198                 }
199                 cur = cur->next;
200         }
201
202         ast_mutex_unlock(&actionlock);
203         return RESULT_SUCCESS;
204 }
205
206 /*! \brief  handle_showmancmds: CLI command */
207 /* Should change to "manager show commands" */
208 static int handle_showmancmds(int fd, int argc, char *argv[])
209 {
210         struct manager_action *cur = first_action;
211         char authority[80];
212         char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
213
214         ast_mutex_lock(&actionlock);
215         ast_cli(fd, format, "Action", "Privilege", "Synopsis");
216         ast_cli(fd, format, "------", "---------", "--------");
217         while (cur) { /* Walk the list of actions */
218                 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
219                 cur = cur->next;
220         }
221
222         ast_mutex_unlock(&actionlock);
223         return RESULT_SUCCESS;
224 }
225
226 /*! \brief  handle_showmanconn: CLI command show manager connected */
227 /* Should change to "manager show connected" */
228 static int handle_showmanconn(int fd, int argc, char *argv[])
229 {
230         struct mansession *s;
231         char iabuf[INET_ADDRSTRLEN];
232         char *format = "  %-15.15s  %-15.15s\n";
233         ast_mutex_lock(&sessionlock);
234         s = sessions;
235         ast_cli(fd, format, "Username", "IP Address");
236         while (s) {
237                 ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
238                 s = s->next;
239         }
240
241         ast_mutex_unlock(&sessionlock);
242         return RESULT_SUCCESS;
243 }
244
245 static char showmancmd_help[] = 
246 "Usage: show manager command <actionname>\n"
247 "       Shows the detailed description for a specific Asterisk manager interface command.\n";
248
249 static char showmancmds_help[] = 
250 "Usage: show manager commands\n"
251 "       Prints a listing of all the available Asterisk manager interface commands.\n";
252
253 static char showmanconn_help[] = 
254 "Usage: show manager connected\n"
255 "       Prints a listing of the users that are currently connected to the\n"
256 "Asterisk manager interface.\n";
257
258 static struct ast_cli_entry show_mancmd_cli =
259         { { "show", "manager", "command", NULL },
260         handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
261
262 static struct ast_cli_entry show_mancmds_cli =
263         { { "show", "manager", "commands", NULL },
264         handle_showmancmds, "List manager interface commands", showmancmds_help };
265
266 static struct ast_cli_entry show_manconn_cli =
267         { { "show", "manager", "connected", NULL },
268         handle_showmanconn, "Show connected manager interface users", showmanconn_help };
269
270 static void free_session(struct mansession *s)
271 {
272         struct eventqent *eqe;
273         if (s->fd > -1)
274                 close(s->fd);
275         ast_mutex_destroy(&s->__lock);
276         while(s->eventq) {
277                 eqe = s->eventq;
278                 s->eventq = s->eventq->next;
279                 free(eqe);
280         }
281         free(s);
282 }
283
284 static void destroy_session(struct mansession *s)
285 {
286         struct mansession *cur, *prev = NULL;
287         ast_mutex_lock(&sessionlock);
288         cur = sessions;
289         while(cur) {
290                 if (cur == s)
291                         break;
292                 prev = cur;
293                 cur = cur->next;
294         }
295         if (cur) {
296                 if (prev)
297                         prev->next = cur->next;
298                 else
299                         sessions = cur->next;
300                 free_session(s);
301         } else
302                 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
303         ast_mutex_unlock(&sessionlock);
304         
305 }
306
307 char *astman_get_header(struct message *m, char *var)
308 {
309         char cmp[80];
310         int x;
311         snprintf(cmp, sizeof(cmp), "%s: ", var);
312         for (x=0;x<m->hdrcount;x++)
313                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
314                         return m->headers[x] + strlen(cmp);
315         return "";
316 }
317
318 struct ast_variable *astman_get_variables(struct message *m)
319 {
320         int varlen, x, y;
321         struct ast_variable *head = NULL, *cur;
322         char *var, *val;
323         unsigned int var_count;
324         char *vars[32];
325         
326         varlen = strlen("Variable: ");  
327
328         for (x = 0; x < m->hdrcount; x++) {
329                 if (strncasecmp("Variable: ", m->headers[x], varlen))
330                         continue;
331
332                 if (!(var = ast_strdupa(m->headers[x] + varlen)))
333                         return head;
334
335                 if ((var_count = ast_app_separate_args(var, '|', vars, sizeof(vars) / sizeof(vars[0])))) {
336                         for (y = 0; y < var_count; y++) {
337                                 if (!vars[y])
338                                         continue;
339                                 var = val = ast_strdupa(vars[y]);
340                                 strsep(&val, "=");
341                                 if (!val || ast_strlen_zero(var))
342                                         continue;
343                                 cur = ast_variable_new(var, val);
344                                 if (head) {
345                                         cur->next = head;
346                                         head = cur;
347                                 } else
348                                         head = cur;
349                         }
350                 }
351         }
352
353         return head;
354 }
355
356 /*! NOTE:
357    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
358    hold the session lock _or_ be running in an action callback (in which case s->busy will
359    be non-zero). In either of these cases, there is no need to lock-protect the session's
360    fd, since no other output will be sent (events will be queued), and no input will
361    be read until either the current action finishes or get_input() obtains the session
362    lock.
363  */
364 void astman_send_error(struct mansession *s, struct message *m, char *error)
365 {
366         char *id = astman_get_header(m,"ActionID");
367
368         ast_cli(s->fd, "Response: Error\r\n");
369         if (!ast_strlen_zero(id))
370                 ast_cli(s->fd, "ActionID: %s\r\n",id);
371         ast_cli(s->fd, "Message: %s\r\n\r\n", error);
372 }
373
374 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
375 {
376         char *id = astman_get_header(m,"ActionID");
377
378         ast_cli(s->fd, "Response: %s\r\n", resp);
379         if (!ast_strlen_zero(id))
380                 ast_cli(s->fd, "ActionID: %s\r\n",id);
381         if (msg)
382                 ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
383         else
384                 ast_cli(s->fd, "\r\n");
385 }
386
387 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
388 {
389         astman_send_response(s, m, "Success", msg);
390 }
391
392 /*! Tells you if smallstr exists inside bigstr
393    which is delim by delim and uses no buf or stringsep
394    ast_instring("this|that|more","this",',') == 1;
395
396    feel free to move this to app.c -anthm */
397 static int ast_instring(char *bigstr, char *smallstr, char delim) 
398 {
399         char *val = bigstr, *next;
400
401         do {
402                 if ((next = strchr(val, delim))) {
403                         if (!strncmp(val, smallstr, (next - val)))
404                                 return 1;
405                         else
406                                 continue;
407                 } else
408                         return !strcmp(smallstr, val);
409
410         } while (*(val = (next + 1)));
411
412         return 0;
413 }
414
415 static int get_perm(char *instr)
416 {
417         int x = 0, ret = 0;
418
419         if (!instr)
420                 return 0;
421
422         for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
423                 if (ast_instring(instr, perms[x].label, ','))
424                         ret |= perms[x].num;
425         
426         return ret;
427 }
428
429 static int ast_is_number(char *string) 
430 {
431         int ret = 1, x = 0;
432
433         if (!string)
434                 return 0;
435
436         for (x=0; x < strlen(string); x++) {
437                 if (!(string[x] >= 48 && string[x] <= 57)) {
438                         ret = 0;
439                         break;
440                 }
441         }
442         
443         return ret ? atoi(string) : 0;
444 }
445
446 static int ast_strings_to_mask(char *string) 
447 {
448         int x, ret = -1;
449         
450         x = ast_is_number(string);
451
452         if (x) {
453                 ret = x;
454         } else if (ast_strlen_zero(string)) {
455                 ret = -1;
456         } else if (ast_false(string)) {
457                 ret = 0;
458         } else if (ast_true(string)) {
459                 ret = 0;
460                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
461                         ret |= perms[x].num;            
462         } else {
463                 ret = 0;
464                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
465                         if (ast_instring(string, perms[x].label, ',')) 
466                                 ret |= perms[x].num;            
467                 }
468         }
469
470         return ret;
471 }
472
473 /*! 
474    Rather than braindead on,off this now can also accept a specific int mask value 
475    or a ',' delim list of mask strings (the same as manager.conf) -anthm
476 */
477
478 static int set_eventmask(struct mansession *s, char *eventmask)
479 {
480         int maskint = ast_strings_to_mask(eventmask);
481
482         ast_mutex_lock(&s->__lock);
483         if (maskint >= 0)       
484                 s->send_events = maskint;
485         ast_mutex_unlock(&s->__lock);
486         
487         return maskint;
488 }
489
490 static int authenticate(struct mansession *s, struct message *m)
491 {
492         struct ast_config *cfg;
493         char iabuf[INET_ADDRSTRLEN];
494         char *cat;
495         char *user = astman_get_header(m, "Username");
496         char *pass = astman_get_header(m, "Secret");
497         char *authtype = astman_get_header(m, "AuthType");
498         char *key = astman_get_header(m, "Key");
499         char *events = astman_get_header(m, "Events");
500         
501         cfg = ast_config_load("manager.conf");
502         if (!cfg)
503                 return -1;
504         cat = ast_category_browse(cfg, NULL);
505         while(cat) {
506                 if (strcasecmp(cat, "general")) {
507                         /* This is a user */
508                         if (!strcasecmp(cat, user)) {
509                                 struct ast_variable *v;
510                                 struct ast_ha *ha = NULL;
511                                 char *password = NULL;
512                                 v = ast_variable_browse(cfg, cat);
513                                 while (v) {
514                                         if (!strcasecmp(v->name, "secret")) {
515                                                 password = v->value;
516                                         } else if (!strcasecmp(v->name, "permit") ||
517                                                    !strcasecmp(v->name, "deny")) {
518                                                 ha = ast_append_ha(v->name, v->value, ha);
519                                         } else if (!strcasecmp(v->name, "writetimeout")) {
520                                                 int val = atoi(v->value);
521
522                                                 if (val < 100)
523                                                         ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
524                                                 else
525                                                         s->writetimeout = val;
526                                         }
527                                                 
528                                         v = v->next;
529                                 }
530                                 if (ha && !ast_apply_ha(ha, &(s->sin))) {
531                                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
532                                         ast_free_ha(ha);
533                                         ast_config_destroy(cfg);
534                                         return -1;
535                                 } else if (ha)
536                                         ast_free_ha(ha);
537                                 if (!strcasecmp(authtype, "MD5")) {
538                                         if (!ast_strlen_zero(key) && s->challenge) {
539                                                 int x;
540                                                 int len=0;
541                                                 char md5key[256] = "";
542                                                 struct MD5Context md5;
543                                                 unsigned char digest[16];
544                                                 MD5Init(&md5);
545                                                 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
546                                                 MD5Update(&md5, (unsigned char *) password, strlen(password));
547                                                 MD5Final(digest, &md5);
548                                                 for (x=0;x<16;x++)
549                                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
550                                                 if (!strcmp(md5key, key))
551                                                         break;
552                                                 else {
553                                                         ast_config_destroy(cfg);
554                                                         return -1;
555                                                 }
556                                         }
557                                 } else if (password && !strcasecmp(password, pass)) {
558                                         break;
559                                 } else {
560                                         ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
561                                         ast_config_destroy(cfg);
562                                         return -1;
563                                 }       
564                         }
565                 }
566                 cat = ast_category_browse(cfg, cat);
567         }
568         if (cat) {
569                 ast_copy_string(s->username, cat, sizeof(s->username));
570                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
571                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
572                 ast_config_destroy(cfg);
573                 if (events)
574                         set_eventmask(s, events);
575                 return 0;
576         }
577         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
578         ast_config_destroy(cfg);
579         return -1;
580 }
581
582 /*! \brief PING: Manager PING */
583 static char mandescr_ping[] = 
584 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the "
585 "  manager connection open.\n"
586 "Variables: NONE\n";
587
588 static int action_ping(struct mansession *s, struct message *m)
589 {
590         astman_send_response(s, m, "Pong", NULL);
591         return 0;
592 }
593
594 static char mandescr_listcommands[] = 
595 "Description: Returns the action name and synopsis for every\n"
596 "  action that is available to the user\n"
597 "Variables: NONE\n";
598
599 static int action_listcommands(struct mansession *s, struct message *m)
600 {
601         struct manager_action *cur = first_action;
602         char idText[256] = "";
603         char temp[BUFSIZ];
604         char *id = astman_get_header(m,"ActionID");
605
606         if (!ast_strlen_zero(id))
607                 snprintf(idText,256,"ActionID: %s\r\n",id);
608         ast_cli(s->fd, "Response: Success\r\n%s", idText);
609         ast_mutex_lock(&actionlock);
610         while (cur) { /* Walk the list of actions */
611                 if ((s->writeperm & cur->authority) == cur->authority)
612                         ast_cli(s->fd, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)) );
613                 cur = cur->next;
614         }
615         ast_mutex_unlock(&actionlock);
616         ast_cli(s->fd, "\r\n");
617
618         return 0;
619 }
620
621 static char mandescr_events[] = 
622 "Description: Enable/Disable sending of events to this manager\n"
623 "  client.\n"
624 "Variables:\n"
625 "       EventMask: 'on' if all events should be sent,\n"
626 "               'off' if no events should be sent,\n"
627 "               'system,call,log' to select which flags events should have to be sent.\n";
628
629 static int action_events(struct mansession *s, struct message *m)
630 {
631         char *mask = astman_get_header(m, "EventMask");
632         int res;
633
634         res = set_eventmask(s, mask);
635         if (res > 0)
636                 astman_send_response(s, m, "Events On", NULL);
637         else if (res == 0)
638                 astman_send_response(s, m, "Events Off", NULL);
639
640         return 0;
641 }
642
643 static char mandescr_logoff[] = 
644 "Description: Logoff this manager session\n"
645 "Variables: NONE\n";
646
647 static int action_logoff(struct mansession *s, struct message *m)
648 {
649         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
650         return -1;
651 }
652
653 static char mandescr_hangup[] = 
654 "Description: Hangup a channel\n"
655 "Variables: \n"
656 "       Channel: The channel name to be hungup\n";
657
658 static int action_hangup(struct mansession *s, struct message *m)
659 {
660         struct ast_channel *c = NULL;
661         char *name = astman_get_header(m, "Channel");
662         if (ast_strlen_zero(name)) {
663                 astman_send_error(s, m, "No channel specified");
664                 return 0;
665         }
666         c = ast_get_channel_by_name_locked(name);
667         if (!c) {
668                 astman_send_error(s, m, "No such channel");
669                 return 0;
670         }
671         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
672         ast_mutex_unlock(&c->lock);
673         astman_send_ack(s, m, "Channel Hungup");
674         return 0;
675 }
676
677 static char mandescr_setvar[] = 
678 "Description: Set a global or local channel variable.\n"
679 "Variables: (Names marked with * are required)\n"
680 "       Channel: Channel to set variable for\n"
681 "       *Variable: Variable name\n"
682 "       *Value: Value\n";
683
684 static int action_setvar(struct mansession *s, struct message *m)
685 {
686         struct ast_channel *c = NULL;
687         char *name = astman_get_header(m, "Channel");
688         char *varname = astman_get_header(m, "Variable");
689         char *varval = astman_get_header(m, "Value");
690         
691         if (ast_strlen_zero(varname)) {
692                 astman_send_error(s, m, "No variable specified");
693                 return 0;
694         }
695         
696         if (ast_strlen_zero(varval)) {
697                 astman_send_error(s, m, "No value specified");
698                 return 0;
699         }
700
701         if (!ast_strlen_zero(name)) {
702                 c = ast_get_channel_by_name_locked(name);
703                 if (!c) {
704                         astman_send_error(s, m, "No such channel");
705                         return 0;
706                 }
707         }
708         
709         pbx_builtin_setvar_helper(c, varname, varval);
710           
711         if (c)
712                 ast_mutex_unlock(&c->lock);
713
714         astman_send_ack(s, m, "Variable Set");  
715
716         return 0;
717 }
718
719 static char mandescr_getvar[] = 
720 "Description: Get the value of a global or local channel variable.\n"
721 "Variables: (Names marked with * are required)\n"
722 "       Channel: Channel to read variable from\n"
723 "       *Variable: Variable name\n"
724 "       ActionID: Optional Action id for message matching.\n";
725
726 static int action_getvar(struct mansession *s, struct message *m)
727 {
728         struct ast_channel *c = NULL;
729         char *name = astman_get_header(m, "Channel");
730         char *varname = astman_get_header(m, "Variable");
731         char *id = astman_get_header(m,"ActionID");
732         char *varval;
733         char workspace[1024];
734
735         if (ast_strlen_zero(varname)) {
736                 astman_send_error(s, m, "No variable specified");
737                 return 0;
738         }
739
740         if (!ast_strlen_zero(name)) {
741                 c = ast_get_channel_by_name_locked(name);
742                 if (!c) {
743                         astman_send_error(s, m, "No such channel");
744                         return 0;
745                 }
746         }
747
748         if (varname[strlen(varname) - 1] == ')') {
749                 varval = ast_func_read(c, varname, workspace, sizeof(workspace));
750         } else {
751                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
752         }
753
754         if (c)
755                 ast_mutex_unlock(&c->lock);
756         ast_cli(s->fd, "Response: Success\r\n"
757                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
758         if (!ast_strlen_zero(id))
759                 ast_cli(s->fd, "ActionID: %s\r\n",id);
760         ast_cli(s->fd, "\r\n");
761
762         return 0;
763 }
764
765
766 /*! \brief  action_status: Manager "status" command to show channels */
767 /* Needs documentation... */
768 static int action_status(struct mansession *s, struct message *m)
769 {
770         char *id = astman_get_header(m,"ActionID");
771         char *name = astman_get_header(m,"Channel");
772         char idText[256] = "";
773         struct ast_channel *c;
774         char bridge[256];
775         struct timeval now = ast_tvnow();
776         long elapsed_seconds=0;
777         int all = ast_strlen_zero(name); /* set if we want all channels */
778
779         astman_send_ack(s, m, "Channel status will follow");
780         if (!ast_strlen_zero(id))
781                 snprintf(idText,256,"ActionID: %s\r\n",id);
782         if (all)
783                 c = ast_channel_walk_locked(NULL);
784         else {
785                 c = ast_get_channel_by_name_locked(name);
786                 if (!c) {
787                         astman_send_error(s, m, "No such channel");
788                         return 0;
789                 }
790         }
791         /* if we look by name, we break after the first iteration */
792         while(c) {
793                 if (c->_bridge)
794                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
795                 else
796                         bridge[0] = '\0';
797                 if (c->pbx) {
798                         if (c->cdr) {
799                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
800                         }
801                         ast_cli(s->fd,
802                         "Event: Status\r\n"
803                         "Privilege: Call\r\n"
804                         "Channel: %s\r\n"
805                         "CallerID: %s\r\n"
806                         "CallerIDName: %s\r\n"
807                         "Account: %s\r\n"
808                         "State: %s\r\n"
809                         "Context: %s\r\n"
810                         "Extension: %s\r\n"
811                         "Priority: %d\r\n"
812                         "Seconds: %ld\r\n"
813                         "%s"
814                         "Uniqueid: %s\r\n"
815                         "%s"
816                         "\r\n",
817                         c->name, 
818                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
819                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
820                         c->accountcode,
821                         ast_state2str(c->_state), c->context,
822                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
823                 } else {
824                         ast_cli(s->fd,
825                         "Event: Status\r\n"
826                         "Privilege: Call\r\n"
827                         "Channel: %s\r\n"
828                         "CallerID: %s\r\n"
829                         "CallerIDName: %s\r\n"
830                         "Account: %s\r\n"
831                         "State: %s\r\n"
832                         "%s"
833                         "Uniqueid: %s\r\n"
834                         "%s"
835                         "\r\n",
836                         c->name, 
837                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
838                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
839                         c->accountcode,
840                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
841                 }
842                 ast_mutex_unlock(&c->lock);
843                 if (!all)
844                         break;
845                 c = ast_channel_walk_locked(c);
846         }
847         ast_cli(s->fd,
848         "Event: StatusComplete\r\n"
849         "%s"
850         "\r\n",idText);
851         return 0;
852 }
853
854 static char mandescr_redirect[] = 
855 "Description: Redirect (transfer) a call.\n"
856 "Variables: (Names marked with * are required)\n"
857 "       *Channel: Channel to redirect\n"
858 "       ExtraChannel: Second call leg to transfer (optional)\n"
859 "       *Exten: Extension to transfer to\n"
860 "       *Context: Context to transfer to\n"
861 "       *Priority: Priority to transfer to\n"
862 "       ActionID: Optional Action id for message matching.\n";
863
864 /*! \brief  action_redirect: The redirect manager command */
865 static int action_redirect(struct mansession *s, struct message *m)
866 {
867         char *name = astman_get_header(m, "Channel");
868         char *name2 = astman_get_header(m, "ExtraChannel");
869         char *exten = astman_get_header(m, "Exten");
870         char *context = astman_get_header(m, "Context");
871         char *priority = astman_get_header(m, "Priority");
872         struct ast_channel *chan, *chan2 = NULL;
873         int pi = 0;
874         int res;
875
876         if (ast_strlen_zero(name)) {
877                 astman_send_error(s, m, "Channel not specified");
878                 return 0;
879         }
880         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
881                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
882                         astman_send_error(s, m, "Invalid priority\n");
883                         return 0;
884                 }
885         }
886         chan = ast_get_channel_by_name_locked(name);
887         if (!chan) {
888                 char buf[BUFSIZ];
889                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
890                 astman_send_error(s, m, buf);
891                 return 0;
892         }
893         if (!ast_strlen_zero(name2))
894                 chan2 = ast_get_channel_by_name_locked(name2);
895         res = ast_async_goto(chan, context, exten, pi);
896         if (!res) {
897                 if (!ast_strlen_zero(name2)) {
898                         if (chan2)
899                                 res = ast_async_goto(chan2, context, exten, pi);
900                         else
901                                 res = -1;
902                         if (!res)
903                                 astman_send_ack(s, m, "Dual Redirect successful");
904                         else
905                                 astman_send_error(s, m, "Secondary redirect failed");
906                 } else
907                         astman_send_ack(s, m, "Redirect successful");
908         } else
909                 astman_send_error(s, m, "Redirect failed");
910         if (chan)
911                 ast_mutex_unlock(&chan->lock);
912         if (chan2)
913                 ast_mutex_unlock(&chan2->lock);
914         return 0;
915 }
916
917 static char mandescr_command[] = 
918 "Description: Run a CLI command.\n"
919 "Variables: (Names marked with * are required)\n"
920 "       *Command: Asterisk CLI command to run\n"
921 "       ActionID: Optional Action id for message matching.\n";
922
923 /*! \brief  action_command: Manager command "command" - execute CLI command */
924 static int action_command(struct mansession *s, struct message *m)
925 {
926         char *cmd = astman_get_header(m, "Command");
927         char *id = astman_get_header(m, "ActionID");
928         ast_cli(s->fd, "Response: Follows\r\nPrivilege: Command\r\n");
929         if (!ast_strlen_zero(id))
930                 ast_cli(s->fd, "ActionID: %s\r\n", id);
931         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
932         ast_cli_command(s->fd, cmd);
933         ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
934         return 0;
935 }
936
937 static void *fast_originate(void *data)
938 {
939         struct fast_originate_helper *in = data;
940         int res;
941         int reason = 0;
942         struct ast_channel *chan = NULL;
943
944         if (!ast_strlen_zero(in->app)) {
945                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
946                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
947                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
948                         in->vars, &chan);
949         } else {
950                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
951                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
952                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
953                         in->vars, &chan);
954         }   
955         
956         /* Tell the manager what happened with the channel */
957         manager_event(EVENT_FLAG_CALL,
958                 res ? "OriginateSuccess" : "OriginateFailure",
959                 "%s"
960                 "Channel: %s/%s\r\n"
961                 "Context: %s\r\n"
962                 "Exten: %s\r\n"
963                 "Reason: %d\r\n"
964                 "Uniqueid: %s\r\n"
965                 "CallerID: %s\r\n"
966                 "CallerIDName: %s\r\n",
967                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
968                 chan ? chan->uniqueid : "<null>",
969                 in->cid_num ? in->cid_num : "<unknown>",
970                 in->cid_name ? in->cid_name : "<unknown>"
971                 );
972
973         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
974         if (chan)
975                 ast_mutex_unlock(&chan->lock);
976         free(in);
977         return NULL;
978 }
979
980 static char mandescr_originate[] = 
981 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
982 "  Application/Data\n"
983 "Variables: (Names marked with * are required)\n"
984 "       *Channel: Channel name to call\n"
985 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
986 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
987 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
988 "       Application: Application to use\n"
989 "       Data: Data to use (requires 'Application')\n"
990 "       Timeout: How long to wait for call to be answered (in ms)\n"
991 "       CallerID: Caller ID to be set on the outgoing channel\n"
992 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
993 "       Account: Account code\n"
994 "       Async: Set to 'true' for fast origination\n";
995
996 static int action_originate(struct mansession *s, struct message *m)
997 {
998         char *name = astman_get_header(m, "Channel");
999         char *exten = astman_get_header(m, "Exten");
1000         char *context = astman_get_header(m, "Context");
1001         char *priority = astman_get_header(m, "Priority");
1002         char *timeout = astman_get_header(m, "Timeout");
1003         char *callerid = astman_get_header(m, "CallerID");
1004         char *account = astman_get_header(m, "Account");
1005         char *app = astman_get_header(m, "Application");
1006         char *appdata = astman_get_header(m, "Data");
1007         char *async = astman_get_header(m, "Async");
1008         char *id = astman_get_header(m, "ActionID");
1009         struct ast_variable *vars = astman_get_variables(m);
1010         char *tech, *data;
1011         char *l=NULL, *n=NULL;
1012         int pi = 0;
1013         int res;
1014         int to = 30000;
1015         int reason = 0;
1016         char tmp[256];
1017         char tmp2[256];
1018         
1019         pthread_t th;
1020         pthread_attr_t attr;
1021         if (!name) {
1022                 astman_send_error(s, m, "Channel not specified");
1023                 return 0;
1024         }
1025         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1026                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1027                         astman_send_error(s, m, "Invalid priority\n");
1028                         return 0;
1029                 }
1030         }
1031         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1032                 astman_send_error(s, m, "Invalid timeout\n");
1033                 return 0;
1034         }
1035         ast_copy_string(tmp, name, sizeof(tmp));
1036         tech = tmp;
1037         data = strchr(tmp, '/');
1038         if (!data) {
1039                 astman_send_error(s, m, "Invalid channel\n");
1040                 return 0;
1041         }
1042         *data = '\0';
1043         data++;
1044         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1045         ast_callerid_parse(tmp2, &n, &l);
1046         if (n) {
1047                 if (ast_strlen_zero(n))
1048                         n = NULL;
1049         }
1050         if (l) {
1051                 ast_shrink_phone_number(l);
1052                 if (ast_strlen_zero(l))
1053                         l = NULL;
1054         }
1055         if (account) {
1056                 struct ast_variable *newvar;
1057                 newvar = ast_variable_new("CDR(accountcode|r)", account);
1058                 newvar->next = vars;
1059                 vars = newvar;
1060         }
1061         if (ast_true(async)) {
1062                 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1063                 if (!fast) {
1064                         res = -1;
1065                 } else {
1066                         memset(fast, 0, sizeof(struct fast_originate_helper));
1067                         if (!ast_strlen_zero(id))
1068                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1069                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1070                         ast_copy_string(fast->data, data, sizeof(fast->data));
1071                         ast_copy_string(fast->app, app, sizeof(fast->app));
1072                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1073                         if (l)
1074                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1075                         if (n)
1076                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1077                         fast->vars = vars;      
1078                         ast_copy_string(fast->context, context, sizeof(fast->context));
1079                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1080                         fast->timeout = to;
1081                         fast->priority = pi;
1082                         pthread_attr_init(&attr);
1083                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1084                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1085                                 res = -1;
1086                         } else {
1087                                 res = 0;
1088                         }
1089                 }
1090         } else if (!ast_strlen_zero(app)) {
1091                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, NULL);
1092         } else {
1093                 if (exten && context && pi)
1094                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, NULL);
1095                 else {
1096                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1097                         return 0;
1098                 }
1099         }   
1100         if (!res)
1101                 astman_send_ack(s, m, "Originate successfully queued");
1102         else
1103                 astman_send_error(s, m, "Originate failed");
1104         return 0;
1105 }
1106
1107 /*!     \brief Help text for manager command mailboxstatus
1108  */
1109 static char mandescr_mailboxstatus[] = 
1110 "Description: Checks a voicemail account for status.\n"
1111 "Variables: (Names marked with * are required)\n"
1112 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1113 "       ActionID: Optional ActionID for message matching.\n"
1114 "Returns number of messages.\n"
1115 "       Message: Mailbox Status\n"
1116 "       Mailbox: <mailboxid>\n"
1117 "       Waiting: <count>\n"
1118 "\n";
1119
1120 static int action_mailboxstatus(struct mansession *s, struct message *m)
1121 {
1122         char *mailbox = astman_get_header(m, "Mailbox");
1123         char *id = astman_get_header(m,"ActionID");
1124         char idText[256] = "";
1125         int ret;
1126         if (ast_strlen_zero(mailbox)) {
1127                 astman_send_error(s, m, "Mailbox not specified");
1128                 return 0;
1129         }
1130         if (!ast_strlen_zero(id))
1131                 snprintf(idText,256,"ActionID: %s\r\n",id);
1132         ret = ast_app_has_voicemail(mailbox, NULL);
1133         ast_cli(s->fd, "Response: Success\r\n"
1134                                    "%s"
1135                                    "Message: Mailbox Status\r\n"
1136                                    "Mailbox: %s\r\n"
1137                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1138         return 0;
1139 }
1140
1141 static char mandescr_mailboxcount[] = 
1142 "Description: Checks a voicemail account for new messages.\n"
1143 "Variables: (Names marked with * are required)\n"
1144 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1145 "       ActionID: Optional ActionID for message matching.\n"
1146 "Returns number of new and old messages.\n"
1147 "       Message: Mailbox Message Count\n"
1148 "       Mailbox: <mailboxid>\n"
1149 "       NewMessages: <count>\n"
1150 "       OldMessages: <count>\n"
1151 "\n";
1152 static int action_mailboxcount(struct mansession *s, struct message *m)
1153 {
1154         char *mailbox = astman_get_header(m, "Mailbox");
1155         char *id = astman_get_header(m,"ActionID");
1156         char idText[256] = "";
1157         int newmsgs = 0, oldmsgs = 0;
1158         if (ast_strlen_zero(mailbox)) {
1159                 astman_send_error(s, m, "Mailbox not specified");
1160                 return 0;
1161         }
1162         ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
1163         if (!ast_strlen_zero(id)) {
1164                 snprintf(idText,256,"ActionID: %s\r\n",id);
1165         }
1166         ast_cli(s->fd, "Response: Success\r\n"
1167                                    "%s"
1168                                    "Message: Mailbox Message Count\r\n"
1169                                    "Mailbox: %s\r\n"
1170                                    "NewMessages: %d\r\n"
1171                                    "OldMessages: %d\r\n" 
1172                                    "\r\n",
1173                                     idText,mailbox, newmsgs, oldmsgs);
1174         return 0;
1175 }
1176
1177 static char mandescr_extensionstate[] = 
1178 "Description: Report the extension state for given extension.\n"
1179 "  If the extension has a hint, will use devicestate to check\n"
1180 "  the status of the device connected to the extension.\n"
1181 "Variables: (Names marked with * are required)\n"
1182 "       *Exten: Extension to check state on\n"
1183 "       *Context: Context for extension\n"
1184 "       ActionId: Optional ID for this transaction\n"
1185 "Will return an \"Extension Status\" message.\n"
1186 "The response will include the hint for the extension and the status.\n";
1187
1188 static int action_extensionstate(struct mansession *s, struct message *m)
1189 {
1190         char *exten = astman_get_header(m, "Exten");
1191         char *context = astman_get_header(m, "Context");
1192         char *id = astman_get_header(m,"ActionID");
1193         char idText[256] = "";
1194         char hint[256] = "";
1195         int status;
1196         if (ast_strlen_zero(exten)) {
1197                 astman_send_error(s, m, "Extension not specified");
1198                 return 0;
1199         }
1200         if (ast_strlen_zero(context))
1201                 context = "default";
1202         status = ast_extension_state(NULL, context, exten);
1203         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1204         if (!ast_strlen_zero(id)) {
1205                 snprintf(idText,256,"ActionID: %s\r\n",id);
1206         }
1207         ast_cli(s->fd, "Response: Success\r\n"
1208                                    "%s"
1209                                    "Message: Extension Status\r\n"
1210                                    "Exten: %s\r\n"
1211                                    "Context: %s\r\n"
1212                                    "Hint: %s\r\n"
1213                                    "Status: %d\r\n\r\n",
1214                                    idText,exten, context, hint, status);
1215         return 0;
1216 }
1217
1218 static char mandescr_timeout[] = 
1219 "Description: Hangup a channel after a certain time.\n"
1220 "Variables: (Names marked with * are required)\n"
1221 "       *Channel: Channel name to hangup\n"
1222 "       *Timeout: Maximum duration of the call (sec)\n"
1223 "Acknowledges set time with 'Timeout Set' message\n";
1224
1225 static int action_timeout(struct mansession *s, struct message *m)
1226 {
1227         struct ast_channel *c = NULL;
1228         char *name = astman_get_header(m, "Channel");
1229         int timeout = atoi(astman_get_header(m, "Timeout"));
1230         if (ast_strlen_zero(name)) {
1231                 astman_send_error(s, m, "No channel specified");
1232                 return 0;
1233         }
1234         if (!timeout) {
1235                 astman_send_error(s, m, "No timeout specified");
1236                 return 0;
1237         }
1238         c = ast_get_channel_by_name_locked(name);
1239         if (!c) {
1240                 astman_send_error(s, m, "No such channel");
1241                 return 0;
1242         }
1243         ast_channel_setwhentohangup(c, timeout);
1244         ast_mutex_unlock(&c->lock);
1245         astman_send_ack(s, m, "Timeout Set");
1246         return 0;
1247 }
1248
1249 static int process_message(struct mansession *s, struct message *m)
1250 {
1251         char action[80] = "";
1252         struct manager_action *tmp = first_action;
1253         char *id = astman_get_header(m,"ActionID");
1254         char idText[256] = "";
1255         char iabuf[INET_ADDRSTRLEN];
1256
1257         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1258         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1259
1260         if (ast_strlen_zero(action)) {
1261                 astman_send_error(s, m, "Missing action in request");
1262                 return 0;
1263         }
1264         if (!ast_strlen_zero(id)) {
1265                 snprintf(idText,256,"ActionID: %s\r\n",id);
1266         }
1267         if (!s->authenticated) {
1268                 if (!strcasecmp(action, "Challenge")) {
1269                         char *authtype;
1270                         authtype = astman_get_header(m, "AuthType");
1271                         if (!strcasecmp(authtype, "MD5")) {
1272                                 if (ast_strlen_zero(s->challenge))
1273                                         snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
1274                                 ast_mutex_lock(&s->__lock);
1275                                 ast_cli(s->fd, "Response: Success\r\n"
1276                                                 "%s"
1277                                                 "Challenge: %s\r\n\r\n",
1278                                                 idText,s->challenge);
1279                                 ast_mutex_unlock(&s->__lock);
1280                                 return 0;
1281                         } else {
1282                                 astman_send_error(s, m, "Must specify AuthType");
1283                                 return 0;
1284                         }
1285                 } else if (!strcasecmp(action, "Login")) {
1286                         if (authenticate(s, m)) {
1287                                 sleep(1);
1288                                 astman_send_error(s, m, "Authentication failed");
1289                                 return -1;
1290                         } else {
1291                                 s->authenticated = 1;
1292                                 if (option_verbose > 1) {
1293                                         if ( displayconnects ) {
1294                                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1295                                         }
1296                                 }
1297                                 ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1298                                 astman_send_ack(s, m, "Authentication accepted");
1299                         }
1300                 } else if (!strcasecmp(action, "Logoff")) {
1301                         astman_send_ack(s, m, "See ya");
1302                         return -1;
1303                 } else
1304                         astman_send_error(s, m, "Authentication Required");
1305         } else {
1306                 int ret=0;
1307                 struct eventqent *eqe;
1308                 ast_mutex_lock(&s->__lock);
1309                 s->busy = 1;
1310                 ast_mutex_unlock(&s->__lock);
1311                 while( tmp ) {          
1312                         if (!strcasecmp(action, tmp->action)) {
1313                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1314                                         if (tmp->func(s, m))
1315                                                 ret = -1;
1316                                 } else {
1317                                         astman_send_error(s, m, "Permission denied");
1318                                 }
1319                                 break;
1320                         }
1321                         tmp = tmp->next;
1322                 }
1323                 if (!tmp)
1324                         astman_send_error(s, m, "Invalid/unknown command");
1325                 ast_mutex_lock(&s->__lock);
1326                 s->busy = 0;
1327                 while(s->eventq) {
1328                         if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
1329                                 ret = -1;
1330                                 break;
1331                         }
1332                         eqe = s->eventq;
1333                         s->eventq = s->eventq->next;
1334                         free(eqe);
1335                 }
1336                 ast_mutex_unlock(&s->__lock);
1337                 return ret;
1338         }
1339         return 0;
1340 }
1341
1342 static int get_input(struct mansession *s, char *output)
1343 {
1344         /* output must have at least sizeof(s->inbuf) space */
1345         int res;
1346         int x;
1347         struct pollfd fds[1];
1348         char iabuf[INET_ADDRSTRLEN];
1349         for (x=1;x<s->inlen;x++) {
1350                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1351                         /* Copy output data up to and including \r\n */
1352                         memcpy(output, s->inbuf, x + 1);
1353                         /* Add trailing \0 */
1354                         output[x+1] = '\0';
1355                         /* Move remaining data back to the front */
1356                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1357                         s->inlen -= (x + 1);
1358                         return 1;
1359                 }
1360         } 
1361         if (s->inlen >= sizeof(s->inbuf) - 1) {
1362                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
1363                 s->inlen = 0;
1364         }
1365         fds[0].fd = s->fd;
1366         fds[0].events = POLLIN;
1367         do {
1368                 res = poll(fds, 1, -1);
1369                 if (res < 0) {
1370                         if (errno == EINTR) {
1371                                 if (s->dead)
1372                                         return -1;
1373                                 continue;
1374                         }
1375                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1376                         return -1;
1377                 } else if (res > 0) {
1378                         ast_mutex_lock(&s->__lock);
1379                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1380                         ast_mutex_unlock(&s->__lock);
1381                         if (res < 1)
1382                                 return -1;
1383                         break;
1384                 }
1385         } while(1);
1386         s->inlen += res;
1387         s->inbuf[s->inlen] = '\0';
1388         return 0;
1389 }
1390
1391 static void *session_do(void *data)
1392 {
1393         struct mansession *s = data;
1394         struct message m;
1395         char iabuf[INET_ADDRSTRLEN];
1396         int res;
1397         
1398         ast_mutex_lock(&s->__lock);
1399         ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
1400         ast_mutex_unlock(&s->__lock);
1401         memset(&m, 0, sizeof(m));
1402         for (;;) {
1403                 res = get_input(s, m.headers[m.hdrcount]);
1404                 if (res > 0) {
1405                         /* Strip trailing \r\n */
1406                         if (strlen(m.headers[m.hdrcount]) < 2)
1407                                 continue;
1408                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1409                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
1410                                 if (process_message(s, &m))
1411                                         break;
1412                                 memset(&m, 0, sizeof(m));
1413                         } else if (m.hdrcount < MAX_HEADERS - 1)
1414                                 m.hdrcount++;
1415                 } else if (res < 0)
1416                         break;
1417         }
1418         if (s->authenticated) {
1419                 if (option_verbose > 1) {
1420                         if (displayconnects) 
1421                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
1422                 }
1423                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1424         } else {
1425                 if (option_verbose > 1) {
1426                         if ( displayconnects )
1427                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1428                 }
1429                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1430         }
1431         destroy_session(s);
1432         return NULL;
1433 }
1434
1435 static void *accept_thread(void *ignore)
1436 {
1437         int as;
1438         struct sockaddr_in sin;
1439         socklen_t sinlen;
1440         struct mansession *s;
1441         struct protoent *p;
1442         int arg = 1;
1443         int flags;
1444         pthread_attr_t attr;
1445
1446         pthread_attr_init(&attr);
1447         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1448
1449         for (;;) {
1450                 sinlen = sizeof(sin);
1451                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1452                 if (as < 0) {
1453                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1454                         continue;
1455                 }
1456                 p = getprotobyname("tcp");
1457                 if (p) {
1458                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1459                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1460                         }
1461                 }
1462                 s = malloc(sizeof(struct mansession));
1463                 if (!s) {
1464                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1465                         continue;
1466                 } 
1467                 memset(s, 0, sizeof(struct mansession));
1468                 memcpy(&s->sin, &sin, sizeof(sin));
1469                 s->writetimeout = 100;
1470
1471                 if(! block_sockets) {
1472                         /* For safety, make sure socket is non-blocking */
1473                         flags = fcntl(as, F_GETFL);
1474                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
1475                 }
1476                 ast_mutex_init(&s->__lock);
1477                 s->fd = as;
1478                 s->send_events = -1;
1479                 ast_mutex_lock(&sessionlock);
1480                 s->next = sessions;
1481                 sessions = s;
1482                 ast_mutex_unlock(&sessionlock);
1483                 if (ast_pthread_create(&s->t, &attr, session_do, s))
1484                         destroy_session(s);
1485         }
1486         pthread_attr_destroy(&attr);
1487         return NULL;
1488 }
1489
1490 static int append_event(struct mansession *s, const char *str)
1491 {
1492         struct eventqent *tmp, *prev=NULL;
1493         tmp = malloc(sizeof(struct eventqent) + strlen(str));
1494         if (tmp) {
1495                 tmp->next = NULL;
1496                 strcpy(tmp->eventdata, str);
1497                 if (s->eventq) {
1498                         prev = s->eventq;
1499                         while(prev->next) 
1500                                 prev = prev->next;
1501                         prev->next = tmp;
1502                 } else {
1503                         s->eventq = tmp;
1504                 }
1505                 return 0;
1506         }
1507         return -1;
1508 }
1509
1510 /*! \brief  manager_event: Send AMI event to client */
1511 int manager_event(int category, const char *event, const char *fmt, ...)
1512 {
1513         struct mansession *s;
1514         char auth[80];
1515         char tmp[4096] = "";
1516         char *tmp_next = tmp;
1517         size_t tmp_left = sizeof(tmp) - 2;
1518         va_list ap;
1519
1520         ast_mutex_lock(&sessionlock);
1521         for (s = sessions; s; s = s->next) {
1522                 if ((s->readperm & category) != category)
1523                         continue;
1524
1525                 if ((s->send_events & category) != category)
1526                         continue;
1527
1528                 if (ast_strlen_zero(tmp)) {
1529                         struct timeval now;
1530
1531                         ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1532                                          event, authority_to_str(category, auth, sizeof(auth)));
1533                         if (timestampevents) {
1534                                 now = ast_tvnow();
1535                                 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1536                                                  now.tv_sec, (unsigned long) now.tv_usec);
1537                         }
1538                         va_start(ap, fmt);
1539                         ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1540                         va_end(ap);
1541                         *tmp_next++ = '\r';
1542                         *tmp_next++ = '\n';
1543                         *tmp_next = '\0';
1544                 }
1545
1546                 ast_mutex_lock(&s->__lock);
1547                 if (s->busy) {
1548                         append_event(s, tmp);
1549                 } else if (!s->dead) {
1550                         if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
1551                                 ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
1552                                 s->dead = 1;
1553                                 pthread_kill(s->t, SIGURG);
1554                         }
1555                 }
1556                 ast_mutex_unlock(&s->__lock);
1557         }
1558         ast_mutex_unlock(&sessionlock);
1559
1560         return 0;
1561 }
1562
1563 int ast_manager_unregister( char *action ) 
1564 {
1565         struct manager_action *cur = first_action, *prev = first_action;
1566
1567         ast_mutex_lock(&actionlock);
1568         while( cur ) {          
1569                 if (!strcasecmp(action, cur->action)) {
1570                         prev->next = cur->next;
1571                         free(cur);
1572                         if (option_verbose > 1) 
1573                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
1574                         ast_mutex_unlock(&actionlock);
1575                         return 0;
1576                 }
1577                 prev = cur;
1578                 cur = cur->next;
1579         }
1580         ast_mutex_unlock(&actionlock);
1581         return 0;
1582 }
1583
1584 static int manager_state_cb(char *context, char *exten, int state, void *data)
1585 {
1586         /* Notify managers of change */
1587         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
1588         return 0;
1589 }
1590
1591 static int ast_manager_register_struct(struct manager_action *act)
1592 {
1593         struct manager_action *cur = first_action, *prev = NULL;
1594         int ret;
1595
1596         ast_mutex_lock(&actionlock);
1597         while(cur) { /* Walk the list of actions */
1598                 ret = strcasecmp(cur->action, act->action);
1599                 if (ret == 0) {
1600                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
1601                         ast_mutex_unlock(&actionlock);
1602                         return -1;
1603                 } else if (ret > 0) {
1604                         /* Insert these alphabetically */
1605                         if (prev) {
1606                                 act->next = prev->next;
1607                                 prev->next = act;
1608                         } else {
1609                                 act->next = first_action;
1610                                 first_action = act;
1611                         }
1612                         break;
1613                 }
1614                 prev = cur; 
1615                 cur = cur->next;
1616         }
1617         
1618         if (!cur) {
1619                 if (prev)
1620                         prev->next = act;
1621                 else
1622                         first_action = act;
1623                 act->next = NULL;
1624         }
1625
1626         if (option_verbose > 1) 
1627                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
1628         ast_mutex_unlock(&actionlock);
1629         return 0;
1630 }
1631
1632 /*! \brief register a new command with manager, including online help. This is 
1633         the preferred way to register a manager command */
1634 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
1635 {
1636         struct manager_action *cur;
1637
1638         cur = malloc(sizeof(struct manager_action));
1639         if (!cur) {
1640                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
1641                 return -1;
1642         }
1643         cur->action = action;
1644         cur->authority = auth;
1645         cur->func = func;
1646         cur->synopsis = synopsis;
1647         cur->description = description;
1648         cur->next = NULL;
1649
1650         ast_manager_register_struct(cur);
1651
1652         return 0;
1653 }
1654 /*! @}
1655  END Doxygen group */
1656
1657 static int registered = 0;
1658
1659 int init_manager(void)
1660 {
1661         struct ast_config *cfg;
1662         char *val;
1663         int oldportno = portno;
1664         static struct sockaddr_in ba;
1665         int x = 1;
1666         if (!registered) {
1667                 /* Register default actions */
1668                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
1669                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
1670                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
1671                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
1672                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
1673                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
1674                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
1675                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
1676                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
1677                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
1678                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
1679                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
1680                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
1681                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
1682                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
1683
1684                 ast_cli_register(&show_mancmd_cli);
1685                 ast_cli_register(&show_mancmds_cli);
1686                 ast_cli_register(&show_manconn_cli);
1687                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
1688                 registered = 1;
1689         }
1690         portno = DEFAULT_MANAGER_PORT;
1691         displayconnects = 1;
1692         cfg = ast_config_load("manager.conf");
1693         if (!cfg) {
1694                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
1695                 return 0;
1696         }
1697         memset(&ba, 0, sizeof(ba));
1698         val = ast_variable_retrieve(cfg, "general", "enabled");
1699         if (val)
1700                 enabled = ast_true(val);
1701
1702         val = ast_variable_retrieve(cfg, "general", "block-sockets");
1703         if(val)
1704                 block_sockets = ast_true(val);
1705
1706         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
1707                 if (sscanf(val, "%d", &portno) != 1) {
1708                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
1709                         portno = DEFAULT_MANAGER_PORT;
1710                 }
1711         }
1712
1713         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
1714                 displayconnects = ast_true(val);
1715
1716         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
1717                 timestampevents = ast_true(val);
1718         
1719         ba.sin_family = AF_INET;
1720         ba.sin_port = htons(portno);
1721         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1722         
1723         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
1724                 if (!inet_aton(val, &ba.sin_addr)) { 
1725                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
1726                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1727                 }
1728         }
1729         
1730         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
1731 #if 0
1732                 /* Can't be done yet */
1733                 close(asock);
1734                 asock = -1;
1735 #else
1736                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
1737 #endif
1738         }
1739         ast_config_destroy(cfg);
1740         
1741         /* If not enabled, do nothing */
1742         if (!enabled) {
1743                 return 0;
1744         }
1745         if (asock < 0) {
1746                 asock = socket(AF_INET, SOCK_STREAM, 0);
1747                 if (asock < 0) {
1748                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
1749                         return -1;
1750                 }
1751                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
1752                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
1753                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
1754                         close(asock);
1755                         asock = -1;
1756                         return -1;
1757                 }
1758                 if (listen(asock, 2)) {
1759                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
1760                         close(asock);
1761                         asock = -1;
1762                         return -1;
1763                 }
1764                 if (option_verbose)
1765                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
1766                 ast_pthread_create(&t, NULL, accept_thread, NULL);
1767         }
1768         return 0;
1769 }
1770
1771 int reload_manager(void)
1772 {
1773         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
1774         return init_manager();
1775 }