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