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