Change select references to poll in core asterisk functions (hope this works)
[asterisk/asterisk.git] / manager.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Channel Management and more
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include <string.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <netdb.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <arpa/inet.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/poll.h>
29 #include <asterisk/channel.h>
30 #include <asterisk/file.h>
31 #include <asterisk/manager.h>
32 #include <asterisk/config.h>
33 #include <asterisk/lock.h>
34 #include <asterisk/logger.h>
35 #include <asterisk/options.h>
36 #include <asterisk/cli.h>
37 #include <asterisk/app.h>
38 #include <asterisk/pbx.h>
39 #include <asterisk/md5.h>
40 #include <asterisk/acl.h>
41
42 static int enabled = 0;
43 static int portno = DEFAULT_MANAGER_PORT;
44 static int asock = -1;
45 static pthread_t t;
46 static ast_mutex_t sessionlock = AST_MUTEX_INITIALIZER;
47 static int block_sockets = 0;
48
49 static struct permalias {
50         int num;
51         char *label;
52 } perms[] = {
53         { EVENT_FLAG_SYSTEM, "system" },
54         { EVENT_FLAG_CALL, "call" },
55         { EVENT_FLAG_LOG, "log" },
56         { EVENT_FLAG_VERBOSE, "verbose" },
57         { EVENT_FLAG_COMMAND, "command" },
58         { EVENT_FLAG_AGENT, "agent" },
59         { EVENT_FLAG_USER, "user" },
60         { -1, "all" },
61 };
62
63 static struct mansession *sessions = NULL;
64 static struct manager_action *first_action = NULL;
65 static ast_mutex_t actionlock = AST_MUTEX_INITIALIZER;
66
67 int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
68 {
69         /* Try to write string, but wait no more than ms milliseconds
70            before timing out */
71         int res=0;
72         struct pollfd fds[1];
73         while(len) {
74                 res = write(fd, s, len);
75                 if ((res < 0) && (errno != EAGAIN)) {
76                         return -1;
77                 }
78                 if (res < 0) res = 0;
79                 len -= res;
80                 s += res;
81                 fds[0].fd = fd;
82                 fds[0].events = POLLOUT;
83                 /* Wait until writable again */
84                 res = poll(fds, 1, timeoutms);
85                 if (res < 1)
86                         return -1;
87         }
88         return res;
89 }
90
91 static int handle_showmancmds(int fd, int argc, char *argv[])
92 {
93         struct manager_action *cur = first_action;
94         char *format = "  %-15.15s  %-45.45s\n";
95
96         ast_mutex_lock(&actionlock);
97         ast_cli(fd, format, "Action", "Synopsis");
98         while(cur) { /* Walk the list of actions */
99                 ast_cli(fd, format, cur->action, cur->synopsis);
100                 cur = cur->next;
101         }
102
103         ast_mutex_unlock(&actionlock);
104         return RESULT_SUCCESS;
105 }
106
107 static int handle_showmanconn(int fd, int argc, char *argv[])
108 {
109         struct mansession *s;
110         char *format = "  %-15.15s  %-15.15s\n";
111         ast_mutex_lock(&sessionlock);
112         s = sessions;
113         ast_cli(fd, format, "Username", "IP Address");
114         while(s) {
115                 ast_cli(fd, format,s->username, inet_ntoa(s->sin.sin_addr));
116                 s = s->next;
117         }
118
119         ast_mutex_unlock(&sessionlock);
120         return RESULT_SUCCESS;
121 }
122
123 static char showmancmds_help[] = 
124 "Usage: show manager commands\n"
125 "       Prints a listing of all the available manager commands.\n";
126
127 static char showmanconn_help[] = 
128 "Usage: show manager connected\n"
129 "       Prints a listing of the users that are connected to the\n"
130 "manager interface.\n";
131
132 static struct ast_cli_entry show_mancmds_cli =
133         { { "show", "manager", "commands", NULL },
134         handle_showmancmds, "Show manager commands", showmancmds_help };
135
136 static struct ast_cli_entry show_manconn_cli =
137         { { "show", "manager", "connected", NULL },
138         handle_showmanconn, "Show connected manager users", showmanconn_help };
139
140 static void destroy_session(struct mansession *s)
141 {
142         struct mansession *cur, *prev = NULL;
143         ast_mutex_lock(&sessionlock);
144         cur = sessions;
145         while(cur) {
146                 if (cur == s)
147                         break;
148                 prev = cur;
149                 cur = cur->next;
150         }
151         if (cur) {
152                 if (prev)
153                         prev->next = cur->next;
154                 else
155                         sessions = cur->next;
156                 if (s->fd > -1)
157                         close(s->fd);
158                 free(s);
159         } else
160                 ast_log(LOG_WARNING, "Trying to delete non-existant session %p?\n", s);
161         ast_mutex_unlock(&sessionlock);
162         
163 }
164
165 char *astman_get_header(struct message *m, char *var)
166 {
167         char cmp[80];
168         int x;
169         snprintf(cmp, sizeof(cmp), "%s: ", var);
170         for (x=0;x<m->hdrcount;x++)
171                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
172                         return m->headers[x] + strlen(cmp);
173         return "";
174 }
175
176 void astman_send_error(struct mansession *s, struct message *m, char *error)
177 {
178         char *id = astman_get_header(m,"ActionID");
179         ast_mutex_lock(&s->lock);
180         ast_cli(s->fd, "Response: Error\r\n");
181         if (id && strlen(id))
182                 ast_cli(s->fd, "ActionID: %s\r\n",id);
183         ast_cli(s->fd, "Message: %s\r\n\r\n", error);
184         ast_mutex_unlock(&s->lock);
185 }
186
187 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
188 {
189         char *id = astman_get_header(m,"ActionID");
190         ast_mutex_lock(&s->lock);
191         ast_cli(s->fd, "Response: %s\r\n", resp);
192         if (id && strlen(id))
193                 ast_cli(s->fd, "ActionID: %s\r\n",id);
194         if (msg)
195                 ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
196         else
197                 ast_cli(s->fd, "\r\n");
198         ast_mutex_unlock(&s->lock);
199 }
200
201 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
202 {
203         astman_send_response(s, m, "Success", msg);
204 }
205
206 static int get_perm(char *instr)
207 {
208         char tmp[256];
209         char *c;
210         int x;
211         int ret = 0;
212         char *stringp=NULL;
213         if (!instr)
214                 return 0;
215         strncpy(tmp, instr, sizeof(tmp) - 1);
216         stringp=tmp;
217         c = strsep(&stringp, ",");
218         while(c) {
219                 for (x=0;x<sizeof(perms) / sizeof(perms[0]);x++) {
220                         if (!strcasecmp(perms[x].label, c)) 
221                                 ret |= perms[x].num;
222                 }
223                 c = strsep(&stringp, ",");
224         }
225         return ret;
226 }
227
228 static int set_eventmask(struct mansession *s, char *eventmask)
229 {
230         if (!eventmask)
231                 return -1;
232         if (!strcasecmp(eventmask, "on") || ast_true(eventmask)) {
233                 ast_mutex_lock(&s->lock);
234                 s->send_events = 1;
235                 ast_mutex_unlock(&s->lock);
236                 return 1;
237         } else if (!strcasecmp(eventmask, "off") || ast_false(eventmask)) {
238                 ast_mutex_lock(&s->lock);
239                 s->send_events = 0;
240                 ast_mutex_unlock(&s->lock);
241                 return 0;
242         }
243         return -1;
244 }
245
246 static int authenticate(struct mansession *s, struct message *m)
247 {
248         struct ast_config *cfg;
249         char *cat;
250         char *user = astman_get_header(m, "Username");
251         char *pass = astman_get_header(m, "Secret");
252         char *authtype = astman_get_header(m, "AuthType");
253         char *key = astman_get_header(m, "Key");
254         char *events = astman_get_header(m, "Events");
255         
256         cfg = ast_load("manager.conf");
257         if (!cfg)
258                 return -1;
259         cat = ast_category_browse(cfg, NULL);
260         while(cat) {
261                 if (strcasecmp(cat, "general")) {
262                         /* This is a user */
263                         if (!strcasecmp(cat, user)) {
264                                 struct ast_variable *v;
265                                 struct ast_ha *ha = NULL;
266                                 char *password = NULL;
267                                 v = ast_variable_browse(cfg, cat);
268                                 while (v) {
269                                         if (!strcasecmp(v->name, "secret")) {
270                                                 password = v->value;
271                                         } else if (!strcasecmp(v->name, "permit") ||
272                                                    !strcasecmp(v->name, "deny")) {
273                                                         ha = ast_append_ha(v->name, v->value, ha);
274                                         }                                               
275                                         v = v->next;
276                                 }
277                                 if (ha && !ast_apply_ha(ha, &(s->sin))) {
278                                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", inet_ntoa(s->sin.sin_addr), user);
279                                         ast_free_ha(ha);
280                                         ast_destroy(cfg);
281                                         return -1;
282                                 } else if (ha)
283                                         ast_free_ha(ha);
284                                 if (!strcasecmp(authtype, "MD5")) {
285                                         if (key && strlen(key) && s->challenge) {
286                                                 int x;
287                                                 int len=0;
288                                                 char md5key[256] = "";
289                                                 struct MD5Context md5;
290                                                 unsigned char digest[16];
291                                                 MD5Init(&md5);
292                                                 MD5Update(&md5, s->challenge, strlen(s->challenge));
293                                                 MD5Update(&md5, password, strlen(password));
294                                                 MD5Final(digest, &md5);
295                                                 for (x=0;x<16;x++)
296                                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
297                                                 if (!strcmp(md5key, key))
298                                                         break;
299                                                 else {
300                                                         ast_destroy(cfg);
301                                                         return -1;
302                                                 }
303                                         }
304                                 } else if (password && !strcasecmp(password, pass)) {
305                                         break;
306                                 } else {
307                                         ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", inet_ntoa(s->sin.sin_addr), user);
308                                         ast_destroy(cfg);
309                                         return -1;
310                                 }       
311                         }
312                 }
313                 cat = ast_category_browse(cfg, cat);
314         }
315         if (cat) {
316                 strncpy(s->username, cat, sizeof(s->username) - 1);
317                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
318                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
319                 ast_destroy(cfg);
320                 if (events)
321                         set_eventmask(s, events);
322                 return 0;
323         }
324         ast_log(LOG_NOTICE, "%s tried to authenticate with non-existant user '%s'\n", inet_ntoa(s->sin.sin_addr), user);
325         ast_destroy(cfg);
326         return -1;
327 }
328
329 static int action_ping(struct mansession *s, struct message *m)
330 {
331         astman_send_response(s, m, "Pong", NULL);
332         return 0;
333 }
334
335 static int action_events(struct mansession *s, struct message *m)
336 {
337         char *mask = astman_get_header(m, "EventMask");
338         int res;
339
340         res = set_eventmask(s, mask);
341         if (res > 0)
342                 astman_send_response(s, m, "Events On", NULL);
343         else if (res == 0)
344                 astman_send_response(s, m, "Events Off", NULL);
345         else
346                 astman_send_response(s, m, "EventMask parse error", NULL);
347         return 0;
348 }
349
350 static int action_logoff(struct mansession *s, struct message *m)
351 {
352         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
353         return -1;
354 }
355
356 static int action_hangup(struct mansession *s, struct message *m)
357 {
358         struct ast_channel *c = NULL;
359         char *name = astman_get_header(m, "Channel");
360         if (!strlen(name)) {
361                 astman_send_error(s, m, "No channel specified");
362                 return 0;
363         }
364         c = ast_channel_walk(NULL);
365         while(c) {
366                 if (!strcasecmp(c->name, name)) {
367                         break;
368                 }
369                 c = ast_channel_walk(c);
370         }
371         if (!c) {
372                 astman_send_error(s, m, "No such channel");
373                 return 0;
374         }
375         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
376         astman_send_ack(s, m, "Channel Hungup");
377         return 0;
378 }
379
380 static int action_status(struct mansession *s, struct message *m)
381 {
382         char *id = astman_get_header(m,"ActionID");
383         char idText[256] = "";
384         struct ast_channel *c;
385         char bridge[256];
386         astman_send_ack(s, m, "Channel status will follow");
387         c = ast_channel_walk(NULL);
388         if (id && strlen(id))
389                 snprintf(idText,256,"ActionID: %s\r\n",id);
390         while(c) {
391                 if (c->bridge)
392                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->bridge->name);
393                 else
394                         strcpy(bridge, "");
395                 if (c->pbx) {
396                         ast_cli(s->fd,
397                         "Event: Status\r\n"
398                         "Channel: %s\r\n"
399                         "CallerID: %s\r\n"
400                         "State: %s\r\n"
401                         "Context: %s\r\n"
402                         "Extension: %s\r\n"
403                         "Priority: %d\r\n"
404                         "%s"
405                         "Uniqueid: %s\r\n"
406                         "%s"
407                         "\r\n",
408                         c->name, c->callerid ? c->callerid : "<unknown>", 
409                         ast_state2str(c->_state), c->context,
410                         c->exten, c->priority, bridge, c->uniqueid, idText);
411                 } else {
412                         ast_cli(s->fd,
413                         "Event: Status\r\n"
414                         "Channel: %s\r\n"
415                         "CallerID: %s\r\n"
416                         "State: %s\r\n"
417                         "%s"
418                         "Uniqueid: %s\r\n"
419                         "%s"
420                         "\r\n",
421                         c->name, c->callerid ? c->callerid : "<unknown>", 
422                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
423                 }
424                 c = ast_channel_walk(c);
425         }
426         ast_cli(s->fd,
427         "Event: StatusComplete\r\n"
428         "%s"
429         "\r\n",idText);
430         return 0;
431 }
432
433 static int action_redirect(struct mansession *s, struct message *m)
434 {
435         char *name = astman_get_header(m, "Channel");
436         char *name2 = astman_get_header(m, "ExtraChannel");
437         char *exten = astman_get_header(m, "Exten");
438         char *context = astman_get_header(m, "Context");
439         char *priority = astman_get_header(m, "Priority");
440         int pi = 0;
441         int res;
442         if (!name || !strlen(name)) {
443                 astman_send_error(s, m, "Channel not specified");
444                 return 0;
445         }
446         if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
447                 astman_send_error(s, m, "Invalid priority\n");
448                 return 0;
449         }
450         res = ast_async_goto_by_name(name, context, exten, pi);
451         if (!res) {
452                 if (strlen(name2)) {
453                         res = ast_async_goto_by_name(name2, context, exten, pi);
454                         if (!res)
455                                 astman_send_ack(s, m, "Dual Redirect successful");
456                         else
457                                 astman_send_error(s, m, "Secondary redirect failed");
458                 } else
459                         astman_send_ack(s, m, "Redirect successful");
460         } else
461                 astman_send_error(s, m, "Redirect failed");
462         return 0;
463 }
464
465 static int action_command(struct mansession *s, struct message *m)
466 {
467         char *cmd = astman_get_header(m, "Command");
468         ast_mutex_lock(&s->lock);
469         s->blocking = 1;
470         ast_mutex_unlock(&s->lock);
471         ast_cli(s->fd, "Response: Follows\r\n");
472         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
473         ast_cli_command(s->fd, cmd);
474         ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
475         ast_mutex_lock(&s->lock);
476         s->blocking = 0;
477         ast_mutex_unlock(&s->lock);
478         return 0;
479 }
480
481 static int action_originate(struct mansession *s, struct message *m)
482 {
483         char *name = astman_get_header(m, "Channel");
484         char *exten = astman_get_header(m, "Exten");
485         char *context = astman_get_header(m, "Context");
486         char *priority = astman_get_header(m, "Priority");
487         char *timeout = astman_get_header(m, "Timeout");
488         char *callerid = astman_get_header(m, "CallerID");
489         char *variable = astman_get_header(m, "Variable");
490         char *account = astman_get_header(m, "Account");
491         char *app = astman_get_header(m, "Application");
492         char *appdata = astman_get_header(m, "Data");
493         char *tech, *data;
494         int pi = 0;
495         int res;
496         int to = 30000;
497         int reason = 0;
498         char tmp[256];
499         if (!name) {
500                 astman_send_error(s, m, "Channel not specified");
501                 return 0;
502         }
503         if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
504                 astman_send_error(s, m, "Invalid priority\n");
505                 return 0;
506         }
507         if (strlen(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
508                 astman_send_error(s, m, "Invalid timeout\n");
509                 return 0;
510         }
511         strncpy(tmp, name, sizeof(tmp) - 1);
512         tech = tmp;
513         data = strchr(tmp, '/');
514         if (!data) {
515                 astman_send_error(s, m, "Invalid channel\n");
516                 return 0;
517         }
518         *data = '\0';
519         data++;
520         if (strlen(app)) {
521                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 0, strlen(callerid) ? callerid : NULL, variable, account);
522         } else {
523                 if (exten && context && pi)
524                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 0, strlen(callerid) ? callerid : NULL, variable, account);
525                 else {
526                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
527                         return 0;
528                 }
529         }   
530         if (!res)
531                 astman_send_ack(s, m, "Originate successfully queued");
532         else
533                 astman_send_error(s, m, "Originate failed");
534         return 0;
535 }
536
537 static int action_mailboxstatus(struct mansession *s, struct message *m)
538 {
539         char *mailbox = astman_get_header(m, "Mailbox");
540         char *id = astman_get_header(m,"ActionID");
541         char idText[256] = "";
542         if (!mailbox || !strlen(mailbox)) {
543                 astman_send_error(s, m, "Mailbox not specified");
544                 return 0;
545         }
546         if (id && strlen(id))
547                 snprintf(idText,256,"ActionID: %s\r\n",id);
548         ast_cli(s->fd, "Response: Success\r\n"
549                                    "%s"
550                                    "Message: Mailbox Status\r\n"
551                                    "Mailbox: %s\r\n"
552                                    "Waiting: %d\r\n\r\n", idText, mailbox, ast_app_has_voicemail(mailbox));
553         return 0;
554 }
555
556 static int action_mailboxcount(struct mansession *s, struct message *m)
557 {
558         char *mailbox = astman_get_header(m, "Mailbox");
559         char *id = astman_get_header(m,"ActionID");
560         char idText[256] = "";
561         int newmsgs = 0, oldmsgs = 0;
562         if (!mailbox || !strlen(mailbox)) {
563                 astman_send_error(s, m, "Mailbox not specified");
564                 return 0;
565         }
566         ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
567         if (id && strlen(id)) {
568                 snprintf(idText,256,"ActionID: %s\r\n",id);
569         }
570         ast_cli(s->fd, "Response: Success\r\n"
571                                    "%s"
572                                    "Message: Mailbox Message Count\r\n"
573                                    "Mailbox: %s\r\n"
574                                    "NewMessages: %d\r\n"
575                                    "OldMessages: %d\r\n" 
576                                    "\r\n",
577                                     idText,mailbox, newmsgs, oldmsgs);
578         return 0;
579 }
580
581 static int action_extensionstate(struct mansession *s, struct message *m)
582 {
583         char *exten = astman_get_header(m, "Exten");
584         char *context = astman_get_header(m, "Context");
585         char *id = astman_get_header(m,"ActionID");
586         char idText[256] = "";
587         char hint[256] = "";
588         int status;
589         if (!exten || !strlen(exten)) {
590                 astman_send_error(s, m, "Extension not specified");
591                 return 0;
592         }
593         if (!context || !strlen(context))
594                 context = "default";
595         status = ast_extension_state(NULL, context, exten);
596         ast_get_hint(hint, sizeof(hint) - 1, NULL, context, exten);
597         if (id && strlen(id)) {
598                 snprintf(idText,256,"ActionID: %s\r\n",id);
599         }
600         ast_cli(s->fd, "Response: Success\r\n"
601                                    "%s"
602                                    "Message: Extension Status\r\n"
603                                    "Exten: %s\r\n"
604                                    "Context: %s\r\n"
605                                    "Hint: %s\r\n"
606                                    "Status: %d\r\n\r\n",
607                                    idText,exten, context, hint, status);
608         return 0;
609 }
610
611 static int action_timeout(struct mansession *s, struct message *m)
612 {
613         struct ast_channel *c = NULL;
614         char *name = astman_get_header(m, "Channel");
615         int timeout = atoi(astman_get_header(m, "Timeout"));
616         if (!strlen(name)) {
617                 astman_send_error(s, m, "No channel specified");
618                 return 0;
619         }
620         if (!timeout) {
621                 astman_send_error(s, m, "No timeout specified");
622                 return 0;
623         }
624         c = ast_channel_walk(NULL);
625         while(c) {
626                 if (!strcasecmp(c->name, name)) {
627                         break;
628                 }
629                 c = ast_channel_walk(c);
630         }
631         if (!c) {
632                 astman_send_error(s, m, "No such channel");
633                 return 0;
634         }
635         ast_channel_setwhentohangup(c, timeout);
636         astman_send_ack(s, m, "Timeout Set");
637         return 0;
638 }
639
640 static int process_message(struct mansession *s, struct message *m)
641 {
642         char action[80];
643         struct manager_action *tmp = first_action;
644         char *id = astman_get_header(m,"ActionID");
645         char idText[256] = "";
646
647         strncpy(action, astman_get_header(m, "Action"), sizeof(action));
648         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
649
650         if (!strlen(action)) {
651                 astman_send_error(s, m, "Missing action in request");
652                 return 0;
653         }
654         if (id && strlen(id)) {
655                 snprintf(idText,256,"ActionID: %s\r\n",id);
656         }
657         if (!s->authenticated) {
658                 if (!strcasecmp(action, "Challenge")) {
659                         char *authtype;
660                         authtype = astman_get_header(m, "AuthType");
661                         if (!strcasecmp(authtype, "MD5")) {
662                                 if (!s->challenge || !strlen(s->challenge)) {
663                                         ast_mutex_lock(&s->lock);
664                                         snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
665                                         ast_mutex_unlock(&s->lock);
666                                 }
667                                 ast_cli(s->fd, "Response: Success\r\n"
668                                                 "%s"
669                                                 "Challenge: %s\r\n\r\n",
670                                                 idText,s->challenge);
671                                 return 0;
672                         } else {
673                                 astman_send_error(s, m, "Must specify AuthType");
674                                 return 0;
675                         }
676                 } else if (!strcasecmp(action, "Login")) {
677                         if (authenticate(s, m)) {
678                                 sleep(1);
679                                 astman_send_error(s, m, "Authentication failed");
680                                 return -1;
681                         } else {
682                                 s->authenticated = 1;
683                                 if (option_verbose > 1) 
684                                         ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
685                                 ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
686                                 astman_send_ack(s, m, "Authentication accepted");
687                         }
688                 } else if (!strcasecmp(action, "Logoff")) {
689                         astman_send_ack(s, m, "See ya");
690                         return -1;
691                 } else
692                         astman_send_error(s, m, "Authentication Required");
693         } else {
694                 while( tmp ) {          
695                         if (!strcasecmp(action, tmp->action)) {
696                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
697                                         if (tmp->func(s, m))
698                                                 return -1;
699                                 } else {
700                                         astman_send_error(s, m, "Permission denied");
701                                 }
702                                 return 0;
703                         }
704                         tmp = tmp->next;
705                 }
706                 astman_send_error(s, m, "Invalid/unknown command");
707         }
708         return 0;
709 }
710
711 static int get_input(struct mansession *s, char *output)
712 {
713         /* output must have at least sizeof(s->inbuf) space */
714         int res;
715         int x;
716         struct pollfd fds[1];
717         for (x=1;x<s->inlen;x++) {
718                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
719                         /* Copy output data up to and including \r\n */
720                         memcpy(output, s->inbuf, x + 1);
721                         /* Add trailing \0 */
722                         output[x+1] = '\0';
723                         /* Move remaining data back to the front */
724                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
725                         s->inlen -= (x + 1);
726                         return 1;
727                 }
728         } 
729         if (s->inlen >= sizeof(s->inbuf) - 1) {
730                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
731                 s->inlen = 0;
732         }
733         fds[0].fd = s->fd;
734         fds[0].events = POLLIN;
735         res = poll(fds, 1, -1);
736         if (res < 0) {
737                 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
738         } else if (res > 0) {
739                 ast_mutex_lock(&s->lock);
740                 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
741                 ast_mutex_unlock(&s->lock);
742                 if (res < 1)
743                         return -1;
744         }
745         s->inlen += res;
746         s->inbuf[s->inlen] = '\0';
747         return 0;
748 }
749
750 static void *session_do(void *data)
751 {
752         struct mansession *s = data;
753         struct message m;
754         int res;
755         
756         ast_mutex_lock(&s->lock);
757         ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
758         ast_mutex_unlock(&s->lock);
759         memset(&m, 0, sizeof(&m));
760         for (;;) {
761                 res = get_input(s, m.headers[m.hdrcount]);
762                 if (res > 0) {
763                         /* Strip trailing \r\n */
764                         if (strlen(m.headers[m.hdrcount]) < 2)
765                                 continue;
766                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
767                         if (!strlen(m.headers[m.hdrcount])) {
768                                 if (process_message(s, &m))
769                                         break;
770                                 memset(&m, 0, sizeof(&m));
771                         } else if (m.hdrcount < MAX_HEADERS - 1)
772                                 m.hdrcount++;
773                 } else if (res < 0)
774                         break;
775         }
776         if (s->authenticated) {
777                 if (option_verbose > 1) 
778                         ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
779                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
780         } else {
781                 if (option_verbose > 1)
782                         ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", inet_ntoa(s->sin.sin_addr));
783                 ast_log(LOG_EVENT, "Failed attempt from %s\n", inet_ntoa(s->sin.sin_addr));
784         }
785         destroy_session(s);
786         return NULL;
787 }
788
789 static void *accept_thread(void *ignore)
790 {
791         int as;
792         struct sockaddr_in sin;
793         int sinlen;
794         struct mansession *s;
795         struct protoent *p;
796         int arg = 1;
797         int flags;
798         pthread_attr_t attr;
799
800         pthread_attr_init(&attr);
801         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
802
803         for (;;) {
804                 sinlen = sizeof(sin);
805                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
806                 if (as < 0) {
807                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
808                         continue;
809                 }
810                 p = getprotobyname("tcp");
811                 if( p ) {
812                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
813                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
814                         }
815                 }
816                 s = malloc(sizeof(struct mansession));
817                 if (!s) {
818                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
819                         continue;
820                 } 
821                 memset(s, 0, sizeof(struct mansession));
822                 memcpy(&s->sin, &sin, sizeof(sin));
823
824                 if(! block_sockets) {
825                         /* For safety, make sure socket is non-blocking */
826                         flags = fcntl(as, F_GETFL);
827                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
828                 }
829                 ast_mutex_init(&s->lock);
830                 s->fd = as;
831                 s->send_events = 1;
832                 ast_mutex_lock(&sessionlock);
833                 s->next = sessions;
834                 sessions = s;
835                 ast_mutex_unlock(&sessionlock);
836                 if (pthread_create(&t, &attr, session_do, s))
837                         destroy_session(s);
838         }
839         pthread_attr_destroy(&attr);
840         return NULL;
841 }
842
843 int manager_event(int category, char *event, char *fmt, ...)
844 {
845         struct mansession *s;
846         char tmp[4096];
847         va_list ap;
848
849         ast_mutex_lock(&sessionlock);
850         s = sessions;
851         while(s) {
852                 if (((s->readperm & category) == category) && s->send_events) {
853                         ast_mutex_lock(&s->lock);
854                         if (!s->blocking) {
855                                 ast_cli(s->fd, "Event: %s\r\n", event);
856                                 va_start(ap, fmt);
857                                 vsnprintf(tmp, sizeof(tmp), fmt, ap);
858                                 va_end(ap);
859                                 ast_carefulwrite(s->fd,tmp,strlen(tmp),100);
860                                 ast_cli(s->fd, "\r\n");
861                         }
862                         ast_mutex_unlock(&s->lock);
863                 }
864                 s = s->next;
865         }
866         ast_mutex_unlock(&sessionlock);
867         return 0;
868 }
869
870 int ast_manager_unregister( char *action ) {
871         struct manager_action *cur = first_action, *prev = first_action;
872
873         ast_mutex_lock(&actionlock);
874         while( cur ) {          
875                 if (!strcasecmp(action, cur->action)) {
876                         prev->next = cur->next;
877                         free(cur);
878                         if (option_verbose > 1) 
879                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
880                         ast_mutex_unlock(&actionlock);
881                         return 0;
882                 }
883                 prev = cur;
884                 cur = cur->next;
885         }
886         ast_mutex_unlock(&actionlock);
887         return 0;
888 }
889
890 static int manager_state_cb(char *context, char *exten, int state, void *data)
891 {
892         /* Notify managers of change */
893         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
894         return 0;
895 }
896
897 int ast_manager_register( char *action, int auth, 
898         int (*func)(struct mansession *s, struct message *m), char *synopsis)
899 {
900         struct manager_action *cur = first_action, *prev = NULL;
901
902         ast_mutex_lock(&actionlock);
903         while(cur) { /* Walk the list of actions */
904                 if (!strcasecmp(cur->action, action)) {
905                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", action);
906                         ast_mutex_unlock(&actionlock);
907                         return -1;
908                 }
909                 prev = cur; 
910                 cur = cur->next;
911         }
912         cur = malloc( sizeof(struct manager_action) );
913         if( !cur ) {
914                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
915                 ast_mutex_unlock(&actionlock);
916                 return -1;
917         }
918         strncpy( cur->action, action, 255 );
919         cur->authority = auth;
920         cur->func = func;
921         cur->synopsis = synopsis;
922         cur->next = NULL;
923
924         if( prev ) prev->next = cur;
925         else first_action = cur;
926
927         if (option_verbose > 1) 
928                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", action);
929         ast_mutex_unlock(&actionlock);
930         return 0;
931 }
932
933 static int registered = 0;
934
935 int init_manager(void)
936 {
937         struct ast_config *cfg;
938         char *val;
939         int oldportno = portno;
940         static struct sockaddr_in ba;
941         int x = 1;
942         if (!registered) {
943                 /* Register default actions */
944                 ast_manager_register( "Ping", 0, action_ping, "Ping" );
945                 ast_manager_register( "Events", 0, action_events, "Contol Event Flow" );
946                 ast_manager_register( "Logoff", 0, action_logoff, "Logoff Manager" );
947                 ast_manager_register( "Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel" );
948                 ast_manager_register( "Status", EVENT_FLAG_CALL, action_status, "Status" );
949                 ast_manager_register( "Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect" );
950                 ast_manager_register( "Originate", EVENT_FLAG_CALL, action_originate, "Originate Call" );
951                 ast_manager_register( "MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox" );
952                 ast_manager_register( "Command", EVENT_FLAG_COMMAND, action_command, "Execute Command" );
953                 ast_manager_register( "ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status" );
954                 ast_manager_register( "AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout" );
955                 ast_manager_register( "MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count" );
956
957                 ast_cli_register(&show_mancmds_cli);
958                 ast_cli_register(&show_manconn_cli);
959                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
960                 registered = 1;
961         }
962         portno = DEFAULT_MANAGER_PORT;
963         cfg = ast_load("manager.conf");
964         if (!cfg) {
965                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
966                 return 0;
967         }
968         memset(&ba, 0, sizeof(ba));
969         val = ast_variable_retrieve(cfg, "general", "enabled");
970         if (val)
971                 enabled = ast_true(val);
972
973         val = ast_variable_retrieve(cfg, "general", "block-sockets");
974         if(val)
975                 block_sockets = ast_true(val);
976
977         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
978                 if (sscanf(val, "%d", &portno) != 1) {
979                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
980                         portno = DEFAULT_MANAGER_PORT;
981                 }
982         } else if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
983                 if (sscanf(val, "%d", &portno) != 1) {
984                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
985                         portno = DEFAULT_MANAGER_PORT;
986                 }
987                 ast_log(LOG_NOTICE, "Use of portno in manager.conf deprecated.  Please use 'port=%s' instead.\n", val);
988         }
989         
990         ba.sin_family = AF_INET;
991         ba.sin_port = htons(portno);
992         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
993         
994         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
995                 if (!inet_aton(val, &ba.sin_addr)) { 
996                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
997                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
998                 }
999         }
1000         
1001         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
1002 #if 0
1003                 /* Can't be done yet */
1004                 close(asock);
1005                 asock = -1;
1006 #else
1007                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
1008 #endif
1009         }
1010         ast_destroy(cfg);
1011         
1012         /* If not enabled, do nothing */
1013         if (!enabled) {
1014                 return 0;
1015         }
1016         if (asock < 0) {
1017                 asock = socket(AF_INET, SOCK_STREAM, 0);
1018                 if (asock < 0) {
1019                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
1020                         return -1;
1021                 }
1022                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
1023                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
1024                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
1025                         close(asock);
1026                         asock = -1;
1027                         return -1;
1028                 }
1029                 if (listen(asock, 2)) {
1030                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
1031                         close(asock);
1032                         asock = -1;
1033                         return -1;
1034                 }
1035                 if (option_verbose)
1036                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
1037                 pthread_create(&t, NULL, accept_thread, NULL);
1038         }
1039         return 0;
1040 }
1041
1042 int reload_manager(void)
1043 {
1044         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
1045         return init_manager();
1046 }