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