diff options
author | Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> | 2021-10-01 16:44:42 +0300 |
---|---|---|
committer | Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> | 2021-10-01 16:44:42 +0300 |
commit | 54bbc52f32deb0ee5a1c53245b078677b07667db (patch) | |
tree | 6962101974f63371fb6cfe446f763941d7c795f9 /plugins | |
parent | b8b840fbd4eb40d875e41eb01fb29908012418ee (diff) |
x2go_plugin.c: Complete refactor; Use 8-space tab instead of 4; Introduce enumeration_prettifier; Add missing _() for translation; Comply more with style-guide.
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/x2go/x2go_plugin.c | 937 |
1 files changed, 517 insertions, 420 deletions
diff --git a/plugins/x2go/x2go_plugin.c b/plugins/x2go/x2go_plugin.c index 72673f4e7..e55b5b1be 100644 --- a/plugins/x2go/x2go_plugin.c +++ b/plugins/x2go/x2go_plugin.c @@ -57,62 +57,67 @@ #include <ctype.h> #define FEATURE_AVAILABLE(gpdata, feature) \ - gpdata->available_features ? \ - (g_list_find_custom(gpdata->available_features, \ - feature, \ - remmina_plugin_x2go_safe_strcmp) \ - ? TRUE : FALSE) : FALSE + gpdata->available_features ? (g_list_find_custom( \ + gpdata->available_features, \ + feature, \ + rmplugin_x2go_safe_strcmp \ + ) ? TRUE : FALSE) : FALSE #define GET_PLUGIN_DATA(gp) \ - (RemminaPluginX2GoData*) g_object_get_data(G_OBJECT(gp), "plugin-data") + (RemminaPluginX2GoData*) g_object_get_data(G_OBJECT(gp), "plugin-data") #define SET_DIALOG_DATA(gp, ddata) \ - g_object_set_data_full(G_OBJECT(gp), "dialog-data", ddata, g_free); + g_object_set_data_full(G_OBJECT(gp), "dialog-data", ddata, g_free); #define GET_DIALOG_DATA(gp) \ - (DialogData*) g_object_get_data(G_OBJECT(gp), "dialog-data"); + (DialogData*) g_object_get_data(G_OBJECT(gp), "dialog-data"); #define REMMINA_PLUGIN_INFO(fmt, ...)\ - remmina_plugin_service->_remmina_info("[%s] " fmt, PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_info("[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define REMMINA_PLUGIN_MESSAGE(fmt, ...)\ - remmina_plugin_service->_remmina_message("[%s] " fmt, PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_message("[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define REMMINA_PLUGIN_DEBUG(fmt, ...)\ - remmina_plugin_service->_remmina_debug(__func__, "[%s] " fmt, \ - PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_debug(__func__, "[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define REMMINA_PLUGIN_WARNING(fmt, ...)\ - remmina_plugin_service->_remmina_warning(__func__, "[%s] " fmt, \ - PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_warning(__func__, "[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define REMMINA_PLUGIN_ERROR(fmt, ...)\ - remmina_plugin_service->_remmina_error(__func__, "[%s] " fmt, \ - PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_error(__func__, "[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define REMMINA_PLUGIN_CRITICAL(fmt, ...)\ - remmina_plugin_service->_remmina_critical(__func__, "[%s] " fmt, \ - PLUGIN_NAME, ##__VA_ARGS__) + rm_plugin_service->_remmina_critical(__func__, "[%s] " fmt, \ + PLUGIN_NAME, ##__VA_ARGS__) #define GET_PLUGIN_STRING(value) \ - g_strdup(remmina_plugin_service->file_get_string(remminafile, value)) + g_strdup(rm_plugin_service->file_get_string(remminafile, value)) + #define GET_PLUGIN_PASSWORD(value) \ GET_PLUGIN_STRING(value) + #define GET_PLUGIN_INT(value, default_value) \ - remmina_plugin_service->file_get_int(remminafile, value, default_value) + rm_plugin_service->file_get_int(remminafile, value, default_value) + #define GET_PLUGIN_BOOLEAN(value) \ - remmina_plugin_service->file_get_int(remminafile, value, FALSE) + rm_plugin_service->file_get_int(remminafile, value, FALSE) -static RemminaPluginService *remmina_plugin_service = NULL; +static RemminaPluginService *rm_plugin_service = NULL; // Following str2int code was adapted from Stackoverflow: // https://stackoverflow.com/questions/7021725/how-to-convert-a-string-to-integer-in-c typedef enum { - STR2INT_SUCCESS, - STR2INT_OVERFLOW, - STR2INT_UNDERFLOW, - STR2INT_INCONVERTIBLE, - STR2INT_INVALID_DATA + STR2INT_SUCCESS, + STR2INT_OVERFLOW, + STR2INT_UNDERFLOW, + STR2INT_INCONVERTIBLE, + STR2INT_INVALID_DATA } str2int_errno; /** @@ -121,34 +126,34 @@ typedef enum { * @param out The converted int. Cannot be NULL. * * @param s Input string to be converted. \n - * The format is the same as strtol, - * except that the following are inconvertible: \n - * empty string, leading whitespace \n - * or any trailing characters that are not part of the number \n - * Cannot be NULL. + * The format is the same as strtol, + * except that the following are inconvertible: \n + * * empty string \n + * * leading whitespace \n + * * or any trailing characters that are not part of the number \n + * Cannot be NULL. * @param base Base to interpret string in. Same range as strtol (2 to 36). * * @return Indicates if the operation succeeded, or why it failed with str2int_errno enum. */ -str2int_errno str2int(gint *out, gchar *s, gint base) { - gchar *end; - - if (!s || !out || base <= 0) - return STR2INT_INVALID_DATA; - - if (s[0] == '\0' || isspace(s[0])) - return STR2INT_INCONVERTIBLE; - errno = 0; - glong l = strtol(s, &end, base); - /* Both checks are needed because INT_MAX == LONG_MAX is possible. */ - if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) - return STR2INT_OVERFLOW; - if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN)) - return STR2INT_UNDERFLOW; - if (*end != '\0') - return STR2INT_INCONVERTIBLE; - *out = l; - return STR2INT_SUCCESS; +str2int_errno str2int(gint *out, gchar *s, gint base) +{ + gchar *end; + + if (!s || !out || base <= 0) return STR2INT_INVALID_DATA; + + if (s[0] == '\0' || isspace(s[0])) return STR2INT_INCONVERTIBLE; + + errno = 0; + glong l = strtol(s, &end, base); + + /* Both checks are needed because INT_MAX == LONG_MAX is possible. */ + if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW; + if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN)) return STR2INT_UNDERFLOW; + if (*end != '\0') return STR2INT_INCONVERTIBLE; + + *out = l; + return STR2INT_SUCCESS; } /** @@ -160,9 +165,8 @@ str2int_errno str2int(gint *out, gchar *s, gint base) { * @brief Splits a string into a gchar* list using delim as a single-character delimeter. * */ -static gchar** remmina_plugin_x2go_split_string(gchar* data, - gchar delim, - guint *occurences) { +static gchar** rmplugin_x2go_split_string(gchar* data, gchar delim, guint *occurences) +{ // Counts the occurence of 'delim', so the amount of numbers passed. guint delim_occurence = 0; // work on a copy of the string, because strchr alters the string. @@ -200,12 +204,12 @@ static gchar** remmina_plugin_x2go_split_string(gchar* data, return returning_string_list; } -// Wrapper for strcmp which doesn't throw an exception when 'a' or 'b' are a nullpointer. -static gint remmina_plugin_x2go_safe_strcmp(gconstpointer a, gconstpointer b) { - if (a && b) { - return strcmp(a, b); - } - +/** + * @brief Wrapper for strcmp which doesn't throw an exception + * when 'a' or 'b' are a nullpointer. + */ +static gint rmplugin_x2go_safe_strcmp(gconstpointer a, gconstpointer b) { + if (a && b) return strcmp(a, b); return -1; } @@ -216,43 +220,41 @@ static gint remmina_plugin_x2go_safe_strcmp(gconstpointer a, gconstpointer b) { * @param buttons see GtkButtonsType * @param title Title of the Dialog * @param message Message of the Dialog - * @param callbackfunc A GCallback function like - * callback(RemminaProtocolWidget *gp, GtkWidget *dialog) - * which will be executed on the dialogs 'response' signal. - * The callback function is obliged to destroy - * the dialog widget. + * @param callbackfunc A GCallback function like: \n + * callback(RemminaProtocolWidget *gp, GtkWidget *dialog) \n + * which will be executed on the dialogs 'response' signal. + * The callback function is obliged to destroy the dialog widget. * - * The `DialogData` structure contains all info needed - * to open a GTK dialog with remmina_plugin_x2go_open_dialog() \n + * The `DialogData` structure contains all info needed to open a + * GTK dialog with rmplugin_x2go_open_dialog() \n * * Quick example of a callback function: \n - * static void remmina_plugin_x2go_test_callback(RemminaProtocolWidget *gp, \n - * gint response_id, \n - * GtkDialog *self) { \n - * REMMINA_PLUGIN_DEBUG("response: %i", response_id); \n - * if (response_id == GTK_RESPONSE_OK) { \n - * REMMINA_PLUGIN_DEBUG("OK!"); \n - * } \n - * gtk_widget_destroy(self); \n + * static void rmplugin_x2go_test_callback(RemminaProtocolWidget *gp, gint response_id, \n + * GtkDialog *self) { \n + * REMMINA_PLUGIN_DEBUG("response: %i", response_id); \n + * if (response_id == GTK_RESPONSE_OK) { \n + * REMMINA_PLUGIN_DEBUG("OK!"); \n + * } \n + * gtk_widget_destroy(self); \n * } * */ struct _DialogData { - /** see GtkWindow */ - GtkWindow *parent; - /** see GtkDialogFlags */ - GtkDialogFlags flags; - /** see GtkMessageType */ - GtkMessageType type; - /** see GtkButtonsType */ - GtkButtonsType buttons; - /** Title of the Dialog */ - gchar *title; - /** Message of the Dialog */ - gchar *message; - /** Calls this function if - * the user pressed a button. */ - GCallback callbackfunc; + /** see GtkWindow */ + GtkWindow* parent; + /** see GtkDialogFlags */ + GtkDialogFlags flags; + /** see GtkMessageType */ + GtkMessageType type; + /** see GtkButtonsType */ + GtkButtonsType buttons; + /** Title of the Dialog */ + gchar* title; + /** Message of the Dialog */ + gchar* message; + /** Calls this function if + * the user pressed a button. */ + GCallback callbackfunc; }; typedef struct _DialogData DialogData; @@ -260,7 +262,8 @@ typedef struct _DialogData DialogData; * @param gp getting DialogData via dialog-data saved in gp. * See define GET_DIALOG_DATA */ -static void remmina_plugin_x2go_open_dialog(RemminaProtocolWidget *gp) { +static void rmplugin_x2go_open_dialog(RemminaProtocolWidget *gp) +{ REMMINA_PLUGIN_DEBUG("Function entry."); DialogData *ddata = GET_DIALOG_DATA(gp); @@ -281,24 +284,22 @@ static void remmina_plugin_x2go_open_dialog(RemminaProtocolWidget *gp) { GtkWidget *widget_gtk_dialog; widget_gtk_dialog = gtk_message_dialog_new(ddata->parent, - ddata->flags, - ddata->type, - ddata->buttons, - ddata->title); + ddata->flags, + ddata->type, + ddata->buttons, + ddata->title); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG (widget_gtk_dialog), - ddata->message); + ddata->message); if (ddata->callbackfunc) { - g_signal_connect_swapped(G_OBJECT(widget_gtk_dialog), - "response", - G_CALLBACK(ddata->callbackfunc), - gp); + g_signal_connect_swapped(G_OBJECT(widget_gtk_dialog), "response", + G_CALLBACK(ddata->callbackfunc), + gp); } else { - g_signal_connect(G_OBJECT(widget_gtk_dialog), - "response", - G_CALLBACK(gtk_widget_destroy), - NULL); + g_signal_connect(G_OBJECT(widget_gtk_dialog), "response", + G_CALLBACK(gtk_widget_destroy), + NULL); } gtk_widget_show_all(widget_gtk_dialog); @@ -324,10 +325,10 @@ typedef struct _RemminaPluginX2GoData { GList* available_features; } RemminaPluginX2GoData; -#define REMMINA_PLUGIN_X2GO_FEATURE_GTKSOCKET 1 +#define RMPLUGIN_X2GO_FEATURE_GTKSOCKET 1 /* Forward declaration */ -static RemminaProtocolPlugin remmina_plugin_x2go; +static RemminaProtocolPlugin rmplugin_x2go; /* When more than one NX sessions is connecting in progress, we need this mutex and array * to prevent them from stealing the same window ID. @@ -335,9 +336,9 @@ static RemminaProtocolPlugin remmina_plugin_x2go; static pthread_mutex_t remmina_x2go_init_mutex; static GArray *remmina_x2go_window_id_array; -/* --------- Support for execution on main thread of GTK functions -------------- */ - -struct onMainThread_cb_data { +/* ------------- Support for execution on main thread of GTK functions ------------- */ +struct onMainThread_cb_data +{ enum { FUNC_GTK_SOCKET_ADD_ID } func; GtkSocket* sk; @@ -349,7 +350,8 @@ struct onMainThread_cb_data { gboolean cancelled; }; -static gboolean onMainThread_cb(struct onMainThread_cb_data *d) { +static gboolean onMainThread_cb(struct onMainThread_cb_data *d) +{ TRACE_CALL(__func__); if (!d->cancelled) { switch (d->func) { @@ -366,13 +368,15 @@ static gboolean onMainThread_cb(struct onMainThread_cb_data *d) { } -static void onMainThread_cleanup_handler(gpointer data) { +static void onMainThread_cleanup_handler(gpointer data) +{ TRACE_CALL(__func__); struct onMainThread_cb_data *d = data; d->cancelled = TRUE; } -static void onMainThread_schedule_callback_and_wait(struct onMainThread_cb_data *d) { +static void onMainThread_schedule_callback_and_wait(struct onMainThread_cb_data *d) +{ TRACE_CALL(__func__); d->cancelled = FALSE; pthread_cleanup_push(onMainThread_cleanup_handler, d); @@ -387,7 +391,8 @@ static void onMainThread_schedule_callback_and_wait(struct onMainThread_cb_data pthread_mutex_destroy(&d->mu); } -static void onMainThread_gtk_socket_add_id(GtkSocket* sk, Window w) { +static void onMainThread_gtk_socket_add_id(GtkSocket* sk, Window w) +{ TRACE_CALL(__func__); struct onMainThread_cb_data *d; @@ -400,34 +405,34 @@ static void onMainThread_gtk_socket_add_id(GtkSocket* sk, Window w) { onMainThread_schedule_callback_and_wait(d); g_free(d); } +/* /-/-/-/-/-/-/ Support for execution on main thread of GTK functions /-/-/-/-/-/-/ */ -/* --------------------------------------- */ - -static void remmina_plugin_x2go_remove_window_id (Window window_id) { +static void rmplugin_x2go_remove_window_id (Window window_id) +{ gint i; gboolean already_seen = FALSE; - pthread_mutex_lock (&remmina_x2go_init_mutex); - for (i = 0; i < remmina_x2go_window_id_array->len; i++) - { - if (g_array_index (remmina_x2go_window_id_array, Window, i) == window_id) - { + pthread_mutex_lock(&remmina_x2go_init_mutex); + for (i = 0; i < remmina_x2go_window_id_array->len; i++) { + if (g_array_index(remmina_x2go_window_id_array, Window, i) == window_id) { already_seen = TRUE; REMMINA_PLUGIN_DEBUG("Window for X2Go Agent with ID [0x%lx] already seen.", - window_id); + window_id); break; } } - if (already_seen) - { - g_array_remove_index_fast (remmina_x2go_window_id_array, i); + + if (already_seen) { + g_array_remove_index_fast(remmina_x2go_window_id_array, i); REMMINA_PLUGIN_DEBUG("forgetting about window for X2Go Agent with ID [0x%lx]", - window_id); + window_id); } - pthread_mutex_unlock (&remmina_x2go_init_mutex); + + pthread_mutex_unlock(&remmina_x2go_init_mutex); } -static gboolean remmina_plugin_x2go_cleanup(RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_cleanup(RemminaProtocolWidget *gp) +{ REMMINA_PLUGIN_DEBUG("Function entry."); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); @@ -438,12 +443,11 @@ static gboolean remmina_plugin_x2go_cleanup(RemminaProtocolWidget *gp) { if (gpdata->thread) { pthread_cancel(gpdata->thread); - if (gpdata->thread) - pthread_join(gpdata->thread, NULL); + if (gpdata->thread) pthread_join(gpdata->thread, NULL); } if (gpdata->window_id) { - remmina_plugin_x2go_remove_window_id(gpdata->window_id); + rmplugin_x2go_remove_window_id(gpdata->window_id); } if (gpdata->pidx2go) { @@ -459,12 +463,13 @@ static gboolean remmina_plugin_x2go_cleanup(RemminaProtocolWidget *gp) { } g_object_steal_data(G_OBJECT(gp), "plugin-data"); - remmina_plugin_service->protocol_plugin_signal_connection_closed(gp); + rm_plugin_service->protocol_plugin_signal_connection_closed(gp); return FALSE; } -static gboolean remmina_plugin_x2go_close_connection(RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_close_connection(RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); @@ -475,14 +480,15 @@ static gboolean remmina_plugin_x2go_close_connection(RemminaProtocolWidget *gp) return FALSE; } - remmina_plugin_x2go_cleanup(gp); + rmplugin_x2go_cleanup(gp); return TRUE; } -static void remmina_plugin_x2go_pyhoca_cli_exited(GPid pid, - int status, - struct _RemminaProtocolWidget *gp) { +static void rmplugin_x2go_pyhoca_cli_exited(GPid pid, + gint status, + struct _RemminaProtocolWidget *gp) +{ REMMINA_PLUGIN_DEBUG("Function entry."); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); @@ -497,7 +503,7 @@ static void remmina_plugin_x2go_pyhoca_cli_exited(GPid pid, } REMMINA_PLUGIN_CRITICAL("%s", _("pyhoca-cli exited unexpectedly. " - "This connection will now be closed.")); + "This connection will now be closed.")); DialogData *ddata = g_new0(DialogData, 1); SET_DIALOG_DATA(gp, ddata); @@ -507,25 +513,34 @@ static void remmina_plugin_x2go_pyhoca_cli_exited(GPid pid, ddata->buttons = GTK_BUTTONS_OK; ddata->title = _("An error occured."); ddata->message = _("The necessary child process 'pyhoca-cli' stopped unexpectedly.\n" - "Please check your profile settings and pyhoca-cli's output for " - "possible errors and ensure the remote server is " - "reachable."); - ddata->callbackfunc = NULL; // Dialog frees itself, no need for an callbackfunc. - IDLE_ADD((GSourceFunc) remmina_plugin_x2go_open_dialog, gp); + "Please check your profile settings and pyhoca-cli's output for " + "possible errors and ensure the remote server is " + "reachable."); + // We don't need the response. + ddata->callbackfunc = NULL; + IDLE_ADD((GSourceFunc) rmplugin_x2go_open_dialog, gp); // 1 Second. Give `Dialog` chance to open. usleep(1000 * 1000); - remmina_plugin_x2go_close_connection(gp); + rmplugin_x2go_close_connection(gp); } -// Returns either all features separated with a '\n' or NULL if it failed. -static gchar* remmina_plugin_x2go_get_pyhoca_features() { +/** + * @brief Get all available pyhoca-cli features by + * executing `pyhoca-cli --list-cmdline-features`. + * + * @returns Returns either a gchar* with all features, + * separated by a '\n' or NULL if it failed. + */ +static gchar* rmplugin_x2go_get_pyhoca_features() +{ REMMINA_PLUGIN_DEBUG("Function entry."); // We will now start pyhoca-cli with only the '--list-cmdline-features' option - // and depending on the exit code and stdout output we will determine if some features - // are available or not. + // and depending on the exit code and stdout output we will determine if some + // features are available or not. + gchar *argv[50]; gint argc = 0; GError *error = NULL; @@ -540,18 +555,34 @@ static gchar* remmina_plugin_x2go_get_pyhoca_features() { gchar **envp = g_get_environ(); gboolean success_ret = g_spawn_sync (NULL, argv, envp, G_SPAWN_SEARCH_PATH, - NULL, NULL, &standard_out, &standard_err, - &exit_code, &error); + NULL, NULL, &standard_out, &standard_err, + &exit_code, &error); + + REMMINA_PLUGIN_INFO("%s", _("Started pyhoca-cli with following arguments:")); + // Print every argument except passwords. Free all arg strings. + for (gint i = 0; i < argc - 1; i++) { + if (strcmp(argv[i], "--password") == 0) { + g_printf("%s ", argv[i]); + g_printf("XXXXXX "); + g_free (argv[i]); + g_free (argv[++i]); + continue; + } else { + g_printf("%s ", argv[i]); + g_free (argv[i]); + } + } + g_printf("\n"); if (!success_ret || error || strcmp(standard_out, "") == 0 || exit_code) { if (!error) { REMMINA_PLUGIN_WARNING("%s", - g_strdup_printf(_("An unknown error happened while retrieving " - "pyhoca-cli's cmdline features! " - "Exit code: %i"), exit_code)); + g_strdup_printf(_("An unknown error happened while retrieving " + "pyhoca-cli's cmdline features! Exit code: %i"), + exit_code)); } else { REMMINA_PLUGIN_WARNING("%s", - g_strdup_printf(_("Error: '%s'"), error->message)); + g_strdup_printf(_("Error: '%s'"), error->message)); g_error_free(error); } @@ -561,6 +592,43 @@ static gchar* remmina_plugin_x2go_get_pyhoca_features() { return standard_out; } + +/** + * @brief Saves s_password and s_username if set. + * @returns either TRUE or FALSE. If FALSE gets returned `errmsg` is set. + */ +static gboolean rmplugin_x2go_save_credentials(RemminaFile* remminafile, + gchar* s_username, gchar* s_password, + gchar* errmsg) +{ + // User has requested to save credentials. We put all the new credentials + // into remminafile->settings. They will be saved later, on successful + // connection, by rcw.c + if (s_password && s_username) { + if (strcmp(s_username, "") == 0) { + g_strlcpy(errmsg, _("Given username can't get saved " + "since it's empty!"), 512); + //REMMINA_PLUGIN_CRITICAL("%s", errmsg); // No need. + return FALSE; + } + + // We allow the possibility to set an empty password because a X2Go + // session can be still made using keyfiles or similar. + rm_plugin_service->file_set_string(remminafile, "password", + s_password); + rm_plugin_service->file_set_string(remminafile, "username", + s_username); + } else { + g_strlcpy(errmsg, _("An error occured while trying to save " + "new credentials: 's_password' or " + "'s_username' strings were not set."), 512); + //REMMINA_PLUGIN_CRITICAL("%s", errmsg); // No need. + return FALSE; + } + + return TRUE; +} + /** * @brief Asks the user for a username and password. * @@ -570,24 +638,24 @@ static gchar* remmina_plugin_x2go_get_pyhoca_features() { * * @returns FALSE if auth failed and TRUE on success. */ -static gboolean remmina_plugin_x2go_get_auth(RemminaProtocolWidget *gp, gchar* errmsg, - gchar* username, gchar* password) { +static gboolean rmplugin_x2go_get_auth(RemminaProtocolWidget *gp, gchar* errmsg, + gchar* username, gchar* password) +{ gchar *s_username, *s_password; gint ret; gboolean save; - gboolean password_storing_disabled; + gboolean disable_password_storing; RemminaFile *remminafile; - remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + remminafile = rm_plugin_service->protocol_plugin_get_file(gp); - password_storing_disabled = remmina_plugin_service->file_get_int( - remminafile, - "disablepasswordstoring", - FALSE); - ret = remmina_plugin_service->protocol_plugin_init_auth( - gp, - (password_storing_disabled ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD | - REMMINA_MESSAGE_PANEL_FLAG_USERNAME), + disable_password_storing = rm_plugin_service->file_get_int(remminafile, + "disablepasswordstoring", + FALSE); + ret = rm_plugin_service->protocol_plugin_init_auth( + gp, (disable_password_storing ? 0 : + REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD | + REMMINA_MESSAGE_PANEL_FLAG_USERNAME), _("Enter X2Go credentials"), username, // function arg 'username' is default username password, // function arg 'password' is default password @@ -596,38 +664,19 @@ static gboolean remmina_plugin_x2go_get_auth(RemminaProtocolWidget *gp, gchar* e if (ret == GTK_RESPONSE_OK) { - s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp); - s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp); - if (remmina_plugin_service->protocol_plugin_init_get_savepassword(gp)) - remmina_plugin_service->file_set_string(remminafile, "password", s_password); + s_username = rm_plugin_service->protocol_plugin_init_get_username(gp); + s_password = rm_plugin_service->protocol_plugin_init_get_password(gp); + if (rm_plugin_service->protocol_plugin_init_get_savepassword(gp)) + rm_plugin_service->file_set_string(remminafile, "password", + s_password); // Should be renamed to protocol_plugin_init_get_savecredentials()?! - save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp); + save = rm_plugin_service->protocol_plugin_init_get_savepassword(gp); if (save) { - // User has requested to save credentials. We put all the new credentials - // into remminafile->settings. They will be saved later, on successful - // connection, by rcw.c - - if (s_password && s_username) { - if (strcmp(s_username, "") == 0) { - g_strlcpy(errmsg, _("Given username can't get saved since " - "it's empty!"), 512); - //REMMINA_PLUGIN_CRITICAL("%s", errmsg); - return FALSE; - } - - // We allow the possibility to set an empty password because a X2Go - // session can be still made using keyfiles or similar. - remmina_plugin_service->file_set_string(remminafile, "password", - s_password); - remmina_plugin_service->file_set_string(remminafile, "username", - s_username); - } else { - g_strlcpy(errmsg, _("An error occured while trying to save " - "new credentials: 's_password' or " - "'s_username' strings were not set."), 512); - //REMMINA_PLUGIN_CRITICAL("%s", errmsg); - return FALSE; + if (!rmplugin_x2go_save_credentials(remminafile, + s_username, s_password, + errmsg)) { + } } if (s_username) { @@ -647,26 +696,28 @@ static gboolean remmina_plugin_x2go_get_auth(RemminaProtocolWidget *gp, gchar* e return TRUE; } -static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, - gint sshport, - gchar *username, - gchar *password, - gchar *command, - gchar *kbdlayout, - gchar *kbdtype, - gchar *audio, - gchar *clipboard, - gint dpi, - gchar *resolution, - RemminaProtocolWidget *gp, - gchar *errmsg) { +static gboolean rmplugin_x2go_exec_x2go(gchar *host, + gint sshport, + gchar *username, + gchar *password, + gchar *command, + gchar *kbdlayout, + gchar *kbdtype, + gchar *audio, + gchar *clipboard, + gint dpi, + gchar *resolution, + RemminaProtocolWidget *gp, + gchar *errmsg) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); gchar *argv[50]; gint argc = 0; - if(!remmina_plugin_x2go_get_auth(gp, errmsg, username, password)) { + // Sets `username` and `password`. + if (!rmplugin_x2go_get_auth(gp, errmsg, username, password)) { return FALSE; } @@ -703,8 +754,9 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, if (FEATURE_AVAILABLE(gpdata, "COMMAND")) { argv[argc++] = g_strdup("-c"); - // FIXME: pyhoca-cli is picky about multiple quotes around the command string... - // argv[argc++] = g_strdup_printf ("%s", g_shell_quote(command)); + // FIXME: pyhoca-cli is picky about multiple quotes around + // the command string... + // argv[argc++] = g_strdup_printf ("%s", g_shell_quote(command)); argv[argc++] = g_strdup(command); } @@ -759,10 +811,12 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, } if (FEATURE_AVAILABLE(gpdata, "DPI")) { + // Event though we validate the users input in Remmina Editor, + // manipulating profile files is still very possible.. // Values are extracted from pyhoca-cli. if (dpi < 20 || dpi > 400) { - g_strlcpy(errmsg, _("DPI setting is out of bounds. " - "Please adjust it in profile settings."), 512); + g_strlcpy(errmsg, _("DPI setting is out of bounds. Please adjust " + "it in profile settings."), 512); // No need, start_session() will handle output. //REMMINA_PLUGIN_CRITICAL("%s", errmsg); return FALSE; @@ -776,12 +830,12 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, GError *error = NULL; gchar **envp = g_get_environ(); gboolean success = g_spawn_async_with_pipes (NULL, argv, envp, - G_SPAWN_DO_NOT_REAP_CHILD | - G_SPAWN_SEARCH_PATH, NULL, - NULL, &gpdata->pidx2go, - NULL, NULL, NULL, &error); + (G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_SEARCH_PATH), NULL, + NULL, &gpdata->pidx2go, + NULL, NULL, NULL, &error); - REMMINA_PLUGIN_DEBUG("Started pyhoca-cli with following arguments:"); + REMMINA_PLUGIN_INFO("%s", _("Started pyhoca-cli with following arguments:")); // Print every argument except passwords. Free all arg strings. for (gint i = 0; i < argc - 1; i++) { if (strcmp(argv[i], "--password") == 0) { @@ -798,11 +852,11 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, g_printf("\n"); if (!success || error) { - if (!error) - error = g_error_new(0, 0, "<error not available>"); + // TRANSLATORS: Meta-error. Shouldn't be visible. + if (!error) error = g_error_new(0, 0, _("Internal error.")); gchar *error_title = _("An error occured while " - "starting an X2Go session…"); + "starting an X2Go session…"); DialogData *ddata = g_new0(DialogData, 1); SET_DIALOG_DATA(gp, ddata); @@ -812,14 +866,15 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, ddata->buttons = GTK_BUTTONS_OK; ddata->title = _("Could not start X2Go session."); ddata->message = g_strdup_printf(_("Could not start PyHoca-CLI (%i): '%s'"), - error->code, - error->message); - ddata->callbackfunc = NULL; // Dialog frees itself, no need for an callbackfunc. - IDLE_ADD((GSourceFunc) remmina_plugin_x2go_open_dialog, gp); + error->code, + error->message); + // We don't need the response. + ddata->callbackfunc = NULL; + IDLE_ADD((GSourceFunc) rmplugin_x2go_open_dialog, gp); g_strlcpy(errmsg, error_title, 512); - // No need to output here. remmina_plugin_x2go_start_session will do this. + // No need to output here. rmplugin_x2go_start_session will do this. g_error_free(error); @@ -834,15 +889,17 @@ static gboolean remmina_plugin_x2go_exec_x2go(gchar *host, REMMINA_PLUGIN_DEBUG("Watching child pyhoca-cli process now…"); g_child_watch_add(gpdata->pidx2go, - (GChildWatchFunc) remmina_plugin_x2go_pyhoca_cli_exited, - gp); + (GChildWatchFunc) rmplugin_x2go_pyhoca_cli_exited, + gp); return TRUE; } -// Returns a GList* with all features which pyhoca-cli had before even -// implementing the feature system. (It can't change since its an old version) -static GList* remmina_plugin_x2go_old_pyhoca_features() { +/** + * @returns a GList* with all features which pyhoca-cli had before the feature system. + */ +static GList* rmplugin_x2go_old_pyhoca_features() +{ REMMINA_PLUGIN_DEBUG("Function entry."); #define AMOUNT_FEATURES 43 @@ -862,17 +919,21 @@ static GList* remmina_plugin_x2go_old_pyhoca_features() { for (int i = 0; i < AMOUNT_FEATURES; i++) { features_list = g_list_append(features_list, features[i]); } + return features_list; } -// Returns a GList* which includes all features we *can* use. -static GList* remmina_plugin_x2go_populate_available_features_list() { +/** + * @returns a GList* which includes all pyhoca-cli command line features we can use. + */ +static GList* rmplugin_x2go_populate_available_features_list() +{ REMMINA_PLUGIN_DEBUG("Function entry."); GList* returning_glist = NULL; // Querying pyhoca-cli's command line features. - gchar* features_string = remmina_plugin_x2go_get_pyhoca_features(); + gchar* features_string = rmplugin_x2go_get_pyhoca_features(); if (!features_string) { // We added the '--list-cmdline-features' on commit 17d1be1319ba6 of @@ -880,52 +941,58 @@ static GList* remmina_plugin_x2go_populate_available_features_list() { // version of pyhoca-cli available yet we artificially create a list // of an old limited set of features. - REMMINA_PLUGIN_WARNING("%s", _("Couldn't get pyhoca-cli's cmdline-features. This " - "indicates either your pyhoca-cli version is too " - "old or pyhoca-cli is not installed! An old limited set " - "of features will be used now.")); + REMMINA_PLUGIN_WARNING("%s", + _("Couldn't get pyhoca-cli's cmdline-features. This " + "indicates either your pyhoca-cli version is too " + "old or pyhoca-cli is not installed! An old limited set " + "of features will be used now.")); - return remmina_plugin_x2go_old_pyhoca_features(); + return rmplugin_x2go_old_pyhoca_features(); } else { guint features_amount = 0; - gchar **features_list = remmina_plugin_x2go_split_string(features_string, - '\n', &features_amount); + gchar **features_list = rmplugin_x2go_split_string(features_string, '\n', + &features_amount); + if (features_list == NULL || features_amount <= 0) { - gchar *error_msg = _("parsing pyhoca-cli functionality was not possible! " - "Using a limited feature-set for now."); + gchar *error_msg = _("Parsing pyhoca-cli functionality was not " + "possible! Using a limited feature-set for " + "now."); REMMINA_PLUGIN_WARNING("%s", error_msg); - return remmina_plugin_x2go_old_pyhoca_features(); + return rmplugin_x2go_old_pyhoca_features(); } - REMMINA_PLUGIN_INFO("%s", _("Retrieved the following pyhoca-cli functionality:")); + REMMINA_PLUGIN_INFO("%s", _("Retrieved the following pyhoca-cli " + "functionality:")); for(int k = 0; k < features_amount; k++) { - REMMINA_PLUGIN_INFO("%s", g_strdup_printf(_("Available feature[%i]: '%s'"), - k+1, features_list[k])); + REMMINA_PLUGIN_INFO("%s", + g_strdup_printf(_("Available feature[%i]: '%s'"), + k+1, features_list[k])); returning_glist = g_list_append(returning_glist, features_list[k]); } return returning_glist; } } -static void remmina_plugin_x2go_on_plug_added(GtkSocket *socket, - RemminaProtocolWidget *gp) { +static void rmplugin_x2go_on_plug_added(GtkSocket *socket, RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); REMMINA_PLUGIN_DEBUG("Socket %d", gpdata->socket_id); - remmina_plugin_service->protocol_plugin_signal_connection_opened(gp); + rm_plugin_service->protocol_plugin_signal_connection_opened(gp); return; } -static gboolean remmina_plugin_x2go_on_plug_removed(GtkSocket *socket, - RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_on_plug_removed(GtkSocket *socket, RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); REMMINA_PLUGIN_DEBUG("Function entry."); - remmina_plugin_x2go_close_connection(gp); + rmplugin_x2go_close_connection(gp); return TRUE; } -static void remmina_plugin_x2go_init(RemminaProtocolWidget *gp) { +static void rmplugin_x2go_init(RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); REMMINA_PLUGIN_DEBUG("Function entry.", PLUGIN_NAME); RemminaPluginX2GoData *gpdata; @@ -933,12 +1000,12 @@ static void remmina_plugin_x2go_init(RemminaProtocolWidget *gp) { gpdata = g_new0(RemminaPluginX2GoData, 1); g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free); - if (!remmina_plugin_service->gtksocket_available()) { + if (!rm_plugin_service->gtksocket_available()) { /* report this in open_connection, not reportable here... */ return; } - GList* available_features = remmina_plugin_x2go_populate_available_features_list(); + GList* available_features = rmplugin_x2go_populate_available_features_list(); // available_features can't be NULL cause if it fails, it gets populated with an // old standard feature set. @@ -953,73 +1020,75 @@ static void remmina_plugin_x2go_init(RemminaProtocolWidget *gp) { gpdata->orig_handler = NULL; gpdata->socket = gtk_socket_new(); - remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->socket); + rm_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->socket); gtk_widget_show(gpdata->socket); g_signal_connect(G_OBJECT(gpdata->socket), "plug-added", - G_CALLBACK(remmina_plugin_x2go_on_plug_added), gp); + G_CALLBACK(rmplugin_x2go_on_plug_added), gp); g_signal_connect(G_OBJECT(gpdata->socket), "plug-removed", - G_CALLBACK(remmina_plugin_x2go_on_plug_removed), gp); + G_CALLBACK(rmplugin_x2go_on_plug_removed), gp); gtk_container_add(GTK_CONTAINER(gp), gpdata->socket); } -static gboolean remmina_plugin_x2go_try_window_id(Window window_id) { +static gboolean rmplugin_x2go_try_window_id(Window window_id) +{ TRACE_CALL(__func__); gint i; gboolean already_seen = FALSE; - REMMINA_PLUGIN_DEBUG("Check if X2Go Agent window [0x%lx] is already known or if it " - "needs registration", window_id); + REMMINA_PLUGIN_DEBUG("Check if X2Go Agent window [0x%lx] is already known or if " + "it needs registration", window_id); pthread_mutex_lock(&remmina_x2go_init_mutex); for (i = 0; i < remmina_x2go_window_id_array->len; i++) { if (g_array_index(remmina_x2go_window_id_array, Window, i) == window_id) { already_seen = TRUE; - REMMINA_PLUGIN_DEBUG("X2Go window for Agent with ID [0x%lx] already seen.", - window_id); + REMMINA_PLUGIN_DEBUG("X2Go window for Agent with ID [0x%lx] " + "already seen.", window_id); break; } } if (!already_seen) { g_array_append_val(remmina_x2go_window_id_array, window_id); - REMMINA_PLUGIN_DEBUG("Registered new window for X2Go Agent with ID [0x%lx].", - window_id); + REMMINA_PLUGIN_DEBUG("Registered new window for X2Go Agent with " + "ID [0x%lx].", window_id); } pthread_mutex_unlock(&remmina_x2go_init_mutex); return (!already_seen); } -static int remmina_plugin_x2go_dummy_handler(Display *dsp, XErrorEvent *err) { +static int rmplugin_x2go_dummy_handler(Display *dsp, XErrorEvent *err) +{ TRACE_CALL(__func__); return 0; } -static gboolean remmina_plugin_x2go_start_create_notify(RemminaProtocolWidget *gp, - gchar *errmsg) { +static gboolean rmplugin_x2go_start_create_notify(RemminaProtocolWidget *gp, gchar *errmsg) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); gpdata->display = XOpenDisplay(gdk_display_get_name(gdk_display_get_default())); if (gpdata->display == NULL) { - g_strlcpy(errmsg, "Could not open X11 DISPLAY.", 512); + g_strlcpy(errmsg, _("Could not open X11 DISPLAY."), 512); return FALSE; } - gpdata->orig_handler = XSetErrorHandler(remmina_plugin_x2go_dummy_handler); + gpdata->orig_handler = XSetErrorHandler(rmplugin_x2go_dummy_handler); XSelectInput(gpdata->display, - XDefaultRootWindow(gpdata->display), - SubstructureNotifyMask); + XDefaultRootWindow(gpdata->display), + SubstructureNotifyMask); REMMINA_PLUGIN_DEBUG("X11 event watcher created."); return TRUE; } -static gboolean remmina_plugin_x2go_monitor_create_notify(RemminaProtocolWidget *gp, - const gchar *cmd, - gchar *errmsg) +static gboolean rmplugin_x2go_monitor_create_notify(RemminaProtocolWidget *gp, + const gchar *cmd, + gchar *errmsg) { TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata; @@ -1033,7 +1102,7 @@ static gboolean remmina_plugin_x2go_monitor_create_notify(RemminaProtocolWidget unsigned long nitems, rest; unsigned char *data = NULL; - guint16 counter_x11_event_non_createnotify = 0; + guint16 non_createnotify_count = 0; struct timespec ts; // wait_amount * ts.tv_nsec = 20s @@ -1068,8 +1137,8 @@ static gboolean remmina_plugin_x2go_monitor_create_notify(RemminaProtocolWidget wait_amount--; // Don't spam the console. Print every second though. if (wait_amount % 5 == 0) { - REMMINA_PLUGIN_INFO("%s", _("Waiting for pyhoca-cli " - "to show the session's window.")); + REMMINA_PLUGIN_INFO("%s", _("Waiting for pyhoca-cli to " + "show the session's window.")); } continue; } @@ -1077,33 +1146,32 @@ static gboolean remmina_plugin_x2go_monitor_create_notify(RemminaProtocolWidget XNextEvent(gpdata->display, &xev); // Just ignore non CreatNotify events. if (xev.type != CreateNotify) { - counter_x11_event_non_createnotify++; - if (counter_x11_event_non_createnotify % 5 == 0) { - REMMINA_PLUGIN_DEBUG("Saw '%i' X11 events, which weren't CreateNotify.", - counter_x11_event_non_createnotify); + non_createnotify_count++; + if (non_createnotify_count % 5 == 0) { + REMMINA_PLUGIN_DEBUG("Saw '%i' X11 events, which weren't " + "CreateNotify.", non_createnotify_count); } continue; } w = xev.xcreatewindow.window; - if (XGetWindowProperty(gpdata->display, w, atom, 0, 255, False, AnyPropertyType, - &type, &format, &nitems, &rest, - &data) != Success) { - REMMINA_PLUGIN_DEBUG("Could not get WM_COMMAND property from X11 window ID " - "[0x%lx].", w); + if (XGetWindowProperty(gpdata->display, w, atom, 0, 255, False, + AnyPropertyType, &type, &format, &nitems, &rest, + &data) != Success) { + REMMINA_PLUGIN_DEBUG("Could not get WM_COMMAND property from X11 " + "window ID [0x%lx].", w); continue; } if (data) { - REMMINA_PLUGIN_DEBUG("Saw '%i' X11 events, which weren't CreateNotify.", - counter_x11_event_non_createnotify); - REMMINA_PLUGIN_DEBUG("Found X11 window with WM_COMMAND set to '%s', " - "the window ID is [0x%lx].", (char*)data, w); + REMMINA_PLUGIN_DEBUG("Saw '%i' X11 events, which weren't " + "CreateNotify.", non_createnotify_count); + REMMINA_PLUGIN_DEBUG("Found X11 window with WM_COMMAND set " + "to '%s', the window ID is [0x%lx].", + (char*)data, w); } - if (data && - g_strrstr((gchar*)data, cmd) && - remmina_plugin_x2go_try_window_id(w)) { - + if (data && g_strrstr((gchar*)data, cmd) && + rmplugin_x2go_try_window_id(w)) { gpdata->window_id = w; agent_window_found = TRUE; XFree(data); @@ -1120,15 +1188,16 @@ static gboolean remmina_plugin_x2go_monitor_create_notify(RemminaProtocolWidget CANCEL_ASYNC if (!agent_window_found) { - g_strlcpy(errmsg, "No X2Go session window appeared. Something went wrong…", - 512); + g_strlcpy(errmsg, _("No X2Go session window appeared. " + "Something went wrong…"), 512); return FALSE; } return TRUE; } -static gboolean remmina_plugin_x2go_start_session(RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_start_session(RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); REMMINA_PLUGIN_DEBUG("Function entry."); @@ -1137,40 +1206,45 @@ static gboolean remmina_plugin_x2go_start_session(RemminaProtocolWidget *gp) { const gchar errmsg[512] = {0}; gboolean ret = TRUE; - gchar *servstr, *host, *username, *password, *command, *kbdlayout, *kbdtype, *audio, - *clipboard, *res; + gchar *servstr, *host, *username, *password, *command, *kbdlayout, *kbdtype, + *audio, *clipboard, *res; gint sshport, dpi; GdkDisplay *default_dsp; gint width, height; - /* We save the X Display name (:0) as we will need to synchronize the clipboards */ + // We save the X Display name (:0) as we will need to synchronize the clipboards default_dsp = gdk_display_get_default(); const gchar *default_dsp_name = gdk_display_get_name(default_dsp); REMMINA_PLUGIN_DEBUG("Default display is '%s'.", default_dsp_name); - remminafile = remmina_plugin_service->protocol_plugin_get_file(gp); + remminafile = rm_plugin_service->protocol_plugin_get_file(gp); servstr = GET_PLUGIN_STRING("server"); if (servstr) { - remmina_plugin_service->get_server_port(servstr, 22, &host, &sshport); + rm_plugin_service->get_server_port(servstr, 22, &host, &sshport); } else { return FALSE; } - if (!sshport) - sshport=22; + + if (!sshport) sshport=22; + username = GET_PLUGIN_STRING("username"); password = GET_PLUGIN_PASSWORD("password"); + command = GET_PLUGIN_STRING("command"); - if (!command) - command = "TERMINAL"; + if (!command) command = "TERMINAL"; + kbdlayout = GET_PLUGIN_STRING("kbdlayout"); kbdtype = GET_PLUGIN_STRING("kbdtype"); + audio = GET_PLUGIN_STRING("audio"); + clipboard = GET_PLUGIN_STRING("clipboard"); + dpi = GET_PLUGIN_INT("dpi", 0); - width = remmina_plugin_service->get_profile_remote_width(gp); - height = remmina_plugin_service->get_profile_remote_height(gp); + width = rm_plugin_service->get_profile_remote_width(gp); + height = rm_plugin_service->get_profile_remote_height(gp); /* multiple of 4 */ width = (width + 3) & ~0x3; height = (height + 3) & ~0x3; @@ -1184,22 +1258,20 @@ static gboolean remmina_plugin_x2go_start_session(RemminaProtocolWidget *gp) { REMMINA_PLUGIN_DEBUG("Attached window to socket '%d'.", gpdata->socket_id); /* register for notifications of window creation events */ - if (ret) - ret = remmina_plugin_x2go_start_create_notify(gp, (gchar*)&errmsg); + if (ret) ret = rmplugin_x2go_start_create_notify(gp, (gchar*)&errmsg); /* trigger the session start, session window should appear soon after this */ - if (ret) - ret = remmina_plugin_x2go_exec_x2go(host, sshport, username, password, command, - kbdlayout, kbdtype, audio, clipboard, dpi, - res, gp, (gchar*)&errmsg); + if (ret) ret = rmplugin_x2go_exec_x2go(host, sshport, username, password, command, + kbdlayout, kbdtype, audio, clipboard, dpi, + res, gp, (gchar*)&errmsg); /* get the window id of the remote x2goagent */ - if (ret) - ret = remmina_plugin_x2go_monitor_create_notify(gp, "x2goagent", (gchar*)&errmsg); + if (ret) ret = rmplugin_x2go_monitor_create_notify(gp, "x2goagent", + (gchar*)&errmsg); if (!ret) { - REMMINA_PLUGIN_CRITICAL("%s", errmsg); - remmina_plugin_service->protocol_plugin_set_error(gp, "%s", errmsg); + REMMINA_PLUGIN_CRITICAL("%s", _(errmsg)); + rm_plugin_service->protocol_plugin_set_error(gp, "%s", _(errmsg)); return FALSE; } @@ -1209,46 +1281,52 @@ static gboolean remmina_plugin_x2go_start_session(RemminaProtocolWidget *gp) { return TRUE; } -static gboolean remmina_plugin_x2go_main(RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_main(RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); gboolean ret = FALSE; - ret = remmina_plugin_x2go_start_session(gp); + ret = rmplugin_x2go_start_session(gp); gpdata->thread = 0; return ret; } -static gpointer remmina_plugin_x2go_main_thread(gpointer data) { +static gpointer rmplugin_x2go_main_thread(RemminaProtocolWidget* gp) +{ TRACE_CALL(__func__); + if (!gp) { + REMMINA_PLUGIN_CRITICAL("%s", _("RemminaProtocolWidget* gp is null!")); + return NULL; + } + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); CANCEL_ASYNC - if (!remmina_plugin_x2go_main((RemminaProtocolWidget*)data)) { - IDLE_ADD( - (GSourceFunc) remmina_plugin_x2go_cleanup, - (RemminaProtocolWidget*) data - ); + if (!rmplugin_x2go_main(gp)) { + IDLE_ADD((GSourceFunc) rmplugin_x2go_cleanup, gp); } + return NULL; } -static gboolean remmina_plugin_x2go_open_connection(RemminaProtocolWidget *gp) { +static gboolean rmplugin_x2go_open_connection(RemminaProtocolWidget *gp) +{ TRACE_CALL(__func__); RemminaPluginX2GoData *gpdata = GET_PLUGIN_DATA(gp); - if (!remmina_plugin_service->gtksocket_available()) { - remmina_plugin_service->protocol_plugin_set_error(gp, - _("The protocol %s is unavailable because GtkSocket only works under X.org"), - PLUGIN_NAME); + if (!rm_plugin_service->gtksocket_available()) { + rm_plugin_service->protocol_plugin_set_error(gp, _("The protocol %s is " + "unavailable because GtkSocket only works under X.org"), + PLUGIN_NAME); return FALSE; } gpdata->socket_id = gtk_socket_get_id(GTK_SOCKET(gpdata->socket)); - if (pthread_create(&gpdata->thread, NULL, remmina_plugin_x2go_main_thread, gp)) { - remmina_plugin_service->protocol_plugin_set_error(gp, - "Could not initialize pthread. Falling back to non-threaded mode…"); + if (pthread_create(&gpdata->thread, NULL, rmplugin_x2go_main_thread, gp)) { + rm_plugin_service->protocol_plugin_set_error(gp, _("Could not initialize " + "pthread. Falling back to non-threaded mode…")); gpdata->thread = 0; return FALSE; } else { @@ -1256,35 +1334,85 @@ static gboolean remmina_plugin_x2go_open_connection(RemminaProtocolWidget *gp) { } } -static gboolean remmina_plugin_x2go_query_feature(RemminaProtocolWidget* gp, - const RemminaProtocolFeature* feature) { +static gboolean rmplugin_x2go_query_feature(RemminaProtocolWidget* gp, + const RemminaProtocolFeature* feature) +{ TRACE_CALL(__func__); return TRUE; } -static const RemminaProtocolFeature remmina_plugin_x2go_features[] = { - {REMMINA_PROTOCOL_FEATURE_TYPE_GTKSOCKET, - REMMINA_PLUGIN_X2GO_FEATURE_GTKSOCKET, NULL, NULL, NULL}, - {REMMINA_PROTOCOL_FEATURE_TYPE_END, - 0, NULL, NULL, NULL} +static const RemminaProtocolFeature rmplugin_x2go_features[] = { + {REMMINA_PROTOCOL_FEATURE_TYPE_GTKSOCKET, RMPLUGIN_X2GO_FEATURE_GTKSOCKET, NULL, NULL, NULL}, + {REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL} }; + +/** + * @brief This function builds a string like: "'value1', 'value2' and 'value3'" \n + * To be used in a loop. \n + * See rmplugin_x2go_string_setting_validator() for an example. + * + * @param max_elements Number of maximum elements. + * @param element_to_add Next element to add to the string + * @param current_element Which element is element_to_add? + * @param string The string to which `element_to_add` will be added. + */ +static gchar* rmplugin_x2go_enumeration_prettifier(const guint max_elements, + const guint current_element, + gchar* element_to_add, + gchar* string) +{ + if (max_elements > 2) { + if (current_element == max_elements - 1) { + // TRANSLATORS: Presumably you just want to translate 'and' into + // your language. + // (Except your listing-grammar differs from english.) + // 'value1', 'value2', 'valueN-1' and 'valueN' + return g_strdup_printf(_("%sand '%s'"), string, element_to_add); + } else if (current_element == max_elements - 2) { + // TRANSLATORS: Presumably you just want to leave it english. + // (Except your listing-grammar differs from english.) + // 'value1', 'value2', 'valueN-1' and 'valueN' + return g_strdup_printf(_("%s'%s' "), string, element_to_add); + } else { + // TRANSLATORS: Presumably you just want to leave it english. + // (Except your listing-grammar differs from english.) + // 'value1', 'value2', 'valueN-1' and 'valueN' + return g_strdup_printf(_("%s'%s', "), string, element_to_add); + } + } else if (max_elements == 2) { + if (current_element == max_elements - 1) { + // TRANSLATORS: Presumably you just want to translate 'and' into + // your language. + // (Except your listing-grammar differs from english.) + // 'value1' and 'value2' + return g_strdup_printf(_("%sand '%s'"), string, element_to_add); + } else { + // TRANSLATORS: Presumably you just want to leave it english. + // (Except your listing-grammar differs from english.) + // 'value1' and 'value2' + return g_strdup_printf(_("%s'%s' "), string, element_to_add); + } + } else { + return g_strdup(element_to_add); + } +} + /** * @brief Validator-functions are getting executed when the user wants to save profile - * settings. It uses the given data (See RemminaProtocolSetting array) to determine - * which strings are allowed and returns a end-user friendly error message. + * settings. It uses the given data (See RemminaProtocolSetting array) to determine + * which strings are allowed and returns a end-user friendly error message. * * @param key Key is the setting's name. * @param value Value to validate. * @param data Data needed for validation process. See RemminaProtocolSetting array. * * @returns End-user friendly and translated error message, explaining why the given - * value is invalid. If the given value is error-free then NULL gets returned. + * value is invalid. If the given value is error-free then NULL gets returned. * */ -static GError* remmina_plugin_x2go_string_setting_validator(gchar* key, - gchar* value, - gchar* data) { +static GError* rmplugin_x2go_string_setting_validator(gchar* key, gchar* value, gchar* data) +{ GError *error = NULL; if (!data) { @@ -1295,8 +1423,7 @@ static GError* remmina_plugin_x2go_string_setting_validator(gchar* key, } guint elements_amount = 0; - gchar **elements_list = remmina_plugin_x2go_split_string(data, ',', - &elements_amount); + gchar **elements_list = rmplugin_x2go_split_string(data, ',', &elements_amount); if (elements_amount <= 0 || elements_list == NULL) { // Something went wrong, there can't be less than 1 element! @@ -1323,38 +1450,8 @@ static GError* remmina_plugin_x2go_string_setting_validator(gchar* key, return NULL; } - if (elements_amount > 2) { - if (i == elements_amount - 1) { - // TRANSLATORS: Presumably you just want to translate 'and' - // into your language. (Except your listing-grammar differs from english.) - // 'value1', 'value2', 'valueN-1' and 'valueN' - data_str = g_strdup_printf(_("%sand '%s'"), data_str, element); - } else if (i == elements_amount - 2) { - // TRANSLATORS: Presumably you just want to leave it english. - // (Except your listing-grammar differs from english.) - // 'value1', 'value2', 'valueN-1' and 'valueN' - data_str = g_strdup_printf(_("%s'%s' "), data_str, element); - } else { - // TRANSLATORS: Presumably you just want to leave it english. - // (Except your listing-grammar differs from english.) - // 'value1', 'value2', 'valueN-1' and 'valueN' - data_str = g_strdup_printf(_("%s'%s', "), data_str, element); - } - } else if (elements_amount == 2) { - if (i == elements_amount - 1) { - // TRANSLATORS: Presumably you just want to translate 'and' - // into your language. (Except your listing-grammar differs from english.) - // 'value1' and 'value2' - data_str = g_strdup_printf(_("%sand '%s'"), data_str, element); - } else { - // TRANSLATORS: Presumably you just want to leave it english. - // (Except your listing-grammar differs from english.) - // 'value1' and 'value2' - data_str = g_strdup_printf(_("%s'%s' "), data_str, element); - } - } else { - data_str = g_strdup(element); - } + data_str = rmplugin_x2go_enumeration_prettifier(elements_amount, i, + element, data_str); } if (elements_amount > 1) { @@ -1371,25 +1468,24 @@ static GError* remmina_plugin_x2go_string_setting_validator(gchar* key, /** * @brief Validator-functions are getting executed when the user wants to save profile - * settings. It uses the given data (See RemminaProtocolSetting array) to determine - * if the given value is a valid integer is in range and returns - * a end-user friendly error message. + * settings. It uses the given data (See RemminaProtocolSetting array) to determine + * if the given value is a valid integer is in range and returns a end-user + * friendly error message. * * @param key Key is the setting's name. * @param value Value to validate. * @param data Data needed for validation process. See RemminaProtocolSetting array. * * @returns End-user friendly and translated error message, explaining why the given - * value is invalid. If the given value is error-free then NULL gets returned. + * value is invalid. If the given value is error-free then NULL gets returned. * */ -static GError* remmina_plugin_x2go_int_setting_validator(gchar* key, - gpointer value, - gchar* data) { +static GError* rmplugin_x2go_int_setting_validator(gchar* key, gpointer value, gchar* data) +{ GError *error = NULL; guint integer_amount = 0; - gchar **integer_list = remmina_plugin_x2go_split_string(data, ';', &integer_amount); + gchar **integer_list = rmplugin_x2go_split_string(data, ';', &integer_amount); if (integer_amount != 2 || integer_list == NULL) { // Something went wrong, there can't be more or less than 2 list entries. @@ -1449,7 +1545,7 @@ static GError* remmina_plugin_x2go_int_setting_validator(gchar* key, if (err == STR2INT_SUCCESS && (minimum > int_value || int_value > maximum)) { g_set_error(&error, 1, 1, _("Input must be a number between %i and %i."), - minimum, maximum); + minimum, maximum); } return error; @@ -1467,13 +1563,13 @@ static GError* remmina_plugin_x2go_int_setting_validator(gchar* key, * h) Validation callback method (Can be null. Every entry will be valid then.) * use following prototype: * gboolean mysetting_validator_method(gpointer key, gpointer value, - * gpointer validator_data); + * gpointer validator_data); * gpointer key is a gchar* containing the setting's name, * gpointer value contains the value which should be validated, * gpointer validator_data contains your passed data. */ -static const RemminaProtocolSetting remmina_plugin_x2go_basic_settings[] = { +static const RemminaProtocolSetting rmplugin_x2go_basic_settings[] = { {REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL, /* Validation Data */ NULL, /* Validation Method */ NULL}, @@ -1502,51 +1598,52 @@ static const RemminaProtocolSetting remmina_plugin_x2go_basic_settings[] = { /* Options to select (or custom user string) */ "pulse,esd,none", /* Tooltip */ N_("The X2Go server's sound system (default: 'pulse')."), /* Validation Data */ "pulse,esd,none", - /* Validation Method */ G_CALLBACK(remmina_plugin_x2go_string_setting_validator)}, + /* Validation Method */ G_CALLBACK(rmplugin_x2go_string_setting_validator)}, {REMMINA_PROTOCOL_SETTING_TYPE_COMBO, "clipboard", N_("Clipboard direction"), FALSE, /* Options to select (or custom user string) */ "none,server,client,both", /* Tooltip */ N_("Which direction should clipboard content be copied? " "(default: 'both')."), /* Validation Data */ "none,server,client,both", - /* Validation Method */ G_CALLBACK(remmina_plugin_x2go_string_setting_validator)}, + /* Validation Method */ G_CALLBACK(rmplugin_x2go_string_setting_validator)}, {REMMINA_PROTOCOL_SETTING_TYPE_INT, "dpi", N_("DPI resolution"), FALSE, NULL, /* Tooltip */ N_("Launch session with a specific resolution (in dots per inch). " "Must be between 20 and 400."), /* Validation Data */ "20;400", // "<min>;<max>;" - /* Validation Method */ G_CALLBACK(remmina_plugin_x2go_int_setting_validator)}, + /* Validation Method */ G_CALLBACK(rmplugin_x2go_int_setting_validator)}, {REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL}}; /* Protocol plugin definition and features */ -static RemminaProtocolPlugin remmina_plugin_x2go = { - REMMINA_PLUGIN_TYPE_PROTOCOL, // Type - PLUGIN_NAME, // Name - PLUGIN_DESCRIPTION, // Description - GETTEXT_PACKAGE, // Translation domain - PLUGIN_VERSION, // Version number - PLUGIN_APPICON, // Icon for normal connection - PLUGIN_APPICON, // Icon for SSH connection - remmina_plugin_x2go_basic_settings, // Array for basic settings - NULL, // Array for advanced settings - REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type - /* REMMINA_PROTOCOL_SSH_SETTING_NONE, // SSH settings type */ - remmina_plugin_x2go_features, // Array for available features - remmina_plugin_x2go_init, // Plugin initialization method - remmina_plugin_x2go_open_connection, // Plugin open connection method - remmina_plugin_x2go_close_connection, // Plugin connection-method closure - remmina_plugin_x2go_query_feature, // Query for available features - NULL, // Call a feature - NULL, // Send a keystroke - NULL, // Screenshot +static RemminaProtocolPlugin rmplugin_x2go = { + REMMINA_PLUGIN_TYPE_PROTOCOL, // Type + PLUGIN_NAME, // Name + PLUGIN_DESCRIPTION, // Description + GETTEXT_PACKAGE, // Translation domain + PLUGIN_VERSION, // Version number + PLUGIN_APPICON, // Icon for normal connection + PLUGIN_APPICON, // Icon for SSH connection + rmplugin_x2go_basic_settings, // Array for basic settings + NULL, // Array for advanced settings + REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type + /* REMMINA_PROTOCOL_SSH_SETTING_NONE, // SSH settings type */ + rmplugin_x2go_features, // Array for available features + rmplugin_x2go_init, // Plugin initialization method + rmplugin_x2go_open_connection, // Plugin open connection method + rmplugin_x2go_close_connection, // Plugin connection-method closure + rmplugin_x2go_query_feature, // Query for available features + NULL, // Call a feature + NULL, // Send a keystroke + NULL, // Screenshot }; -G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service) { +G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service) +{ TRACE_CALL("remmina_plugin_entry"); - remmina_plugin_service = service; + rm_plugin_service = service; bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - if (!service->register_plugin((RemminaPlugin *) &remmina_plugin_x2go)) { + if (!service->register_plugin((RemminaPlugin *) &rmplugin_x2go)) { return FALSE; } |