Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
www_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2020 Antenore Gatta & Giovanni Panozzo
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL.
27  * If you modify file(s) with this exception, you may extend this exception
28  * to your version of the file(s), but you are not obligated to do so.
29  * If you do not wish to do so, delete this exception statement from your
30  * version.
31  * If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  *
34  */
35 
36 #include "www_config.h"
37 
38 #include "common/remmina_plugin.h"
39 
40 #include <gdk/gdkkeysyms.h>
41 #include <gtk/gtk.h>
42 #include <glib.h>
43 #include <glib.h>
44 #include <glib/gprintf.h>
45 #include <stdlib.h>
46 
47 #include <webkit2/webkit2.h>
48 #if WEBKIT_CHECK_VERSION(2, 21, 1)
49 #include <jsc/jsc.h>
50 #endif
51 #include "www_utils.h"
52 #include "www_plugin.h"
53 
54 
55 #define GET_PLUGIN_DATA(gp) (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data")
56 
57 typedef struct _RemminaPluginWWWData {
59  GtkWidget * box;
60  WebKitSettings * settings;
61  WebKitWebContext * context;
62  WebKitWebsiteDataManager * data_mgr;
63  WebKitCredential * credentials;
64  WebKitAuthenticationRequest * request;
65  WebKitWebView * webview;
66  WebKitLoadEvent load_event;
67 
68  gchar * url;
69  gboolean authenticated;
72 
74 
76  WebKitDownload *download, RemminaProtocolWidget *gp)
77 {
78  TRACE_CALL(__func__);
79  webkit_download_set_allow_overwrite(download, TRUE);
80  g_signal_connect(G_OBJECT(download), "notify::response",
81  G_CALLBACK(remmina_plugin_www_response_received), gp);
82  g_signal_connect(download, "created-destination",
83  G_CALLBACK(remmina_plugin_www_notify_download), gp);
84 }
85 
86 void remmina_plugin_www_response_received(WebKitDownload *download, GParamSpec *ps, RemminaProtocolWidget *gp)
87 {
88  TRACE_CALL(__func__);
89  remmina_plugin_service->debug("Download response received");
90 }
91 
92 void remmina_plugin_www_notify_download(WebKitDownload *download, gchar *destination, RemminaProtocolWidget *gp)
93 {
94  TRACE_CALL(__func__);
95  remmina_plugin_service->debug("Download is finished");
96  const gchar *dest = webkit_download_get_destination(download);
97  www_utils_send_notification("www-plugin-download-completed-id", _("File downloaded"), dest);
98  //download(gp, webkit_download_get_response(download));
99  //webkit_download_cancel(download);
100 }
101 
103  WebKitWebView * webview,
104  WebKitPolicyDecision * decision,
105  WebKitPolicyDecisionType decision_type,
107 {
108  TRACE_CALL(__func__);
109 
110  gboolean res = TRUE;
111 
112  switch (decision_type) {
113  case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
114  remmina_plugin_www_decide_nav(decision, gp);
115  break;
116  case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
117  remmina_plugin_www_decide_newwin(decision, gp);
118  break;
119  case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
120  res = remmina_plugin_www_decide_resource(decision, gp);
121  break;
122  default:
123  webkit_policy_decision_ignore(decision);
124  break;
125  }
126  return res;
127 }
128 void remmina_plugin_www_decide_nav(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
129 {
130  TRACE_CALL(__func__);
131  remmina_plugin_service->debug("Policy decision navigation");
132  const gchar *url = NULL;
133  WebKitNavigationAction *a =
134  webkit_navigation_policy_decision_get_navigation_action(
135  WEBKIT_NAVIGATION_POLICY_DECISION(decision));
136 
137  switch (webkit_navigation_action_get_navigation_type(a)) {
138  case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED:
139  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_LINK_CLICKED");
140  url = webkit_uri_request_get_uri(
141  webkit_navigation_action_get_request(a));
142  remmina_plugin_service->debug("url is %s ", url);
143  break;
144  case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED:
145  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED");
146  break;
147  case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD:
148  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_BACK_FORWARD");
149  break;
150  case WEBKIT_NAVIGATION_TYPE_RELOAD:
151  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_RELOAD");
152  break;
153  case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
154  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED");
155  break;
156  case WEBKIT_NAVIGATION_TYPE_OTHER:
157  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_OTHER");
158  break;
159  default:
160  /* Do not navigate to links with a "_blank" target (popup) */
161  if (webkit_navigation_policy_decision_get_frame_name(
162  WEBKIT_NAVIGATION_POLICY_DECISION(decision))) {
163  webkit_policy_decision_ignore(decision);
164  } else {
165  /* Filter out navigation to different domain ? */
166  /* get action→urirequest, copy and load in new window+view
167  * on Ctrl+Click ? */
168  webkit_policy_decision_use(decision);
169  }
170  break;
171  }
172 }
173 
174 void remmina_plugin_www_on_create(WebKitWebView *webview, WebKitNavigationAction *a, RemminaProtocolWidget *gp)
175 {
176  TRACE_CALL(__func__);
177  remmina_plugin_service->debug("New web-view");
178 
179  const gchar *url = NULL;
180 
181  RemminaPluginWWWData *gpdata;
182  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
183 
184  switch (webkit_navigation_action_get_navigation_type(a)) {
185  case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED:
186  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_LINK_CLICKED");
187  url = webkit_uri_request_get_uri(
188  webkit_navigation_action_get_request(a));
189  remmina_plugin_service->debug("Downloading url %s ", url);
190  WebKitDownload *d = webkit_web_view_download_uri(gpdata->webview, url);
191 
192  break;
193  case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED:
194  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED");
195  break;
196  case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD:
197  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_BACK_FORWARD");
198  break;
199  case WEBKIT_NAVIGATION_TYPE_RELOAD:
200  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_RELOAD");
201  break;
202  case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
203  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED");
204  /* Filter domains here */
205  /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
206  * test for link clicked but no button ? */
207  url = webkit_uri_request_get_uri(
208  webkit_navigation_action_get_request(a));
209  remmina_plugin_service->debug("Trying to open url: %s", url);
210  webkit_web_view_load_uri(gpdata->webview, url);
211  break;
212  case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
213  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_OTHER");
214  /* Filter domains here */
215  /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
216  * test for link clicked but no button ? */
217  url = webkit_uri_request_get_uri(
218  webkit_navigation_action_get_request(a));
219  remmina_plugin_service->debug("Trying to open url: %s", url);
220  webkit_web_view_load_uri(gpdata->webview, url);
221  break;
222  default:
223  break;
224  }
225  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE is %d", webkit_navigation_action_get_navigation_type(a));
226 }
227 
228 void remmina_plugin_www_decide_newwin(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
229 {
230  TRACE_CALL(__func__);
231  remmina_plugin_service->debug("Policy decision new window");
232 
233  const gchar *url = NULL;
234 
235  RemminaPluginWWWData *gpdata;
236  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
237 
238  WebKitNavigationAction *a =
239  webkit_navigation_policy_decision_get_navigation_action(
240  WEBKIT_NAVIGATION_POLICY_DECISION(decision));
241 
242  switch (webkit_navigation_action_get_navigation_type(a)) {
243  case WEBKIT_NAVIGATION_TYPE_LINK_CLICKED:
244  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_LINK_CLICKED");
245  url = webkit_uri_request_get_uri(
246  webkit_navigation_action_get_request(a));
247  remmina_plugin_service->debug("Downloading url %s ", url);
248  WebKitDownload *d = webkit_web_view_download_uri(gpdata->webview, url);
249 
250 
251  break;
252  case WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED:
253  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_SUBMITTED");
254  break;
255  case WEBKIT_NAVIGATION_TYPE_BACK_FORWARD:
256  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_BACK_FORWARD");
257  break;
258  case WEBKIT_NAVIGATION_TYPE_RELOAD:
259  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_RELOAD");
260  break;
261  case WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED:
262  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_FORM_RESUBMITTED");
263  /* Filter domains here */
264  /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
265  * test for link clicked but no button ? */
266  url = webkit_uri_request_get_uri(
267  webkit_navigation_action_get_request(a));
268  remmina_plugin_service->debug("Trying to open url: %s", url);
269  webkit_web_view_load_uri(gpdata->webview, url);
270  break;
271  case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */
272  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE_OTHER");
273  /* Filter domains here */
274  /* If the value of “mouse-button” is not 0, then the navigation was triggered by a mouse event.
275  * test for link clicked but no button ? */
276  url = webkit_uri_request_get_uri(
277  webkit_navigation_action_get_request(a));
278  remmina_plugin_service->debug("Trying to open url: %s", url);
279  webkit_web_view_load_uri(gpdata->webview, url);
280  break;
281  default:
282  break;
283  }
284  remmina_plugin_service->debug("WEBKIT_NAVIGATION_TYPE is %d", webkit_navigation_action_get_navigation_type(a));
285 
286  webkit_policy_decision_ignore(decision);
287 }
288 gboolean remmina_plugin_www_decide_resource(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
289 {
290  TRACE_CALL(__func__);
291  remmina_plugin_service->debug("Policy decision resource");
292  WebKitResponsePolicyDecision *response_decision =
293  WEBKIT_RESPONSE_POLICY_DECISION(decision);
294  WebKitURIResponse *response =
295  webkit_response_policy_decision_get_response(response_decision);;
296  const gchar *request_uri = webkit_uri_response_get_uri(response);
297 
298  WebKitURIRequest *request;
299  WebKitWebResource *main_resource;
301  const char *mime_type;
302 
303  RemminaPluginWWWData *gpdata;
304  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
305 
306  mime_type = webkit_uri_response_get_mime_type(response);
307 
308  remmina_plugin_service->debug("The media type is %s", mime_type);
309 
310  /* If WebKit can't handle the media type, start the download
311  * process */
312  if (webkit_response_policy_decision_is_mime_type_supported(response_decision))
313  return FALSE;
314 
315  /* If it's not the main resource we don't need to set the document type. */
316  request = webkit_response_policy_decision_get_request(response_decision);
317  request_uri = webkit_uri_request_get_uri(request);
318  main_resource = webkit_web_view_get_main_resource(gpdata->webview);
319  if (g_strcmp0(webkit_web_resource_get_uri(main_resource), request_uri) != 0)
320  return FALSE;
321 
323  if (!strcmp(mime_type, "text/html") || !strcmp(mime_type, "text/plain"))
325  else if (!strcmp(mime_type, "application/xhtml+xml"))
327  else if (!strncmp(mime_type, "image/", 6))
329  else if (!strncmp(mime_type, "application/octet-stream", 6))
331 
332  remmina_plugin_service->debug("Document type is %i", type);
333 
334  /* FIXME: Maybe it makes more sense to have an API to query the media
335  * type when the load of a page starts than doing this here.
336  */
337  if (gpdata->document_type != type) {
338  gpdata->document_type = type;
339 
340  //g_object_notify_by_pspec (G_OBJECT (webview), obj_properties[PROP_DOCUMENT_TYPE]);
341  }
342 
343  webkit_policy_decision_download(decision);
344  return TRUE;
345 }
346 
347 static void remmina_www_web_view_js_finished(GObject *object, GAsyncResult *result, gpointer user_data)
348 {
349  TRACE_CALL(__func__);
350 
351  RemminaProtocolWidget *gp = (RemminaProtocolWidget *)user_data;
352  RemminaPluginWWWData *gpdata = GET_PLUGIN_DATA(gp);
353  WebKitJavascriptResult *js_result;
354  GError *error = NULL;
355 
356  js_result = webkit_web_view_run_javascript_finish(WEBKIT_WEB_VIEW(object), result, &error);
357  if (!js_result) {
358  g_warning("Could not run JavaScript code: %s", error->message);
359  g_error_free(error);
360  return;
361  }
362 
363 #if WEBKIT_CHECK_VERSION(2, 21, 1)
364  gchar *str_value;
365  JSCValue *value = webkit_javascript_result_get_js_value(js_result);
366  if (jsc_value_is_string(value) || jsc_value_is_boolean(value)) {
367  JSCException *exception;
368 
369  str_value = jsc_value_to_string(value);
370  exception = jsc_context_get_exception(jsc_value_get_context(value));
371  if (exception)
372  g_warning("Could not run JavaScript code: %s", jsc_exception_get_message(exception));
373  else
374  g_print("Script result: %s\n", str_value);
375  g_free(str_value);
376  } else {
377  str_value = jsc_value_to_string(value);
378  g_warning("Received something other than a string from JavaScript: %s", str_value);
379  g_free(str_value);
380  }
381 #endif
382  if (js_result) webkit_javascript_result_unref(js_result);
383  gpdata->formauthenticated = TRUE;
384 }
385 
387 {
388  TRACE_CALL(__func__);
389  return TRUE;
390 }
391 
392 static gboolean remmina_plugin_www_load_failed_tls_cb(WebKitWebView *webview,
393  gchar *failing_uri, GTlsCertificate *certificate,
394  GTlsCertificateFlags errors, RemminaProtocolWidget *gp)
395 {
396  TRACE_CALL(__func__);
397  /* Avoid failing if certificate is not good. TODO: Add widgets to let the user decide */
398  remmina_plugin_service->debug("Ignoring certificate and return TRUE");
399  return TRUE;
400 }
401 
403 {
404  TRACE_CALL(__func__);
405 
406  RemminaPluginWWWData *gpdata;
407  RemminaFile *remminafile;
408  gchar *datapath;
409  gchar *cache_dir;
410 
411  gpdata = g_new0(RemminaPluginWWWData, 1);
412  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
413 
414  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
415 
416  /* RemminaPluginWWWData initialization */
417 
418  gpdata->authenticated = FALSE;
419  gpdata->formauthenticated = FALSE;
421 
422  datapath = g_build_path("/",
423  g_path_get_dirname(remmina_plugin_service->file_get_path(remminafile)),
424  PLUGIN_NAME,
425  NULL);
426  cache_dir = g_build_path("/", datapath, "cache", NULL);
427  remmina_plugin_service->debug("WWW data path is %s", datapath);
428 
429  if (datapath) {
430  gchar *indexeddb_dir = g_build_filename(datapath, "indexeddb", NULL);
431  gchar *local_storage_dir = g_build_filename(datapath, "local_storage", NULL);
432  gchar *applications_dir = g_build_filename(datapath, "applications", NULL);
433  gchar *websql_dir = g_build_filename(datapath, "websql", NULL);
434  gpdata->data_mgr = webkit_website_data_manager_new(
435  "disk-cache-directory", cache_dir,
436  "indexeddb-directory", indexeddb_dir,
437  "local-storage-directory", local_storage_dir,
438  "offline-application-cache-directory", applications_dir,
439  "websql-directory", websql_dir,
440  NULL
441  );
442  g_free(indexeddb_dir);
443  g_free(local_storage_dir);
444  g_free(applications_dir);
445  g_free(websql_dir);
446  g_free(datapath);
447  } else {
448  gpdata->data_mgr = webkit_website_data_manager_new_ephemeral();
449  }
450 
451 
452  if (remmina_plugin_service->file_get_string(remminafile, "server"))
453  gpdata->url = g_strdup(remmina_plugin_service->file_get_string(remminafile, "server"));
454  else
455  gpdata->url = "https://remmina.org";
456  g_info("URL is set to %s", gpdata->url);
457 
458  gpdata->settings = webkit_settings_new();
459  gpdata->context = webkit_web_context_new_with_website_data_manager(gpdata->data_mgr);
460 
461  /* enable-fullscreen, default TRUE, TODO: Try FALSE */
462 
463 #ifdef DEBUG
464  /* Turn on the developer extras */
465  webkit_settings_set_enable_developer_extras(gpdata->settings, TRUE);
466  webkit_settings_set_enable_write_console_messages_to_stdout(gpdata->settings, TRUE);
467 #endif
468 
469  /* allow-file-access-from-file-urls */
470  webkit_settings_set_allow_file_access_from_file_urls(gpdata->settings, TRUE);
471  /* allow-modal-dialogs */
472  webkit_settings_set_allow_modal_dialogs(gpdata->settings, TRUE);
473  /* enable-caret-browsing */
474  webkit_settings_set_enable_caret_browsing(gpdata->settings, TRUE);
475  /* enable-html5-database */
476  webkit_settings_set_enable_html5_database(gpdata->settings, TRUE);
477 
478  /* user-agent. */
479  if (remmina_plugin_service->file_get_string(remminafile, "user-agent")) {
480  gchar *useragent = g_strdup(remmina_plugin_service->file_get_string(remminafile, "user-agent"));
481  webkit_settings_set_user_agent(gpdata->settings, useragent);
482  g_info("User Agent set to: %s", useragent);
483  g_free(useragent);
484  }
485  /* enable-java */
486  if (remmina_plugin_service->file_get_int(remminafile, "enable-java", FALSE)) {
487  webkit_settings_set_enable_java(gpdata->settings, TRUE);
488  g_info("Enable Java");
489  }
490  /* enable-smooth-scrolling */
491  if (remmina_plugin_service->file_get_int(remminafile, "enable-smooth-scrolling", FALSE)) {
492  webkit_settings_set_enable_smooth_scrolling(gpdata->settings, TRUE);
493  g_info("enable-smooth-scrolling enabled");
494  }
495  /* enable-spatial-navigation */
496  if (remmina_plugin_service->file_get_int(remminafile, "enable-spatial-navigation", FALSE)) {
497  webkit_settings_set_enable_spatial_navigation(gpdata->settings, TRUE);
498  g_info("enable-spatial-navigation enabled");
499  }
500  /* enable-webaudio */
501  if (remmina_plugin_service->file_get_int(remminafile, "enable-webaudio", FALSE)) {
502  webkit_settings_set_enable_webaudio(gpdata->settings, TRUE);
503  g_info("enable-webaudio enabled");
504  }
505  /* enable-plugins */
506  if (remmina_plugin_service->file_get_int(remminafile, "enable-plugins", FALSE)) {
507  webkit_settings_set_enable_plugins(gpdata->settings, TRUE);
508  g_info("Enable plugins");
509  }
510  /* enable-webgl */
511  if (remmina_plugin_service->file_get_int(remminafile, "enable-webgl", FALSE)) {
512  webkit_settings_set_enable_webgl(gpdata->settings, TRUE);
513  webkit_settings_set_enable_accelerated_2d_canvas(gpdata->settings, TRUE);
514  g_info("enable-webgl enabled");
515  }
516 
517  if (remmina_plugin_service->file_get_int(remminafile, "ignore-tls-errors", FALSE)) {
518  webkit_web_context_set_tls_errors_policy(gpdata->context, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
519  g_info("Ignore TLS errors");
520  }
521  if (remmina_plugin_service->file_get_string(remminafile, "proxy-url")) {
522  gchar *proxyurl = g_strdup(remmina_plugin_service->file_get_string(remminafile, "proxy-url"));
523  WebKitNetworkProxySettings *proxy_settings = webkit_network_proxy_settings_new (proxyurl, NULL);
524  webkit_web_context_set_network_proxy_settings(gpdata->context, WEBKIT_NETWORK_PROXY_MODE_CUSTOM, proxy_settings);
525  webkit_network_proxy_settings_free(proxy_settings);
526  g_free(proxyurl);
527  }
528 
529  webkit_web_context_set_automation_allowed(gpdata->context, TRUE);
530  webkit_settings_set_javascript_can_open_windows_automatically(gpdata->settings, TRUE);
531  webkit_settings_set_allow_modal_dialogs(gpdata->settings, TRUE);
537  if (!webkit_settings_get_enable_frame_flattening(gpdata->settings)) {
538  webkit_settings_set_enable_frame_flattening(gpdata->settings, true);
539  }
540  webkit_settings_set_enable_resizable_text_areas(gpdata->settings, true);
541 
542  g_signal_connect(G_OBJECT(gpdata->context), "download-started",
543  G_CALLBACK(remmina_plugin_www_download_started), gp);
544 }
545 
546 static gboolean remmina_plugin_www_on_auth(WebKitWebView *webview, WebKitAuthenticationRequest *request, RemminaProtocolWidget *gp)
547 {
548  TRACE_CALL(__func__);
549 
550  gchar *s_username, *s_password;
551  gint ret;
552  RemminaPluginWWWData *gpdata;
553  gboolean save;
554  gboolean disablepasswordstoring;
555  RemminaFile *remminafile;
556 
557  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
558 
559  g_info("Authenticate");
560 
561  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
562 
563  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
564  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
566  _("Enter WWW authentication credentials"),
567  remmina_plugin_service->file_get_string(remminafile, "username"),
568  remmina_plugin_service->file_get_string(remminafile, "password"),
569  NULL,
570  NULL);
571  if (ret == GTK_RESPONSE_OK) {
572  s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
573  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
574 
575  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
576  if (save) {
577  remmina_plugin_service->file_set_string(remminafile, "username", s_username);
578  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
579  } else {
580  remmina_plugin_service->file_set_string(remminafile, "username", NULL);
581  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
582  }
583  if (request) {
584  gpdata->credentials = webkit_credential_new(
585  g_strdup(s_username),
586  g_strdup(s_password),
587  WEBKIT_CREDENTIAL_PERSISTENCE_FOR_SESSION);
588  webkit_authentication_request_authenticate(request, gpdata->credentials);
589  webkit_credential_free(gpdata->credentials);
590  }
591  if (s_username) g_free(s_username);
592  if (s_password) g_free(s_password);
593  gpdata->authenticated = TRUE;
594  } else {
595  gpdata->authenticated = FALSE;
596  }
597 
598  return gpdata->authenticated;
599 }
600 
601 static void remmina_plugin_www_form_auth(WebKitWebView *webview,
602  WebKitLoadEvent load_event, RemminaProtocolWidget *gp)
603 {
604  TRACE_CALL(__func__);
605  gchar *s_js;
606  GString *jsstr;
607  RemminaPluginWWWData *gpdata;
608  RemminaFile *remminafile;
609  gchar *remmina_dir;
610  gchar *www_js_file = NULL;
611  GError *error = NULL;
612 
613  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
614  if (gpdata && !gpdata->formauthenticated)
615  gpdata->formauthenticated = FALSE;
616 
617  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
618 
619  remmina_plugin_service->debug("load-changed emitted");
620 
621 
622  const gchar *const *dirs = g_get_system_data_dirs();
623 
624  unsigned int i = 0;
625  for (i = 0; dirs[i] != NULL; ++i) {
626  remmina_dir = g_build_path("/", dirs[i], "remmina", "res", NULL);
627  GDir *system_data_dir = g_dir_open(remmina_dir, 0, &error);
628  // ignoring this error is ok, because the folder may not exists
629  if (error) {
630  g_error_free(error);
631  error = NULL;
632  } else {
633  if (system_data_dir) {
634  g_dir_close(system_data_dir);
635  g_free(www_js_file);
636  www_js_file = g_strdup_printf("%s/www-js.js", remmina_dir);
637  if (g_file_test(www_js_file, G_FILE_TEST_EXISTS))
638  break;
639  }
640  }
641  g_free(remmina_dir);
642  }
643 
644  switch (load_event) {
645  case WEBKIT_LOAD_STARTED:
646  remmina_plugin_service->debug("Load started");
647  break;
648  case WEBKIT_LOAD_REDIRECTED:
649  remmina_plugin_service->debug("Load redirected");
650  break;
651  case WEBKIT_LOAD_COMMITTED:
652  /* The load is being performed. Current URI is
653  * the final one and it won't change unless a new
654  * load is requested or a navigation within the
655  * same page is performed
656  * uri = webkit_web_view_get_uri (webview); */
657  remmina_plugin_service->debug("Load committed");
658  break;
659  case WEBKIT_LOAD_FINISHED:
660  /* Load finished, we can now set user/password
661  * in the HTML form */
662  remmina_plugin_service->debug("Load finished");
663  if (gpdata && gpdata->formauthenticated == TRUE)
664  break;
665 
666  if (remmina_plugin_service->file_get_string(remminafile, "username") ||
667  remmina_plugin_service->file_get_string(remminafile, "password")) {
668  remmina_plugin_service->debug("Authentication is enabled");
669  if (www_js_file) {
670  error = NULL;
671  if (g_file_get_contents(www_js_file, &s_js, NULL, &error)) {
672  jsstr = g_string_new(s_js);
673  if (remmina_plugin_service->file_get_string(remminafile, "username"))
674  www_utils_string_replace_all(jsstr, "USRPLACEHOLDER",
675  remmina_plugin_service->file_get_string(remminafile, "username"));
676  if (remmina_plugin_service->file_get_string(remminafile, "password"))
677  www_utils_string_replace_all(jsstr, "PWDPLACEHOLDER",
678  remmina_plugin_service->file_get_string(remminafile, "password"));
679  s_js = g_string_free(jsstr, FALSE);
680 
681  if (!s_js || s_js[0] == '\0') {
682  break;
683  } else {
684  remmina_plugin_service->debug("Trying to send this JavaScript: %s", s_js);
685  webkit_web_view_run_javascript(
686  webview,
687  s_js,
688  NULL,
690  gp);
691  g_free(s_js);
692  }
693  } else {
694  if (error) {
695  remmina_plugin_service->debug("Unable to read file: %s\n", error->message);
696  g_error_free(error);
697  } else {
698  remmina_plugin_service->debug("Unable to read file. No error returned from glib.\n");
699  }
700  }
701  }
702  }
703  break;
704  }
705 }
706 
708 {
709  TRACE_CALL(__func__);
710  RemminaPluginWWWData *gpdata;
711  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
712 
713  webkit_web_view_stop_loading(gpdata->webview);
714  webkit_web_view_try_close(gpdata->webview);
715 
716  if (gpdata->url) g_free(gpdata->url);
717  gpdata->authenticated = FALSE;
718  gpdata->formauthenticated = FALSE;
719  gpdata->webview = NULL;
720  gpdata->data_mgr = NULL;
721  gpdata->settings = NULL;
722  gpdata->context = NULL;
723 
724  /* Remove instance->context from gp object data to avoid double free */
725  g_object_steal_data(G_OBJECT(gp), "plugin-data");
726 
727  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
728  return FALSE;
729 }
730 
732 {
733  TRACE_CALL(__func__);
734 
735  RemminaPluginWWWData *gpdata;
736  RemminaFile *remminafile;
737 
738  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
739 
740  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
741 
742  gpdata->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
743  gtk_container_add(GTK_CONTAINER(gp), gpdata->box);
744 
745  remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->box);
746 
747  gpdata->webview = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(gpdata->context));
748  webkit_web_view_set_settings(gpdata->webview, gpdata->settings);
749 
750  if (remmina_plugin_service->file_get_string(remminafile, "username") ||
751  remmina_plugin_service->file_get_string(remminafile, "password")) {
752  remmina_plugin_service->debug("Authentication is enabled");
753  remmina_plugin_www_on_auth(gpdata->webview, NULL, gp);
754  }
755 
756  //"signal::load-failed-with-tls-errors", G_CALLBACK(remmina_plugin_www_load_failed_tls_cb), gp,
757  g_object_connect(
758  G_OBJECT(gpdata->webview),
759  "signal::create", G_CALLBACK(remmina_plugin_www_on_create), gp,
760  "signal::load-changed", G_CALLBACK(remmina_plugin_www_form_auth), gp,
761  "signal::authenticate", G_CALLBACK(remmina_plugin_www_on_auth), gp,
762  "signal::decide-policy", G_CALLBACK(remmina_plugin_www_decide_policy_cb), gp,
763  NULL);
764 
765  gtk_widget_set_hexpand(GTK_WIDGET(gpdata->webview), TRUE);
766  gtk_widget_set_vexpand(GTK_WIDGET(gpdata->webview), TRUE);
767  gtk_container_add(GTK_CONTAINER(gpdata->box), GTK_WIDGET(gpdata->webview));
768  webkit_web_view_load_uri(gpdata->webview, gpdata->url);
769 #ifdef DEBUG
770  if (remmina_plugin_service->file_get_int(remminafile, "enable-webinspector", FALSE)) {
771  g_info("WebInspector enabled");
772  WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(gpdata->webview));
773  webkit_web_inspector_attach(inspector);
774  webkit_web_inspector_show(WEBKIT_WEB_INSPECTOR(inspector));
775  }
776 #endif
777  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
778  gtk_widget_show_all(gpdata->box);
779 
780  return TRUE;
781 }
782 
783 static void remmina_plugin_www_save_snapshot(GObject *object, GAsyncResult *result, RemminaProtocolWidget *gp)
784 {
785  TRACE_CALL(__func__);
786 
787  WebKitWebView *webview = WEBKIT_WEB_VIEW(object);
788 
789  RemminaFile *remminafile;
790 
791  GError *err = NULL;
792  cairo_surface_t *surface;
793  //unsigned char* buffer;
794  int width;
795  int height;
796  GdkPixbuf *screenshot;
797  GString *pngstr;
798  gchar *pngname;
799  //cairo_forma_t* cairo_format;
800  GDateTime *date = g_date_time_new_now_utc();
801 
802  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
803 
804  surface = webkit_web_view_get_snapshot_finish(WEBKIT_WEB_VIEW(webview), result, &err);
805  if (err)
806  g_warning("An error happened generating the snapshot: %s\n", err->message);
807  //buffer = cairo_image_surface_get_data (surface);
808  width = cairo_image_surface_get_width(surface);
809  height = cairo_image_surface_get_height(surface);
810  //cairo_format = cairo_image_surface_get_format (surface);
811 
812  screenshot = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
813  if (screenshot == NULL)
814  remmina_plugin_service->debug("WWW: gdk_pixbuf_get_from_surface failed");
815 
816  pngstr = g_string_new(g_strdup_printf("%s/%s.png",
817  remmina_plugin_service->pref_get_value("screenshot_path"),
818  remmina_plugin_service->pref_get_value("screenshot_name")));
819  www_utils_string_replace_all(pngstr, "%p",
820  remmina_plugin_service->file_get_string(remminafile, "name"));
821  www_utils_string_replace_all(pngstr, "%h", "URL");
822  www_utils_string_replace_all(pngstr, "%Y",
823  g_strdup_printf("%d", g_date_time_get_year(date)));
824  www_utils_string_replace_all(pngstr, "%m", g_strdup_printf("%d",
825  g_date_time_get_month(date)));
826  www_utils_string_replace_all(pngstr, "%d",
827  g_strdup_printf("%d", g_date_time_get_day_of_month(date)));
828  www_utils_string_replace_all(pngstr, "%H",
829  g_strdup_printf("%d", g_date_time_get_hour(date)));
830  www_utils_string_replace_all(pngstr, "%M",
831  g_strdup_printf("%d", g_date_time_get_minute(date)));
832  www_utils_string_replace_all(pngstr, "%S",
833  g_strdup_printf("%f", g_date_time_get_seconds(date)));
834  g_date_time_unref(date);
835  pngname = g_string_free(pngstr, FALSE);
836  remmina_plugin_service->debug("Saving screenshot as %s", pngname);
837 
838  cairo_surface_write_to_png(surface, pngname);
839  if (g_file_test(pngname, G_FILE_TEST_EXISTS))
840  www_utils_send_notification("www-plugin-screenshot-is-ready-id", _("Screenshot taken"), pngname);
841 
842  cairo_surface_destroy(surface);
843 }
845 {
846  TRACE_CALL(__func__);
847  RemminaPluginWWWData *gpdata;
848  gpdata = (RemminaPluginWWWData *)g_object_get_data(G_OBJECT(gp), "plugin-data");
849 
850  webkit_web_view_get_snapshot(gpdata->webview,
851  WEBKIT_SNAPSHOT_REGION_FULL_DOCUMENT,
852  WEBKIT_SNAPSHOT_OPTIONS_NONE,
853  NULL,
854  (GAsyncReadyCallback)remmina_plugin_www_save_snapshot,
855  gp);
856  return FALSE;
857 }
858 
859 /* Array of RemminaProtocolSetting for basic settings.
860  * Each item is composed by:
861  * a) RemminaProtocolSettingType for setting type
862  * b) Setting name
863  * c) Setting description
864  * d) Compact disposition
865  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
866  * f) Setting Tooltip
867  */
869 {
870  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "server", N_("URL"), FALSE, NULL, N_("http://address or https://address") },
871  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL },
872  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("Password"), FALSE, NULL, NULL },
873  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
874 };
875 
876 /* Array of RemminaProtocolSetting for advanced settings.
877  * Each item is composed by:
878  * a) RemminaProtocolSettingType for setting type
879  * b) Setting name
880  * c) Setting description
881  * d) Compact disposition
882  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
883  * f) Setting Tooltip
884  */
886 {
887  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "user-agent", N_("User agent"), FALSE, NULL, NULL },
888  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "proxy-url", N_("Proxy URL"), FALSE, NULL, N_("E.g. https://example.org, socks://mysocks:1080") },
889  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-java", N_("Turn on Java support"), TRUE, NULL, NULL },
890  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-smooth-scrolling", N_("Turn on smooth scrolling"), TRUE, NULL, NULL },
891  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-spatial-navigation", N_("Turn on spatial navigation"), TRUE, NULL, NULL },
892  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-plugins", N_("Turn on plugin support"), TRUE, NULL, NULL },
893  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-webgl", N_("Turn on WebGL support"), TRUE, NULL, NULL },
894  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-webaudio", N_("Turn on HTML5 audio support"), TRUE, NULL, NULL },
895  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "ignore-tls-errors", N_("Ignore TLS errors"), TRUE, NULL, NULL },
896  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
897 #ifdef DEBUG
898  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enable-webinspector", N_("Turn on Web Inspector"), TRUE, NULL, NULL },
899 #endif
900  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
901 };
902 
903 /* Array for available features.
904  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
906 {
907  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
908 };
909 
910 /* Protocol plugin definition and features */
912 {
914  PLUGIN_NAME, // Name
915  PLUGIN_DESCRIPTION, // Description
916  GETTEXT_PACKAGE, // Translation domain
917  PLUGIN_VERSION, // Version number
918  PLUGIN_APPICON, // Icon for normal connection
919  NULL, // Icon for SSH connection
920  remmina_plugin_www_basic_settings, // Array for basic settings
921  remmina_plugin_www_advanced_settings, // Array for advanced settings
922  REMMINA_PROTOCOL_SSH_SETTING_NONE, // SSH settings type
923  remmina_www_features, // Array for available features
924  remmina_plugin_www_init, // Plugin initialization
925  remmina_plugin_www_open_connection, // Plugin open connection
926  remmina_plugin_www_close_connection, // Plugin close connection
927  remmina_www_query_feature, // Query for available features
928  NULL, // Call feature
929  NULL, // Send keystroke
930  remmina_plugin_www_get_snapshot // Capture screenshot
931 };
932 
933 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
934 {
935  TRACE_CALL(__func__);
936  remmina_plugin_service = service;
937 
938  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
939  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
940 
941 
942  if (!service->register_plugin((RemminaPlugin *)&remmina_plugin))
943  return FALSE;
944  return TRUE;
945 }
static void remmina_plugin_www_save_snapshot(GObject *object, GAsyncResult *result, RemminaProtocolWidget *gp)
Definition: www_plugin.c:783
static void remmina_plugin_www_init(RemminaProtocolWidget *gp)
Definition: www_plugin.c:402
void remmina_plugin_www_decide_newwin(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
Definition: www_plugin.c:228
WebKitCredential * credentials
Definition: www_plugin.c:63
gboolean formauthenticated
Definition: www_plugin.c:70
static gboolean remmina_plugin_www_close_connection(RemminaProtocolWidget *gp)
Definition: www_plugin.c:707
static gboolean remmina_plugin_www_get_snapshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
Definition: www_plugin.c:844
void(* debug)(const gchar *fmt,...)
Definition: plugin.h:214
WWWWebViewDocumentType
Definition: www_plugin.h:42
static gboolean remmina_plugin_www_on_auth(WebKitWebView *webview, WebKitAuthenticationRequest *request, RemminaProtocolWidget *gp)
Definition: www_plugin.c:546
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
static RemminaPluginService * remmina_plugin_service
Definition: www_plugin.c:73
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: www_plugin.c:933
static void remmina_plugin_www_form_auth(WebKitWebView *webview, WebKitLoadEvent load_event, RemminaProtocolWidget *gp)
Definition: www_plugin.c:601
WebKitWebContext * context
Definition: www_plugin.c:61
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
static gboolean remmina_plugin_www_load_failed_tls_cb(WebKitWebView *webview, gchar *failing_uri, GTlsCertificate *certificate, GTlsCertificateFlags errors, RemminaProtocolWidget *gp)
Definition: www_plugin.c:392
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
static gboolean remmina_plugin_www_open_connection(RemminaProtocolWidget *gp)
Definition: www_plugin.c:731
WebKitWebView * webview
Definition: www_plugin.c:65
guint www_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
Definition: www_utils.c:137
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
WWWWebViewDocumentType document_type
Definition: www_plugin.c:58
static const RemminaProtocolFeature remmina_www_features[]
Definition: www_plugin.c:905
gboolean(* protocol_plugin_init_get_savepassword)(RemminaProtocolWidget *gp)
Definition: plugin.h:178
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
static const RemminaProtocolSetting remmina_plugin_www_basic_settings[]
Definition: www_plugin.c:868
static const RemminaProtocolSetting remmina_plugin_www_advanced_settings[]
Definition: www_plugin.c:885
static gboolean remmina_www_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: www_plugin.c:386
void remmina_plugin_www_download_started(WebKitWebContext *context, WebKitDownload *download, RemminaProtocolWidget *gp)
Definition: www_plugin.c:75
gchar *(* pref_get_value)(const gchar *key)
Definition: plugin.h:207
WebKitLoadEvent load_event
Definition: www_plugin.c:66
gboolean remmina_plugin_www_decide_resource(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
Definition: www_plugin.c:288
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:172
struct _RemminaPluginWWWData RemminaPluginWWWData
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
const gchar *(* file_get_path)(RemminaFile *remminafile)
Definition: plugin.h:198
void remmina_plugin_www_decide_nav(WebKitPolicyDecision *decision, RemminaProtocolWidget *gp)
Definition: www_plugin.c:128
void remmina_plugin_www_response_received(WebKitDownload *download, GParamSpec *ps, RemminaProtocolWidget *gp)
Definition: www_plugin.c:86
static RemminaProtocolPlugin remmina_plugin
Definition: www_plugin.c:911
gchar *(* protocol_plugin_init_get_username)(RemminaProtocolWidget *gp)
Definition: plugin.h:175
gchar *(* protocol_plugin_init_get_password)(RemminaProtocolWidget *gp)
Definition: plugin.h:176
WebKitSettings * settings
Definition: www_plugin.c:60
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
void www_utils_send_notification(const gchar *notification_id, const gchar *notification_title, const gchar *notification_message)
Definition: www_utils.c:53
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:199
gboolean authenticated
Definition: www_plugin.c:69
static gboolean remmina_plugin_www_decide_policy_cb(WebKitWebView *webview, WebKitPolicyDecision *decision, WebKitPolicyDecisionType decision_type, RemminaProtocolWidget *gp)
Definition: www_plugin.c:102
void remmina_plugin_www_notify_download(WebKitDownload *download, gchar *destination, RemminaProtocolWidget *gp)
Definition: www_plugin.c:92
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
WebKitAuthenticationRequest * request
Definition: www_plugin.c:64
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
static void remmina_www_web_view_js_finished(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: www_plugin.c:347
WebKitWebsiteDataManager * data_mgr
Definition: www_plugin.c:62
void remmina_plugin_www_on_create(WebKitWebView *webview, WebKitNavigationAction *a, RemminaProtocolWidget *gp)
Definition: www_plugin.c:174