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