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 },
173 { "StatusComplete", event_ignore }
176 static int process_message(struct ast_mansession *s, struct message *m)
180 strncpy(event, get_header(m, "Event"), sizeof(event));
181 if (!strlen(event)) {
182 fprintf(stderr, "Missing event in request");
185 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
186 if (!strcasecmp(event, events[x].event)) {
187 if (events[x].func(s, m))
192 if (x >= sizeof(events) / sizeof(events[0]))
193 fprintf(stderr, "Ignoring unknown event '%s'", event);
195 for (x=0;x<m->hdrcount;x++) {
196 printf("Header: %s\n", m->headers[x]);
202 static void rebuild_channels(newtComponent c)
205 struct ast_chan *chan;
209 prev = newtListboxGetCurrent(c);
213 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
214 if (strlen(chan->exten))
215 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
217 chan->exten, chan->context, chan->priority);
219 snprintf(tmp, sizeof(tmp), "%-30s %8s",
221 newtListboxAppendEntry(c, tmp, chan);
226 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
227 newtListboxSetCurrentByKey(c, prev);
230 static int has_input(struct ast_mansession *s)
233 for (x=1;x<s->inlen;x++)
234 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
239 static int get_input(struct ast_mansession *s, char *output)
241 /* output must have at least sizeof(s->inbuf) space */
244 struct timeval tv = {0, 0};
246 for (x=1;x<s->inlen;x++) {
247 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
248 /* Copy output data up to and including \r\n */
249 memcpy(output, s->inbuf, x + 1);
250 /* Add trailing \0 */
252 /* Move remaining data back to the front */
253 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
258 if (s->inlen >= sizeof(s->inbuf) - 1) {
259 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
264 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
266 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
267 } else if (res > 0) {
268 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
272 s->inbuf[s->inlen] = '\0';
279 static int input_check(struct ast_mansession *s, struct message **mout)
281 static struct message m;
288 res = get_input(s, m.headers[m.hdrcount]);
291 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
294 /* Strip trailing \r\n */
295 if (strlen(m.headers[m.hdrcount]) < 2)
297 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
298 if (!strlen(m.headers[m.hdrcount])) {
299 if (mout && strlen(get_header(&m, "Response"))) {
303 if (process_message(s, &m))
305 memset(&m, 0, sizeof(&m));
306 } else if (m.hdrcount < MAX_HEADERS - 1)
308 } else if (res < 0) {
316 static struct message *wait_for_response(int timeout)
323 tv.tv_sec = timeout / 1000;
324 tv.tv_usec = (timeout % 1000) * 1000;
325 FD_SET(session.fd, &fds);
326 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
329 if (input_check(&session, &m) < 0) {
338 static int manager_action(char *action, char *fmt, ...)
340 struct ast_mansession *s;
345 fdprintf(s->fd, "Action: %s\r\n", action);
347 vsnprintf(tmp, sizeof(tmp), fmt, ap);
349 write(s->fd, tmp, strlen(tmp));
350 fdprintf(s->fd, "\r\n");
354 static int show_message(char *title, char *msg)
359 struct newtExitStruct es;
361 newtCenteredWindow(60,7, title);
363 label = newtLabel(4,1,msg);
364 ok = newtButton(27, 3, "OK");
365 form = newtForm(NULL, NULL, 0);
366 newtFormAddComponents(form, label, ok, NULL);
367 newtFormRun(form, &es);
369 newtFormDestroy(form);
373 static newtComponent showform;
374 static int show_doing(char *title, char *tmp)
376 struct newtExitStruct es;
378 showform = newtForm(NULL, NULL, 0);
379 newtCenteredWindow(70,4, title);
380 label = newtLabel(3,1,tmp);
381 newtFormAddComponents(showform,label, NULL);
382 newtFormSetTimer(showform, 200);
383 newtFormRun(showform, &es);
387 static int hide_doing(void)
390 newtFormDestroy(showform);
394 static void try_status(void)
397 manager_action("Status", "");
398 m = wait_for_response(10000);
400 show_message("Status Failed", "Timeout waiting for response");
401 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
402 show_message("Status Failed Failed", get_header(m, "Message"));
407 static void try_hangup(newtComponent c)
409 struct ast_chan *chan;
412 chan = newtListboxGetCurrent(c);
414 manager_action("Hangup", "Channel: %s\r\n", chan->name);
415 m = wait_for_response(10000);
417 show_message("Hangup Failed", "Timeout waiting for response");
418 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
419 show_message("Hangup Failed", get_header(m, "Message"));
425 static int get_user_input(char *msg, char *buf, int buflen)
429 newtComponent cancel;
430 newtComponent inpfield;
433 struct newtExitStruct es;
435 newtCenteredWindow(60,7, msg);
437 inpfield = newtEntry(5, 2, "", 50, &input, 0);
438 ok = newtButton(22, 3, "OK");
439 cancel = newtButton(32, 3, "Cancel");
440 form = newtForm(NULL, NULL, 0);
441 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
442 newtFormRun(form, &es);
443 strncpy(buf, input, buflen - 1);
449 newtFormDestroy(form);
453 static void try_redirect(newtComponent c)
455 struct ast_chan *chan;
462 chan = newtListboxGetCurrent(c);
464 strncpy(channame, chan->name, sizeof(channame) - 1);
465 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
466 if (get_user_input(tmp, dest, sizeof(dest)))
468 if ((context = strchr(dest, '@'))) {
471 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
473 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
475 m = wait_for_response(10000);
477 show_message("Hangup Failed", "Timeout waiting for response");
478 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
479 show_message("Hangup Failed", get_header(m, "Message"));
485 static int manage_calls(char *host)
489 newtComponent hangup;
490 newtComponent redirect;
491 newtComponent channels;
492 struct newtExitStruct es;
495 /* If there's one thing you learn from this code, it is this...
496 Never, ever fly Air France. Their customer service is absolutely
497 the worst. I've never heard the words "That's not my problem" as
498 many times as I have from their staff -- It should, without doubt
499 be their corporate motto if it isn't already. Don't bother giving
500 them business because you're just a pain in their side and they
501 will be sure to let you know the first time you speak to them.
503 If you ever want to make me happy just tell me that you, too, will
504 never fly Air France again either (in spite of their excellent
506 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
507 newtCenteredWindow(74, 20, tmp);
508 form = newtForm(NULL, NULL, 0);
509 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
510 newtFormSetTimer(form, 100);
511 quit = newtButton(62, 16, "Quit");
512 redirect = newtButton(35, 16, "Redirect");
513 hangup = newtButton(50, 16, "Hangup");
514 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
515 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
516 newtListboxSetWidth(channels, 72);
518 show_doing("Getting Status", "Retrieving system status...");
523 newtFormRun(form, &es);
524 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
525 if (input_check(&session, NULL)) {
526 show_message("Disconnected", "Disconnected from remote host");
529 } else if (es.reason == NEWT_EXIT_COMPONENT) {
532 if (es.u.co == hangup) {
533 try_hangup(channels);
534 } else if (es.u.co == redirect) {
535 try_redirect(channels);
538 rebuild_channels(channels);
540 newtFormDestroy(form);
544 static int login(char *hostname)
547 newtComponent cancel;
549 newtComponent username;
550 newtComponent password;
552 newtComponent ulabel;
553 newtComponent plabel;
557 struct newtExitStruct es;
562 session.fd = socket(AF_INET, SOCK_STREAM, 0);
563 if (session.fd < 0) {
564 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
565 show_message("Socket failed", tmp);
569 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
570 show_doing("Connecting....", tmp);
573 hp = gethostbyname(hostname);
575 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
576 show_message("Host lookup failed", tmp);
580 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
581 show_doing("Connecting...", tmp);
583 session.sin.sin_family = AF_INET;
584 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
585 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
587 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
588 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
589 show_message("Connect Failed", tmp);
595 login = newtButton(5, 6, "Login");
596 cancel = newtButton(25, 6, "Cancel");
597 newtCenteredWindow(40, 10, "Asterisk Manager Login");
598 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
599 label = newtLabel(4,1, tmp);
601 ulabel = newtLabel(4,2,"Username:");
602 plabel = newtLabel(4,3,"Password:");
604 username = newtEntry(14, 2, "", 20, &user, 0);
605 password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN);
607 form = newtForm(NULL, NULL, 0);
608 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
609 newtFormRun(form, &es);
610 if (es.reason == NEWT_EXIT_COMPONENT) {
611 if (es.u.co == login) {
612 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
613 show_doing("Logging in", tmp);
614 /* Check to see if the remote host supports MD5 Authentication */
615 manager_action("Challenge", "AuthType: MD5\r\n");
616 m = wait_for_response(10000);
617 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
618 char *challenge = get_header(m, "Challenge");
621 char md5key[256] = "";
622 struct MD5Context md5;
623 unsigned char digest[16];
625 MD5Update(&md5, challenge, strlen(challenge));
626 MD5Update(&md5, pass, strlen(pass));
627 MD5Final(digest, &md5);
629 len += sprintf(md5key + len, "%2.2x", digest[x]);
630 manager_action("Login",
635 m = wait_for_response(10000);
637 if (!strcasecmp(get_header(m, "Response"), "Success")) {
640 show_message("Login Failed", get_header(m, "Message"));
643 memset(m, 0, sizeof(m));
644 manager_action("Login",
648 m = wait_for_response(10000);
651 if (!strcasecmp(get_header(m, "Response"), "Success")) {
654 show_message("Login Failed", get_header(m, "Message"));
660 newtFormDestroy(form);
664 int main(int argc, char *argv[])
667 fprintf(stderr, "Usage: astman <host>\n");
672 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
673 newtPushHelpLine("Welcome to the Asterisk Manager!");
674 if (login(argv[1])) {
678 manage_calls(argv[1]);