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