3 * Copyright (C) 2002, Linux Support Services, Inc.
5 * Distributed under the terms of the GNU General Public License
10 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <sys/select.h>
23 #include <asterisk/md5.h>
24 #include <asterisk/manager.h>
26 #define MAX_HEADERS 80
29 static struct ast_mansession {
30 struct sockaddr_in sin;
36 static struct ast_chan {
43 struct ast_chan *next;
46 static struct ast_chan *find_chan(char *name)
48 struct ast_chan *prev = NULL, *chan = chans;
50 if (!strcmp(name, chan->name))
55 chan = malloc(sizeof(struct ast_chan));
57 memset(chan, 0, sizeof(struct ast_chan));
58 strncpy(chan->name, name, sizeof(chan->name) - 1);
67 static void del_chan(char *name)
69 struct ast_chan *prev = NULL, *chan = chans;
71 if (!strcmp(name, chan->name)) {
73 prev->next = chan->next;
83 static void fdprintf(int fd, char *fmt, ...)
88 vsnprintf(stuff, sizeof(stuff), fmt, ap);
90 write(fd, stuff, strlen(stuff));
93 static char *get_header(struct message *m, char *var)
97 snprintf(cmp, sizeof(cmp), "%s: ", var);
98 for (x=0;x<m->hdrcount;x++)
99 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
100 return m->headers[x] + strlen(cmp);
104 static int event_newstate(struct ast_mansession *s, struct message *m)
106 struct ast_chan *chan;
107 chan = find_chan(get_header(m, "Channel"));
108 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
112 static int event_newexten(struct ast_mansession *s, struct message *m)
114 struct ast_chan *chan;
115 chan = find_chan(get_header(m, "Channel"));
116 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
117 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
118 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
122 static int event_newchannel(struct ast_mansession *s, struct message *m)
124 struct ast_chan *chan;
125 chan = find_chan(get_header(m, "Channel"));
126 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
127 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
131 static int event_status(struct ast_mansession *s, struct message *m)
133 struct ast_chan *chan;
134 chan = find_chan(get_header(m, "Channel"));
135 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
136 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
137 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
138 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
139 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
143 static int event_hangup(struct ast_mansession *s, struct message *m)
145 del_chan(get_header(m, "Channel"));
149 static int event_ignore(struct ast_mansession *s, struct message *m)
154 static int event_rename(struct ast_mansession *s, struct message *m)
156 struct ast_chan *chan;
157 chan = find_chan(get_header(m, "Oldname"));
158 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
161 static struct event {
163 int (*func)(struct ast_mansession *s, struct message *m);
165 { "Newstate", event_newstate },
166 { "Newchannel", event_newchannel },
167 { "Newexten", event_newexten },
168 { "Hangup", event_hangup },
169 { "Rename", event_rename },
170 { "Status", event_status },
171 { "Link", event_ignore },
172 { "Unlink", event_ignore },
175 static int process_message(struct ast_mansession *s, struct message *m)
179 strncpy(event, get_header(m, "Event"), sizeof(event));
180 if (!strlen(event)) {
181 fprintf(stderr, "Missing event in request");
184 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
185 if (!strcasecmp(event, events[x].event)) {
186 if (events[x].func(s, m))
191 if (x >= sizeof(events) / sizeof(events[0]))
192 fprintf(stderr, "Ignoring unknown event '%s'", event);
194 for (x=0;x<m->hdrcount;x++) {
195 printf("Header: %s\n", m->headers[x]);
201 static void rebuild_channels(newtComponent c)
204 struct ast_chan *chan;
208 prev = newtListboxGetCurrent(c);
212 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
213 if (strlen(chan->exten))
214 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
216 chan->exten, chan->context, chan->priority);
218 snprintf(tmp, sizeof(tmp), "%-30s %8s",
220 newtListboxAppendEntry(c, tmp, chan);
225 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
226 newtListboxSetCurrentByKey(c, prev);
229 static int has_input(struct ast_mansession *s)
232 for (x=1;x<s->inlen;x++)
233 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
238 static int get_input(struct ast_mansession *s, char *output)
240 /* output must have at least sizeof(s->inbuf) space */
243 struct timeval tv = {0, 0};
245 for (x=1;x<s->inlen;x++) {
246 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
247 /* Copy output data up to and including \r\n */
248 memcpy(output, s->inbuf, x + 1);
249 /* Add trailing \0 */
251 /* Move remaining data back to the front */
252 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
257 if (s->inlen >= sizeof(s->inbuf) - 1) {
258 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
263 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
265 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
266 } else if (res > 0) {
267 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
271 s->inbuf[s->inlen] = '\0';
278 static int input_check(struct ast_mansession *s, struct message **mout)
280 static struct message m;
287 res = get_input(s, m.headers[m.hdrcount]);
290 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
293 /* Strip trailing \r\n */
294 if (strlen(m.headers[m.hdrcount]) < 2)
296 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
297 if (!strlen(m.headers[m.hdrcount])) {
298 if (mout && strlen(get_header(&m, "Response"))) {
302 if (process_message(s, &m))
304 memset(&m, 0, sizeof(&m));
305 } else if (m.hdrcount < MAX_HEADERS - 1)
307 } else if (res < 0) {
315 static struct message *wait_for_response(int timeout)
322 tv.tv_sec = timeout / 1000;
323 tv.tv_usec = (timeout % 1000) * 1000;
324 FD_SET(session.fd, &fds);
325 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
328 if (input_check(&session, &m) < 0) {
337 static int manager_action(char *action, char *fmt, ...)
339 struct ast_mansession *s;
344 fdprintf(s->fd, "Action: %s\r\n", action);
346 vsnprintf(tmp, sizeof(tmp), fmt, ap);
348 write(s->fd, tmp, strlen(tmp));
349 fdprintf(s->fd, "\r\n");
353 static int show_message(char *title, char *msg)
358 struct newtExitStruct es;
360 newtCenteredWindow(60,7, title);
362 label = newtLabel(4,1,msg);
363 ok = newtButton(27, 3, "OK");
364 form = newtForm(NULL, NULL, 0);
365 newtFormAddComponents(form, label, ok, NULL);
366 newtFormRun(form, &es);
368 newtFormDestroy(form);
372 static newtComponent showform;
373 static int show_doing(char *title, char *tmp)
375 struct newtExitStruct es;
377 showform = newtForm(NULL, NULL, 0);
378 newtCenteredWindow(70,4, title);
379 label = newtLabel(3,1,tmp);
380 newtFormAddComponents(showform,label, NULL);
381 newtFormSetTimer(showform, 200);
382 newtFormRun(showform, &es);
386 static int hide_doing(void)
389 newtFormDestroy(showform);
393 static void try_status(void)
396 manager_action("Status", "");
397 m = wait_for_response(10000);
399 show_message("Status Failed", "Timeout waiting for response");
400 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
401 show_message("Status Failed Failed", get_header(m, "Message"));
406 static void try_hangup(newtComponent c)
408 struct ast_chan *chan;
411 chan = newtListboxGetCurrent(c);
413 manager_action("Hangup", "Channel: %s\r\n", chan->name);
414 m = wait_for_response(10000);
416 show_message("Hangup Failed", "Timeout waiting for response");
417 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
418 show_message("Hangup Failed", get_header(m, "Message"));
424 static int get_user_input(char *msg, char *buf, int buflen)
428 newtComponent cancel;
429 newtComponent inpfield;
432 struct newtExitStruct es;
434 newtCenteredWindow(60,7, msg);
436 inpfield = newtEntry(5, 2, "", 50, &input, 0);
437 ok = newtButton(22, 3, "OK");
438 cancel = newtButton(32, 3, "Cancel");
439 form = newtForm(NULL, NULL, 0);
440 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
441 newtFormRun(form, &es);
442 strncpy(buf, input, buflen - 1);
448 newtFormDestroy(form);
452 static void try_redirect(newtComponent c)
454 struct ast_chan *chan;
461 chan = newtListboxGetCurrent(c);
463 strncpy(channame, chan->name, sizeof(channame) - 1);
464 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
465 if (get_user_input(tmp, dest, sizeof(dest)))
467 if ((context = strchr(dest, '@'))) {
470 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
472 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
474 m = wait_for_response(10000);
476 show_message("Hangup Failed", "Timeout waiting for response");
477 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
478 show_message("Hangup Failed", get_header(m, "Message"));
484 static int manage_calls(char *host)
488 newtComponent hangup;
489 newtComponent redirect;
490 newtComponent channels;
491 struct newtExitStruct es;
494 /* If there's one thing you learn from this code, it is this...
495 Never, ever fly Air France. Their customer service is absolutely
496 the worst. I've never heard the words "That's not my problem" as
497 many times as I have from their staff -- It should, without doubt
498 be their corporate motto if it isn't already. Don't bother giving
499 them business because you're just a pain in their side and they
500 will be sure to let you know the first time you speak to them.
502 If you ever want to make me happy just tell me that you, too, will
503 never fly Air France again either (in spite of their excellent
505 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
506 newtCenteredWindow(74, 20, tmp);
507 form = newtForm(NULL, NULL, 0);
508 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
509 newtFormSetTimer(form, 100);
510 quit = newtButton(62, 16, "Quit");
511 redirect = newtButton(35, 16, "Redirect");
512 hangup = newtButton(50, 16, "Hangup");
513 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
514 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
515 newtListboxSetWidth(channels, 72);
517 show_doing("Getting Status", "Retrieving system status...");
522 newtFormRun(form, &es);
523 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
524 if (input_check(&session, NULL)) {
525 show_message("Disconnected", "Disconnected from remote host");
528 } else if (es.reason == NEWT_EXIT_COMPONENT) {
531 if (es.u.co == hangup) {
532 try_hangup(channels);
533 } else if (es.u.co == redirect) {
534 try_redirect(channels);
537 rebuild_channels(channels);
539 newtFormDestroy(form);
543 static int login(char *hostname)
546 newtComponent cancel;
548 newtComponent username;
549 newtComponent password;
551 newtComponent ulabel;
552 newtComponent plabel;
556 struct newtExitStruct es;
561 session.fd = socket(AF_INET, SOCK_STREAM, 0);
562 if (session.fd < 0) {
563 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
564 show_message("Socket failed", tmp);
568 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
569 show_doing("Connecting....", tmp);
572 hp = gethostbyname(hostname);
574 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
575 show_message("Host lookup failed", tmp);
579 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
580 show_doing("Connecting...", tmp);
582 session.sin.sin_family = AF_INET;
583 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
584 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
586 if (connect(session.fd, &session.sin, sizeof(session.sin))) {
587 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
588 show_message("Connect Failed", tmp);
594 login = newtButton(5, 6, "Login");
595 cancel = newtButton(25, 6, "Cancel");
596 newtCenteredWindow(40, 10, "Asterisk Manager Login");
597 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
598 label = newtLabel(4,1, tmp);
600 ulabel = newtLabel(4,2,"Username:");
601 plabel = newtLabel(4,3,"Password:");
603 username = newtEntry(14, 2, "", 20, &user, 0);
604 password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN);
606 form = newtForm(NULL, NULL, 0);
607 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
608 newtFormRun(form, &es);
609 if (es.reason == NEWT_EXIT_COMPONENT) {
610 if (es.u.co == login) {
611 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
612 show_doing("Logging in", tmp);
613 /* Check to see if the remote host supports MD5 Authentication */
614 manager_action("Challenge", "AuthType: MD5\r\n");
615 m = wait_for_response(10000);
616 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
617 char *challenge = get_header(m, "Challenge");
620 char md5key[256] = "";
621 struct MD5Context md5;
622 unsigned char digest[16];
624 MD5Update(&md5, challenge, strlen(challenge));
625 MD5Update(&md5, pass, strlen(pass));
626 MD5Final(digest, &md5);
628 len += sprintf(md5key + len, "%2.2x", digest[x]);
629 manager_action("Login",
634 m = wait_for_response(10000);
636 if (!strcasecmp(get_header(m, "Response"), "Success")) {
639 show_message("Login Failed", get_header(m, "Message"));
642 memset(m, 0, sizeof(m));
643 manager_action("Login",
647 m = wait_for_response(10000);
650 if (!strcasecmp(get_header(m, "Response"), "Success")) {
653 show_message("Login Failed", get_header(m, "Message"));
659 newtFormDestroy(form);
663 int main(int argc, char *argv[])
666 fprintf(stderr, "Usage: astman <host>\n");
671 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
672 newtPushHelpLine("Welcome to the Asterisk Manager!");
673 if (login(argv[1])) {
677 manage_calls(argv[1]);