mirror of
https://gitlab.com/Remmina/Remmina.git
synced 2025-04-29 08:22:32 +08:00
Bring back remmina server features with new additions
This commit is contained in:
parent
b2686ae53e
commit
8920157bcc
@ -498,6 +498,33 @@ if(WITH_TRACE_CALLS)
|
||||
add_definitions(-DWITH_TRACE_CALLS)
|
||||
endif()
|
||||
|
||||
option(WITH_NEWS "Enable online news check" ON)
|
||||
if(WITH_NEWS)
|
||||
message(STATUS "Enabling news check connecting to remmina.org")
|
||||
add_definitions(-DWITH_NEWS)
|
||||
else()
|
||||
message(STATUS "Disabling news check connecting to remmina.org")
|
||||
add_definitions(-DDISABLE_NEWS)
|
||||
endif()
|
||||
|
||||
option(WITH_STATS "Enable stats" ON)
|
||||
if(WITH_STATS)
|
||||
message(STATUS "Enabling stats")
|
||||
add_definitions(-DWITH_STATS)
|
||||
else()
|
||||
message(STATUS "Disabling stats")
|
||||
add_definitions(-DDISABLE_STATS)
|
||||
endif()
|
||||
|
||||
option(WITH_TIP "Enable tip of the day" ON)
|
||||
if(WITH_TIP)
|
||||
message(STATUS "Enabling tip of the day")
|
||||
add_definitions(-DWITH_TIP)
|
||||
else()
|
||||
message(STATUS "Disabling tip of the day")
|
||||
add_definitions(-DDISABLE_TIP)
|
||||
endif()
|
||||
|
||||
option(WITH_MANPAGES "Build with MANPAGES" ON)
|
||||
if(WITH_MANPAGES)
|
||||
message(STATUS "Enabling man pages.")
|
||||
|
@ -33,7 +33,6 @@
|
||||
find_package(PkgConfig)
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
#pkg_check_modules(_WEBKIT2GTK webkit2gtk-4.0)
|
||||
pkg_search_module(_WEBKIT2GTK webkit2gtk-4.1 webkit2gtk-4.0)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
|
||||
@ -50,6 +49,7 @@ find_library(WEBKIT2GTK_LIB
|
||||
${_WEBKIT2GTK_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(WEBKIT2GTK DEFAULT_MSG WEBKIT2GTK_LIB WEBKIT2GTK_INCLUDE_DIR)
|
||||
|
281
data/ui/remmina_bug_report.glade
Normal file
281
data/ui/remmina_bug_report.glade
Normal file
@ -0,0 +1,281 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.24"/>
|
||||
<object class="GtkDialog" id="RemminaBugReportDialog">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="default-width">800</property>
|
||||
<property name="default-height">600</property>
|
||||
<property name="icon-name">org.remmina.Remmina</property>
|
||||
<property name="type-hint">normal</property>
|
||||
<property name="gravity">center</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="bug_report_box">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="bug_report_buttons">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="bug_report_include_system_info_check_button">
|
||||
<property name="label" translatable="yes">Include System Info</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="bug_report_debug_data_check_button">
|
||||
<property name="label" translatable="yes">Include Debug Data</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw-indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="bug_report_submit_button">
|
||||
<property name="label" translatable="yes">Submit</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="margin-right">15</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bug_report_fields_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<!-- n-columns=2 n-rows=2 -->
|
||||
<object class="GtkGrid" id="bug_report_info_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="column-spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_name_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Name/Username:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="bug_report_name_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="max-length">80</property>
|
||||
<property name="activates-default">True</property>
|
||||
<property name="placeholder-text" translatable="yes">Name</property>
|
||||
<property name="input-purpose">name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_email_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">E-mail:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="bug_report_email_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="max-length">80</property>
|
||||
<property name="placeholder-text" translatable="yes">Email</property>
|
||||
<property name="input-purpose">email</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_title_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Bug Title</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="bug_report_title_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="max-length">400</property>
|
||||
<property name="placeholder-text" translatable="yes">Bug Title</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_description_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Bug Description</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<property name="propagate-natural-width">True</property>
|
||||
<property name="propagate-natural-height">True</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="bug_report_description_textview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="left-margin">5</property>
|
||||
<property name="right-margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_disclaimer">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="label" translatable="yes">Clicking "Submit" will send this to Remmina's GitLab page and automatically create a new issue on the issue tracker.</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="max-width-chars">80</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">10</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bug_report_status_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bug_report_submit_status_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-bottom">20</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Remmina Report Bug</property>
|
||||
<property name="show-close-button">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
100
data/ui/remmina_info.glade
Normal file
100
data/ui/remmina_info.glade
Normal file
@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<!-- interface-license-type gplv2 -->
|
||||
<!-- interface-name Remmina news widget -->
|
||||
<!-- interface-description A widget that show the release notes and some quick settings -->
|
||||
<!-- interface-copyright Antenore Gatta and Giovanni Panozzo -->
|
||||
<!-- interface-authors Antenore Gatta and Giovanni Panozzo -->
|
||||
<object class="GtkDialog" id="RemminaInfoDialog">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="default-width">640</property>
|
||||
<property name="default-height">480</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<property name="urgency-hint">True</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="remmina_info_button_close">
|
||||
<property name="label" translatable="yes">Close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="remmina_info_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<property name="min-content-height">380</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="remmina_info_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">18</property>
|
||||
<property name="margin-right">18</property>
|
||||
<property name="margin-top">18</property>
|
||||
<property name="margin-bottom">18</property>
|
||||
<property name="label" translatable="yes"><big><b>The news failed to load</b></big>
|
||||
|
||||
<span>
|
||||
<a href="https://gitlab.com/Remmina/Remmina/-/tags/" title="Remmina release notes"><i>Visit the website to read the release notes</i></a>.
|
||||
</span></property>
|
||||
<property name="use-markup">True</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap-mode">word-char</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
@ -331,6 +331,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem_bug_report">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="action-name">app.bug_report</property>
|
||||
<property name="label" translatable="yes">Report Bug</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="menuitem_help_separator1">
|
||||
<property name="visible">True</property>
|
||||
|
@ -1,272 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<!-- interface-license-type gplv2 -->
|
||||
<!-- interface-name Remmina news widget -->
|
||||
<!-- interface-description A widget that show the release notes and some quick settings -->
|
||||
<!-- interface-copyright Antenore Gatta and Giovanni Panozzo -->
|
||||
<!-- interface-authors Antenore Gatta and Giovanni Panozzo -->
|
||||
<object class="GtkDialog" id="RemminaNewsDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="default_width">640</property>
|
||||
<property name="default_height">480</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="urgency_hint">True</property>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="rmnews_button_close">
|
||||
<property name="label" translatable="yes">Close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="rmnews_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">380</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
<child>
|
||||
<object class="GtkViewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="rmnews_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">18</property>
|
||||
<property name="margin_right">18</property>
|
||||
<property name="margin_top">18</property>
|
||||
<property name="margin_bottom">18</property>
|
||||
<property name="label" translatable="yes"><big><b>The news are turned off</b></big>
|
||||
|
||||
<span>
|
||||
Turning on news means the program connects to a Remmina server to download the release notes.
|
||||
</span>
|
||||
|
||||
<span>
|
||||
Version checking can only be activated at compile time.
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<a href="https://gitlab.com/Remmina/Remmina/-/tags/" title="Remmina release notes"><i>Visit the website to read the release notes</i></a>.
|
||||
</span></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.5</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="margin_top">6</property>
|
||||
<property name="margin_bottom">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="rmnews_defaultcl_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Use as default remote desktop client</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="rmnews_defaultcl_button">
|
||||
<property name="label" translatable="yes">Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Allow Remmina to automatically open .rdp and .remmina files.</property>
|
||||
<property name="halign">start</property>
|
||||
<signal name="clicked" handler="rmnews_defaultcl_on_click" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="rmnews_allow_news_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes" comments="The star (*) is a reference to privacy consent">Fetch news from <a href="https://remmina.org" title="Remmina news site">remmina.org</a> (*)</property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="rmnews_news_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Receives updated news from remmina.org</property>
|
||||
<property name="halign">start</property>
|
||||
<signal name="notify" handler="rmnews_news_switch_state_set_cb" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="privacy_disclaimer_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">* By turning on news you consent to fetching data from remmina.org</property>
|
||||
<property name="justify">fill</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="margin_top">6</property>
|
||||
<property name="margin_bottom">6</property>
|
||||
<property name="label" translatable="yes"><big>Take part</big></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.5</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkAlignment">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes" comments="Pay attention to the quoting characters as they may break the pango layout. If in doubt and cannot test, copy and paste the symbols from the English string."><span>
|
||||
<b>You have our gratitude in choosing copylefted libre software, <a href="https://remmina.org/donations/" title="Where’s the money, Lebowski? “blblblblblb”">donations also make us happy</a>, and further help improve Remmina.</b>
|
||||
</span>
|
||||
</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">6</property>
|
||||
<property name="margin_right">6</property>
|
||||
<property name="margin_top">6</property>
|
||||
<property name="margin_bottom">6</property>
|
||||
<property name="label" translatable="yes"><big>Contribute</big></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
@ -26,7 +26,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=15 -->
|
||||
<!-- n-columns=3 n-rows=17 -->
|
||||
<object class="GtkGrid" id="grid_options">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
@ -389,7 +389,7 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">11</property>
|
||||
<property name="top-attach">14</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -402,7 +402,7 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">11</property>
|
||||
<property name="top-attach">14</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
@ -450,7 +450,7 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">12</property>
|
||||
<property name="top-attach">15</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -464,7 +464,104 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">15</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="remmina_info_disable_stats_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-left">18</property>
|
||||
<property name="margin-right">6</property>
|
||||
<property name="margin-start">18</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="label" translatable="yes">Disable anonymous statistics</property>
|
||||
<property name="justify">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_disable_stats">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin-right">18</property>
|
||||
<property name="margin-end">18</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">12</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="remmina_info_disable_news_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-left">18</property>
|
||||
<property name="margin-right">6</property>
|
||||
<property name="margin-start">18</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="label" translatable="yes">Disable news from <a href="https://remmina.org" title="Remmina news site">remmina.org</a></property>
|
||||
<property name="use-markup">True</property>
|
||||
<property name="justify">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_disable_news">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin-right">18</property>
|
||||
<property name="margin-end">18</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">11</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="remmina_info_disable_tip">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-left">18</property>
|
||||
<property name="margin-right">6</property>
|
||||
<property name="margin-start">18</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="label" translatable="yes">Disable tip of the day</property>
|
||||
<property name="justify">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">13</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="switch_disable_tip">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin-right">18</property>
|
||||
<property name="margin-end">18</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">13</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
@ -478,6 +478,21 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "curl",
|
||||
"buildsystem": "autotools",
|
||||
"cleanup": [],
|
||||
"config-opts": [
|
||||
"--with-openssl"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/curl/curl/releases/download/curl-7_81_0/curl-7.81.0.tar.xz",
|
||||
"sha256": "a067b688d1645183febc31309ec1f3cdce9213d02136b6a6de3d50f69c95a7d3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "freerdp",
|
||||
"buildsystem": "cmake-ninja",
|
||||
|
@ -469,6 +469,21 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "curl",
|
||||
"buildsystem": "autotools",
|
||||
"cleanup": [],
|
||||
"config-opts": [
|
||||
"--with-openssl"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/curl/curl/releases/download/curl-7_81_0/curl-7.81.0.tar.xz",
|
||||
"sha256": "a067b688d1645183febc31309ec1f3cdce9213d02136b6a6de3d50f69c95a7d3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "freerdp",
|
||||
"buildsystem": "cmake-ninja",
|
||||
|
@ -57,10 +57,6 @@
|
||||
* An null terminated array of commands that are executed after the initialization of the Python engine. Every entry
|
||||
* represents a line of Python code.
|
||||
*/
|
||||
static const char
|
||||
* python_init_commands[] = { "import sys", "sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')", NULL // Sentinel
|
||||
};
|
||||
|
||||
|
||||
static const gchar* python_wrapper_supported_extensions[] = {"py", NULL};
|
||||
|
||||
@ -200,11 +196,21 @@ G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
|
||||
python_wrapper_module_init();
|
||||
Py_InitializeEx(0);
|
||||
|
||||
for (const char** ptr = python_init_commands; *ptr; ++ptr)
|
||||
gchar* plugin_dir;
|
||||
plugin_dir = g_build_path("/", g_get_user_config_dir(), "remmina", "plugins", NULL);
|
||||
|
||||
gchar* python_command = g_strdup_printf("sys.path.append('%s')", plugin_dir);
|
||||
|
||||
char* python_init_commands[] = { "import sys", python_command, "sys.path.append('" REMMINA_RUNTIME_PLUGINDIR "')", NULL }; // Sentinel};
|
||||
|
||||
for (char** ptr = python_init_commands; *ptr; ++ptr)
|
||||
{
|
||||
PyRun_SimpleString(*ptr);
|
||||
}
|
||||
|
||||
g_free(python_command);
|
||||
g_free(plugin_dir);
|
||||
|
||||
python_wrapper_protocol_widget_init();
|
||||
|
||||
service->register_plugin((RemminaPlugin*)&remmina_python_wrapper);
|
||||
|
@ -178,9 +178,11 @@ parts:
|
||||
- libjson-glib-dev
|
||||
- libkf5wallet-dev
|
||||
- libsodium-dev
|
||||
- libcurl4-openssl-dev
|
||||
- libspice-client-gtk-3.0-dev
|
||||
- libspice-protocol-dev
|
||||
- libssh-dev
|
||||
- libssl3
|
||||
- libvncserver-dev
|
||||
- libvte-2.91-dev
|
||||
- libwebkit2gtk-4.0-dev
|
||||
@ -223,7 +225,7 @@ parts:
|
||||
- -DSNAP_BUILD=on
|
||||
|
||||
# XXX: This is an hack to have a kind of bind-mount with absolute prefix.
|
||||
- -DCMAKE_INSTALL_PREFIX=/snap/$SNAPCRAFT_PROJECT_NAME/current/usr
|
||||
- -DCMAKE_INSTALL_PREFIX=/snap/$CRAFT_PROJECT_NAME/current/usr
|
||||
|
||||
organize:
|
||||
snap/remmina/current/usr: usr
|
||||
|
@ -42,6 +42,8 @@ list(
|
||||
"remmina_avahi.h"
|
||||
"remmina.c"
|
||||
"remmina.h"
|
||||
"remmina_bug_report.c"
|
||||
"remmina_bug_report.h"
|
||||
"remmina_chat_window.c"
|
||||
"remmina_chat_window.h"
|
||||
"remmina_crypt.c"
|
||||
@ -97,6 +99,8 @@ list(
|
||||
"remmina_sftp_plugin.h"
|
||||
"remmina_sodium.c"
|
||||
"remmina_sodium.h"
|
||||
"remmina_curl_connector.c"
|
||||
"remmina_curl_connector.h"
|
||||
"remmina_ssh.c"
|
||||
"remmina_ssh.h"
|
||||
"remmina_ssh_plugin.c"
|
||||
@ -121,15 +125,17 @@ list(
|
||||
"remmina_mpchange.h"
|
||||
"remmina_scheduler.c"
|
||||
"remmina_scheduler.h"
|
||||
"remmina_stats.c"
|
||||
"remmina_stats.h"
|
||||
"remmina_info.c"
|
||||
"remmina_info.h"
|
||||
"resources.c")
|
||||
|
||||
set(RESOURCE_LIST
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_about.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_bug_report.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_key_chooser.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_main.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_mpc.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_info.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_passwd.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_preferences.glade
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../data/ui/remmina_search.glade
|
||||
@ -250,6 +256,14 @@ if(GTK3_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_required_package(CURL)
|
||||
if(NOT CURL_FOUND)
|
||||
message(FATAL_ERROR "libcurl not found")
|
||||
else()
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
target_link_libraries(remmina ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_required_package(PCRE2)
|
||||
if(NOT PCRE2_FOUND)
|
||||
message(FATAL_ERROR "libpcre2 library not found")
|
||||
@ -264,3 +278,5 @@ install(
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/remmina
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h")
|
||||
|
||||
add_definitions(-DG_LOG_DOMAIN="remmina")
|
||||
|
@ -61,6 +61,21 @@ typedef struct _RemminaPlugin {
|
||||
const gchar * version;
|
||||
} RemminaPlugin;
|
||||
|
||||
typedef struct _RemminaServerPluginResponse {
|
||||
const gchar * name;
|
||||
const gchar * version;
|
||||
const gchar * file_name;
|
||||
/*
|
||||
* This is the signature received directly from the server. It should be base64 encoded.
|
||||
*/
|
||||
const guchar * signature;
|
||||
/*
|
||||
* This is the data received directly from the server. It should be
|
||||
* first compressed with gzip and then base64 encoded.
|
||||
*/
|
||||
guchar * data;
|
||||
} RemminaServerPluginResponse;
|
||||
|
||||
typedef struct _RemminaProtocolPlugin _RemminaProtocolPlugin;
|
||||
typedef struct _RemminaProtocolPlugin {
|
||||
RemminaPluginType type;
|
||||
|
@ -31,7 +31,7 @@
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#define G_LOG_USE_STRUCTURED
|
||||
@ -53,6 +53,7 @@
|
||||
#include "remmina_exec.h"
|
||||
#include "remmina_file_manager.h"
|
||||
#include "remmina_icon.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_main.h"
|
||||
#include "remmina_masterthread_exec.h"
|
||||
#include "remmina_plugin_manager.h"
|
||||
@ -66,6 +67,7 @@
|
||||
#include "remmina_ssh_plugin.h"
|
||||
#include "remmina_widget_pool.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
#include "remmina_info.h"
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
@ -85,6 +87,9 @@ static int gcrypt_thread_initialized = 0;
|
||||
#endif /* HAVE_LIBGCRYPT */
|
||||
|
||||
gboolean kioskmode;
|
||||
gboolean imode;
|
||||
gboolean disablenews;
|
||||
gboolean disablestats;
|
||||
gboolean disabletoolbar;
|
||||
gboolean fullscreen;
|
||||
gboolean extrahardening;
|
||||
@ -129,6 +134,8 @@ static GOptionEntry remmina_options[] =
|
||||
// TRANSLATORS: Shown in terminal. Do not use characters that may be not supported on a terminal
|
||||
{ "set-option", 0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, N_("Set one or more profile settings, to be used with --update-profile"), NULL },
|
||||
{ "encrypt-password", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Encrypt a password"), NULL },
|
||||
{ "disable-news", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Disable news"), NULL },
|
||||
{ "disable-stats", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Disable stats"), NULL },
|
||||
{ "disable-toolbar", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Disable toolbar"), NULL },
|
||||
{ "enable-fullscreen", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Enable fullscreen"), NULL },
|
||||
{ "enable-extra-hardening", 0, 0, G_OPTION_ARG_NONE, NULL, N_("Enable extra hardening (disable closing confirmation, disable unsafe shortcut keys, hide tabs, hide search bar)"), NULL },
|
||||
@ -166,6 +173,14 @@ static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *
|
||||
#endif
|
||||
opts = g_application_command_line_get_options_dict(cmdline);
|
||||
|
||||
if (g_variant_dict_lookup_value(opts, "disable-news", NULL)) {
|
||||
disablenews = TRUE;
|
||||
}
|
||||
|
||||
if (g_variant_dict_lookup_value(opts, "disable-stats", NULL)) {
|
||||
disablestats = TRUE;
|
||||
}
|
||||
|
||||
if (g_variant_dict_lookup_value(opts, "disable-toolbar", NULL)) {
|
||||
disabletoolbar = TRUE;
|
||||
}
|
||||
@ -191,6 +206,7 @@ static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *
|
||||
}
|
||||
|
||||
if (g_variant_dict_lookup_value(opts, "about", NULL)) {
|
||||
imode = TRUE;
|
||||
remmina_exec_command(REMMINA_COMMAND_ABOUT, NULL);
|
||||
executed = TRUE;
|
||||
}
|
||||
@ -214,6 +230,7 @@ static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *
|
||||
}
|
||||
|
||||
if (g_variant_dict_lookup(opts, "edit", "^aay", &files)) {
|
||||
imode = TRUE;
|
||||
if (files)
|
||||
for (gint i = 0; files[i]; i++) {
|
||||
g_debug ("Editing file: %s", files[i]);
|
||||
@ -226,6 +243,7 @@ static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *
|
||||
|
||||
if (g_variant_dict_lookup_value(opts, "kiosk", NULL)) {
|
||||
kioskmode = TRUE;
|
||||
imode = TRUE;
|
||||
remmina_exec_command(REMMINA_COMMAND_MAIN, NULL);
|
||||
executed = TRUE;
|
||||
}
|
||||
@ -245,6 +263,7 @@ static gint remmina_on_command_line(GApplication *app, GApplicationCommandLine *
|
||||
}
|
||||
|
||||
if (g_variant_dict_lookup(opts, "pref", "&s", &str)) {
|
||||
imode = TRUE;
|
||||
remmina_exec_command(REMMINA_COMMAND_PREF, str);
|
||||
executed = TRUE;
|
||||
}
|
||||
@ -281,7 +300,6 @@ static void remmina_on_startup(GApplication *app)
|
||||
remmina_sftp_plugin_register();
|
||||
remmina_ssh_plugin_register();
|
||||
remmina_icon_init();
|
||||
|
||||
g_set_application_name("Remmina");
|
||||
gtk_window_set_default_icon_name(REMMINA_APP_ID);
|
||||
|
||||
@ -292,9 +310,19 @@ static void remmina_on_startup(GApplication *app)
|
||||
gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
|
||||
REMMINA_RUNTIME_DATADIR G_DIR_SEPARATOR_S "icons");
|
||||
g_application_hold(app);
|
||||
remmina_info_schedule();
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "w"); // Flush log file
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL) {
|
||||
fclose(log_file);
|
||||
}
|
||||
|
||||
/* Check for secret plugin and service initialization and show console warnings if
|
||||
* something is missing */
|
||||
remmina_plugin_manager_get_available_plugins();
|
||||
secret_plugin = remmina_plugin_manager_get_secret_plugin();
|
||||
if (!secret_plugin)
|
||||
g_print("Warning: Remmina is running without a secret plugin. Passwords will be saved in a less secure way.\n");
|
||||
@ -363,7 +391,7 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
g_message(_("Remmina does not log all output statements. "
|
||||
"Turn on more verbose output by using "
|
||||
"\"G_MESSAGES_DEBUG=all\" as an environment variable.\n"
|
||||
"\"G_MESSAGES_DEBUG=remmina\" as an environment variable.\n"
|
||||
"More info available on the Remmina wiki at:\n"
|
||||
"https://gitlab.com/Remmina/Remmina/-/wikis/Usage/Remmina-debugging"
|
||||
));
|
||||
@ -397,9 +425,9 @@ int main(int argc, char *argv[])
|
||||
#endif /* !HAVE_LIBGCRYPT */
|
||||
|
||||
/* Initialize some Remmina parts needed also on a local instance for correct handle-local-options */
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
remmina_pref_init();
|
||||
remmina_file_manager_init();
|
||||
|
||||
remmina_plugin_manager_init();
|
||||
|
||||
|
||||
|
@ -38,4 +38,5 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
extern gboolean kioskmode;
|
||||
extern gboolean imode;
|
||||
G_END_DECLS
|
||||
|
@ -100,6 +100,12 @@ The filetype can be ".remmina" or one supported by a plugin capable of
|
||||
*--display=DISPLAY*
|
||||
X display to use
|
||||
|
||||
*--disable-news*
|
||||
Disable news
|
||||
|
||||
*--disable-stats*
|
||||
Disable stats
|
||||
|
||||
*--disable-toolbar*
|
||||
Disable toolbar
|
||||
|
||||
|
410
src/remmina_bug_report.c
Normal file
410
src/remmina_bug_report.c
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Remmina - The GTK+ Remote Desktop Client
|
||||
* Copyright (C) 2009-2011 Vic Lee
|
||||
* Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL.
|
||||
* If you modify file(s) with this exception, you may extend this exception
|
||||
* to your version of the file(s), but you are not obligated to do so.
|
||||
* If you do not wish to do so, delete this exception statement from your
|
||||
* version.
|
||||
* If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <string.h>
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_bug_report.h"
|
||||
#include "remmina_public.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
#include "remmina_info.h"
|
||||
#include "remmina_curl_connector.h"
|
||||
#include "remmina_utils.h"
|
||||
|
||||
#if !JSON_CHECK_VERSION(1, 2, 0)
|
||||
#define json_node_unref(x) json_node_free(x)
|
||||
#endif
|
||||
|
||||
#define BUG_REPORT_UPLOAD_URL "https://info.remmina.org/bug_report/bug_report"
|
||||
static RemminaBugReportDialog *remmina_bug_report_dialog;
|
||||
|
||||
#define GET_OBJECT(object_name) gtk_builder_get_object(remmina_bug_report_dialog->builder, object_name)
|
||||
|
||||
#define TEMP_LOG_TEXT_FILE_NAME "remmina_shorter_logs.log"
|
||||
#define COMPRESSED_LOG_FILE_NAME "remmina_log_file.gz"
|
||||
#define LOG_RECENT_WINDOW 4000
|
||||
#define MAX_DESCRIPTION_LENGTH 8000
|
||||
const char *bug_report_preview_text_md = \
|
||||
"## Problem Description\n"
|
||||
"\n"
|
||||
"Write a detailed description of the problem.\n"
|
||||
"\n"
|
||||
"## What is the expected correct behavior?\n"
|
||||
"\n"
|
||||
"(What you want to see instead.)\n"
|
||||
"\n"
|
||||
"## Remote System Description\n"
|
||||
"\n"
|
||||
"* Server (OS name and version):\n"
|
||||
"* Special notes regarding the remote system (i.e. gateways, tunnel, etc.):\n"
|
||||
"\n"
|
||||
"## Debugging\n"
|
||||
"\n"
|
||||
"Please check the Include Debug Data box to allow for debug log to be sent with this bug report\n"
|
||||
"\n"
|
||||
"## Local System Description\n"
|
||||
"\n"
|
||||
"* Client (OS name and version):\n"
|
||||
"* Remmina version ( ```remmina --version``` ):\n"
|
||||
"* Installation(s):\n"
|
||||
" - [ ] Distribution package.\n"
|
||||
" - [ ] PPA.\n"
|
||||
" - [ ] Snap.\n"
|
||||
" - [ ] Flatpak.\n"
|
||||
" - [ ] Compiled from sources.\n"
|
||||
" - [ ] Other - detail:\n"
|
||||
"* Desktop environment (GNOME, Unity, KDE, ..):\n"
|
||||
"* Plugin(s):\n"
|
||||
" - [ ] RDP - freerdp version ( ```xfreerdp --version``` ):\n"
|
||||
" - [ ] VNC\n"
|
||||
" - [ ] SSH\n"
|
||||
" - [ ] SFTP\n"
|
||||
" - [ ] SPICE\n"
|
||||
" - [ ] WWW\n"
|
||||
" - [ ] EXEC\n"
|
||||
" - [ ] Other (please specify):\n"
|
||||
"* GTK backend (Wayland, Xorg):\n"
|
||||
"\n";
|
||||
|
||||
|
||||
/* Show the bug report dialog from the file ui/remmina_bug_report.glade */
|
||||
void remmina_bug_report_open(GtkWindow *parent)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
GtkTextBuffer* bug_report_preview_text_buffer;
|
||||
remmina_bug_report_dialog = g_new0(RemminaBugReportDialog, 1);
|
||||
|
||||
remmina_bug_report_dialog->builder = remmina_public_gtk_builder_new_from_resource ("/org/remmina/Remmina/src/../data/ui/remmina_bug_report.glade");
|
||||
remmina_bug_report_dialog->dialog = GTK_WIDGET(gtk_builder_get_object(remmina_bug_report_dialog->builder, "RemminaBugReportDialog"));
|
||||
remmina_bug_report_dialog->bug_report_submit_button = GTK_BUTTON(GET_OBJECT("bug_report_submit_button"));
|
||||
remmina_bug_report_dialog->bug_report_name_entry = GTK_ENTRY(GET_OBJECT("bug_report_name_entry"));
|
||||
remmina_bug_report_dialog->bug_report_email_entry = GTK_ENTRY(GET_OBJECT("bug_report_email_entry"));
|
||||
remmina_bug_report_dialog->bug_report_title_entry = GTK_ENTRY(GET_OBJECT("bug_report_title_entry"));
|
||||
remmina_bug_report_dialog->bug_report_description_textview = GTK_TEXT_VIEW(GET_OBJECT("bug_report_description_textview"));
|
||||
remmina_bug_report_dialog->bug_report_submit_status_label = GTK_LABEL(GET_OBJECT("bug_report_submit_status_label"));
|
||||
remmina_bug_report_dialog->bug_report_debug_data_check_button = GTK_CHECK_BUTTON(GET_OBJECT("bug_report_debug_data_check_button"));
|
||||
remmina_bug_report_dialog->bug_report_include_system_info_check_button = GTK_CHECK_BUTTON(GET_OBJECT("bug_report_include_system_info_check_button"));
|
||||
|
||||
// Set bug report markdown text in bug description window
|
||||
bug_report_preview_text_buffer = gtk_text_buffer_new(NULL);
|
||||
gtk_text_buffer_set_text(bug_report_preview_text_buffer, bug_report_preview_text_md, strlen(bug_report_preview_text_md));
|
||||
gtk_text_view_set_buffer(remmina_bug_report_dialog->bug_report_description_textview, bug_report_preview_text_buffer);
|
||||
|
||||
if (parent) {
|
||||
gtk_window_set_transient_for(GTK_WINDOW(remmina_bug_report_dialog->dialog), parent);
|
||||
gtk_window_set_destroy_with_parent(GTK_WINDOW(remmina_bug_report_dialog->dialog), TRUE);
|
||||
}
|
||||
|
||||
g_signal_connect(remmina_bug_report_dialog->bug_report_submit_button, "clicked",
|
||||
G_CALLBACK(remmina_bug_report_dialog_on_action_submit), (gpointer)remmina_bug_report_dialog);
|
||||
|
||||
gtk_window_present(GTK_WINDOW(remmina_bug_report_dialog->dialog));
|
||||
g_object_unref(bug_report_preview_text_buffer);
|
||||
}
|
||||
|
||||
|
||||
void remmina_bug_report_dialog_on_action_submit(GSimpleAction *action, GVariant *param, gpointer data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonGenerator *g;
|
||||
JsonObject *o;
|
||||
gchar *b;
|
||||
|
||||
REMMINA_DEBUG("Submit Button Clicked. Uploading Bug Report data.");
|
||||
|
||||
gchar *markup = GRAY_TEXT("Sending Bug Report...");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
|
||||
// Store bug report info in JSON blob and encode before submitting
|
||||
JsonNode *bug_report_data = remmina_bug_report_get_all();
|
||||
|
||||
if (bug_report_data == NULL || (o = json_node_get_object(bug_report_data)) == NULL) {
|
||||
REMMINA_DEBUG("Failed to grab bug report data, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: Unable to generate bug report message. Please try again.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
} else if (strcmp(json_object_get_string_member(json_node_get_object(bug_report_data), "Name"), "") == 0) {
|
||||
REMMINA_DEBUG("No text in name entry of bug report data, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: Name/Username is required. Please enter a Name/Username.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
} else {
|
||||
gchar *email_text = g_strdup(json_object_get_string_member(json_node_get_object(bug_report_data), "Email"));
|
||||
if (strcmp(email_text, "") == 0) {
|
||||
REMMINA_DEBUG("No text in email entry of bug report data, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: Email is required. Please enter an email.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
g_free(email_text);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
} else {
|
||||
gchar *save_ptr;
|
||||
gchar *check_for_at_symbol = strtok_r(email_text, "@", &save_ptr);
|
||||
gchar *check_for_dot = strtok_r(NULL, ".", &save_ptr);
|
||||
gchar *check_for_domain = strtok_r(NULL, "", &save_ptr);
|
||||
if (check_for_at_symbol == NULL || check_for_dot == NULL || check_for_domain == NULL) {
|
||||
REMMINA_DEBUG("Text in email entry of bug report data is not a valid email, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: A valid email is required. Email is missing a prefix or domain.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
g_free(email_text);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
} else if (strpbrk(check_for_at_symbol, "@.") != NULL || strpbrk(check_for_dot, "@.") != NULL || strpbrk(check_for_domain, "@.") != NULL) {
|
||||
REMMINA_DEBUG("Text in email entry of bug report data is not a valid email, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: A valid email is required. Email contains extra @ and . characters.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
g_free(email_text);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_free(email_text);
|
||||
if (strcmp(json_object_get_string_member(json_node_get_object(bug_report_data), "Bug_Title"), "") == 0) {
|
||||
REMMINA_DEBUG("No text in bug title entry of bug report data, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: Bug Title is required. Please enter a Bug Title.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
} else if (strcmp(json_object_get_string_member(json_node_get_object(bug_report_data), "Bug_Description"), "") == 0 ||
|
||||
strcmp(json_object_get_string_member(json_node_get_object(bug_report_data), "Bug_Description"), bug_report_preview_text_md) == 0) {
|
||||
REMMINA_DEBUG("No text in bug description of bug report data, no request is sent");
|
||||
gchar *markup = RED_TEXT("Failure: Bug Description is required. Please fill out the template.");
|
||||
gtk_label_set_markup(remmina_bug_report_dialog->bug_report_submit_status_label, markup);
|
||||
g_free(markup);
|
||||
json_node_unref(bug_report_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g = json_generator_new();
|
||||
json_generator_set_root(g, bug_report_data);
|
||||
b = json_generator_to_data(g, NULL);
|
||||
g_object_unref(g);
|
||||
|
||||
remmina_curl_compose_message(b, "POST", BUG_REPORT_UPLOAD_URL, remmina_bug_report_dialog->bug_report_submit_status_label);
|
||||
json_node_unref(bug_report_data);
|
||||
}
|
||||
|
||||
JsonNode *remmina_bug_report_get_all()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b_inner, *b_outer; // holds entire JSON blob
|
||||
JsonGenerator *g;
|
||||
JsonNode *n; // temp JSON node
|
||||
gchar *unenc_b, *enc_b;
|
||||
gchar *uid;
|
||||
|
||||
// Initialize JSON blob
|
||||
b_outer = json_builder_new();
|
||||
if (b_outer == NULL) {
|
||||
g_object_unref(b_outer);
|
||||
return NULL;
|
||||
}
|
||||
json_builder_begin_object(b_outer);
|
||||
|
||||
gboolean include_debug_data = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remmina_bug_report_dialog->bug_report_debug_data_check_button));
|
||||
gboolean include_system_info = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remmina_bug_report_dialog->bug_report_include_system_info_check_button));
|
||||
|
||||
if (include_debug_data || include_system_info) {
|
||||
b_inner = json_builder_new();
|
||||
if (b_inner == NULL) {
|
||||
g_object_unref(b_inner);
|
||||
g_object_unref(b_outer);
|
||||
return NULL;
|
||||
}
|
||||
json_builder_begin_object(b_inner);
|
||||
|
||||
if (include_system_info){
|
||||
n = remmina_info_stats_get_all();
|
||||
|
||||
json_builder_set_member_name(b_outer, "System_Info_Enabled");
|
||||
json_builder_add_string_value(b_outer, "ON");
|
||||
|
||||
json_builder_set_member_name(b_inner, "System_Info");
|
||||
json_builder_add_value(b_inner, n);
|
||||
}
|
||||
else{
|
||||
json_builder_set_member_name(b_outer, "System_Info_Enabled");
|
||||
json_builder_add_string_value(b_outer, "OFF");
|
||||
}
|
||||
|
||||
if (include_debug_data){
|
||||
gboolean log_file_result;
|
||||
gchar *log_text;
|
||||
gsize log_length;
|
||||
GError *log_read_error;
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
log_file_result = g_file_get_contents(log_filename, &log_text, &log_length, &log_read_error);
|
||||
g_free(log_filename);
|
||||
if (!log_file_result && log_read_error == NULL) {
|
||||
REMMINA_DEBUG("Failed to grab entire log text, read error: %s", log_read_error->message);
|
||||
}
|
||||
gboolean shorter_logs_result;
|
||||
gchar *temp_log_filename = g_build_filename(g_get_tmp_dir(), TEMP_LOG_TEXT_FILE_NAME, NULL);
|
||||
gchar *recent_log_text;
|
||||
if (log_length <= LOG_RECENT_WINDOW) {
|
||||
recent_log_text = log_text;
|
||||
shorter_logs_result = g_file_set_contents(temp_log_filename, recent_log_text, log_length, NULL);
|
||||
}
|
||||
else {
|
||||
recent_log_text = log_text + (log_length - LOG_RECENT_WINDOW);
|
||||
shorter_logs_result = g_file_set_contents(temp_log_filename, recent_log_text, LOG_RECENT_WINDOW, NULL);
|
||||
}
|
||||
g_free(log_text);
|
||||
if (!shorter_logs_result) {
|
||||
REMMINA_DEBUG("Failed to write recent window of logs to temp file.");
|
||||
}
|
||||
|
||||
gchar *comp_log_filename = g_build_filename(g_get_tmp_dir(), COMPRESSED_LOG_FILE_NAME, NULL);
|
||||
GFile *in = g_file_new_for_path(temp_log_filename);
|
||||
GFile *out = g_file_new_for_path(comp_log_filename);
|
||||
// Clear out old compressed log file to prevent appending
|
||||
g_file_delete(out, NULL, NULL);
|
||||
remmina_compress_from_file_to_file(in, out);
|
||||
g_free(temp_log_filename);
|
||||
|
||||
gboolean com_log_file_result;
|
||||
gchar *compressed_logs;
|
||||
gsize com_log_length;
|
||||
GError *com_log_read_error;
|
||||
com_log_file_result = g_file_get_contents(comp_log_filename, &compressed_logs, &com_log_length, &com_log_read_error);
|
||||
n = json_node_alloc();
|
||||
if (!com_log_file_result && com_log_read_error != NULL) {
|
||||
REMMINA_DEBUG("Failed to grab entire compressed log text, read error: %s", com_log_read_error->message);
|
||||
json_node_init_string(n, "");
|
||||
}
|
||||
else {
|
||||
gchar *logs_base64 = g_base64_encode(compressed_logs, com_log_length);
|
||||
json_node_init_string(n, logs_base64);
|
||||
g_free(logs_base64);
|
||||
}
|
||||
|
||||
json_builder_set_member_name(b_outer, "Debug_Log_Enabled");
|
||||
json_builder_add_string_value(b_outer, "ON");
|
||||
json_builder_set_member_name(b_inner, "Compressed_Logs");
|
||||
json_builder_add_value(b_inner, n);
|
||||
g_free(compressed_logs);
|
||||
g_free(comp_log_filename);
|
||||
|
||||
}
|
||||
else{
|
||||
json_builder_set_member_name(b_outer, "Debug_Log_Enabled");
|
||||
json_builder_add_string_value(b_outer, "OFF");
|
||||
}
|
||||
|
||||
// Wrap up by setting n to the final JSON blob
|
||||
json_builder_end_object(b_inner);
|
||||
n = json_builder_get_root(b_inner);
|
||||
g_object_unref(b_inner);
|
||||
|
||||
g = json_generator_new();
|
||||
json_generator_set_root(g, n);
|
||||
json_node_unref(n);
|
||||
unenc_b = json_generator_to_data(g, NULL); // unenc_b=bug report data
|
||||
g_object_unref(g);
|
||||
|
||||
// Now base64 encode report data
|
||||
enc_b = g_base64_encode(unenc_b, strlen(unenc_b));
|
||||
if (enc_b == NULL) {
|
||||
g_object_unref(b_outer);
|
||||
g_free(unenc_b);
|
||||
REMMINA_DEBUG("Failed to encode inner JSON");
|
||||
return NULL;
|
||||
}
|
||||
g_free(unenc_b);
|
||||
|
||||
json_builder_set_member_name(b_outer, "encdata");
|
||||
json_builder_add_string_value(b_outer, enc_b);
|
||||
g_free(enc_b);
|
||||
}
|
||||
else {
|
||||
json_builder_set_member_name(b_outer, "System_Info_Enabled");
|
||||
json_builder_add_string_value(b_outer, "OFF");
|
||||
json_builder_set_member_name(b_outer, "Debug_Log_Enabled");
|
||||
json_builder_add_string_value(b_outer, "OFF");
|
||||
}
|
||||
|
||||
n = remmina_info_stats_get_uid();
|
||||
uid = g_strdup(json_node_get_string(n));
|
||||
json_builder_set_member_name(b_outer, "UID");
|
||||
json_builder_add_string_value(b_outer, uid);
|
||||
|
||||
// Allocate new json node for each text entry and add to total blob
|
||||
n = json_node_alloc();
|
||||
json_node_init_string(n, gtk_entry_get_text(remmina_bug_report_dialog->bug_report_name_entry));
|
||||
json_builder_set_member_name(b_outer, "Name");
|
||||
json_builder_add_value(b_outer, n);
|
||||
|
||||
n = json_node_alloc();
|
||||
json_node_init_string(n, gtk_entry_get_text(remmina_bug_report_dialog->bug_report_email_entry));
|
||||
json_builder_set_member_name(b_outer, "Email");
|
||||
json_builder_add_value(b_outer, n);
|
||||
|
||||
n = json_node_alloc();
|
||||
json_node_init_string(n, gtk_entry_get_text(remmina_bug_report_dialog->bug_report_title_entry));
|
||||
json_builder_set_member_name(b_outer, "Bug_Title");
|
||||
json_builder_add_value(b_outer, n);
|
||||
|
||||
GtkTextBuffer *buffer;
|
||||
gchar *description_text;
|
||||
GtkTextIter start, end;
|
||||
buffer = gtk_text_view_get_buffer(remmina_bug_report_dialog->bug_report_description_textview);
|
||||
gtk_text_buffer_get_start_iter(buffer, &start);
|
||||
gtk_text_buffer_get_iter_at_offset(buffer, &end, MAX_DESCRIPTION_LENGTH);
|
||||
description_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
|
||||
|
||||
n = json_node_alloc();
|
||||
json_node_init_string(n, description_text);
|
||||
json_builder_set_member_name(b_outer, "Bug_Description");
|
||||
json_builder_add_value(b_outer, n);
|
||||
|
||||
json_builder_end_object(b_outer);
|
||||
n = json_builder_get_root(b_outer);
|
||||
g_object_unref(b_outer);
|
||||
|
||||
g_free(uid);
|
||||
|
||||
return n;
|
||||
}
|
60
src/remmina_bug_report.h
Normal file
60
src/remmina_bug_report.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Remmina - The GTK+ Remote Desktop Client
|
||||
* Copyright (C) 2009-2011 Vic Lee
|
||||
* Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
|
||||
* Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. * If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. * If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. * If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <gtk/gtk.h>
|
||||
#include "json-glib/json-glib.h"
|
||||
|
||||
typedef struct _RemminaBugReportDialog {
|
||||
GtkBuilder *builder;
|
||||
GtkWidget *dialog;
|
||||
GtkButton *bug_report_submit_button;
|
||||
GtkEntry *bug_report_name_entry;
|
||||
GtkEntry *bug_report_email_entry;
|
||||
GtkEntry *bug_report_title_entry;
|
||||
GtkTextView *bug_report_description_textview;
|
||||
GtkLabel *bug_report_submit_status_label;
|
||||
GtkCheckButton *bug_report_debug_data_check_button;
|
||||
GtkCheckButton *bug_report_include_system_info_check_button;
|
||||
} RemminaBugReportDialog;
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void remmina_bug_report_open(GtkWindow *parent);
|
||||
void remmina_bug_report_dialog_on_action_submit(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
JsonNode *remmina_bug_report_get_all(void);
|
||||
|
||||
G_END_DECLS
|
265
src/remmina_curl_connector.c
Normal file
265
src/remmina_curl_connector.c
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Remmina - The GTK+ Remote Desktop Client
|
||||
* Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. * If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. * If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. * If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
#include "remmina.h"
|
||||
#include "remmina_info.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_plugin_manager.h"
|
||||
#include "remmina_pref.h"
|
||||
#include "remmina_curl_connector.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
#include "remmina_utils.h"
|
||||
|
||||
|
||||
#ifdef GDK_WINDOWING_WAYLAND
|
||||
#include <gdk/gdkwayland.h>
|
||||
#endif
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
extern gboolean info_disable_stats;
|
||||
extern gboolean info_disable_news;
|
||||
extern gboolean info_disable_tip;
|
||||
|
||||
struct curl_msg {
|
||||
gchar *body;
|
||||
gchar *url;
|
||||
gpointer *user_data;
|
||||
char* response;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
enum ShowValue {ShowNews, ShowTip, ShowNone};
|
||||
|
||||
|
||||
// If we receive a response from the server parse out the message
|
||||
// and call the appropiate functions to handle the message.
|
||||
void handle_resp(struct curl_msg * message){
|
||||
gchar *news_checksum = NULL;
|
||||
JsonParser *parser = NULL;
|
||||
enum ShowValue to_show = ShowNone;
|
||||
|
||||
if (!imode) {
|
||||
parser = json_parser_new();
|
||||
if (!parser) {
|
||||
return;
|
||||
}
|
||||
if (!json_parser_load_from_data(parser, message->response , message->size, NULL)){
|
||||
g_object_unref(parser);
|
||||
return;
|
||||
}
|
||||
JsonReader *reader = json_reader_new(json_parser_get_root(parser));
|
||||
if (!reader) {
|
||||
g_object_unref(parser);
|
||||
return;
|
||||
}
|
||||
const gchar *json_tip_string;
|
||||
const gchar *json_news_string;
|
||||
gint64 json_stats_int = 0;
|
||||
|
||||
if (!json_reader_read_element(reader, 0)) {
|
||||
g_object_unref(parser);
|
||||
g_object_unref(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json_reader_read_member(reader, "TIP")) {
|
||||
json_tip_string = NULL;
|
||||
}
|
||||
else {
|
||||
json_tip_string = json_reader_get_string_value(reader);
|
||||
}
|
||||
json_reader_end_member(reader);
|
||||
|
||||
if (!json_reader_read_member(reader, "NEWS")) {
|
||||
json_news_string = NULL;
|
||||
}
|
||||
else {
|
||||
json_news_string = json_reader_get_string_value(reader);
|
||||
|
||||
}
|
||||
json_reader_end_member(reader);
|
||||
|
||||
if (!json_reader_read_member(reader, "STATS")) {
|
||||
json_stats_int = 0;
|
||||
}
|
||||
else {
|
||||
json_stats_int = json_reader_get_int_value(reader);
|
||||
}
|
||||
json_reader_end_member(reader);
|
||||
|
||||
if (json_reader_read_member(reader, "LIST")){
|
||||
g_idle_add(remmina_plugin_manager_parse_plugin_list, json_reader_new(json_parser_get_root(parser)));
|
||||
}
|
||||
json_reader_end_member(reader);
|
||||
|
||||
if (json_reader_read_member(reader, "PLUGINS")){
|
||||
g_idle_add(remmina_plugin_manager_download_plugins, json_reader_new(json_parser_get_root(parser)));
|
||||
}
|
||||
|
||||
json_reader_end_member(reader);
|
||||
json_reader_end_element(reader);
|
||||
g_object_unref(reader);
|
||||
|
||||
if (json_news_string == NULL) {
|
||||
if (json_tip_string == NULL) {
|
||||
to_show = ShowNone;
|
||||
}
|
||||
else if (info_disable_tip == 0) {
|
||||
to_show = ShowTip;
|
||||
}
|
||||
}
|
||||
else {
|
||||
news_checksum = remmina_sha256_buffer(json_news_string, strlen(json_news_string));
|
||||
if (news_checksum == NULL) {
|
||||
REMMINA_DEBUG("News checksum is NULL");
|
||||
}
|
||||
else if ((remmina_pref.periodic_news_last_checksum == NULL) || strcmp(news_checksum, remmina_pref.periodic_news_last_checksum) != 0) {
|
||||
REMMINA_DEBUG("Latest news checksum: %s", news_checksum);
|
||||
remmina_pref.periodic_news_last_checksum = g_strdup(news_checksum);
|
||||
remmina_pref_save();
|
||||
if (info_disable_news == 0) {
|
||||
to_show = ShowNews;
|
||||
}
|
||||
}
|
||||
|
||||
if (to_show == ShowNone && json_tip_string != NULL && info_disable_tip == 0) {
|
||||
to_show = ShowTip;
|
||||
}
|
||||
g_free(news_checksum);
|
||||
}
|
||||
|
||||
if (to_show == ShowTip) {
|
||||
RemminaInfoMessage *message = malloc(sizeof(RemminaInfoMessage));
|
||||
message->info_string= g_strdup(json_tip_string);
|
||||
message->title_string= g_strdup("Tip of the Day");
|
||||
g_idle_add(remmina_info_show_response, message);
|
||||
}
|
||||
else if (to_show == ShowNews) {
|
||||
RemminaInfoMessage *message = malloc(sizeof(RemminaInfoMessage));
|
||||
message->info_string= g_strdup(json_news_string);
|
||||
message->title_string= g_strdup("NEWS");
|
||||
g_idle_add(remmina_info_show_response, message);
|
||||
}
|
||||
|
||||
if (json_stats_int){
|
||||
remmina_info_stats_collector();
|
||||
}
|
||||
|
||||
g_object_unref(parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Allocate memory to hold response from the server in msg->response
|
||||
size_t remmina_curl_process_response(void *buffer, size_t size, size_t nmemb, void *userp){
|
||||
size_t realsize = size * nmemb;
|
||||
struct curl_msg *msg = (struct curl_msg *)userp;
|
||||
|
||||
char *ptr = realloc(msg->response, msg->size + realsize + 1);
|
||||
if (!ptr) {
|
||||
return 0; /* out of memory! */
|
||||
}
|
||||
|
||||
msg->response = ptr;
|
||||
memcpy(&(msg->response[msg->size]), buffer, realsize);
|
||||
msg->size += realsize;
|
||||
msg->response[msg->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
|
||||
void remmina_curl_send_message(gpointer data)
|
||||
{
|
||||
gchar *result_message = NULL;
|
||||
gchar *marked_up_message = NULL;
|
||||
struct curl_msg* message = (struct curl_msg*)data;
|
||||
message->size = 0;
|
||||
message->response = NULL;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl){
|
||||
CURLcode res;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, message->url);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message->body);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, remmina_curl_process_response);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, message);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
|
||||
res = curl_easy_perform(curl);
|
||||
if(res != CURLE_OK){
|
||||
curl_easy_strerror(res);
|
||||
result_message = "Failure: Transport error occurred - see debug or logs for more information";
|
||||
marked_up_message = RED_TEXT(result_message);
|
||||
}
|
||||
else{
|
||||
handle_resp(message);
|
||||
result_message = "Success: Message sent successfully, please wait for report to automatically open a new issue. This may take a little while.";
|
||||
marked_up_message = GREEN_TEXT(result_message);
|
||||
|
||||
}
|
||||
if (message->user_data != NULL){
|
||||
gtk_label_set_markup(GTK_LABEL(message->user_data), marked_up_message);
|
||||
|
||||
}
|
||||
if (message->body){
|
||||
g_free(message->body);
|
||||
}
|
||||
if (message->response){
|
||||
g_free(message->response);
|
||||
}
|
||||
g_free(message);
|
||||
g_free(marked_up_message);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void remmina_curl_compose_message(gchar* body, char* type, char* url, gpointer data)
|
||||
{
|
||||
if (!imode){
|
||||
struct curl_msg* message = (struct curl_msg*)malloc(sizeof(struct curl_msg));
|
||||
message->body = body;
|
||||
message->url = url;
|
||||
message->user_data = data;
|
||||
g_thread_new("send_curl_message", (GThreadFunc)remmina_curl_send_message, message);
|
||||
}
|
||||
|
||||
}
|
@ -31,15 +31,23 @@
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include "json-glib/json-glib.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#include "json-glib/json-glib.h"
|
||||
#define DOWNLOAD_URL "https://plugins.remmina.org/plugins/plugin_download"
|
||||
#define LIST_URL "https://plugins.remmina.org/plugins/get_list"
|
||||
#define PERIODIC_UPLOAD_URL "https://info.remmina.org/info/upload_stats"
|
||||
#define INFO_REQUEST_URL "https://info.remmina.org/info/handshake"
|
||||
|
||||
JsonNode *remmina_stats_get_all(void);
|
||||
#define RED_TEXT(str) g_markup_printf_escaped("<span color=\"red\">%s</span>", str)
|
||||
#define GREEN_TEXT(str) g_markup_printf_escaped("<span color=\"green\">%s</span>", str)
|
||||
#define GRAY_TEXT(str) g_markup_printf_escaped("<span color=\"gray\">%s</span>", str)
|
||||
|
||||
void remmina_curl_handshake(gpointer data);
|
||||
void remmina_curl_compose_message(gchar* body, char* type, char* url, gpointer data);
|
||||
|
||||
G_END_DECLS
|
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file remmina_stats.c
|
||||
* @file remmina_info.c
|
||||
* @brief Remmina usage statistics module.
|
||||
* @author Antenore Gatta and Giovanni Panozzo
|
||||
* @date 12 Feb 2018
|
||||
@ -142,14 +142,19 @@
|
||||
#include <glib/gstdio.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
|
||||
#include "remmina.h"
|
||||
#include "remmina_file.h"
|
||||
#include "remmina_file_manager.h"
|
||||
#include "remmina_icon.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_pref.h"
|
||||
#include "remmina_curl_connector.h"
|
||||
#include "remmina_sysinfo.h"
|
||||
#include "remmina_utils.h"
|
||||
#include "remmina_scheduler.h"
|
||||
#include "remmina_public.h"
|
||||
#include "remmina_main.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
#include "remmina_plugin_manager.h"
|
||||
|
||||
@ -159,7 +164,14 @@
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
#include "remmina_stats.h"
|
||||
#include "remmina_info.h"
|
||||
|
||||
#define MAX_ENV_LEN 10000
|
||||
|
||||
gboolean info_disable_stats = 1;
|
||||
gboolean info_disable_news = 1;
|
||||
gboolean info_disable_tip = 1;
|
||||
|
||||
|
||||
struct ProfilesData {
|
||||
GHashTable *proto_count;
|
||||
@ -170,7 +182,69 @@ struct ProfilesData {
|
||||
gchar datestr;
|
||||
};
|
||||
|
||||
JsonNode *remmina_stats_get_os_info()
|
||||
#if !JSON_CHECK_VERSION(1, 2, 0)
|
||||
#define json_node_unref(x) json_node_free(x)
|
||||
#endif
|
||||
|
||||
/* Timers */
|
||||
#define INFO_PERIODIC_CHECK_1ST_MS 1000
|
||||
#define INFO_PERIODIC_CHECK_INTERVAL_MS 86400000
|
||||
|
||||
#define PERIODIC_UPLOAD_URL "https://info.remmina.org/info/upload_stats"
|
||||
#define INFO_REQUEST_URL "https://info.remmina.org/info/handshake"
|
||||
|
||||
|
||||
static RemminaInfoDialog *remmina_info_dialog;
|
||||
#define GET_OBJ(object_name) gtk_builder_get_object(remmina_info_dialog->builder, object_name)
|
||||
|
||||
typedef struct {
|
||||
gboolean send_stats;
|
||||
JsonNode *statsroot;
|
||||
} sc_tdata;
|
||||
|
||||
JsonNode *remmina_info_stats_get_uid()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonNode *r;
|
||||
GChecksum *chs;
|
||||
const gchar *uname, *hname;
|
||||
const gchar *uid_suffix;
|
||||
gchar *uid_prefix;
|
||||
gchar *uid;
|
||||
|
||||
/** @warning this function is usually executed on a dedicated thread,
|
||||
* not on the main thread
|
||||
*/
|
||||
|
||||
if (remmina_pref.info_uid_prefix == NULL || remmina_pref.info_uid_prefix[0] == 0) {
|
||||
/* Generate a new UUID_PREFIX for this installation */
|
||||
uid_prefix = remmina_gen_random_uuid();
|
||||
if (remmina_pref.info_uid_prefix) {
|
||||
g_free(remmina_pref.info_uid_prefix);
|
||||
}
|
||||
remmina_pref.info_uid_prefix = uid_prefix;
|
||||
remmina_pref_save();
|
||||
}
|
||||
|
||||
uname = g_get_user_name();
|
||||
hname = g_get_host_name();
|
||||
chs = g_checksum_new(G_CHECKSUM_SHA256);
|
||||
g_checksum_update(chs, (const guchar *)uname, strlen(uname));
|
||||
g_checksum_update(chs, (const guchar *)hname, strlen(hname));
|
||||
uid_suffix = g_checksum_get_string(chs);
|
||||
|
||||
uid = g_strdup_printf("%s-%.10s", remmina_pref.info_uid_prefix, uid_suffix);
|
||||
g_checksum_free(chs);
|
||||
|
||||
r = json_node_alloc();
|
||||
json_node_init_string(r, uid);
|
||||
|
||||
g_free(uid);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
JsonNode *remmina_info_stats_get_os_info()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
@ -186,15 +260,18 @@ JsonNode *remmina_stats_get_os_info()
|
||||
gchar *codename;
|
||||
GHashTableIter iter;
|
||||
gchar *key, *value;
|
||||
gchar *mage;
|
||||
|
||||
/** @warning this function is usually executed on a dedicated thread,
|
||||
* not on the main thread */
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "kernel_name");
|
||||
kernel_name = g_strdup_printf("%s", remmina_utils_get_kernel_name());
|
||||
kernel_name = remmina_utils_get_kernel_name();
|
||||
if (!kernel_name || kernel_name[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
}else {
|
||||
@ -203,7 +280,7 @@ JsonNode *remmina_stats_get_os_info()
|
||||
g_free(kernel_name);
|
||||
|
||||
json_builder_set_member_name(b, "kernel_release");
|
||||
kernel_release = g_strdup_printf("%s", remmina_utils_get_kernel_release());
|
||||
kernel_release = remmina_utils_get_kernel_release();
|
||||
if (!kernel_release || kernel_release[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
}else {
|
||||
@ -212,7 +289,7 @@ JsonNode *remmina_stats_get_os_info()
|
||||
g_free(kernel_release);
|
||||
|
||||
json_builder_set_member_name(b, "kernel_arch");
|
||||
kernel_arch = g_strdup_printf("%s", remmina_utils_get_kernel_arch());
|
||||
kernel_arch = remmina_utils_get_kernel_arch();
|
||||
if (!kernel_arch || kernel_arch[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
}else {
|
||||
@ -272,6 +349,12 @@ JsonNode *remmina_stats_get_os_info()
|
||||
json_builder_add_null_value(b);
|
||||
}
|
||||
|
||||
mage = remmina_utils_get_mage();
|
||||
json_builder_set_member_name(b, "mage");
|
||||
json_builder_add_string_value(b, mage);
|
||||
g_free(mage);
|
||||
|
||||
|
||||
/** @todo Add other means to identify a release name/description
|
||||
* to cover as much OS as possible, like /etc/issue
|
||||
*/
|
||||
@ -288,30 +371,98 @@ JsonNode *remmina_stats_get_os_info()
|
||||
*
|
||||
* @return a JSON Node structure containing the user’s environment.
|
||||
*/
|
||||
JsonNode *remmina_stats_get_user_env()
|
||||
JsonNode *remmina_info_stats_get_user_env()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
JsonNode *r;
|
||||
|
||||
gchar *language;
|
||||
gchar **environment;
|
||||
gchar **uenv;
|
||||
gchar *str;
|
||||
gsize env_len = 0;
|
||||
|
||||
language = remmina_utils_get_lang();
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
environment = g_get_environ();
|
||||
uenv = environment;
|
||||
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "language");
|
||||
|
||||
json_builder_add_string_value(b, language);
|
||||
|
||||
gchar *safe_ptr;
|
||||
while (*uenv && env_len <= MAX_ENV_LEN) {
|
||||
str = strtok_r(*uenv, "=", &safe_ptr);
|
||||
if (str != NULL) {
|
||||
env_len += strlen(str);
|
||||
json_builder_set_member_name(b, str);
|
||||
}
|
||||
str = strtok_r(NULL, "\n", &safe_ptr);
|
||||
if (str != NULL) {
|
||||
env_len += strlen(str);
|
||||
json_builder_add_string_value(b, str);
|
||||
}
|
||||
else{
|
||||
json_builder_add_string_value(b, "");
|
||||
}
|
||||
uenv++;
|
||||
}
|
||||
|
||||
json_builder_end_object(b);
|
||||
r = json_builder_get_root(b);
|
||||
g_object_unref(b);
|
||||
g_strfreev(environment);
|
||||
return r;
|
||||
}
|
||||
|
||||
JsonNode *remmina_info_stats_get_host() {
|
||||
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
JsonNode *r;
|
||||
|
||||
gchar *logical, *physical;
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "host");
|
||||
logical = remmina_utils_get_logical();
|
||||
if (!logical || logical[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
}
|
||||
else {
|
||||
json_builder_add_string_value(b, logical);
|
||||
}
|
||||
g_free(logical);
|
||||
|
||||
json_builder_set_member_name(b, "hw");
|
||||
physical = remmina_utils_get_link();
|
||||
if (!physical || physical[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
}
|
||||
else {
|
||||
json_builder_add_string_value(b, physical);
|
||||
}
|
||||
g_free(physical);
|
||||
|
||||
json_builder_end_object(b);
|
||||
r = json_builder_get_root(b);
|
||||
g_object_unref(b);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
JsonNode *remmina_stats_get_version()
|
||||
JsonNode *remmina_info_stats_get_version()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
@ -322,6 +473,9 @@ JsonNode *remmina_stats_get_version()
|
||||
* not on the main thread */
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "version");
|
||||
json_builder_add_string_value(b, VERSION);
|
||||
@ -353,7 +507,7 @@ JsonNode *remmina_stats_get_version()
|
||||
return r;
|
||||
}
|
||||
|
||||
JsonNode *remmina_stats_get_gtk_version()
|
||||
JsonNode *remmina_info_stats_get_gtk_version()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
@ -364,6 +518,10 @@ JsonNode *remmina_stats_get_gtk_version()
|
||||
*/
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "major");
|
||||
json_builder_add_int_value(b, gtk_get_major_version());
|
||||
@ -375,10 +533,9 @@ JsonNode *remmina_stats_get_gtk_version()
|
||||
r = json_builder_get_root(b);
|
||||
g_object_unref(b);
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
JsonNode *remmina_stats_get_gtk_backend()
|
||||
JsonNode *remmina_info_stats_get_gtk_backend()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonNode *r;
|
||||
@ -410,7 +567,7 @@ JsonNode *remmina_stats_get_gtk_backend()
|
||||
|
||||
}
|
||||
|
||||
JsonNode *remmina_stats_get_wm_name()
|
||||
JsonNode *remmina_info_stats_get_wm_name()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
@ -419,6 +576,10 @@ JsonNode *remmina_stats_get_wm_name()
|
||||
gchar *wmname;
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "window_manager");
|
||||
@ -426,9 +587,7 @@ JsonNode *remmina_stats_get_wm_name()
|
||||
/** We try to get the GNOME Shell version */
|
||||
wmver = remmina_sysinfo_get_gnome_shell_version();
|
||||
if (!wmver || wmver[0] == '\0') {
|
||||
REMMINA_DEBUG("GNOME Shell not found");
|
||||
}else {
|
||||
REMMINA_DEBUG("GNOME Shell version: %s\n", wmver);
|
||||
json_builder_add_string_value(b, "GNOME Shell");
|
||||
json_builder_set_member_name(b, "gnome_shell_ver");
|
||||
json_builder_add_string_value(b, wmver);
|
||||
@ -438,11 +597,8 @@ JsonNode *remmina_stats_get_wm_name()
|
||||
|
||||
wmname = remmina_sysinfo_get_wm_name();
|
||||
if (!wmname || wmname[0] == '\0') {
|
||||
/** When everything else fails with set the WM name to NULL **/
|
||||
REMMINA_DEBUG("Cannot determine the window manger name");
|
||||
json_builder_add_string_value(b, "n/a");
|
||||
}else {
|
||||
REMMINA_DEBUG("Window manger names %s", wmname);
|
||||
json_builder_add_string_value(b, wmname);
|
||||
}
|
||||
g_free(wmname);
|
||||
@ -454,7 +610,7 @@ JsonNode *remmina_stats_get_wm_name()
|
||||
return r;
|
||||
}
|
||||
|
||||
JsonNode *remmina_stats_get_indicator()
|
||||
JsonNode *remmina_info_stats_get_indicator()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
@ -462,6 +618,10 @@ JsonNode *remmina_stats_get_indicator()
|
||||
gboolean sni; /** Support for StatusNotifier or AppIndicator */
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "appindicator_supported");
|
||||
@ -478,8 +638,11 @@ JsonNode *remmina_stats_get_indicator()
|
||||
json_builder_add_int_value(b, 0);
|
||||
#endif
|
||||
}
|
||||
/** StatusNotifier/Appindicator NOT supported by desktop */
|
||||
json_builder_add_int_value(b, 0);
|
||||
else{
|
||||
/** StatusNotifier/Appindicator NOT supported by desktop */
|
||||
json_builder_add_int_value(b, 0);
|
||||
}
|
||||
|
||||
json_builder_set_member_name(b, "icon_is_active");
|
||||
if (remmina_icon_is_available()) {
|
||||
/** Remmina icon is active */
|
||||
@ -508,7 +671,7 @@ JsonNode *remmina_stats_get_indicator()
|
||||
* This is used as a callback function with remmina_file_manager_iterate.
|
||||
* @todo Move this in a separate file.
|
||||
*/
|
||||
static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_data)
|
||||
static void remmina_info_profiles_get_data(RemminaFile *remminafile, gpointer user_data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
|
||||
@ -527,7 +690,6 @@ static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_da
|
||||
pdata->protocol = remmina_file_get_string(remminafile, "protocol");
|
||||
//pdata->pdatestr = remmina_file_get_string(remminafile, "last_success");
|
||||
const gchar *last_success = remmina_file_get_string(remminafile, "last_success");
|
||||
g_debug("%s date %s", pdata->protocol, last_success);
|
||||
|
||||
prof_gdate = pdata_gdate = NULL;
|
||||
if (last_success && last_success[0] != '\0' && strlen(last_success) >= 6) {
|
||||
@ -572,32 +734,26 @@ static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_da
|
||||
|
||||
/** When both date in the hash and in the profile are valid we compare the date */
|
||||
if (prof_gdate != NULL && pdata_gdate != NULL ) {
|
||||
g_debug("Comparing dates");
|
||||
gint res = g_date_time_compare( pdata_gdate, prof_gdate );
|
||||
/** If the date in the hash less than the date in the profile, we take the latter */
|
||||
if (res < 0 ) {
|
||||
g_debug("hash date is less than profile date. Replacing date in the hashtable");
|
||||
g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(last_success));
|
||||
} else {
|
||||
g_debug("profile date is less than hash date. Replacing date in the hashtable");
|
||||
g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
|
||||
}
|
||||
|
||||
}
|
||||
/** If the date in the profile is NOT valid and the date in the hash is valid we keep the latter */
|
||||
if (prof_gdate == NULL && pdata_gdate != NULL) {
|
||||
g_debug("prof_gdate is NULL, replacing date in the hashtable");
|
||||
g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(pdata->pdatestr));
|
||||
}
|
||||
|
||||
/** If the date in the hash is NOT valid and the date in the profile is valid we keep the latter */
|
||||
if (prof_gdate != NULL && pdata_gdate == NULL) {
|
||||
g_debug("pdata_gdate is NULL, replacing date in the hashtable");
|
||||
g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), g_strdup(last_success));
|
||||
}
|
||||
/** If both date are NULL, we insert NULL for that protocol */
|
||||
if ((prof_gdate == NULL && pdata_gdate == NULL) && pdata->pdatestr) {
|
||||
g_debug("All dates are NULL, replacing date in the hashtable");
|
||||
g_hash_table_replace(pdata->proto_date, g_strdup(pdata->protocol), NULL);
|
||||
}
|
||||
} else {
|
||||
@ -611,7 +767,6 @@ static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_da
|
||||
}
|
||||
}
|
||||
}
|
||||
g_debug("pdata set to %s protocol with last_success to %s", pdata->protocol, pdata->pdatestr);
|
||||
if (pdata_gdate)
|
||||
g_date_time_unref(pdata_gdate);
|
||||
if (prof_gdate)
|
||||
@ -641,7 +796,7 @@ static void remmina_profiles_get_data(RemminaFile *remminafile, gpointer user_da
|
||||
* @return a JSON Node structure containing the protocol usage statistics.
|
||||
*
|
||||
*/
|
||||
JsonNode *remmina_stats_get_profiles()
|
||||
JsonNode *remmina_info_stats_get_profiles()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
|
||||
@ -658,6 +813,10 @@ JsonNode *remmina_stats_get_profiles()
|
||||
pdata = g_malloc0(sizeof(struct ProfilesData));
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "profile_count");
|
||||
@ -671,9 +830,8 @@ JsonNode *remmina_stats_get_profiles()
|
||||
(GDestroyNotify)g_free, NULL);
|
||||
|
||||
profiles_count = remmina_file_manager_iterate(
|
||||
(GFunc)remmina_profiles_get_data,
|
||||
(GFunc)remmina_info_profiles_get_data,
|
||||
(gpointer)pdata);
|
||||
g_debug("Number of profiles: %d", profiles_count);
|
||||
|
||||
json_builder_add_int_value(b, profiles_count);
|
||||
|
||||
@ -686,11 +844,9 @@ JsonNode *remmina_stats_get_profiles()
|
||||
g_hash_table_iter_init(&pdateiter, pdata->proto_date);
|
||||
while (g_hash_table_iter_next(&pdateiter, &pdatekey, &pdatevalue)) {
|
||||
s = g_strdup_printf("DATE_%s", (gchar*)pdatekey);
|
||||
g_debug("Protocol date label: %s", s);
|
||||
json_builder_set_member_name(b, s);
|
||||
g_free(s);
|
||||
json_builder_add_string_value(b, (gchar*)pdatevalue);
|
||||
g_debug("Protocol date: %s", (gchar*)pdatevalue);
|
||||
}
|
||||
|
||||
json_builder_end_object(b);
|
||||
@ -713,7 +869,7 @@ JsonNode *remmina_stats_get_profiles()
|
||||
* @return a JSON Node structure containing the secret plugin in use
|
||||
*
|
||||
*/
|
||||
JsonNode *remmina_stats_get_secret_plugin()
|
||||
JsonNode *remmina_info_stats_get_secret_plugin()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
|
||||
@ -723,6 +879,10 @@ JsonNode *remmina_stats_get_secret_plugin()
|
||||
secret_plugin = remmina_plugin_manager_get_secret_plugin();
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
if (secret_plugin && secret_plugin->is_service_available) {
|
||||
@ -742,7 +902,7 @@ JsonNode *remmina_stats_get_secret_plugin()
|
||||
* @return a JSON Node structure containing the status of the primary password
|
||||
*
|
||||
*/
|
||||
JsonNode *remmina_stats_get_primary_password_status()
|
||||
JsonNode *remmina_info_stats_get_primary_password_status()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
|
||||
@ -750,6 +910,10 @@ JsonNode *remmina_stats_get_primary_password_status()
|
||||
JsonNode *r;
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "primary_password_status");
|
||||
@ -772,7 +936,7 @@ JsonNode *remmina_stats_get_primary_password_status()
|
||||
* @return a JSON Node structure containing the status of the primary password
|
||||
*
|
||||
*/
|
||||
JsonNode *remmina_stats_get_kiosk_mode()
|
||||
JsonNode *remmina_info_stats_get_kiosk_mode()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
|
||||
@ -780,6 +944,10 @@ JsonNode *remmina_stats_get_kiosk_mode()
|
||||
JsonNode *r;
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
json_builder_set_member_name(b, "kiosk_status");
|
||||
@ -796,75 +964,357 @@ JsonNode *remmina_stats_get_kiosk_mode()
|
||||
return r;
|
||||
}
|
||||
|
||||
JsonNode *remmina_info_stats_get_python()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
JsonNode *r;
|
||||
gchar *version;
|
||||
|
||||
version = remmina_utils_get_python();
|
||||
b = json_builder_new();
|
||||
if(b != NULL)
|
||||
{
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "version");
|
||||
if (!version || version[0] == '\0') {
|
||||
json_builder_add_null_value(b);
|
||||
} else {
|
||||
json_builder_add_string_value(b, version);
|
||||
}
|
||||
|
||||
json_builder_end_object(b);
|
||||
r = json_builder_get_root(b);
|
||||
g_object_unref(b);
|
||||
return r;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all statistics in JSON format to send periodically to the PHP server.
|
||||
* Get all statistics in JSON format to send periodically to the server.
|
||||
* The caller should free the returned buffer with g_free()
|
||||
* @warning This function is usually executed on a dedicated thread,
|
||||
* not on the main thread.
|
||||
* @return a pointer to the JSON string.
|
||||
*/
|
||||
JsonNode *remmina_stats_get_all()
|
||||
JsonNode *remmina_info_stats_get_all()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
JsonBuilder *b;
|
||||
JsonNode *n;
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
|
||||
n = remmina_stats_get_version();
|
||||
n = remmina_info_stats_get_uid();
|
||||
json_builder_set_member_name(b, "UID");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_info_stats_get_version();
|
||||
json_builder_set_member_name(b, "REMMINAVERSION");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_os_info();
|
||||
n = remmina_info_stats_get_os_info();
|
||||
json_builder_set_member_name(b, "SYSTEM");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
/**
|
||||
* The section ENVIRONMENT collect all the user’s environment related
|
||||
* settings.
|
||||
*/
|
||||
n = remmina_stats_get_user_env();
|
||||
n = remmina_info_stats_get_user_env();
|
||||
json_builder_set_member_name(b, "ENVIRONMENT");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_gtk_version();
|
||||
n = remmina_info_stats_get_gtk_version();
|
||||
json_builder_set_member_name(b, "GTKVERSION");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_gtk_backend();
|
||||
n = remmina_info_stats_get_gtk_backend();
|
||||
json_builder_set_member_name(b, "GTKBACKEND");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_wm_name();
|
||||
n = remmina_info_stats_get_wm_name();
|
||||
json_builder_set_member_name(b, "WINDOWMANAGER");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_indicator();
|
||||
n = remmina_info_stats_get_indicator();
|
||||
json_builder_set_member_name(b, "APPINDICATOR");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_profiles();
|
||||
n = remmina_info_stats_get_profiles();
|
||||
json_builder_set_member_name(b, "PROFILES");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_secret_plugin();
|
||||
n = remmina_info_stats_get_secret_plugin();
|
||||
json_builder_set_member_name(b, "ACTIVESECRETPLUGIN");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_primary_password_status();
|
||||
n = remmina_info_stats_get_primary_password_status();
|
||||
json_builder_set_member_name(b, "HASPRIMARYPASSWORD");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_stats_get_kiosk_mode();
|
||||
n = remmina_info_stats_get_kiosk_mode();
|
||||
json_builder_set_member_name(b, "KIOSK");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_info_stats_get_python();
|
||||
json_builder_set_member_name(b, "PYTHON");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
n = remmina_info_stats_get_host();
|
||||
json_builder_set_member_name(b, "HOST");
|
||||
json_builder_add_value(b, n);
|
||||
|
||||
json_builder_end_object(b);
|
||||
n = json_builder_get_root(b);
|
||||
g_object_unref(b);
|
||||
|
||||
return n;
|
||||
|
||||
}
|
||||
|
||||
void remmina_info_schedule()
|
||||
{
|
||||
sc_tdata *data;
|
||||
|
||||
data = g_malloc(sizeof(sc_tdata));
|
||||
data->send_stats = !info_disable_stats;
|
||||
|
||||
remmina_scheduler_setup(remmina_info_periodic_check,
|
||||
data,
|
||||
INFO_PERIODIC_CHECK_1ST_MS,
|
||||
INFO_PERIODIC_CHECK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
static void remmina_info_close_clicked(GtkButton *btn, gpointer user_data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
if (remmina_info_dialog->dialog) {
|
||||
gtk_widget_destroy(GTK_WIDGET(remmina_info_dialog->dialog));
|
||||
}
|
||||
remmina_info_dialog->dialog = NULL;
|
||||
g_free(remmina_info_dialog);
|
||||
remmina_info_dialog = NULL;
|
||||
}
|
||||
|
||||
static gboolean remmina_info_dialog_deleted(GtkButton *btn, gpointer user_data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gtk_widget_destroy(GTK_WIDGET(remmina_info_dialog->dialog));
|
||||
remmina_info_dialog->dialog = NULL;
|
||||
g_free(remmina_info_dialog);
|
||||
remmina_info_dialog = NULL;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gboolean remmina_info_show_response(gpointer user_data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
GtkWindow * parent = remmina_main_get_window();
|
||||
remmina_info_dialog = g_new0(RemminaInfoDialog, 1);
|
||||
remmina_info_dialog->retval = 1;
|
||||
|
||||
RemminaInfoMessage *message = (RemminaInfoMessage*)user_data;
|
||||
|
||||
remmina_info_dialog->builder = remmina_public_gtk_builder_new_from_resource("/org/remmina/Remmina/src/../data/ui/remmina_info.glade");
|
||||
remmina_info_dialog->dialog = GTK_DIALOG(gtk_builder_get_object(remmina_info_dialog->builder, "RemminaInfoDialog"));
|
||||
|
||||
remmina_info_dialog->remmina_info_text_view = GTK_TEXT_VIEW(GET_OBJ("remmina_info_text_view"));
|
||||
remmina_info_dialog->remmina_info_label = GTK_LABEL(GET_OBJ("remmina_info_label"));
|
||||
|
||||
remmina_info_dialog->remmina_info_button_close = GTK_BUTTON(GET_OBJ("remmina_info_button_close"));
|
||||
gtk_widget_set_can_default(GTK_WIDGET(remmina_info_dialog->remmina_info_button_close), TRUE);
|
||||
gtk_widget_grab_default(GTK_WIDGET(remmina_info_dialog->remmina_info_button_close));
|
||||
|
||||
gtk_label_set_markup(remmina_info_dialog->remmina_info_label, message->info_string);
|
||||
|
||||
g_signal_connect(remmina_info_dialog->remmina_info_button_close, "clicked",
|
||||
G_CALLBACK(remmina_info_close_clicked), (gpointer)remmina_info_dialog);
|
||||
g_signal_connect(remmina_info_dialog->dialog, "close",
|
||||
G_CALLBACK(remmina_info_close_clicked), NULL);
|
||||
g_signal_connect(remmina_info_dialog->dialog, "delete-event",
|
||||
G_CALLBACK(remmina_info_dialog_deleted), NULL);
|
||||
|
||||
/* Connect signals */
|
||||
gtk_builder_connect_signals(remmina_info_dialog->builder, NULL);
|
||||
|
||||
/* Show the non-modal news dialog */
|
||||
gtk_widget_show_all(GTK_WIDGET(remmina_info_dialog->dialog));
|
||||
gtk_window_present(GTK_WINDOW(remmina_info_dialog->dialog));
|
||||
if (parent) {
|
||||
gtk_window_set_transient_for(GTK_WINDOW(remmina_info_dialog->dialog), parent);
|
||||
}
|
||||
gtk_window_set_modal(GTK_WINDOW(remmina_info_dialog->dialog), TRUE);
|
||||
gtk_window_set_title(GTK_WINDOW(remmina_info_dialog->dialog), message->title_string);
|
||||
g_free(message);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Post request to info server
|
||||
*
|
||||
* @return gboolean
|
||||
*/
|
||||
static gboolean remmina_info_stats_collector_done(gpointer data)
|
||||
{
|
||||
JsonNode *n;
|
||||
JsonGenerator *g;
|
||||
gchar *unenc_s, *enc_s;
|
||||
JsonBuilder *b;
|
||||
JsonObject *o;
|
||||
gchar *uid;
|
||||
JsonNode *root;
|
||||
|
||||
root = (JsonNode *)data;
|
||||
if (root == NULL) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
n = root;
|
||||
|
||||
if ((o = json_node_get_object(n)) == NULL) {
|
||||
g_free(data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
uid = g_strdup(json_object_get_string_member(o, "UID"));
|
||||
|
||||
g = json_generator_new();
|
||||
json_generator_set_root(g, n);
|
||||
json_node_unref(n);
|
||||
unenc_s = json_generator_to_data(g, NULL);
|
||||
g_object_unref(g);
|
||||
|
||||
EVP_PKEY *remmina_pubkey = remmina_get_pubkey(RSA_KEYTYPE, remmina_RSA_PubKey_v1);
|
||||
if (remmina_pubkey == NULL) {
|
||||
g_free(uid);
|
||||
g_free(unenc_s);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
enc_s = remmina_rsa_encrypt_string(remmina_pubkey, unenc_s);
|
||||
if (enc_s == NULL) {
|
||||
g_free(uid);
|
||||
g_free(unenc_s);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
|
||||
EVP_PKEY_free(remmina_pubkey);
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "keyversion");
|
||||
json_builder_add_int_value(b, 1);
|
||||
json_builder_set_member_name(b, "encdata");
|
||||
json_builder_add_string_value(b, enc_s);
|
||||
json_builder_set_member_name(b, "UID");
|
||||
json_builder_add_string_value(b, uid);
|
||||
json_builder_set_member_name(b, "news_enabled");
|
||||
json_builder_add_int_value(b, TRUE ? 0 : 1);
|
||||
json_builder_set_member_name(b, "stats_enabled");
|
||||
json_builder_add_int_value(b, TRUE ? 0 : 1);
|
||||
json_builder_set_member_name(b, "tip_enabled");
|
||||
json_builder_add_int_value(b, TRUE ? 0 : 1);
|
||||
|
||||
|
||||
json_builder_end_object(b);
|
||||
n = json_builder_get_root(b);
|
||||
|
||||
g_free(unenc_s);
|
||||
g_object_unref(b);
|
||||
g_free(uid);
|
||||
g_free(enc_s);
|
||||
|
||||
g = json_generator_new();
|
||||
json_generator_set_root(g, n);
|
||||
enc_s = json_generator_to_data(g, NULL);
|
||||
g_object_unref(g);
|
||||
|
||||
remmina_curl_compose_message(enc_s, "POST", PERIODIC_UPLOAD_URL, NULL);
|
||||
|
||||
json_node_unref(n);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect stats and notify when collection is done
|
||||
*
|
||||
* @return gpointer
|
||||
*/
|
||||
gpointer remmina_info_stats_collector()
|
||||
{
|
||||
JsonNode *n;
|
||||
|
||||
n = remmina_info_stats_get_all();
|
||||
|
||||
/* Stats collecting is done. Notify main thread calling
|
||||
* remmina_info_stats_collector_done() */
|
||||
g_idle_add(remmina_info_stats_collector_done, n);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void remmina_info_request(gpointer data)
|
||||
{
|
||||
// send initial handshake here, in a callback decide how to handle response
|
||||
|
||||
JsonNode *n;
|
||||
JsonGenerator *g;
|
||||
gchar *enc_s;
|
||||
JsonBuilder *b;
|
||||
const gchar *uid = "000000";
|
||||
|
||||
n = remmina_info_stats_get_uid();
|
||||
if (n != NULL){
|
||||
uid = json_node_get_string(n);
|
||||
}
|
||||
|
||||
b = json_builder_new();
|
||||
if (b == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
json_builder_begin_object(b);
|
||||
json_builder_set_member_name(b, "keyversion");
|
||||
json_builder_add_int_value(b, 1);
|
||||
json_builder_set_member_name(b, "UID");
|
||||
json_builder_add_string_value(b, uid);
|
||||
json_builder_set_member_name(b, "news_enabled");
|
||||
json_builder_add_int_value(b, info_disable_news ? 0 : 1);
|
||||
json_builder_set_member_name(b, "stats_enabled");
|
||||
json_builder_add_int_value(b, info_disable_stats ? 0 : 1);
|
||||
json_builder_set_member_name(b, "tip_enabled");
|
||||
json_builder_add_int_value(b, info_disable_tip ? 0 : 1);
|
||||
|
||||
|
||||
json_builder_end_object(b);
|
||||
n = json_builder_get_root(b);
|
||||
|
||||
g_object_unref(b);
|
||||
|
||||
|
||||
g = json_generator_new();
|
||||
json_generator_set_root(g, n);
|
||||
enc_s = json_generator_to_data(g, NULL);
|
||||
g_object_unref(g);
|
||||
remmina_curl_compose_message(enc_s, "POST", INFO_REQUEST_URL, NULL);
|
||||
json_node_unref(n);
|
||||
}
|
||||
|
||||
gboolean remmina_info_periodic_check(gpointer user_data)
|
||||
{
|
||||
remmina_info_request(user_data);
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
70
src/remmina_info.h
Normal file
70
src/remmina_info.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Remmina - The GTK+ Remote Desktop Client
|
||||
* Copyright (C) 2016-2023 Antenore Gatta, Giovanni Panozzo
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. * If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. * If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. * If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include "json-glib/json-glib.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
JsonNode *remmina_info_stats_get_all(void);
|
||||
JsonNode *remmina_info_stats_get_os_info(void);
|
||||
JsonNode *remmina_info_stats_get_python(void);
|
||||
JsonNode *remmina_info_stats_get_uid(void);
|
||||
gboolean remmina_info_show_response(gpointer user_data);
|
||||
gpointer remmina_info_stats_collector();
|
||||
gboolean remmina_info_periodic_check(gpointer user_data);
|
||||
void remmina_info_schedule(void);
|
||||
|
||||
typedef struct _RemminaInfoDialog
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GtkDialog *dialog;
|
||||
|
||||
GtkTextView *remmina_info_text_view;
|
||||
GtkLabel *remmina_info_label;
|
||||
GtkButton *remmina_info_button_close;
|
||||
|
||||
gint retval;
|
||||
} RemminaInfoDialog;
|
||||
|
||||
typedef struct _RemminaInfoMessage
|
||||
{
|
||||
const gchar *info_string;
|
||||
const gchar *title_string;
|
||||
|
||||
} RemminaInfoMessage;
|
||||
|
||||
G_END_DECLS
|
@ -39,7 +39,7 @@
|
||||
#include "remmina_public.h"
|
||||
#include "remmina_pref.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_stats.h"
|
||||
#include "remmina_info.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
|
||||
gboolean logstart;
|
||||
@ -73,7 +73,7 @@ void remmina_log_stats()
|
||||
TRACE_CALL(__func__);
|
||||
JsonNode *n;
|
||||
|
||||
n = remmina_stats_get_all();
|
||||
n = remmina_info_stats_get_all();
|
||||
if (n != NULL) {
|
||||
|
||||
JsonGenerator *g = json_generator_new();
|
||||
@ -213,6 +213,18 @@ void _remmina_info(const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
// always appends newline
|
||||
g_info ("%s", text);
|
||||
|
||||
@ -237,6 +249,18 @@ void _remmina_message(const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
// always appends newline
|
||||
g_message ("%s", text);
|
||||
|
||||
@ -253,7 +277,7 @@ void _remmina_message(const gchar *fmt, ...)
|
||||
|
||||
/**
|
||||
* Print a string in the Remmina Debug Windows and in the terminal.
|
||||
* The string will be visible in the terminal if G_MESSAGES_DEBUG=all
|
||||
* The string will be visible in the terminal if G_MESSAGES_DEBUG=remmina
|
||||
* Variadic function of REMMINA_DEBUG
|
||||
*/
|
||||
void _remmina_debug(const gchar *fun, const gchar *fmt, ...)
|
||||
@ -266,6 +290,18 @@ void _remmina_debug(const gchar *fun, const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
g_autofree gchar *buf = g_strconcat("(", fun, ") - ", text, NULL);
|
||||
g_free(text);
|
||||
|
||||
@ -293,6 +329,18 @@ void _remmina_warning(const gchar *fun, const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
g_autofree gchar *buf = g_strconcat("(", fun, ") - ", text, NULL);
|
||||
g_free(text);
|
||||
|
||||
@ -318,6 +366,18 @@ void _remmina_audit(const gchar *fun, const gchar *fmt, ...)
|
||||
gchar *text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
#if GLIB_CHECK_VERSION(2,62,0)
|
||||
GDateTime* tv = g_date_time_new_now_local();
|
||||
gchar *isodate = g_date_time_format_iso8601(tv);
|
||||
@ -362,6 +422,18 @@ void _remmina_error(const gchar *fun, const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
g_autofree gchar *buf = g_strconcat("(", fun, ") - ", text, NULL);
|
||||
g_free(text);
|
||||
|
||||
@ -389,6 +461,18 @@ void _remmina_critical(const gchar *fun, const gchar *fmt, ...)
|
||||
text = g_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
gchar *log_filename = g_build_filename(g_get_tmp_dir(), LOG_FILE_NAME, NULL);
|
||||
FILE *log_file = fopen(log_filename, "a");
|
||||
g_free(log_filename);
|
||||
|
||||
if (log_file != NULL)
|
||||
{
|
||||
gchar* text_log = g_strconcat(text, "\n", NULL);
|
||||
fwrite(text_log, sizeof(char), strlen(text_log), log_file);
|
||||
fclose(log_file);
|
||||
g_free(text_log);
|
||||
}
|
||||
|
||||
g_autofree gchar *buf = g_strconcat("(", fun, ") - ", text, NULL);
|
||||
g_free(text);
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define LOG_FILE_NAME "remmina_log_file.log"
|
||||
#define REMMINA_INFO(fmt, ...) _remmina_info(fmt, ## __VA_ARGS__)
|
||||
#define REMMINA_MESSAGE(fmt, ...) _remmina_message(fmt, ## __VA_ARGS__)
|
||||
#define REMMINA_DEBUG(fmt, ...) _remmina_debug(__func__, fmt, ## __VA_ARGS__)
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "remmina_pref_dialog.h"
|
||||
#include "remmina_widget_pool.h"
|
||||
#include "remmina_plugin_manager.h"
|
||||
#include "remmina_bug_report.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_icon.h"
|
||||
#include "remmina_main.h"
|
||||
@ -99,6 +100,7 @@ static GActionEntry app_actions[] = {
|
||||
{ "mpchange", remmina_main_on_action_application_mpchange, NULL, NULL, NULL },
|
||||
{ "plugins", remmina_main_on_action_application_plugins, NULL, NULL, NULL },
|
||||
{ "preferences", remmina_main_on_action_application_preferences, "i", NULL, NULL },
|
||||
{ "bug_report", remmina_main_on_action_application_bug_report, NULL, NULL, NULL},
|
||||
{ "dark", remmina_main_on_action_application_dark_theme, NULL, NULL, NULL },
|
||||
{ "debug", remmina_main_on_action_help_debug, NULL, NULL, NULL },
|
||||
{ "community", remmina_main_on_action_help_community, NULL, NULL, NULL },
|
||||
@ -855,6 +857,7 @@ void remmina_main_on_action_connection_new(GSimpleAction *action, GVariant *para
|
||||
return;
|
||||
GtkWidget *widget;
|
||||
|
||||
remmina_plugin_manager_get_available_plugins();
|
||||
if (remmina_pref_get_boolean("use_primary_password")
|
||||
&& remmina_pref_get_boolean("lock_edit")
|
||||
&& remmina_unlock_new(remminamain->window) == 0)
|
||||
@ -1111,7 +1114,7 @@ void remmina_main_on_action_application_preferences(GSimpleAction *action, GVari
|
||||
|
||||
GtkWidget *widget = remmina_pref_dialog_new(tab_num, remminamain->window);
|
||||
|
||||
gtk_widget_show_all(widget);
|
||||
gtk_widget_show(widget);
|
||||
}
|
||||
|
||||
void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data)
|
||||
@ -1334,6 +1337,7 @@ void remmina_main_on_action_tools_export(GSimpleAction *action, GVariant *param,
|
||||
void remmina_main_on_action_application_plugins(GSimpleAction *action, GVariant *param, gpointer data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
remmina_plugin_manager_get_available_plugins();
|
||||
remmina_plugin_manager_show(remminamain->window);
|
||||
}
|
||||
|
||||
@ -1389,6 +1393,12 @@ void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *p
|
||||
remmina_about_open(remminamain->window);
|
||||
};
|
||||
|
||||
void remmina_main_on_action_application_bug_report(GSimpleAction *action, GVariant *param, gpointer data)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
remmina_bug_report_open(remminamain->window);
|
||||
};
|
||||
|
||||
static gboolean is_empty(const gchar *s)
|
||||
{
|
||||
if (s == NULL)
|
||||
|
@ -115,6 +115,7 @@ void remmina_main_save_before_destroy(void);
|
||||
void remmina_main_show_dialog(GtkMessageType msg, GtkButtonsType buttons, const gchar* message);
|
||||
void remmina_main_show_warning_dialog(const gchar *message);
|
||||
void remmina_main_on_action_application_about(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
void remmina_main_on_action_application_bug_report(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
void remmina_main_on_action_application_default(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
void remmina_main_on_action_application_mpchange(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
void remmina_main_on_action_application_plugins(GSimpleAction *action, GVariant *param, gpointer data);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,25 +37,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "remmina/plugin.h"
|
||||
#include "json-glib/json-glib.h"
|
||||
|
||||
|
||||
#define DOWNLOAD_URL "https://plugins.remmina.org/plugins/plugin_download"
|
||||
#define LIST_URL "https://plugins.remmina.org/plugins/get_list"
|
||||
|
||||
#define ON_DOWNLOAD 1
|
||||
#define ON_CLOSE 2
|
||||
#define FAIL 0
|
||||
#define PARTIAL_SUCCESS 1
|
||||
#define SUCCESS 2
|
||||
#define MAX_PLUGIN_NAME_SIZE 100
|
||||
#define MAX_PLUGINS_ALLOWED_TO_DOWNLOAD 20
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gboolean (*RemminaPluginFunc)(gchar *name, RemminaPlugin *plugin, gpointer data);
|
||||
|
||||
void remmina_plugin_manager_init(void);
|
||||
JsonNode *remmina_plugin_manager_get_installed_plugins(void);
|
||||
RemminaPlugin *remmina_plugin_manager_get_plugin(RemminaPluginType type, const gchar *name);
|
||||
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype);
|
||||
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data);
|
||||
void remmina_plugin_manager_show(GtkWindow *parent);
|
||||
void remmina_plugin_manager_for_each_plugin_stdout(RemminaPluginType type, RemminaPluginFunc func, gpointer data);
|
||||
void remmina_plugin_manager_show_stdout();
|
||||
void* remmina_plugin_manager_get_available_plugins();
|
||||
gboolean remmina_plugin_manager_parse_plugin_list(gpointer user_data);
|
||||
gboolean remmina_plugin_manager_download_plugins(gpointer user_data);
|
||||
RemminaFilePlugin *remmina_plugin_manager_get_import_file_handler(const gchar *file);
|
||||
RemminaFilePlugin *remmina_plugin_manager_get_export_file_handler(RemminaFile *remminafile);
|
||||
RemminaSecretPlugin *remmina_plugin_manager_get_secret_plugin(void);
|
||||
const gchar *remmina_plugin_manager_get_canonical_setting_name(const RemminaProtocolSetting *setting);
|
||||
gboolean remmina_plugin_manager_is_encrypted_setting(RemminaProtocolPlugin *pp, const char *setting);
|
||||
gboolean remmina_gtksocket_available();
|
||||
|
||||
gboolean remmina_plugin_manager_verify_duplicate_plugins(RemminaPlugin *test_plugin);
|
||||
void remmina_plugin_manager_toggle_checkbox(GtkCellRendererToggle *cell, gchar *path, GtkListStore *model);
|
||||
void remmina_plugin_manager_on_response(GtkDialog *dialog, gint response_id, gpointer user_data);
|
||||
void remmina_append_json_objects_from_response_str(JsonReader* response_str, GArray* data_array);
|
||||
guint remmina_plugin_manager_deserialize_plugin_response(GArray *name_array);
|
||||
gboolean remmina_attempt_to_write_plugin_data_to_disk(RemminaServerPluginResponse *plugin);
|
||||
GFile* remmina_create_plugin_file(const gchar* plugin_name, const gchar* plugin_version);
|
||||
JsonNode *remmina_plugin_manager_plugin_stats_get_all(void);
|
||||
extern RemminaPluginService remmina_plugin_manager_service;
|
||||
|
||||
typedef gboolean (*RemminaPluginLoaderFunc)(RemminaPluginService*, const gchar* name);
|
||||
@ -65,6 +89,13 @@ typedef struct {
|
||||
RemminaPluginLoaderFunc func;
|
||||
} RemminaPluginLoader;
|
||||
|
||||
typedef struct {
|
||||
GtkWidget *label;
|
||||
GtkListStore *store;
|
||||
GtkWidget *spinner;
|
||||
gboolean downloading;
|
||||
} SignalData;
|
||||
|
||||
gboolean remmina_plugin_manager_loader_supported(const char *filetype);
|
||||
void remmina_plugin_manager_add_loader(char *filetype, RemminaPluginLoaderFunc func);
|
||||
|
||||
|
@ -58,6 +58,10 @@
|
||||
const gchar *default_resolutions = "640x480,800x600,1024x768,1152x864,1280x960,1400x1050,1920x1080";
|
||||
const gchar *default_keystrokes = "Send hello world§hello world\\n";
|
||||
|
||||
extern gboolean info_disable_stats;
|
||||
extern gboolean info_disable_news;
|
||||
extern gboolean info_disable_tip;
|
||||
|
||||
gchar *remmina_keymap_file;
|
||||
static GHashTable *remmina_keymap_table = NULL;
|
||||
|
||||
@ -217,6 +221,9 @@ void remmina_pref_file_load_colors(GKeyFile *gkeyfile, RemminaColorPref *color_p
|
||||
}
|
||||
}
|
||||
|
||||
extern gboolean disablenews;
|
||||
extern gboolean disablestats;
|
||||
|
||||
void remmina_pref_init(void)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
@ -751,9 +758,72 @@ void remmina_pref_init(void)
|
||||
else
|
||||
remmina_pref.vte_shortcutkey_search_text = GDK_KEY_g;
|
||||
|
||||
|
||||
remmina_pref_file_load_colors(gkeyfile, &remmina_pref.color_pref);
|
||||
|
||||
if (g_key_file_has_key(gkeyfile, "remmina_info", "periodic_news_last_checksum", NULL)) {
|
||||
remmina_pref.periodic_news_last_checksum = g_key_file_get_string(gkeyfile, "remmina_info", "periodic_news_last_checksum", NULL);
|
||||
}
|
||||
else {
|
||||
remmina_pref.periodic_news_last_checksum = NULL;
|
||||
}
|
||||
|
||||
if (disablenews) {
|
||||
info_disable_news = 1;
|
||||
}
|
||||
else if (g_key_file_has_key(gkeyfile, "remmina_info", "periodic_news_permitted", NULL)) {
|
||||
remmina_pref.disable_news = !g_key_file_get_boolean(gkeyfile, "remmina_info", "periodic_news_permitted", NULL);
|
||||
info_disable_news = remmina_pref.disable_news;
|
||||
}
|
||||
else {
|
||||
remmina_pref.disable_news = TRUE;
|
||||
info_disable_news = remmina_pref.disable_news;
|
||||
}
|
||||
|
||||
if (disablestats) {
|
||||
info_disable_stats = 1;
|
||||
}
|
||||
else if (g_key_file_has_key(gkeyfile, "remmina_info", "periodic_usage_stats_permitted", NULL)) {
|
||||
remmina_pref.disable_stats = !g_key_file_get_boolean(gkeyfile, "remmina_info", "periodic_usage_stats_permitted", NULL);
|
||||
info_disable_stats = remmina_pref.disable_stats;
|
||||
}
|
||||
else {
|
||||
remmina_pref.disable_stats = TRUE;
|
||||
info_disable_stats = remmina_pref.disable_stats;
|
||||
}
|
||||
|
||||
if (g_key_file_has_key(gkeyfile, "remmina_info", "disable_tip", NULL)) {
|
||||
remmina_pref.disable_tip = g_key_file_get_boolean(gkeyfile, "remmina_info", "disable_tip", NULL);
|
||||
info_disable_tip = remmina_pref.disable_tip;
|
||||
}
|
||||
else {
|
||||
remmina_pref.disable_tip = TRUE;
|
||||
info_disable_tip = remmina_pref.disable_tip;
|
||||
}
|
||||
|
||||
#ifdef DISABLE_NEWS
|
||||
info_disable_news = 1;
|
||||
remmina_pref.disable_news = TRUE;
|
||||
#endif
|
||||
|
||||
#ifdef DISABLE_STATS
|
||||
info_disable_stats = 1;
|
||||
remmina_pref.disable_stats = TRUE;
|
||||
#endif
|
||||
|
||||
#ifdef DISABLE_TIP
|
||||
info_disable_tip = 1;
|
||||
remmina_pref.disable_tip = TRUE;
|
||||
#endif
|
||||
|
||||
|
||||
if (g_key_file_has_key(gkeyfile, "remmina_info", "info_uid_prefix", NULL)) {
|
||||
remmina_pref.info_uid_prefix = g_key_file_get_string(gkeyfile, "remmina_info", "info_uid_prefix", NULL);
|
||||
}
|
||||
else {
|
||||
remmina_pref.info_uid_prefix = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* If we have a color scheme file, we switch to it, GIO will merge it in the
|
||||
* remmina.pref file */
|
||||
if (g_file_test(remmina_colors_file, G_FILE_TEST_IS_REGULAR)) {
|
||||
@ -921,6 +991,11 @@ gboolean remmina_pref_save(void)
|
||||
g_key_file_set_string(gkeyfile, "ssh_colors", "color13", remmina_pref.color_pref.color13 ? remmina_pref.color_pref.color13 : "");
|
||||
g_key_file_set_string(gkeyfile, "ssh_colors", "color14", remmina_pref.color_pref.color14 ? remmina_pref.color_pref.color14 : "");
|
||||
g_key_file_set_string(gkeyfile, "ssh_colors", "color15", remmina_pref.color_pref.color15 ? remmina_pref.color_pref.color15 : "");
|
||||
g_key_file_set_boolean(gkeyfile, "remmina_info", "periodic_news_permitted", !remmina_pref.disable_news);
|
||||
g_key_file_set_string(gkeyfile, "remmina_info", "periodic_news_last_checksum", remmina_pref.periodic_news_last_checksum ? remmina_pref.periodic_news_last_checksum: "");
|
||||
g_key_file_set_boolean(gkeyfile, "remmina_info", "periodic_usage_stats_permitted", !remmina_pref.disable_stats);
|
||||
g_key_file_set_string(gkeyfile, "remmina_info", "info_uid_prefix", remmina_pref.info_uid_prefix ? remmina_pref.info_uid_prefix : "");
|
||||
g_key_file_set_boolean(gkeyfile, "remmina_info", "disable_tip", remmina_pref.disable_tip);
|
||||
|
||||
/* Default settings */
|
||||
g_key_file_set_string(gkeyfile, "remmina", "name", "");
|
||||
|
@ -236,7 +236,14 @@ typedef struct _RemminaPref {
|
||||
RemminaColorPref color_pref;
|
||||
|
||||
/* Usage stats */
|
||||
gchar * last_success;
|
||||
gboolean disable_stats;
|
||||
gchar * info_uid_prefix;
|
||||
|
||||
/* Remmina news */
|
||||
gboolean disable_news;
|
||||
gchar * periodic_news_last_checksum;
|
||||
|
||||
gboolean disable_tip;
|
||||
|
||||
} RemminaPref;
|
||||
|
||||
|
@ -247,7 +247,9 @@ void remmina_pref_on_dialog_destroy(GtkWidget *widget, gpointer user_data)
|
||||
remmina_pref.always_show_notes = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remmina_pref_dialog->checkbutton_appearance_show_notes));
|
||||
remmina_pref.hide_connection_toolbar = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remmina_pref_dialog->checkbutton_appearance_hide_toolbar));
|
||||
remmina_pref.hide_searchbar = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remmina_pref_dialog->checkbutton_appearance_hide_searchbar));
|
||||
|
||||
remmina_pref.disable_news = gtk_switch_get_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_news));
|
||||
remmina_pref.disable_stats = gtk_switch_get_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_stats));
|
||||
remmina_pref.disable_tip = gtk_switch_get_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_tip));
|
||||
remmina_pref.default_action = gtk_combo_box_get_active(remmina_pref_dialog->comboboxtext_options_double_click);
|
||||
remmina_pref.default_mode = gtk_combo_box_get_active(remmina_pref_dialog->comboboxtext_appearance_view_mode);
|
||||
remmina_pref.tab_mode = gtk_combo_box_get_active(remmina_pref_dialog->comboboxtext_appearance_tab_interface);
|
||||
@ -498,6 +500,25 @@ static void remmina_pref_dialog_init(void)
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remmina_pref_dialog->checkbutton_appearance_hide_toolbar), remmina_pref.hide_connection_toolbar);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remmina_pref_dialog->checkbutton_appearance_hide_searchbar), remmina_pref.hide_searchbar);
|
||||
|
||||
gtk_switch_set_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_news), remmina_pref.disable_news);
|
||||
#ifdef DISABLE_NEWS
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->switch_disable_news));
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->label_disable_news));
|
||||
#endif
|
||||
|
||||
gtk_switch_set_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_stats), remmina_pref.disable_stats);
|
||||
#ifdef DISABLE_STATS
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->switch_disable_stats));
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->label_disable_stats));
|
||||
gtk_switch_set_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_stats), TRUE);
|
||||
#endif
|
||||
|
||||
#ifdef DISABLE_TIP
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->switch_disable_tip));
|
||||
gtk_widget_hide(GTK_WIDGET(remmina_pref_dialog->label_disable_tip));
|
||||
#endif
|
||||
gtk_switch_set_active(GTK_SWITCH(remmina_pref_dialog->switch_disable_tip), remmina_pref.disable_tip);
|
||||
|
||||
g_snprintf(buf, sizeof(buf), "%i", remmina_pref.sshtunnel_port);
|
||||
gtk_entry_set_text(remmina_pref_dialog->entry_options_ssh_port, buf);
|
||||
g_snprintf(buf, sizeof(buf), "%i", remmina_pref.ssh_tcp_keepidle);
|
||||
@ -728,6 +749,14 @@ GtkWidget *remmina_pref_dialog_new(gint default_tab, GtkWindow *parent)
|
||||
remmina_pref_dialog->checkbutton_appearance_show_notes = GTK_CHECK_BUTTON(GET_OBJECT("checkbutton_appearance_show_notes"));
|
||||
remmina_pref_dialog->checkbutton_appearance_hide_toolbar = GTK_CHECK_BUTTON(GET_OBJECT("checkbutton_appearance_hide_toolbar"));
|
||||
remmina_pref_dialog->checkbutton_appearance_hide_searchbar = GTK_CHECK_BUTTON(GET_OBJECT("checkbutton_appearance_hide_searchbar"));
|
||||
|
||||
remmina_pref_dialog->switch_disable_news = GTK_SWITCH(GET_OBJECT("switch_disable_news"));
|
||||
remmina_pref_dialog->switch_disable_stats = GTK_SWITCH(GET_OBJECT("switch_disable_stats"));
|
||||
remmina_pref_dialog->switch_disable_tip = GTK_SWITCH(GET_OBJECT("switch_disable_tip"));
|
||||
remmina_pref_dialog->label_disable_news = GTK_LABEL(GET_OBJECT("remmina_info_disable_news_label"));
|
||||
remmina_pref_dialog->label_disable_stats = GTK_LABEL(GET_OBJECT("remmina_info_disable_stats_label"));
|
||||
remmina_pref_dialog->label_disable_tip = GTK_LABEL(GET_OBJECT("remmina_info_disable_tip"));
|
||||
|
||||
remmina_pref_dialog->comboboxtext_options_double_click = GTK_COMBO_BOX(GET_OBJECT("comboboxtext_options_double_click"));
|
||||
remmina_pref_dialog->comboboxtext_appearance_view_mode = GTK_COMBO_BOX(GET_OBJECT("comboboxtext_appearance_view_mode"));
|
||||
remmina_pref_dialog->comboboxtext_appearance_tab_interface = GTK_COMBO_BOX(GET_OBJECT("comboboxtext_appearance_tab_interface"));
|
||||
|
@ -65,6 +65,12 @@ typedef struct _RemminaPrefDialog {
|
||||
GtkSwitch * switch_security_lock_view_passwords;
|
||||
GtkSwitch * switch_security_audit;
|
||||
GtkSwitch * switch_security_trust_all;
|
||||
GtkSwitch * switch_disable_news;
|
||||
GtkSwitch * switch_disable_stats;
|
||||
GtkSwitch * switch_disable_tip;
|
||||
GtkLabel * label_disable_news;
|
||||
GtkLabel * label_disable_stats;
|
||||
GtkLabel * label_disable_tip;
|
||||
GtkCheckButton * checkbutton_options_save_settings;
|
||||
GtkCheckButton * checkbutton_appearance_fullscreen_on_auto;
|
||||
GtkCheckButton * checkbutton_appearance_show_tabs;
|
||||
|
@ -44,15 +44,21 @@
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gio/gio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/decoder.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/err.h>
|
||||
#include "remmina_sodium.h"
|
||||
#include "remmina/remmina_trace_calls.h"
|
||||
#include "remmina_utils.h"
|
||||
#include "remmina_log.h"
|
||||
#include "remmina_plugin_manager.h"
|
||||
|
||||
/** Returns @c TRUE if @a ptr is @c NULL or @c *ptr is @c FALSE. */
|
||||
#define EMPTY(ptr) \
|
||||
(!(ptr) || !*(ptr))
|
||||
|
||||
struct utsname u;
|
||||
|
||||
/* Copyright (C) 1998 VMware, Inc. All rights reserved.
|
||||
* Some of the code in this file is taken from the VMware open client.
|
||||
*/
|
||||
@ -120,6 +126,40 @@ static DistroInfo distroArray[] = {
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
const char *remmina_RSA_PubKey_v1 =
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgaHpEQgAzIDWO2If1MVa\n"
|
||||
"Qm+vyl2UNdwxFpMBEhC81rdtTqoJBHsMWINheE91AiupXQib9viLNLyu3GhXVd2G\n"
|
||||
"rGu9QHjw2+UUe35Z8jjUIbSx42PXKxFB49FhuZJ1iBLuP4qLKJoOUKEWrHaP9cYa\n"
|
||||
"w4G5NN7/f+HVdc7zhWbj4ffeBzblHyxoDSXXEuaBD3xc2MKngn8ZmLi6oeRpEk7v\n"
|
||||
"L0TdURLRkBrLdYiMxXF8SJpOyLs4wl6w6X6hxJsN2Mtme8XSX1+jK+2uWZKZ3s5K\n"
|
||||
"tEiHrWUGHbYofv/8cGmgPTCSX+6Ou++lshYjwZYo64of9hK8ttdUnydK5iNFBlWL\n"
|
||||
"C6YdNSyUNGbtR/CoIYL0dOREpflR5w5801xuNN8XifO1dTOe9glejKdUnWF3hbaR\n"
|
||||
"XKCgk9UqhlZC1fP5FNcth62OoZdpvHIfPkuTI6add/+sVHeANbza1MFwgNS5rEh0\n"
|
||||
"U4Gf27Ki0IvWmw8AKHY+0R0e1BCfyClIKjA4pkWSIn9q0YQkHkAze7W5enTA+rND\n"
|
||||
"yYZuJ72rWmS6Yz05J+I9YlqBWOq07g64YSH+RQWSRRIE47lK9+NXgCGYmwpLsEZo\n"
|
||||
"Tu4rXKPJ2yyQGYFcVWtWdXfre2VPhoovuQzytSCd2Q57lFXsyw713z/6OCQz9zGb\n"
|
||||
"eOM74qMLE+jkL1n7snbX5eECAwEAAQ==\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
|
||||
const char *remmina_RSA_PubKey_v2 =
|
||||
"-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgaHpEQgAzIDWO2If1MVa\n"
|
||||
"Qm+vyl2UNdwxFpMBEhC81rdtTqoJBHsMWINheE91AiupXQib9viLNLyu3GhXVd2G\n"
|
||||
"rGu9QHjw2+UUe35Z8jjUIbSx42PXKxFB49FhuZJ1iBLuP4qLKJoOUKEWrHaP9cYa\n"
|
||||
"w4G5NN7/f+HVdc7zhWbj4ffeBzblHyxoDSXXEuaBD3xc2MKngn8ZmLi6oeRpEk7v\n"
|
||||
"L0TdURLRkBrLdYiMxXF8SJpOyLs4wl6w6X6hxJsN2Mtme8XSX1+jK+2uWZKZ3s5K\n"
|
||||
"tEiHrWUGHbYofv/8cGmgPTCSX+6Ou++lshYjwZYo64of9hK8ttdUnydK5iNFBlWL\n"
|
||||
"C6YdNSyUNGbtR/CoIYL0dOREpflR5w5801xuNN8XifO1dTOe9glejKdUnWF3hbaR\n"
|
||||
"XKCgk9UqhlZC1fP5FNcth62OoZdpvHIfPkuTI6add/+sVHeANbza1MFwgNS5rEh0\n"
|
||||
"U4Gf27Ki0IvWmw8AKHY+0R0e1BCfyClIKjA4pkWSIn9q0YQkHkAze7W5enTA+rND\n"
|
||||
"yYZuJ72rWmS6Yz05J+I9YlqBWOq07g64YSH+RQWSRRIE47lK9+NXgCGYmwpLsEZo\n"
|
||||
"Tu4rXKPJ2yyQGYFcVWtWdXfre2VPhoovuQzytSCd2Q57lFXsyw713z/6OCQz9zGb\n"
|
||||
"eOM74qMLE+jkL1n7snbX5eECAwEAAQ==\n"
|
||||
"-----END PUBLIC KEY-----\n";
|
||||
|
||||
|
||||
gint remmina_utils_strpos(const gchar *haystack, const gchar *needle)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
@ -257,26 +297,21 @@ static gchar *remmina_utils_read_distrofile(gchar *filename)
|
||||
GError *err = NULL;
|
||||
|
||||
if (g_stat(filename, &st) == -1) {
|
||||
g_debug("%s: could not stat the file %s\n", __func__, filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_debug("%s: File %s is %lu bytes long\n", __func__, filename, st.st_size);
|
||||
if (st.st_size > 131072)
|
||||
return NULL;
|
||||
|
||||
if (!g_file_get_contents(filename, &distro_desc, &file_sz, &err)) {
|
||||
g_debug("%s: could not get the file content%s: %s\n", __func__, filename, err->message);
|
||||
g_error_free(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (file_sz == 0) {
|
||||
g_debug("%s: Cannot work with empty file.\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_debug("%s: Distro description %s\n", __func__, distro_desc);
|
||||
return distro_desc;
|
||||
}
|
||||
|
||||
@ -286,6 +321,7 @@ static gchar *remmina_utils_read_distrofile(gchar *filename)
|
||||
*/
|
||||
gchar *remmina_utils_get_lang()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *lang = setlocale(LC_ALL, NULL);
|
||||
gchar *ptr;
|
||||
|
||||
@ -299,34 +335,50 @@ gchar *remmina_utils_get_lang()
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OS name as in "uname -s".
|
||||
* @return The OS name or NULL.
|
||||
*/
|
||||
const gchar *remmina_utils_get_kernel_name()
|
||||
gchar *remmina_utils_get_kernel_name()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
return u.sysname;
|
||||
struct utsname u;
|
||||
|
||||
if (uname(&u) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return g_strdup(u.sysname);
|
||||
}
|
||||
|
||||
const gchar *remmina_utils_get_kernel_release()
|
||||
/**
|
||||
* Return the OS version as in "uname -r".
|
||||
* @return The OS release or NULL.
|
||||
*/
|
||||
gchar *remmina_utils_get_kernel_release()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
return u.release;
|
||||
struct utsname u;
|
||||
|
||||
if (uname(&u) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return g_strdup(u.release);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the machine hardware name as in "uname -m".
|
||||
* @return The machine hardware name or NULL.
|
||||
*/
|
||||
const gchar *remmina_utils_get_kernel_arch()
|
||||
gchar *remmina_utils_get_kernel_arch()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
return u.machine;
|
||||
struct utsname u;
|
||||
|
||||
if (uname(&u) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return g_strdup(u.machine);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,15 +402,8 @@ gchar *remmina_utils_get_lsb_description()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *lsb_description = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
if (g_spawn_command_line_sync("/usr/bin/lsb_release -sd", &lsb_description, NULL, NULL, &err)) {
|
||||
if (g_spawn_command_line_sync("/usr/bin/lsb_release -sd", &lsb_description, NULL, NULL, NULL))
|
||||
return lsb_description;
|
||||
} else {
|
||||
g_debug("%s: could not execute lsb_release %s\n", __func__, err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
g_debug("%s: lsb_release %s\n", __func__, lsb_description);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -403,11 +448,9 @@ GHashTable *remmina_utils_get_etc_release()
|
||||
r = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
|
||||
|
||||
for (i = 0; distroArray[i].filename != NULL; i++) {
|
||||
g_debug("%s: File %s\n", __func__, distroArray[i].filename);
|
||||
etc_release = remmina_utils_read_distrofile(distroArray[i].filename);
|
||||
if (etc_release) {
|
||||
if (etc_release[0] != '\0') {
|
||||
g_debug("%s: Distro description %s\n", __func__, etc_release);
|
||||
g_hash_table_insert(r, distroArray[i].filename, etc_release);
|
||||
} else {
|
||||
g_free(etc_release);
|
||||
@ -418,30 +461,160 @@ GHashTable *remmina_utils_get_etc_release()
|
||||
}
|
||||
|
||||
/**
|
||||
* A sample function to show how use the other fOS related functions.
|
||||
* @return a semicolon separated OS data like in "uname -srm".
|
||||
* Print device associated with default route.
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
const gchar *remmina_utils_get_os_info()
|
||||
gchar *remmina_utils_get_dev()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *kernel_string;
|
||||
gchar *dif;
|
||||
gint pos = 0;
|
||||
GString *dev;
|
||||
|
||||
if (uname(&u) == -1)
|
||||
g_print("uname:");
|
||||
|
||||
kernel_string = g_strdup_printf("%s;%s;%s\n",
|
||||
remmina_utils_get_kernel_name(),
|
||||
remmina_utils_get_kernel_release(),
|
||||
remmina_utils_get_kernel_arch());
|
||||
if (!kernel_string || kernel_string[0] == '\0') {
|
||||
if (kernel_string)
|
||||
g_free(kernel_string);
|
||||
kernel_string = g_strdup_printf("%s;%s;%s\n",
|
||||
"UNKNOWN",
|
||||
"UNKNOWN",
|
||||
"UNKNOWN");
|
||||
if (g_spawn_command_line_sync("ip route show default", &dif, NULL, NULL, NULL)) {
|
||||
dev = g_string_new(dif);
|
||||
g_free(dif);
|
||||
pos = remmina_utils_string_find(dev, pos, -1, "dev ");
|
||||
dev = g_string_erase(dev, 0, pos + 4);
|
||||
pos = remmina_utils_string_find(dev, 0, -1, " ");
|
||||
dev = g_string_truncate(dev, pos);
|
||||
return g_string_free(dev, FALSE);
|
||||
}
|
||||
return kernel_string;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print address associated with default route.
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
gchar *remmina_utils_get_logical()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *dev = NULL;
|
||||
gchar *dlog;
|
||||
gint pos = 0;
|
||||
|
||||
dev = remmina_utils_get_dev();
|
||||
GString *lbuf = g_string_new("ip -4 -o addr show ");
|
||||
if ( dev != NULL ) {
|
||||
lbuf = g_string_append(lbuf, dev);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
if (g_spawn_command_line_sync(lbuf->str, &dlog, NULL, NULL, NULL)) {
|
||||
g_string_free(lbuf, TRUE);
|
||||
GString *log = g_string_new(dlog);
|
||||
g_free(dlog);
|
||||
pos = remmina_utils_string_find(log, pos, -1, "inet ");
|
||||
log = g_string_erase(log, 0, pos + 5);
|
||||
pos = remmina_utils_string_find(log, 0, -1, " ");
|
||||
log = g_string_truncate(log, pos);
|
||||
return g_string_free(log, FALSE);
|
||||
}
|
||||
g_string_free(lbuf, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print link associated with default route.
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
gchar *remmina_utils_get_link()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *dev = NULL;
|
||||
gchar *plink;
|
||||
gint pos = 0;
|
||||
|
||||
dev = remmina_utils_get_dev();
|
||||
GString *pbuf = g_string_new("ip -4 -o link show ");
|
||||
if ( dev != NULL ) {
|
||||
pbuf = g_string_append(pbuf, dev);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
if (g_spawn_command_line_sync(pbuf->str, &plink, NULL, NULL, NULL))
|
||||
{
|
||||
g_string_free(pbuf, TRUE);
|
||||
GString *link = g_string_new(plink);
|
||||
g_free(plink);
|
||||
pos = remmina_utils_string_find(link, pos, -1, "link/ether ");
|
||||
link = g_string_erase(link, 0, pos + 11);
|
||||
pos = remmina_utils_string_find(link, 0, -1, " ");
|
||||
link = g_string_truncate(link, pos);
|
||||
return g_string_free(link, FALSE);
|
||||
}
|
||||
g_string_free(pbuf, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print python version.
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
gchar *remmina_utils_get_python()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *version;
|
||||
|
||||
version = (g_spawn_command_line_sync("python -V", &version, NULL, NULL, NULL)) ||
|
||||
(g_spawn_command_line_sync("python3 -V", &version, NULL, NULL, NULL)) ? version : NULL;
|
||||
if (version != NULL)
|
||||
version = remmina_utils_string_strip(version);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print machine age.
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
gchar *remmina_utils_get_mage()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *mage = malloc(21);
|
||||
struct stat sb;
|
||||
|
||||
if (stat("/etc/machine-id", &sb) == 0) {
|
||||
sprintf(mage, "%ld", (long)(time(NULL) - sb.st_mtim.tv_sec));
|
||||
}
|
||||
else {
|
||||
strcpy(mage, "0");
|
||||
}
|
||||
|
||||
return mage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hexadecimal string version of the SHA-256 digest of the
|
||||
* buffer.
|
||||
*
|
||||
* @return a newly allocated string which the caller should free() when
|
||||
* finished.
|
||||
*
|
||||
* If any error occurs, this function returns NULL.
|
||||
*/
|
||||
gchar *remmina_sha256_buffer(const guchar *buffer, gssize length)
|
||||
{
|
||||
GChecksum *sha256 = NULL;
|
||||
gchar *digest_string = NULL;
|
||||
|
||||
sha256 = g_checksum_new(G_CHECKSUM_SHA256);
|
||||
if (sha256 == NULL) {
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
g_checksum_update(sha256, buffer, length);
|
||||
|
||||
digest_string = g_strdup(g_checksum_get_string(sha256));
|
||||
|
||||
DONE:
|
||||
if (sha256) {
|
||||
g_checksum_free(sha256);
|
||||
}
|
||||
|
||||
return digest_string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -496,4 +669,424 @@ DONE:
|
||||
fclose(file);
|
||||
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random sting of chars to be used as part of UID for news or stats
|
||||
* @return a string or NULL. Caller must free it with g_free().
|
||||
*/
|
||||
gchar *remmina_gen_random_uuid()
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
gchar *result;
|
||||
int i;
|
||||
static char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
result = g_malloc0(15);
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
result[i] = alpha[randombytes_uniform(sizeof(alpha) - 1)];
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
result[i + 7] = alpha[randombytes_uniform(sizeof(alpha) - 1)];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses OSSL_DECODER_CTX to convert public key string to usable OpenSSL structs
|
||||
* @return an EVP_PKEY that can be used for public encryption
|
||||
*/
|
||||
EVP_PKEY *remmina_get_pubkey(const char *keytype, const char *public_key_selection) {
|
||||
BIO *pkbio;
|
||||
OSSL_DECODER_CTX *dctx;
|
||||
|
||||
EVP_PKEY *pubkey = NULL;
|
||||
const char *format = "PEM";
|
||||
const char *structure = NULL;
|
||||
dctx = OSSL_DECODER_CTX_new_for_pkey(&pubkey, format, structure,
|
||||
keytype,
|
||||
EVP_PKEY_PUBLIC_KEY,
|
||||
NULL, NULL);
|
||||
|
||||
if (dctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
pkbio = BIO_new_mem_buf(public_key_selection, -1);
|
||||
|
||||
if (OSSL_DECODER_from_bio(dctx, pkbio) == 0) {
|
||||
BIO_free(pkbio);
|
||||
OSSL_DECODER_CTX_free(dctx);
|
||||
return NULL;
|
||||
}
|
||||
BIO_free(pkbio);
|
||||
OSSL_DECODER_CTX_free(dctx);
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls EVP_PKEY_encrypt multiple times to encrypt instr. At the end, base64
|
||||
* encode the resulting buffer.
|
||||
*
|
||||
* @param pubkey an RSA public key
|
||||
* @param instr input string to be encrypted
|
||||
*
|
||||
* @return a buffer ptr. Use g_free() to deallocate it
|
||||
*/
|
||||
gchar *remmina_rsa_encrypt_string(EVP_PKEY *pubkey, const char *instr)
|
||||
{
|
||||
#define RSA_OAEP_PAD_SIZE 42
|
||||
gchar *enc;
|
||||
int remaining;
|
||||
size_t blksz, maxblksz;
|
||||
unsigned char *outptr;
|
||||
|
||||
unsigned char *ebuf = NULL;
|
||||
gsize ebufLen;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
int pkeyLen = EVP_PKEY_size(pubkey);
|
||||
int inLen = strlen(instr);
|
||||
remaining = inLen;
|
||||
|
||||
maxblksz = pkeyLen - RSA_OAEP_PAD_SIZE;
|
||||
ebufLen = (((inLen - 1) / maxblksz) + 1) * pkeyLen;
|
||||
ebuf = g_malloc(ebufLen);
|
||||
outptr = ebuf;
|
||||
|
||||
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pubkey, NULL);
|
||||
if (ctx == NULL) {
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_encrypt_init_ex(ctx, NULL) <= 0) {;
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* encrypt input string block by block */
|
||||
while (remaining > 0) {
|
||||
blksz = remaining > maxblksz ? maxblksz : remaining;
|
||||
size_t out_blksz;
|
||||
|
||||
int calc_size_return_val = EVP_PKEY_encrypt(ctx, NULL, &out_blksz, (const unsigned char *)instr, blksz);
|
||||
if (calc_size_return_val == -2) {
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
else if (calc_size_return_val <= 0) {
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_encrypt(ctx, outptr, &out_blksz, (const unsigned char *)instr, blksz) <= 0) {;
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
g_free(ebuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instr += blksz;
|
||||
remaining -= blksz;
|
||||
outptr += out_blksz;
|
||||
}
|
||||
|
||||
enc = g_base64_encode(ebuf, ebufLen);
|
||||
g_free(ebuf);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return enc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to verify the contents of @a plugin_file with base64 encoded @a signature. This function uses the OpenSSL EVP API
|
||||
* and expects a well-formed EC signature in DER format. The digest function used is SHA256.
|
||||
*
|
||||
* @param signature A base64 encoded signature.
|
||||
* @param plugin_file The file containing the message you wish to verify.
|
||||
* @param message_length The length of the message.
|
||||
* @param signature_length The length of the signature.
|
||||
*
|
||||
* @return TRUE if the signature can be verified or FALSE if it cannot or there is an error.
|
||||
*/
|
||||
gboolean remmina_verify_plugin_signature(const guchar *signature, GFile* plugin_file, size_t message_length,
|
||||
size_t signature_length) {
|
||||
EVP_PKEY *public_key = NULL;
|
||||
|
||||
/* Get public key */
|
||||
public_key = remmina_get_pubkey(RSA_KEYTYPE, remmina_RSA_PubKey_v2);
|
||||
if (public_key == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean verified = remmina_execute_plugin_signature_verification(plugin_file, message_length, signature,
|
||||
signature_length, public_key);
|
||||
|
||||
EVP_PKEY_free(public_key);
|
||||
if (verified) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that performs the actual signature verification (that is, this function makes direct calls to the
|
||||
* OPENSSL EVP API) of @a plugin_file using signature @a sig with @a public_key.
|
||||
*
|
||||
* @param plugin_file The file containing the message you wish to verify.
|
||||
* @param msg_len The length of the message.
|
||||
* @param sig The signature to verify the message with.
|
||||
* @param sig_len The length of the signature.
|
||||
* @param public_key The public key to use in the signature verification.
|
||||
*
|
||||
* @return TRUE if the signature can be verified or FALSE if there is an error or the signature cannot be verified.
|
||||
*/
|
||||
gboolean remmina_execute_plugin_signature_verification(GFile* plugin_file, size_t msg_len, const guchar *sig, size_t sig_len, EVP_PKEY* public_key)
|
||||
{
|
||||
/* Returned to caller */
|
||||
if(!plugin_file || !msg_len || !sig || !sig_len || !public_key) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
EVP_MD_CTX* ctx = NULL;
|
||||
gssize read = 0;
|
||||
gssize total_read = 0;
|
||||
unsigned char buffer[1025];
|
||||
|
||||
|
||||
/* Create context for verification */
|
||||
ctx = EVP_MD_CTX_create();
|
||||
if(ctx == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Returns the message digest (hash function) specified*/
|
||||
const EVP_MD* message_digest = EVP_get_digestbyname(NAME_OF_DIGEST);
|
||||
if(message_digest == NULL) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set up context with the corresponding message digest. 1 is the only code for success */
|
||||
int return_val = EVP_DigestInit_ex(ctx, message_digest, NULL);
|
||||
|
||||
if(return_val != 1) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Add the public key and initialize the context for verification*/
|
||||
|
||||
return_val = EVP_DigestVerifyInit(ctx, NULL, message_digest, NULL, public_key);
|
||||
|
||||
if(return_val != 1) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GFileInputStream *in_stream = g_file_read(plugin_file, NULL, NULL);
|
||||
|
||||
/* Add the msg to the context. The next step will be to actually verify the added message */
|
||||
read = g_input_stream_read(G_INPUT_STREAM(in_stream), buffer, 1024, NULL, NULL);
|
||||
while (read > 0){
|
||||
total_read += read;
|
||||
return_val = EVP_DigestVerifyUpdate(ctx, buffer, read);
|
||||
|
||||
if(return_val != 1) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return FALSE;
|
||||
}
|
||||
read = g_input_stream_read(G_INPUT_STREAM(in_stream), buffer, 1024, NULL, NULL);
|
||||
}
|
||||
msg_len = total_read;
|
||||
|
||||
/* Clear any errors for the call below */
|
||||
ERR_clear_error();
|
||||
|
||||
/* Execute the signature verification. */
|
||||
return_val = EVP_DigestVerifyFinal(ctx, sig, sig_len);
|
||||
|
||||
if(return_val != 1) {
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(ctx != NULL) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Decompresses pointer @source with length @len to file pointed to by @plugin_file. It is the caller's responsibility
|
||||
* to free @source and @plugin_file when no longer in use.
|
||||
*
|
||||
* @return The total number of compressed bytes read or -1 on error.
|
||||
*/
|
||||
int remmina_decompress_from_memory_to_file(guchar *source, int len, GFile* plugin_file) {
|
||||
GError *error = NULL;
|
||||
//Compress with gzip. The level is -1 for default
|
||||
GZlibDecompressor* gzlib_decompressor = g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP);
|
||||
if (gzlib_decompressor == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
GInputStream *base_stream = g_memory_input_stream_new_from_data(source, len, NULL);
|
||||
|
||||
if (base_stream == NULL) {
|
||||
g_object_unref(gzlib_decompressor);
|
||||
return -1;
|
||||
}
|
||||
GInputStream *converted_input_stream = g_converter_input_stream_new(base_stream, G_CONVERTER(gzlib_decompressor));
|
||||
|
||||
if (converted_input_stream == NULL) {
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_decompressor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int total_read = 0;
|
||||
|
||||
GFileOutputStream *f_out_stream = g_file_replace(plugin_file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
|
||||
if (f_out_stream == NULL){
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_decompressor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_output_stream_splice(f_out_stream, converted_input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, NULL);
|
||||
GFileInfo* info = g_file_query_info(plugin_file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
|
||||
total_read = g_file_info_get_size(info);
|
||||
|
||||
|
||||
//Freeing stuff
|
||||
g_object_unref(converted_input_stream);
|
||||
g_object_unref(f_out_stream);
|
||||
g_object_unref(gzlib_decompressor);
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(info);
|
||||
|
||||
|
||||
return total_read;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses file @source to file @dest. Creates enough space to compress MAX_COMPRESSED_FILE_SIZE. It is the caller's
|
||||
* responsibility to close @source and @dest.
|
||||
*
|
||||
* @return Total number of bytes read from source or -1 on error.
|
||||
*/
|
||||
int remmina_compress_from_file_to_file(GFile *source, GFile *dest)
|
||||
{
|
||||
TRACE_CALL(__func__);
|
||||
GError *error = NULL;
|
||||
|
||||
// Compress with gzip. The level is -1 for default
|
||||
GZlibCompressor *gzlib_compressor = g_zlib_compressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1);
|
||||
|
||||
if (gzlib_compressor == NULL) {
|
||||
REMMINA_DEBUG("Compressor instantiation failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GFileInputStream *base_stream = g_file_read(source, NULL, &error);
|
||||
|
||||
if (base_stream == NULL) {
|
||||
REMMINA_DEBUG("Base stream is null, error: %s", error->message);
|
||||
g_free(error);
|
||||
g_object_unref(gzlib_compressor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
GInputStream *converted_input_stream = g_converter_input_stream_new((GInputStream *) base_stream, G_CONVERTER(gzlib_compressor));
|
||||
|
||||
if (converted_input_stream == NULL) {
|
||||
REMMINA_DEBUG("Failed to allocate converted input stream");
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int total_read = 0;
|
||||
|
||||
guint8 *converted, *current_position;
|
||||
converted = g_malloc(sizeof(guint8) * MAX_COMPRESSED_FILE_SIZE); // 100kb file max
|
||||
|
||||
current_position = converted; // Set traveling pointer
|
||||
|
||||
gsize bytes_read = 0;
|
||||
while (total_read < MAX_COMPRESSED_FILE_SIZE) {
|
||||
bytes_read = g_input_stream_read(G_INPUT_STREAM(converted_input_stream), current_position, 1, NULL, &error);
|
||||
if (error != NULL) {
|
||||
REMMINA_DEBUG("Error reading input stream: %s", error->message);
|
||||
g_free(error);
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
g_object_unref(converted_input_stream);
|
||||
g_free(converted);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bytes_read == 0) {
|
||||
break; // Reached EOF, break read
|
||||
}
|
||||
current_position += bytes_read;
|
||||
|
||||
total_read += bytes_read;
|
||||
}
|
||||
|
||||
if (total_read >= MAX_COMPRESSED_FILE_SIZE) {
|
||||
REMMINA_DEBUG("Detected heap overflow in allocating compressed file");
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
g_object_unref(converted_input_stream);
|
||||
g_free(converted);
|
||||
}
|
||||
|
||||
gsize *bytes_written = g_malloc(sizeof(gsize) * total_read);
|
||||
|
||||
GFileOutputStream *g_output_stream = g_file_append_to(dest, G_FILE_CREATE_NONE, NULL, &error);
|
||||
|
||||
if (g_output_stream == NULL) {
|
||||
REMMINA_DEBUG("Error appending to file: %s", error->message);
|
||||
g_error_free(error);
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
g_object_unref(converted_input_stream);
|
||||
g_free(bytes_written);
|
||||
g_free(converted);
|
||||
return -1;
|
||||
}
|
||||
g_output_stream_write_all(g_output_stream, converted, total_read, bytes_written, NULL, &error);
|
||||
if (error != NULL) {
|
||||
REMMINA_DEBUG("Error writing all bytes to file: %s", error->message);
|
||||
g_error_free(error);
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
g_object_unref(converted_input_stream);
|
||||
g_object_unref(g_output_stream);
|
||||
g_free(bytes_written);
|
||||
g_free(converted);
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_object_unref(base_stream);
|
||||
g_object_unref(gzlib_compressor);
|
||||
g_object_unref(converted_input_stream);
|
||||
g_object_unref(g_output_stream);
|
||||
g_free(bytes_written);
|
||||
g_free(converted);
|
||||
return total_read;
|
||||
}
|
||||
|
@ -40,22 +40,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/decoder.h>
|
||||
|
||||
#define MAX_COMPRESSED_FILE_SIZE 100000 //100kb file size max
|
||||
#define NAME_OF_DIGEST "SHA256"
|
||||
#define RSA_KEYTYPE "RSA"
|
||||
|
||||
extern const char *remmina_RSA_PubKey_v1;
|
||||
extern const char *remmina_RSA_PubKey_v2;
|
||||
extern const char *remmina_EC_PubKey;
|
||||
G_BEGIN_DECLS
|
||||
gint remmina_utils_string_find(GString *haystack, gint start, gint end, const gchar *needle);
|
||||
gint remmina_utils_string_replace(GString *str, gint pos, gint len, const gchar *replace);
|
||||
|
||||
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace);
|
||||
gchar *remmina_utils_string_strip(const gchar *s);
|
||||
|
||||
gchar *remmina_utils_get_lang();
|
||||
const gchar *remmina_utils_get_kernel_name();
|
||||
const gchar *remmina_utils_get_kernel_release();
|
||||
const gchar *remmina_utils_get_kernel_arch();
|
||||
gchar *remmina_utils_get_kernel_name();
|
||||
gchar *remmina_utils_get_kernel_release();
|
||||
gchar *remmina_utils_get_kernel_arch();
|
||||
gchar *remmina_utils_get_lsb_id();
|
||||
gchar *remmina_utils_get_lsb_description();
|
||||
gchar *remmina_utils_get_lsb_release();
|
||||
gchar *remmina_utils_get_lsb_codename();
|
||||
GHashTable *remmina_utils_get_etc_release();
|
||||
const gchar *remmina_utils_get_os_info();
|
||||
gchar *remmina_utils_get_dev();
|
||||
gchar *remmina_utils_get_logical();
|
||||
gchar *remmina_utils_get_link();
|
||||
gchar *remmina_utils_get_python();
|
||||
gchar *remmina_utils_get_mage();
|
||||
gchar *remmina_sha256_buffer(const guchar *buffer, gssize length);
|
||||
gchar *remmina_sha1_file(const gchar *filename);
|
||||
gchar *remmina_gen_random_uuid();
|
||||
EVP_PKEY *remmina_get_pubkey(const char *keytype, const char *public_key_selection);
|
||||
gchar *remmina_rsa_encrypt_string(EVP_PKEY *pubkey, const char *instr);
|
||||
int remmina_decompress_from_memory_to_file(guchar *source, int len, GFile* plugin_file);
|
||||
int remmina_compress_from_file_to_file(GFile *source, GFile *dest);
|
||||
gboolean remmina_verify_plugin_signature(const guchar *signature, GFile* plugin_file, size_t message_length, size_t signature_length);
|
||||
gboolean remmina_execute_plugin_signature_verification(GFile* plugin_file, size_t msg_len, const guchar *sig, size_t sig_len, EVP_PKEY* public_key);
|
||||
G_END_DECLS
|
||||
|
Loading…
x
Reference in New Issue
Block a user