2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
25 #include "asterisk/autoconfig.h"
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
42 #include "asterisk/md5.h"
43 #include "asterisk/manager.h"
44 #include "asterisk/linkedlists.h"
48 #define MAX_HEADERS 80
52 * 2005.05.27 - different versions of newt define the type of the buffer
53 * for the 5th argument to newtEntry() as char ** or const char ** . To
54 * let the code compile cleanly with -Werror, we cast it to void * through
57 #define _NEWT_CAST (void *)
59 static struct ast_mansession {
60 struct sockaddr_in sin;
73 AST_LIST_ENTRY(ast_chan) list;
76 static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
78 /* dummy functions to be compatible with the Asterisk core for md5.c */
79 void ast_register_file_version(const char *file, const char *version);
80 void ast_register_file_version(const char *file, const char *version)
84 void ast_unregister_file_version(const char *file);
85 void ast_unregister_file_version(const char *file)
89 int ast_add_profile(const char *, uint64_t scale);
90 int ast_add_profile(const char *s, uint64_t scale)
95 int64_t ast_profile(int, int64_t);
96 int64_t ast_profile(int key, int64_t val)
100 int64_t ast_mark(int, int start1_stop0);
101 int64_t ast_mark(int key, int start1_stop0)
106 /* end of dummy functions */
108 static struct ast_chan *find_chan(char *name)
110 struct ast_chan *chan;
111 AST_LIST_TRAVERSE(&chans, chan, list) {
112 if (!strcmp(name, chan->name))
115 chan = malloc(sizeof(struct ast_chan));
117 memset(chan, 0, sizeof(struct ast_chan));
118 strncpy(chan->name, name, sizeof(chan->name) - 1);
119 AST_LIST_INSERT_TAIL(&chans, chan, list);
124 static void del_chan(char *name)
126 struct ast_chan *chan;
127 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
128 if (!strcmp(name, chan->name)) {
129 AST_LIST_REMOVE_CURRENT(&chans, list);
134 AST_LIST_TRAVERSE_SAFE_END
137 static void fdprintf(int fd, char *fmt, ...)
142 vsnprintf(stuff, sizeof(stuff), fmt, ap);
144 write(fd, stuff, strlen(stuff));
147 static char *get_header(struct message *m, char *var)
151 snprintf(cmp, sizeof(cmp), "%s: ", var);
152 for (x=0;x<m->hdrcount;x++)
153 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
154 return m->headers[x] + strlen(cmp);
158 static int event_newstate(struct ast_mansession *s, struct message *m)
160 struct ast_chan *chan;
161 chan = find_chan(get_header(m, "Channel"));
162 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
166 static int event_newexten(struct ast_mansession *s, struct message *m)
168 struct ast_chan *chan;
169 chan = find_chan(get_header(m, "Channel"));
170 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
171 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
172 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
176 static int event_newchannel(struct ast_mansession *s, struct message *m)
178 struct ast_chan *chan;
179 chan = find_chan(get_header(m, "Channel"));
180 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
181 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
185 static int event_status(struct ast_mansession *s, struct message *m)
187 struct ast_chan *chan;
188 chan = find_chan(get_header(m, "Channel"));
189 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
190 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
191 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
192 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
193 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
197 static int event_hangup(struct ast_mansession *s, struct message *m)
199 del_chan(get_header(m, "Channel"));
203 static int event_ignore(struct ast_mansession *s, struct message *m)
208 static int event_rename(struct ast_mansession *s, struct message *m)
210 struct ast_chan *chan;
211 chan = find_chan(get_header(m, "Oldname"));
212 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
215 static struct event {
217 int (*func)(struct ast_mansession *s, struct message *m);
219 { "Newstate", event_newstate },
220 { "Newchannel", event_newchannel },
221 { "Newexten", event_newexten },
222 { "Hangup", event_hangup },
223 { "Rename", event_rename },
224 { "Status", event_status },
225 { "Link", event_ignore },
226 { "Unlink", event_ignore },
227 { "StatusComplete", event_ignore }
230 static int process_message(struct ast_mansession *s, struct message *m)
234 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
235 if (!strlen(event)) {
236 fprintf(stderr, "Missing event in request");
239 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
240 if (!strcasecmp(event, events[x].event)) {
241 if (events[x].func(s, m))
246 if (x >= sizeof(events) / sizeof(events[0]))
247 fprintf(stderr, "Ignoring unknown event '%s'", event);
249 for (x=0;x<m->hdrcount;x++) {
250 printf("Header: %s\n", m->headers[x]);
256 static void rebuild_channels(newtComponent c)
259 struct ast_chan *chan;
263 prev = newtListboxGetCurrent(c);
265 AST_LIST_TRAVERSE(&chans, chan, list) {
266 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
267 if (strlen(chan->exten))
268 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
270 chan->exten, chan->context, chan->priority);
272 snprintf(tmp, sizeof(tmp), "%-30s %8s",
274 newtListboxAppendEntry(c, tmp, chan);
278 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
279 newtListboxSetCurrentByKey(c, prev);
282 static int has_input(struct ast_mansession *s)
285 for (x=1;x<s->inlen;x++)
286 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
291 static int get_input(struct ast_mansession *s, char *output)
293 /* output must have at least sizeof(s->inbuf) space */
296 struct timeval tv = {0, 0};
298 for (x=1;x<s->inlen;x++) {
299 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
300 /* Copy output data up to and including \r\n */
301 memcpy(output, s->inbuf, x + 1);
302 /* Add trailing \0 */
304 /* Move remaining data back to the front */
305 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
310 if (s->inlen >= sizeof(s->inbuf) - 1) {
311 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
316 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
318 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
319 } else if (res > 0) {
320 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
324 s->inbuf[s->inlen] = '\0';
331 static int input_check(struct ast_mansession *s, struct message **mout)
333 static struct message m;
340 res = get_input(s, m.headers[m.hdrcount]);
343 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
346 /* Strip trailing \r\n */
347 if (strlen(m.headers[m.hdrcount]) < 2)
349 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
350 if (!strlen(m.headers[m.hdrcount])) {
351 if (mout && strlen(get_header(&m, "Response"))) {
355 if (process_message(s, &m))
357 memset(&m, 0, sizeof(&m));
358 } else if (m.hdrcount < MAX_HEADERS - 1)
360 } else if (res < 0) {
368 static struct message *wait_for_response(int timeout)
375 tv.tv_sec = timeout / 1000;
376 tv.tv_usec = (timeout % 1000) * 1000;
377 FD_SET(session.fd, &fds);
378 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
381 if (input_check(&session, &m) < 0) {
390 static int manager_action(char *action, char *fmt, ...)
392 struct ast_mansession *s;
397 fdprintf(s->fd, "Action: %s\r\n", action);
399 vsnprintf(tmp, sizeof(tmp), fmt, ap);
401 write(s->fd, tmp, strlen(tmp));
402 fdprintf(s->fd, "\r\n");
406 static int show_message(char *title, char *msg)
411 struct newtExitStruct es;
413 newtCenteredWindow(60,7, title);
415 label = newtLabel(4,1,msg);
416 ok = newtButton(27, 3, "OK");
417 form = newtForm(NULL, NULL, 0);
418 newtFormAddComponents(form, label, ok, NULL);
419 newtFormRun(form, &es);
421 newtFormDestroy(form);
425 static newtComponent showform;
426 static int show_doing(char *title, char *tmp)
428 struct newtExitStruct es;
430 showform = newtForm(NULL, NULL, 0);
431 newtCenteredWindow(70,4, title);
432 label = newtLabel(3,1,tmp);
433 newtFormAddComponents(showform,label, NULL);
434 newtFormSetTimer(showform, 200);
435 newtFormRun(showform, &es);
439 static int hide_doing(void)
442 newtFormDestroy(showform);
446 static void try_status(void)
449 manager_action("Status", "");
450 m = wait_for_response(10000);
452 show_message("Status Failed", "Timeout waiting for response");
453 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
454 show_message("Status Failed Failed", get_header(m, "Message"));
459 static void try_hangup(newtComponent c)
461 struct ast_chan *chan;
464 chan = newtListboxGetCurrent(c);
466 manager_action("Hangup", "Channel: %s\r\n", chan->name);
467 m = wait_for_response(10000);
469 show_message("Hangup Failed", "Timeout waiting for response");
470 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
471 show_message("Hangup Failed", get_header(m, "Message"));
477 static int get_user_input(char *msg, char *buf, int buflen)
481 newtComponent cancel;
482 newtComponent inpfield;
485 struct newtExitStruct es;
487 newtCenteredWindow(60,7, msg);
489 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
490 ok = newtButton(22, 3, "OK");
491 cancel = newtButton(32, 3, "Cancel");
492 form = newtForm(NULL, NULL, 0);
493 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
494 newtFormRun(form, &es);
495 strncpy(buf, input, buflen - 1);
501 newtFormDestroy(form);
505 static void try_redirect(newtComponent c)
507 struct ast_chan *chan;
514 chan = newtListboxGetCurrent(c);
516 strncpy(channame, chan->name, sizeof(channame) - 1);
517 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
518 if (get_user_input(tmp, dest, sizeof(dest)))
520 if ((context = strchr(dest, '@'))) {
523 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
525 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
527 m = wait_for_response(10000);
529 show_message("Hangup Failed", "Timeout waiting for response");
530 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
531 show_message("Hangup Failed", get_header(m, "Message"));
537 static int manage_calls(char *host)
541 newtComponent hangup;
542 newtComponent redirect;
543 newtComponent channels;
544 struct newtExitStruct es;
547 /* Mark: If there's one thing you learn from this code, it is this...
548 Never, ever fly Air France. Their customer service is absolutely
549 the worst. I've never heard the words "That's not my problem" as
550 many times as I have from their staff -- It should, without doubt
551 be their corporate motto if it isn't already. Don't bother giving
552 them business because you're just a pain in their side and they
553 will be sure to let you know the first time you speak to them.
555 If you ever want to make me happy just tell me that you, too, will
556 never fly Air France again either (in spite of their excellent
559 Update by oej: The merger with KLM has transferred this
560 behaviour to KLM as well.
561 Don't bother giving them business either...
563 Only if you want to travel randomly without luggage, you
564 might pick either of them.
567 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
568 newtCenteredWindow(74, 20, tmp);
569 form = newtForm(NULL, NULL, 0);
570 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
571 newtFormSetTimer(form, 100);
572 quit = newtButton(62, 16, "Quit");
573 redirect = newtButton(35, 16, "Redirect");
574 hangup = newtButton(50, 16, "Hangup");
575 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
576 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
577 newtListboxSetWidth(channels, 72);
579 show_doing("Getting Status", "Retrieving system status...");
584 newtFormRun(form, &es);
585 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
586 if (input_check(&session, NULL)) {
587 show_message("Disconnected", "Disconnected from remote host");
590 } else if (es.reason == NEWT_EXIT_COMPONENT) {
593 if (es.u.co == hangup) {
594 try_hangup(channels);
595 } else if (es.u.co == redirect) {
596 try_redirect(channels);
599 rebuild_channels(channels);
601 newtFormDestroy(form);
605 static int login(char *hostname)
608 newtComponent cancel;
610 newtComponent username;
611 newtComponent password;
613 newtComponent ulabel;
614 newtComponent plabel;
618 struct newtExitStruct es;
623 session.fd = socket(AF_INET, SOCK_STREAM, 0);
624 if (session.fd < 0) {
625 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
626 show_message("Socket failed", tmp);
630 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
631 show_doing("Connecting....", tmp);
634 hp = gethostbyname(hostname);
636 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
637 show_message("Host lookup failed", tmp);
641 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
642 show_doing("Connecting...", tmp);
644 session.sin.sin_family = AF_INET;
645 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
646 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
648 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
649 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
650 show_message("Connect Failed", tmp);
656 login = newtButton(5, 6, "Login");
657 cancel = newtButton(25, 6, "Cancel");
658 newtCenteredWindow(40, 10, "Asterisk Manager Login");
659 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
660 label = newtLabel(4,1, tmp);
662 ulabel = newtLabel(4,2,"Username:");
663 plabel = newtLabel(4,3,"Password:");
665 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
666 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
668 form = newtForm(NULL, NULL, 0);
669 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
670 newtFormRun(form, &es);
671 if (es.reason == NEWT_EXIT_COMPONENT) {
672 if (es.u.co == login) {
673 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
674 show_doing("Logging in", tmp);
675 /* Check to see if the remote host supports MD5 Authentication */
676 manager_action("Challenge", "AuthType: MD5\r\n");
677 m = wait_for_response(10000);
678 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
679 char *challenge = get_header(m, "Challenge");
682 char md5key[256] = "";
683 struct MD5Context md5;
684 unsigned char digest[16];
686 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
687 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
688 MD5Final(digest, &md5);
690 len += sprintf(md5key + len, "%2.2x", digest[x]);
691 manager_action("Login",
696 m = wait_for_response(10000);
698 if (!strcasecmp(get_header(m, "Response"), "Success")) {
701 show_message("Login Failed", get_header(m, "Message"));
704 memset(m, 0, sizeof(m));
705 manager_action("Login",
709 m = wait_for_response(10000);
712 if (!strcasecmp(get_header(m, "Response"), "Success")) {
715 show_message("Login Failed", get_header(m, "Message"));
721 newtFormDestroy(form);
725 int main(int argc, char *argv[])
728 fprintf(stderr, "Usage: astman <host>\n");
733 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
734 newtPushHelpLine("Welcome to the Asterisk Manager!");
735 if (login(argv[1])) {
739 manage_calls(argv[1]);