52 #include <glib/gi18n.h> 53 #include <glib/gstdio.h> 67 #define MIN_WINDOW_WIDTH 10 68 #define MIN_WINDOW_HEIGHT 10 70 #define KEYFILE_GROUP_REMMINA "remmina" 71 #define KEYFILE_GROUP_STATE "Remmina Connection States" 73 static struct timespec
times[2];
82 remminafile->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
83 remminafile->states = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
87 remminafile->spsettings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
88 remminafile->prevent_saving = FALSE;
102 g_free(remminafile->filename);
103 remminafile->filename = NULL;
116 TRACE_CALL(__func__);
127 gchar *invalid_chars =
"\\%|/$?<>:*. \"";
128 GString *filenamestr;
149 filenamestr = g_string_new(g_strdup_printf(
"%s",
152 if (g_strstr_len(filenamestr->str, -1,
"%N") != NULL)
156 if (g_strstr_len(filenamestr->str, -1,
"%G") != NULL)
160 if (g_strstr_len(filenamestr->str, -1,
"%P") != NULL)
164 if (g_strstr_len(filenamestr->str, -1,
"%h") != NULL)
169 g_autofree gchar *filename = g_strdelimit(g_ascii_strdown(g_strstrip(g_string_free(filenamestr, FALSE)), -1),
177 remminafile->filename = NULL;
184 TRACE_CALL(__func__);
185 g_free(remminafile->filename);
186 remminafile->filename = g_strdup(filename);
191 TRACE_CALL(__func__);
196 g_free(remminafile->statefile);
198 gchar *basename = g_path_get_basename(remminafile->filename);
199 gchar *
cachedir = g_build_path(
"/", g_get_user_cache_dir(),
"remmina", NULL);
200 GString *fname = g_string_new(basename);
202 remminafile->statefile = g_strdup_printf(
"%s/%s.state", cachedir, fname->str);
205 g_string_free(fname, TRUE);
212 TRACE_CALL(__func__);
213 return remminafile->filename;
219 TRACE_CALL(__func__);
224 buf = g_strdup_printf(
"COPY %s",
237 TRACE_CALL(__func__);
240 if (protocol_plugin == NULL)
271 src_key = g_strdup_printf(
"ssh_%s", suffix);
272 dst_key = g_strdup_printf(
"ssh_tunnel_%s", suffix);
281 if (ssh_enabled && val && val[0] != 0)
284 if (!protocol_is_ssh)
293 TRACE_CALL(__func__);
295 gboolean protocol_is_ssh;
296 gboolean ssh_enabled;
304 protocol_is_ssh = (strcmp(val,
"SSH") == 0);
352 TRACE_CALL(__func__);
359 gboolean secret_service_available;
362 gkeyfile = g_key_file_new();
364 if (g_file_test(filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) {
365 if (!g_key_file_load_from_file(gkeyfile, filename, G_KEY_FILE_NONE, NULL)) {
366 g_key_file_free(gkeyfile);
367 REMMINA_DEBUG(
"Unable to load remmina profile file %s: g_key_file_load_from_file() returned NULL.\n", filename);
372 if (!g_key_file_has_key(gkeyfile, KEYFILE_GROUP_REMMINA,
"name", NULL)) {
374 REMMINA_DEBUG(
"Unable to load remmina profile file %s: cannot find key name= in section remmina.\n", filename);
378 g_key_file_free(gkeyfile);
384 protocol_plugin = NULL;
387 gchar *proto = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA,
"protocol", NULL);
396 remminafile->filename = g_strdup(filename);
400 gchar **keys = g_key_file_get_keys(gkeyfile, KEYFILE_GROUP_REMMINA, &nkeys, &err);
404 for (keyindex = 0; keyindex < nkeys; ++keyindex) {
405 key = keys[keyindex];
411 s = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
417 #if SODIUM_VERSION_INT >= 90200 427 if ((g_strcmp0(s,
".") == 0) && (secret_service_available)) {
428 gchar *sec = secret_plugin->
get_password(secret_plugin, remminafile, key);
431 g_hash_table_insert(remminafile->spsettings, g_strdup(key), NULL);
442 if (strcmp(key,
"resolution") == 0) {
443 gchar *resolution_str = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
452 g_free(resolution_str);
455 value = g_key_file_get_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, NULL);
465 g_key_file_free(gkeyfile);
471 TRACE_CALL(__func__);
481 d->
func = FUNC_FILE_SET_STRING;
492 if (strcmp(setting,
"resolution") == 0) {
494 const gchar *message = _(
"Using the «resolution» parameter in the Remmina preferences file is deprecated.\n");
495 REMMINA_CRITICAL(message);
499 g_hash_table_insert(remminafile->settings, g_strdup(setting), g_strdup(value));
501 g_hash_table_insert(remminafile->settings, g_strdup(setting), g_strdup(
""));
507 TRACE_CALL(__func__);
509 if (value && value[0] != 0)
510 g_hash_table_insert(remminafile->states, g_strdup(setting), g_strdup(value));
512 g_hash_table_insert(remminafile->states, g_strdup(setting), g_strdup(
""));
518 TRACE_CALL(__func__);
529 d->
func = FUNC_FILE_GET_STRING;
538 if (strcmp(setting,
"resolution") == 0) {
540 const gchar *message = _(
"Using the «resolution» parameter in the Remmina preferences file is deprecated.\n");
541 REMMINA_CRITICAL(message);
546 value = (gchar *)g_hash_table_lookup(remminafile->settings, setting);
547 return value && value[0] ? value : NULL;
553 TRACE_CALL(__func__);
557 g_warning(
"remmina_file_get_secret(remminafile,“%s”) is deprecated and must not be called. Use remmina_file_get_string() and do not deallocate returned memory.\n", setting);
566 gchar *date_str = NULL;
568 fmt_str = g_string_new(setting);
576 now = g_date_time_new_now_local();
577 date_str = g_date_time_format(now,
"%FT%TZ");
581 res = g_string_free(fmt_str, FALSE);
587 TRACE_CALL(__func__);
589 g_hash_table_insert(remminafile->settings,
591 g_strdup_printf(
"%i", value));
596 TRACE_CALL(__func__);
598 g_hash_table_insert(remminafile->states,
600 g_strdup_printf(
"%i", value));
605 TRACE_CALL(__func__);
609 value = g_hash_table_lookup(remminafile->settings, setting);
610 r = value == NULL ? default_value : (value[0] ==
't' ? TRUE : atoi(value));
617 TRACE_CALL(__func__);
621 value = g_hash_table_lookup(remminafile->states, setting);
622 r = value == NULL ? default_value : (value[0] ==
't' ? TRUE : atoi(value));
630 const gchar * setting,
631 gdouble default_value)
633 TRACE_CALL(__func__);
636 value = g_hash_table_lookup(remminafile->settings, setting);
638 return default_value;
643 gint ret = sscanf(value,
"%lf", &d);
656 const gchar * setting,
657 gdouble default_value)
659 TRACE_CALL(__func__);
662 value = g_hash_table_lookup(remminafile->states, setting);
664 return default_value;
669 gint ret = sscanf(value,
"%lf", &d);
682 TRACE_CALL(__func__);
685 if (remminafile->filename == NULL)
687 gkeyfile = g_key_file_new();
688 if (!g_key_file_load_from_file(gkeyfile, remminafile->filename, G_KEY_FILE_NONE, NULL)) {
697 TRACE_CALL(__func__);
700 if (remminafile->statefile == NULL)
702 gkeyfile = g_key_file_new();
703 if (!g_key_file_load_from_file(gkeyfile, remminafile->statefile, G_KEY_FILE_NONE, NULL)) {
711 TRACE_CALL(__func__);
712 if (remminafile == NULL)
715 if (remminafile->filename)
716 g_free(remminafile->filename);
717 if (remminafile->statefile)
718 g_free(remminafile->statefile);
719 if (remminafile->settings)
720 g_hash_table_destroy(remminafile->settings);
721 if (remminafile->spsettings)
722 g_hash_table_destroy(remminafile->spsettings);
723 if (remminafile->states)
724 g_hash_table_destroy(remminafile->states);
732 TRACE_CALL(__func__);
734 gboolean secret_service_available;
737 const gchar *key, *value;
738 gchar *s, *proto, *content;
745 if (remminafile->prevent_saving)
754 REMMINA_DEBUG(
"Saving profile");
758 proto = (gchar *)g_hash_table_lookup(remminafile->settings,
"protocol");
762 REMMINA_CRITICAL(
"Saving settings for unknown protocol:", proto);
763 protocol_plugin = NULL;
769 g_hash_table_iter_init(&iter, remminafile->settings);
770 while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
772 if (remminafile->filename && g_strcmp0(remminafile->filename,
remmina_pref_file)) {
773 if (secret_service_available && nopasswdsave == 0) {
774 REMMINA_DEBUG(
"We have a secret and disablepasswordstoring=0");
775 if (value && value[0]) {
776 if (g_strcmp0(value,
".") != 0)
777 secret_plugin->
store_password(secret_plugin, remminafile, key, value);
778 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key,
".");
780 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key,
"");
784 REMMINA_DEBUG(
"We have a password and disablepasswordstoring=0");
785 if (value && value[0] && nopasswdsave == 0) {
787 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, s);
790 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key,
"");
793 if (secret_service_available && nopasswdsave == 1) {
794 if (value && value[0]) {
795 if (g_strcmp0(value,
".") != 0) {
796 REMMINA_DEBUG(
"Deleting the secret in the keyring as disablepasswordstoring=1");
798 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key,
".");
804 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_REMMINA, key, value);
809 g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA,
"resolution", NULL);
812 g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA,
"ssh_enabled", NULL);
813 g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA,
"save_ssh_server", NULL);
814 g_key_file_remove_key(gkeyfile, KEYFILE_GROUP_REMMINA,
"save_ssh_username", NULL);
817 content = g_key_file_to_data(gkeyfile, &length, NULL);
819 if (g_file_set_contents(remminafile->filename, content, length, &err))
820 REMMINA_DEBUG(
"Profile saved");
822 REMMINA_WARNING(
"Remmina connection profile cannot be saved, with error %d (%s)", err->code, err->message);
826 g_free(content), content = NULL;
828 g_hash_table_iter_init(&iter, remminafile->states);
829 while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
830 g_key_file_set_string(gkeyfile, KEYFILE_GROUP_STATE, key, value);
831 content = g_key_file_to_data(gkeystate, &length, NULL);
832 if (g_file_set_contents(remminafile->statefile, content, length, &err))
833 REMMINA_DEBUG(
"Connection profile states saved");
835 REMMINA_WARNING(
"Remmina connection profile cannot be saved, with error %d (%s)", err->code, err->message);
838 g_free(content), content = NULL;
839 g_key_file_free(gkeyfile);
840 g_key_file_free(gkeystate);
848 TRACE_CALL(__func__);
855 if (g_hash_table_lookup_extended(remminafile->spsettings, g_strdup(key), NULL, NULL)) {
867 TRACE_CALL(__func__);
870 const gchar *key, *value;
873 dupfile->filename = g_strdup(remminafile->filename);
875 g_hash_table_iter_init(&iter, remminafile->settings);
876 while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
887 TRACE_CALL(__func__);
893 return g_strconcat(REMMINA_APP_ID,
"-symbolic", NULL);
901 TRACE_CALL(__func__);
905 g_free(tmp->filename);
906 tmp->filename = NULL;
913 TRACE_CALL(__func__);
927 TRACE_CALL(__func__);
928 g_autoptr(GError) error = NULL;
929 g_autoptr(GKeyFile) key_file = g_key_file_new();
931 if (!g_key_file_load_from_file(key_file, remminafile->statefile, G_KEY_FILE_NONE, &error)) {
932 if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
933 REMMINA_CRITICAL(
"Could not load the state file. %s", error->message);
937 g_autofree gchar *val = g_key_file_get_string(key_file, KEYFILE_GROUP_STATE, setting, &error);
940 !g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
941 REMMINA_CRITICAL(
"Could not find \"%s\" in the \"%s\" state file. %s",
942 setting, remminafile->statefile, error->message);
945 return val && val[0] ? val : NULL;
950 TRACE_CALL(__func__);
952 g_autoptr(GKeyFile) key_statefile = g_key_file_new();
953 g_autoptr(GKeyFile) key_remminafile = g_key_file_new();
954 GError *error = NULL;
956 const gchar *date = NULL;
957 GDateTime *d = g_date_time_new_now_utc();
959 date = g_strdup_printf(
"%d%02d%02d",
960 g_date_time_get_year(d),
961 g_date_time_get_month(d),
962 g_date_time_get_day_of_month(d));
964 g_key_file_set_string(key_statefile, KEYFILE_GROUP_STATE,
"last_success", date);
966 REMMINA_DEBUG(
"State file %s.", remminafile->statefile);
967 if (!g_key_file_save_to_file(key_statefile, remminafile->statefile, &error)) {
968 REMMINA_CRITICAL(
"Could not save the key file. %s", error->message);
974 g_key_file_remove_key(key_remminafile, KEYFILE_GROUP_REMMINA,
"last_success", NULL);
975 REMMINA_DEBUG(
"Last connection made on %s.", date);
982 TRACE_CALL(__func__);
987 protocol_plugin = NULL;
991 proto = (gchar *)g_hash_table_lookup(remminafile->settings,
"protocol");
994 if (protocol_plugin) {
999 if (setting_iter->
name == NULL)
1000 g_error(
"Internal error: a setting name in protocol plugin %s is null. Please fix RemminaProtocolSetting struct content.", proto);
1033 TRACE_CALL(__func__);
1040 char time_string[256];
1045 if (remminafile->statefile)
1047 file = g_file_new_for_path(remminafile->statefile);
1049 file = g_file_new_for_path(remminafile->filename);
1051 info = g_file_query_info(file,
1052 G_FILE_ATTRIBUTE_TIME_MODIFIED,
1053 G_FILE_QUERY_INFO_NONE,
1057 g_object_unref(file);
1068 tmps = g_strconcat(last_success,
"T00:00:00Z", NULL);
1069 dt = g_date_time_new_from_iso8601(tmps, NULL);
1073 tmps = g_date_time_format(dt,
"%s");
1074 mtime = g_ascii_strtoull(tmps, NULL, 10);
1076 g_date_time_unref(dt);
1083 mtime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1084 g_object_unref(info);
1089 ptm = localtime(&tv.tv_sec);
1090 strftime(time_string,
sizeof(time_string),
"%F - %T", ptm);
1092 gchar *modtime_string = g_locale_to_utf8(time_string, -1, NULL, NULL, NULL);
1094 return modtime_string;
1107 TRACE_CALL(__func__);
1112 if ((r = stat(remminafile->statefile, &st)) < 0) {
1113 if (errno != ENOENT)
1114 REMMINA_DEBUG(
"stat %s:", remminafile->statefile);
1117 times[0] = st.st_atimespec;
1118 times[1] = st.st_mtimespec;
1120 times[0] = st.st_atim;
1121 times[1] = st.st_mtim;
1123 if (utimensat(AT_FDCWD, remminafile->statefile,
times, 0) < 0)
1124 REMMINA_DEBUG(
"utimensat %s:", remminafile->statefile);
1128 if ((fd = open(remminafile->statefile, O_CREAT | O_EXCL, 0644)) < 0)
1129 REMMINA_DEBUG(
"open %s:", remminafile->statefile);
RemminaFile * remmina_file_load(const gchar *filename)
const gchar * remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting)
void remmina_file_free(RemminaFile *remminafile)
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting)
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
RemminaFile * remmina_file_dup(RemminaFile *remminafile)
gchar * remmina_pref_file
const gchar * remmina_file_get_filename(RemminaFile *remminafile)
gchar * remmina_crypt_decrypt(const gchar *str)
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
static struct timespec times[2]
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
static GKeyFile * remmina_file_get_keystate(RemminaFile *remminafile)
gchar * remmina_file_get_secret(RemminaFile *remminafile, const gchar *setting)
RemminaSecretPlugin * remmina_plugin_manager_get_secret_plugin(void)
gboolean(* is_service_available)(struct _RemminaSecretPlugin *instance)
void(* delete_password)(struct _RemminaSecretPlugin *instance, RemminaFile *remminafile, const gchar *key)
gchar * remmina_file_get_datetime(RemminaFile *remminafile)
Return the string date of the last time a Remmina state file has been modified.
static void upgrade_sshkeys_202001(RemminaFile *remminafile)
gdouble remmina_file_get_double(RemminaFile *remminafile, const gchar *setting, gdouble default_value)
void remmina_file_generate_filename(RemminaFile *remminafile)
Generate a new Remmina connection profile file name.
const gchar * remmina_file_get_state(RemminaFile *remminafile, const gchar *setting)
int remmina_public_split_resolution_string(const char *resolution_string, int *w, int *h)
gchar * remmina_file_format_properties(RemminaFile *remminafile, const gchar *setting)
const RemminaProtocolSetting * find_protocol_setting(const gchar *name, RemminaProtocolPlugin *protocol_plugin)
void remmina_main_update_file_datetime(RemminaFile *file)
const gchar * icon_name_ssh
gint remmina_file_get_state_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
void remmina_file_state_last_success(RemminaFile *remminafile)
General utility functions, non-GTK related.
void remmina_file_set_statefile(RemminaFile *remminafile)
void remmina_file_set_filename(RemminaFile *remminafile, const gchar *filename)
gboolean remmina_masterthread_exec_is_main_thread()
gchar * remmina_crypt_encrypt(const gchar *str)
gdouble remmina_file_get_state_double(RemminaFile *remminafile, const gchar *setting, gdouble default_value)
void remmina_main_show_warning_dialog(const gchar *message)
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
static GKeyFile * remmina_file_get_keyfile(RemminaFile *remminafile)
void remmina_file_delete(const gchar *filename)
const RemminaProtocolSetting * basic_settings
struct remmina_masterthread_exec_data::@12::@17 file_set_string
struct remmina_masterthread_exec_data::@12::@16 file_get_string
gchar * remmina_file_get_datadir(void)
Return datadir_path from pref or first found data dir as per XDG specs.
void remmina_file_touch(RemminaFile *remminafile)
Update the atime and mtime of a given filename.
gboolean list_refresh_workaround
RemminaProtocolSettingType type
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
RemminaFile * remmina_file_new(void)
const gchar * remmina_file_name
void remmina_file_set_state(RemminaFile *remminafile, const gchar *setting, const gchar *value)
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
void remmina_file_store_secret_plugin_password(RemminaFile *remminafile, const gchar *key, const gchar *value)
RemminaPlugin * remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name)
const RemminaProtocolSetting * advanced_settings
union remmina_masterthread_exec_data::@12 p
gchar *(* get_password)(struct _RemminaSecretPlugin *instance, RemminaFile *remminafile, const gchar *key)
void(* store_password)(struct _RemminaSecretPlugin *instance, RemminaFile *remminafile, const gchar *key, const gchar *password)
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
void remmina_file_save(RemminaFile *remminafile)
RemminaFile * remmina_file_copy(const gchar *filename)
static void upgrade_sshkeys_202001_mig_common_setting(RemminaFile *remminafile, gboolean protocol_is_ssh, gboolean ssh_enabled, gchar *suffix)
void remmina_file_unsave_passwords(RemminaFile *remminafile)
void remmina_file_set_state_int(RemminaFile *remminafile, const gchar *setting, gint value)
const gchar * remmina_file_get_icon_name(RemminaFile *remminafile)
static RemminaFile * remmina_file_new_empty(void)
enum remmina_masterthread_exec_data::@11 func