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