Make astman ignore 'StatusComplete' events.
[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/socket.h>
11 #include <sys/time.h>
12 #include <netdb.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.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 #define MAX_HEADERS 80
27 #define MAX_LEN 256
28
29 static struct ast_mansession {
30         struct sockaddr_in sin;
31         int fd;
32         char inbuf[MAX_LEN];
33         int inlen;
34 } session;
35
36 static struct ast_chan {
37         char name[80];
38         char exten[20];
39         char context[20];
40         char priority[20];
41         char callerid[40];
42         char state[10];
43         struct ast_chan *next;
44 } *chans;
45
46 static struct ast_chan *find_chan(char *name)
47 {
48         struct ast_chan *prev = NULL, *chan = chans;
49         while(chan) {
50                 if (!strcmp(name, chan->name))
51                         return chan;
52                 prev = chan;
53                 chan = chan->next;
54         }
55         chan = malloc(sizeof(struct ast_chan));
56         if (chan) {
57                 memset(chan, 0, sizeof(struct ast_chan));
58                 strncpy(chan->name, name, sizeof(chan->name) - 1);
59                 if (prev) 
60                         prev->next = chan;
61                 else
62                         chans = chan;
63         }
64         return chan;
65 }
66
67 static void del_chan(char *name)
68 {
69         struct ast_chan *prev = NULL, *chan = chans;
70         while(chan) {
71                 if (!strcmp(name, chan->name)) {
72                         if (prev)
73                                 prev->next = chan->next;
74                         else
75                                 chans = chan->next;
76                         return;
77                 }
78                 prev = chan;
79                 chan = chan->next;
80         }
81 }
82
83 static void fdprintf(int fd, char *fmt, ...)
84 {
85         char stuff[4096];
86         va_list ap;
87         va_start(ap, fmt);
88         vsnprintf(stuff, sizeof(stuff), fmt, ap);
89         va_end(ap);
90         write(fd, stuff, strlen(stuff));
91 }
92
93 static char *get_header(struct message *m, char *var)
94 {
95         char cmp[80];
96         int x;
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);
101         return "";
102 }
103
104 static int event_newstate(struct ast_mansession *s, struct message *m)
105 {
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);
109         return 0;
110 }
111
112 static int event_newexten(struct ast_mansession *s, struct message *m)
113 {
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);
119         return 0;
120 }
121
122 static int event_newchannel(struct ast_mansession *s, struct message *m)
123 {
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);
128         return 0;
129 }
130
131 static int event_status(struct ast_mansession *s, struct message *m)
132 {
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);
140         return 0;
141 }
142
143 static int event_hangup(struct ast_mansession *s, struct message *m)
144 {
145         del_chan(get_header(m, "Channel"));
146         return 0;
147 }
148
149 static int event_ignore(struct ast_mansession *s, struct message *m)
150 {
151         return 0;
152 }
153
154 static int event_rename(struct ast_mansession *s, struct message *m)
155 {
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);
159         return 0;
160 }
161 static struct event {
162         char *event;
163         int (*func)(struct ast_mansession *s, struct message *m);
164 } events[] = {
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 }
174 };
175
176 static int process_message(struct ast_mansession *s, struct message *m)
177 {
178         int x;
179         char event[80];
180         strncpy(event, get_header(m, "Event"), sizeof(event));
181         if (!strlen(event)) {
182                 fprintf(stderr, "Missing event in request");
183                 return 0;
184         }
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))
188                                 return -1;
189                         break;
190                 }
191         }
192         if (x >= sizeof(events) / sizeof(events[0]))
193                 fprintf(stderr, "Ignoring unknown event '%s'", event);
194 #if 0
195         for (x=0;x<m->hdrcount;x++) {
196                 printf("Header: %s\n", m->headers[x]);
197         }
198 #endif  
199         return 0;
200 }
201
202 static void rebuild_channels(newtComponent c)
203 {
204         void *prev = NULL;
205         struct ast_chan *chan;
206         char tmpn[42];
207         char tmp[256];
208         int x=0;
209         prev = newtListboxGetCurrent(c);
210         newtListboxClear(c);
211         chan = chans;
212         while(chan) {
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", 
216                                 tmpn, chan->state,
217                                 chan->exten, chan->context, chan->priority);
218                 else
219                         snprintf(tmp, sizeof(tmp), "%-30s %8s",
220                                 tmpn, chan->state);
221                 newtListboxAppendEntry(c, tmp, chan);
222                 x++;
223                 chan = chan->next;
224         }
225         if (!x)
226                 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
227         newtListboxSetCurrentByKey(c, prev);
228 }
229
230 static int has_input(struct ast_mansession *s)
231 {
232         int x;
233         for (x=1;x<s->inlen;x++) 
234                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) 
235                         return 1;
236         return 0;
237 }
238
239 static int get_input(struct ast_mansession *s, char *output)
240 {
241         /* output must have at least sizeof(s->inbuf) space */
242         int res;
243         int x;
244         struct timeval tv = {0, 0};
245         fd_set fds;
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 */
251                         output[x+1] = '\0';
252                         /* Move remaining data back to the front */
253                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
254                         s->inlen -= (x + 1);
255                         return 1;
256                 }
257         } 
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);
260                 s->inlen = 0;
261         }
262         FD_ZERO(&fds);
263         FD_SET(s->fd, &fds);
264         res = select(s->fd + 1, &fds, NULL, NULL, &tv);
265         if (res < 0) {
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);
269                 if (res < 1)
270                         return -1;
271                 s->inlen += res;
272                 s->inbuf[s->inlen] = '\0';
273         } else {
274                 return 2;
275         }
276         return 0;
277 }
278
279 static int input_check(struct ast_mansession *s, struct message **mout)
280 {
281         static struct message m;
282         int res;
283
284         if (mout)
285                 *mout = NULL;
286
287         for(;;) {
288                 res = get_input(s, m.headers[m.hdrcount]);
289                 if (res == 1) {
290 #if 0
291                         fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
292                         fgetc(stdin);
293 #endif
294                         /* Strip trailing \r\n */
295                         if (strlen(m.headers[m.hdrcount]) < 2)
296                                 continue;
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"))) {
300                                         *mout = &m;
301                                         return 0;
302                                 }
303                                 if (process_message(s, &m))
304                                         break;
305                                 memset(&m, 0, sizeof(&m));
306                         } else if (m.hdrcount < MAX_HEADERS - 1)
307                                 m.hdrcount++;
308                 } else if (res < 0) {
309                         return -1;
310                 } else if (res == 2)
311                         return 0;
312         }
313         return -1;
314 }
315
316 static struct message *wait_for_response(int timeout)
317 {
318         struct message *m;
319         struct timeval tv;
320         int res;
321         fd_set fds;
322         for (;;) {
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);
327                 if (res < 1)
328                         break;
329                 if (input_check(&session, &m) < 0) {
330                         return NULL;
331                 }
332                 if (m)
333                         return m;
334         }
335         return NULL;
336 }
337
338 static int manager_action(char *action, char *fmt, ...)
339 {
340         struct ast_mansession *s;
341         char tmp[4096];
342         va_list ap;
343
344         s = &session;
345         fdprintf(s->fd, "Action: %s\r\n", action);
346         va_start(ap, fmt);
347         vsnprintf(tmp, sizeof(tmp), fmt, ap);
348         va_end(ap);
349         write(s->fd, tmp, strlen(tmp));
350         fdprintf(s->fd, "\r\n");
351         return 0;
352 }
353
354 static int show_message(char *title, char *msg)
355 {
356         newtComponent form;
357         newtComponent label;
358         newtComponent ok;
359         struct newtExitStruct es;
360
361         newtCenteredWindow(60,7, title);
362
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);
368         newtPopWindow();
369         newtFormDestroy(form);
370         return 0;
371 }
372
373 static newtComponent showform;
374 static int show_doing(char *title, char *tmp)
375 {
376         struct newtExitStruct es;
377         newtComponent label;
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);
384         return 0;
385 }
386
387 static int hide_doing(void)
388 {
389         newtPopWindow();
390         newtFormDestroy(showform);
391         return 0;
392 }
393
394 static void try_status(void)
395 {
396         struct message *m;
397         manager_action("Status", "");
398         m = wait_for_response(10000);
399         if (!m) {
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"));
403         }
404 }
405         
406
407 static void try_hangup(newtComponent c)
408 {
409         struct ast_chan *chan;
410         struct message *m;
411
412         chan = newtListboxGetCurrent(c);
413         if (chan) {
414                 manager_action("Hangup", "Channel: %s\r\n", chan->name);
415                 m = wait_for_response(10000);
416                 if (!m) {
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"));
420                 }
421         }
422         
423 }
424
425 static int get_user_input(char *msg, char *buf, int buflen)
426 {
427         newtComponent form;
428         newtComponent ok;
429         newtComponent cancel;
430         newtComponent inpfield;
431         char *input;
432         int res = -1;
433         struct newtExitStruct es;
434
435         newtCenteredWindow(60,7, msg);
436
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);
444         if (es.u.co == ok) 
445                 res = 0;
446         else
447                 res = -1;
448         newtPopWindow();
449         newtFormDestroy(form);
450         return res;
451 }
452
453 static void try_redirect(newtComponent c)
454 {
455         struct ast_chan *chan;
456         char dest[256];
457         struct message *m;
458         char channame[256];
459         char tmp[80];
460         char *context;
461
462         chan = newtListboxGetCurrent(c);
463         if (chan) {
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))) 
467                         return;
468                 if ((context = strchr(dest, '@'))) {
469                         *context = '\0';
470                         context++;
471                         manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
472                 } else {
473                         manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
474                 }
475                 m = wait_for_response(10000);
476                 if (!m) {
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"));
480                 }
481         }
482         
483 }
484
485 static int manage_calls(char *host)
486 {
487         newtComponent form;
488         newtComponent quit;
489         newtComponent hangup;
490         newtComponent redirect;
491         newtComponent channels;
492         struct newtExitStruct es;
493         char tmp[80];
494
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.
502            
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
505            cuisine). */
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);
517         
518         show_doing("Getting Status", "Retrieving system status...");
519         try_status();
520         hide_doing();
521
522         for(;;) {
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");
527                                 break;
528                         }
529                 } else if (es.reason == NEWT_EXIT_COMPONENT) {
530                         if (es.u.co == quit)
531                                 break;
532                         if (es.u.co == hangup) {
533                                 try_hangup(channels);
534                         } else if (es.u.co == redirect) {
535                                 try_redirect(channels);
536                         }
537                 }
538                 rebuild_channels(channels);
539         }
540         newtFormDestroy(form);
541         return 0;
542 }
543
544 static int login(char *hostname)
545 {
546         newtComponent form;
547         newtComponent cancel;
548         newtComponent login;
549         newtComponent username;
550         newtComponent password;
551         newtComponent label;
552         newtComponent ulabel;
553         newtComponent plabel;
554         char *user;
555         char *pass;
556         struct message *m;
557         struct newtExitStruct es;
558         char tmp[55];
559         struct hostent *hp;
560         int res = -1;
561         
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);
566                 return -1;
567         }
568         
569         snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
570         show_doing("Connecting....", tmp);
571         
572         
573         hp = gethostbyname(hostname);
574         if (!hp) {
575                 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
576                 show_message("Host lookup failed", tmp);
577                 return -1;
578         }
579         hide_doing();
580         snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
581         show_doing("Connecting...", tmp);
582
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));
586
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);
590                 return -1;
591         }
592         
593         hide_doing();
594         
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);
600         
601         ulabel = newtLabel(4,2,"Username:");
602         plabel = newtLabel(4,3,"Password:");
603         
604         username = newtEntry(14, 2, "", 20, &user, 0);
605         password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN);
606         
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");
619                                 int x;
620                                 int len = 0;
621                                 char md5key[256] = "";
622                                 struct MD5Context md5;
623                                 unsigned char digest[16];
624                                 MD5Init(&md5);
625                                 MD5Update(&md5, challenge, strlen(challenge));
626                                 MD5Update(&md5, pass, strlen(pass));
627                                 MD5Final(digest, &md5);
628                                 for (x=0; x<16; x++)
629                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
630                                 manager_action("Login",
631                                                 "AuthType: MD5\r\n"
632                                                 "Username: %s\r\n"
633                                                 "Key: %s\r\n",
634                                                 user, md5key);
635                                 m = wait_for_response(10000);
636                                 hide_doing();
637                                 if (!strcasecmp(get_header(m, "Response"), "Success")) {
638                                         res = 0;
639                                 } else {
640                                         show_message("Login Failed", get_header(m, "Message"));
641                                 }
642                         } else {
643                                 memset(m, 0, sizeof(m));
644                                 manager_action("Login", 
645                                         "Username: %s\r\n"
646                                         "Secret: %s\r\n",
647                                                 user, pass);
648                                 m = wait_for_response(10000);
649                                 hide_doing();
650                                 if (m) {
651                                         if (!strcasecmp(get_header(m, "Response"), "Success")) {
652                                                 res = 0;
653                                         } else {
654                                                 show_message("Login Failed", get_header(m, "Message"));
655                                         }
656                                 }
657                         }
658                 }
659         }
660         newtFormDestroy(form);
661         return res;
662 }
663
664 int main(int argc, char *argv[])
665 {
666         if (argc < 2) {
667                 fprintf(stderr, "Usage: astman <host>\n");
668                 exit(1);
669         }
670         newtInit();
671         newtCls();
672         newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
673         newtPushHelpLine("Welcome to the Asterisk Manager!");
674         if (login(argv[1])) {
675                 newtFinished();
676                 exit(1);
677         }
678         manage_calls(argv[1]);
679         newtFinished();
680         return 0;
681 }