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