36 #define G_LOG_DOMAIN "Dialogs.Ssh"
51 #include <sys/types.h>
82 #define SSH_CACHE_FILE "rofi-2.sshcache"
88 #define SSH_TOKEN_DELIM "= \t\r\n"
100 gchar *portstr = NULL;
101 if (entry->
port > 0) {
102 portstr = g_strdup_printf(
"%d", entry->
port);
105 entry->
hostname,
"{port}", portstr, (
char *)0);
108 gsize l = strlen(
"Connecting to '' via rofi") + strlen(entry->
hostname) + 1;
109 gchar *desc = g_newa(gchar, l);
111 g_snprintf(desc, l,
"Connecting to '%s' via rofi", entry->
hostname);
139 if (entry->
port > 0) {
140 char *store = g_strdup_printf(
"%s\x1F%d", entry->
hostname, entry->
port);
155 if (!host || !host[0]) {
173 unsigned int *length) {
174 FILE *fd = fopen(path,
"r");
177 size_t buffer_length = 0;
179 while (getline(&buffer, &buffer_length, fd) > 0) {
181 char *start = g_strstrip(&(buffer[0]));
183 if (*start ==
'#' || *start ==
'@') {
192 char *end = strstr(start,
" ");
199 start = strsep(&sep,
", ");
202 if (start[0] ==
'[') {
204 char *strend = strchr(start,
']');
205 if (strend[1] ==
':') {
208 gchar *endptr = NULL;
209 gint64 number = g_ascii_strtoll(&(strend[2]), &endptr, 10);
211 g_warning(
"Failed to parse port number: %s.", &(strend[2]));
212 }
else if (endptr == &(strend[2])) {
213 g_warning(
"Failed to parse port number: %s, invalid number.",
215 }
else if (number < 0 || number > 65535) {
216 g_warning(
"Failed to parse port number: %s, out of range.",
226 for (
unsigned int j = 0; j < (*length); j++) {
227 if (!g_ascii_strcasecmp(start, retv[j].hostname)) {
235 retv = g_realloc(retv, ((*length) + 2) *
sizeof(
SshEntry));
236 retv[(*length)].
hostname = g_strdup(start);
237 retv[(*length)].
port = port;
238 retv[(*length) + 1].
hostname = NULL;
239 retv[(*length) + 1].
port = 0;
242 start = strsep(&sep,
", ");
245 if (buffer != NULL) {
248 if (fclose(fd) != 0) {
249 g_warning(
"Failed to close hosts file: '%s'", g_strerror(errno));
252 g_debug(
"Failed to open KnownHostFile: '%s'", path);
268 FILE *fd = fopen(
"/etc/hosts",
"r");
271 size_t buffer_length = 0;
273 while (getline(&buffer, &buffer_length, fd) > 0) {
275 unsigned int index = 0, ti = 0;
276 char *token = buffer;
280 char c = buffer[index];
282 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\0' || c ==
'#') {
283 buffer[index] =
'\0';
285 if (token[0] !=
'\0') {
292 for (
unsigned int j = 0; j < (*length); j++) {
293 if (!g_ascii_strcasecmp(token, retv[j].hostname)) {
301 retv = g_realloc(retv, ((*length) + 2) *
sizeof(
SshEntry));
302 retv[(*length)].
hostname = g_strdup(token);
303 retv[(*length)].
port = 0;
304 retv[(*length) + 1].
hostname = NULL;
310 token = &buffer[index + 1];
318 }
while (buffer[index] !=
'\0' && buffer[index] !=
'#');
320 if (buffer != NULL) {
323 if (fclose(fd) != 0) {
324 g_warning(
"Failed to close hosts file: '%s'", g_strerror(errno));
335 g_debug(
"Add '%s' to UserKnownHost list", token);
338 g_debug(
"File '%s' already in UserKnownHostsFile list", token);
343 SshEntry **retv,
unsigned int *length,
344 unsigned int num_favorites) {
345 FILE *fd = fopen(filename,
"r");
347 g_debug(
"Parsing ssh config file: %s", filename);
350 size_t buffer_length = 0;
351 char *strtok_pointer = NULL;
352 while (getline(&buffer, &buffer_length, fd) > 0) {
361 if (!token || *token ==
'#') {
364 char *low_token = g_ascii_strdown(token, -1);
365 if (g_strcmp0(low_token,
"include") == 0) {
367 g_debug(
"Found Include: %s", token);
369 gchar *full_path = NULL;
370 if (!g_path_is_absolute(path)) {
371 char *dirname = g_path_get_dirname(filename);
372 full_path = g_build_filename(dirname, path, NULL);
375 full_path = g_strdup(path);
377 glob_t globbuf = {.gl_pathc = 0, .gl_pathv = NULL, .gl_offs = 0};
379 if (glob(full_path, 0, NULL, &globbuf) == 0) {
380 for (
size_t iter = 0; iter < globbuf.gl_pathc; iter++) {
389 }
else if (g_strcmp0(low_token,
"userknownhostsfile") == 0) {
391 g_debug(
"Found extra UserKnownHostsFile: %s", token);
394 }
else if (g_strcmp0(low_token,
"host") == 0) {
402 const char *
const sep =
"*?";
403 if (*token ==
'!' || strpbrk(token, sep)) {
416 for (
unsigned int j = 0; j < num_favorites; j++) {
417 if (!g_ascii_strcasecmp(token, (*retv)[j].hostname)) {
428 (*retv) = g_realloc((*retv), ((*length) + 2) *
sizeof(
SshEntry));
429 (*retv)[(*length)].hostname = g_strdup(token);
430 (*retv)[(*length)].port = 0;
431 (*retv)[(*length) + 1].hostname = NULL;
437 if (buffer != NULL) {
441 if (fclose(fd) != 0) {
442 g_warning(
"Failed to close ssh configuration file: '%s'",
458 unsigned int num_favorites = 0;
461 if (g_get_home_dir() == NULL) {
468 retv = malloc((*length) *
sizeof(
SshEntry));
469 for (
unsigned int i = 0; i < (*length); i++) {
471 char *portstr = strchr(h[i],
'\x1F');
472 if (portstr != NULL) {
475 gchar *endptr = NULL;
476 gint64 number = g_ascii_strtoll(&(portstr[1]), &endptr, 10);
478 g_warning(
"Failed to parse port number: %s.", &(portstr[1]));
479 }
else if (endptr == &(portstr[1])) {
480 g_warning(
"Failed to parse port number: %s, invalid number.",
482 }
else if (number < 0 || number > 65535) {
483 g_warning(
"Failed to parse port number: %s, out of range.",
495 num_favorites = (*length);
497 const char *hd = g_get_home_dir();
498 path = g_build_filename(hd,
".ssh",
"config", NULL);
502 char *known_hosts_path =
503 g_build_filename(g_get_home_dir(),
".ssh",
"known_hosts", NULL);
505 g_free(known_hosts_path);
507 iter = g_list_next(iter)) {
511 g_free(user_known_hosts_path);
580 unsigned int selected_line) {
615 G_GNUC_UNUSED
int *state,
616 G_GNUC_UNUSED GList **attr_list,
632 unsigned int index) {
638 .cfg_name_key =
"display-ssh",
645 ._get_completion = NULL,
646 ._preprocess_input = NULL,
647 .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