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.
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <sys/socket.h>
32 #include <sys/select.h>
40 #include "asterisk/md5.h"
41 #include "asterisk/manager.h"
42 #include "asterisk/linkedlists.h"
46 #define MAX_HEADERS 80
50 * 2005.05.27 - different versions of newt define the type of the buffer
51 * for the 5th argument to newtEntry() as char ** or const char ** . To
52 * let the code compile cleanly with -Werror, we cast it to void * through
55 #define _NEWT_CAST (void *)
57 static struct ast_mansession {
58 struct sockaddr_in sin;
71 AST_LIST_ENTRY(ast_chan) list;
74 static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
76 /* dummy functions to be compatible with the Asterisk core for md5.c */
77 void ast_register_file_version(const char *file, const char *version);
78 void ast_register_file_version(const char *file, const char *version)
82 void ast_unregister_file_version(const char *file);
83 void ast_unregister_file_version(const char *file)
87 int ast_add_profile(const char *, uint64_t scale);
88 int ast_add_profile(const char *s, uint64_t scale)
93 int64_t ast_profile(int, int64_t);
94 int64_t ast_profile(int key, int64_t val)
98 int64_t ast_mark(int, int start1_stop0);
99 int64_t ast_mark(int key, int start1_stop0)
104 /* end of dummy functions */
106 static struct ast_chan *find_chan(char *name)
108 struct ast_chan *chan;
109 AST_LIST_TRAVERSE(&chans, chan, list) {
110 if (!strcmp(name, chan->name))
113 chan = malloc(sizeof(struct ast_chan));
115 memset(chan, 0, sizeof(struct ast_chan));
116 strncpy(chan->name, name, sizeof(chan->name) - 1);
117 AST_LIST_INSERT_TAIL(&chans, chan, list);
122 static void del_chan(char *name)
124 struct ast_chan *chan;
125 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
126 if (!strcmp(name, chan->name)) {
127 AST_LIST_REMOVE_CURRENT(&chans, list);
132 AST_LIST_TRAVERSE_SAFE_END
135 static void fdprintf(int fd, char *fmt, ...)
140 vsnprintf(stuff, sizeof(stuff), fmt, ap);
142 write(fd, stuff, strlen(stuff));
145 static char *get_header(struct message *m, char *var)
149 snprintf(cmp, sizeof(cmp), "%s: ", var);
150 for (x=0;x<m->hdrcount;x++)
151 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
152 return m->headers[x] + strlen(cmp);
156 static int event_newstate(struct ast_mansession *s, struct message *m)
158 struct ast_chan *chan;
159 chan = find_chan(get_header(m, "Channel"));
160 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
164 static int event_newexten(struct ast_mansession *s, struct message *m)
166 struct ast_chan *chan;
167 chan = find_chan(get_header(m, "Channel"));
168 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
169 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
170 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
174 static int event_newchannel(struct ast_mansession *s, struct message *m)
176 struct ast_chan *chan;
177 chan = find_chan(get_header(m, "Channel"));
178 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
179 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
183 static int event_status(struct ast_mansession *s, struct message *m)
185 struct ast_chan *chan;
186 chan = find_chan(get_header(m, "Channel"));
187 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
188 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
189 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
190 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
191 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
195 static int event_hangup(struct ast_mansession *s, struct message *m)
197 del_chan(get_header(m, "Channel"));
201 static int event_ignore(struct ast_mansession *s, struct message *m)
206 static int event_rename(struct ast_mansession *s, struct message *m)
208 struct ast_chan *chan;
209 chan = find_chan(get_header(m, "Oldname"));
210 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
213 static struct event {
215 int (*func)(struct ast_mansession *s, struct message *m);
217 { "Newstate", event_newstate },
218 { "Newchannel", event_newchannel },
219 { "Newexten", event_newexten },
220 { "Hangup", event_hangup },
221 { "Rename", event_rename },
222 { "Status", event_status },
223 { "Link", event_ignore },
224 { "Unlink", event_ignore },
225 { "StatusComplete", event_ignore }
228 static int process_message(struct ast_mansession *s, struct message *m)
232 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
233 if (!strlen(event)) {
234 fprintf(stderr, "Missing event in request");
237 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
238 if (!strcasecmp(event, events[x].event)) {
239 if (events[x].func(s, m))
244 if (x >= sizeof(events) / sizeof(events[0]))
245 fprintf(stderr, "Ignoring unknown event '%s'", event);
247 for (x=0;x<m->hdrcount;x++) {
248 printf("Header: %s\n", m->headers[x]);
254 static void rebuild_channels(newtComponent c)
257 struct ast_chan *chan;
261 prev = newtListboxGetCurrent(c);
263 AST_LIST_TRAVERSE(&chans, chan, list) {
264 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
265 if (strlen(chan->exten))
266 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
268 chan->exten, chan->context, chan->priority);
270 snprintf(tmp, sizeof(tmp), "%-30s %8s",
272 newtListboxAppendEntry(c, tmp, chan);
276 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
277 newtListboxSetCurrentByKey(c, prev);
280 static int has_input(struct ast_mansession *s)
283 for (x=1;x<s->inlen;x++)
284 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
289 static int get_input(struct ast_mansession *s, char *output)
291 /* output must have at least sizeof(s->inbuf) space */
294 struct timeval tv = {0, 0};
296 for (x=1;x<s->inlen;x++) {
297 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
298 /* Copy output data up to and including \r\n */
299 memcpy(output, s->inbuf, x + 1);
300 /* Add trailing \0 */
302 /* Move remaining data back to the front */
303 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
308 if (s->inlen >= sizeof(s->inbuf) - 1) {
309 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
314 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
316 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
317 } else if (res > 0) {
318 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
322 s->inbuf[s->inlen] = '\0';
329 static int input_check(struct ast_mansession *s, struct message **mout)
331 static struct message m;
338 res = get_input(s, m.headers[m.hdrcount]);
341 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
344 /* Strip trailing \r\n */
345 if (strlen(m.headers[m.hdrcount]) < 2)
347 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
348 if (!strlen(m.headers[m.hdrcount])) {
349 if (mout && strlen(get_header(&m, "Response"))) {
353 if (process_message(s, &m))
355 memset(&m, 0, sizeof(&m));
356 } else if (m.hdrcount < MAX_HEADERS - 1)
358 } else if (res < 0) {
366 static struct message *wait_for_response(int timeout)
373 tv.tv_sec = timeout / 1000;
374 tv.tv_usec = (timeout % 1000) * 1000;
375 FD_SET(session.fd, &fds);
376 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
379 if (input_check(&session, &m) < 0) {
388 static int manager_action(char *action, char *fmt, ...)
390 struct ast_mansession *s;
395 fdprintf(s->fd, "Action: %s\r\n", action);
397 vsnprintf(tmp, sizeof(tmp), fmt, ap);
399 write(s->fd, tmp, strlen(tmp));
400 fdprintf(s->fd, "\r\n");
404 static int show_message(char *title, char *msg)
409 struct newtExitStruct es;
411 newtCenteredWindow(60,7, title);
413 label = newtLabel(4,1,msg);
414 ok = newtButton(27, 3, "OK");
415 form = newtForm(NULL, NULL, 0);
416 newtFormAddComponents(form, label, ok, NULL);
417 newtFormRun(form, &es);
419 newtFormDestroy(form);
423 static newtComponent showform;
424 static int show_doing(char *title, char *tmp)
426 struct newtExitStruct es;
428 showform = newtForm(NULL, NULL, 0);
429 newtCenteredWindow(70,4, title);
430 label = newtLabel(3,1,tmp);
431 newtFormAddComponents(showform,label, NULL);
432 newtFormSetTimer(showform, 200);
433 newtFormRun(showform, &es);
437 static int hide_doing(void)
440 newtFormDestroy(showform);
444 static void try_status(void)
447 manager_action("Status", "");
448 m = wait_for_response(10000);
450 show_message("Status Failed", "Timeout waiting for response");
451 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
452 show_message("Status Failed Failed", get_header(m, "Message"));
457 static void try_hangup(newtComponent c)
459 struct ast_chan *chan;
462 chan = newtListboxGetCurrent(c);
464 manager_action("Hangup", "Channel: %s\r\n", chan->name);
465 m = wait_for_response(10000);
467 show_message("Hangup Failed", "Timeout waiting for response");
468 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
469 show_message("Hangup Failed", get_header(m, "Message"));
475 static int get_user_input(char *msg, char *buf, int buflen)
479 newtComponent cancel;
480 newtComponent inpfield;
483 struct newtExitStruct es;
485 newtCenteredWindow(60,7, msg);
487 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
488 ok = newtButton(22, 3, "OK");
489 cancel = newtButton(32, 3, "Cancel");
490 form = newtForm(NULL, NULL, 0);
491 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
492 newtFormRun(form, &es);
493 strncpy(buf, input, buflen - 1);
499 newtFormDestroy(form);
503 static void try_redirect(newtComponent c)
505 struct ast_chan *chan;
512 chan = newtListboxGetCurrent(c);
514 strncpy(channame, chan->name, sizeof(channame) - 1);
515 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
516 if (get_user_input(tmp, dest, sizeof(dest)))
518 if ((context = strchr(dest, '@'))) {
521 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
523 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
525 m = wait_for_response(10000);
527 show_message("Hangup Failed", "Timeout waiting for response");
528 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
529 show_message("Hangup Failed", get_header(m, "Message"));
535 static int manage_calls(char *host)
539 newtComponent hangup;
540 newtComponent redirect;
541 newtComponent channels;
542 struct newtExitStruct es;
545 /* Mark: If there's one thing you learn from this code, it is this...
546 Never, ever fly Air France. Their customer service is absolutely
547 the worst. I've never heard the words "That's not my problem" as
548 many times as I have from their staff -- It should, without doubt
549 be their corporate motto if it isn't already. Don't bother giving
550 them business because you're just a pain in their side and they
551 will be sure to let you know the first time you speak to them.
553 If you ever want to make me happy just tell me that you, too, will
554 never fly Air France again either (in spite of their excellent
557 Update by oej: The merger with KLM has transferred this
558 behaviour to KLM as well.
559 Don't bother giving them business either...
561 Only if you want to travel randomly without luggage, you
562 might pick either of them.
565 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
566 newtCenteredWindow(74, 20, tmp);
567 form = newtForm(NULL, NULL, 0);
568 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
569 newtFormSetTimer(form, 100);
570 quit = newtButton(62, 16, "Quit");
571 redirect = newtButton(35, 16, "Redirect");
572 hangup = newtButton(50, 16, "Hangup");
573 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
574 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
575 newtListboxSetWidth(channels, 72);
577 show_doing("Getting Status", "Retrieving system status...");
582 newtFormRun(form, &es);
583 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
584 if (input_check(&session, NULL)) {
585 show_message("Disconnected", "Disconnected from remote host");
588 } else if (es.reason == NEWT_EXIT_COMPONENT) {
591 if (es.u.co == hangup) {
592 try_hangup(channels);
593 } else if (es.u.co == redirect) {
594 try_redirect(channels);
597 rebuild_channels(channels);
599 newtFormDestroy(form);
603 static int login(char *hostname)
606 newtComponent cancel;
608 newtComponent username;
609 newtComponent password;
611 newtComponent ulabel;
612 newtComponent plabel;
616 struct newtExitStruct es;
621 session.fd = socket(AF_INET, SOCK_STREAM, 0);
622 if (session.fd < 0) {
623 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
624 show_message("Socket failed", tmp);
628 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
629 show_doing("Connecting....", tmp);
632 hp = gethostbyname(hostname);
634 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
635 show_message("Host lookup failed", tmp);
639 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
640 show_doing("Connecting...", tmp);
642 session.sin.sin_family = AF_INET;
643 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
644 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
646 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
647 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
648 show_message("Connect Failed", tmp);
654 login = newtButton(5, 6, "Login");
655 cancel = newtButton(25, 6, "Cancel");
656 newtCenteredWindow(40, 10, "Asterisk Manager Login");
657 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
658 label = newtLabel(4,1, tmp);
660 ulabel = newtLabel(4,2,"Username:");
661 plabel = newtLabel(4,3,"Password:");
663 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
664 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
666 form = newtForm(NULL, NULL, 0);
667 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
668 newtFormRun(form, &es);
669 if (es.reason == NEWT_EXIT_COMPONENT) {
670 if (es.u.co == login) {
671 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
672 show_doing("Logging in", tmp);
673 /* Check to see if the remote host supports MD5 Authentication */
674 manager_action("Challenge", "AuthType: MD5\r\n");
675 m = wait_for_response(10000);
676 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
677 char *challenge = get_header(m, "Challenge");
680 char md5key[256] = "";
681 struct MD5Context md5;
682 unsigned char digest[16];
684 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
685 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
686 MD5Final(digest, &md5);
688 len += sprintf(md5key + len, "%2.2x", digest[x]);
689 manager_action("Login",
694 m = wait_for_response(10000);
696 if (!strcasecmp(get_header(m, "Response"), "Success")) {
699 show_message("Login Failed", get_header(m, "Message"));
702 memset(m, 0, sizeof(m));
703 manager_action("Login",
707 m = wait_for_response(10000);
710 if (!strcasecmp(get_header(m, "Response"), "Success")) {
713 show_message("Login Failed", get_header(m, "Message"));
719 newtFormDestroy(form);
723 int main(int argc, char *argv[])
726 fprintf(stderr, "Usage: astman <host>\n");
731 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
732 newtPushHelpLine("Welcome to the Asterisk Manager!");
733 if (login(argv[1])) {
737 manage_calls(argv[1]);