36 #define G_LOG_DOMAIN "Dialogs.Ssh"
45 #include <sys/types.h>
84 #define SSH_CACHE_FILE "rofi-2.sshcache"
90 #define SSH_TOKEN_DELIM "= \t\r\n"
103 gchar *portstr = NULL;
104 if ( entry->
port > 0 ) {
105 portstr = g_strdup_printf (
"%d", entry->
port );
113 gsize l = strlen (
"Connecting to '' via rofi" ) + strlen ( entry->
hostname ) + 1;
114 gchar *desc = g_newa ( gchar, l );
116 g_snprintf ( desc, l,
"Connecting to '%s' via rofi", entry->
hostname );
145 if ( entry->
port > 0 ) {
146 char *store = g_strdup_printf (
"%s\x1F%d", entry->
hostname, entry->
port );
163 if ( !host || !host[0] ) {
182 FILE *fd = fopen ( path,
"r" );
185 size_t buffer_length = 0;
187 while ( getline ( &buffer, &buffer_length, fd ) > 0 ) {
189 char *start = g_strstrip ( &( buffer[0] ) );
191 if ( *start ==
'#' || *start ==
'@' ) {
195 if ( *start ==
'|' ) {
200 char *end = strstr ( start,
" " );
207 start = strsep ( &sep,
", " );
210 if ( start[0] ==
'[' ) {
212 char *end = strchr ( start,
']' );
213 if ( end[1] ==
':' ) {
216 gchar *endptr = NULL;
217 gint64 number = g_ascii_strtoll ( &( end[2] ), &endptr, 10 );
219 g_warning (
"Failed to parse port number: %s.", &( end[2] ) );
221 else if ( endptr == &( end[2] ) ) {
222 g_warning (
"Failed to parse port number: %s, invalid number.", &( end[2] ) );
224 else if ( number < 0 || number > 65535 ) {
225 g_warning (
"Failed to parse port number: %s, out of range.", &( end[2] ) );
235 for (
unsigned int j = 0; j < ( *length ); j++ ) {
236 if ( !g_ascii_strcasecmp ( start, retv[j].hostname ) ) {
244 retv = g_realloc ( retv, ( ( *length ) + 2 ) *
sizeof (
SshEntry ) );
245 retv[( *length )].
hostname = g_strdup ( start );
246 retv[( *length )].
port = port;
247 retv[( *length ) + 1].
hostname = NULL;
248 retv[( *length ) + 1].
port = 0;
251 start = strsep ( &sep,
", " );
254 if ( buffer != NULL ) {
257 if ( fclose ( fd ) != 0 ) {
258 g_warning (
"Failed to close hosts file: '%s'", g_strerror ( errno ) );
262 g_debug (
"Failed to open KnownHostFile: '%s'", path );
279 FILE *fd = fopen (
"/etc/hosts",
"r" );
282 size_t buffer_length = 0;
284 while ( getline ( &buffer, &buffer_length, fd ) > 0 ) {
286 unsigned int index = 0, ti = 0;
287 char *token = buffer;
291 char c = buffer[index];
293 if ( c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\0' || c ==
'#' ) {
294 buffer[index] =
'\0';
296 if ( token[0] !=
'\0' ) {
303 for (
unsigned int j = 0; j < ( *length ); j++ ) {
304 if ( !g_ascii_strcasecmp ( token, retv[j].hostname ) ) {
312 retv = g_realloc ( retv,
313 ( ( *length ) + 2 ) *
sizeof (
SshEntry ) );
314 retv[( *length )].
hostname = g_strdup ( token );
315 retv[( *length )].
port = 0;
316 retv[( *length ) + 1].
hostname = NULL;
322 token = &buffer[index + 1];
330 }
while ( buffer[index] !=
'\0' && buffer[index] !=
'#' );
332 if ( buffer != NULL ) {
335 if ( fclose ( fd ) != 0 ) {
336 g_warning (
"Failed to close hosts file: '%s'", g_strerror ( errno ) );
345 GList *item = g_list_find_custom ( pd->
user_known_hosts, token, (GCompareFunc) g_strcmp0 );
346 if ( item == NULL ) {
347 g_debug (
"Add '%s' to UserKnownHost list", token );
351 g_debug (
"File '%s' already in UserKnownHostsFile list", token );
357 FILE *fd = fopen ( filename,
"r" );
359 g_debug (
"Parsing ssh config file: %s", filename );
362 size_t buffer_length = 0;
363 char *strtok_pointer = NULL;
364 while ( getline ( &buffer, &buffer_length, fd ) > 0 ) {
373 if ( !token || *token ==
'#' ) {
376 char *low_token = g_ascii_strdown ( token, -1 );
377 if ( g_strcmp0 ( low_token,
"include" ) == 0 ) {
379 g_debug (
"Found Include: %s", token );
381 gchar *full_path = NULL;
382 if ( !g_path_is_absolute ( path ) ) {
383 char *dirname = g_path_get_dirname ( filename );
384 full_path = g_build_filename ( dirname, path, NULL );
388 full_path = g_strdup ( path );
390 glob_t globbuf = { .gl_pathc = 0, .gl_pathv = NULL, .gl_offs = 0 };
392 if ( glob ( full_path, 0, NULL, &globbuf ) == 0 ) {
393 for (
size_t iter = 0; iter < globbuf.gl_pathc; iter++ ) {
397 globfree ( &globbuf );
399 g_free ( full_path );
402 else if ( g_strcmp0 ( low_token,
"userknownhostsfile" ) == 0 ) {
403 while ( ( token = strtok_r ( NULL,
SSH_TOKEN_DELIM, &strtok_pointer ) ) ) {
404 g_debug (
"Found extra UserKnownHostsFile: %s", token );
408 else if ( g_strcmp0 ( low_token,
"host" ) == 0 ) {
414 while ( ( token = strtok_r ( NULL,
SSH_TOKEN_DELIM, &strtok_pointer ) ) ) {
416 const char *
const sep =
"*?";
417 if ( *token ==
'!' || strpbrk ( token, sep ) ) {
422 if ( *token ==
'#' ) {
430 for (
unsigned int j = 0; j < num_favorites; j++ ) {
431 if ( !g_ascii_strcasecmp ( token, ( *retv )[j].hostname ) ) {
442 ( *retv ) = g_realloc ( ( *retv ), ( ( *length ) + 2 ) *
sizeof (
SshEntry ) );
443 ( *retv )[( *length )].hostname = g_strdup ( token );
444 ( *retv )[( *length )].port = 0;
445 ( *retv )[( *length ) + 1].hostname = NULL;
449 g_free ( low_token );
451 if ( buffer != NULL ) {
455 if ( fclose ( fd ) != 0 ) {
456 g_warning (
"Failed to close ssh configuration file: '%s'", g_strerror ( errno ) );
472 unsigned int num_favorites = 0;
475 if ( g_get_home_dir () == NULL ) {
482 retv = malloc ( ( *length ) *
sizeof (
SshEntry ) );
483 for (
unsigned int i = 0; i < ( *length ); i++ ) {
485 char *portstr = strchr ( h[i],
'\x1F' );
486 if ( portstr != NULL ) {
489 gchar *endptr = NULL;
490 gint64 number = g_ascii_strtoll ( &( portstr[1] ), &endptr, 10 );
492 g_warning (
"Failed to parse port number: %s.", &( portstr[1] ) );
494 else if ( endptr == &( portstr[1] ) ) {
495 g_warning (
"Failed to parse port number: %s, invalid number.", &( portstr[1] ) );
497 else if ( number < 0 || number > 65535 ) {
498 g_warning (
"Failed to parse port number: %s, out of range.", &( portstr[1] ) );
510 num_favorites = ( *length );
512 const char *hd = g_get_home_dir ();
513 path = g_build_filename ( hd,
".ssh",
"config", NULL );
517 char *path = g_build_filename ( g_get_home_dir (),
".ssh",
"known_hosts", NULL );
520 for ( GList *iter = g_list_first ( pd->
user_known_hosts ); iter; iter = g_list_next ( iter ) ) {
571 if ( rmpd != NULL ) {
599 else if ( ( mretv &
MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] !=
'\0' ) {
625 static char *
_get_display_value (
const Mode *sw,
unsigned int selected_line, G_GNUC_UNUSED
int *state, G_GNUC_UNUSED GList **attr_list,
int get_entry )
649 .cfg_name_key =
"display-ssh",
656 ._get_completion = NULL,
657 ._preprocess_input = NULL,
658 .private_data = NULL,
gboolean helper_execute(const char *wd, char **args, const char *error_precmd, const char *error_cmd, RofiHelperExecuteContext *context)
int helper_parse_setup(char *string, char ***output, int *length,...)
char * rofi_expand_path(const char *input)
int helper_token_match(rofi_int_matcher *const *tokens, const char *input)
void history_set(const char *filename, const char *entry)
void history_remove(const char *filename, const char *entry)
char ** history_get_list(const char *filename, unsigned int *length)
void mode_set_private_data(Mode *mode, void *pd)
void * mode_get_private_data(const Mode *mode)
static int ssh_mode_init(Mode *sw)
static SshEntry * read_known_hosts_file(const char *path, SshEntry *retv, unsigned int *length)
static int ssh_token_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index)
static void add_known_hosts_file(SSHModePrivateData *pd, const char *token)
static void exec_ssh(const SshEntry *entry)
static char * _get_display_value(const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry)
static SshEntry * read_hosts_file(SshEntry *retv, unsigned int *length)
static int execshssh(const SshEntry *entry)
static void delete_ssh(const char *host)
static ModeMode ssh_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
struct _SshEntry SshEntry
static SshEntry * get_ssh(SSHModePrivateData *pd, unsigned int *length)
static void ssh_mode_destroy(Mode *sw)
static void parse_ssh_config_file(SSHModePrivateData *pd, const char *filename, SshEntry **retv, unsigned int *length, unsigned int num_favorites)
static unsigned int ssh_mode_get_num_entries(const Mode *sw)
unsigned int hosts_list_length
unsigned int parse_known_hosts