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.
26 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
41 #include "asterisk/md5.h"
42 #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 #if !defined(LOW_MEMORY)
96 int ast_add_profile(const char *, uint64_t scale
);
97 int ast_add_profile(const char *s
, uint64_t scale
)
102 int64_t ast_profile(int, int64_t);
103 int64_t ast_profile(int key
, int64_t val
)
107 int64_t ast_mark(int, int start1_stop0
);
108 int64_t ast_mark(int key
, int start1_stop0
)
112 #endif /* LOW_MEMORY */
114 /* end of dummy functions */
116 static struct ast_chan
*find_chan(char *name
)
118 struct ast_chan
*chan
;
119 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
120 if (!strcmp(name
, chan
->name
))
123 chan
= malloc(sizeof(struct ast_chan
));
125 memset(chan
, 0, sizeof(struct ast_chan
));
126 strncpy(chan
->name
, name
, sizeof(chan
->name
) - 1);
127 AST_LIST_INSERT_TAIL(&chans
, chan
, list
);
132 static void del_chan(char *name
)
134 struct ast_chan
*chan
;
136 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans
, chan
, list
) {
137 if (!strcmp(name
, chan
->name
)) {
138 AST_LIST_REMOVE_CURRENT(list
);
143 AST_LIST_TRAVERSE_SAFE_END
;
147 static void __attribute__((format (printf
, 2, 3))) fdprintf(int fd
, char *fmt
, ...)
152 vsnprintf(stuff
, sizeof(stuff
), fmt
, ap
);
154 write(fd
, stuff
, strlen(stuff
));
157 static char *get_header(struct message
*m
, char *var
)
161 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
162 for (x
=0;x
<m
->hdrcount
;x
++)
163 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
164 return m
->headers
[x
] + strlen(cmp
);
168 static int event_newstate(struct ast_mansession
*s
, struct message
*m
)
170 struct ast_chan
*chan
;
171 chan
= find_chan(get_header(m
, "Channel"));
172 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
176 static int event_newexten(struct ast_mansession
*s
, struct message
*m
)
178 struct ast_chan
*chan
;
179 chan
= find_chan(get_header(m
, "Channel"));
180 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
181 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
182 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
186 static int event_newchannel(struct ast_mansession
*s
, struct message
*m
)
188 struct ast_chan
*chan
;
189 chan
= find_chan(get_header(m
, "Channel"));
190 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
191 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
195 static int event_status(struct ast_mansession
*s
, struct message
*m
)
197 struct ast_chan
*chan
;
198 chan
= find_chan(get_header(m
, "Channel"));
199 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
200 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
201 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
202 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
203 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
207 static int event_hangup(struct ast_mansession
*s
, struct message
*m
)
209 del_chan(get_header(m
, "Channel"));
213 static int event_ignore(struct ast_mansession
*s
, struct message
*m
)
218 static int event_rename(struct ast_mansession
*s
, struct message
*m
)
220 struct ast_chan
*chan
;
221 chan
= find_chan(get_header(m
, "Oldname"));
222 strncpy(chan
->name
, get_header(m
, "Newname"), sizeof(chan
->name
) - 1);
225 static struct event
{
227 int (*func
)(struct ast_mansession
*s
, struct message
*m
);
229 { "Newstate", event_newstate
},
230 { "Newchannel", event_newchannel
},
231 { "Newexten", event_newexten
},
232 { "Hangup", event_hangup
},
233 { "Rename", event_rename
},
234 { "Status", event_status
},
235 { "Link", event_ignore
},
236 { "Unlink", event_ignore
},
237 { "StatusComplete", event_ignore
},
238 { "Dial", event_ignore
},
239 { "PeerStatus", event_ignore
},
240 { "MessageWaiting", event_ignore
},
241 { "Newcallerid", event_ignore
},
242 { "AGIExec", event_ignore
},
243 { "VarSet", event_ignore
},
244 { "MeetmeTalking", event_ignore
},
245 { "MeetmeJoin", event_ignore
},
246 { "MeetmeLeave", event_ignore
},
247 { "MeetmeEnd", event_ignore
},
248 { "MeetmeMute", event_ignore
},
249 { "Masquerade", event_ignore
},
252 static int process_message(struct ast_mansession
*s
, struct message
*m
)
256 strncpy(event
, get_header(m
, "Event"), sizeof(event
) - 1);
257 if (!strlen(event
)) {
258 fprintf(stderr
, "Missing event in request");
261 for (x
=0;x
<sizeof(events
) / sizeof(events
[0]);x
++) {
262 if (!strcasecmp(event
, events
[x
].event
)) {
263 if (events
[x
].func(s
, m
))
268 if (x
>= sizeof(events
) / sizeof(events
[0]))
269 fprintf(stderr
, "Ignoring unknown event '%s'", event
);
271 for (x
=0;x
<m
->hdrcount
;x
++) {
272 printf("Header: %s\n", m
->headers
[x
]);
278 static void rebuild_channels(newtComponent c
)
281 struct ast_chan
*chan
;
285 prev
= newtListboxGetCurrent(c
);
287 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
288 snprintf(tmpn
, sizeof(tmpn
), "%s (%s)", chan
->name
, chan
->callerid
);
289 if (strlen(chan
->exten
))
290 snprintf(tmp
, sizeof(tmp
), "%-30s %8s -> %s@%s:%s",
292 chan
->exten
, chan
->context
, chan
->priority
);
294 snprintf(tmp
, sizeof(tmp
), "%-30s %8s",
296 newtListboxAppendEntry(c
, tmp
, chan
);
300 newtListboxAppendEntry(c
, " << No Active Channels >> ", NULL
);
301 newtListboxSetCurrentByKey(c
, prev
);
304 static int has_input(struct ast_mansession
*s
)
307 for (x
=1;x
<s
->inlen
;x
++)
308 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r'))
313 static int get_input(struct ast_mansession
*s
, char *output
)
315 /* output must have at least sizeof(s->inbuf) space */
318 struct timeval tv
= {0, 0};
320 for (x
=1;x
<s
->inlen
;x
++) {
321 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
322 /* Copy output data up to and including \r\n */
323 memcpy(output
, s
->inbuf
, x
+ 1);
324 /* Add trailing \0 */
326 /* Move remaining data back to the front */
327 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
332 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
333 fprintf(stderr
, "Dumping long line with no return from %s: %s\n", inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
338 res
= select(s
->fd
+ 1, &fds
, NULL
, NULL
, &tv
);
340 fprintf(stderr
, "Select returned error: %s\n", strerror(errno
));
341 } else if (res
> 0) {
342 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
346 s
->inbuf
[s
->inlen
] = '\0';
353 static int input_check(struct ast_mansession
*s
, struct message
**mout
)
355 static struct message m
;
362 res
= get_input(s
, m
.headers
[m
.hdrcount
]);
365 fprintf(stderr
, "Got header: %s", m
.headers
[m
.hdrcount
]);
368 /* Strip trailing \r\n */
369 if (strlen(m
.headers
[m
.hdrcount
]) < 2)
371 m
.headers
[m
.hdrcount
][strlen(m
.headers
[m
.hdrcount
]) - 2] = '\0';
372 if (!strlen(m
.headers
[m
.hdrcount
])) {
373 if (mout
&& strlen(get_header(&m
, "Response"))) {
377 if (process_message(s
, &m
))
379 memset(&m
, 0, sizeof(&m
));
380 } else if (m
.hdrcount
< MAX_HEADERS
- 1)
382 } else if (res
< 0) {
390 static struct message
*wait_for_response(int timeout
)
397 tv
.tv_sec
= timeout
/ 1000;
398 tv
.tv_usec
= (timeout
% 1000) * 1000;
399 FD_SET(session
.fd
, &fds
);
400 res
= select(session
.fd
+ 1, &fds
, NULL
, NULL
, &tv
);
403 if (input_check(&session
, &m
) < 0) {
413 static int __attribute__((format (printf
, 2, 3))) manager_action(char *action
, char *fmt
, ...)
415 struct ast_mansession
*s
;
420 fdprintf(s
->fd
, "Action: %s\r\n", action
);
422 vsnprintf(tmp
, sizeof(tmp
), fmt
, ap
);
424 write(s
->fd
, tmp
, strlen(tmp
));
425 fdprintf(s
->fd
, "\r\n");
429 static int show_message(char *title
, char *msg
)
434 struct newtExitStruct es
;
436 newtCenteredWindow(60,7, title
);
438 label
= newtLabel(4,1,msg
);
439 ok
= newtButton(27, 3, "OK");
440 form
= newtForm(NULL
, NULL
, 0);
441 newtFormAddComponents(form
, label
, ok
, NULL
);
442 newtFormRun(form
, &es
);
444 newtFormDestroy(form
);
448 static newtComponent showform
;
449 static int show_doing(char *title
, char *tmp
)
451 struct newtExitStruct es
;
453 showform
= newtForm(NULL
, NULL
, 0);
454 newtCenteredWindow(70,4, title
);
455 label
= newtLabel(3,1,tmp
);
456 newtFormAddComponents(showform
,label
, NULL
);
457 newtFormSetTimer(showform
, 200);
458 newtFormRun(showform
, &es
);
462 static int hide_doing(void)
465 newtFormDestroy(showform
);
469 static void try_status(void)
472 manager_action("Status", "%s", "");
473 m
= wait_for_response(10000);
475 show_message("Status Failed", "Timeout waiting for response");
476 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
477 show_message("Status Failed Failed", get_header(m
, "Message"));
482 static void try_hangup(newtComponent c
)
484 struct ast_chan
*chan
;
487 chan
= newtListboxGetCurrent(c
);
489 manager_action("Hangup", "Channel: %s\r\n", chan
->name
);
490 m
= wait_for_response(10000);
492 show_message("Hangup Failed", "Timeout waiting for response");
493 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
494 show_message("Hangup Failed", get_header(m
, "Message"));
500 static int get_user_input(char *msg
, char *buf
, int buflen
)
504 newtComponent cancel
;
505 newtComponent inpfield
;
508 struct newtExitStruct es
;
510 newtCenteredWindow(60,7, msg
);
512 inpfield
= newtEntry(5, 2, "", 50, _NEWT_CAST
&input
, 0);
513 ok
= newtButton(22, 3, "OK");
514 cancel
= newtButton(32, 3, "Cancel");
515 form
= newtForm(NULL
, NULL
, 0);
516 newtFormAddComponents(form
, inpfield
, ok
, cancel
, NULL
);
517 newtFormRun(form
, &es
);
518 strncpy(buf
, input
, buflen
- 1);
524 newtFormDestroy(form
);
528 static void try_redirect(newtComponent c
)
530 struct ast_chan
*chan
;
537 chan
= newtListboxGetCurrent(c
);
539 strncpy(channame
, chan
->name
, sizeof(channame
) - 1);
540 snprintf(tmp
, sizeof(tmp
), "Enter new extension for %s", channame
);
541 if (get_user_input(tmp
, dest
, sizeof(dest
)))
543 if ((context
= strchr(dest
, '@'))) {
546 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
,context
,dest
);
548 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
, dest
);
550 m
= wait_for_response(10000);
552 show_message("Hangup Failed", "Timeout waiting for response");
553 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
554 show_message("Hangup Failed", get_header(m
, "Message"));
560 static int manage_calls(char *host
)
564 newtComponent hangup
;
565 newtComponent redirect
;
566 newtComponent channels
;
567 struct newtExitStruct es
;
570 /* Mark: If there's one thing you learn from this code, it is this...
571 Never, ever fly Air France. Their customer service is absolutely
572 the worst. I've never heard the words "That's not my problem" as
573 many times as I have from their staff -- It should, without doubt
574 be their corporate motto if it isn't already. Don't bother giving
575 them business because you're just a pain in their side and they
576 will be sure to let you know the first time you speak to them.
578 If you ever want to make me happy just tell me that you, too, will
579 never fly Air France again either (in spite of their excellent
582 Update by oej: The merger with KLM has transferred this
583 behaviour to KLM as well.
584 Don't bother giving them business either...
586 Only if you want to travel randomly without luggage, you
587 might pick either of them.
590 snprintf(tmp
, sizeof(tmp
), "Asterisk Manager at %s", host
);
591 newtCenteredWindow(74, 20, tmp
);
592 form
= newtForm(NULL
, NULL
, 0);
593 newtFormWatchFd(form
, session
.fd
, NEWT_FD_READ
);
594 newtFormSetTimer(form
, 100);
595 quit
= newtButton(62, 16, "Quit");
596 redirect
= newtButton(35, 16, "Redirect");
597 hangup
= newtButton(50, 16, "Hangup");
598 channels
= newtListbox(1,1,14, NEWT_FLAG_SCROLL
);
599 newtFormAddComponents(form
, channels
, redirect
, hangup
, quit
, NULL
);
600 newtListboxSetWidth(channels
, 72);
602 show_doing("Getting Status", "Retrieving system status...");
607 newtFormRun(form
, &es
);
608 if (has_input(&session
) || (es
.reason
== NEWT_EXIT_FDREADY
)) {
609 if (input_check(&session
, NULL
)) {
610 show_message("Disconnected", "Disconnected from remote host");
613 } else if (es
.reason
== NEWT_EXIT_COMPONENT
) {
616 if (es
.u
.co
== hangup
) {
617 try_hangup(channels
);
618 } else if (es
.u
.co
== redirect
) {
619 try_redirect(channels
);
622 rebuild_channels(channels
);
624 newtFormDestroy(form
);
628 static int login(char *hostname
)
631 newtComponent cancel
;
633 newtComponent username
;
634 newtComponent password
;
636 newtComponent ulabel
;
637 newtComponent plabel
;
641 struct newtExitStruct es
;
646 session
.fd
= socket(AF_INET
, SOCK_STREAM
, 0);
647 if (session
.fd
< 0) {
648 snprintf(tmp
, sizeof(tmp
), "socket() failed: %s\n", strerror(errno
));
649 show_message("Socket failed", tmp
);
653 snprintf(tmp
, sizeof(tmp
), "Looking up %s\n", hostname
);
654 show_doing("Connecting....", tmp
);
657 hp
= gethostbyname(hostname
);
659 snprintf(tmp
, sizeof(tmp
), "No such address: %s\n", hostname
);
660 show_message("Host lookup failed", tmp
);
664 snprintf(tmp
, sizeof(tmp
), "Connecting to %s", hostname
);
665 show_doing("Connecting...", tmp
);
667 session
.sin
.sin_family
= AF_INET
;
668 session
.sin
.sin_port
= htons(DEFAULT_MANAGER_PORT
);
669 memcpy(&session
.sin
.sin_addr
, hp
->h_addr
, sizeof(session
.sin
.sin_addr
));
671 if (connect(session
.fd
,(struct sockaddr
*)&session
.sin
, sizeof(session
.sin
))) {
672 snprintf(tmp
, sizeof(tmp
), "%s failed: %s\n", hostname
, strerror(errno
));
673 show_message("Connect Failed", tmp
);
679 login
= newtButton(5, 6, "Login");
680 cancel
= newtButton(25, 6, "Cancel");
681 newtCenteredWindow(40, 10, "Asterisk Manager Login");
682 snprintf(tmp
, sizeof(tmp
), "Host: %s", hostname
);
683 label
= newtLabel(4,1, tmp
);
685 ulabel
= newtLabel(4,2,"Username:");
686 plabel
= newtLabel(4,3,"Password:");
688 username
= newtEntry(14, 2, "", 20, _NEWT_CAST
&user
, 0);
689 password
= newtEntry(14, 3, "", 20, _NEWT_CAST
&pass
, NEWT_FLAG_HIDDEN
);
691 form
= newtForm(NULL
, NULL
, 0);
692 newtFormAddComponents(form
, username
, password
, login
, cancel
, label
, ulabel
, plabel
,NULL
);
693 newtFormRun(form
, &es
);
694 if (es
.reason
== NEWT_EXIT_COMPONENT
) {
695 if (es
.u
.co
== login
) {
696 snprintf(tmp
, sizeof(tmp
), "Logging in '%s'...", user
);
697 show_doing("Logging in", tmp
);
698 /* Check to see if the remote host supports MD5 Authentication */
699 manager_action("Challenge", "AuthType: MD5\r\n");
700 m
= wait_for_response(10000);
701 if (m
&& !strcasecmp(get_header(m
, "Response"), "Success")) {
702 char *challenge
= get_header(m
, "Challenge");
705 char md5key
[256] = "";
706 struct MD5Context md5
;
707 unsigned char digest
[16];
709 MD5Update(&md5
, (unsigned char *)challenge
, strlen(challenge
));
710 MD5Update(&md5
, (unsigned char *)pass
, strlen(pass
));
711 MD5Final(digest
, &md5
);
713 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
714 manager_action("Login",
719 m
= wait_for_response(10000);
721 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
724 show_message("Login Failed", get_header(m
, "Message"));
727 memset(m
, 0, sizeof(m
));
728 manager_action("Login",
732 m
= wait_for_response(10000);
735 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
738 show_message("Login Failed", get_header(m
, "Message"));
744 newtFormDestroy(form
);
748 int main(int argc
, char *argv
[])
751 fprintf(stderr
, "Usage: astman <host>\n");
756 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
757 newtPushHelpLine("Welcome to the Asterisk Manager!");
758 if (login(argv
[1])) {
762 manage_calls(argv
[1]);