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