add optional timestamps to manager events (issue #5535, simplified)
[asterisk/asterisk.git] / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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(char *line, 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         pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
749
750         if (c)
751                 ast_mutex_unlock(&c->lock);
752         ast_cli(s->fd, "Response: Success\r\n"
753                 "Variable: %s\r\nValue: %s\r\n", varname, varval);
754         if (!ast_strlen_zero(id))
755                 ast_cli(s->fd, "ActionID: %s\r\n",id);
756         ast_cli(s->fd, "\r\n");
757
758         return 0;
759 }
760
761
762 /*! \brief  action_status: Manager "status" command to show channels */
763 /* Needs documentation... */
764 static int action_status(struct mansession *s, struct message *m)
765 {
766         char *id = astman_get_header(m,"ActionID");
767         char *name = astman_get_header(m,"Channel");
768         char idText[256] = "";
769         struct ast_channel *c;
770         char bridge[256];
771         struct timeval now = ast_tvnow();
772         long elapsed_seconds=0;
773         int all = ast_strlen_zero(name); /* set if we want all channels */
774
775         astman_send_ack(s, m, "Channel status will follow");
776         if (!ast_strlen_zero(id))
777                 snprintf(idText,256,"ActionID: %s\r\n",id);
778         if (all)
779                 c = ast_channel_walk_locked(NULL);
780         else {
781                 c = ast_get_channel_by_name_locked(name);
782                 if (!c) {
783                         astman_send_error(s, m, "No such channel");
784                         return 0;
785                 }
786         }
787         /* if we look by name, we break after the first iteration */
788         while(c) {
789                 if (c->_bridge)
790                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
791                 else
792                         bridge[0] = '\0';
793                 if (c->pbx) {
794                         if (c->cdr) {
795                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
796                         }
797                         ast_cli(s->fd,
798                         "Event: Status\r\n"
799                         "Privilege: Call\r\n"
800                         "Channel: %s\r\n"
801                         "CallerID: %s\r\n"
802                         "CallerIDName: %s\r\n"
803                         "Account: %s\r\n"
804                         "State: %s\r\n"
805                         "Context: %s\r\n"
806                         "Extension: %s\r\n"
807                         "Priority: %d\r\n"
808                         "Seconds: %ld\r\n"
809                         "%s"
810                         "Uniqueid: %s\r\n"
811                         "%s"
812                         "\r\n",
813                         c->name, 
814                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
815                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
816                         c->accountcode,
817                         ast_state2str(c->_state), c->context,
818                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
819                 } else {
820                         ast_cli(s->fd,
821                         "Event: Status\r\n"
822                         "Privilege: Call\r\n"
823                         "Channel: %s\r\n"
824                         "CallerID: %s\r\n"
825                         "CallerIDName: %s\r\n"
826                         "Account: %s\r\n"
827                         "State: %s\r\n"
828                         "%s"
829                         "Uniqueid: %s\r\n"
830                         "%s"
831                         "\r\n",
832                         c->name, 
833                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
834                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
835                         c->accountcode,
836                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
837                 }
838                 ast_mutex_unlock(&c->lock);
839                 if (!all)
840                         break;
841                 c = ast_channel_walk_locked(c);
842         }
843         ast_cli(s->fd,
844         "Event: StatusComplete\r\n"
845         "%s"
846         "\r\n",idText);
847         return 0;
848 }
849
850 static char mandescr_redirect[] = 
851 "Description: Redirect (transfer) a call.\n"
852 "Variables: (Names marked with * are required)\n"
853 "       *Channel: Channel to redirect\n"
854 "       ExtraChannel: Second call leg to transfer (optional)\n"
855 "       *Exten: Extension to transfer to\n"
856 "       *Context: Context to transfer to\n"
857 "       *Priority: Priority to transfer to\n"
858 "       ActionID: Optional Action id for message matching.\n";
859
860 /*! \brief  action_redirect: The redirect manager command */
861 static int action_redirect(struct mansession *s, struct message *m)
862 {
863         char *name = astman_get_header(m, "Channel");
864         char *name2 = astman_get_header(m, "ExtraChannel");
865         char *exten = astman_get_header(m, "Exten");
866         char *context = astman_get_header(m, "Context");
867         char *priority = astman_get_header(m, "Priority");
868         struct ast_channel *chan, *chan2 = NULL;
869         int pi = 0;
870         int res;
871
872         if (ast_strlen_zero(name)) {
873                 astman_send_error(s, m, "Channel not specified");
874                 return 0;
875         }
876         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
877                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
878                         astman_send_error(s, m, "Invalid priority\n");
879                         return 0;
880                 }
881         }
882         chan = ast_get_channel_by_name_locked(name);
883         if (!chan) {
884                 char buf[BUFSIZ];
885                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
886                 astman_send_error(s, m, buf);
887                 return 0;
888         }
889         if (!ast_strlen_zero(name2))
890                 chan2 = ast_get_channel_by_name_locked(name2);
891         res = ast_async_goto(chan, context, exten, pi);
892         if (!res) {
893                 if (!ast_strlen_zero(name2)) {
894                         if (chan2)
895                                 res = ast_async_goto(chan2, context, exten, pi);
896                         else
897                                 res = -1;
898                         if (!res)
899                                 astman_send_ack(s, m, "Dual Redirect successful");
900                         else
901                                 astman_send_error(s, m, "Secondary redirect failed");
902                 } else
903                         astman_send_ack(s, m, "Redirect successful");
904         } else
905                 astman_send_error(s, m, "Redirect failed");
906         if (chan)
907                 ast_mutex_unlock(&chan->lock);
908         if (chan2)
909                 ast_mutex_unlock(&chan2->lock);
910         return 0;
911 }
912
913 static char mandescr_command[] = 
914 "Description: Run a CLI command.\n"
915 "Variables: (Names marked with * are required)\n"
916 "       *Command: Asterisk CLI command to run\n"
917 "       ActionID: Optional Action id for message matching.\n";
918
919 /*! \brief  action_command: Manager command "command" - execute CLI command */
920 static int action_command(struct mansession *s, struct message *m)
921 {
922         char *cmd = astman_get_header(m, "Command");
923         char *id = astman_get_header(m, "ActionID");
924         ast_cli(s->fd, "Response: Follows\r\nPrivilege: Command\r\n");
925         if (!ast_strlen_zero(id))
926                 ast_cli(s->fd, "ActionID: %s\r\n", id);
927         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
928         ast_cli_command(s->fd, cmd);
929         ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
930         return 0;
931 }
932
933 static void *fast_originate(void *data)
934 {
935         struct fast_originate_helper *in = data;
936         int res;
937         int reason = 0;
938         struct ast_channel *chan = NULL;
939
940         if (!ast_strlen_zero(in->app)) {
941                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
942                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
943                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
944                         in->vars, &chan);
945         } else {
946                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
947                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
948                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
949                         in->vars, &chan);
950         }   
951         
952         /* Tell the manager what happened with the channel */
953         manager_event(EVENT_FLAG_CALL,
954                 res ? "OriginateSuccess" : "OriginateFailure",
955                 "%s"
956                 "Channel: %s/%s\r\n"
957                 "Context: %s\r\n"
958                 "Exten: %s\r\n"
959                 "Reason: %d\r\n"
960                 "Uniqueid: %s\r\n"
961                 "CallerID: %s\r\n"
962                 "CallerIDName: %s\r\n",
963                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
964                 chan ? chan->uniqueid : "<null>",
965                 in->cid_num ? in->cid_num : "<unknown>",
966                 in->cid_name ? in->cid_name : "<unknown>"
967                 );
968
969         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
970         if (chan)
971                 ast_mutex_unlock(&chan->lock);
972         free(in);
973         return NULL;
974 }
975
976 static char mandescr_originate[] = 
977 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
978 "  Application/Data\n"
979 "Variables: (Names marked with * are required)\n"
980 "       *Channel: Channel name to call\n"
981 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
982 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
983 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
984 "       Application: Application to use\n"
985 "       Data: Data to use (requires 'Application')\n"
986 "       Timeout: How long to wait for call to be answered (in ms)\n"
987 "       CallerID: Caller ID to be set on the outgoing channel\n"
988 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
989 "       Account: Account code\n"
990 "       Async: Set to 'true' for fast origination\n";
991
992 static int action_originate(struct mansession *s, struct message *m)
993 {
994         char *name = astman_get_header(m, "Channel");
995         char *exten = astman_get_header(m, "Exten");
996         char *context = astman_get_header(m, "Context");
997         char *priority = astman_get_header(m, "Priority");
998         char *timeout = astman_get_header(m, "Timeout");
999         char *callerid = astman_get_header(m, "CallerID");
1000         char *account = astman_get_header(m, "Account");
1001         char *app = astman_get_header(m, "Application");
1002         char *appdata = astman_get_header(m, "Data");
1003         char *async = astman_get_header(m, "Async");
1004         char *id = astman_get_header(m, "ActionID");
1005         struct ast_variable *vars = astman_get_variables(m);
1006         char *tech, *data;
1007         char *l=NULL, *n=NULL;
1008         int pi = 0;
1009         int res;
1010         int to = 30000;
1011         int reason = 0;
1012         char tmp[256];
1013         char tmp2[256];
1014         
1015         pthread_t th;
1016         pthread_attr_t attr;
1017         if (!name) {
1018                 astman_send_error(s, m, "Channel not specified");
1019                 return 0;
1020         }
1021         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1022                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1023                         astman_send_error(s, m, "Invalid priority\n");
1024                         return 0;
1025                 }
1026         }
1027         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1028                 astman_send_error(s, m, "Invalid timeout\n");
1029                 return 0;
1030         }
1031         ast_copy_string(tmp, name, sizeof(tmp));
1032         tech = tmp;
1033         data = strchr(tmp, '/');
1034         if (!data) {
1035                 astman_send_error(s, m, "Invalid channel\n");
1036                 return 0;
1037         }
1038         *data = '\0';
1039         data++;
1040         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1041         ast_callerid_parse(tmp2, &n, &l);
1042         if (n) {
1043                 if (ast_strlen_zero(n))
1044                         n = NULL;
1045         }
1046         if (l) {
1047                 ast_shrink_phone_number(l);
1048                 if (ast_strlen_zero(l))
1049                         l = NULL;
1050         }
1051         if (account) {
1052                 struct ast_variable *newvar;
1053                 newvar = ast_variable_new("CDR(accountcode|r)", account);
1054                 newvar->next = vars;
1055                 vars = newvar;
1056         }
1057         if (ast_true(async)) {
1058                 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1059                 if (!fast) {
1060                         res = -1;
1061                 } else {
1062                         memset(fast, 0, sizeof(struct fast_originate_helper));
1063                         if (!ast_strlen_zero(id))
1064                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1065                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1066                         ast_copy_string(fast->data, data, sizeof(fast->data));
1067                         ast_copy_string(fast->app, app, sizeof(fast->app));
1068                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1069                         if (l)
1070                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1071                         if (n)
1072                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1073                         fast->vars = vars;      
1074                         ast_copy_string(fast->context, context, sizeof(fast->context));
1075                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1076                         fast->timeout = to;
1077                         fast->priority = pi;
1078                         pthread_attr_init(&attr);
1079                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1080                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1081                                 res = -1;
1082                         } else {
1083                                 res = 0;
1084                         }
1085                 }
1086         } else if (!ast_strlen_zero(app)) {
1087                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, NULL);
1088         } else {
1089                 if (exten && context && pi)
1090                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, NULL);
1091                 else {
1092                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1093                         return 0;
1094                 }
1095         }   
1096         if (!res)
1097                 astman_send_ack(s, m, "Originate successfully queued");
1098         else
1099                 astman_send_error(s, m, "Originate failed");
1100         return 0;
1101 }
1102
1103 /*!     \brief Help text for manager command mailboxstatus
1104  */
1105 static char mandescr_mailboxstatus[] = 
1106 "Description: Checks a voicemail account for status.\n"
1107 "Variables: (Names marked with * are required)\n"
1108 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1109 "       ActionID: Optional ActionID for message matching.\n"
1110 "Returns number of messages.\n"
1111 "       Message: Mailbox Status\n"
1112 "       Mailbox: <mailboxid>\n"
1113 "       Waiting: <count>\n"
1114 "\n";
1115
1116 static int action_mailboxstatus(struct mansession *s, struct message *m)
1117 {
1118         char *mailbox = astman_get_header(m, "Mailbox");
1119         char *id = astman_get_header(m,"ActionID");
1120         char idText[256] = "";
1121         int ret;
1122         if (ast_strlen_zero(mailbox)) {
1123                 astman_send_error(s, m, "Mailbox not specified");
1124                 return 0;
1125         }
1126         if (!ast_strlen_zero(id))
1127                 snprintf(idText,256,"ActionID: %s\r\n",id);
1128         ret = ast_app_has_voicemail(mailbox, NULL);
1129         ast_cli(s->fd, "Response: Success\r\n"
1130                                    "%s"
1131                                    "Message: Mailbox Status\r\n"
1132                                    "Mailbox: %s\r\n"
1133                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1134         return 0;
1135 }
1136
1137 static char mandescr_mailboxcount[] = 
1138 "Description: Checks a voicemail account for new messages.\n"
1139 "Variables: (Names marked with * are required)\n"
1140 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1141 "       ActionID: Optional ActionID for message matching.\n"
1142 "Returns number of new and old messages.\n"
1143 "       Message: Mailbox Message Count\n"
1144 "       Mailbox: <mailboxid>\n"
1145 "       NewMessages: <count>\n"
1146 "       OldMessages: <count>\n"
1147 "\n";
1148 static int action_mailboxcount(struct mansession *s, struct message *m)
1149 {
1150         char *mailbox = astman_get_header(m, "Mailbox");
1151         char *id = astman_get_header(m,"ActionID");
1152         char idText[256] = "";
1153         int newmsgs = 0, oldmsgs = 0;
1154         if (ast_strlen_zero(mailbox)) {
1155                 astman_send_error(s, m, "Mailbox not specified");
1156                 return 0;
1157         }
1158         ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
1159         if (!ast_strlen_zero(id)) {
1160                 snprintf(idText,256,"ActionID: %s\r\n",id);
1161         }
1162         ast_cli(s->fd, "Response: Success\r\n"
1163                                    "%s"
1164                                    "Message: Mailbox Message Count\r\n"
1165                                    "Mailbox: %s\r\n"
1166                                    "NewMessages: %d\r\n"
1167                                    "OldMessages: %d\r\n" 
1168                                    "\r\n",
1169                                     idText,mailbox, newmsgs, oldmsgs);
1170         return 0;
1171 }
1172
1173 static char mandescr_extensionstate[] = 
1174 "Description: Report the extension state for given extension.\n"
1175 "  If the extension has a hint, will use devicestate to check\n"
1176 "  the status of the device connected to the extension.\n"
1177 "Variables: (Names marked with * are required)\n"
1178 "       *Exten: Extension to check state on\n"
1179 "       *Context: Context for extension\n"
1180 "       ActionId: Optional ID for this transaction\n"
1181 "Will return an \"Extension Status\" message.\n"
1182 "The response will include the hint for the extension and the status.\n";
1183
1184 static int action_extensionstate(struct mansession *s, struct message *m)
1185 {
1186         char *exten = astman_get_header(m, "Exten");
1187         char *context = astman_get_header(m, "Context");
1188         char *id = astman_get_header(m,"ActionID");
1189         char idText[256] = "";
1190         char hint[256] = "";
1191         int status;
1192         if (ast_strlen_zero(exten)) {
1193                 astman_send_error(s, m, "Extension not specified");
1194                 return 0;
1195         }
1196         if (ast_strlen_zero(context))
1197                 context = "default";
1198         status = ast_extension_state(NULL, context, exten);
1199         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1200         if (!ast_strlen_zero(id)) {
1201                 snprintf(idText,256,"ActionID: %s\r\n",id);
1202         }
1203         ast_cli(s->fd, "Response: Success\r\n"
1204                                    "%s"
1205                                    "Message: Extension Status\r\n"
1206                                    "Exten: %s\r\n"
1207                                    "Context: %s\r\n"
1208                                    "Hint: %s\r\n"
1209                                    "Status: %d\r\n\r\n",
1210                                    idText,exten, context, hint, status);
1211         return 0;
1212 }
1213
1214 static char mandescr_timeout[] = 
1215 "Description: Hangup a channel after a certain time.\n"
1216 "Variables: (Names marked with * are required)\n"
1217 "       *Channel: Channel name to hangup\n"
1218 "       *Timeout: Maximum duration of the call (sec)\n"
1219 "Acknowledges set time with 'Timeout Set' message\n";
1220
1221 static int action_timeout(struct mansession *s, struct message *m)
1222 {
1223         struct ast_channel *c = NULL;
1224         char *name = astman_get_header(m, "Channel");
1225         int timeout = atoi(astman_get_header(m, "Timeout"));
1226         if (ast_strlen_zero(name)) {
1227                 astman_send_error(s, m, "No channel specified");
1228                 return 0;
1229         }
1230         if (!timeout) {
1231                 astman_send_error(s, m, "No timeout specified");
1232                 return 0;
1233         }
1234         c = ast_get_channel_by_name_locked(name);
1235         if (!c) {
1236                 astman_send_error(s, m, "No such channel");
1237                 return 0;
1238         }
1239         ast_channel_setwhentohangup(c, timeout);
1240         ast_mutex_unlock(&c->lock);
1241         astman_send_ack(s, m, "Timeout Set");
1242         return 0;
1243 }
1244
1245 static int process_message(struct mansession *s, struct message *m)
1246 {
1247         char action[80] = "";
1248         struct manager_action *tmp = first_action;
1249         char *id = astman_get_header(m,"ActionID");
1250         char idText[256] = "";
1251         char iabuf[INET_ADDRSTRLEN];
1252
1253         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1254         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1255
1256         if (ast_strlen_zero(action)) {
1257                 astman_send_error(s, m, "Missing action in request");
1258                 return 0;
1259         }
1260         if (!ast_strlen_zero(id)) {
1261                 snprintf(idText,256,"ActionID: %s\r\n",id);
1262         }
1263         if (!s->authenticated) {
1264                 if (!strcasecmp(action, "Challenge")) {
1265                         char *authtype;
1266                         authtype = astman_get_header(m, "AuthType");
1267                         if (!strcasecmp(authtype, "MD5")) {
1268                                 if (ast_strlen_zero(s->challenge))
1269                                         snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
1270                                 ast_mutex_lock(&s->__lock);
1271                                 ast_cli(s->fd, "Response: Success\r\n"
1272                                                 "%s"
1273                                                 "Challenge: %s\r\n\r\n",
1274                                                 idText,s->challenge);
1275                                 ast_mutex_unlock(&s->__lock);
1276                                 return 0;
1277                         } else {
1278                                 astman_send_error(s, m, "Must specify AuthType");
1279                                 return 0;
1280                         }
1281                 } else if (!strcasecmp(action, "Login")) {
1282                         if (authenticate(s, m)) {
1283                                 sleep(1);
1284                                 astman_send_error(s, m, "Authentication failed");
1285                                 return -1;
1286                         } else {
1287                                 s->authenticated = 1;
1288                                 if (option_verbose > 1) {
1289                                         if ( displayconnects ) {
1290                                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1291                                         }
1292                                 }
1293                                 ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1294                                 astman_send_ack(s, m, "Authentication accepted");
1295                         }
1296                 } else if (!strcasecmp(action, "Logoff")) {
1297                         astman_send_ack(s, m, "See ya");
1298                         return -1;
1299                 } else
1300                         astman_send_error(s, m, "Authentication Required");
1301         } else {
1302                 int ret=0;
1303                 struct eventqent *eqe;
1304                 ast_mutex_lock(&s->__lock);
1305                 s->busy = 1;
1306                 ast_mutex_unlock(&s->__lock);
1307                 while( tmp ) {          
1308                         if (!strcasecmp(action, tmp->action)) {
1309                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1310                                         if (tmp->func(s, m))
1311                                                 ret = -1;
1312                                 } else {
1313                                         astman_send_error(s, m, "Permission denied");
1314                                 }
1315                                 break;
1316                         }
1317                         tmp = tmp->next;
1318                 }
1319                 if (!tmp)
1320                         astman_send_error(s, m, "Invalid/unknown command");
1321                 ast_mutex_lock(&s->__lock);
1322                 s->busy = 0;
1323                 while(s->eventq) {
1324                         if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
1325                                 ret = -1;
1326                                 break;
1327                         }
1328                         eqe = s->eventq;
1329                         s->eventq = s->eventq->next;
1330                         free(eqe);
1331                 }
1332                 ast_mutex_unlock(&s->__lock);
1333                 return ret;
1334         }
1335         return 0;
1336 }
1337
1338 static int get_input(struct mansession *s, char *output)
1339 {
1340         /* output must have at least sizeof(s->inbuf) space */
1341         int res;
1342         int x;
1343         struct pollfd fds[1];
1344         char iabuf[INET_ADDRSTRLEN];
1345         for (x=1;x<s->inlen;x++) {
1346                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1347                         /* Copy output data up to and including \r\n */
1348                         memcpy(output, s->inbuf, x + 1);
1349                         /* Add trailing \0 */
1350                         output[x+1] = '\0';
1351                         /* Move remaining data back to the front */
1352                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1353                         s->inlen -= (x + 1);
1354                         return 1;
1355                 }
1356         } 
1357         if (s->inlen >= sizeof(s->inbuf) - 1) {
1358                 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);
1359                 s->inlen = 0;
1360         }
1361         fds[0].fd = s->fd;
1362         fds[0].events = POLLIN;
1363         do {
1364                 res = poll(fds, 1, -1);
1365                 if (res < 0) {
1366                         if (errno == EINTR) {
1367                                 if (s->dead)
1368                                         return -1;
1369                                 continue;
1370                         }
1371                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1372                         return -1;
1373                 } else if (res > 0) {
1374                         ast_mutex_lock(&s->__lock);
1375                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1376                         ast_mutex_unlock(&s->__lock);
1377                         if (res < 1)
1378                                 return -1;
1379                         break;
1380                 }
1381         } while(1);
1382         s->inlen += res;
1383         s->inbuf[s->inlen] = '\0';
1384         return 0;
1385 }
1386
1387 static void *session_do(void *data)
1388 {
1389         struct mansession *s = data;
1390         struct message m;
1391         char iabuf[INET_ADDRSTRLEN];
1392         int res;
1393         
1394         ast_mutex_lock(&s->__lock);
1395         ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
1396         ast_mutex_unlock(&s->__lock);
1397         memset(&m, 0, sizeof(m));
1398         for (;;) {
1399                 res = get_input(s, m.headers[m.hdrcount]);
1400                 if (res > 0) {
1401                         /* Strip trailing \r\n */
1402                         if (strlen(m.headers[m.hdrcount]) < 2)
1403                                 continue;
1404                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1405                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
1406                                 if (process_message(s, &m))
1407                                         break;
1408                                 memset(&m, 0, sizeof(m));
1409                         } else if (m.hdrcount < MAX_HEADERS - 1)
1410                                 m.hdrcount++;
1411                 } else if (res < 0)
1412                         break;
1413         }
1414         if (s->authenticated) {
1415                 if (option_verbose > 1) {
1416                         if (displayconnects) 
1417                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
1418                 }
1419                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1420         } else {
1421                 if (option_verbose > 1) {
1422                         if ( displayconnects )
1423                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1424                 }
1425                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1426         }
1427         destroy_session(s);
1428         return NULL;
1429 }
1430
1431 static void *accept_thread(void *ignore)
1432 {
1433         int as;
1434         struct sockaddr_in sin;
1435         socklen_t sinlen;
1436         struct mansession *s;
1437         struct protoent *p;
1438         int arg = 1;
1439         int flags;
1440         pthread_attr_t attr;
1441
1442         pthread_attr_init(&attr);
1443         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1444
1445         for (;;) {
1446                 sinlen = sizeof(sin);
1447                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1448                 if (as < 0) {
1449                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1450                         continue;
1451                 }
1452                 p = getprotobyname("tcp");
1453                 if (p) {
1454                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1455                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1456                         }
1457                 }
1458                 s = malloc(sizeof(struct mansession));
1459                 if (!s) {
1460                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1461                         continue;
1462                 } 
1463                 memset(s, 0, sizeof(struct mansession));
1464                 memcpy(&s->sin, &sin, sizeof(sin));
1465                 s->writetimeout = 100;
1466
1467                 if(! block_sockets) {
1468                         /* For safety, make sure socket is non-blocking */
1469                         flags = fcntl(as, F_GETFL);
1470                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
1471                 }
1472                 ast_mutex_init(&s->__lock);
1473                 s->fd = as;
1474                 s->send_events = -1;
1475                 ast_mutex_lock(&sessionlock);
1476                 s->next = sessions;
1477                 sessions = s;
1478                 ast_mutex_unlock(&sessionlock);
1479                 if (ast_pthread_create(&s->t, &attr, session_do, s))
1480                         destroy_session(s);
1481         }
1482         pthread_attr_destroy(&attr);
1483         return NULL;
1484 }
1485
1486 static int append_event(struct mansession *s, const char *str)
1487 {
1488         struct eventqent *tmp, *prev=NULL;
1489         tmp = malloc(sizeof(struct eventqent) + strlen(str));
1490         if (tmp) {
1491                 tmp->next = NULL;
1492                 strcpy(tmp->eventdata, str);
1493                 if (s->eventq) {
1494                         prev = s->eventq;
1495                         while(prev->next) 
1496                                 prev = prev->next;
1497                         prev->next = tmp;
1498                 } else {
1499                         s->eventq = tmp;
1500                 }
1501                 return 0;
1502         }
1503         return -1;
1504 }
1505
1506 /*! \brief  manager_event: Send AMI event to client */
1507 int manager_event(int category, char *event, char *fmt, ...)
1508 {
1509         struct mansession *s;
1510         char auth[80];
1511         char tmp[4096] = "";
1512         char *tmp_next = tmp;
1513         size_t tmp_left = sizeof(tmp) - 2;
1514         va_list ap;
1515
1516         ast_mutex_lock(&sessionlock);
1517         for (s = sessions; s; s = s->next) {
1518                 if ((s->readperm & category) != category)
1519                         continue;
1520
1521                 if ((s->send_events & category) != category)
1522                         continue;
1523
1524                 if (ast_strlen_zero(tmp)) {
1525                         struct timeval now;
1526
1527                         ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1528                                          event, authority_to_str(category, auth, sizeof(auth)));
1529                         if (timestampevents) {
1530                                 now = ast_tvnow();
1531                                 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1532                                                  now.tv_sec, now.tv_usec);
1533                         }
1534                         va_start(ap, fmt);
1535                         ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1536                         va_end(ap);
1537                         *tmp_next++ = '\r';
1538                         *tmp_next++ = '\n';
1539                         *tmp_next = '\0';
1540                 }
1541
1542                 ast_mutex_lock(&s->__lock);
1543                 if (s->busy) {
1544                         append_event(s, tmp);
1545                 } else if (!s->dead) {
1546                         if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
1547                                 ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
1548                                 s->dead = 1;
1549                                 pthread_kill(s->t, SIGURG);
1550                         }
1551                 }
1552                 ast_mutex_unlock(&s->__lock);
1553         }
1554         ast_mutex_unlock(&sessionlock);
1555
1556         return 0;
1557 }
1558
1559 int ast_manager_unregister( char *action ) 
1560 {
1561         struct manager_action *cur = first_action, *prev = first_action;
1562
1563         ast_mutex_lock(&actionlock);
1564         while( cur ) {          
1565                 if (!strcasecmp(action, cur->action)) {
1566                         prev->next = cur->next;
1567                         free(cur);
1568                         if (option_verbose > 1) 
1569                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
1570                         ast_mutex_unlock(&actionlock);
1571                         return 0;
1572                 }
1573                 prev = cur;
1574                 cur = cur->next;
1575         }
1576         ast_mutex_unlock(&actionlock);
1577         return 0;
1578 }
1579
1580 static int manager_state_cb(char *context, char *exten, int state, void *data)
1581 {
1582         /* Notify managers of change */
1583         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
1584         return 0;
1585 }
1586
1587 static int ast_manager_register_struct(struct manager_action *act)
1588 {
1589         struct manager_action *cur = first_action, *prev = NULL;
1590         int ret;
1591
1592         ast_mutex_lock(&actionlock);
1593         while(cur) { /* Walk the list of actions */
1594                 ret = strcasecmp(cur->action, act->action);
1595                 if (ret == 0) {
1596                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
1597                         ast_mutex_unlock(&actionlock);
1598                         return -1;
1599                 } else if (ret > 0) {
1600                         /* Insert these alphabetically */
1601                         if (prev) {
1602                                 act->next = prev->next;
1603                                 prev->next = act;
1604                         } else {
1605                                 act->next = first_action;
1606                                 first_action = act;
1607                         }
1608                         break;
1609                 }
1610                 prev = cur; 
1611                 cur = cur->next;
1612         }
1613         
1614         if (!cur) {
1615                 if (prev)
1616                         prev->next = act;
1617                 else
1618                         first_action = act;
1619                 act->next = NULL;
1620         }
1621
1622         if (option_verbose > 1) 
1623                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
1624         ast_mutex_unlock(&actionlock);
1625         return 0;
1626 }
1627
1628 /*! \brief register a new command with manager, including online help. This is 
1629         the preferred way to register a manager command */
1630 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
1631 {
1632         struct manager_action *cur;
1633
1634         cur = malloc(sizeof(struct manager_action));
1635         if (!cur) {
1636                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
1637                 ast_mutex_unlock(&actionlock);
1638                 return -1;
1639         }
1640         cur->action = action;
1641         cur->authority = auth;
1642         cur->func = func;
1643         cur->synopsis = synopsis;
1644         cur->description = description;
1645         cur->next = NULL;
1646
1647         ast_manager_register_struct(cur);
1648
1649         return 0;
1650 }
1651 /*! @}
1652  END Doxygen group */
1653
1654 static int registered = 0;
1655
1656 int init_manager(void)
1657 {
1658         struct ast_config *cfg;
1659         char *val;
1660         int oldportno = portno;
1661         static struct sockaddr_in ba;
1662         int x = 1;
1663         if (!registered) {
1664                 /* Register default actions */
1665                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
1666                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
1667                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
1668                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
1669                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
1670                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
1671                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
1672                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
1673                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
1674                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
1675                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
1676                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
1677                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
1678                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
1679                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
1680
1681                 ast_cli_register(&show_mancmd_cli);
1682                 ast_cli_register(&show_mancmds_cli);
1683                 ast_cli_register(&show_manconn_cli);
1684                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
1685                 registered = 1;
1686         }
1687         portno = DEFAULT_MANAGER_PORT;
1688         displayconnects = 1;
1689         cfg = ast_config_load("manager.conf");
1690         if (!cfg) {
1691                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
1692                 return 0;
1693         }
1694         memset(&ba, 0, sizeof(ba));
1695         val = ast_variable_retrieve(cfg, "general", "enabled");
1696         if (val)
1697                 enabled = ast_true(val);
1698
1699         val = ast_variable_retrieve(cfg, "general", "block-sockets");
1700         if(val)
1701                 block_sockets = ast_true(val);
1702
1703         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
1704                 if (sscanf(val, "%d", &portno) != 1) {
1705                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
1706                         portno = DEFAULT_MANAGER_PORT;
1707                 }
1708         } else if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
1709                 if (sscanf(val, "%d", &portno) != 1) {
1710                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
1711                         portno = DEFAULT_MANAGER_PORT;
1712                 }
1713                 ast_log(LOG_NOTICE, "Use of portno in manager.conf deprecated.  Please use 'port=%s' instead.\n", val);
1714         }
1715
1716         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
1717                 displayconnects = ast_true(val);
1718
1719         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
1720                 timestampevents = ast_true(val);
1721         
1722         ba.sin_family = AF_INET;
1723         ba.sin_port = htons(portno);
1724         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1725         
1726         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
1727                 if (!inet_aton(val, &ba.sin_addr)) { 
1728                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
1729                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1730                 }
1731         }
1732         
1733         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
1734 #if 0
1735                 /* Can't be done yet */
1736                 close(asock);
1737                 asock = -1;
1738 #else
1739                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
1740 #endif
1741         }
1742         ast_config_destroy(cfg);
1743         
1744         /* If not enabled, do nothing */
1745         if (!enabled) {
1746                 return 0;
1747         }
1748         if (asock < 0) {
1749                 asock = socket(AF_INET, SOCK_STREAM, 0);
1750                 if (asock < 0) {
1751                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
1752                         return -1;
1753                 }
1754                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
1755                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
1756                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
1757                         close(asock);
1758                         asock = -1;
1759                         return -1;
1760                 }
1761                 if (listen(asock, 2)) {
1762                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
1763                         close(asock);
1764                         asock = -1;
1765                         return -1;
1766                 }
1767                 if (option_verbose)
1768                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
1769                 ast_pthread_create(&t, NULL, accept_thread, NULL);
1770         }
1771         return 0;
1772 }
1773
1774 int reload_manager(void)
1775 {
1776         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
1777         return init_manager();
1778 }