36#define G_LOG_DOMAIN "Modes.Ssh"
83#define SSH_CACHE_FILE "rofi-2.sshcache"
89#define SSH_TOKEN_DELIM "= \t\r\n"
101 gchar *portstr = NULL;
102 if (entry->
port > 0) {
103 portstr = g_strdup_printf(
"%d", entry->
port);
106 entry->
hostname,
"{port}", portstr, (
char *)0);
109 gsize l = strlen(
"Connecting to '' via rofi") + strlen(entry->
hostname) + 1;
110 gchar *desc = g_newa(gchar, l);
112 g_snprintf(desc, l,
"Connecting to '%s' via rofi", entry->
hostname);
140 GString *str = g_string_new(entry->
hostname);
141 g_string_append_printf(str,
"\x1F%d", entry->
port);
143 g_string_append_printf(str,
"\x1F%s", entry->
aliases[x]);
146 g_string_free(str, TRUE);
156 if (!host || !host[0]) {
174 unsigned int *length) {
175 FILE *fd = fopen(path,
"r");
178 size_t buffer_length = 0;
180 while (getline(&buffer, &buffer_length, fd) > 0) {
182 char *start = g_strstrip(&(buffer[0]));
184 if (*start ==
'#' || *start ==
'@') {
193 char *end = strstr(start,
" ");
200 start = strsep(&sep,
", ");
203 if (start[0] ==
'[') {
205 char *strend = strchr(start,
']');
210 start = strsep(&sep,
", ");
213 if (strend[1] ==
':')
217 gchar *endptr = NULL;
218 gint64 number = g_ascii_strtoll(&(strend[2]), &endptr, 10);
220 g_warning(
"Failed to parse port number: %s.", &(strend[2]));
221 }
else if (endptr == &(strend[2])) {
222 g_warning(
"Failed to parse port number: %s, invalid number.",
224 }
else if (number < 0 || number > 65535) {
225 g_warning(
"Failed to parse port number: %s, out of range.",
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)].
aliases = NULL;
247 retv[(*length)].
port = port;
248 retv[(*length) + 1].
hostname = NULL;
249 retv[(*length) + 1].
aliases = NULL;
250 retv[(*length) + 1].
port = 0;
253 start = strsep(&sep,
", ");
256 if (buffer != NULL) {
259 if (fclose(fd) != 0) {
260 g_warning(
"Failed to close hosts file: '%s'", g_strerror(errno));
263 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, ((*length) + 2) *
sizeof(
SshEntry));
313 retv[(*length)].
hostname = g_strdup(token);
314 retv[(*length)].
aliases = NULL;
315 retv[(*length)].
port = 0;
316 retv[(*length) + 1].
hostname = NULL;
317 retv[(*length) + 1].
aliases = NULL;
323 token = &buffer[index + 1];
331 }
while (buffer[index] !=
'\0' && buffer[index] !=
'#');
333 if (buffer != NULL) {
336 if (fclose(fd) != 0) {
337 g_warning(
"Failed to close hosts file: '%s'", g_strerror(errno));
348 g_debug(
"Add '%s' to UserKnownHost list", token);
351 g_debug(
"File '%s' already in UserKnownHostsFile list", token);
356 SshEntry **retv,
unsigned int *length,
357 unsigned int num_favorites) {
358 FILE *fd = fopen(filename,
"r");
360 g_debug(
"Parsing ssh config file: %s", filename);
363 size_t buffer_length = 0;
364 char *strtok_pointer = NULL;
365 while (getline(&buffer, &buffer_length, fd) > 0) {
374 if (!token || *token ==
'#') {
377 char *low_token = g_ascii_strdown(token, -1);
378 if (g_strcmp0(low_token,
"include") == 0) {
380 g_debug(
"Found Include: %s", token);
382 gchar *full_path = NULL;
383 if (!g_path_is_absolute(path)) {
384 char *dirname = g_path_get_dirname(filename);
385 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++) {
402 }
else if (g_strcmp0(low_token,
"userknownhostsfile") == 0) {
404 g_debug(
"Found extra UserKnownHostsFile: %s", token);
407 }
else if (g_strcmp0(low_token,
"host") == 0) {
416 const char *
const sep =
"*?";
417 if (*token ==
'!' || strpbrk(token, sep)) {
430 for (
unsigned int j = 0; j < num_favorites; j++) {
431 if (!g_ascii_strcasecmp(token, (*retv)[j].hostname)) {
435 if ((*retv)[j].aliases != NULL) {
436 for (
int x = 0; (*retv)[j].aliases[x] != NULL; x++) {
437 if (!g_ascii_strcasecmp(token, (*retv)[j].aliases[x])) {
451 (*retv) = g_realloc((*retv), ((*length) + 2) *
sizeof(
SshEntry));
452 (*retv)[(*length)].hostname = g_strdup(token);
453 (*retv)[(*length)].aliases = NULL;
454 (*retv)[(*length)].port = 0;
455 (*retv)[(*length) + 1].hostname = NULL;
456 (*retv)[(*length) + 1].aliases = NULL;
460 int index = (*length) - 1;
462 if ((*retv)[index].aliases != NULL) {
463 l = g_strv_length((*retv)[index].aliases);
465 (*retv)[index].aliases =
466 g_realloc((*retv)[index].aliases, (l + 2) *
sizeof(
char *));
467 (*retv)[index].aliases[l] = g_strdup(token);
468 (*retv)[index].aliases[l + 1] = NULL;
474 if (buffer != NULL) {
478 if (fclose(fd) != 0) {
479 g_warning(
"Failed to close ssh configuration file: '%s'",
495 unsigned int num_favorites = 0;
498 if (g_get_home_dir() == NULL) {
505 retv = g_malloc0((*length) *
sizeof(
SshEntry));
506 for (
unsigned int i = 0; i < (*length); i++) {
508 char **ssplit = g_strsplit(h[i],
"\x1F", -1);
509 guint ssplit_len = g_strv_length(ssplit);
510 if (ssplit_len > 1) {
512 gchar *endptr = NULL;
513 gint64 number = g_ascii_strtoll(ssplit[1], &endptr, 10);
515 g_warning(
"Failed to parse port number: %s.", (ssplit[1]));
516 }
else if (endptr == (ssplit[1])) {
517 g_warning(
"Failed to parse port number: %s, invalid number.",
519 }
else if (number < 0 || number > 65535) {
520 g_warning(
"Failed to parse port number: %s, out of range.",
526 retv[i].
hostname = g_strdup(ssplit[0]);
529 gint naliases = ssplit_len - 2;
531 retv[i].
aliases = g_malloc0(
sizeof(
char *) * (naliases + 1));
532 for (gint j = 0; j < naliases; j++) {
533 retv[i].
aliases[j] = g_strdup(ssplit[2 + j]);
541 num_favorites = (*length);
543 const char *hd = g_get_home_dir();
544 path = g_build_filename(hd,
".ssh",
"config", NULL);
547 if (
config.parse_known_hosts == TRUE) {
548 char *known_hosts_path =
549 g_build_filename(g_get_home_dir(),
".ssh",
"known_hosts", NULL);
551 g_free(known_hosts_path);
553 iter = g_list_next(iter)) {
557 g_free(user_known_hosts_path);
560 if (
config.parse_hosts == TRUE) {
627 unsigned int selected_line) {
634 SshEntry entry = {.hostname = *input, .port = 0};
662 G_GNUC_UNUSED
int *state,
663 G_GNUC_UNUSED GList **attr_list,
670 g_string_append(str,
" (");
672 g_string_append_printf(str,
" %s",
675 g_string_append(str,
",");
678 g_string_append(str,
" )");
681 char *cstr = str->str;
682 g_string_free(str, FALSE);
699 unsigned int index) {
711 .cfg_name_key =
"display-ssh",
718 ._get_completion = NULL,
719 ._preprocess_input = NULL,
720 .private_data = NULL,
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)
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_get_private_data(const Mode *mode)
void mode_set_private_data(Mode *mode, void *pd)
static int ssh_mode_init(Mode *sw)
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 int execshssh(const SshEntry *entry)
static void delete_ssh(const char *host)
static SshEntry * read_known_hosts_file(const char *path, SshEntry *retv, unsigned int *length)
static SshEntry * read_hosts_file(SshEntry *retv, unsigned int *length)
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 ModeMode ssh_mode_result(Mode *sw, int mretv, char **input, unsigned int selected_line)
static SshEntry * get_ssh(SSHModePrivateData *pd, unsigned int *length)
struct _SshEntry SshEntry
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)
struct rofi_int_matcher_t rofi_int_matcher
unsigned int hosts_list_length