2 * transsip - the telephony toolkit
3 * By Daniel Borkmann <daniel@transsip.org>
4 * Copyright 2011, 2012 Daniel Borkmann <dborkma@tik.ee.ethz.ch>
5 * Subject to the GPL, version 2.
13 #include <readline/readline.h>
14 #include <readline/history.h>
21 #include "call_notifier.h"
24 #define MAX_MENU_ELEMS 100
28 static int exit_val
= 0;
30 volatile sig_atomic_t quit
= 0;
32 static char *prompt
= NULL
;
34 static size_t prompt_len
= 256;
36 extern volatile sig_atomic_t stun_done
;
38 extern int print_stun_probe(char *server
, int sport
, int tport
);
40 extern void init_cli_cmds(int ti
, int to
);
42 static struct shell_cmd show_node
[] = {
43 { "settings", cmd_help
, "Show settings", NULL
, },
44 { "pubkey", cmd_help
, "Show my public key", NULL
, },
45 { "contacts", cmd_help
, "Show my contacts", NULL
, },
46 { NULL
, NULL
, NULL
, NULL
, },
49 static struct shell_cmd import_node
[] = {
50 { "contact", cmd_help
, "Import a contact user/pubkey", NULL
, },
51 { NULL
, NULL
, NULL
, NULL
, },
54 static struct shell_cmd cmd_tree
[] = {
55 { "help", cmd_help
, "Show help", NULL
, },
56 { "quit", cmd_quit
, "Exit shell", NULL
, },
57 { "call", cmd_call
, "Perform a call", NULL
, },
58 { "hangup", cmd_hangup
, "Hangup the current call", NULL
, },
59 { "take", cmd_take
, "Take a call", NULL
, },
60 { "show", NULL
, "Show information", show_node
, },
61 { "import", NULL
, "Import things", import_node
, },
62 { NULL
, NULL
, NULL
, NULL
, },
65 static void fetch_user(char *user
, size_t len
)
67 int ret
= getlogin_r(user
, len
);
69 strlcpy(user
, "anon", len
);
73 static void fetch_host(char *host
, size_t len
)
75 int ret
= gethostname(host
, len
);
77 strlcpy(host
, "local", len
);
81 static void setup_prompt(char *prompt
, size_t len
, char *state
)
83 char user
[64], host
[64];
85 fetch_user(user
, sizeof(user
));
86 fetch_host(host
, sizeof(host
));
88 memset(prompt
, 0, len
);
89 slprintf(prompt
, len
, "\r%s@%s:%s> ", user
, host
, state
);
92 static void find_list(struct shell_cmd
**list
, const char *token
)
97 for (i
= 0; (cmd
= (*list
)[i
].name
); ++i
) {
98 if (strncmp(cmd
, token
, min(strlen(token
), strlen(cmd
))))
100 if (strlen(cmd
) != strlen(token
))
102 if ((*list
)[i
].sub_cmd
!= NULL
)
103 (*list
) = (*list
)[i
].sub_cmd
;
108 static struct shell_cmd
*find_elem(struct shell_cmd
*list
, const char *token
)
112 struct shell_cmd
*elem
;
117 for (i
= 0, elem
= NULL
; (cmd
= list
[i
].name
); ++i
) {
118 if (strncmp(cmd
, token
, min(strlen(token
), strlen(cmd
))))
126 static char *get_next_token(char *line
, int *off
)
131 while (line
[i
] && isspace(line
[i
]))
134 while (line
[i
] && !isspace(line
[i
]))
143 static struct shell_cmd
*walk_commands(char *line_buffer
, int len
, int ret
)
147 struct shell_cmd
*list
, *elem
= NULL
;
150 line
= xmalloc(len
+ 1);
151 strlcpy(line
, line_buffer
, len
+ 1);
153 while (list
&& len
> 0) {
154 token
= get_next_token(line
, &off
);
155 if (strlen(token
) == 0)
158 find_list(&list
, token
);
159 elem
= find_elem(list
, token
);
161 if (strlen(elem
->name
) == strlen(token
))
168 return (ret
== FETCH_ELEM
? elem
: list
);
171 static char **__cmd_complete_line(const char *text
, char *line_buffer
, int point
)
174 char *word
, *cmd
, **list
;
175 struct shell_cmd
*curr
;
177 word
= line_buffer
+ point
- strlen(text
);
178 curr
= walk_commands(line_buffer
, strlen(line_buffer
), FETCH_LIST
);
183 list
= xzmalloc(MAX_MENU_ELEMS
* sizeof(*list
));
185 for (i
= j
= 0; (cmd
= curr
[j
].name
); ++j
)
186 if (strncmp(cmd
, word
, min(wlen
, strlen(cmd
))) == 0)
187 list
[i
++] = xstrdup(curr
[j
].name
);
192 static char *cmd_line_completion(const char *text
, int matches
,
193 char *line_buffer
, int point
)
195 static char **list
= NULL
;
206 list
= __cmd_complete_line(text
, line_buffer
, point
);
218 char *cmd_completion(const char *text
, int matches
)
220 return cmd_line_completion(text
, matches
, rl_line_buffer
, rl_point
);
223 static int process_command(char *line
)
227 struct shell_cmd
*curr
;
229 curr
= walk_commands(line
, strlen(line
), FETCH_ELEM
);
230 if (!curr
|| curr
->callback
== NULL
)
233 ptr
= strstr(line
, curr
->name
);
236 ptr
+= strlen(curr
->name
);
238 while (ptr
[i
] && isspace(ptr
[i
]))
242 return curr
->callback(token
);
244 printf("Ooops, bad command! Try `help` for more information.\n");
248 void clear_term(int signal
)
253 rl_refresh_line(0, 0);
254 rl_free_line_state();
257 static void setup_readline(void)
259 rl_readline_name
= "transsip";
260 rl_completion_entry_function
= cmd_completion
;
261 rl_catch_signals
= 0;
262 rl_catch_sigwinch
= 1;
265 register_signal(SIGINT
, clear_term
);
268 static char *strip_white(char *line
)
272 for (l
= line
; isspace(*l
); l
++)
277 t
= l
+ strlen(l
) - 1;
278 while (t
> l
&& isspace(*t
))
285 static int call_event_hook_handle_changed(int num
)
288 case CALL_STATE_MACHINE_IDLE
:
289 setup_prompt(prompt
, prompt_len
, "idle");
291 case CALL_STATE_MACHINE_CALLIN
:
292 setup_prompt(prompt
, prompt_len
, "call-in");
294 case CALL_STATE_MACHINE_CALLOUT
:
295 setup_prompt(prompt
, prompt_len
, "call-out");
297 case CALL_STATE_MACHINE_SPEAKING
:
298 setup_prompt(prompt
, prompt_len
, "speaking");
307 static int call_event_hook(const struct event_block
*self
,
308 unsigned long event
, const void *arg
)
313 case CALL_STATE_MACHINE_CHANGED
: {
314 int num
= *(int *) arg
;
315 ret
= call_event_hook_handle_changed(num
);
324 struct event_block call_event
= {
326 .hook
= call_event_hook
,
329 static inline void print_shell_header(void)
331 printf("\n%s%s%s shell\n\n", colorize_start(bold
),
332 PROGNAME_STRING
" " VERSION_STRING
, colorize_end());
335 static inline void init_stun(void)
337 int ret
= print_stun_probe("stunserver.org", 3478, 30111);
339 printf("STUN failed!\n");
344 int cmd_help(char *args
)
346 struct shell_cmd
*cmd
;
347 int i
, entries
= (sizeof(cmd_tree
) / sizeof(cmd_tree
[0])) - 1;
350 for (i
= 0; i
< entries
; ++i
)
351 printf("%s - %s\n", cmd_tree
[i
].name
, cmd_tree
[i
].doc
);
355 cmd
= walk_commands(args
, strlen(args
), FETCH_ELEM
);
356 if (!cmd
|| !cmd
->doc
|| !cmd
->name
)
359 printf("%s - %s\n", cmd
->name
, cmd
->doc
);
363 int cmd_quit(char *args
)
369 void enter_shell_loop(int ti
, int to
)
373 init_cli_cmds(ti
, to
);
375 prompt
= xzmalloc(prompt_len
);
376 setup_prompt(prompt
, prompt_len
, "idle");
379 print_shell_header();
382 register_call_notifier(&call_event
);
385 line
= readline(prompt
);
392 cmd
= strip_white(line
);
395 exit_val
= process_command(cmd
);