2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
42 #include "asterisk/md5.h"
43 #include "asterisk/linkedlists.h"
47 #define MAX_HEADERS 80
51 * 2005.05.27 - different versions of newt define the type of the buffer
52 * for the 5th argument to newtEntry() as char ** or const char ** . To
53 * let the code compile cleanly with -Werror, we cast it to void * through
56 #define _NEWT_CAST (void *)
58 #define DEFAULT_MANAGER_PORT 5038
61 unsigned int hdrcount
;
62 char headers
[MAX_HEADERS
][MAX_LEN
];
65 static struct ast_mansession
{
66 struct sockaddr_in sin
;
79 AST_LIST_ENTRY(ast_chan
) list
;
82 static AST_LIST_HEAD_NOLOCK_STATIC(chans
, ast_chan
);
84 /* dummy functions to be compatible with the Asterisk core for md5.c */
85 void ast_register_file_version(const char *file
, const char *version
);
86 void ast_register_file_version(const char *file
, const char *version
)
90 void ast_unregister_file_version(const char *file
);
91 void ast_unregister_file_version(const char *file
)
95 int ast_add_profile(const char *, uint64_t scale
);
96 int ast_add_profile(const char *s
, uint64_t scale
)
101 int64_t ast_profile(int, int64_t);
102 int64_t ast_profile(int key
, int64_t val
)
106 int64_t ast_mark(int, int start1_stop0
);
107 int64_t ast_mark(int key
, int start1_stop0
)
112 /* end of dummy functions */
114 static struct ast_chan
*find_chan(char *name
)
116 struct ast_chan
*chan
;
117 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
118 if (!strcmp(name
, chan
->name
))
121 chan
= malloc(sizeof(struct ast_chan
));
123 memset(chan
, 0, sizeof(struct ast_chan
));
124 strncpy(chan
->name
, name
, sizeof(chan
->name
) - 1);
125 AST_LIST_INSERT_TAIL(&chans
, chan
, list
);
130 static void del_chan(char *name
)
132 struct ast_chan
*chan
;
133 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans
, chan
, list
) {
134 if (!strcmp(name
, chan
->name
)) {
135 AST_LIST_REMOVE_CURRENT(&chans
, list
);
140 AST_LIST_TRAVERSE_SAFE_END
143 static void fdprintf(int fd
, char *fmt
, ...)
148 vsnprintf(stuff
, sizeof(stuff
), fmt
, ap
);
150 write(fd
, stuff
, strlen(stuff
));
153 static char *get_header(struct message
*m
, char *var
)
157 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
158 for (x
=0;x
<m
->hdrcount
;x
++)
159 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
160 return m
->headers
[x
] + strlen(cmp
);
164 static int event_newstate(struct ast_mansession
*s
, struct message
*m
)
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);
172 static int event_newexten(struct ast_mansession
*s
, struct message
*m
)
174 struct ast_chan
*chan
;
175 chan
= find_chan(get_header(m
, "Channel"));
176 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
177 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
178 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
182 static int event_newchannel(struct ast_mansession
*s
, struct message
*m
)
184 struct ast_chan
*chan
;
185 chan
= find_chan(get_header(m
, "Channel"));
186 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
187 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
191 static int event_status(struct ast_mansession
*s
, struct message
*m
)
193 struct ast_chan
*chan
;
194 chan
= find_chan(get_header(m
, "Channel"));
195 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
196 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
197 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
198 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
199 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
203 static int event_hangup(struct ast_mansession
*s
, struct message
*m
)
205 del_chan(get_header(m
, "Channel"));
209 static int event_ignore(struct ast_mansession
*s
, struct message
*m
)
214 static int event_rename(struct ast_mansession
*s
, struct message
*m
)
216 struct ast_chan
*chan
;
217 chan
= find_chan(get_header(m
, "Oldname"));
218 strncpy(chan
->name
, get_header(m
, "Newname"), sizeof(chan
->name
) - 1);
221 static struct event
{
223 int (*func
)(struct ast_mansession
*s
, struct message
*m
);
225 { "Newstate", event_newstate
},
226 { "Newchannel", event_newchannel
},
227 { "Newexten", event_newexten
},
228 { "Hangup", event_hangup
},
229 { "Rename", event_rename
},
230 { "Status", event_status
},
231 { "Link", event_ignore
},
232 { "Unlink", event_ignore
},
233 { "StatusComplete", event_ignore
}
236 static int process_message(struct ast_mansession
*s
, struct message
*m
)
240 strncpy(event
, get_header(m
, "Event"), sizeof(event
) - 1);
241 if (!strlen(event
)) {
242 fprintf(stderr
, "Missing event in request");
245 for (x
=0;x
<sizeof(events
) / sizeof(events
[0]);x
++) {
246 if (!strcasecmp(event
, events
[x
].event
)) {
247 if (events
[x
].func(s
, m
))
252 if (x
>= sizeof(events
) / sizeof(events
[0]))
253 fprintf(stderr
, "Ignoring unknown event '%s'", event
);
255 for (x
=0;x
<m
->hdrcount
;x
++) {
256 printf("Header: %s\n", m
->headers
[x
]);
262 static void rebuild_channels(newtComponent c
)
265 struct ast_chan
*chan
;
269 prev
= newtListboxGetCurrent(c
);
271 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
272 snprintf(tmpn
, sizeof(tmpn
), "%s (%s)", chan
->name
, chan
->callerid
);
273 if (strlen(chan
->exten
))
274 snprintf(tmp
, sizeof(tmp
), "%-30s %8s -> %s@%s:%s",
276 chan
->exten
, chan
->context
, chan
->priority
);
278 snprintf(tmp
, sizeof(tmp
), "%-30s %8s",
280 newtListboxAppendEntry(c
, tmp
, chan
);
284 newtListboxAppendEntry(c
, " << No Active Channels >> ", NULL
);
285 newtListboxSetCurrentByKey(c
, prev
);
288 static int has_input(struct ast_mansession
*s
)
291 for (x
=1;x
<s
->inlen
;x
++)
292 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r'))
297 static int get_input(struct ast_mansession
*s
, char *output
)
299 /* output must have at least sizeof(s->inbuf) space */
302 struct timeval tv
= {0, 0};
304 for (x
=1;x
<s
->inlen
;x
++) {
305 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
306 /* Copy output data up to and including \r\n */
307 memcpy(output
, s
->inbuf
, x
+ 1);
308 /* Add trailing \0 */
310 /* Move remaining data back to the front */
311 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
316 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
317 fprintf(stderr
, "Dumping long line with no return from %s: %s\n", inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
322 res
= select(s
->fd
+ 1, &fds
, NULL
, NULL
, &tv
);
324 fprintf(stderr
, "Select returned error: %s\n", strerror(errno
));
325 } else if (res
> 0) {
326 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
330 s
->inbuf
[s
->inlen
] = '\0';
337 static int input_check(struct ast_mansession
*s
, struct message
**mout
)
339 static struct message m
;
346 res
= get_input(s
, m
.headers
[m
.hdrcount
]);
349 fprintf(stderr
, "Got header: %s", m
.headers
[m
.hdrcount
]);
352 /* Strip trailing \r\n */
353 if (strlen(m
.headers
[m
.hdrcount
]) < 2)
355 m
.headers
[m
.hdrcount
][strlen(m
.headers
[m
.hdrcount
]) - 2] = '\0';
356 if (!strlen(m
.headers
[m
.hdrcount
])) {
357 if (mout
&& strlen(get_header(&m
, "Response"))) {
361 if (process_message(s
, &m
))
363 memset(&m
, 0, sizeof(&m
));
364 } else if (m
.hdrcount
< MAX_HEADERS
- 1)
366 } else if (res
< 0) {
374 static struct message
*wait_for_response(int timeout
)
381 tv
.tv_sec
= timeout
/ 1000;
382 tv
.tv_usec
= (timeout
% 1000) * 1000;
383 FD_SET(session
.fd
, &fds
);
384 res
= select(session
.fd
+ 1, &fds
, NULL
, NULL
, &tv
);
387 if (input_check(&session
, &m
) < 0) {
396 static int manager_action(char *action
, char *fmt
, ...)
398 struct ast_mansession
*s
;
403 fdprintf(s
->fd
, "Action: %s\r\n", action
);
405 vsnprintf(tmp
, sizeof(tmp
), fmt
, ap
);
407 write(s
->fd
, tmp
, strlen(tmp
));
408 fdprintf(s
->fd
, "\r\n");
412 static int show_message(char *title
, char *msg
)
417 struct newtExitStruct es
;
419 newtCenteredWindow(60,7, title
);
421 label
= newtLabel(4,1,msg
);
422 ok
= newtButton(27, 3, "OK");
423 form
= newtForm(NULL
, NULL
, 0);
424 newtFormAddComponents(form
, label
, ok
, NULL
);
425 newtFormRun(form
, &es
);
427 newtFormDestroy(form
);
431 static newtComponent showform
;
432 static int show_doing(char *title
, char *tmp
)
434 struct newtExitStruct es
;
436 showform
= newtForm(NULL
, NULL
, 0);
437 newtCenteredWindow(70,4, title
);
438 label
= newtLabel(3,1,tmp
);
439 newtFormAddComponents(showform
,label
, NULL
);
440 newtFormSetTimer(showform
, 200);
441 newtFormRun(showform
, &es
);
445 static int hide_doing(void)
448 newtFormDestroy(showform
);
452 static void try_status(void)
455 manager_action("Status", "");
456 m
= wait_for_response(10000);
458 show_message("Status Failed", "Timeout waiting for response");
459 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
460 show_message("Status Failed Failed", get_header(m
, "Message"));
465 static void try_hangup(newtComponent c
)
467 struct ast_chan
*chan
;
470 chan
= newtListboxGetCurrent(c
);
472 manager_action("Hangup", "Channel: %s\r\n", chan
->name
);
473 m
= wait_for_response(10000);
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"));
483 static int get_user_input(char *msg
, char *buf
, int buflen
)
487 newtComponent cancel
;
488 newtComponent inpfield
;
491 struct newtExitStruct es
;
493 newtCenteredWindow(60,7, msg
);
495 inpfield
= newtEntry(5, 2, "", 50, _NEWT_CAST
&input
, 0);
496 ok
= newtButton(22, 3, "OK");
497 cancel
= newtButton(32, 3, "Cancel");
498 form
= newtForm(NULL
, NULL
, 0);
499 newtFormAddComponents(form
, inpfield
, ok
, cancel
, NULL
);
500 newtFormRun(form
, &es
);
501 strncpy(buf
, input
, buflen
- 1);
507 newtFormDestroy(form
);
511 static void try_redirect(newtComponent c
)
513 struct ast_chan
*chan
;
520 chan
= newtListboxGetCurrent(c
);
522 strncpy(channame
, chan
->name
, sizeof(channame
) - 1);
523 snprintf(tmp
, sizeof(tmp
), "Enter new extension for %s", channame
);
524 if (get_user_input(tmp
, dest
, sizeof(dest
)))
526 if ((context
= strchr(dest
, '@'))) {
529 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
,context
,dest
);
531 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
, dest
);
533 m
= wait_for_response(10000);
535 show_message("Hangup Failed", "Timeout waiting for response");
536 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
537 show_message("Hangup Failed", get_header(m
, "Message"));
543 static int manage_calls(char *host
)
547 newtComponent hangup
;
548 newtComponent redirect
;
549 newtComponent channels
;
550 struct newtExitStruct es
;
553 /* Mark: If there's one thing you learn from this code, it is this...
554 Never, ever fly Air France. Their customer service is absolutely
555 the worst. I've never heard the words "That's not my problem" as
556 many times as I have from their staff -- It should, without doubt
557 be their corporate motto if it isn't already. Don't bother giving
558 them business because you're just a pain in their side and they
559 will be sure to let you know the first time you speak to them.
561 If you ever want to make me happy just tell me that you, too, will
562 never fly Air France again either (in spite of their excellent
565 Update by oej: The merger with KLM has transferred this
566 behaviour to KLM as well.
567 Don't bother giving them business either...
569 Only if you want to travel randomly without luggage, you
570 might pick either of them.
573 snprintf(tmp
, sizeof(tmp
), "Asterisk Manager at %s", host
);
574 newtCenteredWindow(74, 20, tmp
);
575 form
= newtForm(NULL
, NULL
, 0);
576 newtFormWatchFd(form
, session
.fd
, NEWT_FD_READ
);
577 newtFormSetTimer(form
, 100);
578 quit
= newtButton(62, 16, "Quit");
579 redirect
= newtButton(35, 16, "Redirect");
580 hangup
= newtButton(50, 16, "Hangup");
581 channels
= newtListbox(1,1,14, NEWT_FLAG_SCROLL
);
582 newtFormAddComponents(form
, channels
, redirect
, hangup
, quit
, NULL
);
583 newtListboxSetWidth(channels
, 72);
585 show_doing("Getting Status", "Retrieving system status...");
590 newtFormRun(form
, &es
);
591 if (has_input(&session
) || (es
.reason
== NEWT_EXIT_FDREADY
)) {
592 if (input_check(&session
, NULL
)) {
593 show_message("Disconnected", "Disconnected from remote host");
596 } else if (es
.reason
== NEWT_EXIT_COMPONENT
) {
599 if (es
.u
.co
== hangup
) {
600 try_hangup(channels
);
601 } else if (es
.u
.co
== redirect
) {
602 try_redirect(channels
);
605 rebuild_channels(channels
);
607 newtFormDestroy(form
);
611 static int login(char *hostname
)
614 newtComponent cancel
;
616 newtComponent username
;
617 newtComponent password
;
619 newtComponent ulabel
;
620 newtComponent plabel
;
624 struct newtExitStruct es
;
629 session
.fd
= socket(AF_INET
, SOCK_STREAM
, 0);
630 if (session
.fd
< 0) {
631 snprintf(tmp
, sizeof(tmp
), "socket() failed: %s\n", strerror(errno
));
632 show_message("Socket failed", tmp
);
636 snprintf(tmp
, sizeof(tmp
), "Looking up %s\n", hostname
);
637 show_doing("Connecting....", tmp
);
640 hp
= gethostbyname(hostname
);
642 snprintf(tmp
, sizeof(tmp
), "No such address: %s\n", hostname
);
643 show_message("Host lookup failed", tmp
);
647 snprintf(tmp
, sizeof(tmp
), "Connecting to %s", hostname
);
648 show_doing("Connecting...", tmp
);
650 session
.sin
.sin_family
= AF_INET
;
651 session
.sin
.sin_port
= htons(DEFAULT_MANAGER_PORT
);
652 memcpy(&session
.sin
.sin_addr
, hp
->h_addr
, sizeof(session
.sin
.sin_addr
));
654 if (connect(session
.fd
,(struct sockaddr
*)&session
.sin
, sizeof(session
.sin
))) {
655 snprintf(tmp
, sizeof(tmp
), "%s failed: %s\n", hostname
, strerror(errno
));
656 show_message("Connect Failed", tmp
);
662 login
= newtButton(5, 6, "Login");
663 cancel
= newtButton(25, 6, "Cancel");
664 newtCenteredWindow(40, 10, "Asterisk Manager Login");
665 snprintf(tmp
, sizeof(tmp
), "Host: %s", hostname
);
666 label
= newtLabel(4,1, tmp
);
668 ulabel
= newtLabel(4,2,"Username:");
669 plabel
= newtLabel(4,3,"Password:");
671 username
= newtEntry(14, 2, "", 20, _NEWT_CAST
&user
, 0);
672 password
= newtEntry(14, 3, "", 20, _NEWT_CAST
&pass
, NEWT_FLAG_HIDDEN
);
674 form
= newtForm(NULL
, NULL
, 0);
675 newtFormAddComponents(form
, username
, password
, login
, cancel
, label
, ulabel
, plabel
,NULL
);
676 newtFormRun(form
, &es
);
677 if (es
.reason
== NEWT_EXIT_COMPONENT
) {
678 if (es
.u
.co
== login
) {
679 snprintf(tmp
, sizeof(tmp
), "Logging in '%s'...", user
);
680 show_doing("Logging in", tmp
);
681 /* Check to see if the remote host supports MD5 Authentication */
682 manager_action("Challenge", "AuthType: MD5\r\n");
683 m
= wait_for_response(10000);
684 if (m
&& !strcasecmp(get_header(m
, "Response"), "Success")) {
685 char *challenge
= get_header(m
, "Challenge");
688 char md5key
[256] = "";
689 struct MD5Context md5
;
690 unsigned char digest
[16];
692 MD5Update(&md5
, (unsigned char *)challenge
, strlen(challenge
));
693 MD5Update(&md5
, (unsigned char *)pass
, strlen(pass
));
694 MD5Final(digest
, &md5
);
696 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
697 manager_action("Login",
702 m
= wait_for_response(10000);
704 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
707 show_message("Login Failed", get_header(m
, "Message"));
710 memset(m
, 0, sizeof(m
));
711 manager_action("Login",
715 m
= wait_for_response(10000);
718 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
721 show_message("Login Failed", get_header(m
, "Message"));
727 newtFormDestroy(form
);
731 int main(int argc
, char *argv
[])
734 fprintf(stderr
, "Usage: astman <host>\n");
739 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
740 newtPushHelpLine("Welcome to the Asterisk Manager!");
741 if (login(argv
[1])) {
745 manage_calls(argv
[1]);