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