2a19304adce00ecbe60d5fe3f702b0242c28705f
[asterisk/asterisk.git] / utils / astman.c
1 /*
2  * ASTerisk MANager
3  * Copyright (C) 2002, Linux Support Services, Inc.
4  *
5  * Distributed under the terms of the GNU General Public License
6  */
7  
8 #include <newt.h>
9 #include <stdio.h>
10 #include <sys/time.h>
11 #include <netdb.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <sys/socket.h>
15 #include <sys/select.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22
23 #include "asterisk/md5.h"
24 #include "asterisk/manager.h"
25
26 #undef gethostbyname
27
28 #define MAX_HEADERS 80
29 #define MAX_LEN 256
30
31 /*
32  * 2005.05.27 - different versions of newt define the type of the buffer
33  * for the 5th argument to newtEntry() as char ** or const char ** . To 
34  * let the code compile cleanly with -Werror, we cast it to void * through 
35  * _NEWT_CAST.
36  */
37 #define _NEWT_CAST (void *)
38
39 static struct ast_mansession {
40         struct sockaddr_in sin;
41         int fd;
42         char inbuf[MAX_LEN];
43         int inlen;
44 } session;
45
46 static struct ast_chan {
47         char name[80];
48         char exten[20];
49         char context[20];
50         char priority[20];
51         char callerid[40];
52         char state[10];
53         struct ast_chan *next;
54 } *chans;
55
56 static struct ast_chan *find_chan(char *name)
57 {
58         struct ast_chan *prev = NULL, *chan = chans;
59         while(chan) {
60                 if (!strcmp(name, chan->name))
61                         return chan;
62                 prev = chan;
63                 chan = chan->next;
64         }
65         chan = malloc(sizeof(struct ast_chan));
66         if (chan) {
67                 memset(chan, 0, sizeof(struct ast_chan));
68                 strncpy(chan->name, name, sizeof(chan->name) - 1);
69                 if (prev) 
70                         prev->next = chan;
71                 else
72                         chans = chan;
73         }
74         return chan;
75 }
76
77 static void del_chan(char *name)
78 {
79         struct ast_chan *prev = NULL, *chan = chans;
80         while(chan) {
81                 if (!strcmp(name, chan->name)) {
82                         if (prev)
83                                 prev->next = chan->next;
84                         else
85                                 chans = chan->next;
86                         return;
87                 }
88                 prev = chan;
89                 chan = chan->next;
90         }
91 }
92
93 static void fdprintf(int fd, char *fmt, ...)
94 {
95         char stuff[4096];
96         va_list ap;
97         va_start(ap, fmt);
98         vsnprintf(stuff, sizeof(stuff), fmt, ap);
99         va_end(ap);
100         write(fd, stuff, strlen(stuff));
101 }
102
103 static char *get_header(struct message *m, char *var)
104 {
105         char cmp[80];
106         int x;
107         snprintf(cmp, sizeof(cmp), "%s: ", var);
108         for (x=0;x<m->hdrcount;x++)
109                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
110                         return m->headers[x] + strlen(cmp);
111         return "";
112 }
113
114 static int event_newstate(struct ast_mansession *s, struct message *m)
115 {
116         struct ast_chan *chan;
117         chan = find_chan(get_header(m, "Channel"));
118         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
119         return 0;
120 }
121
122 static int event_newexten(struct ast_mansession *s, struct message *m)
123 {
124         struct ast_chan *chan;
125         chan = find_chan(get_header(m, "Channel"));
126         strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
127         strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
128         strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
129         return 0;
130 }
131
132 static int event_newchannel(struct ast_mansession *s, struct message *m)
133 {
134         struct ast_chan *chan;
135         chan = find_chan(get_header(m, "Channel"));
136         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
137         strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
138         return 0;
139 }
140
141 static int event_status(struct ast_mansession *s, struct message *m)
142 {
143         struct ast_chan *chan;
144         chan = find_chan(get_header(m, "Channel"));
145         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
146         strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
147         strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
148         strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
149         strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
150         return 0;
151 }
152
153 static int event_hangup(struct ast_mansession *s, struct message *m)
154 {
155         del_chan(get_header(m, "Channel"));
156         return 0;
157 }
158
159 static int event_ignore(struct ast_mansession *s, struct message *m)
160 {
161         return 0;
162 }
163
164 static int event_rename(struct ast_mansession *s, struct message *m)
165 {
166         struct ast_chan *chan;
167         chan = find_chan(get_header(m, "Oldname"));
168         strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
169         return 0;
170 }
171 static struct event {
172         char *event;
173         int (*func)(struct ast_mansession *s, struct message *m);
174 } events[] = {
175         { "Newstate", event_newstate },
176         { "Newchannel", event_newchannel },
177         { "Newexten", event_newexten },
178         { "Hangup", event_hangup },
179         { "Rename", event_rename },
180         { "Status", event_status },
181         { "Link", event_ignore },
182         { "Unlink", event_ignore },
183         { "StatusComplete", event_ignore }
184 };
185
186 static int process_message(struct ast_mansession *s, struct message *m)
187 {
188         int x;
189         char event[80] = "";
190         strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
191         if (!strlen(event)) {
192                 fprintf(stderr, "Missing event in request");
193                 return 0;
194         }
195         for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
196                 if (!strcasecmp(event, events[x].event)) {
197                         if (events[x].func(s, m))
198                                 return -1;
199                         break;
200                 }
201         }
202         if (x >= sizeof(events) / sizeof(events[0]))
203                 fprintf(stderr, "Ignoring unknown event '%s'", event);
204 #if 0
205         for (x=0;x<m->hdrcount;x++) {
206                 printf("Header: %s\n", m->headers[x]);
207         }
208 #endif  
209         return 0;
210 }
211
212 static void rebuild_channels(newtComponent c)
213 {
214         void *prev = NULL;
215         struct ast_chan *chan;
216         char tmpn[42];
217         char tmp[256];
218         int x=0;
219         prev = newtListboxGetCurrent(c);
220         newtListboxClear(c);
221         chan = chans;
222         while(chan) {
223                 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
224                 if (strlen(chan->exten)) 
225                         snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s", 
226                                 tmpn, chan->state,
227                                 chan->exten, chan->context, chan->priority);
228                 else
229                         snprintf(tmp, sizeof(tmp), "%-30s %8s",
230                                 tmpn, chan->state);
231                 newtListboxAppendEntry(c, tmp, chan);
232                 x++;
233                 chan = chan->next;
234         }
235         if (!x)
236                 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
237         newtListboxSetCurrentByKey(c, prev);
238 }
239
240 static int has_input(struct ast_mansession *s)
241 {
242         int x;
243         for (x=1;x<s->inlen;x++) 
244                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) 
245                         return 1;
246         return 0;
247 }
248
249 static int get_input(struct ast_mansession *s, char *output)
250 {
251         /* output must have at least sizeof(s->inbuf) space */
252         int res;
253         int x;
254         struct timeval tv = {0, 0};
255         fd_set fds;
256         for (x=1;x<s->inlen;x++) {
257                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
258                         /* Copy output data up to and including \r\n */
259                         memcpy(output, s->inbuf, x + 1);
260                         /* Add trailing \0 */
261                         output[x+1] = '\0';
262                         /* Move remaining data back to the front */
263                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
264                         s->inlen -= (x + 1);
265                         return 1;
266                 }
267         } 
268         if (s->inlen >= sizeof(s->inbuf) - 1) {
269                 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
270                 s->inlen = 0;
271         }
272         FD_ZERO(&fds);
273         FD_SET(s->fd, &fds);
274         res = select(s->fd + 1, &fds, NULL, NULL, &tv);
275         if (res < 0) {
276                 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
277         } else if (res > 0) {
278                 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
279                 if (res < 1)
280                         return -1;
281                 s->inlen += res;
282                 s->inbuf[s->inlen] = '\0';
283         } else {
284                 return 2;
285         }
286         return 0;
287 }
288
289 static int input_check(struct ast_mansession *s, struct message **mout)
290 {
291         static struct message m;
292         int res;
293
294         if (mout)
295                 *mout = NULL;
296
297         for(;;) {
298                 res = get_input(s, m.headers[m.hdrcount]);
299                 if (res == 1) {
300 #if 0
301                         fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
302                         fgetc(stdin);
303 #endif
304                         /* Strip trailing \r\n */
305                         if (strlen(m.headers[m.hdrcount]) < 2)
306                                 continue;
307                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
308                         if (!strlen(m.headers[m.hdrcount])) {
309                                 if (mout && strlen(get_header(&m, "Response"))) {
310                                         *mout = &m;
311                                         return 0;
312                                 }
313                                 if (process_message(s, &m))
314                                         break;
315                                 memset(&m, 0, sizeof(&m));
316                         } else if (m.hdrcount < MAX_HEADERS - 1)
317                                 m.hdrcount++;
318                 } else if (res < 0) {
319                         return -1;
320                 } else if (res == 2)
321                         return 0;
322         }
323         return -1;
324 }
325
326 static struct message *wait_for_response(int timeout)
327 {
328         struct message *m;
329         struct timeval tv;
330         int res;
331         fd_set fds;
332         for (;;) {
333                 tv.tv_sec = timeout / 1000;
334                 tv.tv_usec = (timeout % 1000) * 1000;
335                 FD_SET(session.fd, &fds);
336                 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
337                 if (res < 1)
338                         break;
339                 if (input_check(&session, &m) < 0) {
340                         return NULL;
341                 }
342                 if (m)
343                         return m;
344         }
345         return NULL;
346 }
347
348 static int manager_action(char *action, char *fmt, ...)
349 {
350         struct ast_mansession *s;
351         char tmp[4096];
352         va_list ap;
353
354         s = &session;
355         fdprintf(s->fd, "Action: %s\r\n", action);
356         va_start(ap, fmt);
357         vsnprintf(tmp, sizeof(tmp), fmt, ap);
358         va_end(ap);
359         write(s->fd, tmp, strlen(tmp));
360         fdprintf(s->fd, "\r\n");
361         return 0;
362 }
363
364 static int show_message(char *title, char *msg)
365 {
366         newtComponent form;
367         newtComponent label;
368         newtComponent ok;
369         struct newtExitStruct es;
370
371         newtCenteredWindow(60,7, title);
372
373         label = newtLabel(4,1,msg);
374         ok = newtButton(27, 3, "OK");
375         form = newtForm(NULL, NULL, 0);
376         newtFormAddComponents(form, label, ok, NULL);
377         newtFormRun(form, &es);
378         newtPopWindow();
379         newtFormDestroy(form);
380         return 0;
381 }
382
383 static newtComponent showform;
384 static int show_doing(char *title, char *tmp)
385 {
386         struct newtExitStruct es;
387         newtComponent label;
388         showform = newtForm(NULL, NULL, 0);
389         newtCenteredWindow(70,4, title);
390         label = newtLabel(3,1,tmp);
391         newtFormAddComponents(showform,label, NULL);
392         newtFormSetTimer(showform, 200);
393         newtFormRun(showform, &es);
394         return 0;
395 }
396
397 static int hide_doing(void)
398 {
399         newtPopWindow();
400         newtFormDestroy(showform);
401         return 0;
402 }
403
404 static void try_status(void)
405 {
406         struct message *m;
407         manager_action("Status", "");
408         m = wait_for_response(10000);
409         if (!m) {
410                 show_message("Status Failed", "Timeout waiting for response");
411         } else if (strcasecmp(get_header(m, "Response"), "Success"))  {
412                 show_message("Status Failed Failed", get_header(m, "Message"));
413         }
414 }
415         
416
417 static void try_hangup(newtComponent c)
418 {
419         struct ast_chan *chan;
420         struct message *m;
421
422         chan = newtListboxGetCurrent(c);
423         if (chan) {
424                 manager_action("Hangup", "Channel: %s\r\n", chan->name);
425                 m = wait_for_response(10000);
426                 if (!m) {
427                         show_message("Hangup Failed", "Timeout waiting for response");
428                 } else if (strcasecmp(get_header(m, "Response"), "Success"))  {
429                         show_message("Hangup Failed", get_header(m, "Message"));
430                 }
431         }
432         
433 }
434
435 static int get_user_input(char *msg, char *buf, int buflen)
436 {
437         newtComponent form;
438         newtComponent ok;
439         newtComponent cancel;
440         newtComponent inpfield;
441         const char *input;
442         int res = -1;
443         struct newtExitStruct es;
444
445         newtCenteredWindow(60,7, msg);
446
447         inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
448         ok = newtButton(22, 3, "OK");
449         cancel = newtButton(32, 3, "Cancel");
450         form = newtForm(NULL, NULL, 0);
451         newtFormAddComponents(form, inpfield, ok, cancel, NULL);
452         newtFormRun(form, &es);
453         strncpy(buf, input, buflen - 1);
454         if (es.u.co == ok) 
455                 res = 0;
456         else
457                 res = -1;
458         newtPopWindow();
459         newtFormDestroy(form);
460         return res;
461 }
462
463 static void try_redirect(newtComponent c)
464 {
465         struct ast_chan *chan;
466         char dest[256];
467         struct message *m;
468         char channame[256];
469         char tmp[80];
470         char *context;
471
472         chan = newtListboxGetCurrent(c);
473         if (chan) {
474                 strncpy(channame, chan->name, sizeof(channame) - 1);
475                 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
476                 if (get_user_input(tmp, dest, sizeof(dest))) 
477                         return;
478                 if ((context = strchr(dest, '@'))) {
479                         *context = '\0';
480                         context++;
481                         manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
482                 } else {
483                         manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
484                 }
485                 m = wait_for_response(10000);
486                 if (!m) {
487                         show_message("Hangup Failed", "Timeout waiting for response");
488                 } else if (strcasecmp(get_header(m, "Response"), "Success"))  {
489                         show_message("Hangup Failed", get_header(m, "Message"));
490                 }
491         }
492         
493 }
494
495 static int manage_calls(char *host)
496 {
497         newtComponent form;
498         newtComponent quit;
499         newtComponent hangup;
500         newtComponent redirect;
501         newtComponent channels;
502         struct newtExitStruct es;
503         char tmp[80];
504
505         /* If there's one thing you learn from this code, it is this...
506            Never, ever fly Air France.  Their customer service is absolutely
507            the worst.  I've never heard the words "That's not my problem" as 
508            many times as I have from their staff -- It should, without doubt
509            be their corporate motto if it isn't already.  Don't bother giving 
510            them business because you're just a pain in their side and they
511            will be sure to let you know the first time you speak to them.
512            
513            If you ever want to make me happy just tell me that you, too, will
514            never fly Air France again either (in spite of their excellent
515            cuisine). */
516         snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
517         newtCenteredWindow(74, 20, tmp);
518         form = newtForm(NULL, NULL, 0);
519         newtFormWatchFd(form, session.fd, NEWT_FD_READ);
520         newtFormSetTimer(form, 100);
521         quit = newtButton(62, 16, "Quit");
522         redirect = newtButton(35, 16, "Redirect");
523         hangup = newtButton(50, 16, "Hangup");
524         channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
525         newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
526         newtListboxSetWidth(channels, 72);
527         
528         show_doing("Getting Status", "Retrieving system status...");
529         try_status();
530         hide_doing();
531
532         for(;;) {
533                 newtFormRun(form, &es);
534                 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
535                         if (input_check(&session, NULL)) {
536                                 show_message("Disconnected", "Disconnected from remote host");
537                                 break;
538                         }
539                 } else if (es.reason == NEWT_EXIT_COMPONENT) {
540                         if (es.u.co == quit)
541                                 break;
542                         if (es.u.co == hangup) {
543                                 try_hangup(channels);
544                         } else if (es.u.co == redirect) {
545                                 try_redirect(channels);
546                         }
547                 }
548                 rebuild_channels(channels);
549         }
550         newtFormDestroy(form);
551         return 0;
552 }
553
554 static int login(char *hostname)
555 {
556         newtComponent form;
557         newtComponent cancel;
558         newtComponent login;
559         newtComponent username;
560         newtComponent password;
561         newtComponent label;
562         newtComponent ulabel;
563         newtComponent plabel;
564         const char *user;
565         const char *pass;
566         struct message *m;
567         struct newtExitStruct es;
568         char tmp[55];
569         struct hostent *hp;
570         int res = -1;
571         
572         session.fd = socket(AF_INET, SOCK_STREAM, 0);
573         if (session.fd < 0) {
574                 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
575                 show_message("Socket failed", tmp);
576                 return -1;
577         }
578         
579         snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
580         show_doing("Connecting....", tmp);
581         
582         
583         hp = gethostbyname(hostname);
584         if (!hp) {
585                 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
586                 show_message("Host lookup failed", tmp);
587                 return -1;
588         }
589         hide_doing();
590         snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
591         show_doing("Connecting...", tmp);
592
593         session.sin.sin_family = AF_INET;
594         session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
595         memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
596
597         if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
598                 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
599                 show_message("Connect Failed", tmp);
600                 return -1;
601         }
602         
603         hide_doing();
604         
605         login = newtButton(5, 6, "Login");
606         cancel = newtButton(25, 6, "Cancel");
607         newtCenteredWindow(40, 10, "Asterisk Manager Login");
608         snprintf(tmp, sizeof(tmp), "Host:     %s", hostname);
609         label = newtLabel(4,1, tmp);
610         
611         ulabel = newtLabel(4,2,"Username:");
612         plabel = newtLabel(4,3,"Password:");
613         
614         username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
615         password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
616         
617         form = newtForm(NULL, NULL, 0);
618         newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
619         newtFormRun(form, &es);
620         if (es.reason == NEWT_EXIT_COMPONENT) {
621                 if (es.u.co == login) {
622                         snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
623                         show_doing("Logging in", tmp);
624                         /* Check to see if the remote host supports MD5 Authentication */
625                         manager_action("Challenge", "AuthType: MD5\r\n");
626                         m = wait_for_response(10000);
627                         if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
628                                 char *challenge = get_header(m, "Challenge");
629                                 int x;
630                                 int len = 0;
631                                 char md5key[256] = "";
632                                 struct MD5Context md5;
633                                 unsigned char digest[16];
634                                 MD5Init(&md5);
635                                 MD5Update(&md5, challenge, strlen(challenge));
636                                 MD5Update(&md5, pass, strlen(pass));
637                                 MD5Final(digest, &md5);
638                                 for (x=0; x<16; x++)
639                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
640                                 manager_action("Login",
641                                                 "AuthType: MD5\r\n"
642                                                 "Username: %s\r\n"
643                                                 "Key: %s\r\n",
644                                                 user, md5key);
645                                 m = wait_for_response(10000);
646                                 hide_doing();
647                                 if (!strcasecmp(get_header(m, "Response"), "Success")) {
648                                         res = 0;
649                                 } else {
650                                         show_message("Login Failed", get_header(m, "Message"));
651                                 }
652                         } else {
653                                 memset(m, 0, sizeof(m));
654                                 manager_action("Login", 
655                                         "Username: %s\r\n"
656                                         "Secret: %s\r\n",
657                                                 user, pass);
658                                 m = wait_for_response(10000);
659                                 hide_doing();
660                                 if (m) {
661                                         if (!strcasecmp(get_header(m, "Response"), "Success")) {
662                                                 res = 0;
663                                         } else {
664                                                 show_message("Login Failed", get_header(m, "Message"));
665                                         }
666                                 }
667                         }
668                 }
669         }
670         newtFormDestroy(form);
671         return res;
672 }
673
674 int main(int argc, char *argv[])
675 {
676         if (argc < 2) {
677                 fprintf(stderr, "Usage: astman <host>\n");
678                 exit(1);
679         }
680         newtInit();
681         newtCls();
682         newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
683         newtPushHelpLine("Welcome to the Asterisk Manager!");
684         if (login(argv[1])) {
685                 newtFinished();
686                 exit(1);
687         }
688         manage_calls(argv[1]);
689         newtFinished();
690         return 0;
691 }