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