summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2024-01-03 15:59:40 +0100
committerKarol Lewandowski <k.lewandowsk@samsung.com>2024-01-03 15:59:40 +0100
commit1b11ff8d06ac53ccc79aa7595e2cd7b7972507f3 (patch)
tree77b766225dc9a54292b9a2ceece11041f64cc5d9 /gio
parentcbef7bb66192abf9d98aba079fe5236a8fb54e6f (diff)
downloadglib-1b11ff8d06ac53ccc79aa7595e2cd7b7972507f3.tar.gz
glib-1b11ff8d06ac53ccc79aa7595e2cd7b7972507f3.tar.bz2
glib-1b11ff8d06ac53ccc79aa7595e2cd7b7972507f3.zip
Imported Upstream version 2.72.3
Diffstat (limited to 'gio')
-rw-r--r--gio/fam/meson.build6
-rw-r--r--gio/gappinfo.c62
-rw-r--r--gio/gappinfo.h4
-rw-r--r--gio/gapplicationcommandline.c8
-rw-r--r--gio/gcontenttype-win32.c44
-rw-r--r--gio/gcontenttype.c53
-rw-r--r--gio/gcredentials.c45
-rw-r--r--gio/gcredentialsprivate.h12
-rw-r--r--gio/gdbus-2.0/codegen/.flake84
-rw-r--r--gio/gdbus-2.0/codegen/codegen.py2
-rw-r--r--gio/gdbus-2.0/codegen/codegen_docbook.py18
-rw-r--r--gio/gdbus-2.0/codegen/codegen_main.py17
-rw-r--r--gio/gdbus-2.0/codegen/codegen_rst.py332
-rw-r--r--gio/gdbus-2.0/codegen/meson.build1
-rw-r--r--gio/gdbus-2.0/codegen/parser.py13
-rw-r--r--gio/gdbus-tool.c15
-rw-r--r--gio/gdbusaddress.c16
-rw-r--r--gio/gdbusauthmechanismexternal.c27
-rw-r--r--gio/gdbusauthmechanismsha1.c46
-rw-r--r--gio/gdbusconnection.c34
-rw-r--r--gio/gdbuserror.c2
-rw-r--r--gio/gdbusmessage.c12
-rw-r--r--gio/gdbusmethodinvocation.c53
-rw-r--r--gio/gdbusobjectmanagerclient.c10
-rw-r--r--gio/gdbusobjectmanagerserver.c34
-rw-r--r--gio/gdbusprivate.c199
-rw-r--r--gio/gdbusproxy.c18
-rw-r--r--gio/gdbusutils.c30
-rw-r--r--gio/gdebugcontroller.c119
-rw-r--r--gio/gdebugcontroller.h79
-rw-r--r--gio/gdebugcontrollerdbus.c709
-rw-r--r--gio/gdebugcontrollerdbus.h69
-rw-r--r--gio/gdesktopappinfo.c204
-rw-r--r--gio/gdrive.c2
-rw-r--r--gio/gdtlsconnection.c33
-rw-r--r--gio/gfile.c720
-rw-r--r--gio/gfile.h32
-rw-r--r--gio/gfileenumerator.c22
-rw-r--r--gio/gfilemonitor.c2
-rw-r--r--gio/ginputstream.c4
-rw-r--r--gio/gio-querymodules.c2
-rw-r--r--gio/gio-tool-set.c10
-rw-r--r--gio/gio.h2
-rw-r--r--gio/gioenums.h39
-rw-r--r--gio/giomodule.c51
-rw-r--r--gio/giomodule.h4
-rw-r--r--gio/giowin32-afunix.h40
-rw-r--r--gio/glib-compile-resources.c145
-rw-r--r--gio/glib-compile-schemas.c20
-rw-r--r--gio/glistmodel.c33
-rw-r--r--gio/glocalfileinfo.h12
-rw-r--r--gio/glocalfilemonitor.c35
-rw-r--r--gio/glocalfileoutputstream.c14
-rw-r--r--gio/gmemorymonitorwin32.c261
-rw-r--r--gio/gmemoryoutputstream.c12
-rw-r--r--gio/gnetworkservice.c18
-rw-r--r--gio/gopenuriportal.c4
-rw-r--r--gio/goutputstream.c2
-rw-r--r--gio/gpowerprofilemonitor.c6
-rw-r--r--gio/gproxyaddressenumerator.c40
-rw-r--r--gio/gregistrysettingsbackend.c10
-rw-r--r--gio/gresolver.c65
-rw-r--r--gio/gresource-tool.c6
-rw-r--r--gio/gresource.c4
-rw-r--r--gio/gresourcefile.c4
-rw-r--r--gio/gsettings-tool.c16
-rw-r--r--gio/gsettings.c36
-rw-r--r--gio/gsettings.h2
-rw-r--r--gio/gsettingsbackend.c9
-rw-r--r--gio/gsettingsschema.c42
-rw-r--r--gio/gsimpleasyncresult.c6
-rw-r--r--gio/gsimpleproxyresolver.c4
-rw-r--r--gio/gsocket.c51
-rw-r--r--gio/gsocketaddress.c6
-rw-r--r--gio/gsocketclient.c69
-rw-r--r--gio/gsocketclient.h4
-rw-r--r--gio/gsocketconnection.c2
-rw-r--r--gio/gsubprocess.c7
-rw-r--r--gio/gsubprocesslauncher-private.h1
-rw-r--r--gio/gtask.c38
-rw-r--r--gio/gthreadedresolver.c218
-rw-r--r--gio/gthreadedresolver.h13
-rw-r--r--gio/gtlscertificate.c218
-rw-r--r--gio/gtlscertificate.h10
-rw-r--r--gio/gtlsclientconnection.c29
-rw-r--r--gio/gtlsclientconnection.h4
-rw-r--r--gio/gtlsconnection.c35
-rw-r--r--gio/gtlsdatabase.c32
-rw-r--r--gio/gtrashportal.c8
-rw-r--r--gio/gunixconnection.c23
-rw-r--r--gio/gunixcredentialsmessage.c10
-rw-r--r--gio/gunixmounts.c34
-rw-r--r--gio/gunixsocketaddress.c13
-rw-r--r--gio/gvolume.c6
-rw-r--r--gio/gwin32appinfo.c31
-rwxr-xr-xgio/gwin32packageparser.c4
-rw-r--r--gio/gwin32sid.c234
-rw-r--r--gio/gwin32sid.h40
-rw-r--r--gio/meson.build59
-rw-r--r--gio/tests/actions.c123
-rw-r--r--gio/tests/appmonitor.c5
-rw-r--r--gio/tests/cert-tests/key-cert-password-123.p12bin0 -> 1717 bytes
-rw-r--r--gio/tests/codegen.py96
-rw-r--r--gio/tests/contenttype.c80
-rw-r--r--gio/tests/converter-stream.c100
-rw-r--r--gio/tests/credentials.c24
-rw-r--r--gio/tests/cxx.cpp26
-rw-r--r--gio/tests/debugcontroller.c396
-rw-r--r--gio/tests/desktop-app-info.c41
-rw-r--r--gio/tests/file.c106
-rw-r--r--gio/tests/g-file-info-filesystem-readonly.c29
-rw-r--r--gio/tests/g-file-info.c4
-rw-r--r--gio/tests/gdbus-auth.c4
-rw-r--r--gio/tests/gdbus-connection-flush.c37
-rw-r--r--gio/tests/gdbus-connection.c2
-rw-r--r--gio/tests/gdbus-export.c52
-rw-r--r--gio/tests/gdbus-method-invocation.c406
-rw-r--r--gio/tests/gdbus-non-socket.c2
-rw-r--r--gio/tests/gdbus-object-manager-example/meson.build21
-rw-r--r--gio/tests/gdbus-proxy.c91
-rw-r--r--gio/tests/gdbus-test-codegen.c23
-rw-r--r--gio/tests/gdbus-threading.c17
-rw-r--r--gio/tests/gio-du.c1
-rw-r--r--gio/tests/gsettings.c30
-rw-r--r--gio/tests/gsubprocess-testprog.c53
-rw-r--r--gio/tests/gsubprocess.c259
-rw-r--r--gio/tests/memory-monitor.c22
-rw-r--r--gio/tests/memory-output-stream.c20
-rw-r--r--gio/tests/meson.build87
-rw-r--r--gio/tests/mock-resolver.c113
-rw-r--r--gio/tests/modules/symbol-visibility.h14
-rw-r--r--gio/tests/network-address.c40
-rw-r--r--gio/tests/proxy-test.c154
-rw-r--r--gio/tests/resolver-parsing.c879
-rw-r--r--gio/tests/resolver.c54
-rw-r--r--gio/tests/resources.c128
-rw-r--r--gio/tests/socket.c198
-rw-r--r--gio/tests/testfilemonitor.c81
-rw-r--r--gio/tests/tls-certificate.c45
-rw-r--r--gio/win32/gwin32fsmonitorutils.c4
-rw-r--r--gio/win32/gwinhttpfile.c2
-rw-r--r--gio/win32/gwinhttpvfs.c4
-rw-r--r--gio/xdgmime/meson.build3
-rw-r--r--gio/xdgmime/xdgmime.c54
-rw-r--r--gio/xdgmime/xdgmime.h18
-rw-r--r--gio/xdgmime/xdgmimealias.c12
-rw-r--r--gio/xdgmime/xdgmimealias.h7
-rw-r--r--gio/xdgmime/xdgmimecache.c137
-rw-r--r--gio/xdgmime/xdgmimecache.h10
-rw-r--r--gio/xdgmime/xdgmimeglob.c17
-rw-r--r--gio/xdgmime/xdgmimeglob.h6
-rw-r--r--gio/xdgmime/xdgmimeicon.c11
-rw-r--r--gio/xdgmime/xdgmimeicon.h6
-rw-r--r--gio/xdgmime/xdgmimeint.c10
-rw-r--r--gio/xdgmime/xdgmimeint.h4
-rw-r--r--gio/xdgmime/xdgmimemagic.c20
-rw-r--r--gio/xdgmime/xdgmimemagic.h4
-rw-r--r--gio/xdgmime/xdgmimeparent.c11
-rw-r--r--gio/xdgmime/xdgmimeparent.h6
159 files changed, 7943 insertions, 1531 deletions
diff --git a/gio/fam/meson.build b/gio/fam/meson.build
index 8019fe5c4..e3f57037c 100644
--- a/gio/fam/meson.build
+++ b/gio/fam/meson.build
@@ -34,3 +34,9 @@ module = shared_module('giofam', 'gfamfilemonitor.c',
if not meson.is_cross_build()
meson.add_install_script('../gio-querymodules-wrapper.py', gio_querymodules.full_path(), glib_giomodulesdir)
endif
+
+if meson.version().version_compare('>=0.58')
+ env = environment()
+ env.prepend('GIO_EXTRA_MODULES', meson.current_build_dir())
+ meson.add_devenv(env)
+endif \ No newline at end of file
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index eff18da83..3f0332825 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -326,7 +326,12 @@ g_app_info_set_as_default_for_type (GAppInfo *appinfo,
iface = G_APP_INFO_GET_IFACE (appinfo);
- return (* iface->set_as_default_for_type) (appinfo, content_type, error);
+ if (iface->set_as_default_for_type)
+ return (* iface->set_as_default_for_type) (appinfo, content_type, error);
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Setting default applications not supported yet"));
+ return FALSE;
}
/**
@@ -354,7 +359,12 @@ g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
iface = G_APP_INFO_GET_IFACE (appinfo);
- return (* iface->set_as_last_used_for_type) (appinfo, content_type, error);
+ if (iface->set_as_last_used_for_type)
+ return (* iface->set_as_last_used_for_type) (appinfo, content_type, error);
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Setting application as last used for type not supported yet"));
+ return FALSE;
}
/**
@@ -1103,6 +1113,7 @@ g_app_info_delete (GAppInfo *appinfo)
enum {
LAUNCH_FAILED,
+ LAUNCH_STARTED,
LAUNCHED,
LAST_SIGNAL
};
@@ -1147,7 +1158,7 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
* @context: the object emitting the signal
* @startup_notify_id: the startup notification id for the failed launch
*
- * The ::launch-failed signal is emitted when a #GAppInfo launch
+ * The #GAppLaunchContext::launch-failed signal is emitted when a #GAppInfo launch
* fails. The startup notification id is provided, so that the launcher
* can cancel the startup notification.
*
@@ -1161,16 +1172,55 @@ g_app_launch_context_class_init (GAppLaunchContextClass *klass)
G_TYPE_NONE, 1, G_TYPE_STRING);
/**
+ * GAppLaunchContext::launch-started:
+ * @context: the object emitting the signal
+ * @info: the #GAppInfo that is about to be launched
+ * @platform_data: (nullable): additional platform-specific data for this launch
+ *
+ * The #GAppLaunchContext::launch-started signal is emitted when a #GAppInfo is
+ * about to be launched. If non-null the @platform_data is an
+ * GVariant dictionary mapping strings to variants (ie `a{sv}`), which
+ * contains additional, platform-specific data about this launch. On
+ * UNIX, at least the `startup-notification-id` keys will be
+ * present.
+ *
+ * The value of the `startup-notification-id` key (type `s`) is a startup
+ * notification ID corresponding to the format from the [startup-notification
+ * specification](https://specifications.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt).
+ * It allows tracking the progress of the launchee through startup.
+ *
+ * It is guaranteed that this signal is followed by either a #GAppLaunchContext::launched or
+ * #GAppLaunchContext::launch-failed signal.
+ *
+ * Since: 2.72
+ */
+ signals[LAUNCH_STARTED] = g_signal_new (I_("launch-started"),
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GAppLaunchContextClass, launch_started),
+ NULL, NULL,
+ _g_cclosure_marshal_VOID__OBJECT_VARIANT,
+ G_TYPE_NONE, 2,
+ G_TYPE_APP_INFO, G_TYPE_VARIANT);
+ g_signal_set_va_marshaller (signals[LAUNCH_STARTED],
+ G_TYPE_FROM_CLASS (klass),
+ _g_cclosure_marshal_VOID__OBJECT_VARIANTv);
+
+ /**
* GAppLaunchContext::launched:
* @context: the object emitting the signal
* @info: the #GAppInfo that was just launched
* @platform_data: additional platform-specific data for this launch
*
- * The ::launched signal is emitted when a #GAppInfo is successfully
+ * The #GAppLaunchContext::launched signal is emitted when a #GAppInfo is successfully
* launched. The @platform_data is an GVariant dictionary mapping
- * strings to variants (ie a{sv}), which contains additional,
+ * strings to variants (ie `a{sv}`), which contains additional,
* platform-specific data about this launch. On UNIX, at least the
- * "pid" and "startup-notification-id" keys will be present.
+ * `pid` and `startup-notification-id` keys will be present.
+ *
+ * Since 2.72 the `pid` may be 0 if the process id wasn't known (for
+ * example if the process was launched via D-Bus). The `pid` may not be
+ * set at all in subsequent releases.
*
* Since: 2.36
*/
diff --git a/gio/gappinfo.h b/gio/gappinfo.h
index d26d048a5..ad3068e31 100644
--- a/gio/gappinfo.h
+++ b/gio/gappinfo.h
@@ -293,12 +293,14 @@ struct _GAppLaunchContextClass
void (* launched) (GAppLaunchContext *context,
GAppInfo *info,
GVariant *platform_data);
+ void (* launch_started) (GAppLaunchContext *context,
+ GAppInfo *info,
+ GVariant *platform_data);
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
- void (*_g_reserved4) (void);
};
GLIB_AVAILABLE_IN_ALL
diff --git a/gio/gapplicationcommandline.c b/gio/gapplicationcommandline.c
index 741aa97f1..d7be108bf 100644
--- a/gio/gapplicationcommandline.c
+++ b/gio/gapplicationcommandline.c
@@ -106,7 +106,7 @@
* The complete example can be found here:
* [gapplication-example-cmdline.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline.c)
*
- * In more complicated cases, the handling of the comandline can be
+ * In more complicated cases, the handling of the commandline can be
* split between the launcher and the primary instance.
* |[<!-- language="C" -->
* static gboolean
@@ -119,6 +119,12 @@
*
* argv = *arguments;
*
+ * if (argv[0] == NULL)
+ * {
+ * *exit_status = 0;
+ * return FALSE;
+ * }
+ *
* i = 1;
* while (argv[i])
* {
diff --git a/gio/gcontenttype-win32.c b/gio/gcontenttype-win32.c
index 2f07fa36e..9b6f69ece 100644
--- a/gio/gcontenttype-win32.c
+++ b/gio/gcontenttype-win32.c
@@ -59,7 +59,7 @@ get_registry_classes_key (const char *subdir,
if (key_type == REG_EXPAND_SZ)
{
wchar_t dummy[1];
- int len = ExpandEnvironmentStringsW (wc_temp, dummy, 1);
+ DWORD len = ExpandEnvironmentStringsW (wc_temp, dummy, 1);
if (len > 0)
{
wchar_t *wc_temp_expanded = g_new (wchar_t, len);
@@ -128,7 +128,8 @@ g_content_type_is_a (const gchar *type,
const gchar *supertype)
{
gboolean res;
- char *value_utf8;
+ char *perceived_type;
+ char *perceived_supertype;
g_return_val_if_fail (type != NULL, FALSE);
g_return_val_if_fail (supertype != NULL, FALSE);
@@ -136,12 +137,15 @@ g_content_type_is_a (const gchar *type,
if (g_content_type_equals (type, supertype))
return TRUE;
- res = FALSE;
- value_utf8 = get_registry_classes_key (type, L"PerceivedType");
- if (value_utf8 && strcmp (value_utf8, supertype) == 0)
- res = TRUE;
- g_free (value_utf8);
-
+ perceived_type = get_registry_classes_key (type, L"PerceivedType");
+ perceived_supertype = get_registry_classes_key (supertype, L"PerceivedType");
+
+ res = perceived_type && perceived_supertype &&
+ strcmp (perceived_type, perceived_supertype) == 0;
+
+ g_free (perceived_type);
+ g_free (perceived_supertype);
+
return res;
}
@@ -342,7 +346,8 @@ g_content_type_from_mime_type (const gchar *mime_type)
content_type = get_registry_classes_key (key, L"Extension");
g_free (key);
- return content_type;
+
+ return content_type ? g_steal_pointer (&content_type) : g_strdup ("*");
}
gchar *
@@ -354,6 +359,7 @@ g_content_type_guess (const gchar *filename,
char *basename;
char *type;
char *dot;
+ size_t i;
type = NULL;
@@ -366,11 +372,21 @@ g_content_type_guess (const gchar *filename,
if (filename)
{
- basename = g_path_get_basename (filename);
- dot = strrchr (basename, '.');
- if (dot)
- type = g_strdup (dot);
- g_free (basename);
+ i = strlen (filename);
+ if (i > 0 && filename[i - 1] == G_DIR_SEPARATOR)
+ {
+ type = g_strdup ("inode/directory");
+ if (result_uncertain)
+ *result_uncertain = TRUE;
+ }
+ else
+ {
+ basename = g_path_get_basename (filename);
+ dot = strrchr (basename, '.');
+ if (dot)
+ type = g_strdup (dot);
+ g_free (basename);
+ }
}
if (type)
diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c
index 83075fcb6..3c9522bc6 100644
--- a/gio/gcontenttype.c
+++ b/gio/gcontenttype.c
@@ -32,6 +32,7 @@
#include "gfileenumerator.h"
#include "gfileinfo.h"
#include "glibintl.h"
+#include "glib-private.h"
/**
@@ -57,7 +58,12 @@
static void tree_magic_schedule_reload (void);
-/* We lock this mutex whenever we modify global state in this module. */
+/* We lock this mutex whenever we modify global state in this module.
+ * Taking and releasing this lock should always be associated with a pair of
+ * g_begin_ignore_leaks()/g_end_ignore_leaks() calls, as any call into xdgmime
+ * could trigger xdg_mime_init(), which makes a number of one-time allocations
+ * which GLib can never free as it doesn’t know when is suitable to call
+ * xdg_mime_shutdown(). */
G_LOCK_DEFINE_STATIC (gio_xdgmime);
gsize
@@ -66,7 +72,9 @@ _g_unix_content_type_get_sniff_len (void)
gsize size;
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
size = xdg_mime_get_max_buffer_extents ();
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return size;
@@ -78,7 +86,9 @@ _g_unix_content_type_unalias (const gchar *type)
gchar *res;
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
res = g_strdup (xdg_mime_unalias_mime_type (type));
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return res;
@@ -95,6 +105,7 @@ _g_unix_content_type_get_parents (const gchar *type)
array = g_ptr_array_new ();
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
umime = xdg_mime_unalias_mime_type (type);
@@ -106,6 +117,7 @@ _g_unix_content_type_get_parents (const gchar *type)
free (parents);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
g_ptr_array_add (array, NULL);
@@ -233,7 +245,9 @@ g_content_type_equals (const gchar *type1,
g_return_val_if_fail (type2 != NULL, FALSE);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
res = xdg_mime_mime_type_equal (type1, type2);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return res;
@@ -259,7 +273,9 @@ g_content_type_is_a (const gchar *type,
g_return_val_if_fail (supertype != NULL, FALSE);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
res = xdg_mime_mime_type_subclass (type, supertype);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return res;
@@ -472,7 +488,9 @@ g_content_type_get_description (const gchar *type)
g_return_val_if_fail (type != NULL, NULL);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
type = xdg_mime_unalias_mime_type (type);
+ g_end_ignore_leaks ();
if (type_comment_cache == NULL)
type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@@ -528,7 +546,9 @@ g_content_type_get_icon_internal (const gchar *type,
g_return_val_if_fail (type != NULL, NULL);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
xdg_icon = xdg_mime_get_icon (type);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
if (xdg_icon)
@@ -619,7 +639,9 @@ g_content_type_get_generic_icon_name (const gchar *type)
g_return_val_if_fail (type != NULL, NULL);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
xdg_icon_name = xdg_mime_get_generic_icon (type);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
if (!xdg_icon_name)
@@ -703,8 +725,10 @@ g_content_type_from_mime_type (const gchar *mime_type)
g_return_val_if_fail (mime_type != NULL, NULL);
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
/* mime type and content type are same on unixes */
umime = g_strdup (xdg_mime_unalias_mime_type (mime_type));
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return umime;
@@ -712,7 +736,7 @@ g_content_type_from_mime_type (const gchar *mime_type)
/**
* g_content_type_guess:
- * @filename: (nullable): a string, or %NULL
+ * @filename: (nullable) (type filename): a path, or %NULL
* @data: (nullable) (array length=data_size): a stream of data, or %NULL
* @data_size: the size of @data
* @result_uncertain: (out) (optional): return location for the certainty
@@ -751,11 +775,12 @@ g_content_type_guess (const gchar *filename,
g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN));
G_LOCK (gio_xdgmime);
+ g_begin_ignore_leaks ();
if (filename)
{
i = strlen (filename);
- if (filename[i - 1] == '/')
+ if (i > 0 && filename[i - 1] == '/')
{
name_mimetypes[0] = "inode/directory";
name_mimetypes[1] = NULL;
@@ -775,6 +800,7 @@ g_content_type_guess (const gchar *filename,
if (n_name_mimetypes == 1)
{
gchar *s = g_strdup (name_mimetypes[0]);
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return s;
}
@@ -843,6 +869,7 @@ g_content_type_guess (const gchar *filename,
}
}
+ g_end_ignore_leaks ();
G_UNLOCK (gio_xdgmime);
return mimetype;
@@ -997,6 +1024,8 @@ parse_header (gchar *line)
line[len - 1] = 0;
s = strchr (line, ':');
+ if (s == NULL)
+ return NULL;
match = g_slice_new0 (TreeMatch);
match->priority = atoi (line + 1);
@@ -1025,9 +1054,13 @@ parse_match_line (gchar *line,
{
*depth = atoi (line);
s = strchr (line, '>');
+ if (s == NULL)
+ goto handle_error;
}
s += 2;
p = strchr (s, '"');
+ if (p == NULL)
+ goto handle_error;
*p = 0;
matchlet->path = g_strdup (s);
@@ -1058,6 +1091,10 @@ parse_match_line (gchar *line,
g_strfreev (parts);
return matchlet;
+
+handle_error:
+ g_slice_free (TreeMatchlet, matchlet);
+ return NULL;
}
static gint
@@ -1119,7 +1156,7 @@ read_tree_magic_from_directory (const gchar *prefix)
gchar *text;
gsize len;
gchar **lines;
- gint i;
+ gsize i;
TreeMatch *match;
TreeMatchlet *matchlet;
gint depth;
@@ -1134,14 +1171,18 @@ read_tree_magic_from_directory (const gchar *prefix)
match = NULL;
for (i = 0; lines[i] && lines[i][0]; i++)
{
- if (lines[i][0] == '[')
+ if (lines[i][0] == '[' && (match = parse_header (lines[i])) != NULL)
{
- match = parse_header (lines[i]);
insert_match (match);
}
else if (match != NULL)
{
matchlet = parse_match_line (lines[i], &depth);
+ if (matchlet == NULL)
+ {
+ g_warning ("%s: body corrupt; skipping", filename);
+ break;
+ }
insert_matchlet (match, matchlet, depth);
}
else
diff --git a/gio/gcredentials.c b/gio/gcredentials.c
index ebbc2cc5b..17378e881 100644
--- a/gio/gcredentials.c
+++ b/gio/gcredentials.c
@@ -72,6 +72,9 @@
* On Solaris (including OpenSolaris and its derivatives), the native
* credential type is a `ucred_t`. This corresponds to
* %G_CREDENTIALS_TYPE_SOLARIS_UCRED.
+ *
+ * Since GLib 2.72, on Windows, the native credentials may contain the PID of a
+ * process. This corresponds to %G_CREDENTIALS_TYPE_WIN32_PID.
*/
/**
@@ -91,6 +94,7 @@ struct _GCredentials
struct ucred native;
#elif G_CREDENTIALS_USE_APPLE_XUCRED
struct xucred native;
+ pid_t pid;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
struct cmsgcred native;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
@@ -99,6 +103,8 @@ struct _GCredentials
struct sockpeercred native;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
ucred_t *native;
+#elif G_CREDENTIALS_USE_WIN32_PID
+ DWORD native;
#else
#ifdef __GNUC__
#pragma GCC diagnostic push
@@ -170,6 +176,8 @@ g_credentials_init (GCredentials *credentials)
* For now we fill it with -1 (meaning "no data"). */
for (i = 1; i < NGROUPS; i++)
credentials->native.cr_groups[i] = -1;
+
+ credentials->pid = -1;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
memset (&credentials->native, 0, sizeof (struct cmsgcred));
credentials->native.cmcred_pid = getpid ();
@@ -185,6 +193,8 @@ g_credentials_init (GCredentials *credentials)
credentials->native.gid = getegid ();
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
credentials->native = ucred_get (P_MYID);
+#elif G_CREDENTIALS_USE_WIN32_PID
+ credentials->native = GetCurrentProcessId ();
#endif
}
@@ -290,6 +300,8 @@ g_credentials_to_string (GCredentials *credentials)
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
}
+#elif G_CREDENTIALS_USE_WIN32_PID
+ g_string_append_printf (ret, "win32-pid:pid=%lu", credentials->native);
#else
g_string_append (ret, "unknown");
#endif
@@ -569,8 +581,7 @@ g_credentials_get_unix_user (GCredentials *credentials,
*
* This operation can fail if #GCredentials is not supported on the
* OS or if the native credentials type does not contain information
- * about the UNIX process ID (for example this is the case for
- * %G_CREDENTIALS_TYPE_APPLE_XUCRED).
+ * about the UNIX process ID.
*
* Returns: The UNIX process ID, or `-1` if @error is set.
*
@@ -598,13 +609,21 @@ g_credentials_get_unix_pid (GCredentials *credentials,
ret = credentials->native.pid;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
ret = ucred_getpid (credentials->native);
+#elif G_CREDENTIALS_USE_WIN32_PID
+ ret = credentials->native;
+#else
+
+#if G_CREDENTIALS_USE_APPLE_XUCRED
+ ret = credentials->pid;
#else
- /* this case includes G_CREDENTIALS_USE_APPLE_XUCRED */
ret = -1;
- g_set_error_literal (error,
- G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- _("GCredentials does not contain a process ID on this OS"));
+#endif
+
+ if (ret == -1)
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("GCredentials does not contain a process ID on this OS"));
#endif
return ret;
@@ -671,4 +690,16 @@ g_credentials_set_unix_user (GCredentials *credentials,
return ret;
}
+#ifdef __APPLE__
+void
+_g_credentials_set_local_peerid (GCredentials *credentials,
+ pid_t pid)
+{
+ g_return_if_fail (G_IS_CREDENTIALS (credentials));
+ g_return_if_fail (pid >= 0);
+
+ credentials->pid = pid;
+}
+#endif /* __APPLE__ */
+
#endif /* G_OS_UNIX */
diff --git a/gio/gcredentialsprivate.h b/gio/gcredentialsprivate.h
index 13d3bf327..c09f9eceb 100644
--- a/gio/gcredentialsprivate.h
+++ b/gio/gcredentialsprivate.h
@@ -40,6 +40,7 @@
#undef G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
#undef G_CREDENTIALS_USE_SOLARIS_UCRED
#undef G_CREDENTIALS_USE_APPLE_XUCRED
+#undef G_CREDENTIALS_USE_WIN32_PID
/*
* G_CREDENTIALS_NATIVE_TYPE:
@@ -168,6 +169,17 @@
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 0
+void _g_credentials_set_local_peerid (GCredentials *credentials,
+ pid_t pid);
+
+#elif defined(_WIN32)
+#define G_CREDENTIALS_SUPPORTED 1
+#define G_CREDENTIALS_USE_WIN32_PID 1
+#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_WIN32_PID
+#define G_CREDENTIALS_NATIVE_SIZE (sizeof (DWORD))
+#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
+#define G_CREDENTIALS_HAS_PID 1
+
#endif
#endif /* __G_CREDENTIALS_PRIVATE_H__ */
diff --git a/gio/gdbus-2.0/codegen/.flake8 b/gio/gdbus-2.0/codegen/.flake8
new file mode 100644
index 000000000..9450a2832
--- /dev/null
+++ b/gio/gdbus-2.0/codegen/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+# We are generating long lines through templates
+max-line-length = 120
+exclude = __pycache__
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index 9d28cb77d..d8d9a8521 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -2101,7 +2101,7 @@ class CodeGenerator:
" *\n"
" * Signal emitted when a remote caller is invoking the %s.%s() D-Bus method.\n"
" *\n"
- " * If a signal handler returns %%TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call %s_complete_%s() or e.g. g_dbus_method_invocation_return_error() on it) and no order signal handlers will run. If no signal handler handles the invocation, the %%G_DBUS_ERROR_UNKNOWN_METHOD error is returned.\n"
+ " * If a signal handler returns %%TRUE, it means the signal handler will handle the invocation (e.g. take a reference to @invocation and eventually call %s_complete_%s() or e.g. g_dbus_method_invocation_return_error() on it) and no other signal handlers will run. If no signal handler handles the invocation, the %%G_DBUS_ERROR_UNKNOWN_METHOD error is returned.\n"
" *\n"
" * Returns: %%G_DBUS_METHOD_INVOCATION_HANDLED or %%TRUE if the invocation was handled, %%G_DBUS_METHOD_INVOCATION_UNHANDLED or %%FALSE to let other signal handlers run.\n"
% (i.name, m.name, i.name_lower, m.name_lower),
diff --git a/gio/gdbus-2.0/codegen/codegen_docbook.py b/gio/gdbus-2.0/codegen/codegen_docbook.py
index 4b69e2927..b7280e306 100644
--- a/gio/gdbus-2.0/codegen/codegen_docbook.py
+++ b/gio/gdbus-2.0/codegen/codegen_docbook.py
@@ -25,10 +25,6 @@ from os import path
from . import utils
-# Disable line length warnings as wrapping the Docbook templates would be hard
-# flake8: noqa: E501
-
-
# ----------------------------------------------------------------------------------------------------
@@ -345,9 +341,17 @@ class DocbookCodeGenerator:
def expand_paras(self, s, expandParamsAndConstants):
s = self.expand(s, expandParamsAndConstants).strip()
- if not s.startswith("<para"):
- s = "<para>%s</para>" % s
- return s
+ res = []
+ if not s.startswith("<para>"):
+ res.append("<para>")
+ for line in s.split("\n"):
+ line = line.strip()
+ if not line:
+ line = "</para><para>"
+ res.append(line)
+ if not s.endswith("</para>"):
+ res.append("</para>")
+ return "\n".join(res)
def generate_expand_dicts(self):
self.expand_member_dict = {}
diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py
index 238d7dd12..194800c78 100644
--- a/gio/gdbus-2.0/codegen/codegen_main.py
+++ b/gio/gdbus-2.0/codegen/codegen_main.py
@@ -30,6 +30,7 @@ from . import dbustypes
from . import parser
from . import codegen
from . import codegen_docbook
+from . import codegen_rst
from .utils import print_error, print_warning
@@ -212,6 +213,11 @@ def codegen_main():
help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
)
arg_parser.add_argument(
+ "--generate-rst",
+ metavar="OUTFILES",
+ help="Generate reStructuredText in OUTFILES-org.Project.IFace.rst",
+ )
+ arg_parser.add_argument(
"--pragma-once",
action="store_true",
help='Use "pragma once" as the inclusion guard',
@@ -287,10 +293,12 @@ def codegen_main():
)
if (
- args.generate_c_code is not None or args.generate_docbook is not None
+ args.generate_c_code is not None
+ or args.generate_docbook is not None
+ or args.generate_rst is not None
) and args.output is not None:
print_error(
- "Using --generate-c-code or --generate-docbook and "
+ "Using --generate-c-code or --generate-docbook or --generate-rst and "
"--output at the same time is not allowed"
)
@@ -420,6 +428,11 @@ def codegen_main():
if docbook:
docbook_gen.generate(docbook, args.output_directory)
+ rst = args.generate_rst
+ rst_gen = codegen_rst.RstCodeGenerator(all_ifaces)
+ if rst:
+ rst_gen.generate(rst, args.output_directory)
+
if args.header:
with open(h_file, "w") as outfile:
gen = codegen.HeaderCodeGenerator(
diff --git a/gio/gdbus-2.0/codegen/codegen_rst.py b/gio/gdbus-2.0/codegen/codegen_rst.py
new file mode 100644
index 000000000..51da2d572
--- /dev/null
+++ b/gio/gdbus-2.0/codegen/codegen_rst.py
@@ -0,0 +1,332 @@
+# SPDX-FileCopyrightText: 2022 Emmanuele Bassi
+#
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+import os
+import re
+
+from . import utils
+
+# Disable line length warnings as wrapping the templates would be hard
+# flake8: noqa: E501
+
+
+class RstCodeGenerator:
+ """Generates documentation in reStructuredText format."""
+
+ def __init__(self, ifaces):
+ self.ifaces = ifaces
+ self._generate_expand_dicts()
+
+ def _expand(self, s, expandParamsAndConstants):
+ """Expands parameters and constant literals."""
+ res = []
+ for line in s.split("\n"):
+ line = line.strip()
+ if line == "":
+ res.append("")
+ continue
+ for key in self._expand_member_dict_keys:
+ line = line.replace(key, self._expand_member_dict[key])
+ for key in self._expand_iface_dict_keys:
+ line = line.replace(key, self._expand_iface_dict[key])
+ if expandParamsAndConstants:
+ # replace @foo with ``foo``
+ line = re.sub(
+ "@[a-zA-Z0-9_]*",
+ lambda m: "``" + m.group(0)[1:] + "``",
+ line,
+ )
+ # replace e.g. %TRUE with ``TRUE``
+ line = re.sub(
+ "%[a-zA-Z0-9_]*",
+ lambda m: "``" + m.group(0)[1:] + "``",
+ line,
+ )
+ res.append(line)
+ return "\n".join(res)
+
+ def _generate_expand_dicts(self):
+ """Generates the dictionaries used to expand gtk-doc sigils."""
+ self._expand_member_dict = {}
+ self._expand_iface_dict = {}
+ for i in self.ifaces:
+ key = f"#{i.name}"
+ value = f"`{i.name}`_"
+ self._expand_iface_dict[key] = value
+
+ for m in i.methods:
+ key = "%s.%s()" % (i.name, m.name)
+ value = f"`{i.name}.{m.name}`_"
+ self._expand_member_dict[key] = value
+
+ for s in i.signals:
+ key = "#%s::%s" % (i.name, s.name)
+ value = f"`{i.name}::{s.name}`_"
+ self._expand_member_dict[key] = value
+
+ for p in i.properties:
+ key = "#%s:%s" % (i.name, p.name)
+ value = f"`{i.name}:{p.name}`_"
+ self._expand_member_dict[key] = value
+
+ # Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
+ # is evaluated before #org.foo.Iface:Media ...
+ self._expand_member_dict_keys = sorted(
+ self._expand_member_dict.keys(), reverse=True
+ )
+ self._expand_iface_dict_keys = sorted(
+ self._expand_iface_dict.keys(), reverse=True
+ )
+
+ def _generate_header(self, iface):
+ """Generates the header and preamble of the document."""
+ header_len = len(iface.name)
+ res = [
+ f".. _{iface.name}:",
+ "",
+ "=" * header_len,
+ iface.name,
+ "=" * header_len,
+ "",
+ "-----------",
+ "Description",
+ "-----------",
+ "",
+ f".. _{iface.name} Description:",
+ "",
+ iface.doc_string_brief.strip(),
+ "",
+ self._expand(iface.doc_string, True),
+ "",
+ ]
+ if iface.since:
+ res += [
+ f"Interface available since: {iface.since}.",
+ "",
+ ]
+ if iface.deprecated:
+ res += [
+ ".. warning::",
+ "",
+ " This interface is deprecated.",
+ "",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def _generate_section(self, title, name):
+ """Generates a section with the given title."""
+ res = [
+ "-" * len(title),
+ title,
+ "-" * len(title),
+ "",
+ f".. {name} {title}:",
+ "",
+ "",
+ ]
+ return "\n".join(res)
+
+ def _generate_properties(self, iface):
+ """Generates the properties section."""
+ res = []
+ for p in iface.properties:
+ title = f"{iface.name}:{p.name}"
+ if p.readable and p.writable:
+ access = "readwrite"
+ elif p.writable:
+ access = "writable"
+ else:
+ access = "readable"
+ res += [
+ title,
+ "^" * len(title),
+ "",
+ "::",
+ "",
+ f" {p.name} {access} {p.signature}",
+ "",
+ "",
+ self._expand(p.doc_string, True),
+ "",
+ ]
+ if p.since:
+ res += [
+ f"Property available since: {p.since}.",
+ "",
+ ]
+ if p.deprecated:
+ res += [
+ ".. warning::",
+ "",
+ " This property is deprecated.",
+ "",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def _generate_method_signature(self, method):
+ """Generates the method signature as a code block."""
+ res = [
+ "::",
+ "",
+ ]
+ n_in_args = len(method.in_args)
+ n_out_args = len(method.out_args)
+ if n_in_args == 0 and n_out_args == 0:
+ res += [
+ f" {method.name} ()",
+ ]
+ else:
+ res += [
+ f" {method.name} (",
+ ]
+ for idx, arg in enumerate(method.in_args):
+ if idx == n_in_args - 1 and n_out_args == 0:
+ res += [
+ f" IN {arg.name} {arg.signature}",
+ ]
+ else:
+ res += [
+ f" IN {arg.name} {arg.signature},",
+ ]
+ for idx, arg in enumerate(method.out_args):
+ if idx == n_out_args - 1:
+ res += [
+ f" OUT {arg.name} {arg.signature}",
+ ]
+ else:
+ res += [
+ f" OUT {arg.name} {arg.signature},",
+ ]
+ res += [
+ " )",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def _generate_methods(self, iface):
+ """Generates the methods section."""
+ res = []
+ for m in iface.methods:
+ title = f"{iface.name}.{m.name}"
+ res += [
+ title,
+ "^" * len(title),
+ "",
+ self._generate_method_signature(m),
+ "",
+ self._expand(m.doc_string, True),
+ "",
+ ]
+ for a in m.in_args:
+ arg_desc = self._expand(a.doc_string, True)
+ res += [
+ f"{a.name}",
+ f" {arg_desc}",
+ "",
+ ]
+ res += [""]
+ if m.since:
+ res += [
+ f"Method available since: {m.since}.",
+ "",
+ ]
+ if m.deprecated:
+ res += [
+ ".. warning::",
+ "",
+ " This method is deprecated.",
+ "",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def _generate_signal_signature(self, signal):
+ """Generates the signal signature."""
+ res = [
+ "::",
+ "",
+ ]
+ n_args = len(signal.args)
+ if n_args == 0:
+ res += [
+ f" {signal.name} ()",
+ ]
+ else:
+ res += [
+ f" {signal.name} (",
+ ]
+ for idx, arg in enumerate(signal.args):
+ if idx == n_args - 1:
+ res += [
+ f" {arg.name} {arg.signature}",
+ ]
+ else:
+ res += [
+ f" {arg.name} {arg.signature},",
+ ]
+ res += [
+ " )",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def _generate_signals(self, iface):
+ """Generates the signals section."""
+ res = []
+ for s in iface.signals:
+ title = f"{iface.name}::{s.name}"
+ res += [
+ title,
+ "^" * len(title),
+ "",
+ self._generate_signal_signature(s),
+ "",
+ self._expand(s.doc_string, True),
+ "",
+ ]
+ for a in s.args:
+ arg_desc = self._expand(a.doc_string, True)
+ res += [
+ f"{a.name}",
+ f" {arg_desc}",
+ "",
+ ]
+ res += [""]
+ if s.since:
+ res += [
+ f"Signal available since: {s.since}.",
+ "",
+ ]
+ if s.deprecated:
+ res += [
+ ".. warning::",
+ "",
+ " This signal is deprecated.",
+ "",
+ "",
+ ]
+ res += [""]
+ return "\n".join(res)
+
+ def generate(self, rst, outdir):
+ """Generates the reStructuredText file for each interface."""
+ for i in self.ifaces:
+ with open(os.path.join(outdir, f"{rst}-{i.name}.rst"), "w") as outfile:
+ outfile.write(self._generate_header(i))
+ if len(i.properties) > 0:
+ outfile.write(self._generate_section("Properties", i.name))
+ outfile.write(self._generate_properties(i))
+ if len(i.methods) > 0:
+ outfile.write(self._generate_section("Methods", i.name))
+ outfile.write(self._generate_methods(i))
+ if len(i.signals) > 0:
+ outfile.write(self._generate_section("Signals", i.name))
+ outfile.write(self._generate_signals(i))
diff --git a/gio/gdbus-2.0/codegen/meson.build b/gio/gdbus-2.0/codegen/meson.build
index c0caf0e50..bf25cdaeb 100644
--- a/gio/gdbus-2.0/codegen/meson.build
+++ b/gio/gdbus-2.0/codegen/meson.build
@@ -3,6 +3,7 @@ gdbus_codegen_files = [
'codegen.py',
'codegen_main.py',
'codegen_docbook.py',
+ 'codegen_rst.py',
'dbustypes.py',
'parser.py',
'utils.py',
diff --git a/gio/gdbus-2.0/codegen/parser.py b/gio/gdbus-2.0/codegen/parser.py
index 45226d540..cf8ea5229 100644
--- a/gio/gdbus-2.0/codegen/parser.py
+++ b/gio/gdbus-2.0/codegen/parser.py
@@ -85,7 +85,7 @@ class DBusXMLParser:
symbol = line[0:colon_index]
rest_of_line = line[colon_index + 2 :].strip()
if len(rest_of_line) > 0:
- body += "<para>" + rest_of_line + "</para>"
+ body += f"{rest_of_line}\n"
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
if line.startswith("@"):
@@ -93,9 +93,9 @@ class DBusXMLParser:
if colon_index == -1:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if not in_para:
- body += "<para>"
+ body += "\n"
in_para = True
- body += orig_line + "\n"
+ body += f"{orig_line}\n"
else:
param = line[1:colon_index]
docs = line[colon_index + 2 :]
@@ -104,21 +104,20 @@ class DBusXMLParser:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if len(line) > 0:
if not in_para:
- body += "<para>"
+ body += "\n"
in_para = True
body += orig_line + "\n"
elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
if len(line) > 0:
if not in_para:
- body += "<para>"
in_para = True
body += orig_line + "\n"
else:
if in_para:
- body += "</para>"
+ body += "\n"
in_para = False
if in_para:
- body += "</para>"
+ body += "\n"
if symbol != "":
self.doc_comment_last_symbol = symbol
diff --git a/gio/gdbus-tool.c b/gio/gdbus-tool.c
index f44253804..476056b9f 100644
--- a/gio/gdbus-tool.c
+++ b/gio/gdbus-tool.c
@@ -107,7 +107,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout)
g_option_context_set_help_enabled (o, FALSE);
/* Ignore parsing result */
g_option_context_parse (o, argc, argv, NULL);
- program_name = g_path_get_basename ((*argv)[0]);
+ program_name = (*argc > 0) ? g_path_get_basename ((*argv)[0]) : g_strdup ("gdbus-tool");
s = g_strdup_printf (_("Commands:\n"
" help Shows this information\n"
" introspect Introspect a remote object\n"
@@ -141,6 +141,7 @@ modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
* 2. save old argv[0] and restore later
*/
+ g_assert (*argc > 1);
g_assert (g_strcmp0 ((*argv)[1], command) == 0);
remove_arg (1, argc, argv);
@@ -887,6 +888,7 @@ static gchar *opt_call_dest = NULL;
static gchar *opt_call_object_path = NULL;
static gchar *opt_call_method = NULL;
static gint opt_call_timeout = -1;
+static gboolean opt_call_interactive = FALSE;
static const GOptionEntry call_entries[] =
{
@@ -894,6 +896,7 @@ static const GOptionEntry call_entries[] =
{ "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_call_object_path, N_("Object path to invoke method on"), NULL},
{ "method", 'm', 0, G_OPTION_ARG_STRING, &opt_call_method, N_("Method and interface name"), NULL},
{ "timeout", 't', 0, G_OPTION_ARG_INT, &opt_call_timeout, N_("Timeout in seconds"), NULL},
+ { "interactive", 'i', 0, G_OPTION_ARG_NONE, &opt_call_interactive, N_("Allow interactive authorization"), NULL},
G_OPTION_ENTRY_NULL
};
@@ -925,6 +928,7 @@ handle_call (gint *argc,
gboolean skip_dashes;
guint parm;
guint n;
+ GDBusCallFlags flags;
ret = FALSE;
c = NULL;
@@ -1204,6 +1208,11 @@ handle_call (gint *argc,
if (parameters != NULL)
parameters = g_variant_ref_sink (parameters);
+
+ flags = G_DBUS_CALL_FLAGS_NONE;
+ if (opt_call_interactive)
+ flags |= G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION;
+
#ifdef G_OS_UNIX
result = g_dbus_connection_call_with_unix_fd_list_sync (c,
opt_call_dest,
@@ -1212,7 +1221,7 @@ handle_call (gint *argc,
method_name,
parameters,
NULL,
- G_DBUS_CALL_FLAGS_NONE,
+ flags,
opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
fd_list,
NULL,
@@ -1226,7 +1235,7 @@ handle_call (gint *argc,
method_name,
parameters,
NULL,
- G_DBUS_CALL_FLAGS_NONE,
+ flags,
opt_call_timeout > 0 ? opt_call_timeout * 1000 : opt_call_timeout,
NULL,
&error);
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
index 48c766682..48fdef2f4 100644
--- a/gio/gdbusaddress.c
+++ b/gio/gdbusaddress.c
@@ -40,12 +40,12 @@
#include "gdbusprivate.h"
#include "gstdio.h"
-#ifdef G_OS_UNIX
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <gio/gunixsocketaddress.h>
-#endif
#ifdef G_OS_WIN32
#include <windows.h>
@@ -66,6 +66,9 @@
*
* TCP D-Bus connections are supported, but accessing them via a proxy is
* currently not supported.
+ *
+ * Since GLib 2.72, `unix:` addresses are supported on Windows with `AF_UNIX`
+ * support (Windows 10).
*/
static gchar *get_session_address_platform_specific (GError **error);
@@ -571,11 +574,7 @@ g_dbus_address_connect (const gchar *address_entry,
ret = NULL;
nonce_file = NULL;
- if (FALSE)
- {
- }
-#ifdef G_OS_UNIX
- else if (g_strcmp0 (transport_name, "unix") == 0)
+ if (g_strcmp0 (transport_name, "unix") == 0)
{
const gchar *path;
const gchar *abstract;
@@ -605,7 +604,6 @@ g_dbus_address_connect (const gchar *address_entry,
g_assert_not_reached ();
}
}
-#endif
else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
{
const gchar *s;
@@ -1098,7 +1096,7 @@ get_session_address_dbus_launch (GError **error)
if (GLIB_PRIVATE_CALL (g_check_setuid) ())
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Cannot spawn a message bus when setuid"));
+ _("Cannot spawn a message bus when AT_SECURE is set"));
goto out;
}
diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c
index 182c57278..b3f21175b 100644
--- a/gio/gdbusauthmechanismexternal.c
+++ b/gio/gdbusauthmechanismexternal.c
@@ -29,6 +29,10 @@
#include "glibintl.h"
+#ifdef G_OS_WIN32
+#include "gwin32sid.h"
+#endif
+
struct _GDBusAuthMechanismExternalPrivate
{
gboolean is_client;
@@ -124,11 +128,17 @@ static gboolean
mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
+
+#if defined(G_OS_WIN32)
+ /* all that is required is current process SID */
+ return TRUE;
+#else
/* This mechanism is only available if credentials has been exchanged */
if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
return TRUE;
else
return FALSE;
+#endif
}
static gint
@@ -329,32 +339,39 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism,
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
gchar *initial_response = NULL;
+#if defined(G_OS_UNIX)
GCredentials *credentials;
+#endif
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
- m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
+ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
*out_initial_response_len = 0;
+ /* return the uid */
+#if defined(G_OS_UNIX)
credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
g_assert (credentials != NULL);
- /* return the uid */
-#if defined(G_OS_UNIX)
initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
- *out_initial_response_len = strlen (initial_response);
#elif defined(G_OS_WIN32)
+ initial_response = _g_win32_current_process_sid_string (NULL);
+#else
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wcpp"
#warning Dont know how to send credentials on this OS. The EXTERNAL D-Bus authentication mechanism will not work.
#pragma GCC diagnostic pop
#endif
- m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
#endif
+ if (initial_response)
+ {
+ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
+ *out_initial_response_len = strlen (initial_response);
+ }
return initial_response;
}
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index 066ef1ab7..ed5aa3f96 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -32,11 +32,13 @@
#endif
#ifdef G_OS_WIN32
#include <io.h>
+#include "gwin32sid.h"
#endif
#include "gdbusauthmechanismsha1.h"
#include "gcredentials.h"
#include "gdbuserror.h"
+#include "glocalfileinfo.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "gdbusprivate.h"
@@ -508,6 +510,7 @@ _log (const gchar *message,
* and was created successfully) */
static gint
create_lock_exclusive (const gchar *lock_path,
+ gint64 *mtime_nsec,
GError **error)
{
int errsv;
@@ -517,6 +520,16 @@ create_lock_exclusive (const gchar *lock_path,
errsv = errno;
if (ret < 0)
{
+ GLocalFileStat stat_buf;
+
+ /* Get the modification time to distinguish between the lock being stale
+ * or highly contested. */
+ if (mtime_nsec != NULL &&
+ g_local_file_stat (lock_path, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &stat_buf) == 0)
+ *mtime_nsec = _g_stat_mtime (&stat_buf) * G_USEC_PER_SEC * 1000 + _g_stat_mtim_nsec (&stat_buf);
+ else if (mtime_nsec != NULL)
+ *mtime_nsec = 0;
+
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errsv),
@@ -537,6 +550,7 @@ keyring_acquire_lock (const gchar *path,
gint ret;
guint num_tries;
int errsv;
+ gint64 lock_mtime_nsec = 0, lock_mtime_nsec_prev = 0;
/* Total possible sleep period = max_tries * timeout_usec = 0.5s */
const guint max_tries = 50;
@@ -564,13 +578,21 @@ keyring_acquire_lock (const gchar *path,
for (num_tries = 0; num_tries < max_tries; num_tries++)
{
+ lock_mtime_nsec_prev = lock_mtime_nsec;
+
/* Ignore the error until the final call. */
- ret = create_lock_exclusive (lock, NULL);
+ ret = create_lock_exclusive (lock, &lock_mtime_nsec, NULL);
if (ret >= 0)
break;
/* sleep 10ms, then try again */
g_usleep (timeout_usec);
+
+ /* If the mtime of the lock file changed, don’t count the retry, as it
+ * seems like there’s contention between processes for the lock file,
+ * rather than a stale lock file from a crashed process. */
+ if (num_tries > 0 && lock_mtime_nsec != lock_mtime_nsec_prev)
+ num_tries--;
}
if (num_tries == max_tries)
@@ -593,7 +615,7 @@ keyring_acquire_lock (const gchar *path,
_log ("Deleted stale lock file '%s'", lock);
/* Try one last time to create it, now that we've deleted the stale one */
- ret = create_lock_exclusive (lock, error);
+ ret = create_lock_exclusive (lock, NULL, error);
if (ret < 0)
goto out;
}
@@ -991,9 +1013,12 @@ mechanism_server_initiate (GDBusAuthMechanism *mechanism,
}
#elif defined(G_OS_WIN32)
gchar *sid;
- sid = _g_dbus_win32_get_user_sid ();
+
+ sid = _g_win32_current_process_sid_string (NULL);
+
if (g_strcmp0 (initial_response, sid) == 0)
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
+
g_free (sid);
#else
#error Please implement for your OS
@@ -1143,20 +1168,25 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism,
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
- m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
*out_initial_response_len = 0;
#ifdef G_OS_UNIX
initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) getuid ());
- *out_initial_response_len = strlen (initial_response);
#elif defined (G_OS_WIN32)
- initial_response = _g_dbus_win32_get_user_sid ();
- *out_initial_response_len = strlen (initial_response);
+ initial_response = _g_win32_current_process_sid_string (NULL);
#else
#error Please implement for your OS
#endif
- g_assert (initial_response != NULL);
+ if (initial_response)
+ {
+ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
+ *out_initial_response_len = strlen (initial_response);
+ }
+ else
+ {
+ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
+ }
return initial_response;
}
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 73b5b309a..1159c2db4 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -282,22 +282,6 @@ call_destroy_notify (GMainContext *context,
/* ---------------------------------------------------------------------------------------------------- */
-static gboolean
-_g_strv_has_string (const gchar* const *haystack,
- const gchar *needle)
-{
- guint n;
-
- for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
- {
- if (g_strcmp0 (haystack[n], needle) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
#ifdef G_OS_WIN32
#define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE)
#else
@@ -5268,7 +5252,7 @@ obj_message_func (GDBusConnection *connection,
* #GVariant of incorrect type.
*
* If an existing callback is already registered at @object_path and
- * @interface_name, then @error is set to #G_IO_ERROR_EXISTS.
+ * @interface_name, then @error is set to %G_IO_ERROR_EXISTS.
*
* GDBus automatically implements the standard D-Bus interfaces
* org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable
@@ -6544,7 +6528,7 @@ handle_subtree_introspect (GDBusConnection *connection,
/* Assert existence of object if we are not dynamic */
if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) &&
- !_g_strv_has_string ((const gchar * const *) children, requested_node))
+ !g_strv_contains ((const gchar * const *) children, requested_node))
goto out;
}
else
@@ -6675,7 +6659,7 @@ handle_subtree_method_invocation (GDBusConnection *connection,
es->object_path,
es->user_data);
- exists = _g_strv_has_string ((const gchar * const *) children, requested_node);
+ exists = g_strv_contains ((const gchar * const *) children, requested_node);
g_strfreev (children);
if (!exists)
@@ -6930,7 +6914,7 @@ subtree_message_func (GDBusConnection *connection,
*
* When handling remote calls into any node in the subtree, first the
* @enumerate function is used to check if the node exists. If the node exists
- * or the #G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set
+ * or the %G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set
* the @introspection function is used to check if the node supports the
* requested method. If so, the @dispatch function is used to determine
* where to dispatch the call. The collected #GDBusInterfaceVTable and
@@ -6942,7 +6926,7 @@ subtree_message_func (GDBusConnection *connection,
* of the thread you are calling this method from.
*
* If an existing subtree is already registered at @object_path or
- * then @error is set to #G_IO_ERROR_EXISTS.
+ * then @error is set to %G_IO_ERROR_EXISTS.
*
* Note that it is valid to register regular objects (using
* g_dbus_connection_register_object()) in a subtree registered with
@@ -7450,7 +7434,9 @@ _g_bus_forget_singleton (GBusType bus_type)
* callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the
* event that you need a private message bus connection, use
* g_dbus_address_get_for_bus_sync() and
- * g_dbus_connection_new_for_address().
+ * g_dbus_connection_new_for_address() with
+ * G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT and
+ * G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION flags.
*
* Note that the returned #GDBusConnection object will (usually) have
* the #GDBusConnection:exit-on-close property set to %TRUE.
@@ -7569,7 +7555,9 @@ g_bus_get (GBusType bus_type,
* callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the
* event that you need a private message bus connection, use
* g_dbus_address_get_for_bus_sync() and
- * g_dbus_connection_new_for_address().
+ * g_dbus_connection_new_for_address() with
+ * G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT and
+ * G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION flags.
*
* Note that the returned #GDBusConnection object will (usually) have
* the #GDBusConnection:exit-on-close property set to %TRUE.
diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c
index b3ea28f5a..4cc542c7e 100644
--- a/gio/gdbuserror.c
+++ b/gio/gdbuserror.c
@@ -588,7 +588,7 @@ g_dbus_error_get_remote_error (const GError *error)
* such that it can be recovered with g_dbus_error_get_remote_error().
*
* Otherwise, a #GError with the error code %G_IO_ERROR_DBUS_ERROR
- * in the #G_IO_ERROR error domain is returned. Also, @dbus_error_name is
+ * in the %G_IO_ERROR error domain is returned. Also, @dbus_error_name is
* added to the error message such that it can be recovered with
* g_dbus_error_get_remote_error().
*
diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
index 3415ed613..ecef6cd3c 100644
--- a/gio/gdbusmessage.c
+++ b/gio/gdbusmessage.c
@@ -51,6 +51,7 @@
#include "gseekable.h"
#include "gioerror.h"
#include "gdbusprivate.h"
+#include "gutilsprivate.h"
#ifdef G_OS_UNIX
#include "gunixfdlist.h"
@@ -257,17 +258,6 @@ g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf,
#define MIN_ARRAY_SIZE 128
-static gsize
-g_nearest_pow (gsize num)
-{
- gsize n = 1;
-
- while (n < num && n > 0)
- n <<= 1;
-
- return n;
-}
-
static void
array_resize (GMemoryBuffer *mbuf,
gsize size)
diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c
index c22e19ef0..705af079f 100644
--- a/gio/gdbusmethodinvocation.c
+++ b/gio/gdbusmethodinvocation.c
@@ -397,14 +397,7 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
if (g_dbus_message_get_flags (invocation->message) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED)
- {
- if (parameters != NULL)
- {
- g_variant_ref_sink (parameters);
- g_variant_unref (parameters);
- }
- goto out;
- }
+ goto out;
if (parameters == NULL)
parameters = g_variant_new_tuple (NULL, 0);
@@ -420,7 +413,7 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
{
gchar *type_string = g_variant_type_dup_string (type);
- g_warning ("Type of return value is incorrect: expected '%s', got '%s''",
+ g_warning ("Type of return value is incorrect: expected '%s', got '%s'",
type_string, g_variant_get_type_string (parameters));
g_variant_type_free (type);
g_free (type_string);
@@ -431,7 +424,9 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
/* property_info is only non-NULL if set that way from
* GDBusConnection, so this must be the case of async property
- * handling on either 'Get', 'Set' or 'GetAll'.
+ * handling on either 'Get' or 'Set'.
+ *
+ * property_info is NULL for 'GetAll'.
*/
if (invocation->property_info != NULL)
{
@@ -461,21 +456,6 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
g_variant_unref (nested);
}
- else if (g_str_equal (invocation->method_name, "GetAll"))
- {
- if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})")))
- {
- g_warning ("Type of return value for property 'GetAll' call should be '(a{sv})' but got '%s'",
- g_variant_get_type_string (parameters));
- goto out;
- }
-
- /* Could iterate the list of properties and make sure that all
- * of them are actually on the interface and with the correct
- * types, but let's not do that for now...
- */
- }
-
else if (g_str_equal (invocation->method_name, "Set"))
{
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE_UNIT))
@@ -489,6 +469,21 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
else
g_assert_not_reached ();
}
+ else if (g_str_equal (invocation->interface_name, "org.freedesktop.DBus.Properties") &&
+ g_str_equal (invocation->method_name, "GetAll"))
+ {
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(a{sv})")))
+ {
+ g_warning ("Type of return value for property 'GetAll' call should be '(a{sv})' but got '%s'",
+ g_variant_get_type_string (parameters));
+ goto out;
+ }
+
+ /* Could iterate the list of properties and make sure that all
+ * of them are actually on the interface and with the correct
+ * types, but let's not do that for now...
+ */
+ }
if (G_UNLIKELY (_g_dbus_debug_return ()))
{
@@ -508,7 +503,7 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
}
reply = g_dbus_message_new_method_reply (invocation->message);
- g_dbus_message_set_body (reply, parameters);
+ g_dbus_message_set_body (reply, g_steal_pointer (&parameters));
#ifdef G_OS_UNIX
if (fd_list != NULL)
@@ -525,6 +520,12 @@ g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocatio
g_object_unref (reply);
out:
+ if (parameters != NULL)
+ {
+ g_variant_ref_sink (parameters);
+ g_variant_unref (parameters);
+ }
+
g_object_unref (invocation);
}
diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c
index 04c55995d..0d6f5e65c 100644
--- a/gio/gdbusobjectmanagerclient.c
+++ b/gio/gdbusobjectmanagerclient.c
@@ -1689,11 +1689,11 @@ remove_interfaces (GDBusObjectManagerClient *manager,
op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
if (op == NULL)
{
- g_warning ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists",
- G_STRLOC,
- object_path);
+ g_debug ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists",
+ G_STRLOC,
+ object_path);
g_mutex_unlock (&manager->priv->lock);
- goto out;
+ return;
}
interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op));
@@ -1730,8 +1730,6 @@ remove_interfaces (GDBusObjectManagerClient *manager,
g_object_unref (op);
}
g_object_unref (manager);
- out:
- ;
}
static void
diff --git a/gio/gdbusobjectmanagerserver.c b/gio/gdbusobjectmanagerserver.c
index a68594765..0b875be30 100644
--- a/gio/gdbusobjectmanagerserver.c
+++ b/gio/gdbusobjectmanagerserver.c
@@ -457,6 +457,34 @@ registration_data_free (RegistrationData *data)
g_free (data);
}
+/* Validate whether an object path is valid as a child of the manager. According
+ * to the specification:
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
+ * this means that:
+ * > All returned object paths are children of the object path implementing this
+ * > interface, i.e. their object paths start with the ObjectManager's object
+ * > path plus '/'
+ *
+ * For example, if the manager is at `/org/gnome/Example`, children will be
+ * `/org/gnome/Example/(.+)`.
+ *
+ * It is permissible (but not encouraged) for the manager to be at `/`. If so,
+ * children will be `/(.+)`.
+ */
+static gboolean
+is_valid_child_object_path (GDBusObjectManagerServer *manager,
+ const gchar *child_object_path)
+{
+ /* Historically GDBus accepted @child_object_paths at `/` if the @manager
+ * itself is also at `/". This is not spec-compliant, but making GDBus enforce
+ * the spec more strictly would be an incompatible change.
+ *
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2500 */
+ g_warn_if_fail (!g_str_equal (child_object_path, manager->priv->object_path_ending_in_slash));
+
+ return g_str_has_prefix (child_object_path, manager->priv->object_path_ending_in_slash);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
static void
@@ -471,7 +499,7 @@ g_dbus_object_manager_server_export_unlocked (GDBusObjectManagerServer *manager
g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
g_return_if_fail (G_IS_DBUS_OBJECT (object));
- g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash));
+ g_return_if_fail (is_valid_child_object_path (manager, object_path));
interface_names = g_ptr_array_new ();
@@ -574,7 +602,7 @@ g_dbus_object_manager_server_export_uniquely (GDBusObjectManagerServer *manager,
g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
g_return_if_fail (G_IS_DBUS_OBJECT (object));
- g_return_if_fail (g_str_has_prefix (orig_object_path, manager->priv->object_path_ending_in_slash));
+ g_return_if_fail (is_valid_child_object_path (manager, orig_object_path));
g_mutex_lock (&manager->priv->lock);
@@ -650,7 +678,7 @@ g_dbus_object_manager_server_unexport_unlocked (GDBusObjectManagerServer *manag
g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE);
g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE);
- g_return_val_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash), FALSE);
+ g_return_val_if_fail (is_valid_child_object_path (manager, object_path), FALSE);
ret = FALSE;
diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
index fc58aea06..0b8630ab2 100644
--- a/gio/gdbusprivate.c
+++ b/gio/gdbusprivate.c
@@ -23,27 +23,28 @@
#include <stdlib.h>
#include <string.h>
-#include "giotypes.h"
-#include "gioenumtypes.h"
-#include "gsocket.h"
#include "gdbusauthobserver.h"
-#include "gdbusprivate.h"
-#include "gdbusmessage.h"
#include "gdbusconnection.h"
-#include "gdbusproxy.h"
+#include "gdbusdaemon.h"
#include "gdbuserror.h"
#include "gdbusintrospection.h"
-#include "gdbusdaemon.h"
-#include "giomodule-priv.h"
-#include "gtask.h"
+#include "gdbusmessage.h"
+#include "gdbusprivate.h"
+#include "gdbusproxy.h"
#include "ginputstream.h"
-#include "gmemoryinputstream.h"
+#include "gioenumtypes.h"
+#include "giomodule-priv.h"
#include "giostream.h"
+#include "giotypes.h"
+#include "glib-private.h"
#include "glib/gstdio.h"
+#include "gmemoryinputstream.h"
+#include "gsocket.h"
#include "gsocketaddress.h"
-#include "gsocketcontrolmessage.h"
#include "gsocketconnection.h"
+#include "gsocketcontrolmessage.h"
#include "gsocketoutputstream.h"
+#include "gtask.h"
#ifdef G_OS_UNIX
#include "gunixfdmessage.h"
@@ -55,6 +56,7 @@
#include <windows.h>
#include <io.h>
#include <conio.h>
+#include "gwin32sid.h"
#endif
#include "glibintl.h"
@@ -2010,69 +2012,6 @@ _g_dbus_compute_complete_signature (GDBusArgInfo **args)
#ifdef G_OS_WIN32
-extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
-
-gchar *
-_g_dbus_win32_get_user_sid (void)
-{
- HANDLE h;
- TOKEN_USER *user;
- DWORD token_information_len;
- PSID psid;
- gchar *sid;
- gchar *ret;
-
- ret = NULL;
- user = NULL;
- h = INVALID_HANDLE_VALUE;
-
- if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &h))
- {
- g_warning ("OpenProcessToken failed with error code %d", (gint) GetLastError ());
- goto out;
- }
-
- /* Get length of buffer */
- token_information_len = 0;
- if (!GetTokenInformation (h, TokenUser, NULL, 0, &token_information_len))
- {
- if (GetLastError () != ERROR_INSUFFICIENT_BUFFER)
- {
- g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
- goto out;
- }
- }
- user = g_malloc (token_information_len);
- if (!GetTokenInformation (h, TokenUser, user, token_information_len, &token_information_len))
- {
- g_warning ("GetTokenInformation() failed with error code %d", (gint) GetLastError ());
- goto out;
- }
-
- psid = user->User.Sid;
- if (!IsValidSid (psid))
- {
- g_warning ("Invalid SID");
- goto out;
- }
-
- if (!ConvertSidToStringSidA (psid, &sid))
- {
- g_warning ("Invalid SID");
- goto out;
- }
-
- ret = g_strdup (sid);
- LocalFree (sid);
-
-out:
- g_free (user);
- if (h != INVALID_HANDLE_VALUE)
- CloseHandle (h);
- return ret;
-}
-
-
#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
#define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
@@ -2337,6 +2276,26 @@ g_win32_run_session_bus (void* hwnd, void* hinst, const char* cmdline, int cmdsh
static gboolean autolaunch_binary_absent = FALSE;
+static wchar_t *
+find_dbus_process_path (void)
+{
+ wchar_t *dbus_path;
+ gchar *exe_path = GLIB_PRIVATE_CALL (g_win32_find_helper_executable_path) ("gdbus.exe", _g_io_win32_get_module ());
+ dbus_path = g_utf8_to_utf16 (exe_path, -1, NULL, NULL, NULL);
+ g_free (exe_path);
+
+ if (dbus_path == NULL)
+ return NULL;
+
+ if (GetFileAttributesW (dbus_path) == INVALID_FILE_ATTRIBUTES)
+ {
+ g_free (dbus_path);
+ return NULL;
+ }
+
+ return dbus_path;
+}
+
gchar *
_g_dbus_win32_get_session_address_dbus_launch (GError **error)
{
@@ -2354,61 +2313,53 @@ _g_dbus_win32_get_session_address_dbus_launch (GError **error)
if (address == NULL && !autolaunch_binary_absent)
{
- wchar_t gio_path[MAX_PATH + 2] = { 0 };
- int gio_path_len = GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH + 1);
-
- /* The <= MAX_PATH check prevents truncated path usage */
- if (gio_path_len > 0 && gio_path_len <= MAX_PATH)
- {
- PROCESS_INFORMATION pi = { 0 };
- STARTUPINFOW si = { 0 };
- BOOL res = FALSE;
- wchar_t exe_path[MAX_PATH + 100] = { 0 };
- /* calculate index of first char of dll file name inside full path */
- int gio_name_index = gio_path_len;
- for (; gio_name_index > 0; --gio_name_index)
- {
- wchar_t prev_char = gio_path[gio_name_index - 1];
- if (prev_char == L'\\' || prev_char == L'/')
- break;
- }
- gio_path[gio_name_index] = L'\0';
- wcscpy (exe_path, gio_path);
- wcscat (exe_path, L"\\gdbus.exe");
-
- if (GetFileAttributesW (exe_path) == INVALID_FILE_ATTRIBUTES)
- {
- /* warning won't be raised another time
- * since autolaunch_binary_absent would be already set.
- */
- autolaunch_binary_absent = TRUE;
- g_warning ("win32 session dbus binary not found: %S", exe_path );
- }
- else
- {
- wchar_t args[MAX_PATH*2 + 100] = { 0 };
- wcscpy (args, L"\"");
- wcscat (args, exe_path);
- wcscat (args, L"\" ");
+ wchar_t *dbus_path = find_dbus_process_path ();
+ if (dbus_path == NULL)
+ {
+ /* warning won't be raised another time
+ * since autolaunch_binary_absent would be already set.
+ */
+ autolaunch_binary_absent = TRUE;
+ g_warning ("win32 session dbus binary not found");
+ }
+ else
+ {
+ PROCESS_INFORMATION pi = { 0 };
+ STARTUPINFOW si = { 0 };
+ BOOL res = FALSE;
+ wchar_t args[MAX_PATH * 2 + 100] = { 0 };
+ wchar_t working_dir[MAX_PATH + 2] = { 0 };
+ wchar_t *p;
+
+ wcscpy (working_dir, dbus_path);
+ p = wcsrchr (working_dir, L'\\');
+ if (p != NULL)
+ *p = L'\0';
+
+ wcscpy (args, L"\"");
+ wcscat (args, dbus_path);
+ wcscat (args, L"\" ");
#define _L_PREFIX_FOR_EXPANDED(arg) L##arg
#define _L_PREFIX(arg) _L_PREFIX_FOR_EXPANDED (arg)
- wcscat (args, _L_PREFIX (_GDBUS_ARG_WIN32_RUN_SESSION_BUS));
+ wcscat (args, _L_PREFIX (_GDBUS_ARG_WIN32_RUN_SESSION_BUS));
#undef _L_PREFIX
#undef _L_PREFIX_FOR_EXPANDED
- res = CreateProcessW (exe_path, args,
- 0, 0, FALSE,
- NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
- 0, gio_path,
- &si, &pi);
- }
- if (res)
- {
- address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
- if (address == NULL)
- g_warning ("%S dbus binary failed to launch bus, maybe incompatible version", exe_path );
- }
- }
+ res = CreateProcessW (dbus_path, args,
+ 0, 0, FALSE,
+ NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
+ 0, working_dir,
+ &si, &pi);
+
+ if (res)
+ {
+ address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
+ if (address == NULL)
+ g_warning ("%S dbus binary failed to launch bus, maybe incompatible version", dbus_path);
+ }
+
+ g_free (dbus_path);
+ }
}
release_mutex (autolaunch_mutex);
diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
index 7a2289bd4..c095bc5fa 100644
--- a/gio/gdbusproxy.c
+++ b/gio/gdbusproxy.c
@@ -593,11 +593,15 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass)
*
* Emitted when a signal from the remote object and interface that @proxy is for, has been received.
*
+ * Since 2.72 this signal supports detailed connections. You can connect to
+ * the detailed signal `g-signal::x` in order to receive callbacks only when
+ * signal `x` is received from the remote object.
+ *
* Since: 2.26
*/
signals[SIGNAL_SIGNAL] = g_signal_new (I_("g-signal"),
G_TYPE_DBUS_PROXY,
- G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT,
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED | G_SIGNAL_MUST_COLLECT,
G_STRUCT_OFFSET (GDBusProxyClass, g_signal),
NULL,
NULL,
@@ -890,7 +894,7 @@ on_signal_received (GDBusConnection *connection,
g_signal_emit (proxy,
signals[SIGNAL_SIGNAL],
- 0,
+ g_quark_try_string (signal_name),
sender_name,
signal_name,
parameters);
@@ -1694,6 +1698,10 @@ static void
async_initable_init_first (GAsyncInitable *initable)
{
GDBusProxy *proxy = G_DBUS_PROXY (initable);
+ GDBusSignalFlags signal_flags = G_DBUS_SIGNAL_FLAGS_NONE;
+
+ if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_NO_MATCH_RULE)
+ signal_flags |= G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE;
if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES))
{
@@ -1705,7 +1713,7 @@ async_initable_init_first (GAsyncInitable *initable)
"PropertiesChanged",
proxy->priv->object_path,
proxy->priv->interface_name,
- G_DBUS_SIGNAL_FLAGS_NONE,
+ signal_flags,
on_properties_changed,
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
@@ -1721,7 +1729,7 @@ async_initable_init_first (GAsyncInitable *initable)
NULL, /* member */
proxy->priv->object_path,
NULL, /* arg0 */
- G_DBUS_SIGNAL_FLAGS_NONE,
+ signal_flags,
on_signal_received,
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
@@ -1737,7 +1745,7 @@ async_initable_init_first (GAsyncInitable *initable)
"NameOwnerChanged", /* signal name */
"/org/freedesktop/DBus", /* path */
proxy->priv->name, /* arg0 */
- G_DBUS_SIGNAL_FLAGS_NONE,
+ signal_flags,
on_name_owner_changed,
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c
index f12e86204..112c24e22 100644
--- a/gio/gdbusutils.c
+++ b/gio/gdbusutils.c
@@ -537,23 +537,23 @@ g_dbus_gvariant_to_gvalue (GVariant *value,
*
* The conversion is using the following rules:
*
- * - #G_TYPE_STRING: 's', 'o', 'g' or 'ay'
- * - #G_TYPE_STRV: 'as', 'ao' or 'aay'
- * - #G_TYPE_BOOLEAN: 'b'
- * - #G_TYPE_UCHAR: 'y'
- * - #G_TYPE_INT: 'i', 'n'
- * - #G_TYPE_UINT: 'u', 'q'
- * - #G_TYPE_INT64 'x'
- * - #G_TYPE_UINT64: 't'
- * - #G_TYPE_DOUBLE: 'd'
- * - #G_TYPE_VARIANT: Any #GVariantType
- *
- * This can fail if e.g. @gvalue is of type #G_TYPE_STRING and @type
- * is ['i'][G-VARIANT-TYPE-INT32:CAPS]. It will also fail for any #GType
- * (including e.g. #G_TYPE_OBJECT and #G_TYPE_BOXED derived-types) not
+ * - `G_TYPE_STRING`: 's', 'o', 'g' or 'ay'
+ * - `G_TYPE_STRV`: 'as', 'ao' or 'aay'
+ * - `G_TYPE_BOOLEAN`: 'b'
+ * - `G_TYPE_UCHAR`: 'y'
+ * - `G_TYPE_INT`: 'i', 'n'
+ * - `G_TYPE_UINT`: 'u', 'q'
+ * - `G_TYPE_INT64`: 'x'
+ * - `G_TYPE_UINT64`: 't'
+ * - `G_TYPE_DOUBLE`: 'd'
+ * - `G_TYPE_VARIANT`: Any #GVariantType
+ *
+ * This can fail if e.g. @gvalue is of type %G_TYPE_STRING and @type
+ * is 'i', i.e. %G_VARIANT_TYPE_INT32. It will also fail for any #GType
+ * (including e.g. %G_TYPE_OBJECT and %G_TYPE_BOXED derived-types) not
* in the table above.
*
- * Note that if @gvalue is of type #G_TYPE_VARIANT and its value is
+ * Note that if @gvalue is of type %G_TYPE_VARIANT and its value is
* %NULL, the empty #GVariant instance (never %NULL) for @type is
* returned (e.g. 0 for scalar types, the empty string for string types,
* '/' for object path types, the empty array for any array type and so on).
diff --git a/gio/gdebugcontroller.c b/gio/gdebugcontroller.c
new file mode 100644
index 000000000..c61561621
--- /dev/null
+++ b/gio/gdebugcontroller.c
@@ -0,0 +1,119 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2021 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+#include "glib.h"
+#include "glibintl.h"
+
+#include "gdebugcontroller.h"
+#include "ginitable.h"
+#include "giomodule-priv.h"
+
+/**
+ * SECTION:gdebugcontroller
+ * @title: GDebugController
+ * @short_description: Debugging controller
+ * @include: gio/gio.h
+ *
+ * #GDebugController is an interface to expose control of debugging features and
+ * debug output.
+ *
+ * It is implemented on Linux using #GDebugControllerDBus, which exposes a D-Bus
+ * interface to allow authenticated peers to control debug features in this
+ * process.
+ *
+ * Whether debug output is enabled is exposed as
+ * #GDebugController:debug-enabled. This controls g_log_set_debug_enabled() by
+ * default. Application code may connect to the #GObject::notify signal for it
+ * to control other parts of its debug infrastructure as necessary.
+ *
+ * If your application or service is using the default GLib log writer function,
+ * creating one of the built-in implementations of #GDebugController should be
+ * all that’s needed to dynamically enable or disable debug output.
+ *
+ * Since: 2.72
+ */
+
+G_DEFINE_INTERFACE_WITH_CODE (GDebugController, g_debug_controller, G_TYPE_OBJECT,
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE))
+
+static void
+g_debug_controller_default_init (GDebugControllerInterface *iface)
+{
+ /**
+ * GDebugController:debug-enabled:
+ *
+ * %TRUE if debug output should be exposed (for example by forwarding it to
+ * the journal), %FALSE otherwise.
+ *
+ * Since: 2.72
+ */
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("debug-enabled",
+ "Debug Enabled",
+ "Whether to expose debug output",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY));
+}
+
+/**
+ * g_debug_controller_get_debug_enabled:
+ * @self: a #GDebugController
+ *
+ * Get the value of #GDebugController:debug-enabled.
+ *
+ * Returns: %TRUE if debug output should be exposed, %FALSE otherwise
+ * Since: 2.72
+ */
+gboolean
+g_debug_controller_get_debug_enabled (GDebugController *self)
+{
+ gboolean enabled;
+
+ g_return_val_if_fail (G_IS_DEBUG_CONTROLLER (self), FALSE);
+
+ g_object_get (G_OBJECT (self),
+ "debug-enabled", &enabled,
+ NULL);
+
+ return enabled;
+}
+
+/**
+ * g_debug_controller_set_debug_enabled:
+ * @self: a #GDebugController
+ * @debug_enabled: %TRUE if debug output should be exposed, %FALSE otherwise
+ *
+ * Set the value of #GDebugController:debug-enabled.
+ *
+ * Since: 2.72
+ */
+void
+g_debug_controller_set_debug_enabled (GDebugController *self,
+ gboolean debug_enabled)
+{
+ g_return_if_fail (G_IS_DEBUG_CONTROLLER (self));
+
+ g_object_set (G_OBJECT (self),
+ "debug-enabled", debug_enabled,
+ NULL);
+}
diff --git a/gio/gdebugcontroller.h b/gio/gdebugcontroller.h
new file mode 100644
index 000000000..ca3a2d29d
--- /dev/null
+++ b/gio/gdebugcontroller.h
@@ -0,0 +1,79 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2021 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __G_DEBUG_CONTROLLER_H__
+#define __G_DEBUG_CONTROLLER_H__
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+/**
+ * G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME:
+ *
+ * Extension point for debug control functionality.
+ * See [Extending GIO][extending-gio].
+ *
+ * Since: 2.72
+ */
+#define G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME "gio-debug-controller"
+
+/**
+ * GDebugController:
+ *
+ * #GDebugController is an interface to expose control of debugging features and
+ * debug output.
+ *
+ * Since: 2.72
+ */
+#define G_TYPE_DEBUG_CONTROLLER (g_debug_controller_get_type ())
+GLIB_AVAILABLE_IN_2_72
+G_DECLARE_INTERFACE(GDebugController, g_debug_controller, g, debug_controller, GObject)
+
+#define G_DEBUG_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DEBUG_CONTROLLER, GDebugController))
+#define G_IS_DEBUG_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DEBUG_CONTROLLER))
+#define G_DEBUG_CONTROLLER_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_DEBUG_CONTROLLER, GDebugControllerInterface))
+
+/**
+ * GDebugControllerInterface:
+ * @g_iface: The parent interface.
+ *
+ * The virtual function table for #GDebugController.
+ *
+ * Since: 2.72
+ */
+struct _GDebugControllerInterface {
+ /*< private >*/
+ GTypeInterface g_iface;
+};
+
+GLIB_AVAILABLE_IN_2_72
+gboolean g_debug_controller_get_debug_enabled (GDebugController *self);
+GLIB_AVAILABLE_IN_2_72
+void g_debug_controller_set_debug_enabled (GDebugController *self,
+ gboolean debug_enabled);
+
+G_END_DECLS
+
+#endif /* __G_DEBUG_CONTROLLER_H__ */
diff --git a/gio/gdebugcontrollerdbus.c b/gio/gdebugcontrollerdbus.c
new file mode 100644
index 000000000..3c0ee007e
--- /dev/null
+++ b/gio/gdebugcontrollerdbus.c
@@ -0,0 +1,709 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2021 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include "gdebugcontroller.h"
+#include "gdebugcontrollerdbus.h"
+#include "giomodule-priv.h"
+#include "gi18n.h"
+#include "gio/gdbusprivate.h"
+#include "gio/gmarshal-internal.h"
+
+/**
+ * SECTION:gdebugcontrollerdbus
+ * @title: GDebugControllerDBus
+ * @short_description: Debugging controller D-Bus implementation
+ * @include: gio/gio.h
+ *
+ * #GDebugControllerDBus is an implementation of #GDebugController which exposes
+ * debug settings as a D-Bus object.
+ *
+ * It is a #GInitable object, and will register an object at
+ * `/org/gtk/Debugging` on the bus given as
+ * #GDebugControllerDBus:connection once it’s initialized. The object will be
+ * unregistered when the last reference to the #GDebugControllerDBus is dropped.
+ *
+ * This D-Bus object can be used by remote processes to enable or disable debug
+ * output in this process. Remote processes calling
+ * `org.gtk.Debugging.SetDebugEnabled()` will affect the value of
+ * #GDebugController:debug-enabled and, by default, g_log_get_debug_enabled().
+ * default.
+ *
+ * By default, all processes will be able to call `SetDebugEnabled()`. If this
+ * process is privileged, or might expose sensitive information in its debug
+ * output, you may want to restrict the ability to enable debug output to
+ * privileged users or processes.
+ *
+ * One option is to install a D-Bus security policy which restricts access to
+ * `SetDebugEnabled()`, installing something like the following in
+ * `$datadir/dbus-1/system.d/`:
+ * |[<!-- language="XML" -->
+ * <?xml version="1.0"?> <!--*-nxml-*-->
+ * <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ * "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+ * <busconfig>
+ * <policy user="root">
+ * <allow send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
+ * </policy>
+ * <policy context="default">
+ * <deny send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
+ * </policy>
+ * </busconfig>
+ * ]|
+ *
+ * This will prevent the `SetDebugEnabled()` method from being called by all
+ * except root. It will not prevent the `DebugEnabled` property from being read,
+ * as it’s accessed through the `org.freedesktop.DBus.Properties` interface.
+ *
+ * Another option is to use polkit to allow or deny requests on a case-by-case
+ * basis, allowing for the possibility of dynamic authorisation. To do this,
+ * connect to the #GDebugControllerDBus::authorize signal and query polkit in
+ * it:
+ * |[<!-- language="C" -->
+ * g_autoptr(GError) child_error = NULL;
+ * g_autoptr(GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
+ * gulong debug_controller_authorize_id = 0;
+ *
+ * // Set up the debug controller.
+ * debug_controller = G_DEBUG_CONTROLLER (g_debug_controller_dbus_new (priv->connection, NULL, &child_error));
+ * if (debug_controller == NULL)
+ * {
+ * g_error ("Could not register debug controller on bus: %s"),
+ * child_error->message);
+ * }
+ *
+ * debug_controller_authorize_id = g_signal_connect (debug_controller,
+ * "authorize",
+ * G_CALLBACK (debug_controller_authorize_cb),
+ * self);
+ *
+ * static gboolean
+ * debug_controller_authorize_cb (GDebugControllerDBus *debug_controller,
+ * GDBusMethodInvocation *invocation,
+ * gpointer user_data)
+ * {
+ * g_autoptr(PolkitAuthority) authority = NULL;
+ * g_autoptr(PolkitSubject) subject = NULL;
+ * g_autoptr(PolkitAuthorizationResult) auth_result = NULL;
+ * g_autoptr(GError) local_error = NULL;
+ * GDBusMessage *message;
+ * GDBusMessageFlags message_flags;
+ * PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
+ *
+ * message = g_dbus_method_invocation_get_message (invocation);
+ * message_flags = g_dbus_message_get_flags (message);
+ *
+ * authority = polkit_authority_get_sync (NULL, &local_error);
+ * if (authority == NULL)
+ * {
+ * g_warning ("Failed to get polkit authority: %s", local_error->message);
+ * return FALSE;
+ * }
+ *
+ * if (message_flags & G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION)
+ * flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
+ *
+ * subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
+ *
+ * auth_result = polkit_authority_check_authorization_sync (authority,
+ * subject,
+ * "com.example.MyService.set-debug-enabled",
+ * NULL,
+ * flags,
+ * NULL,
+ * &local_error);
+ * if (auth_result == NULL)
+ * {
+ * g_warning ("Failed to get check polkit authorization: %s", local_error->message);
+ * return FALSE;
+ * }
+ *
+ * return polkit_authorization_result_get_is_authorized (auth_result);
+ * }
+ * ]|
+ *
+ * Since: 2.72
+ */
+
+static const gchar org_gtk_Debugging_xml[] =
+ "<node>"
+ "<interface name='org.gtk.Debugging'>"
+ "<property name='DebugEnabled' type='b' access='read'/>"
+ "<method name='SetDebugEnabled'>"
+ "<arg type='b' name='debug-enabled' direction='in'/>"
+ "</method>"
+ "</interface>"
+ "</node>";
+
+static GDBusInterfaceInfo *org_gtk_Debugging;
+
+#define G_DEBUG_CONTROLLER_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
+
+static void g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface);
+static void g_debug_controller_dbus_initable_iface_init (GInitableIface *iface);
+static gboolean g_debug_controller_dbus_authorize_default (GDebugControllerDBus *self,
+ GDBusMethodInvocation *invocation);
+
+typedef enum
+{
+ PROP_CONNECTION = 1,
+ /* Overrides: */
+ PROP_DEBUG_ENABLED,
+} GDebugControllerDBusProperty;
+
+static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
+
+typedef enum
+{
+ SIGNAL_AUTHORIZE,
+} GDebugControllerDBusSignal;
+
+static guint signals[SIGNAL_AUTHORIZE + 1] = {0};
+
+typedef struct
+{
+ GObject parent_instance;
+
+ GCancellable *cancellable; /* (owned) */
+ GDBusConnection *connection; /* (owned) */
+ guint object_id;
+ GPtrArray *pending_authorize_tasks; /* (element-type GWeakRef) (owned) (nullable) */
+
+ gboolean debug_enabled;
+} GDebugControllerDBusPrivate;
+
+G_DEFINE_TYPE_WITH_CODE (GDebugControllerDBus, g_debug_controller_dbus, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GDebugControllerDBus)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_debug_controller_dbus_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_DEBUG_CONTROLLER,
+ g_debug_controller_dbus_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "dbus",
+ 30))
+
+static void
+g_debug_controller_dbus_init (GDebugControllerDBus *self)
+{
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ priv->cancellable = g_cancellable_new ();
+}
+
+static void
+set_debug_enabled (GDebugControllerDBus *self,
+ gboolean debug_enabled)
+{
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ if (g_cancellable_is_cancelled (priv->cancellable))
+ return;
+
+ if (debug_enabled != priv->debug_enabled)
+ {
+ GVariantBuilder builder;
+
+ priv->debug_enabled = debug_enabled;
+
+ /* Change the default log writer’s behaviour in GLib. */
+ g_log_set_debug_enabled (debug_enabled);
+
+ /* Notify internally and externally of the property change. */
+ g_object_notify (G_OBJECT (self), "debug-enabled");
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}", "DebugEnabled", g_variant_new_boolean (priv->debug_enabled));
+
+ g_dbus_connection_emit_signal (priv->connection,
+ NULL,
+ "/org/gtk/Debugging",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ g_variant_new ("(sa{sv}as)",
+ "org.gtk.Debugging",
+ &builder,
+ NULL),
+ NULL);
+
+ g_debug ("Debug output %s", debug_enabled ? "enabled" : "disabled");
+ }
+}
+
+/* Called in the #GMainContext which was default when the #GDebugControllerDBus
+ * was initialised. */
+static GVariant *
+dbus_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ GDebugControllerDBus *self = user_data;
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ if (g_str_equal (property_name, "DebugEnabled"))
+ return g_variant_new_boolean (priv->debug_enabled);
+
+ g_assert_not_reached ();
+
+ return NULL;
+}
+
+static GWeakRef *
+weak_ref_new (GObject *obj)
+{
+ GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
+
+ g_weak_ref_init (weak_ref, obj);
+
+ return g_steal_pointer (&weak_ref);
+}
+
+static void
+weak_ref_free (GWeakRef *weak_ref)
+{
+ g_weak_ref_clear (weak_ref);
+ g_free (weak_ref);
+}
+
+/* Called in the #GMainContext which was default when the #GDebugControllerDBus
+ * was initialised. */
+static void
+garbage_collect_weak_refs (GDebugControllerDBus *self)
+{
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+ guint i;
+
+ if (priv->pending_authorize_tasks == NULL)
+ return;
+
+ /* Iterate in reverse order so that if we remove an element the hole won’t be
+ * filled by an element we haven’t checked yet. */
+ for (i = priv->pending_authorize_tasks->len; i > 0; i--)
+ {
+ GWeakRef *weak_ref = g_ptr_array_index (priv->pending_authorize_tasks, i - 1);
+ GObject *obj = g_weak_ref_get (weak_ref);
+
+ if (obj == NULL)
+ g_ptr_array_remove_index_fast (priv->pending_authorize_tasks, i - 1);
+ else
+ g_object_unref (obj);
+ }
+
+ /* Don’t need to keep the array around any more if it’s empty. */
+ if (priv->pending_authorize_tasks->len == 0)
+ g_clear_pointer (&priv->pending_authorize_tasks, g_ptr_array_unref);
+}
+
+/* Called in a worker thread. */
+static void
+authorize_task_cb (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (source_object);
+ GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (task_data);
+ gboolean authorized = TRUE;
+
+ g_signal_emit (self, signals[SIGNAL_AUTHORIZE], 0, invocation, &authorized);
+
+ g_task_return_boolean (task, authorized);
+}
+
+/* Called in the #GMainContext which was default when the #GDebugControllerDBus
+ * was initialised. */
+static void
+authorize_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
+ GDebugControllerDBusPrivate *priv G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
+ GTask *task = G_TASK (result);
+ GDBusMethodInvocation *invocation = g_task_get_task_data (task);
+ GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation);
+ gboolean enabled = FALSE;
+ gboolean authorized;
+
+ priv = g_debug_controller_dbus_get_instance_private (self);
+ authorized = g_task_propagate_boolean (task, NULL);
+
+ if (!authorized)
+ {
+ GError *local_error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+ _("Not authorized to change debug settings"));
+ g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
+ }
+ else
+ {
+ /* Update the property value. */
+ g_variant_get (parameters, "(b)", &enabled);
+ set_debug_enabled (self, enabled);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+
+ /* The GTask will stay alive for a bit longer as the worker thread is
+ * potentially still in the process of dropping its reference to it. */
+ g_assert (priv->pending_authorize_tasks != NULL && priv->pending_authorize_tasks->len > 0);
+}
+
+/* Called in the #GMainContext which was default when the #GDebugControllerDBus
+ * was initialised. */
+static void
+dbus_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ GDebugControllerDBus *self = user_data;
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+ GDebugControllerDBusClass *klass = G_DEBUG_CONTROLLER_DBUS_GET_CLASS (self);
+
+ /* Only on the org.gtk.Debugging interface */
+ if (g_str_equal (method_name, "SetDebugEnabled"))
+ {
+ GTask *task = NULL;
+
+ task = g_task_new (self, priv->cancellable, authorize_cb, NULL);
+ g_task_set_source_tag (task, dbus_method_call);
+ g_task_set_task_data (task, g_object_ref (invocation), (GDestroyNotify) g_object_unref);
+
+ /* Track the pending #GTask with a weak ref as its final strong ref could
+ * be dropped from this thread or an arbitrary #GTask worker thread. The
+ * weak refs will be evaluated in g_debug_controller_dbus_stop(). */
+ if (priv->pending_authorize_tasks == NULL)
+ priv->pending_authorize_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) weak_ref_free);
+ g_ptr_array_add (priv->pending_authorize_tasks, weak_ref_new (G_OBJECT (task)));
+
+ /* Take the opportunity to clean up a bit. */
+ garbage_collect_weak_refs (self);
+
+ /* Check the calling peer is authorised to change the debug mode. So that
+ * the signal handler can block on checking polkit authorisation (which
+ * definitely involves D-Bus calls, and might involve user interaction),
+ * emit the #GDebugControllerDBus::authorize signal in a worker thread, so
+ * that handlers can synchronously block it. This is similar to how
+ * #GDBusInterfaceSkeleton::g-authorize-method works.
+ *
+ * If no signal handlers are connected, don’t bother running the worker
+ * thread, and just return a default value of %FALSE. Fail closed. */
+ if (g_signal_has_handler_pending (self, signals[SIGNAL_AUTHORIZE], 0, FALSE) ||
+ klass->authorize != g_debug_controller_dbus_authorize_default)
+ g_task_run_in_thread (task, authorize_task_cb);
+ else
+ g_task_return_boolean (task, FALSE);
+
+ g_clear_object (&task);
+ }
+ else
+ g_assert_not_reached ();
+}
+
+static gboolean
+g_debug_controller_dbus_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (initable);
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+ static const GDBusInterfaceVTable vtable = {
+ dbus_method_call,
+ dbus_get_property,
+ NULL /* set_property */,
+ { 0 }
+ };
+
+ if (org_gtk_Debugging == NULL)
+ {
+ GError *local_error = NULL;
+ GDBusNodeInfo *info;
+
+ info = g_dbus_node_info_new_for_xml (org_gtk_Debugging_xml, &local_error);
+ if G_UNLIKELY (info == NULL)
+ g_error ("%s", local_error->message);
+ org_gtk_Debugging = g_dbus_node_info_lookup_interface (info, "org.gtk.Debugging");
+ g_assert (org_gtk_Debugging != NULL);
+ g_dbus_interface_info_ref (org_gtk_Debugging);
+ g_dbus_node_info_unref (info);
+ }
+
+ priv->object_id = g_dbus_connection_register_object (priv->connection,
+ "/org/gtk/Debugging",
+ org_gtk_Debugging,
+ &vtable, self, NULL, error);
+ if (priv->object_id == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+g_debug_controller_dbus_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ switch ((GDebugControllerDBusProperty) prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->connection);
+ break;
+ case PROP_DEBUG_ENABLED:
+ g_value_set_boolean (value, priv->debug_enabled);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_debug_controller_dbus_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ switch ((GDebugControllerDBusProperty) prop_id)
+ {
+ case PROP_CONNECTION:
+ /* Construct only */
+ g_assert (priv->connection == NULL);
+ priv->connection = g_value_dup_object (value);
+ break;
+ case PROP_DEBUG_ENABLED:
+ set_debug_enabled (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_debug_controller_dbus_dispose (GObject *object)
+{
+ GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ g_debug_controller_dbus_stop (self);
+ g_assert (priv->pending_authorize_tasks == NULL);
+ g_clear_object (&priv->connection);
+ g_clear_object (&priv->cancellable);
+
+ G_OBJECT_CLASS (g_debug_controller_dbus_parent_class)->dispose (object);
+}
+
+static gboolean
+g_debug_controller_dbus_authorize_default (GDebugControllerDBus *self,
+ GDBusMethodInvocation *invocation)
+{
+ return TRUE;
+}
+
+static void
+g_debug_controller_dbus_class_init (GDebugControllerDBusClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = g_debug_controller_dbus_get_property;
+ gobject_class->set_property = g_debug_controller_dbus_set_property;
+ gobject_class->dispose = g_debug_controller_dbus_dispose;
+
+ klass->authorize = g_debug_controller_dbus_authorize_default;
+
+ /**
+ * GDebugControllerDBus:connection:
+ *
+ * The D-Bus connection to expose the debugging interface on.
+ *
+ * Typically this will be the same connection (to the system or session bus)
+ * which the rest of the application or service’s D-Bus objects are registered
+ * on.
+ *
+ * Since: 2.72
+ */
+ props[PROP_CONNECTION] =
+ g_param_spec_object ("connection", "D-Bus Connection",
+ "The D-Bus connection to expose the debugging interface on.",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, G_N_ELEMENTS (props), props);
+
+ g_object_class_override_property (gobject_class, PROP_DEBUG_ENABLED, "debug-enabled");
+
+ /**
+ * GDebugControllerDBus::authorize:
+ * @controller: The #GDebugControllerDBus emitting the signal.
+ * @invocation: A #GDBusMethodInvocation.
+ *
+ * Emitted when a D-Bus peer is trying to change the debug settings and used
+ * to determine if that is authorized.
+ *
+ * This signal is emitted in a dedicated worker thread, so handlers are
+ * allowed to perform blocking I/O. This means that, for example, it is
+ * appropriate to call `polkit_authority_check_authorization_sync()` to check
+ * authorization using polkit.
+ *
+ * If %FALSE is returned then no further handlers are run and the request to
+ * change the debug settings is rejected.
+ *
+ * Otherwise, if %TRUE is returned, signal emission continues. If no handlers
+ * return %FALSE, then the debug settings are allowed to be changed.
+ *
+ * Signal handlers must not modify @invocation, or cause it to return a value.
+ *
+ * The default class handler just returns %TRUE.
+ *
+ * Returns: %TRUE if the call is authorized, %FALSE otherwise.
+ *
+ * Since: 2.72
+ */
+ signals[SIGNAL_AUTHORIZE] =
+ g_signal_new ("authorize",
+ G_TYPE_DEBUG_CONTROLLER_DBUS,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GDebugControllerDBusClass, authorize),
+ _g_signal_accumulator_false_handled,
+ NULL,
+ _g_cclosure_marshal_BOOLEAN__OBJECT,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_DBUS_METHOD_INVOCATION);
+ g_signal_set_va_marshaller (signals[SIGNAL_AUTHORIZE],
+ G_TYPE_FROM_CLASS (klass),
+ _g_cclosure_marshal_BOOLEAN__OBJECTv);
+}
+
+static void
+g_debug_controller_dbus_iface_init (GDebugControllerInterface *iface)
+{
+}
+
+static void
+g_debug_controller_dbus_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_debug_controller_dbus_initable_init;
+}
+
+/**
+ * g_debug_controller_dbus_new:
+ * @connection: a #GDBusConnection to register the debug object on
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Create a new #GDebugControllerDBus and synchronously initialize it.
+ *
+ * Initializing the object will export the debug object on @connection. The
+ * object will remain registered until the last reference to the
+ * #GDebugControllerDBus is dropped.
+ *
+ * Initialization may fail if registering the object on @connection fails.
+ *
+ * Returns: (nullable) (transfer full): a new #GDebugControllerDBus, or %NULL
+ * on failure
+ * Since: 2.72
+ */
+GDebugControllerDBus *
+g_debug_controller_dbus_new (GDBusConnection *connection,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_initable_new (G_TYPE_DEBUG_CONTROLLER_DBUS,
+ cancellable,
+ error,
+ "connection", connection,
+ NULL);
+}
+
+/**
+ * g_debug_controller_dbus_stop:
+ * @self: a #GDebugControllerDBus
+ *
+ * Stop the debug controller, unregistering its object from the bus.
+ *
+ * Any pending method calls to the object will complete successfully, but new
+ * ones will return an error. This method will block until all pending
+ * #GDebugControllerDBus::authorize signals have been handled. This is expected
+ * to not take long, as it will just be waiting for threads to join. If any
+ * #GDebugControllerDBus::authorize signal handlers are still executing in other
+ * threads, this will block until after they have returned.
+ *
+ * This method will be called automatically when the final reference to the
+ * #GDebugControllerDBus is dropped. You may want to call it explicitly to know
+ * when the controller has been fully removed from the bus, or to break
+ * reference count cycles.
+ *
+ * Calling this method from within a #GDebugControllerDBus::authorize signal
+ * handler will cause a deadlock and must not be done.
+ *
+ * Since: 2.72
+ */
+void
+g_debug_controller_dbus_stop (GDebugControllerDBus *self)
+{
+ GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
+
+ g_cancellable_cancel (priv->cancellable);
+
+ if (priv->object_id != 0)
+ {
+ g_dbus_connection_unregister_object (priv->connection, priv->object_id);
+ priv->object_id = 0;
+ }
+
+ /* Wait for any pending authorize tasks to finish. These will just be waiting
+ * for threads to join at this point, as the D-Bus object has been
+ * unregistered and the cancellable cancelled.
+ *
+ * The loop will never terminate if g_debug_controller_dbus_stop() is
+ * called from within an ::authorize callback.
+ *
+ * See discussion in https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2486 */
+ while (priv->pending_authorize_tasks != NULL)
+ {
+ garbage_collect_weak_refs (self);
+ g_thread_yield ();
+ }
+}
diff --git a/gio/gdebugcontrollerdbus.h b/gio/gdebugcontrollerdbus.h
new file mode 100644
index 000000000..5e54bbfa1
--- /dev/null
+++ b/gio/gdebugcontrollerdbus.h
@@ -0,0 +1,69 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2021 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __G_DEBUG_CONTROLLER_DBUS_H__
+#define __G_DEBUG_CONTROLLER_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GDebugControllerDBus:
+ *
+ * #GDebugControllerDBus is an implementation of #GDebugController over D-Bus.
+ *
+ * Since: 2.72
+ */
+#define G_TYPE_DEBUG_CONTROLLER_DBUS (g_debug_controller_dbus_get_type ())
+GLIB_AVAILABLE_IN_2_72
+G_DECLARE_DERIVABLE_TYPE (GDebugControllerDBus, g_debug_controller_dbus, G, DEBUG_CONTROLLER_DBUS, GObject)
+
+/**
+ * GDebugControllerDBusClass:
+ * @parent_class: The parent class.
+ * @authorize: Default handler for the #GDebugControllerDBus::authorize signal.
+ *
+ * The virtual function table for #GDebugControllerDBus.
+ *
+ * Since: 2.72
+ */
+struct _GDebugControllerDBusClass
+{
+ GObjectClass parent_class;
+
+ gboolean (*authorize) (GDebugControllerDBus *controller,
+ GDBusMethodInvocation *invocation);
+
+ gpointer padding[12];
+};
+
+GLIB_AVAILABLE_IN_2_72
+GDebugControllerDBus *g_debug_controller_dbus_new (GDBusConnection *connection,
+ GCancellable *cancellable,
+ GError **error);
+
+GLIB_AVAILABLE_IN_2_72
+void g_debug_controller_dbus_stop (GDebugControllerDBus *self);
+
+G_END_DECLS
+
+#endif /* __G_DEBUG_CONTROLLER_DBUS_H__ */
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 229e62143..60d6debb2 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -89,6 +89,7 @@ enum {
static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
GError **error);
+static gboolean g_desktop_app_info_load_file (GDesktopAppInfo *self);
/**
* GDesktopAppInfo:
@@ -1003,6 +1004,19 @@ desktop_file_dir_unindexed_init (DesktopFileDir *dir)
}
static GDesktopAppInfo *
+g_desktop_app_info_new_from_filename_unlocked (const char *filename)
+{
+ GDesktopAppInfo *info = NULL;
+
+ info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
+
+ if (!g_desktop_app_info_load_file (info))
+ g_clear_object (&info);
+
+ return info;
+}
+
+static GDesktopAppInfo *
desktop_file_dir_unindexed_get_app (DesktopFileDir *dir,
const gchar *desktop_id)
{
@@ -1013,7 +1027,7 @@ desktop_file_dir_unindexed_get_app (DesktopFileDir *dir,
if (!filename)
return NULL;
- return g_desktop_app_info_new_from_filename (filename);
+ return g_desktop_app_info_new_from_filename_unlocked (filename);
}
static void
@@ -1033,7 +1047,7 @@ desktop_file_dir_unindexed_get_all (DesktopFileDir *dir,
if (desktop_file_dir_app_name_is_masked (dir, app_name))
continue;
- add_to_table_if_appropriate (apps, app_name, g_desktop_app_info_new_from_filename (filename));
+ add_to_table_if_appropriate (apps, app_name, g_desktop_app_info_new_from_filename_unlocked (filename));
}
}
@@ -1754,6 +1768,56 @@ binary_from_exec (const char *exec)
return g_strndup (start, p - start);
}
+/*< internal >
+ * g_desktop_app_info_get_desktop_id_for_filename
+ * @self: #GDesktopAppInfo to get desktop id of
+ *
+ * Tries to find the desktop ID for a particular `.desktop` filename, as per the
+ * [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-
+ * entry-spec/desktop-entry-spec-latest.html#desktop-file-id).
+ *
+ * Returns: desktop id or basename if filename is unknown.
+ */
+static char *
+g_desktop_app_info_get_desktop_id_for_filename (GDesktopAppInfo *self)
+{
+ guint i;
+ gchar *desktop_id = NULL;
+
+ g_return_val_if_fail (self->filename != NULL, NULL);
+
+ for (i = 0; i < desktop_file_dirs->len; i++)
+ {
+ DesktopFileDir *dir = g_ptr_array_index (desktop_file_dirs, i);
+ GHashTable *app_names;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ app_names = dir->app_names;
+
+ if (!app_names)
+ continue;
+
+ g_hash_table_iter_init (&iter, app_names);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if (!strcmp (value, self->filename))
+ {
+ desktop_id = g_strdup (key);
+ break;
+ }
+ }
+
+ if (desktop_id)
+ break;
+ }
+
+ if (!desktop_id)
+ desktop_id = g_path_get_basename (self->filename);
+
+ return g_steal_pointer (&desktop_id);
+}
+
static gboolean
g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
GKeyFile *key_file)
@@ -1816,6 +1880,10 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
else
{
char *t;
+
+ /* Since @exec is not an empty string, there must be at least one
+ * argument, so dereferencing argv[0] should return non-NULL. */
+ g_assert (argc > 0);
t = g_find_program_in_path (argv[0]);
g_strfreev (argv);
@@ -1912,6 +1980,9 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
g_free (basename);
}
+ if (info->filename)
+ info->desktop_id = g_desktop_app_info_get_desktop_id_for_filename (info);
+
info->keyfile = g_key_file_ref (key_file);
return TRUE;
@@ -1925,8 +1996,6 @@ g_desktop_app_info_load_file (GDesktopAppInfo *self)
g_return_val_if_fail (self->filename != NULL, FALSE);
- self->desktop_id = g_path_get_basename (self->filename);
-
key_file = g_key_file_new ();
if (g_key_file_load_from_file (key_file, self->filename, G_KEY_FILE_NONE, NULL))
@@ -1953,11 +2022,14 @@ g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
info->filename = NULL;
+
+ desktop_file_dirs_lock ();
+
if (!g_desktop_app_info_load_from_keyfile (info, key_file))
- {
- g_object_unref (info);
- return NULL;
- }
+ g_clear_object (&info);
+
+ desktop_file_dirs_unlock ();
+
return info;
}
@@ -1975,12 +2047,12 @@ g_desktop_app_info_new_from_filename (const char *filename)
{
GDesktopAppInfo *info = NULL;
- info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
- if (!g_desktop_app_info_load_file (info))
- {
- g_object_unref (info);
- return NULL;
- }
+ desktop_file_dirs_lock ();
+
+ info = g_desktop_app_info_new_from_filename_unlocked (filename);
+
+ desktop_file_dirs_unlock ();
+
return info;
}
@@ -2233,7 +2305,7 @@ g_desktop_app_info_get_generic_name (GDesktopAppInfo *info)
*
* Gets the value of the NoDisplay key, which helps determine if the
* application info should be shown in menus. See
- * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
+ * %G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
*
* Returns: The value of the NoDisplay key
*
@@ -2731,6 +2803,26 @@ notify_desktop_launch (GDBusConnection *session_bus,
g_object_unref (msg);
}
+static void
+emit_launch_started (GAppLaunchContext *context,
+ GDesktopAppInfo *info,
+ const gchar *startup_id)
+{
+ GVariantBuilder builder;
+ GVariant *platform_data = NULL;
+
+ if (startup_id)
+ {
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&builder, "{sv}",
+ "startup-notification-id",
+ g_variant_new_string (startup_id));
+ platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
+ }
+ g_signal_emit_by_name (context, "launch-started", info, platform_data);
+ g_clear_pointer (&platform_data, g_variant_unref);
+}
+
#define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
static gboolean
@@ -2826,6 +2918,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
}
g_list_free_full (launched_files, g_object_unref);
+
+ emit_launch_started (launch_context, info, sn_id);
}
/* Wrap the @argv in a command which will set the
@@ -2959,6 +3053,64 @@ g_desktop_app_info_make_platform_data (GDesktopAppInfo *info,
return g_variant_builder_end (&builder);
}
+typedef struct
+{
+ GDesktopAppInfo *info; /* (owned) */
+ GAppLaunchContext *launch_context; /* (owned) (nullable) */
+ GAsyncReadyCallback callback;
+ gchar *startup_id; /* (owned) */
+ gpointer user_data;
+} LaunchUrisWithDBusData;
+
+static void
+launch_uris_with_dbus_data_free (LaunchUrisWithDBusData *data)
+{
+ g_clear_object (&data->info);
+ g_clear_object (&data->launch_context);
+ g_free (data->startup_id);
+
+ g_free (data);
+}
+
+static void
+launch_uris_with_dbus_signal_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ LaunchUrisWithDBusData *data = user_data;
+ GVariantBuilder builder;
+
+ if (data->launch_context)
+ {
+ if (g_task_had_error (G_TASK (result)))
+ g_app_launch_context_launch_failed (data->launch_context, data->startup_id);
+ else
+ {
+ GVariant *platform_data;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ /* the docs guarantee `pid` will be set, but we can’t
+ * easily know it for a D-Bus process, so set it to zero */
+ g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 (0));
+ if (data->startup_id)
+ g_variant_builder_add (&builder, "{sv}",
+ "startup-notification-id",
+ g_variant_new_string (data->startup_id));
+ platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
+ g_signal_emit_by_name (data->launch_context,
+ "launched",
+ data->info,
+ platform_data);
+ g_variant_unref (platform_data);
+ }
+ }
+
+ if (data->callback)
+ data->callback (object, result, data->user_data);
+
+ launch_uris_with_dbus_data_free (data);
+}
+
static void
launch_uris_with_dbus (GDesktopAppInfo *info,
GDBusConnection *session_bus,
@@ -2968,8 +3120,11 @@ launch_uris_with_dbus (GDesktopAppInfo *info,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GVariant *platform_data;
GVariantBuilder builder;
+ GVariantDict dict;
gchar *object_path;
+ LaunchUrisWithDBusData *data;
g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
@@ -2983,14 +3138,29 @@ launch_uris_with_dbus (GDesktopAppInfo *info,
g_variant_builder_close (&builder);
}
- g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
+ platform_data = g_desktop_app_info_make_platform_data (info, uris, launch_context);
+ g_variant_builder_add_value (&builder, platform_data);
object_path = object_path_from_appid (info->app_id);
+
+ data = g_new0 (LaunchUrisWithDBusData, 1);
+ data->info = g_object_ref (info);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->launch_context = launch_context ? g_object_ref (launch_context) : NULL;
+ g_variant_dict_init (&dict, platform_data);
+ g_variant_dict_lookup (&dict, "desktop-startup-id", "s", &data->startup_id);
+
+ if (launch_context)
+ emit_launch_started (launch_context, info, data->startup_id);
+
g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
uris ? "Open" : "Activate", g_variant_builder_end (&builder),
NULL, G_DBUS_CALL_FLAGS_NONE, -1,
- cancellable, callback, user_data);
+ cancellable, launch_uris_with_dbus_signal_cb, g_steal_pointer (&data));
g_free (object_path);
+
+ g_variant_dict_clear (&dict);
}
static gboolean
diff --git a/gio/gdrive.c b/gio/gdrive.c
index 32bdae879..c6c68dda9 100644
--- a/gio/gdrive.c
+++ b/gio/gdrive.c
@@ -613,7 +613,7 @@ g_drive_poll_for_media_finish (GDrive *drive,
*
* Gets the identifier of the given kind for @drive. The only
* identifier currently available is
- * #G_DRIVE_IDENTIFIER_KIND_UNIX_DEVICE.
+ * %G_DRIVE_IDENTIFIER_KIND_UNIX_DEVICE.
*
* Returns: (nullable) (transfer full): a newly allocated string containing the
* requested identifier, or %NULL if the #GDrive
diff --git a/gio/gdtlsconnection.c b/gio/gdtlsconnection.c
index 880d87d2c..1fd035c57 100644
--- a/gio/gdtlsconnection.c
+++ b/gio/gdtlsconnection.c
@@ -118,6 +118,19 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface)
* If no certificate database is set, then the default database will be
* used. See g_tls_backend_get_default_database().
*
+ * When using a non-default database, #GDtlsConnection must fall back to using
+ * the #GTlsDatabase to perform certificate verification using
+ * g_tls_database_verify_chain(), which means certificate verification will
+ * not be able to make use of TLS session context. This may be less secure.
+ * For example, if you create your own #GTlsDatabase that just wraps the
+ * default #GTlsDatabase, you might expect that you have not changed anything,
+ * but this is not true because you may have altered the behavior of
+ * #GDtlsConnection by causing it to use g_tls_database_verify_chain(). See the
+ * documentation of g_tls_database_verify_chain() for more details on specific
+ * security checks that may not be performed. Accordingly, setting a
+ * non-default database is discouraged except for specialty applications with
+ * unusual security requirements.
+ *
* Since: 2.48
*/
g_object_interface_install_property (iface,
@@ -223,6 +236,14 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface)
* #GDtlsConnection::accept-certificate overrode the default
* behavior.
*
+ * GLib guarantees that if certificate verification fails, at least
+ * one error will be set, but it does not guarantee that all possible
+ * errors will be set. Accordingly, you may not safely decide to
+ * ignore any particular type of error. For example, it would be
+ * incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if you want to allow
+ * expired certificates, because this could potentially be the only
+ * error flag set even if other problems exist with the certificate.
+ *
* Since: 2.48
*/
g_object_interface_install_property (iface,
@@ -314,6 +335,15 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface)
* signal handler. Otherwise, if no handler accepts the certificate,
* the handshake will fail with %G_TLS_ERROR_BAD_CERTIFICATE.
*
+ * GLib guarantees that if certificate verification fails, this signal
+ * will be emitted with at least one error will be set in @errors, but
+ * it does not guarantee that all possible errors will be set.
+ * Accordingly, you may not safely decide to ignore any particular
+ * type of error. For example, it would be incorrect to ignore
+ * %G_TLS_CERTIFICATE_EXPIRED if you want to allow expired
+ * certificates, because this could potentially be the only error flag
+ * set even if other problems exist with the certificate.
+ *
* For a server-side connection, @peer_cert is the certificate
* presented by the client, if this was requested via the server's
* #GDtlsServerConnection:authentication_mode. On the server side,
@@ -372,6 +402,9 @@ g_dtls_connection_default_init (GDtlsConnectionInterface *iface)
* client-side connections, unless that bit is not set in
* #GDtlsClientConnection:validation-flags).
*
+ * There are nonintuitive security implications when using a non-default
+ * database. See #GDtlsConnection:database for details.
+ *
* Since: 2.48
*/
void
diff --git a/gio/gfile.c b/gio/gfile.c
index d7feca6b9..1810e3682 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -247,6 +247,18 @@ static void g_file_real_trash_async (GFile
static gboolean g_file_real_trash_finish (GFile *file,
GAsyncResult *res,
GError **error);
+static void g_file_real_move_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static gboolean g_file_real_move_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error);
static void g_file_real_make_directory_async (GFile *file,
int io_priority,
GCancellable *cancellable,
@@ -381,6 +393,8 @@ g_file_default_init (GFileIface *iface)
iface->delete_file_finish = g_file_real_delete_finish;
iface->trash_async = g_file_real_trash_async;
iface->trash_finish = g_file_real_trash_finish;
+ iface->move_async = g_file_real_move_async;
+ iface->move_finish = g_file_real_move_finish;
iface->make_directory_async = g_file_real_make_directory_async;
iface->make_directory_finish = g_file_real_make_directory_finish;
iface->open_readwrite_async = g_file_real_open_readwrite_async;
@@ -441,8 +455,8 @@ g_file_is_native (GFile *file)
* This call does no blocking I/O.
*
* Returns: %TRUE if #GFile's backend supports the
- * given URI scheme, %FALSE if URI scheme is %NULL,
- * not supported, or #GFile is invalid.
+ * given URI scheme, %FALSE if URI scheme is %NULL,
+ * not supported, or #GFile is invalid.
*/
gboolean
g_file_has_uri_scheme (GFile *file,
@@ -476,8 +490,8 @@ g_file_has_uri_scheme (GFile *file,
* This call does no blocking I/O.
*
* Returns: (nullable): a string containing the URI scheme for the given
- * #GFile or %NULL if the #GFile was constructed with an invalid URI. The
- * returned string should be freed with g_free() when no longer needed.
+ * #GFile or %NULL if the #GFile was constructed with an invalid URI. The
+ * returned string should be freed with g_free() when no longer needed.
*/
char *
g_file_get_uri_scheme (GFile *file)
@@ -511,8 +525,8 @@ g_file_get_uri_scheme (GFile *file)
* This call does no blocking I/O.
*
* Returns: (type filename) (nullable): string containing the #GFile's
- * base name, or %NULL if given #GFile is invalid. The returned string
- * should be freed with g_free() when no longer needed.
+ * base name, or %NULL if given #GFile is invalid. The returned string
+ * should be freed with g_free() when no longer needed.
*/
char *
g_file_get_basename (GFile *file)
@@ -536,8 +550,8 @@ g_file_get_basename (GFile *file)
* This call does no blocking I/O.
*
* Returns: (type filename) (nullable): string containing the #GFile's path,
- * or %NULL if no such path exists. The returned string should be freed
- * with g_free() when no longer needed.
+ * or %NULL if no such path exists. The returned string should be freed
+ * with g_free() when no longer needed.
*/
char *
g_file_get_path (GFile *file)
@@ -609,7 +623,7 @@ file_peek_path_generic (GFile *file)
* This call does no blocking I/O.
*
* Returns: (type filename) (nullable): string containing the #GFile's path,
- * or %NULL if no such path exists. The returned string is owned by @file.
+ * or %NULL if no such path exists. The returned string is owned by @file.
* Since: 2.56
*/
const char *
@@ -629,9 +643,9 @@ g_file_peek_path (GFile *file)
* This call does no blocking I/O.
*
* Returns: a string containing the #GFile's URI. If the #GFile was constructed
- * with an invalid URI, an invalid URI is returned.
- * The returned string should be freed with g_free()
- * when no longer needed.
+ * with an invalid URI, an invalid URI is returned.
+ * The returned string should be freed with g_free()
+ * when no longer needed.
*/
char *
g_file_get_uri (GFile *file)
@@ -665,8 +679,8 @@ g_file_get_uri (GFile *file)
* This call does no blocking I/O.
*
* Returns: a string containing the #GFile's parse name.
- * The returned string should be freed with g_free()
- * when no longer needed.
+ * The returned string should be freed with g_free()
+ * when no longer needed.
*/
char *
g_file_get_parse_name (GFile *file)
@@ -696,7 +710,7 @@ g_file_get_parse_name (GFile *file)
* This call does no blocking I/O.
*
* Returns: (transfer full): a new #GFile that is a duplicate
- * of the given #GFile.
+ * of the given #GFile.
*/
GFile *
g_file_dup (GFile *file)
@@ -720,9 +734,9 @@ g_file_dup (GFile *file)
*
* Virtual: hash
* Returns: 0 if @file is not a valid #GFile, otherwise an
- * integer that can be used as hash value for the #GFile.
- * This function is intended for easily hashing a #GFile to
- * add to a #GHashTable or similar data structure.
+ * integer that can be used as hash value for the #GFile.
+ * This function is intended for easily hashing a #GFile to
+ * add to a #GHashTable or similar data structure.
*/
guint
g_file_hash (gconstpointer file)
@@ -783,8 +797,8 @@ g_file_equal (GFile *file1,
* This call does no blocking I/O.
*
* Returns: (nullable) (transfer full): a #GFile structure to the
- * parent of the given #GFile or %NULL if there is no parent. Free
- * the returned object with g_object_unref().
+ * parent of the given #GFile or %NULL if there is no parent. Free
+ * the returned object with g_object_unref().
*/
GFile *
g_file_get_parent (GFile *file)
@@ -810,7 +824,7 @@ g_file_get_parent (GFile *file)
* if @file is an immediate child of @parent.
*
* Returns: %TRUE if @file is an immediate child of @parent (or any parent in
- * the case that @parent is %NULL).
+ * the case that @parent is %NULL).
*
* Since: 2.24
*/
@@ -855,7 +869,7 @@ g_file_has_parent (GFile *file,
* This call does no blocking I/O.
*
* Returns: (transfer full): a #GFile to a child specified by @name.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_get_child (GFile *file,
@@ -863,6 +877,7 @@ g_file_get_child (GFile *file,
{
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (!g_path_is_absolute (name), NULL);
return g_file_resolve_relative_path (file, name);
}
@@ -883,8 +898,8 @@ g_file_get_child (GFile *file,
* This call does no blocking I/O.
*
* Returns: (transfer full): a #GFile to the specified child, or
- * %NULL if the display name couldn't be converted.
- * Free the returned object with g_object_unref().
+ * %NULL if the display name couldn't be converted.
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_get_child_for_display_name (GFile *file,
@@ -923,7 +938,7 @@ g_file_get_child_for_display_name (GFile *file,
*
* Virtual: prefix_matches
* Returns: %TRUE if the @file's parent, grandparent, etc is @prefix,
- * %FALSE otherwise.
+ * %FALSE otherwise.
*/
gboolean
g_file_has_prefix (GFile *file,
@@ -955,9 +970,9 @@ g_file_has_prefix (GFile *file,
* This call does no blocking I/O.
*
* Returns: (type filename) (nullable): string with the relative path from
- * @descendant to @parent, or %NULL if @descendant doesn't have @parent as
- * prefix. The returned string should be freed with g_free() when
- * no longer needed.
+ * @descendant to @parent, or %NULL if @descendant doesn't have @parent as
+ * prefix. The returned string should be freed with g_free() when
+ * no longer needed.
*/
char *
g_file_get_relative_path (GFile *parent,
@@ -985,9 +1000,10 @@ g_file_get_relative_path (GFile *parent,
*
* This call does no blocking I/O.
*
- * Returns: (transfer full): #GFile to the resolved path.
- * %NULL if @relative_path is %NULL or if @file is invalid.
- * Free the returned object with g_object_unref().
+ * If the @relative_path is an absolute path name, the resolution
+ * is done absolutely (without taking @file path as base).
+ *
+ * Returns: (transfer full): a #GFile for the resolved path.
*/
GFile *
g_file_resolve_relative_path (GFile *file,
@@ -1009,7 +1025,7 @@ g_file_resolve_relative_path (GFile *file,
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: #GError for error reporting
*
* Gets the requested information about the files in a directory.
@@ -1025,7 +1041,9 @@ g_file_resolve_relative_path (GFile *file,
* "standard::*" means all attributes in the standard namespace.
* An example attribute query be "standard::*,owner::user".
* The standard attributes are available as defines, like
- * #G_FILE_ATTRIBUTE_STANDARD_NAME.
+ * %G_FILE_ATTRIBUTE_STANDARD_NAME. %G_FILE_ATTRIBUTE_STANDARD_NAME should
+ * always be specified if you plan to call g_file_enumerator_get_child() or
+ * g_file_enumerator_iterate() on the returned enumerator.
*
* If @cancellable is not %NULL, then the operation can be cancelled
* by triggering the cancellable object from another thread. If the
@@ -1037,7 +1055,7 @@ g_file_resolve_relative_path (GFile *file,
* error will be returned. Other errors are possible too.
*
* Returns: (transfer full): A #GFileEnumerator if successful,
- * %NULL on error. Free the returned object with g_object_unref().
+ * %NULL on error. Free the returned object with g_object_unref().
*/
GFileEnumerator *
g_file_enumerate_children (GFile *file,
@@ -1074,9 +1092,9 @@ g_file_enumerate_children (GFile *file,
* @flags: a set of #GFileQueryInfoFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call when the
- * request is satisfied
+ * request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously gets the requested information about the files
@@ -1123,8 +1141,8 @@ g_file_enumerate_children_async (GFile *file,
* See g_file_enumerate_children_async().
*
* Returns: (transfer full): a #GFileEnumerator or %NULL
- * if an error occurred.
- * Free the returned object with g_object_unref().
+ * if an error occurred.
+ * Free the returned object with g_object_unref().
*/
GFileEnumerator *
g_file_enumerate_children_finish (GFile *file,
@@ -1147,7 +1165,7 @@ g_file_enumerate_children_finish (GFile *file,
* g_file_query_exists:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
*
* Utility function to check if a particular file exists. This is
* implemented using g_file_query_info() and as such does blocking I/O.
@@ -1173,7 +1191,7 @@ g_file_enumerate_children_finish (GFile *file,
* that can happen due to races when you execute the operation.
*
* Returns: %TRUE if the file exists (and can be detected without error),
- * %FALSE otherwise (or if cancelled).
+ * %FALSE otherwise (or if cancelled).
*/
gboolean
g_file_query_exists (GFile *file,
@@ -1199,7 +1217,7 @@ g_file_query_exists (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileQueryInfoFlags passed to g_file_query_info()
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
*
* Utility function to inspect the #GFileType of a file. This is
* implemented using g_file_query_info() and as such does blocking I/O.
@@ -1207,8 +1225,8 @@ g_file_query_exists (GFile *file,
* The primary use case of this method is to check if a file is
* a regular file, directory, or symlink.
*
- * Returns: The #GFileType of the file and #G_FILE_TYPE_UNKNOWN
- * if the file does not exist
+ * Returns: The #GFileType of the file and %G_FILE_TYPE_UNKNOWN
+ * if the file does not exist
*
* Since: 2.18
*/
@@ -1240,7 +1258,7 @@ g_file_query_file_type (GFile *file,
* @attributes: an attribute query string
* @flags: a set of #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError
*
* Gets the requested information about specified @file.
@@ -1256,7 +1274,7 @@ g_file_query_file_type (GFile *file,
* "standard::*" means all attributes in the standard namespace.
* An example attribute query be "standard::*,owner::user".
* The standard attributes are available as defines, like
- * #G_FILE_ATTRIBUTE_STANDARD_NAME.
+ * %G_FILE_ATTRIBUTE_STANDARD_NAME.
*
* If @cancellable is not %NULL, then the operation can be cancelled
* by triggering the cancellable object from another thread. If the
@@ -1265,7 +1283,7 @@ g_file_query_file_type (GFile *file,
*
* For symlinks, normally the information about the target of the
* symlink is returned, rather than information about the symlink
- * itself. However if you pass #G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
+ * itself. However if you pass %G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
* in @flags the information about the symlink itself will be returned.
* Also, for symlinks that point to non-existing files the information
* about the symlink itself will be returned.
@@ -1275,7 +1293,7 @@ g_file_query_file_type (GFile *file,
* filesystem the file is on.
*
* Returns: (transfer full): a #GFileInfo for the given @file, or %NULL
- * on error. Free the returned object with g_object_unref().
+ * on error. Free the returned object with g_object_unref().
*/
GFileInfo *
g_file_query_info (GFile *file,
@@ -1311,9 +1329,9 @@ g_file_query_info (GFile *file,
* @flags: a set of #GFileQueryInfoFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call when the
- * request is satisfied
+ * request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously gets the requested information about specified @file.
@@ -1359,8 +1377,8 @@ g_file_query_info_async (GFile *file,
* See g_file_query_info_async().
*
* Returns: (transfer full): #GFileInfo for given @file
- * or %NULL on error. Free the returned object with
- * g_object_unref().
+ * or %NULL on error. Free the returned object with
+ * g_object_unref().
*/
GFileInfo *
g_file_query_info_finish (GFile *file,
@@ -1384,7 +1402,7 @@ g_file_query_info_finish (GFile *file,
* @file: input #GFile
* @attributes: an attribute query string
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError
*
* Similar to g_file_query_info(), but obtains information
@@ -1400,9 +1418,9 @@ g_file_query_info_finish (GFile *file,
* attributes, and a wildcard like "filesystem::*" means all attributes
* in the filesystem namespace. The standard namespace for filesystem
* attributes is "filesystem". Common attributes of interest are
- * #G_FILE_ATTRIBUTE_FILESYSTEM_SIZE (the total size of the filesystem
- * in bytes), #G_FILE_ATTRIBUTE_FILESYSTEM_FREE (number of bytes available),
- * and #G_FILE_ATTRIBUTE_FILESYSTEM_TYPE (type of the filesystem).
+ * %G_FILE_ATTRIBUTE_FILESYSTEM_SIZE (the total size of the filesystem
+ * in bytes), %G_FILE_ATTRIBUTE_FILESYSTEM_FREE (number of bytes available),
+ * and %G_FILE_ATTRIBUTE_FILESYSTEM_TYPE (type of the filesystem).
*
* If @cancellable is not %NULL, then the operation can be cancelled
* by triggering the cancellable object from another thread. If the
@@ -1414,7 +1432,7 @@ g_file_query_info_finish (GFile *file,
* kind of filesystem the file is on.
*
* Returns: (transfer full): a #GFileInfo or %NULL if there was an error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileInfo *
g_file_query_filesystem_info (GFile *file,
@@ -1448,9 +1466,9 @@ g_file_query_filesystem_info (GFile *file,
* @attributes: an attribute query string
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously gets the requested information about the filesystem
@@ -1496,8 +1514,8 @@ g_file_query_filesystem_info_async (GFile *file,
* See g_file_query_filesystem_info_async().
*
* Returns: (transfer full): #GFileInfo for given @file
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GFileInfo *
g_file_query_filesystem_info_finish (GFile *file,
@@ -1520,7 +1538,7 @@ g_file_query_filesystem_info_finish (GFile *file,
* g_file_find_enclosing_mount:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError
*
* Gets a #GMount for the #GFile.
@@ -1534,8 +1552,8 @@ g_file_query_filesystem_info_finish (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: (transfer full): a #GMount where the @file is located
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GMount *
g_file_find_enclosing_mount (GFile *file,
@@ -1570,9 +1588,9 @@ g_file_find_enclosing_mount (GFile *file,
* @file: a #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously gets the mount for the file.
@@ -1613,7 +1631,7 @@ g_file_find_enclosing_mount_async (GFile *file,
* See g_file_find_enclosing_mount_async().
*
* Returns: (transfer full): #GMount for given @file or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GMount *
g_file_find_enclosing_mount_finish (GFile *file,
@@ -1653,7 +1671,7 @@ g_file_find_enclosing_mount_finish (GFile *file,
*
* Virtual: read_fn
* Returns: (transfer full): #GFileInputStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileInputStream *
g_file_read (GFile *file,
@@ -1685,14 +1703,14 @@ g_file_read (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Gets an output stream for appending data to the file.
* If the file doesn't already exist it is created.
*
* By default files created are generally readable by everyone,
- * but if you pass #G_FILE_CREATE_PRIVATE in @flags the file
+ * but if you pass %G_FILE_CREATE_PRIVATE in @flags the file
* will be made readable only to the current user, to the level that
* is supported on the target filesystem.
*
@@ -1707,7 +1725,7 @@ g_file_read (GFile *file,
* possible too, and depend on what kind of filesystem the file is on.
*
* Returns: (transfer full): a #GFileOutputStream, or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_append_to (GFile *file,
@@ -1740,14 +1758,14 @@ g_file_append_to (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileCreateFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Creates a new file and returns an output stream for writing to it.
* The file must not already exist.
*
* By default files created are generally readable by everyone,
- * but if you pass #G_FILE_CREATE_PRIVATE in @flags the file
+ * but if you pass %G_FILE_CREATE_PRIVATE in @flags the file
* will be made readable only to the current user, to the level
* that is supported on the target filesystem.
*
@@ -1764,8 +1782,8 @@ g_file_append_to (GFile *file,
* of filesystem the file is on.
*
* Returns: (transfer full): a #GFileOutputStream for the newly created
- * file, or %NULL on error.
- * Free the returned object with g_object_unref().
+ * file, or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_create (GFile *file,
@@ -1797,11 +1815,11 @@ g_file_create (GFile *file,
* g_file_replace:
* @file: input #GFile
* @etag: (nullable): an optional [entity tag][gfile-etag]
- * for the current #GFile, or #NULL to ignore
+ * for the current #GFile, or #NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Returns an output stream for overwriting the file, possibly
@@ -1815,7 +1833,7 @@ g_file_create (GFile *file,
* the destination when the stream is closed.
*
* By default files created are generally readable by everyone,
- * but if you pass #G_FILE_CREATE_PRIVATE in @flags the file
+ * but if you pass %G_FILE_CREATE_PRIVATE in @flags the file
* will be made readable only to the current user, to the level that
* is supported on the target filesystem.
*
@@ -1847,7 +1865,7 @@ g_file_create (GFile *file,
* possible too, and depend on what kind of filesystem the file is on.
*
* Returns: (transfer full): a #GFileOutputStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_replace (GFile *file,
@@ -1905,7 +1923,7 @@ g_file_replace (GFile *file,
* for reading or writing.
*
* Returns: (transfer full): #GFileIOStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -1939,14 +1957,14 @@ g_file_open_readwrite (GFile *file,
* @file: a #GFile
* @flags: a set of #GFileCreateFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: return location for a #GError, or %NULL
*
* Creates a new file and returns a stream for reading and
* writing to it. The file must not already exist.
*
* By default files created are generally readable by everyone,
- * but if you pass #G_FILE_CREATE_PRIVATE in @flags the file
+ * but if you pass %G_FILE_CREATE_PRIVATE in @flags the file
* will be made readable only to the current user, to the level
* that is supported on the target filesystem.
*
@@ -1967,8 +1985,8 @@ g_file_open_readwrite (GFile *file,
* streaming, rather than just opening for reading or writing.
*
* Returns: (transfer full): a #GFileIOStream for the newly created
- * file, or %NULL on error.
- * Free the returned object with g_object_unref().
+ * file, or %NULL on error.
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -2002,11 +2020,11 @@ g_file_create_readwrite (GFile *file,
* g_file_replace_readwrite:
* @file: a #GFile
* @etag: (nullable): an optional [entity tag][gfile-etag]
- * for the current #GFile, or #NULL to ignore
+ * for the current #GFile, or #NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: return location for a #GError, or %NULL
*
* Returns an output stream for overwriting the file in readwrite mode,
@@ -2021,7 +2039,7 @@ g_file_create_readwrite (GFile *file,
* rather than just opening for reading or writing.
*
* Returns: (transfer full): a #GFileIOStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -2058,9 +2076,9 @@ g_file_replace_readwrite (GFile *file,
* @file: input #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously opens @file for reading.
@@ -2101,7 +2119,7 @@ g_file_read_async (GFile *file,
* g_file_read_async().
*
* Returns: (transfer full): a #GFileInputStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileInputStream *
g_file_read_finish (GFile *file,
@@ -2126,9 +2144,9 @@ g_file_read_finish (GFile *file,
* @flags: a set of #GFileCreateFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously opens @file for appending.
@@ -2171,8 +2189,8 @@ g_file_append_to_async (GFile *file,
* g_file_append_to_async().
*
* Returns: (transfer full): a valid #GFileOutputStream
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_append_to_finish (GFile *file,
@@ -2197,9 +2215,9 @@ g_file_append_to_finish (GFile *file,
* @flags: a set of #GFileCreateFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously creates a new file and returns an output stream
@@ -2243,7 +2261,7 @@ g_file_create_async (GFile *file,
* g_file_create_async().
*
* Returns: (transfer full): a #GFileOutputStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_create_finish (GFile *file,
@@ -2266,14 +2284,14 @@ g_file_create_finish (GFile *file,
* g_file_replace_async:
* @file: input #GFile
* @etag: (nullable): an [entity tag][gfile-etag] for the current #GFile,
- * or %NULL to ignore
+ * or %NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously overwrites the file, replacing the contents,
@@ -2321,7 +2339,7 @@ g_file_replace_async (GFile *file,
* g_file_replace_async().
*
* Returns: (transfer full): a #GFileOutputStream, or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFileOutputStream *
g_file_replace_finish (GFile *file,
@@ -2345,9 +2363,9 @@ g_file_replace_finish (GFile *file,
* @file: input #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously opens @file for reading and writing.
@@ -2390,7 +2408,7 @@ g_file_open_readwrite_async (GFile *file,
* g_file_open_readwrite_async().
*
* Returns: (transfer full): a #GFileIOStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -2417,9 +2435,9 @@ g_file_open_readwrite_finish (GFile *file,
* @flags: a set of #GFileCreateFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously creates a new file and returns a stream
@@ -2465,7 +2483,7 @@ g_file_create_readwrite_async (GFile *file,
* g_file_create_readwrite_async().
*
* Returns: (transfer full): a #GFileIOStream or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -2490,14 +2508,14 @@ g_file_create_readwrite_finish (GFile *file,
* g_file_replace_readwrite_async:
* @file: input #GFile
* @etag: (nullable): an [entity tag][gfile-etag] for the current #GFile,
- * or %NULL to ignore
+ * or %NULL to ignore
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously overwrites the file in read-write mode,
@@ -2548,7 +2566,7 @@ g_file_replace_readwrite_async (GFile *file,
* g_file_replace_readwrite_async().
*
* Returns: (transfer full): a #GFileIOStream, or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.22
*/
@@ -2718,7 +2736,7 @@ should_copy (GFileAttributeInfo *info,
* @file: a #GFile to copy attributes to
* @flags: a set of #GFileCopyFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, %NULL to ignore
*
* Prepares the file attribute query string for copying to @file.
@@ -2732,7 +2750,7 @@ should_copy (GFileAttributeInfo *info,
* stages (e.g., for recursive move of a directory).
*
* Returns: an attribute query string for g_file_query_info(),
- * or %NULL if an error occurs.
+ * or %NULL if an error occurs.
*
* Since: 2.68
*/
@@ -2826,7 +2844,7 @@ g_file_build_attribute_list_for_copy (GFile *file,
* @destination: a #GFile to copy attributes to
* @flags: a set of #GFileCopyFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, %NULL to ignore
*
* Copies the file attributes from @source to @destination.
@@ -2834,12 +2852,12 @@ g_file_build_attribute_list_for_copy (GFile *file,
* Normally only a subset of the file attributes are copied,
* those that are copies in a normal file copy operation
* (which for instance does not include e.g. owner). However
- * if #G_FILE_COPY_ALL_METADATA is specified in @flags, then
+ * if %G_FILE_COPY_ALL_METADATA is specified in @flags, then
* all the metadata that is possible to copy is copied. This
* is useful when implementing move by copy + delete source.
*
* Returns: %TRUE if the attributes were copied successfully,
- * %FALSE otherwise.
+ * %FALSE otherwise.
*/
gboolean
g_file_copy_attributes (GFile *source,
@@ -3428,23 +3446,23 @@ file_copy_fallback (GFile *source,
* @destination: destination #GFile
* @flags: set of #GFileCopyFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @progress_callback: (nullable) (scope call): function to callback with
- * progress information, or %NULL if progress information is not needed
+ * progress information, or %NULL if progress information is not needed
* @progress_callback_data: (closure): user data to pass to @progress_callback
* @error: #GError to set on error, or %NULL
*
* Copies the file @source to the location specified by @destination.
* Can not handle recursive copies of directories.
*
- * If the flag #G_FILE_COPY_OVERWRITE is specified an already
+ * If the flag %G_FILE_COPY_OVERWRITE is specified an already
* existing @destination file is overwritten.
*
- * If the flag #G_FILE_COPY_NOFOLLOW_SYMLINKS is specified then symlinks
+ * If the flag %G_FILE_COPY_NOFOLLOW_SYMLINKS is specified then symlinks
* will be copied as symlinks, otherwise the target of the
* @source symlink will be copied.
*
- * If the flag #G_FILE_COPY_ALL_METADATA is specified then all the metadata
+ * If the flag %G_FILE_COPY_ALL_METADATA is specified then all the metadata
* that is possible to copy is copied, not just the default subset (which,
* for instance, does not include the owner, see #GFileInfo).
*
@@ -3461,7 +3479,7 @@ file_copy_fallback (GFile *source,
* If the @source file does not exist, then the %G_IO_ERROR_NOT_FOUND error
* is returned, independent on the status of the @destination.
*
- * If #G_FILE_COPY_OVERWRITE is not specified and the target exists, then
+ * If %G_FILE_COPY_OVERWRITE is not specified and the target exists, then
* the error %G_IO_ERROR_EXISTS is returned.
*
* If trying to overwrite a file over a directory, the %G_IO_ERROR_IS_DIRECTORY
@@ -3469,7 +3487,7 @@ file_copy_fallback (GFile *source,
* %G_IO_ERROR_WOULD_MERGE error is returned.
*
* If the source is a directory and the target does not exist, or
- * #G_FILE_COPY_OVERWRITE is specified and the target is a file, then the
+ * %G_FILE_COPY_OVERWRITE is specified and the target is a file, then the
* %G_IO_ERROR_WOULD_RECURSE error is returned.
*
* If you are interested in copying the #GFile object itself (not the on-disk
@@ -3557,9 +3575,9 @@ g_file_copy (GFile *source,
* @flags: set of #GFileCopyFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @progress_callback: (nullable) (scope notified): function to callback with progress
- * information, or %NULL if progress information is not needed
+ * information, or %NULL if progress information is not needed
* @progress_callback_data: (closure progress_callback) (nullable): user data to pass to @progress_callback
* @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: (closure callback): the data to pass to callback function
@@ -3636,11 +3654,11 @@ g_file_copy_finish (GFile *file,
* @destination: #GFile pointing to the destination location
* @flags: set of #GFileCopyFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @progress_callback: (nullable) (scope call): #GFileProgressCallback
- * function for updates
+ * function for updates
* @progress_callback_data: (closure): gpointer to user data for
- * the callback function
+ * the callback function
* @error: #GError for returning error conditions, or %NULL
*
* Tries to move the file or directory @source to the location specified
@@ -3649,7 +3667,7 @@ g_file_copy_finish (GFile *file,
* implementation may support moving directories (for instance on moves
* inside the same filesystem), but the fallback code does not.
*
- * If the flag #G_FILE_COPY_OVERWRITE is specified an already
+ * If the flag %G_FILE_COPY_OVERWRITE is specified an already
* existing @destination file is overwritten.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
@@ -3665,7 +3683,7 @@ g_file_copy_finish (GFile *file,
* If the @source file does not exist, then the %G_IO_ERROR_NOT_FOUND
* error is returned, independent on the status of the @destination.
*
- * If #G_FILE_COPY_OVERWRITE is not specified and the target exists,
+ * If %G_FILE_COPY_OVERWRITE is not specified and the target exists,
* then the error %G_IO_ERROR_EXISTS is returned.
*
* If trying to overwrite a file over a directory, the %G_IO_ERROR_IS_DIRECTORY
@@ -3673,7 +3691,7 @@ g_file_copy_finish (GFile *file,
* %G_IO_ERROR_WOULD_MERGE error is returned.
*
* If the source is a directory and the target does not exist, or
- * #G_FILE_COPY_OVERWRITE is specified and the target is a file, then
+ * %G_FILE_COPY_OVERWRITE is specified and the target is a file, then
* the %G_IO_ERROR_WOULD_RECURSE error may be returned (if the native
* move operation isn't available).
*
@@ -3765,10 +3783,95 @@ g_file_move (GFile *source,
}
/**
+ * g_file_move_async:
+ * @source: #GFile pointing to the source location
+ * @destination: #GFile pointing to the destination location
+ * @flags: set of #GFileCopyFlags
+ * @io_priority: the [I/O priority][io-priority] of the request
+ * @cancellable: (nullable): optional #GCancellable object,
+ * %NULL to ignore
+ * @progress_callback: (nullable) (scope call): #GFileProgressCallback
+ * function for updates
+ * @progress_callback_data: (closure): gpointer to user data for
+ * the callback function
+ * @callback: a #GAsyncReadyCallback to call
+ * when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously moves a file @source to the location of @destination. For details of the behaviour, see g_file_move().
+ *
+ * If @progress_callback is not %NULL, then that function that will be called
+ * just like in g_file_move(). The callback will run in the default main context
+ * of the thread calling g_file_move_async() — the same context as @callback is
+ * run in.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * g_file_move_finish() to get the result of the operation.
+ *
+ * Since: 2.72
+ */
+void
+g_file_move_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GFileIface *iface;
+
+ g_return_if_fail (G_IS_FILE (source));
+ g_return_if_fail (G_IS_FILE (destination));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ iface = G_FILE_GET_IFACE (source);
+ (* iface->move_async) (source,
+ destination,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+}
+
+/**
+ * g_file_move_finish:
+ * @file: input source #GFile
+ * @result: a #GAsyncResult
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous file movement, started with
+ * g_file_move_async().
+ *
+ * Returns: %TRUE on successful file move, %FALSE otherwise.
+ *
+ * Since: 2.72
+ */
+gboolean
+g_file_move_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ GFileIface *iface;
+
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ iface = G_FILE_GET_IFACE (file);
+ return (* iface->move_finish) (file, result, error);
+}
+
+/**
* g_file_make_directory:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Creates a directory. Note that this will only create a child directory
@@ -3818,9 +3921,9 @@ g_file_make_directory (GFile *file,
* @file: input #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: the data to pass to callback function
*
* Asynchronously creates a directory.
@@ -3878,7 +3981,7 @@ g_file_make_directory_finish (GFile *file,
* g_file_make_directory_with_parents:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Creates a directory and any parent directories that may not
@@ -4000,9 +4103,9 @@ g_file_make_directory_with_parents (GFile *file,
* g_file_make_symbolic_link:
* @file: a #GFile with the name of the symlink to create
* @symlink_value: (type filename): a string with the path for the target
- * of the new symlink
+ * of the new symlink
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError
*
* Creates a symbolic link named @file which contains the string
@@ -4053,7 +4156,7 @@ g_file_make_symbolic_link (GFile *file,
* g_file_delete:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Deletes a file. If the @file is a directory, it will only be
@@ -4111,9 +4214,9 @@ g_file_delete (GFile *file,
* @file: input #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: the data to pass to callback function
*
* Asynchronously delete a file. If the @file is a directory, it will
@@ -4175,7 +4278,7 @@ g_file_delete_finish (GFile *file,
* g_file_trash:
* @file: #GFile to send to trash
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sends @file to the "Trashcan", if possible. This is similar to
@@ -4222,9 +4325,9 @@ g_file_trash (GFile *file,
* @file: input #GFile
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: the data to pass to callback function
*
* Asynchronously sends @file to the Trash location, if possible.
@@ -4283,7 +4386,7 @@ g_file_trash_finish (GFile *file,
* @file: input #GFile
* @display_name: a string
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Renames @file to the specified display name.
@@ -4292,7 +4395,7 @@ g_file_trash_finish (GFile *file,
* for the target filesystem if possible and the @file is renamed to this.
*
* If you want to implement a rename operation in the user interface the
- * edit name (#G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME) should be used as the
+ * edit name (%G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME) should be used as the
* initial value in the rename widget, and then the result after editing
* should be passed to g_file_set_display_name().
*
@@ -4303,8 +4406,8 @@ g_file_trash_finish (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: (transfer full): a #GFile specifying what @file was renamed to,
- * or %NULL if there was an error.
- * Free the returned object with g_object_unref().
+ * or %NULL if there was an error.
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_set_display_name (GFile *file,
@@ -4340,9 +4443,9 @@ g_file_set_display_name (GFile *file,
* @display_name: a string
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously sets the display name for a given #GFile.
@@ -4386,7 +4489,7 @@ g_file_set_display_name_async (GFile *file,
* g_file_set_display_name_async().
*
* Returns: (transfer full): a #GFile or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_set_display_name_finish (GFile *file,
@@ -4409,7 +4512,7 @@ g_file_set_display_name_finish (GFile *file,
* g_file_query_settable_attributes:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Obtain the list of settable attributes for the file.
@@ -4423,9 +4526,9 @@ g_file_set_display_name_finish (GFile *file,
* triggering the cancellable object from another thread. If the operation
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
- * Returns: a #GFileAttributeInfoList describing the settable attributes.
- * When you are done with it, release it with
- * g_file_attribute_info_list_unref()
+ * Returns: (transfer full): a #GFileAttributeInfoList describing the settable attributes.
+ * When you are done with it, release it with
+ * g_file_attribute_info_list_unref()
*/
GFileAttributeInfoList *
g_file_query_settable_attributes (GFile *file,
@@ -4467,7 +4570,7 @@ g_file_query_settable_attributes (GFile *file,
* g_file_query_writable_namespaces:
* @file: input #GFile
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Obtain the list of attribute namespaces where new attributes
@@ -4478,9 +4581,9 @@ g_file_query_settable_attributes (GFile *file,
* triggering the cancellable object from another thread. If the operation
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
- * Returns: a #GFileAttributeInfoList describing the writable namespaces.
- * When you are done with it, release it with
- * g_file_attribute_info_list_unref()
+ * Returns: (transfer full): a #GFileAttributeInfoList describing the writable namespaces.
+ * When you are done with it, release it with
+ * g_file_attribute_info_list_unref()
*/
GFileAttributeInfoList *
g_file_query_writable_namespaces (GFile *file,
@@ -4529,10 +4632,10 @@ g_file_query_writable_namespaces (GFile *file,
* @attribute: a string containing the attribute's name
* @type: The type of the attribute
* @value_p: (nullable): a pointer to the value (or the pointer
- * itself if the type is a pointer type)
+ * itself if the type is a pointer type)
* @flags: a set of #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets an attribute in the file with attribute name @attribute to @value_p.
@@ -4582,7 +4685,7 @@ g_file_set_attribute (GFile *file,
* @info: a #GFileInfo
* @flags: #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Tries to set all attributes in the #GFileInfo on the target
@@ -4674,7 +4777,7 @@ g_file_real_set_attributes_from_info (GFile *file,
* @flags: a #GFileQueryInfoFlags
* @io_priority: the [I/O priority][io-priority] of the request
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback
* @user_data: (closure): a #gpointer
*
@@ -4747,7 +4850,7 @@ g_file_set_attributes_finish (GFile *file,
* @value: a string containing the attribute's value
* @flags: #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_STRING to @value.
@@ -4779,7 +4882,7 @@ g_file_set_attribute_string (GFile *file,
* @value: a string containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_BYTE_STRING to @value.
@@ -4791,7 +4894,7 @@ g_file_set_attribute_string (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: %TRUE if the @attribute was successfully set to @value
- * in the @file, %FALSE otherwise.
+ * in the @file, %FALSE otherwise.
*/
gboolean
g_file_set_attribute_byte_string (GFile *file,
@@ -4813,7 +4916,7 @@ g_file_set_attribute_byte_string (GFile *file,
* @value: a #guint32 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_UINT32 to @value.
@@ -4824,7 +4927,7 @@ g_file_set_attribute_byte_string (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: %TRUE if the @attribute was successfully set to @value
- * in the @file, %FALSE otherwise.
+ * in the @file, %FALSE otherwise.
*/
gboolean
g_file_set_attribute_uint32 (GFile *file,
@@ -4846,7 +4949,7 @@ g_file_set_attribute_uint32 (GFile *file,
* @value: a #gint32 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_INT32 to @value.
@@ -4857,7 +4960,7 @@ g_file_set_attribute_uint32 (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: %TRUE if the @attribute was successfully set to @value
- * in the @file, %FALSE otherwise.
+ * in the @file, %FALSE otherwise.
*/
gboolean
g_file_set_attribute_int32 (GFile *file,
@@ -4879,7 +4982,7 @@ g_file_set_attribute_int32 (GFile *file,
* @value: a #guint64 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_UINT64 to @value.
@@ -4890,7 +4993,7 @@ g_file_set_attribute_int32 (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: %TRUE if the @attribute was successfully set to @value
- * in the @file, %FALSE otherwise.
+ * in the @file, %FALSE otherwise.
*/
gboolean
g_file_set_attribute_uint64 (GFile *file,
@@ -4912,7 +5015,7 @@ g_file_set_attribute_uint64 (GFile *file,
* @value: a #guint64 containing the attribute's new value
* @flags: a #GFileQueryInfoFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Sets @attribute of type %G_FILE_ATTRIBUTE_TYPE_INT64 to @value.
@@ -4942,11 +5045,11 @@ g_file_set_attribute_int64 (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @mount_operation: (nullable): a #GMountOperation,
- * or %NULL to avoid user interaction
+ * or %NULL to avoid user interaction
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* Mounts a file of type G_FILE_TYPE_MOUNTABLE.
@@ -5004,7 +5107,7 @@ g_file_mount_mountable (GFile *file,
* with g_file_mount_mountable().
*
* Returns: (transfer full): a #GFile or %NULL on error.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_mount_mountable_finish (GFile *file,
@@ -5030,9 +5133,9 @@ g_file_mount_mountable_finish (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* Unmounts a file of type G_FILE_TYPE_MOUNTABLE.
@@ -5088,10 +5191,10 @@ g_file_unmount_mountable (GFile *file,
* with g_file_unmount_mountable().
*
* Returns: %TRUE if the operation finished successfully.
- * %FALSE otherwise.
+ * %FALSE otherwise.
*
* Deprecated: 2.22: Use g_file_unmount_mountable_with_operation_finish()
- * instead.
+ * instead.
*/
gboolean
g_file_unmount_mountable_finish (GFile *file,
@@ -5117,14 +5220,14 @@ g_file_unmount_mountable_finish (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @mount_operation: (nullable): a #GMountOperation,
- * or %NULL to avoid user interaction
+ * or %NULL to avoid user interaction
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
- * Unmounts a file of type #G_FILE_TYPE_MOUNTABLE.
+ * Unmounts a file of type %G_FILE_TYPE_MOUNTABLE.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
@@ -5187,7 +5290,7 @@ g_file_unmount_mountable_with_operation (GFile *file,
* with g_file_unmount_mountable_with_operation().
*
* Returns: %TRUE if the operation finished successfully.
- * %FALSE otherwise.
+ * %FALSE otherwise.
*
* Since: 2.22
*/
@@ -5218,9 +5321,9 @@ g_file_unmount_mountable_with_operation_finish (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* Starts an asynchronous eject on a mountable.
@@ -5273,10 +5376,10 @@ g_file_eject_mountable (GFile *file,
* g_file_eject_mountable().
*
* Returns: %TRUE if the @file was ejected successfully.
- * %FALSE otherwise.
+ * %FALSE otherwise.
*
* Deprecated: 2.22: Use g_file_eject_mountable_with_operation_finish()
- * instead.
+ * instead.
*/
gboolean
g_file_eject_mountable_finish (GFile *file,
@@ -5302,11 +5405,11 @@ g_file_eject_mountable_finish (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @mount_operation: (nullable): a #GMountOperation,
- * or %NULL to avoid user interaction
+ * or %NULL to avoid user interaction
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (scope async) (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: (closure): the data to pass to callback function
*
* Starts an asynchronous eject on a mountable.
@@ -5368,7 +5471,7 @@ g_file_eject_mountable_with_operation (GFile *file,
* g_file_eject_mountable_with_operation().
*
* Returns: %TRUE if the @file was ejected successfully.
- * %FALSE otherwise.
+ * %FALSE otherwise.
*
* Since: 2.22
*/
@@ -5399,7 +5502,7 @@ g_file_eject_mountable_with_operation_finish (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Obtains a directory monitor for the given file.
@@ -5417,8 +5520,8 @@ g_file_eject_mountable_with_operation_finish (GFile *file,
*
* Virtual: monitor_dir
* Returns: (transfer full): a #GFileMonitor for the given @file,
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GFileMonitor *
g_file_monitor_directory (GFile *file,
@@ -5452,7 +5555,7 @@ g_file_monitor_directory (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Obtains a file monitor for the given file. If no file notification
@@ -5471,8 +5574,8 @@ g_file_monitor_directory (GFile *file,
* backend and/or filesystem type.
*
* Returns: (transfer full): a #GFileMonitor for the given @file,
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*/
GFileMonitor *
g_file_monitor_file (GFile *file,
@@ -5507,7 +5610,7 @@ g_file_monitor_file (GFile *file,
* @file: input #GFile
* @flags: a set of #GFileMonitorFlags
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @error: a #GError, or %NULL
*
* Obtains a file or directory monitor for the given file,
@@ -5518,8 +5621,8 @@ g_file_monitor_file (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: (transfer full): a #GFileMonitor for the given @file,
- * or %NULL on error.
- * Free the returned object with g_object_unref().
+ * or %NULL on error.
+ * Free the returned object with g_object_unref().
*
* Since: 2.18
*/
@@ -5998,6 +6101,125 @@ g_file_real_trash_finish (GFile *file,
return g_task_propagate_boolean (G_TASK (res), error);
}
+
+typedef struct {
+ GFile *source; /* (owned) */
+ GFile *destination; /* (owned) */
+ GFileCopyFlags flags;
+ GFileProgressCallback progress_cb;
+ gpointer progress_cb_data;
+} MoveAsyncData;
+
+static void
+move_async_data_free (MoveAsyncData *data)
+{
+ g_object_unref (data->source);
+ g_object_unref (data->destination);
+ g_slice_free (MoveAsyncData, data);
+}
+
+typedef struct {
+ MoveAsyncData *data; /* (unowned) */
+ goffset current_num_bytes;
+ goffset total_num_bytes;
+} MoveProgressData;
+
+static gboolean
+move_async_progress_in_main (gpointer user_data)
+{
+ MoveProgressData *progress = user_data;
+ MoveAsyncData *data = progress->data;
+
+ data->progress_cb (progress->current_num_bytes,
+ progress->total_num_bytes,
+ data->progress_cb_data);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+move_async_progress_callback (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ MoveAsyncData *data = g_task_get_task_data (task);
+ MoveProgressData *progress;
+
+ progress = g_new0 (MoveProgressData, 1);
+ progress->data = data;
+ progress->current_num_bytes = current_num_bytes;
+ progress->total_num_bytes = total_num_bytes;
+
+ g_main_context_invoke_full (g_task_get_context (task),
+ g_task_get_priority (task),
+ move_async_progress_in_main,
+ g_steal_pointer (&progress),
+ g_free);
+}
+
+static void
+move_async_thread (GTask *task,
+ gpointer source,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ MoveAsyncData *data = task_data;
+ gboolean result;
+ GError *error = NULL;
+
+ result = g_file_move (data->source,
+ data->destination,
+ data->flags,
+ cancellable,
+ (data->progress_cb != NULL) ? move_async_progress_callback : NULL,
+ task,
+ &error);
+ if (result)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+}
+
+static void
+g_file_real_move_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MoveAsyncData *data;
+
+ data = g_slice_new0 (MoveAsyncData);
+ data->source = g_object_ref (source);
+ data->destination = g_object_ref (destination);
+ data->flags = flags;
+ data->progress_cb = progress_callback;
+ data->progress_cb_data = progress_callback_data;
+
+ task = g_task_new (source, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_real_move_async);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) move_async_data_free);
+ g_task_set_priority (task, io_priority);
+ g_task_run_in_thread (task, move_async_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_file_real_move_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, file), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
make_directory_async_thread (GTask *task,
gpointer object,
@@ -6397,12 +6619,12 @@ typedef struct {
CopyAsyncData *data;
goffset current_num_bytes;
goffset total_num_bytes;
-} ProgressData;
+} CopyProgressData;
static gboolean
copy_async_progress_in_main (gpointer user_data)
{
- ProgressData *progress = user_data;
+ CopyProgressData *progress = user_data;
CopyAsyncData *data = progress->data;
data->progress_cb (progress->current_num_bytes,
@@ -6419,9 +6641,9 @@ copy_async_progress_callback (goffset current_num_bytes,
{
GTask *task = user_data;
CopyAsyncData *data = g_task_get_task_data (task);
- ProgressData *progress;
+ CopyProgressData *progress;
- progress = g_new (ProgressData, 1);
+ progress = g_new (CopyProgressData, 1);
progress->data = data;
progress->current_num_bytes = current_num_bytes;
progress->total_num_bytes = total_num_bytes;
@@ -6503,7 +6725,7 @@ g_file_real_copy_finish (GFile *file,
/**
* g_file_new_for_path:
* @path: (type filename): a string containing a relative or absolute path.
- * The string must be encoded in the glib filename encoding.
+ * The string must be encoded in the glib filename encoding.
*
* Constructs a #GFile for a given path. This operation never
* fails, but the returned object might not support any I/O
@@ -6530,7 +6752,7 @@ g_file_new_for_path (const char *path)
* not supported.
*
* Returns: (transfer full): a new #GFile for the given @uri.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_new_for_uri (const char *uri)
@@ -6559,7 +6781,7 @@ g_file_new_for_uri (const char *uri)
* a temporary file could not be created.
*
* Returns: (transfer full): a new #GFile.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*
* Since: 2.32
*/
@@ -6720,7 +6942,7 @@ new_for_cmdline_arg (const gchar *arg,
* #GOptionContext arguments of type %G_OPTION_ARG_FILENAME.
*
* Returns: (transfer full): a new #GFile.
- * Free the returned object with g_object_unref().
+ * Free the returned object with g_object_unref().
*/
GFile *
g_file_new_for_commandline_arg (const char *arg)
@@ -6766,11 +6988,11 @@ g_file_new_for_commandline_arg_and_cwd (const gchar *arg,
* @location: input #GFile
* @flags: flags affecting the operation
* @mount_operation: (nullable): a #GMountOperation
- * or %NULL to avoid user interaction
+ * or %NULL to avoid user interaction
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
* Starts a @mount_operation, mounting the volume that contains
@@ -6820,8 +7042,8 @@ g_file_mount_enclosing_volume (GFile *location,
* Finishes a mount operation started by g_file_mount_enclosing_volume().
*
* Returns: %TRUE if successful. If an error has occurred,
- * this function will return %FALSE and set @error
- * appropriately if present.
+ * this function will return %FALSE and set @error
+ * appropriately if present.
*/
gboolean
g_file_mount_enclosing_volume_finish (GFile *location,
@@ -6861,8 +7083,8 @@ g_file_mount_enclosing_volume_finish (GFile *location,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: (transfer full): a #GAppInfo if the handle was found,
- * %NULL if there were errors.
- * When you are done with it, release it with g_object_unref()
+ * %NULL if there were errors.
+ * When you are done with it, release it with g_object_unref()
*/
GAppInfo *
g_file_query_default_handler (GFile *file,
@@ -7032,8 +7254,8 @@ g_file_query_default_handler_async (GFile *file,
* Finishes a g_file_query_default_handler_async() operation.
*
* Returns: (transfer full): a #GAppInfo if the handle was found,
- * %NULL if there were errors.
- * When you are done with it, release it with g_object_unref()
+ * %NULL if there were errors.
+ * When you are done with it, release it with g_object_unref()
*
* Since: 2.60
*/
@@ -7056,9 +7278,9 @@ g_file_query_default_handler_finish (GFile *file,
* @cancellable: optional #GCancellable object, %NULL to ignore
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
* @length: (out) (optional): a location to place the length of the contents of the file,
- * or %NULL if the length is not needed
+ * or %NULL if the length is not needed
* @etag_out: (out) (optional) (nullable): a location to place the current entity tag for the file,
- * or %NULL if the entity tag is not needed
+ * or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Loads the content of the file into memory. The data is always
@@ -7071,7 +7293,7 @@ g_file_query_default_handler_finish (GFile *file,
* was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
*
* Returns: %TRUE if the @file's contents were successfully loaded.
- * %FALSE if there were errors.
+ * %FALSE if there were errors.
*/
gboolean
g_file_load_contents (GFile *file,
@@ -7294,10 +7516,10 @@ load_contents_open_callback (GObject *obj,
* @file: input #GFile
* @cancellable: optional #GCancellable object, %NULL to ignore
* @read_more_callback: (scope call) (closure user_data): a
- * #GFileReadMoreCallback to receive partial data
- * and to specify whether further data should be read
+ * #GFileReadMoreCallback to receive partial data
+ * and to specify whether further data should be read
* @callback: (scope async) (closure user_data): a #GAsyncReadyCallback to call
- * when the request is satisfied
+ * when the request is satisfied
* @user_data: the data to pass to the callback functions
*
* Reads the partial contents of a file. A #GFileReadMoreCallback should
@@ -7344,9 +7566,9 @@ g_file_load_partial_contents_async (GFile *file,
* @res: a #GAsyncResult
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
* @length: (out) (optional): a location to place the length of the contents of the file,
- * or %NULL if the length is not needed
+ * or %NULL if the length is not needed
* @etag_out: (out) (optional) (nullable): a location to place the current entity tag for the file,
- * or %NULL if the entity tag is not needed
+ * or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Finishes an asynchronous partial load operation that was started
@@ -7356,7 +7578,7 @@ g_file_load_partial_contents_async (GFile *file,
* needed.
*
* Returns: %TRUE if the load was successful. If %FALSE and @error is
- * present, it will be set appropriately.
+ * present, it will be set appropriately.
*/
gboolean
g_file_load_partial_contents_finish (GFile *file,
@@ -7442,9 +7664,9 @@ g_file_load_contents_async (GFile *file,
* @res: a #GAsyncResult
* @contents: (out) (transfer full) (element-type guint8) (array length=length): a location to place the contents of the file
* @length: (out) (optional): a location to place the length of the contents of the file,
- * or %NULL if the length is not needed
+ * or %NULL if the length is not needed
* @etag_out: (out) (optional) (nullable): a location to place the current entity tag for the file,
- * or %NULL if the entity tag is not needed
+ * or %NULL if the entity tag is not needed
* @error: a #GError, or %NULL
*
* Finishes an asynchronous load of the @file's contents.
@@ -7454,7 +7676,7 @@ g_file_load_contents_async (GFile *file,
* set to the new entity tag for the @file.
*
* Returns: %TRUE if the load was successful. If %FALSE and @error is
- * present, it will be set appropriately.
+ * present, it will be set appropriately.
*/
gboolean
g_file_load_contents_finish (GFile *file,
@@ -7478,12 +7700,12 @@ g_file_load_contents_finish (GFile *file,
* @contents: (element-type guint8) (array length=length): a string containing the new contents for @file
* @length: the length of @contents in bytes
* @etag: (nullable): the old [entity-tag][gfile-etag] for the document,
- * or %NULL
+ * or %NULL
* @make_backup: %TRUE if a backup should be created
* @flags: a set of #GFileCreateFlags
* @new_etag: (out) (optional) (nullable): a location to a new [entity tag][gfile-etag]
- * for the document. This should be freed with g_free() when no longer
- * needed, or %NULL
+ * for the document. This should be freed with g_free() when no longer
+ * needed, or %NULL
* @cancellable: optional #GCancellable object, %NULL to ignore
* @error: a #GError, or %NULL
*
@@ -7505,7 +7727,7 @@ g_file_load_contents_finish (GFile *file,
* changed the next time it is saved over.
*
* Returns: %TRUE if successful. If an error has occurred, this function
- * will return %FALSE and set @error appropriately if present.
+ * will return %FALSE and set @error appropriately if present.
*/
gboolean
g_file_replace_contents (GFile *file,
@@ -7790,8 +8012,8 @@ g_file_replace_contents_bytes_async (GFile *file,
* @file: input #GFile
* @res: a #GAsyncResult
* @new_etag: (out) (optional) (nullable): a location of a new [entity tag][gfile-etag]
- * for the document. This should be freed with g_free() when it is no
- * longer needed, or %NULL
+ * for the document. This should be freed with g_free() when it is no
+ * longer needed, or %NULL
* @error: a #GError, or %NULL
*
* Finishes an asynchronous replace of the given @file. See
@@ -8015,7 +8237,7 @@ g_file_real_measure_disk_usage_finish (GFile *file,
* callback will be invoked.
*
* Returns: %TRUE if successful, with the out parameters set.
- * %FALSE otherwise, with @error set.
+ * %FALSE otherwise, with @error set.
*
* Since: 2.38
**/
@@ -8090,7 +8312,7 @@ g_file_measure_disk_usage_async (GFile *file,
* more information.
*
* Returns: %TRUE if successful, with the out parameters set.
- * %FALSE otherwise, with @error set.
+ * %FALSE otherwise, with @error set.
*
* Since: 2.38
**/
@@ -8117,7 +8339,7 @@ g_file_measure_disk_usage_finish (GFile *file,
* @callback: (nullable): a #GAsyncReadyCallback to call when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
- * Starts a file of type #G_FILE_TYPE_MOUNTABLE.
+ * Starts a file of type %G_FILE_TYPE_MOUNTABLE.
* Using @start_operation, you can request callbacks when, for instance,
* passwords are needed during authentication.
*
@@ -8202,14 +8424,14 @@ g_file_start_mountable_finish (GFile *file,
* @file: input #GFile
* @flags: flags affecting the operation
* @mount_operation: (nullable): a #GMountOperation,
- * or %NULL to avoid user interaction.
+ * or %NULL to avoid user interaction.
* @cancellable: (nullable): optional #GCancellable object,
- * %NULL to ignore
+ * %NULL to ignore
* @callback: (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
- * Stops a file of type #G_FILE_TYPE_MOUNTABLE.
+ * Stops a file of type %G_FILE_TYPE_MOUNTABLE.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
@@ -8264,7 +8486,7 @@ g_file_stop_mountable (GFile *file,
* with g_file_stop_mountable().
*
* Returns: %TRUE if the operation finished successfully.
- * %FALSE otherwise.
+ * %FALSE otherwise.
*
* Since: 2.22
*/
@@ -8292,10 +8514,10 @@ g_file_stop_mountable_finish (GFile *file,
* @file: input #GFile
* @cancellable: optional #GCancellable object, %NULL to ignore
* @callback: (nullable): a #GAsyncReadyCallback to call
- * when the request is satisfied, or %NULL
+ * when the request is satisfied, or %NULL
* @user_data: the data to pass to callback function
*
- * Polls a file of type #G_FILE_TYPE_MOUNTABLE.
+ * Polls a file of type %G_FILE_TYPE_MOUNTABLE.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
@@ -8398,7 +8620,7 @@ g_file_supports_thread_contexts (GFile *file)
* @file: a #GFile
* @cancellable: (nullable): a #GCancellable or %NULL
* @etag_out: (out) (nullable) (optional): a location to place the current
- * entity tag for the file, or %NULL if the entity tag is not needed
+ * entity tag for the file, or %NULL if the entity tag is not needed
* @error: a location for a #GError or %NULL
*
* Loads the contents of @file and returns it as #GBytes.
@@ -8485,7 +8707,7 @@ g_file_load_bytes_cb (GObject *object,
* @file: a #GFile
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: (scope async): a #GAsyncReadyCallback to call when the
- * request is satisfied
+ * request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously loads the contents of @file as #GBytes.
@@ -8543,7 +8765,7 @@ g_file_load_bytes_async (GFile *file,
* @file: a #GFile
* @result: a #GAsyncResult provided to the callback
* @etag_out: (out) (nullable) (optional): a location to place the current
- * entity tag for the file, or %NULL if the entity tag is not needed
+ * entity tag for the file, or %NULL if the entity tag is not needed
* @error: a location for a #GError, or %NULL
*
* Completes an asynchronous request to g_file_load_bytes_async().
diff --git a/gio/gfile.h b/gio/gfile.h
index 4cff1a372..3a324cf9d 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -121,8 +121,8 @@ typedef struct _GFileIface GFileIface;
* @copy_async: Asynchronously copies a file.
* @copy_finish: Finishes an asynchronous copy operation.
* @move: Moves a file.
- * @_move_async: Asynchronously moves a file.
- * @_move_finish: Finishes an asynchronous move operation.
+ * @move_async: Asynchronously moves a file. Since: 2.72
+ * @move_finish: Finishes an asynchronous move operation. Since: 2.72
* @mount_mountable: Mounts a mountable object.
* @mount_mountable_finish: Finishes a mounting operation.
* @unmount_mountable: Unmounts a mountable object.
@@ -424,8 +424,18 @@ struct _GFileIface
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GError **error);
- void (* _move_async) (void);
- void (* _move_finish) (void);
+ void (* move_async) (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* move_finish) (GFile *file,
+ GAsyncResult *result,
+ GError **error);
void (* mount_mountable) (GFile *file,
GMountMountFlags flags,
@@ -926,6 +936,20 @@ gboolean g_file_move (GFile
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GError **error);
+GLIB_AVAILABLE_IN_2_72
+void g_file_move_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_72
+gboolean g_file_move_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_file_make_directory (GFile *file,
GCancellable *cancellable,
diff --git a/gio/gfileenumerator.c b/gio/gfileenumerator.c
index 1f9bc24eb..e0ed9720e 100644
--- a/gio/gfileenumerator.c
+++ b/gio/gfileenumerator.c
@@ -662,7 +662,10 @@ g_file_enumerator_iterate (GFileEnumerator *direnum,
const char *name = g_file_info_get_name (ret_info);
if (G_UNLIKELY (name == NULL))
- g_warning ("g_file_enumerator_iterate() created without standard::name");
+ {
+ g_critical ("g_file_enumerator_iterate() created without standard::name");
+ g_return_val_if_reached (FALSE);
+ }
else
{
*out_child = g_file_get_child (g_file_enumerator_get_container (direnum), name);
@@ -718,6 +721,9 @@ g_file_enumerator_get_container (GFileEnumerator *enumerator)
* directory of @enumerator. This function is primarily intended to be used
* inside loops with g_file_enumerator_next_file().
*
+ * To use this, %G_FILE_ATTRIBUTE_STANDARD_NAME must have been listed in the
+ * attributes list used when creating the #GFileEnumerator.
+ *
* This is a convenience method that's equivalent to:
* |[<!-- language="C" -->
* gchar *name = g_file_info_get_name (info);
@@ -733,10 +739,20 @@ GFile *
g_file_enumerator_get_child (GFileEnumerator *enumerator,
GFileInfo *info)
{
+ const gchar *name;
+
g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+ g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+
+ name = g_file_info_get_name (info);
+
+ if (G_UNLIKELY (name == NULL))
+ {
+ g_critical ("GFileEnumerator created without standard::name");
+ g_return_val_if_reached (NULL);
+ }
- return g_file_get_child (enumerator->priv->container,
- g_file_info_get_name (info));
+ return g_file_get_child (enumerator->priv->container, name);
}
static void
diff --git a/gio/gfilemonitor.c b/gio/gfilemonitor.c
index 9b9c0ea2d..89c89d203 100644
--- a/gio/gfilemonitor.c
+++ b/gio/gfilemonitor.c
@@ -172,7 +172,7 @@ g_file_monitor_class_init (GFileMonitorClass *klass)
* that the %G_FILE_MONITOR_WATCH_MOVES flag is not in use.
*
* If using the deprecated flag %G_FILE_MONITOR_SEND_MOVED flag and @event_type is
- * #G_FILE_MONITOR_EVENT_MOVED, @file will be set to a #GFile containing the
+ * %G_FILE_MONITOR_EVENT_MOVED, @file will be set to a #GFile containing the
* old path, and @other_file will be set to a #GFile containing the new path.
*
* In all the other cases, @other_file will be set to #NULL.
diff --git a/gio/ginputstream.c b/gio/ginputstream.c
index a40c7d9c4..8fda2693c 100644
--- a/gio/ginputstream.c
+++ b/gio/ginputstream.c
@@ -429,8 +429,8 @@ g_input_stream_real_skip (GInputStream *stream,
end = g_seekable_tell (seekable);
g_assert (start >= 0);
g_assert (end >= start);
- if ((guint64) start > (G_MAXSIZE - count) ||
- (start + count) > (guint64) end)
+ if (start > (goffset) (G_MAXOFFSET - count) ||
+ (goffset) (start + count) > end)
{
stream->priv->pending = TRUE;
return end - start;
diff --git a/gio/gio-querymodules.c b/gio/gio-querymodules.c
index cbeb9758e..aff194b73 100644
--- a/gio/gio-querymodules.c
+++ b/gio/gio-querymodules.c
@@ -161,7 +161,7 @@ main (gint argc,
{
int i;
- if (argc == 1)
+ if (argc <= 1)
{
g_print ("Usage: gio-querymodules <directory1> [<directory2> ...]\n");
g_print ("Will update giomodule.cache in the listed directories\n");
diff --git a/gio/gio-tool-set.c b/gio/gio-tool-set.c
index 4dbe1214f..c2a9431f6 100644
--- a/gio/gio-tool-set.c
+++ b/gio/gio-tool-set.c
@@ -76,12 +76,14 @@ handle_set (int argc, char *argv[], gboolean do_help)
const char *attribute;
GFileAttributeType type;
gpointer value;
+ gpointer value_allocated = NULL;
gboolean b;
guint32 uint32;
gint32 int32;
guint64 uint64;
gint64 int64;
gchar *param;
+ int retval = 0;
g_set_prgname ("gio set");
@@ -147,7 +149,7 @@ handle_set (int argc, char *argv[], gboolean do_help)
value = argv[3];
break;
case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
- value = hex_unescape (argv[3]);
+ value = value_allocated = hex_unescape (argv[3]);
break;
case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
b = g_ascii_strcasecmp (argv[3], "true") == 0;
@@ -194,11 +196,11 @@ handle_set (int argc, char *argv[], gboolean do_help)
{
print_error ("%s", error->message);
g_error_free (error);
- g_object_unref (file);
- return 1;
+ retval = 1;
}
+ g_clear_pointer (&value_allocated, g_free);
g_object_unref (file);
- return 0;
+ return retval;
}
diff --git a/gio/gio.h b/gio/gio.h
index e9afab666..2d0a9c274 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -69,6 +69,8 @@
#include <gio/gdbusproxy.h>
#include <gio/gdbusserver.h>
#include <gio/gdbusutils.h>
+#include <gio/gdebugcontroller.h>
+#include <gio/gdebugcontrollerdbus.h>
#include <gio/gdrive.h>
#include <gio/gdtlsclientconnection.h>
#include <gio/gdtlsconnection.h>
diff --git a/gio/gioenums.h b/gio/gioenums.h
index d81ada416..efc430152 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -392,7 +392,7 @@ typedef enum {
*
* Indicates a hint from the file system whether files should be
* previewed in a file manager. Returned as the value of the key
- * #G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW.
+ * %G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW.
**/
typedef enum {
G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS = 0,
@@ -522,7 +522,7 @@ typedef enum {
* }
* ]|
* but should instead treat all unrecognized error codes the same as
- * #G_IO_ERROR_FAILED.
+ * %G_IO_ERROR_FAILED.
*
* See also #GPollableReturn for a cheaper way of returning
* %G_IO_ERROR_WOULD_BLOCK to callers without allocating a #GError.
@@ -973,7 +973,7 @@ typedef enum
* @G_BUS_NAME_OWNER_FLAGS_NONE: No flags set.
* @G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT: Allow another message bus connection to claim the name.
* @G_BUS_NAME_OWNER_FLAGS_REPLACE: If another message bus connection owns the name and have
- * specified #G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, then take the name from the other connection.
+ * specified %G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, then take the name from the other connection.
* @G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE: If another message bus connection owns the name, immediately
* return an error from g_bus_own_name() rather than entering the waiting queue for that name. (Since 2.54)
*
@@ -1021,6 +1021,9 @@ typedef enum
* do not ask the bus to launch an owner during proxy initialization, but allow it to be
* autostarted by a method call. This flag is only meaningful in proxies for well-known names,
* and only if %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is not also specified.
+ * @G_DBUS_PROXY_FLAGS_NO_MATCH_RULE: Don't actually send the AddMatch D-Bus
+ * call for this signal subscription. This gives you more control
+ * over which match rules you add (but you must add them manually). (Since: 2.72)
*
* Flags used when constructing an instance of a #GDBusProxy derived class.
*
@@ -1033,7 +1036,8 @@ typedef enum
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1),
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2),
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3),
- G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION = (1<<4)
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION = (1<<4),
+ G_DBUS_PROXY_FLAGS_NO_MATCH_RULE GLIB_AVAILABLE_ENUMERATOR_IN_2_72 = (1<<5)
} GDBusProxyFlags;
/**
@@ -1437,6 +1441,7 @@ typedef enum
* @G_CREDENTIALS_TYPE_SOLARIS_UCRED: The native credentials type is a `ucred_t`. Added in 2.40.
* @G_CREDENTIALS_TYPE_NETBSD_UNPCBID: The native credentials type is a `struct unpcbid`. Added in 2.42.
* @G_CREDENTIALS_TYPE_APPLE_XUCRED: The native credentials type is a `struct xucred`. Added in 2.66.
+ * @G_CREDENTIALS_TYPE_WIN32_PID: The native credentials type is a PID `DWORD`. Added in 2.72.
*
* Enumeration describing different kinds of native credential types.
*
@@ -1451,6 +1456,7 @@ typedef enum
G_CREDENTIALS_TYPE_SOLARIS_UCRED,
G_CREDENTIALS_TYPE_NETBSD_UNPCBID,
G_CREDENTIALS_TYPE_APPLE_XUCRED,
+ G_CREDENTIALS_TYPE_WIN32_PID,
} GCredentialsType;
/**
@@ -1548,6 +1554,8 @@ typedef enum
* @G_TLS_ERROR_INAPPROPRIATE_FALLBACK: The TLS handshake failed
* because the client sent the fallback SCSV, indicating a protocol
* downgrade attack. Since: 2.60
+ * @G_TLS_ERROR_BAD_CERTIFICATE_PASSWORD: The certificate failed
+ * to load because a password was incorrect. Since: 2.72
*
* An error code used with %G_TLS_ERROR in a #GError returned from a
* TLS-related routine.
@@ -1562,7 +1570,8 @@ typedef enum {
G_TLS_ERROR_HANDSHAKE,
G_TLS_ERROR_CERTIFICATE_REQUIRED,
G_TLS_ERROR_EOF,
- G_TLS_ERROR_INAPPROPRIATE_FALLBACK
+ G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+ G_TLS_ERROR_BAD_CERTIFICATE_PASSWORD
} GTlsError;
/**
@@ -1584,10 +1593,16 @@ typedef enum {
* flags
*
* A set of flags describing TLS certification validation. This can be
- * used to set which validation steps to perform (eg, with
- * g_tls_client_connection_set_validation_flags()), or to describe why
- * a particular certificate was rejected (eg, in
- * #GTlsConnection::accept-certificate).
+ * used to describe why a particular certificate was rejected (for
+ * example, in #GTlsConnection::accept-certificate).
+ *
+ * GLib guarantees that if certificate verification fails, at least one
+ * flag will be set, but it does not guarantee that all possible flags
+ * will be set. Accordingly, you may not safely decide to ignore any
+ * particular type of error. For example, it would be incorrect to mask
+ * %G_TLS_CERTIFICATE_EXPIRED if you want to allow expired certificates,
+ * because this could potentially be the only error flag set even if
+ * other problems exist with the certificate.
*
* Since: 2.28
*/
@@ -1973,6 +1988,9 @@ typedef enum /*< flags >*/ {
* file descriptors of their parent, unless those descriptors have
* been explicitly marked as close-on-exec. This flag has no effect
* over the "standard" file descriptors (stdin, stdout, stderr).
+ * @G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP: if path searching is
+ * needed when spawning the subprocess, use the `PATH` in the launcher
+ * environment. (Since: 2.72)
*
* Flags to define the behaviour of a #GSubprocess.
*
@@ -1995,7 +2013,8 @@ typedef enum {
G_SUBPROCESS_FLAGS_STDERR_PIPE = (1u << 4),
G_SUBPROCESS_FLAGS_STDERR_SILENCE = (1u << 5),
G_SUBPROCESS_FLAGS_STDERR_MERGE = (1u << 6),
- G_SUBPROCESS_FLAGS_INHERIT_FDS = (1u << 7)
+ G_SUBPROCESS_FLAGS_INHERIT_FDS = (1u << 7),
+ G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP = (1u << 8)
} GSubprocessFlags;
/**
diff --git a/gio/giomodule.c b/gio/giomodule.c
index d34037a45..2a043ccd9 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -45,6 +45,8 @@
#include "gnotificationbackend.h"
#include "ginitable.h"
#include "gnetworkmonitor.h"
+#include "gdebugcontroller.h"
+#include "gdebugcontrollerdbus.h"
#include "gmemorymonitor.h"
#include "gmemorymonitorportal.h"
#include "gmemorymonitordbus.h"
@@ -68,6 +70,10 @@
#include <AvailabilityMacros.h>
#endif
+#define __GLIB_H_INSIDE__
+#include "gconstructor.h"
+#undef __GLIB_H_INSIDE__
+
/**
* SECTION:giomodule
* @short_description: Loadable GIO Modules
@@ -1078,8 +1084,10 @@ extern GType _g_network_monitor_netlink_get_type (void);
extern GType _g_network_monitor_nm_get_type (void);
#endif
+extern GType g_debug_controller_dbus_get_type (void);
extern GType g_memory_monitor_dbus_get_type (void);
extern GType g_memory_monitor_portal_get_type (void);
+extern GType g_memory_monitor_win32_get_type (void);
extern GType g_power_profile_monitor_dbus_get_type (void);
#ifdef G_OS_UNIX
@@ -1102,7 +1110,7 @@ extern GType _g_win32_network_monitor_get_type (void);
static HMODULE gio_dll = NULL;
-#ifdef DLL_EXPORT
+#ifndef GLIB_STATIC_COMPILATION
BOOL WINAPI DllMain (HINSTANCE hinstDLL,
DWORD fdwReason,
@@ -1122,8 +1130,40 @@ DllMain (HINSTANCE hinstDLL,
return TRUE;
}
+#elif defined(G_HAS_CONSTRUCTORS) /* && G_PLATFORM_WIN32 && GLIB_STATIC_COMPILATION */
+extern void glib_win32_init (void);
+extern void gobject_win32_init (void);
+
+#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(giomodule_init_ctor)
#endif
+G_DEFINE_CONSTRUCTOR (giomodule_init_ctor)
+
+static void
+giomodule_init_ctor (void)
+{
+ /* When built dynamically, module initialization is done through DllMain
+ * function which is called when the dynamic library is loaded by the glib
+ * module AFTER loading gobject. So, in dynamic configuration glib and
+ * gobject are always initialized BEFORE gio.
+ *
+ * When built statically, initialization mechanism relies on hooking
+ * functions to the CRT section directly at compilation time. As we don't
+ * control how each compilation unit will be built and in which order, we
+ * obtain the same kind of issue as the "static initialization order fiasco".
+ * In this case, we must ensure explicitly that glib and gobject are always
+ * well initialized BEFORE gio.
+ */
+ glib_win32_init ();
+ gobject_win32_init ();
+ gio_win32_appinfo_init (FALSE);
+}
+
+#else /* G_PLATFORM_WIN32 && GLIB_STATIC_COMPILATION && !G_HAS_CONSTRUCTORS */
+#error Your platform/compiler is missing constructor support
+#endif /* GLIB_STATIC_COMPILATION */
+
void *
_g_io_win32_get_module (void)
{
@@ -1135,7 +1175,7 @@ _g_io_win32_get_module (void)
return gio_dll;
}
-#endif
+#endif /* G_PLATFORM_WIN32 */
void
_g_io_modules_ensure_extension_points_registered (void)
@@ -1189,6 +1229,9 @@ _g_io_modules_ensure_extension_points_registered (void)
ep = g_io_extension_point_register (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME);
g_io_extension_point_set_required_type (ep, G_TYPE_NOTIFICATION_BACKEND);
+ ep = g_io_extension_point_register (G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME);
+ g_io_extension_point_set_required_type (ep, G_TYPE_DEBUG_CONTROLLER);
+
ep = g_io_extension_point_register (G_MEMORY_MONITOR_EXTENSION_POINT_NAME);
g_io_extension_point_set_required_type (ep, G_TYPE_MEMORY_MONITOR);
@@ -1300,6 +1343,7 @@ _g_io_modules_ensure_loaded (void)
#endif
#ifdef G_OS_UNIX
g_type_ensure (_g_unix_volume_monitor_get_type ());
+ g_type_ensure (g_debug_controller_dbus_get_type ());
g_type_ensure (g_fdo_notification_backend_get_type ());
g_type_ensure (g_gtk_notification_backend_get_type ());
g_type_ensure (g_portal_notification_backend_get_type ());
@@ -1315,6 +1359,7 @@ _g_io_modules_ensure_loaded (void)
#ifdef G_OS_WIN32
g_type_ensure (g_win32_notification_backend_get_type ());
g_type_ensure (_g_winhttp_vfs_get_type ());
+ g_type_ensure (g_memory_monitor_win32_get_type ());
#endif
g_type_ensure (_g_local_vfs_get_type ());
g_type_ensure (_g_dummy_proxy_resolver_get_type ());
@@ -1429,7 +1474,7 @@ g_io_extension_point_set_required_type (GIOExtensionPoint *extension_point,
* Gets the required type for @extension_point.
*
* Returns: the #GType that all implementations must have,
- * or #G_TYPE_INVALID if the extension point has no required type
+ * or %G_TYPE_INVALID if the extension point has no required type
*/
GType
g_io_extension_point_get_required_type (GIOExtensionPoint *extension_point)
diff --git a/gio/giomodule.h b/gio/giomodule.h
index e94b8099d..4457c498e 100644
--- a/gio/giomodule.h
+++ b/gio/giomodule.h
@@ -104,7 +104,7 @@ GTypeClass* g_io_extension_ref_class (GIOExtension
/* API for the modules to implement */
/**
- * g_io_module_load:
+ * g_io_module_load: (skip)
* @module: a #GIOModule.
*
* Required API for GIO modules to implement.
@@ -125,7 +125,7 @@ GLIB_AVAILABLE_IN_ALL
void g_io_module_load (GIOModule *module);
/**
- * g_io_module_unload:
+ * g_io_module_unload: (skip)
* @module: a #GIOModule.
*
* Required API for GIO modules to implement.
diff --git a/gio/giowin32-afunix.h b/gio/giowin32-afunix.h
new file mode 100644
index 000000000..0e8e27f08
--- /dev/null
+++ b/gio/giowin32-afunix.h
@@ -0,0 +1,40 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GIOWIN32_AFUNIX_H_
+#define GIOWIN32_AFUNIX_H_
+
+#ifdef HAVE_AFUNIX_H
+#include <afunix.h>
+#else
+
+/*
+ * Fallback definitions of things we need in afunix.h, if not available from the
+ * used Windows SDK or MinGW headers.
+ */
+#define UNIX_PATH_MAX 108
+
+typedef struct sockaddr_un {
+ ADDRESS_FAMILY sun_family;
+ char sun_path[UNIX_PATH_MAX];
+} SOCKADDR_UN, *PSOCKADDR_UN;
+
+#define SIO_AF_UNIX_GETPEERPID _WSAIOR(IOC_VENDOR, 256)
+#endif
+
+#endif /* GIOWIN32_AFUNIX_H_*/
diff --git a/gio/glib-compile-resources.c b/gio/glib-compile-resources.c
index ac95801f4..82f19563a 100644
--- a/gio/glib-compile-resources.c
+++ b/gio/glib-compile-resources.c
@@ -710,6 +710,86 @@ escape_makefile_string (const char *string)
return g_string_free (str, FALSE);
}
+typedef enum {
+ COMPILER_GCC,
+ COMPILER_CLANG,
+ COMPILER_MSVC,
+ COMPILER_UNKNOWN
+} CompilerType;
+
+/* Get the compiler id from the platform, environment, or command line
+ *
+ * Keep compiler IDs consistent with https://mesonbuild.com/Reference-tables.html#compiler-ids
+ * for simplicity
+ */
+static CompilerType
+get_compiler_id (const char *compiler)
+{
+ char *base, *ext_p;
+ CompilerType compiler_type;
+
+ if (compiler == NULL)
+ {
+#ifdef G_OS_UNIX
+ const char *compiler_env = g_getenv ("CC");
+
+# ifdef __APPLE__
+ if (compiler_env == NULL || *compiler_env == '\0')
+ compiler = "clang";
+ else
+ compiler = compiler_env;
+# elif __linux__
+ if (compiler_env == NULL || *compiler_env == '\0')
+ compiler = "gcc";
+ else
+ compiler = compiler_env;
+# else
+ if (compiler_env == NULL || *compiler_env == '\0')
+ compiler = "unknown";
+ else
+ compiler = compiler_env;
+# endif
+#endif
+
+#ifdef G_OS_WIN32
+ if (g_getenv ("MSYSTEM") != NULL)
+ {
+ const char *compiler_env = g_getenv ("CC");
+
+ if (compiler_env == NULL || *compiler_env == '\0')
+ compiler = "gcc";
+ else
+ compiler = compiler_env;
+ }
+ else
+ compiler = "msvc";
+#endif
+ }
+
+ base = g_path_get_basename (compiler);
+ ext_p = strrchr (base, '.');
+ if (ext_p != NULL)
+ {
+ gsize offset = ext_p - base;
+ base[offset] = '\0';
+ }
+
+ compiler = base;
+
+ if (g_strcmp0 (compiler, "gcc") == 0)
+ compiler_type = COMPILER_GCC;
+ else if (g_strcmp0 (compiler, "clang") == 0)
+ compiler_type = COMPILER_CLANG;
+ else if (g_strcmp0 (compiler, "msvc") == 0)
+ compiler_type = COMPILER_MSVC;
+ else
+ compiler_type = COMPILER_UNKNOWN;
+
+ g_free (base);
+
+ return compiler_type;
+}
+
int
main (int argc, char **argv)
{
@@ -732,6 +812,8 @@ main (int argc, char **argv)
char *c_name = NULL;
char *c_name_no_underscores;
const char *linkage = "extern";
+ char *compiler = NULL;
+ CompilerType compiler_type = COMPILER_GCC;
GOptionContext *context;
GOptionEntry entries[] = {
{ "version", 0, 0, G_OPTION_ARG_NONE, &show_version_and_exit, N_("Show program version and exit"), NULL },
@@ -747,6 +829,7 @@ main (int argc, char **argv)
{ "internal", 0, 0, G_OPTION_ARG_NONE, &internal, N_("Don’t export functions; declare them G_GNUC_INTERNAL"), NULL },
{ "external-data", 0, 0, G_OPTION_ARG_NONE, &external_data, N_("Don’t embed resource data in the C file; assume it's linked externally instead"), NULL },
{ "c-name", 0, 0, G_OPTION_ARG_STRING, &c_name, N_("C identifier name used for the generated source code"), NULL },
+ { "compiler", 'C', 0, G_OPTION_ARG_STRING, &compiler, N_("The target C compiler (default: the CC environment variable)"), NULL },
G_OPTION_ENTRY_NULL
};
@@ -802,6 +885,9 @@ main (int argc, char **argv)
if (internal)
linkage = "G_GNUC_INTERNAL";
+ compiler_type = get_compiler_id (compiler);
+ g_free (compiler);
+
srcfile = argv[1];
xmllint = g_strdup (g_getenv ("XMLLINT"));
@@ -1105,40 +1191,41 @@ main (int argc, char **argv)
}
else
{
- /* For Visual Studio builds: Avoid surpassing the 65535-character limit for a string, GitLab issue #1580 */
- g_fprintf (file, "#ifdef _MSC_VER\n");
- g_fprintf (file,
- "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = { {\n",
- data_size + 1 /* nul terminator */, c_name);
-
- for (i = 0; i < data_size; i++)
+ if (compiler_type == COMPILER_MSVC || compiler_type == COMPILER_UNKNOWN)
{
- if (i % 16 == 0)
- g_fprintf (file, " ");
- g_fprintf (file, "0%3.3o", (int)data[i]);
- if (i != data_size - 1)
- g_fprintf (file, ", ");
- if (i % 16 == 15 || i == data_size - 1)
- g_fprintf (file, "\n");
- }
+ /* For Visual Studio builds: Avoid surpassing the 65535-character limit for a string, GitLab issue #1580 */
+ g_fprintf (file,
+ "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = { {\n",
+ data_size + 1 /* nul terminator */, c_name);
- g_fprintf (file, "} };\n");
-
- /* For other compilers, use the long string approach */
- g_fprintf (file, "#else /* _MSC_VER */\n");
- g_fprintf (file,
- "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = {\n \"",
- data_size + 1 /* nul terminator */, c_name);
+ for (i = 0; i < data_size; i++)
+ {
+ if (i % 16 == 0)
+ g_fprintf (file, " ");
+ g_fprintf (file, "0%3.3o", (int)data[i]);
+ if (i != data_size - 1)
+ g_fprintf (file, ", ");
+ if (i % 16 == 15 || i == data_size - 1)
+ g_fprintf (file, "\n");
+ }
- for (i = 0; i < data_size; i++)
- {
- g_fprintf (file, "\\%3.3o", (int)data[i]);
- if (i % 16 == 15)
- g_fprintf (file, "\"\n \"");
+ g_fprintf (file, "} };\n");
}
+ else
+ {
+ g_fprintf (file,
+ "static const SECTION union { const guint8 data[%"G_GSIZE_FORMAT"]; const double alignment; void * const ptr;} %s_resource_data = {\n \"",
+ data_size + 1 /* nul terminator */, c_name);
- g_fprintf (file, "\" };\n");
- g_fprintf (file, "#endif /* !_MSC_VER */\n");
+ for (i = 0; i < data_size; i++)
+ {
+ g_fprintf (file, "\\%3.3o", (int)data[i]);
+ if (i % 16 == 15)
+ g_fprintf (file, "\"\n \"");
+ }
+
+ g_fprintf (file, "\" };\n");
+ }
}
g_fprintf (file,
diff --git a/gio/glib-compile-schemas.c b/gio/glib-compile-schemas.c
index 7e1152f6b..83184e1e8 100644
--- a/gio/glib-compile-schemas.c
+++ b/gio/glib-compile-schemas.c
@@ -1038,7 +1038,7 @@ schema_state_add_override (SchemaState *state,
GError **error)
{
SchemaState *parent;
- KeyState *original;
+ KeyState *original = NULL;
if (state->extends == NULL)
{
@@ -1386,14 +1386,14 @@ start_element (GMarkupParseContext *context,
}
else if (strcmp (element_name, "override") == 0)
{
- const gchar *name, *l10n, *context;
+ const gchar *name, *l10n, *str_context;
- if (COLLECT (STRING, "name", &name,
- OPTIONAL | STRING, "l10n", &l10n,
- OPTIONAL | STRING, "context", &context))
+ if (COLLECT (STRING, "name", &name,
+ OPTIONAL | STRING, "l10n", &l10n,
+ OPTIONAL | STRING, "context", &str_context))
schema_state_add_override (state->schema_state,
&state->key_state, &state->string,
- name, l10n, context, error);
+ name, l10n, str_context, error);
return;
}
}
@@ -1403,11 +1403,11 @@ start_element (GMarkupParseContext *context,
{
if (strcmp (element_name, "default") == 0)
{
- const gchar *l10n, *context;
- if (COLLECT (STRING | OPTIONAL, "l10n", &l10n,
- STRING | OPTIONAL, "context", &context))
+ const gchar *l10n, *str_context;
+ if (COLLECT (STRING | OPTIONAL, "l10n", &l10n,
+ STRING | OPTIONAL, "context", &str_context))
state->string = key_state_start_default (state->key_state,
- l10n, context, error);
+ l10n, str_context, error);
return;
}
diff --git a/gio/glistmodel.c b/gio/glistmodel.c
index eb797caaf..0411353c2 100644
--- a/gio/glistmodel.c
+++ b/gio/glistmodel.c
@@ -135,7 +135,7 @@ g_list_model_default_init (GListModelInterface *iface)
* from @list. At @position, @removed items were removed and @added
* items were added in their place.
*
- * Note: If @removed != @added, the positions of all later items
+ * Note: If `removed != added`, the positions of all later items
* in the model change.
*
* Since: 2.44
@@ -157,9 +157,11 @@ g_list_model_default_init (GListModelInterface *iface)
* g_list_model_get_item_type:
* @list: a #GListModel
*
- * Gets the type of the items in @list. All items returned from
- * g_list_model_get_type() are of that type or a subtype, or are an
- * implementation of that interface.
+ * Gets the type of the items in @list.
+ *
+ * All items returned from g_list_model_get_item() are of the type
+ * returned by this function, or a subtype, or if the type is an
+ * interface, they are an implementation of that interface.
*
* The item type of a #GListModel can not change during the life of the
* model.
@@ -203,11 +205,15 @@ g_list_model_get_n_items (GListModel *list)
* @list: a #GListModel
* @position: the position of the item to fetch
*
- * Get the item at @position. If @position is greater than the number of
- * items in @list, %NULL is returned.
+ * Get the item at @position.
+ *
+ * If @position is greater than the number of items in @list, %NULL is
+ * returned.
*
* %NULL is never returned for an index that is smaller than the length
- * of the list. See g_list_model_get_n_items().
+ * of the list.
+ *
+ * See also: g_list_model_get_n_items()
*
* Returns: (transfer full) (nullable): the item at @position.
*
@@ -227,11 +233,18 @@ g_list_model_get_item (GListModel *list,
* @list: a #GListModel
* @position: the position of the item to fetch
*
- * Get the item at @position. If @position is greater than the number of
- * items in @list, %NULL is returned.
+ * Get the item at @position.
+ *
+ * If @position is greater than the number of items in @list, %NULL is
+ * returned.
*
* %NULL is never returned for an index that is smaller than the length
- * of the list. See g_list_model_get_n_items().
+ * of the list.
+ *
+ * This function is meant to be used by language bindings in place
+ * of g_list_model_get_item().
+ *
+ * See also: g_list_model_get_n_items()
*
* Returns: (transfer full) (nullable): the object at @position.
*
diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h
index e04e921f2..f73804516 100644
--- a/gio/glocalfileinfo.h
+++ b/gio/glocalfileinfo.h
@@ -297,7 +297,7 @@ inline static guint32 _g_stat_nlink (const GLocalFileStat *buf) { return b
#endif
inline static dev_t _g_stat_dev (const GLocalFileStat *buf) { return buf->st_dev; }
inline static ino_t _g_stat_ino (const GLocalFileStat *buf) { return buf->st_ino; }
-inline static off_t _g_stat_size (const GLocalFileStat *buf) { return buf->st_size; }
+inline static goffset _g_stat_size (const GLocalFileStat *buf) { return buf->st_size; }
#ifndef G_OS_WIN32
inline static uid_t _g_stat_uid (const GLocalFileStat *buf) { return buf->st_uid; }
@@ -317,11 +317,19 @@ inline static blkcnt_t _g_stat_blocks (const GLocalFileStat *buf) { return b
inline static time_t _g_stat_atime (const GLocalFileStat *buf) { return buf->st_atime; }
inline static time_t _g_stat_ctime (const GLocalFileStat *buf) { return buf->st_ctime; }
inline static time_t _g_stat_mtime (const GLocalFileStat *buf) { return buf->st_mtime; }
+#else
+inline static time_t _g_stat_atime (const GLocalFileStat *buf) { return buf->st_atim.tv_sec; }
+inline static time_t _g_stat_ctime (const GLocalFileStat *buf) { return buf->st_ctim.tv_sec; }
+inline static time_t _g_stat_mtime (const GLocalFileStat *buf) { return buf->st_mtim.tv_sec; }
#endif
-#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) || defined(G_OS_WIN32)
inline static guint32 _g_stat_atim_nsec (const GLocalFileStat *buf) { return buf->st_atim.tv_nsec; }
inline static guint32 _g_stat_ctim_nsec (const GLocalFileStat *buf) { return buf->st_ctim.tv_nsec; }
inline static guint32 _g_stat_mtim_nsec (const GLocalFileStat *buf) { return buf->st_mtim.tv_nsec; }
+#else
+inline static guint32 _g_stat_atim_nsec (const GLocalFileStat *buf) { return 0; }
+inline static guint32 _g_stat_ctim_nsec (const GLocalFileStat *buf) { return 0; }
+inline static guint32 _g_stat_mtim_nsec (const GLocalFileStat *buf) { return 0; }
#endif
#endif /* !HAVE_STATX */
diff --git a/gio/glocalfilemonitor.c b/gio/glocalfilemonitor.c
index 7e922561a..68afd7b51 100644
--- a/gio/glocalfilemonitor.c
+++ b/gio/glocalfilemonitor.c
@@ -348,7 +348,6 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
gint64 event_time)
{
gboolean interesting = TRUE;
- GFileMonitor *instance = NULL;
g_assert (!child || is_basename (child));
g_assert (!rename_to || is_basename (rename_to));
@@ -359,9 +358,24 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
g_mutex_lock (&fms->lock);
- /* monitor is already gone -- don't bother */
- instance = g_weak_ref_get (&fms->instance_ref);
- if (instance == NULL)
+ /* NOTE:
+ *
+ * We process events even if the file monitor has already been disposed.
+ * The reason is that we must not take a reference to the instance here as
+ * destroying it from the event handling thread will lead to a deadlock when
+ * taking the lock in _ih_sub_cancel.
+ *
+ * This results in seemingly-unbounded growth of the `event_queue` with the
+ * calls to `g_file_monitor_source_queue_event()`. However, each of those sets
+ * the ready time on the #GSource, which means that it will be dispatched in
+ * a subsequent iteration of the #GMainContext it’s attached to. At that
+ * point, `g_file_monitor_source_dispatch()` will return %FALSE, and this will
+ * trigger finalisation of the source. That will clear the `event_queue`.
+ *
+ * If the source is no longer attached, this will return early to prevent
+ * unbounded queueing.
+ */
+ if (g_source_is_destroyed ((GSource *) fms))
{
g_mutex_unlock (&fms->lock);
return TRUE;
@@ -406,7 +420,7 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
g_assert (!other && rename_to);
if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED))
{
- GFile *other;
+ GFile *other_file;
const gchar *dirname;
gchar *allocated_dirname = NULL;
GFileMonitorEvent event;
@@ -421,11 +435,11 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
dirname = allocated_dirname;
}
- other = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
+ other_file = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
g_file_monitor_source_file_changes_done (fms, rename_to);
- g_file_monitor_source_send_event (fms, event, child, other);
+ g_file_monitor_source_send_event (fms, event, child, other_file);
- g_object_unref (other);
+ g_object_unref (other_file);
g_free (allocated_dirname);
}
else
@@ -452,7 +466,6 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
g_file_monitor_source_update_ready_time (fms);
g_mutex_unlock (&fms->lock);
- g_clear_object (&instance);
return interesting;
}
@@ -599,9 +612,9 @@ g_file_monitor_source_dispose (GFileMonitorSource *fms)
g_file_monitor_source_update_ready_time (fms);
- g_mutex_unlock (&fms->lock);
-
g_source_destroy ((GSource *) fms);
+
+ g_mutex_unlock (&fms->lock);
}
static void
diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
index 71a992668..5d6a48840 100644
--- a/gio/glocalfileoutputstream.c
+++ b/gio/glocalfileoutputstream.c
@@ -855,7 +855,7 @@ handle_overwrite_open (const char *filename,
int open_flags;
int res;
int mode;
- int errsv;
+ int errsv = 0;
gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
mode = mode_from_flags_or_info (flags, reference_info);
@@ -1179,7 +1179,7 @@ handle_overwrite_open (const char *filename,
/* Seek back to the start of the file after the backup copy */
if (lseek (fd, 0, SEEK_SET) == -1)
{
- int errsv = errno;
+ errsv = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errsv),
@@ -1195,7 +1195,7 @@ handle_overwrite_open (const char *filename,
if (g_unlink (filename) != 0)
{
- int errsv = errno;
+ errsv = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errsv),
@@ -1211,8 +1211,10 @@ handle_overwrite_open (const char *filename,
fd = g_open (filename, open_flags, mode);
if (fd == -1)
{
- int errsv = errno;
- char *display_name = g_filename_display_name (filename);
+ char *display_name;
+ errsv = errno;
+ display_name = g_filename_display_name (filename);
+
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errsv),
_("Error opening file “%s”: %s"),
@@ -1230,7 +1232,7 @@ handle_overwrite_open (const char *filename,
if (ftruncate (fd, 0) == -1)
#endif
{
- int errsv = errno;
+ errsv = errno;
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errsv),
diff --git a/gio/gmemorymonitorwin32.c b/gio/gmemorymonitorwin32.c
new file mode 100644
index 000000000..c0e09a5bf
--- /dev/null
+++ b/gio/gmemorymonitorwin32.c
@@ -0,0 +1,261 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gmemorymonitor.h"
+#include "gioerror.h"
+#include "ginitable.h"
+#include "giomodule-priv.h"
+#include "glibintl.h"
+#include "glib/gstdio.h"
+#include "gcancellable.h"
+
+#include <windows.h>
+
+#define G_TYPE_MEMORY_MONITOR_WIN32 (g_memory_monitor_win32_get_type ())
+G_DECLARE_FINAL_TYPE (GMemoryMonitorWin32, g_memory_monitor_win32, G, MEMORY_MONITOR_WIN32, GObject)
+
+#define G_MEMORY_MONITOR_WIN32_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
+
+static void g_memory_monitor_win32_iface_init (GMemoryMonitorInterface *iface);
+static void g_memory_monitor_win32_initable_iface_init (GInitableIface *iface);
+
+struct _GMemoryMonitorWin32
+{
+ GObject parent_instance;
+
+ HANDLE event;
+ HANDLE mem;
+ HANDLE thread;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GMemoryMonitorWin32, g_memory_monitor_win32, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_memory_monitor_win32_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR,
+ g_memory_monitor_win32_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_MEMORY_MONITOR_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "win32",
+ 30))
+
+static void
+g_memory_monitor_win32_init (GMemoryMonitorWin32 *win32)
+{
+}
+
+static gboolean
+watch_handler (gpointer user_data)
+{
+ GMemoryMonitorWin32 *win32 = user_data;
+
+ g_signal_emit_by_name (win32, "low-memory-warning",
+ G_MEMORY_MONITOR_WARNING_LEVEL_LOW);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Thread which watches for win32 memory resource events */
+static DWORD WINAPI
+watch_thread_function (LPVOID parameter)
+{
+ GWeakRef *weak_ref = parameter;
+ GMemoryMonitorWin32 *win32 = NULL;
+ HANDLE handles[2] = { 0, };
+ DWORD result;
+ BOOL low_memory_state;
+
+ win32 = g_weak_ref_get (weak_ref);
+ if (!win32)
+ goto end;
+
+ if (!DuplicateHandle (GetCurrentProcess (),
+ win32->event,
+ GetCurrentProcess (),
+ &handles[0],
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ gchar *emsg;
+
+ emsg = g_win32_error_message (GetLastError ());
+ g_debug ("DuplicateHandle failed: %s", emsg);
+ g_free (emsg);
+ goto end;
+ }
+
+ if (!DuplicateHandle (GetCurrentProcess (),
+ win32->mem,
+ GetCurrentProcess (),
+ &handles[1],
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ gchar *emsg;
+
+ emsg = g_win32_error_message (GetLastError ());
+ g_debug ("DuplicateHandle failed: %s", emsg);
+ g_free (emsg);
+ goto end;
+ }
+
+ g_clear_object (&win32);
+
+ while (1)
+ {
+ if (!QueryMemoryResourceNotification (handles[1], &low_memory_state))
+ {
+ gchar *emsg;
+
+ emsg = g_win32_error_message (GetLastError ());
+ g_debug ("QueryMemoryResourceNotification failed: %s", emsg);
+ g_free (emsg);
+ break;
+ }
+
+ win32 = g_weak_ref_get (weak_ref);
+ if (!win32)
+ break;
+
+ if (low_memory_state)
+ {
+ g_idle_add_full (G_PRIORITY_DEFAULT,
+ watch_handler,
+ g_steal_pointer (&win32),
+ g_object_unref);
+ /* throttle a bit the loop */
+ g_usleep (G_USEC_PER_SEC);
+ continue;
+ }
+
+ g_clear_object (&win32);
+
+ result = WaitForMultipleObjects (G_N_ELEMENTS (handles), handles, FALSE, INFINITE);
+ switch (result)
+ {
+ case WAIT_OBJECT_0 + 1:
+ continue;
+
+ case WAIT_FAILED:
+ {
+ gchar *emsg;
+
+ emsg = g_win32_error_message (GetLastError ());
+ g_debug ("WaitForMultipleObjects failed: %s", emsg);
+ g_free (emsg);
+ }
+ G_GNUC_FALLTHROUGH;
+ default:
+ goto end;
+ }
+ }
+
+end:
+ if (handles[0])
+ CloseHandle (handles[0]);
+ if (handles[1])
+ CloseHandle (handles[1]);
+ g_clear_object (&win32);
+ g_weak_ref_clear (weak_ref);
+ g_free (weak_ref);
+ return 0;
+}
+
+static gboolean
+g_memory_monitor_win32_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GMemoryMonitorWin32 *win32 = G_MEMORY_MONITOR_WIN32 (initable);
+ GWeakRef *weak_ref = NULL;
+
+ win32->event = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (win32->event == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to create event");
+ return FALSE;
+ }
+
+ win32->mem = CreateMemoryResourceNotification (LowMemoryResourceNotification);
+ if (win32->mem == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to create resource notification handle");
+ return FALSE;
+ }
+
+ weak_ref = g_new0 (GWeakRef, 1);
+ g_weak_ref_init (weak_ref, win32);
+ /* Use CreateThread (not GThread) with a small stack to make it more lightweight. */
+ win32->thread = CreateThread (NULL, 1024, watch_thread_function, weak_ref, 0, NULL);
+ if (win32->thread == NULL)
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to create memory resource notification thread");
+ g_weak_ref_clear (weak_ref);
+ g_free (weak_ref);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+g_memory_monitor_win32_finalize (GObject *object)
+{
+ GMemoryMonitorWin32 *win32 = G_MEMORY_MONITOR_WIN32 (object);
+
+ if (win32->thread)
+ {
+ SetEvent (win32->event);
+ WaitForSingleObject (win32->thread, INFINITE);
+ CloseHandle (win32->thread);
+ }
+
+ if (win32->event)
+ CloseHandle (win32->event);
+
+ if (win32->mem)
+ CloseHandle (win32->mem);
+
+ G_OBJECT_CLASS (g_memory_monitor_win32_parent_class)->finalize (object);
+}
+
+static void
+g_memory_monitor_win32_class_init (GMemoryMonitorWin32Class *nl_class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
+
+ gobject_class->finalize = g_memory_monitor_win32_finalize;
+}
+
+static void
+g_memory_monitor_win32_iface_init (GMemoryMonitorInterface *monitor_iface)
+{
+}
+
+static void
+g_memory_monitor_win32_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_memory_monitor_win32_initable_init;
+}
diff --git a/gio/gmemoryoutputstream.c b/gio/gmemoryoutputstream.c
index b120ccedc..6a410ebb5 100644
--- a/gio/gmemoryoutputstream.c
+++ b/gio/gmemoryoutputstream.c
@@ -29,6 +29,7 @@
#include "gioerror.h"
#include "string.h"
#include "glibintl.h"
+#include "gutilsprivate.h"
/**
@@ -596,17 +597,6 @@ array_resize (GMemoryOutputStream *ostream,
return TRUE;
}
-static gsize
-g_nearest_pow (gsize num)
-{
- gsize n = 1;
-
- while (n < num && n > 0)
- n <<= 1;
-
- return n;
-}
-
static gssize
g_memory_output_stream_write (GOutputStream *stream,
const void *buffer,
diff --git a/gio/gnetworkservice.c b/gio/gnetworkservice.c
index 2b8571e9b..f5ba9d848 100644
--- a/gio/gnetworkservice.c
+++ b/gio/gnetworkservice.c
@@ -445,7 +445,7 @@ g_network_service_address_enumerator_next (GSocketAddressEnumerator *enumerator
{
if (srv_enum->addr_enum == NULL && srv_enum->t)
{
- GError *error = NULL;
+ GError *my_error = NULL;
gchar *uri;
gchar *hostname;
GSocketConnectable *addr;
@@ -477,15 +477,15 @@ g_network_service_address_enumerator_next (GSocketAddressEnumerator *enumerator
addr = g_network_address_parse_uri (uri,
g_srv_target_get_port (target),
- &error);
+ &my_error);
g_free (uri);
if (addr == NULL)
{
if (srv_enum->error == NULL)
- srv_enum->error = error;
+ srv_enum->error = my_error;
else
- g_error_free (error);
+ g_error_free (my_error);
continue;
}
@@ -498,18 +498,18 @@ g_network_service_address_enumerator_next (GSocketAddressEnumerator *enumerator
if (srv_enum->addr_enum)
{
- GError *error = NULL;
+ GError *my_error = NULL;
ret = g_socket_address_enumerator_next (srv_enum->addr_enum,
cancellable,
- &error);
+ &my_error);
- if (error)
+ if (my_error)
{
if (srv_enum->error == NULL)
- srv_enum->error = error;
+ srv_enum->error = my_error;
else
- g_error_free (error);
+ g_error_free (my_error);
}
if (!ret)
diff --git a/gio/gopenuriportal.c b/gio/gopenuriportal.c
index 6ef8f037c..2f527d828 100644
--- a/gio/gopenuriportal.c
+++ b/gio/gopenuriportal.c
@@ -108,10 +108,10 @@ g_openuri_portal_open_uri (const char *uri,
errsv = errno;
if (fd == -1)
{
- g_free (path);
- g_variant_builder_clear (&opt_builder);
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
"Failed to open '%s'", path);
+ g_free (path);
+ g_variant_builder_clear (&opt_builder);
return FALSE;
}
diff --git a/gio/goutputstream.c b/gio/goutputstream.c
index 8e48803be..3547b8f12 100644
--- a/gio/goutputstream.c
+++ b/gio/goutputstream.c
@@ -293,7 +293,7 @@ g_output_stream_write_all (GOutputStream *stream,
gssize res;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
- g_return_val_if_fail (buffer != NULL, FALSE);
+ g_return_val_if_fail (buffer != NULL || count == 0, FALSE);
_bytes_written = 0;
while (_bytes_written < count)
diff --git a/gio/gpowerprofilemonitor.c b/gio/gpowerprofilemonitor.c
index f5028b3e8..00bdc94eb 100644
--- a/gio/gpowerprofilemonitor.c
+++ b/gio/gpowerprofilemonitor.c
@@ -41,10 +41,10 @@
* some systems).
*
* When in “Low Power” mode, it is recommended that applications:
- * - disabling automatic downloads
+ * - disable automatic downloads;
* - reduce the rate of refresh from online sources such as calendar or
- * email synchronisation
- * - if the application has expensive visual effects, reduce them
+ * email synchronisation;
+ * - reduce the use of expensive visual effects.
*
* It is also likely that OS components providing services to applications will
* lower their own background activity, for the sake of the system.
diff --git a/gio/gproxyaddressenumerator.c b/gio/gproxyaddressenumerator.c
index 654baade5..de932ff91 100644
--- a/gio/gproxyaddressenumerator.c
+++ b/gio/gproxyaddressenumerator.c
@@ -25,6 +25,7 @@
#include "gasyncresult.h"
#include "ginetaddress.h"
+#include "gioerror.h"
#include "glibintl.h"
#include "gnetworkaddress.h"
#include "gnetworkingprivate.h"
@@ -87,6 +88,20 @@ struct _GProxyAddressEnumeratorPrivate
gboolean supports_hostname;
GList *next_dest_ip;
GError *last_error;
+
+ /* ever_enumerated is TRUE after we've returned a result for the first time
+ * via g_proxy_address_enumerator_next() or _next_async(). If FALSE, we have
+ * never returned yet, and should return an error if returning NULL because
+ * it does not make sense for a proxy resolver to return NULL except on error.
+ * (Whereas a DNS resolver would return NULL with no error to indicate "no
+ * results", a proxy resolver would want to return "direct://" instead, so
+ * NULL without error does not make sense for us.)
+ *
+ * But if ever_enumerated is TRUE, then we must not report any further errors
+ * (except for G_IO_ERROR_CANCELLED), because this is an API contract of
+ * GSocketAddressEnumerator.
+ */
+ gboolean ever_enumerated;
};
G_DEFINE_TYPE_WITH_PRIVATE (GProxyAddressEnumerator, g_proxy_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
@@ -171,8 +186,9 @@ g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
GSocketAddress *result = NULL;
GError *first_error = NULL;
- if (priv->proxies == NULL)
+ if (!priv->ever_enumerated)
{
+ g_assert (priv->proxies == NULL);
priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
priv->dest_uri,
cancellable,
@@ -180,7 +196,10 @@ g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
priv->next_proxy = priv->proxies;
if (priv->proxies == NULL)
- return NULL;
+ {
+ priv->ever_enumerated = TRUE;
+ return NULL;
+ }
}
while (result == NULL && (*priv->next_proxy || priv->addr_enum))
@@ -294,29 +313,37 @@ g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
}
}
- if (result == NULL && first_error)
+ if (result == NULL && first_error && (!priv->ever_enumerated || g_error_matches (first_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
g_propagate_error (error, first_error);
else if (first_error)
g_error_free (first_error);
- return result;
-}
+ if (result == NULL && error != NULL && *error == NULL && !priv->ever_enumerated)
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unspecified proxy lookup failure");
+ priv->ever_enumerated = TRUE;
+ return result;
+}
static void
complete_async (GTask *task)
{
GProxyAddressEnumeratorPrivate *priv = g_task_get_task_data (task);
- if (priv->last_error)
+ if (priv->last_error && (!priv->ever_enumerated || g_error_matches (priv->last_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)))
{
g_task_return_error (task, priv->last_error);
priv->last_error = NULL;
}
+ else if (!priv->ever_enumerated)
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Unspecified proxy lookup failure");
else
g_task_return_pointer (task, NULL, NULL);
+ priv->ever_enumerated = TRUE;
+
+ g_clear_error (&priv->last_error);
g_object_unref (task);
}
@@ -388,6 +415,7 @@ return_result (GTask *task)
}
}
+ priv->ever_enumerated = TRUE;
g_task_return_pointer (task, result, g_object_unref);
g_object_unref (task);
}
diff --git a/gio/gregistrysettingsbackend.c b/gio/gregistrysettingsbackend.c
index bae066a0a..a7171a21f 100644
--- a/gio/gregistrysettingsbackend.c
+++ b/gio/gregistrysettingsbackend.c
@@ -1200,7 +1200,7 @@ g_registry_backend_get_writable (GSettingsBackend *backend,
GRegistryBackend *self = G_REGISTRY_BACKEND (backend);
gchar *path_name;
gunichar2 *path_namew;
- gchar *value_name;
+ gchar *value_name = NULL;
HKEY hpath;
LONG result;
@@ -1495,14 +1495,14 @@ registry_cache_update (GRegistryBackend *self,
child_item->readable = TRUE;
if (changed && event != NULL)
{
- gchar *item;
+ gchar *item_path;
if (partial_key_name == NULL)
- item = g_strdup (buffer);
+ item_path = g_strdup (buffer);
else
- item = g_build_path ("/", partial_key_name, buffer, NULL);
+ item_path = g_build_path ("/", partial_key_name, buffer, NULL);
- g_ptr_array_add (event->items, item);
+ g_ptr_array_add (event->items, item_path);
}
g_free (buffer);
diff --git a/gio/gresolver.c b/gio/gresolver.c
index cd5445a65..90b057cbf 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -64,7 +64,8 @@ static guint signals[LAST_SIGNAL] = { 0 };
struct _GResolverPrivate {
#ifdef G_OS_UNIX
- time_t resolv_conf_timestamp;
+ GMutex mutex;
+ time_t resolv_conf_timestamp; /* protected by @mutex */
#else
int dummy;
#endif
@@ -149,8 +150,24 @@ g_resolver_real_lookup_service_finish (GResolver *resolver,
}
static void
+g_resolver_finalize (GObject *object)
+{
+#ifdef G_OS_UNIX
+ GResolver *resolver = G_RESOLVER (object);
+
+ g_mutex_clear (&resolver->priv->mutex);
+#endif
+
+ G_OBJECT_CLASS (g_resolver_parent_class)->finalize (object);
+}
+
+static void
g_resolver_class_init (GResolverClass *resolver_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (resolver_class);
+
+ object_class->finalize = g_resolver_finalize;
+
/* Automatically pass these over to the lookup_records methods */
resolver_class->lookup_service = g_resolver_real_lookup_service;
resolver_class->lookup_service_async = g_resolver_real_lookup_service_async;
@@ -185,6 +202,8 @@ g_resolver_init (GResolver *resolver)
#ifdef G_OS_UNIX
if (stat (_PATH_RESCONF, &st) == 0)
resolver->priv->resolv_conf_timestamp = st.st_mtime;
+
+ g_mutex_init (&resolver->priv->mutex);
#endif
}
@@ -242,27 +261,23 @@ g_resolver_set_default (GResolver *resolver)
G_UNLOCK (default_resolver);
}
-/* Bionic has res_init() but it's not in any header */
-#ifdef __BIONIC__
-int res_init (void);
-#endif
-
static void
-g_resolver_maybe_reload (GResolver *resolver)
+maybe_emit_reload (GResolver *resolver)
{
#ifdef G_OS_UNIX
struct stat st;
if (stat (_PATH_RESCONF, &st) == 0)
{
+ g_mutex_lock (&resolver->priv->mutex);
if (st.st_mtime != resolver->priv->resolv_conf_timestamp)
{
resolver->priv->resolv_conf_timestamp = st.st_mtime;
-#ifdef HAVE_RES_INIT
- res_init ();
-#endif
+ g_mutex_unlock (&resolver->priv->mutex);
g_signal_emit (resolver, signals[RELOAD], 0);
}
+ else
+ g_mutex_unlock (&resolver->priv->mutex);
}
#endif
}
@@ -444,7 +459,7 @@ lookup_by_name_real (GResolver *resolver,
return NULL;
}
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
if (flags != G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
{
@@ -530,7 +545,7 @@ g_resolver_lookup_by_name (GResolver *resolver,
*
* This differs from g_resolver_lookup_by_name() in that you can modify
* the lookup behavior with @flags. For example this can be used to limit
- * results with #G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY.
+ * results with %G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY.
*
* Returns: (element-type GInetAddress) (transfer full): a non-empty #GList
* of #GInetAddress, or %NULL on error. You
@@ -602,7 +617,7 @@ lookup_by_name_async_real (GResolver *resolver,
return;
}
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
if (flags != G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
{
@@ -839,7 +854,7 @@ g_resolver_lookup_by_address (GResolver *resolver,
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
g_return_val_if_fail (G_IS_INET_ADDRESS (address), NULL);
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
return G_RESOLVER_GET_CLASS (resolver)->
lookup_by_address (resolver, address, cancellable, error);
}
@@ -868,7 +883,7 @@ g_resolver_lookup_by_address_async (GResolver *resolver,
g_return_if_fail (G_IS_RESOLVER (resolver));
g_return_if_fail (G_IS_INET_ADDRESS (address));
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
G_RESOLVER_GET_CLASS (resolver)->
lookup_by_address_async (resolver, address, cancellable, callback, user_data);
}
@@ -985,7 +1000,7 @@ g_resolver_lookup_service (GResolver *resolver,
return NULL;
}
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
targets = G_RESOLVER_GET_CLASS (resolver)->
lookup_service (resolver, rrname, cancellable, error);
@@ -1037,7 +1052,7 @@ g_resolver_lookup_service_async (GResolver *resolver,
return;
}
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
G_RESOLVER_GET_CLASS (resolver)->
lookup_service_async (resolver, rrname, cancellable, callback, user_data);
@@ -1136,7 +1151,7 @@ g_resolver_lookup_records (GResolver *resolver,
g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
g_return_val_if_fail (rrname != NULL, NULL);
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
records = G_RESOLVER_GET_CLASS (resolver)->
lookup_records (resolver, rrname, record_type, cancellable, error);
@@ -1170,7 +1185,7 @@ g_resolver_lookup_records_async (GResolver *resolver,
g_return_if_fail (G_IS_RESOLVER (resolver));
g_return_if_fail (rrname != NULL);
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
G_RESOLVER_GET_CLASS (resolver)->
lookup_records_async (resolver, rrname, record_type, cancellable, callback, user_data);
}
@@ -1210,15 +1225,21 @@ g_resolver_lookup_records_finish (GResolver *resolver,
guint64
g_resolver_get_serial (GResolver *resolver)
{
+ guint64 result;
+
g_return_val_if_fail (G_IS_RESOLVER (resolver), 0);
- g_resolver_maybe_reload (resolver);
+ maybe_emit_reload (resolver);
#ifdef G_OS_UNIX
- return (guint64) resolver->priv->resolv_conf_timestamp;
+ g_mutex_lock (&resolver->priv->mutex);
+ result = resolver->priv->resolv_conf_timestamp;
+ g_mutex_unlock (&resolver->priv->mutex);
#else
- return 1;
+ result = 1;
#endif
+
+ return result;
}
/**
diff --git a/gio/gresource-tool.c b/gio/gresource-tool.c
index aa718974e..7ebdddaf0 100644
--- a/gio/gresource-tool.c
+++ b/gio/gresource-tool.c
@@ -180,7 +180,7 @@ elf_foreach_resource_section (Elf *elf,
SectionCallback callback,
gpointer data)
{
- int ret;
+ int ret G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
size_t shstrndx, shnum;
size_t scnidx;
Elf_Scn *scn;
@@ -477,8 +477,8 @@ static gint
cmd_help (gboolean requested,
const gchar *command)
{
- const gchar *description;
- const gchar *synopsis;
+ const gchar *description = NULL;
+ const gchar *synopsis = NULL;
gchar *option;
GString *string;
diff --git a/gio/gresource.c b/gio/gresource.c
index 53933f9d2..45ca92b1f 100644
--- a/gio/gresource.c
+++ b/gio/gresource.c
@@ -346,7 +346,7 @@ g_resource_find_overlay (const gchar *path,
if (envvar != NULL)
{
gchar **parts;
- gint i, j;
+ gint j;
parts = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
@@ -414,7 +414,7 @@ g_resource_find_overlay (const gchar *path,
/* We go out of the way to avoid malloc() in the normal case
* where the environment variable is not set.
*/
- static const gchar * const empty_strv[0 + 1];
+ static const gchar *const empty_strv[0 + 1] = { 0 };
result = empty_strv;
}
diff --git a/gio/gresourcefile.c b/gio/gresourcefile.c
index 429e9ef49..35dffebe9 100644
--- a/gio/gresourcefile.c
+++ b/gio/gresourcefile.c
@@ -447,8 +447,8 @@ g_resource_file_query_info (GFile *file,
GFileInfo *info;
GFileAttributeMatcher *matcher;
gboolean res;
- gsize size;
- guint32 resource_flags;
+ gsize size = 0;
+ guint32 resource_flags = 0;
char **children;
gboolean is_dir;
char *base;
diff --git a/gio/gsettings-tool.c b/gio/gsettings-tool.c
index 7559a6b1b..9352b70f5 100644
--- a/gio/gsettings-tool.c
+++ b/gio/gsettings-tool.c
@@ -112,11 +112,19 @@ check_path (const gchar *path)
return TRUE;
}
+static int
+qsort_cmp (const void *a,
+ const void *b)
+{
+ return g_strcmp0 (*(gchar* const*)a, *(gchar* const*)b);
+}
+
static void
output_list (gchar **list)
{
gint i;
+ qsort (list, g_strv_length (list), sizeof (gchar*), qsort_cmp);
for (i = 0; list[i]; i++)
g_print ("%s\n", list[i]);
}
@@ -195,6 +203,7 @@ gsettings_list_children (void)
gint i;
children = g_settings_list_children (global_settings);
+ qsort (children, g_strv_length (children), sizeof (gchar*), qsort_cmp);
for (i = 0; children[i]; i++)
{
gsize len = strlen (children[i]);
@@ -239,6 +248,7 @@ enumerate (GSettings *settings)
g_object_get (settings, "settings-schema", &schema, NULL);
keys = g_settings_schema_list_keys (schema);
+ qsort (keys, g_strv_length (keys), sizeof (gchar*), qsort_cmp);
for (i = 0; keys[i]; i++)
{
GVariant *value;
@@ -263,6 +273,7 @@ list_recursively (GSettings *settings)
enumerate (settings);
children = g_settings_list_children (settings);
+ qsort (children, g_strv_length (children), sizeof (gchar*), qsort_cmp);
for (i = 0; children[i]; i++)
{
gboolean will_see_elsewhere = FALSE;
@@ -306,6 +317,7 @@ gsettings_list_recursively (void)
gint i;
g_settings_schema_source_list_schemas (global_schema_source, TRUE, &schemas, NULL);
+ qsort (schemas, g_strv_length (schemas), sizeof (gchar*), qsort_cmp);
for (i = 0; schemas[i]; i++)
{
@@ -558,8 +570,8 @@ static int
gsettings_help (gboolean requested,
const gchar *command)
{
- const gchar *description;
- const gchar *synopsis;
+ const gchar *description = NULL;
+ const gchar *synopsis = NULL;
GString *string;
string = g_string_new (NULL);
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 9130dafd5..21ae2ff10 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -248,7 +248,7 @@
* looks for a boolean property with the name "sensitivity" and
* automatically binds it to the writability of the bound setting.
* If this 'magic' gets in the way, it can be suppressed with the
- * #G_SETTINGS_BIND_NO_SENSITIVITY flag.
+ * %G_SETTINGS_BIND_NO_SENSITIVITY flag.
*
* ## Relocatable schemas # {#gsettings-relocatable}
*
@@ -406,12 +406,12 @@ g_settings_real_writable_change_event (GSettings *settings,
for (i = 0; i < n_keys; i++)
{
- const gchar *key = g_quark_to_string (keys[i]);
+ const gchar *key_name = g_quark_to_string (keys[i]);
- if (g_str_has_suffix (key, "/"))
+ if (g_str_has_suffix (key_name, "/"))
continue;
- g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGED], keys[i], key);
+ g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGED], keys[i], key_name);
}
return FALSE;
@@ -970,7 +970,7 @@ g_settings_class_init (GSettingsClass *class)
* call to g_settings_new(). The new #GSettings will hold a reference
* on the context. See g_main_context_push_thread_default().
*
- * Returns: a new #GSettings object
+ * Returns: (not nullable) (transfer full): a new #GSettings object
*
* Since: 2.26
*/
@@ -1018,7 +1018,7 @@ path_is_valid (const gchar *path)
* begins and ends with '/' and does not contain two consecutive '/'
* characters.
*
- * Returns: a new #GSettings object
+ * Returns: (not nullable) (transfer full): a new #GSettings object
*
* Since: 2.26
*/
@@ -1049,7 +1049,7 @@ g_settings_new_with_path (const gchar *schema_id,
* the system to get a settings object that modifies the system default
* settings instead of the settings for this user.
*
- * Returns: a new #GSettings object
+ * Returns: (not nullable) (transfer full): a new #GSettings object
*
* Since: 2.26
*/
@@ -1078,7 +1078,7 @@ g_settings_new_with_backend (const gchar *schema_id,
* This is a mix of g_settings_new_with_backend() and
* g_settings_new_with_path().
*
- * Returns: a new #GSettings object
+ * Returns: (not nullable) (transfer full): a new #GSettings object
*
* Since: 2.26
*/
@@ -1128,7 +1128,7 @@ g_settings_new_with_backend_and_path (const gchar *schema_id,
* @path is non-%NULL and not equal to the path that the schema does
* have.
*
- * Returns: a new #GSettings object
+ * Returns: (not nullable) (transfer full): a new #GSettings object
*
* Since: 2.32
*/
@@ -1205,7 +1205,7 @@ g_settings_read_from_backend (GSettings *settings,
* It is a programmer error to give a @key that isn't contained in the
* schema for @settings.
*
- * Returns: a new #GVariant
+ * Returns: (not nullable) (transfer full): a new #GVariant
*
* Since: 2.26
*/
@@ -1725,7 +1725,7 @@ g_settings_set (GSettings *settings,
* what is returned by this function. %NULL is valid; it is returned
* just as any other value would be.
*
- * Returns: (transfer full): the result, which may be %NULL
+ * Returns: (nullable) (transfer full): the result, which may be %NULL
**/
gpointer
g_settings_get_mapped (GSettings *settings,
@@ -1792,7 +1792,7 @@ g_settings_get_mapped (GSettings *settings,
* It is a programmer error to give a @key that isn't specified as
* having a string type in the schema for @settings.
*
- * Returns: a newly-allocated string
+ * Returns: (not nullable) (transfer full): a newly-allocated string
*
* Since: 2.26
*/
@@ -2186,7 +2186,7 @@ g_settings_set_boolean (GSettings *settings,
* It is a programmer error to give a @key that isn't specified as
* having an array of strings type in the schema for @settings.
*
- * Returns: (array zero-terminated=1) (transfer full): a
+ * Returns: (array zero-terminated=1) (not nullable) (transfer full): a
* newly-allocated, %NULL-terminated array of strings, the value that
* is stored at @key in @settings.
*
@@ -2471,8 +2471,8 @@ g_settings_get_child (GSettings *settings,
* You should free the return value with g_strfreev() when you are done
* with it.
*
- * Returns: (transfer full) (element-type utf8): a list of the keys on
- * @settings, in no defined order
+ * Returns: (not nullable) (transfer full) (element-type utf8): a list
+ * of the keys on @settings, in no defined order
* Deprecated: 2.46: Use g_settings_schema_list_keys() instead.
*/
gchar **
@@ -2497,8 +2497,8 @@ g_settings_list_keys (GSettings *settings)
* You should free the return value with g_strfreev() when you are done
* with it.
*
- * Returns: (transfer full) (element-type utf8): a list of the children on
- * @settings, in no defined order
+ * Returns: (not nullable) (transfer full) (element-type utf8): a list of the children
+ * on @settings, in no defined order
*/
gchar **
g_settings_list_children (GSettings *settings)
@@ -3382,7 +3382,7 @@ g_settings_action_enabled_changed (GSettings *settings,
* activations take the new value for the key (which must have the
* correct type).
*
- * Returns: (transfer full): a new #GAction
+ * Returns: (not nullable) (transfer full): a new #GAction
*
* Since: 2.32
**/
diff --git a/gio/gsettings.h b/gio/gsettings.h
index d3d5a076f..cb35d288e 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -284,7 +284,7 @@ typedef gboolean (*GSettingsGetMapping) (GVarian
* @G_SETTINGS_BIND_SET: Update the setting when the #GObject property changes.
* It is an error to use this flag if the property is not readable.
* @G_SETTINGS_BIND_NO_SENSITIVITY: Do not try to bind a "sensitivity" property to the writability of the setting
- * @G_SETTINGS_BIND_GET_NO_CHANGES: When set in addition to #G_SETTINGS_BIND_GET, set the #GObject property
+ * @G_SETTINGS_BIND_GET_NO_CHANGES: When set in addition to %G_SETTINGS_BIND_GET, set the #GObject property
* value initially from the setting, but do not listen for changes of the setting
* @G_SETTINGS_BIND_INVERT_BOOLEAN: When passed to g_settings_bind(), uses a pair of mapping functions that invert
* the boolean value when mapping between the setting and the property. The setting and property must both
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index b124bc7ec..a1a23cc56 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -60,7 +60,7 @@ static gboolean g_settings_has_backend;
* non-strictly-typed data that is stored in a hierarchy. To implement
* an alternative storage backend for #GSettings, you need to implement
* the #GSettingsBackend interface and then make it implement the
- * extension point #G_SETTINGS_BACKEND_EXTENSION_POINT_NAME.
+ * extension point %G_SETTINGS_BACKEND_EXTENSION_POINT_NAME.
*
* The interface defines methods for reading and writing values, a
* method for determining if writing of certain values will fail
@@ -703,7 +703,7 @@ g_settings_backend_changed_tree (GSettingsBackend *backend,
* backend (ie: the one that the backend would contain if
* g_settings_reset() were called).
*
- * Returns: the value that was read, or %NULL
+ * Returns: (nullable) (transfer full): the value that was read, or %NULL
*/
GVariant *
g_settings_backend_read (GSettingsBackend *backend,
@@ -741,7 +741,7 @@ g_settings_backend_read (GSettingsBackend *backend,
* value for themselves, then this will return %NULL (even if the
* sysadmin has provided a default value).
*
- * Returns: the value that was read, or %NULL
+ * Returns: (nullable) (transfer full): the value that was read, or %NULL
*/
GVariant *
g_settings_backend_read_user_value (GSettingsBackend *backend,
@@ -1041,7 +1041,8 @@ g_settings_backend_get_default (void)
* If this is not implemented in the backend, then a %TRUE
* #GSimplePermission is returned.
*
- * Returns: a non-%NULL #GPermission. Free with g_object_unref()
+ * Returns: (not nullable) (transfer full): a non-%NULL #GPermission.
+ * Free with g_object_unref()
*/
GPermission *
g_settings_backend_get_permission (GSettingsBackend *backend,
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 3b5ba0d56..ef4ec1799 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -202,7 +202,7 @@ static GSettingsSchemaSource *schema_sources;
*
* Increase the reference count of @source, returning a new reference.
*
- * Returns: a new reference to @source
+ * Returns: (transfer full) (not nullable): a new reference to @source
*
* Since: 2.32
**/
@@ -869,9 +869,9 @@ ensure_schema_lists (void)
*
* Deprecated.
*
- * Returns: (element-type utf8) (transfer none): a list of #GSettings
- * schemas that are available, in no defined order. The list must not be
- * modified or freed.
+ * Returns: (element-type utf8) (transfer none) (not nullable): a list of
+ * #GSettings schemas that are available, in no defined order. The list
+ * must not be modified or freed.
*
* Since: 2.26
*
@@ -893,9 +893,9 @@ g_settings_list_schemas (void)
*
* Deprecated.
*
- * Returns: (element-type utf8) (transfer none): a list of relocatable
- * #GSettings schemas that are available, in no defined order. The list must
- * not be modified or freed.
+ * Returns: (element-type utf8) (transfer none) (not nullable): a list of
+ * relocatable #GSettings schemas that are available, in no defined order.
+ * The list must not be modified or freed.
*
* Since: 2.28
*
@@ -915,7 +915,7 @@ g_settings_list_relocatable_schemas (void)
*
* Increase the reference count of @schema, returning a new reference.
*
- * Returns: a new reference to @schema
+ * Returns: (transfer full) (not nullable): a new reference to @schema
*
* Since: 2.32
**/
@@ -1066,8 +1066,8 @@ g_settings_schema_has_key (GSettingsSchema *schema,
* You should free the return value with g_strfreev() when you are done
* with it.
*
- * Returns: (transfer full) (element-type utf8): a list of the children on
- * @settings, in no defined order
+ * Returns: (not nullable) (transfer full) (element-type utf8): a list of
+ * the children on @settings, in no defined order
*
* Since: 2.44
*/
@@ -1111,8 +1111,8 @@ g_settings_schema_list_children (GSettingsSchema *schema)
* (since you should already know what keys are in your schema). This
* function is intended for introspection reasons.
*
- * Returns: (transfer full) (element-type utf8): a list of the keys on
- * @schema, in no defined order
+ * Returns: (not nullable) (transfer full) (element-type utf8): a list
+ * of the keys on @schema, in no defined order
*
* Since: 2.46
*/
@@ -1250,7 +1250,7 @@ g_settings_schema_list (GSettingsSchema *schema,
*
* Get the ID of @schema.
*
- * Returns: (transfer none): the ID
+ * Returns: (not nullable) (transfer none): the ID
**/
const gchar *
g_settings_schema_get_id (GSettingsSchema *schema)
@@ -1581,7 +1581,7 @@ G_DEFINE_BOXED_TYPE (GSettingsSchemaKey, g_settings_schema_key, g_settings_schem
*
* Increase the reference count of @key, returning a new reference.
*
- * Returns: a new reference to @key
+ * Returns: (not nullable) (transfer full): a new reference to @key
*
* Since: 2.40
**/
@@ -1626,7 +1626,7 @@ g_settings_schema_key_unref (GSettingsSchemaKey *key)
* It is a programmer error to request a key that does not exist. See
* g_settings_schema_list_keys().
*
- * Returns: (transfer full): the #GSettingsSchemaKey for @name
+ * Returns: (not nullable) (transfer full): the #GSettingsSchemaKey for @name
*
* Since: 2.40
**/
@@ -1652,7 +1652,7 @@ g_settings_schema_get_key (GSettingsSchema *schema,
*
* Gets the name of @key.
*
- * Returns: the name of @key.
+ * Returns: (not nullable) (transfer none): the name of @key.
*
* Since: 2.44
*/
@@ -1682,7 +1682,7 @@ g_settings_schema_key_get_name (GSettingsSchemaKey *key)
* function has to parse all of the source XML files in the schema
* directory.
*
- * Returns: (nullable): the summary for @key, or %NULL
+ * Returns: (nullable) (transfer none): the summary for @key, or %NULL
*
* Since: 2.34
**/
@@ -1717,7 +1717,7 @@ g_settings_schema_key_get_summary (GSettingsSchemaKey *key)
* function has to parse all of the source XML files in the schema
* directory.
*
- * Returns: (nullable): the description for @key, or %NULL
+ * Returns: (nullable) (transfer none): the description for @key, or %NULL
*
* Since: 2.34
**/
@@ -1739,7 +1739,7 @@ g_settings_schema_key_get_description (GSettingsSchemaKey *key)
*
* Gets the #GVariantType of @key.
*
- * Returns: (transfer none): the type of @key
+ * Returns: (not nullable) (transfer none): the type of @key
*
* Since: 2.40
**/
@@ -1760,7 +1760,7 @@ g_settings_schema_key_get_value_type (GSettingsSchemaKey *key)
* Note that this is the default value according to the schema. System
* administrator defaults and lockdown are not visible via this API.
*
- * Returns: (transfer full): the default value for the key
+ * Returns: (not nullable) (transfer full): the default value for the key
*
* Since: 2.40
**/
@@ -1823,7 +1823,7 @@ g_settings_schema_key_get_default_value (GSettingsSchemaKey *key)
* You should free the returned value with g_variant_unref() when it is
* no longer needed.
*
- * Returns: (transfer full): a #GVariant describing the range
+ * Returns: (not nullable) (transfer full): a #GVariant describing the range
*
* Since: 2.40
**/
diff --git a/gio/gsimpleasyncresult.c b/gio/gsimpleasyncresult.c
index c971bdfcd..7fd9b43b5 100644
--- a/gio/gsimpleasyncresult.c
+++ b/gio/gsimpleasyncresult.c
@@ -705,7 +705,7 @@ g_simple_async_result_take_error (GSimpleAsyncResult *simple,
/**
* g_simple_async_result_set_error_va: (skip)
* @simple: a #GSimpleAsyncResult.
- * @domain: a #GQuark (usually #G_IO_ERROR).
+ * @domain: a #GQuark (usually %G_IO_ERROR).
* @code: an error code.
* @format: a formatted error reporting string.
* @args: va_list of arguments.
@@ -735,7 +735,7 @@ g_simple_async_result_set_error_va (GSimpleAsyncResult *simple,
/**
* g_simple_async_result_set_error: (skip)
* @simple: a #GSimpleAsyncResult.
- * @domain: a #GQuark (usually #G_IO_ERROR).
+ * @domain: a #GQuark (usually %G_IO_ERROR).
* @code: an error code.
* @format: a formatted error reporting string.
* @...: a list of variables to fill in @format.
@@ -1007,7 +1007,7 @@ g_simple_async_result_is_valid (GAsyncResult *result,
* @object: (nullable): a #GObject, or %NULL.
* @callback: a #GAsyncReadyCallback.
* @user_data: user data passed to @callback.
- * @domain: a #GQuark containing the error domain (usually #G_IO_ERROR).
+ * @domain: a #GQuark containing the error domain (usually %G_IO_ERROR).
* @code: a specific error code.
* @format: a formatted error reporting string.
* @...: a list of variables to fill in @format.
diff --git a/gio/gsimpleproxyresolver.c b/gio/gsimpleproxyresolver.c
index 2d6009975..f19d56743 100644
--- a/gio/gsimpleproxyresolver.c
+++ b/gio/gsimpleproxyresolver.c
@@ -493,7 +493,7 @@ g_simple_proxy_resolver_iface_init (GProxyResolverInterface *iface)
* g_simple_proxy_resolver_new:
* @default_proxy: (nullable): the default proxy to use, eg
* "socks://192.168.1.1"
- * @ignore_hosts: (nullable): an optional list of hosts/IP addresses
+ * @ignore_hosts: (array zero-terminated=1) (nullable): an optional list of hosts/IP addresses
* to not use a proxy for.
*
* Creates a new #GSimpleProxyResolver. See
@@ -544,7 +544,7 @@ g_simple_proxy_resolver_set_default_proxy (GSimpleProxyResolver *resolver,
/**
* g_simple_proxy_resolver_set_ignore_hosts:
* @resolver: a #GSimpleProxyResolver
- * @ignore_hosts: %NULL-terminated list of hosts/IP addresses
+ * @ignore_hosts: (array zero-terminated=1): %NULL-terminated list of hosts/IP addresses
* to not use a proxy for
*
* Sets the list of ignored hosts.
diff --git a/gio/gsocket.c b/gio/gsocket.c
index d13e2cab7..be5b96adf 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -76,6 +76,10 @@
#include "glibintl.h"
#include "gioprivate.h"
+#ifdef G_OS_WIN32
+#include "giowin32-afunix.h"
+#endif
+
/**
* SECTION:gsocket
* @short_description: Low-level socket object
@@ -3138,7 +3142,7 @@ g_socket_get_available_bytes (GSocket *socket)
* systems add internal header size to the reported size, making it
* unusable for this function. */
avail = recv (socket->priv->fd, buf, bufsize, MSG_PEEK);
- if (avail == -1)
+ if ((gint) avail == -1)
{
int errsv = get_socket_errno ();
#ifdef G_OS_WIN32
@@ -3997,7 +4001,10 @@ socket_source_dispatch (GSource *source,
gboolean ret;
#ifdef G_OS_WIN32
- events = update_condition (socket_source->socket);
+ if ((socket_source->pollfd.revents & G_IO_NVAL) != 0)
+ events = G_IO_NVAL;
+ else
+ events = update_condition (socket_source->socket);
#else
if (g_socket_is_closed (socket_source->socket))
{
@@ -4570,8 +4577,7 @@ G_STMT_START { \
_msg->msg_control = NULL; \
else \
{ \
- _msg->msg_control = g_alloca (_msg->msg_controllen); \
- memset (_msg->msg_control, '\0', _msg->msg_controllen); \
+ _msg->msg_control = g_alloca0 (_msg->msg_controllen); \
} \
\
cmsg = CMSG_FIRSTHDR (_msg); \
@@ -5275,7 +5281,7 @@ g_socket_send_messages_with_timeout (GSocket *socket,
#else
{
gssize result;
- gint i;
+ guint i;
gint64 wait_timeout;
wait_timeout = timeout_us;
@@ -5305,7 +5311,11 @@ g_socket_send_messages_with_timeout (GSocket *socket,
#endif
}
- result = pollable_result == G_POLLABLE_RETURN_OK ? bytes_written : -1;
+ if (G_MAXSSIZE > bytes_written &&
+ pollable_result == G_POLLABLE_RETURN_OK)
+ result = (gssize) bytes_written;
+ else
+ result = -1;
/* check if we've timed out or how much time to wait at most */
if (timeout_us > 0)
@@ -6052,10 +6062,22 @@ g_socket_get_credentials (GSocket *socket,
{
if (cred.cr_version == XUCRED_VERSION)
{
+ pid_t pid;
+ socklen_t optlen = sizeof (pid);
+
ret = g_credentials_new ();
g_credentials_set_native (ret,
G_CREDENTIALS_NATIVE_TYPE,
&cred);
+
+#ifdef LOCAL_PEERPID
+ if (getsockopt (socket->priv->fd,
+ SOL_LOCAL,
+ LOCAL_PEERPID,
+ &pid,
+ &optlen) == 0)
+ _g_credentials_set_local_peerid (ret, pid);
+#endif
}
else
{
@@ -6113,6 +6135,23 @@ g_socket_get_credentials (GSocket *socket,
ucred_free (ucred);
}
}
+#elif G_CREDENTIALS_USE_WIN32_PID
+ {
+ DWORD peerid, drc;
+
+ if (WSAIoctl (socket->priv->fd, SIO_AF_UNIX_GETPEERPID,
+ NULL, 0U,
+ &peerid, sizeof(peerid),
+ /* Windows bug: always 0 https://github.com/microsoft/WSL/issues/4676 */
+ &drc,
+ NULL, NULL) == 0)
+ {
+ ret = g_credentials_new ();
+ g_credentials_set_native (ret,
+ G_CREDENTIALS_TYPE_WIN32_PID,
+ &peerid);
+ }
+ }
#else
#error "G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED is set but this is no code for this platform"
#endif
diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c
index 2b7e83ccf..41dbe66ad 100644
--- a/gio/gsocketaddress.c
+++ b/gio/gsocketaddress.c
@@ -35,8 +35,10 @@
#include "glibintl.h"
#include "gioenumtypes.h"
-#ifdef G_OS_UNIX
#include "gunixsocketaddress.h"
+
+#ifdef G_OS_WIN32
+#include "giowin32-afunix.h"
#endif
@@ -265,7 +267,6 @@ g_socket_address_new_from_native (gpointer native,
return sockaddr;
}
-#ifdef G_OS_UNIX
if (family == AF_UNIX)
{
struct sockaddr_un *addr = (struct sockaddr_un *) native;
@@ -299,7 +300,6 @@ g_socket_address_new_from_native (gpointer native,
else
return g_unix_socket_address_new (addr->sun_path);
}
-#endif
return g_native_socket_address_new (native, len);
}
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 62b1afbcd..cd5aa074a 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -289,7 +289,9 @@ g_socket_client_get_property (GObject *object,
break;
case PROP_TLS_VALIDATION_FLAGS:
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_value_set_flags (value, g_socket_client_get_tls_validation_flags (client));
+G_GNUC_END_IGNORE_DEPRECATIONS
break;
case PROP_PROXY_RESOLVER:
@@ -340,7 +342,9 @@ g_socket_client_set_property (GObject *object,
break;
case PROP_TLS_VALIDATION_FLAGS:
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_socket_client_set_tls_validation_flags (client, g_value_get_flags (value));
+G_GNUC_END_IGNORE_DEPRECATIONS
break;
case PROP_PROXY_RESOLVER:
@@ -679,9 +683,15 @@ g_socket_client_set_tls (GSocketClient *client,
* Gets the TLS validation flags used creating TLS connections via
* @client.
*
+ * This function does not work as originally designed and is impossible
+ * to use correctly. See #GSocketClient:tls-validation-flags for more
+ * information.
+ *
* Returns: the TLS validation flags
*
* Since: 2.28
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
*/
GTlsCertificateFlags
g_socket_client_get_tls_validation_flags (GSocketClient *client)
@@ -697,7 +707,13 @@ g_socket_client_get_tls_validation_flags (GSocketClient *client)
* Sets the TLS validation flags used when creating TLS connections
* via @client. The default value is %G_TLS_CERTIFICATE_VALIDATE_ALL.
*
+ * This function does not work as originally designed and is impossible
+ * to use correctly. See #GSocketClient:tls-validation-flags for more
+ * information.
+ *
* Since: 2.28
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
*/
void
g_socket_client_set_tls_validation_flags (GSocketClient *client,
@@ -916,6 +932,29 @@ g_socket_client_class_init (GSocketClientClass *class)
G_PARAM_CONSTRUCT |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GSocketClient:tls-validation-flags:
+ *
+ * The TLS validation flags used when creating TLS connections. The
+ * default value is %G_TLS_CERTIFICATE_VALIDATE_ALL.
+ *
+ * GLib guarantees that if certificate verification fails, at least one
+ * flag will be set, but it does not guarantee that all possible flags
+ * will be set. Accordingly, you may not safely decide to ignore any
+ * particular type of error. For example, it would be incorrect to mask
+ * %G_TLS_CERTIFICATE_EXPIRED if you want to allow expired certificates,
+ * because this could potentially be the only error flag set even if
+ * other problems exist with the certificate. Therefore, there is no
+ * safe way to use this property. This is not a horrible problem,
+ * though, because you should not be attempting to ignore validation
+ * errors anyway. If you really must ignore TLS certificate errors,
+ * connect to the #GSocketClient::event signal, wait for it to be
+ * emitted with %G_SOCKET_CLIENT_TLS_HANDSHAKING, and use that to
+ * connect to #GTlsConnection::accept-certificate.
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
+ */
g_object_class_install_property (gobject_class, PROP_TLS_VALIDATION_FLAGS,
g_param_spec_flags ("tls-validation-flags",
P_("TLS validation flags"),
@@ -924,7 +963,8 @@ g_socket_client_class_init (GSocketClientClass *class)
G_TLS_CERTIFICATE_VALIDATE_ALL,
G_PARAM_CONSTRUCT |
G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_DEPRECATED));
/**
* GSocketClient:proxy-resolver:
@@ -1209,8 +1249,10 @@ g_socket_client_connect (GSocketClient *client,
if (tlsconn)
{
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
client->priv->tls_validation_flags);
+G_GNUC_END_IGNORE_DEPRECATIONS
g_socket_client_emit_event (client, G_SOCKET_CLIENT_TLS_HANDSHAKING, connectable, connection);
if (g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn),
cancellable, &error_info->tmp_error))
@@ -1422,6 +1464,8 @@ typedef struct
GSocketConnectable *connectable;
GSocketAddressEnumerator *enumerator;
GCancellable *enumeration_cancellable;
+ GCancellable *enumeration_parent_cancellable; /* (nullable) (owned) */
+ gulong enumeration_cancelled_id;
GSList *connection_attempts;
GSList *successful_connections;
@@ -1441,7 +1485,12 @@ g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
data->task = NULL;
g_clear_object (&data->connectable);
g_clear_object (&data->enumerator);
+
+ g_cancellable_disconnect (data->enumeration_parent_cancellable, data->enumeration_cancelled_id);
+ g_clear_object (&data->enumeration_parent_cancellable);
+ data->enumeration_cancelled_id = 0;
g_clear_object (&data->enumeration_cancellable);
+
g_slist_free_full (data->connection_attempts, connection_attempt_unref);
g_slist_free_full (data->successful_connections, connection_attempt_unref);
@@ -1459,6 +1508,7 @@ typedef struct
GSocketClientAsyncConnectData *data; /* unowned */
GSource *timeout_source;
GCancellable *cancellable;
+ gulong cancelled_id;
grefcount ref;
} ConnectionAttempt;
@@ -1486,6 +1536,8 @@ connection_attempt_unref (gpointer pointer)
g_clear_object (&attempt->address);
g_clear_object (&attempt->socket);
g_clear_object (&attempt->connection);
+ g_cancellable_disconnect (g_task_get_cancellable (attempt->data->task), attempt->cancelled_id);
+ attempt->cancelled_id = 0;
g_clear_object (&attempt->cancellable);
g_clear_object (&attempt->proxy_addr);
if (attempt->timeout_source)
@@ -1635,8 +1687,10 @@ g_socket_client_tls_handshake (ConnectionAttempt *attempt)
&data->error_info->tmp_error);
if (tlsconn)
{
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
data->client->priv->tls_validation_flags);
+G_GNUC_END_IGNORE_DEPRECATIONS
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKING, data->connectable, G_IO_STREAM (tlsconn));
g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn),
G_PRIORITY_DEFAULT,
@@ -1977,8 +2031,9 @@ g_socket_client_enumerator_callback (GObject *object,
data->connection_attempts = g_slist_append (data->connection_attempts, attempt);
if (g_task_get_cancellable (data->task))
- g_cancellable_connect (g_task_get_cancellable (data->task), G_CALLBACK (on_connection_cancelled),
- g_object_ref (attempt->cancellable), g_object_unref);
+ attempt->cancelled_id =
+ g_cancellable_connect (g_task_get_cancellable (data->task), G_CALLBACK (on_connection_cancelled),
+ g_object_ref (attempt->cancellable), g_object_unref);
g_socket_connection_set_cached_remote_address ((GSocketConnection *)attempt->connection, address);
g_debug ("GSocketClient: Starting TCP connection attempt");
@@ -2083,8 +2138,12 @@ g_socket_client_connect_async (GSocketClient *client,
data->enumeration_cancellable = g_cancellable_new ();
if (cancellable)
- g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled),
- g_object_ref (data->enumeration_cancellable), g_object_unref);
+ {
+ data->enumeration_parent_cancellable = g_object_ref (cancellable);
+ data->enumeration_cancelled_id =
+ g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled),
+ g_object_ref (data->enumeration_cancellable), g_object_unref);
+ }
enumerator_next_async (data, FALSE);
}
diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h
index f0153450d..8f86ce89f 100644
--- a/gio/gsocketclient.h
+++ b/gio/gsocketclient.h
@@ -110,9 +110,9 @@ gboolean g_socket_client_get_tls (GSocket
GLIB_AVAILABLE_IN_2_28
void g_socket_client_set_tls (GSocketClient *client,
gboolean tls);
-GLIB_AVAILABLE_IN_2_28
+GLIB_DEPRECATED_IN_2_72
GTlsCertificateFlags g_socket_client_get_tls_validation_flags (GSocketClient *client);
-GLIB_AVAILABLE_IN_2_28
+GLIB_DEPRECATED_IN_2_72
void g_socket_client_set_tls_validation_flags (GSocketClient *client,
GTlsCertificateFlags flags);
GLIB_AVAILABLE_IN_2_36
diff --git a/gio/gsocketconnection.c b/gio/gsocketconnection.c
index 37d5d330c..64fe9753b 100644
--- a/gio/gsocketconnection.c
+++ b/gio/gsocketconnection.c
@@ -615,9 +615,7 @@ g_socket_connection_factory_register_type (GType g_type,
static void
init_builtin_types (void)
{
-#ifndef G_OS_WIN32
g_type_ensure (G_TYPE_UNIX_CONNECTION);
-#endif
g_type_ensure (G_TYPE_TCP_CONNECTION);
}
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c
index c0f4f8db6..523c80bfc 100644
--- a/gio/gsubprocess.c
+++ b/gio/gsubprocess.c
@@ -61,7 +61,10 @@
* As a matter of principle, #GSubprocess has no API that accepts
* shell-style space-separated strings. It will, however, match the
* typical shell behaviour of searching the PATH for executables that do
- * not contain a directory separator in their name.
+ * not contain a directory separator in their name. By default, the `PATH`
+ * of the current process is used. You can specify
+ * %G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP to use the `PATH` of the
+ * launcher environment instead.
*
* #GSubprocess attempts to have a very simple API for most uses (ie:
* spawning a subprocess with arguments and support for most typical
@@ -380,7 +383,7 @@ initable_init (GInitable *initable,
/* argv0 has no '/' in it? We better do a PATH lookup. */
if (strchr (self->argv[0], G_DIR_SEPARATOR) == NULL)
{
- if (self->launcher && self->launcher->path_from_envp)
+ if (self->launcher && self->launcher->flags & G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP)
spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
else
spawn_flags |= G_SPAWN_SEARCH_PATH;
diff --git a/gio/gsubprocesslauncher-private.h b/gio/gsubprocesslauncher-private.h
index d6fe0d784..8bd1b2844 100644
--- a/gio/gsubprocesslauncher-private.h
+++ b/gio/gsubprocesslauncher-private.h
@@ -28,7 +28,6 @@ struct _GSubprocessLauncher
GObject parent;
GSubprocessFlags flags;
- gboolean path_from_envp;
char **envp;
char *cwd;
diff --git a/gio/gtask.c b/gio/gtask.c
index a767e1909..365f200ad 100644
--- a/gio/gtask.c
+++ b/gio/gtask.c
@@ -58,6 +58,10 @@
* use g_task_propagate_pointer() or the like to extract the
* return value.
*
+ * Using #GTask requires the thread-default #GMainContext from when the
+ * #GTask was constructed to be running at least until the task has completed
+ * and its data has been freed.
+ *
* Here is an example for using GTask as a GAsyncResult:
* |[<!-- language="C" -->
* typedef struct {
@@ -977,13 +981,19 @@ g_task_set_return_on_cancel (GTask *task,
* @task: the #GTask
* @source_tag: an opaque pointer indicating the source of this task
*
- * Sets @task's source tag. You can use this to tag a task return
+ * Sets @task's source tag.
+ *
+ * You can use this to tag a task return
* value with a particular pointer (usually a pointer to the function
* doing the tagging) and then later check it using
* g_task_get_source_tag() (or g_async_result_is_tagged()) in the
* task's "finish" function, to figure out if the response came from a
* particular place.
*
+ * A macro wrapper around this function will automatically set the
+ * task’s name to the string form of @source_tag if it’s not already
+ * set, for convenience.
+ *
* Since: 2.36
*/
void
@@ -1010,7 +1020,8 @@ void
* name of the #GSource used for idle completion of the task.
*
* This function may only be called before the @task is first used in a thread
- * other than the one it was constructed in.
+ * other than the one it was constructed in. It is called automatically by
+ * g_task_set_source_tag() if not called already.
*
* Since: 2.60
*/
@@ -1246,7 +1257,6 @@ g_task_return (GTask *task,
GTaskReturnType type)
{
GSource *source;
- gchar *source_name = NULL;
if (type != G_TASK_RETURN_FROM_THREAD)
task->ever_returned = TRUE;
@@ -1295,10 +1305,22 @@ g_task_return (GTask *task,
/* Otherwise, complete in the next iteration */
source = g_idle_source_new ();
- source_name = g_strdup_printf ("[gio] %s complete_in_idle_cb",
- (task->name != NULL) ? task->name : "(unnamed)");
- g_source_set_name (source, source_name);
- g_free (source_name);
+
+ /* Note: in case the task name is NULL we set it as a const string instead
+ * of going through the concat path which is more expensive and may show in the
+ * profiler if g_task_return is called very often
+ */
+ if (task->name == NULL)
+ g_source_set_static_name (source, "[gio] (unnamed) complete_in_idle_cb");
+ else
+ {
+ gchar *source_name;
+
+ source_name = g_strconcat ("[gio] ", task->name, " complete_in_idle_cb", NULL);
+ g_source_set_name (source, source_name);
+ g_free (source_name);
+ }
+
g_task_attach_source (task, source, complete_in_idle_cb);
g_source_unref (source);
}
@@ -1994,7 +2016,7 @@ value_free (gpointer value)
*
* Sets @task's result to @result (by copying it) and completes the task.
*
- * If @result is %NULL then a #GValue of type #G_TYPE_POINTER
+ * If @result is %NULL then a #GValue of type %G_TYPE_POINTER
* with a value of %NULL will be used for the result.
*
* This is a very generic low-level method intended primarily for use
diff --git a/gio/gthreadedresolver.c b/gio/gthreadedresolver.c
index 93794b5b3..aeeb40e9b 100644
--- a/gio/gthreadedresolver.c
+++ b/gio/gthreadedresolver.c
@@ -529,18 +529,56 @@ typedef enum __ns_type {
#endif /* __BIONIC__ */
+/* Wrapper around dn_expand() which does associated length checks and returns
+ * errors as #GError. */
+static gboolean
+expand_name (const gchar *rrname,
+ const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ gchar *namebuf,
+ gsize namebuf_len,
+ GError **error)
+{
+ int expand_result;
+
+ expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
+ if (expand_result < 0 || end - *p < expand_result)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), rrname);
+ return FALSE;
+ }
+
+ *p += expand_result;
+
+ return TRUE;
+}
+
static GVariant *
-parse_res_srv (guchar *answer,
- guchar *end,
- guchar **p)
+parse_res_srv (const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ GError **error)
{
gchar namebuf[1024];
guint16 priority, weight, port;
+ if (end - *p < 6)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), "SRV");
+ return NULL;
+ }
+
GETSHORT (priority, *p);
GETSHORT (weight, *p);
GETSHORT (port, *p);
- *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
+
+ if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
+ return NULL;
return g_variant_new ("(qqqs)",
priority,
@@ -550,16 +588,28 @@ parse_res_srv (guchar *answer,
}
static GVariant *
-parse_res_soa (guchar *answer,
- guchar *end,
- guchar **p)
+parse_res_soa (const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ GError **error)
{
gchar mnamebuf[1024];
gchar rnamebuf[1024];
guint32 serial, refresh, retry, expire, ttl;
- *p += dn_expand (answer, end, *p, mnamebuf, sizeof (mnamebuf));
- *p += dn_expand (answer, end, *p, rnamebuf, sizeof (rnamebuf));
+ if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
+ return NULL;
+
+ if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
+ return NULL;
+
+ if (end - *p < 20)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), "SOA");
+ return NULL;
+ }
GETLONG (serial, *p);
GETLONG (refresh, *p);
@@ -578,28 +628,40 @@ parse_res_soa (guchar *answer,
}
static GVariant *
-parse_res_ns (guchar *answer,
- guchar *end,
- guchar **p)
+parse_res_ns (const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ GError **error)
{
gchar namebuf[1024];
- *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
+ if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
+ return NULL;
return g_variant_new ("(s)", namebuf);
}
static GVariant *
-parse_res_mx (guchar *answer,
- guchar *end,
- guchar **p)
+parse_res_mx (const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ GError **error)
{
gchar namebuf[1024];
guint16 preference;
+ if (end - *p < 2)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), "MX");
+ return NULL;
+ }
+
GETSHORT (preference, *p);
- *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
+ if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
+ return NULL;
return g_variant_new ("(qs)",
preference,
@@ -607,21 +669,37 @@ parse_res_mx (guchar *answer,
}
static GVariant *
-parse_res_txt (guchar *answer,
- guchar *end,
- guchar **p)
+parse_res_txt (const guint8 *answer,
+ const guint8 *end,
+ const guint8 **p,
+ GError **error)
{
GVariant *record;
GPtrArray *array;
- guchar *at = *p;
+ const guint8 *at = *p;
gsize len;
+ if (end - *p == 0)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
+ return NULL;
+ }
+
array = g_ptr_array_new_with_free_func (g_free);
while (at < end)
{
len = *(at++);
if (len > (gsize) (end - at))
- break;
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
+ _("Error parsing DNS %s record: malformed DNS packet"), "TXT");
+ g_ptr_array_free (array, TRUE);
+ return NULL;
+ }
+
g_ptr_array_add (array, g_strndup ((gchar *)at, len));
at += len;
}
@@ -633,7 +711,7 @@ parse_res_txt (guchar *answer,
return record;
}
-static gint
+gint
g_resolver_record_type_to_rrtype (GResolverRecordType type)
{
switch (type)
@@ -652,21 +730,23 @@ g_resolver_record_type_to_rrtype (GResolverRecordType type)
g_return_val_if_reached (-1);
}
-static GList *
+GList *
g_resolver_records_from_res_query (const gchar *rrname,
gint rrtype,
- guchar *answer,
- gint len,
+ const guint8 *answer,
+ gssize len,
gint herr,
GError **error)
{
- gint count;
+ uint16_t count;
gchar namebuf[1024];
- guchar *end, *p;
+ const guint8 *end, *p;
guint16 type, qclass, rdlength;
- HEADER *header;
+ const HEADER *header;
GList *records;
GVariant *record;
+ gsize len_unsigned;
+ GError *parsing_error = NULL;
if (len <= 0)
{
@@ -689,18 +769,44 @@ g_resolver_records_from_res_query (const gchar *rrname,
return NULL;
}
+ /* We know len ≥ 0 now. */
+ len_unsigned = (gsize) len;
+
+ if (len_unsigned < sizeof (HEADER))
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the first placeholder is a domain name, the
+ * second is an error message */
+ _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
+ return NULL;
+ }
+
records = NULL;
header = (HEADER *)answer;
p = answer + sizeof (HEADER);
- end = answer + len;
+ end = answer + len_unsigned;
/* Skip query */
count = ntohs (header->qdcount);
while (count-- && p < end)
{
- p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
- p += 4;
+ int expand_result;
+
+ expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
+ if (expand_result < 0 || end - p < expand_result + 4)
+ {
+ /* Not possible to recover parsing as the length of the rest of the
+ * record is unknown or is too short. */
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the first placeholder is a domain name, the
+ * second is an error message */
+ _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
+ return NULL;
+ }
+
+ p += expand_result;
+ p += 4; /* skip TYPE and CLASS */
/* To silence gcc warnings */
namebuf[0] = namebuf[1];
@@ -710,12 +816,35 @@ g_resolver_records_from_res_query (const gchar *rrname,
count = ntohs (header->ancount);
while (count-- && p < end)
{
- p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
+ int expand_result;
+
+ expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
+ if (expand_result < 0 || end - p < expand_result + 10)
+ {
+ /* Not possible to recover parsing as the length of the rest of the
+ * record is unknown or is too short. */
+ g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the first placeholder is a domain name, the
+ * second is an error message */
+ _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
+ break;
+ }
+
+ p += expand_result;
GETSHORT (type, p);
GETSHORT (qclass, p);
p += 4; /* ignore the ttl (type=long) value */
GETSHORT (rdlength, p);
+ if (end - p < rdlength)
+ {
+ g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ /* Translators: the first placeholder is a domain name, the
+ * second is an error message */
+ _("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
+ break;
+ }
+
if (type != rrtype || qclass != C_IN)
{
p += rdlength;
@@ -725,31 +854,40 @@ g_resolver_records_from_res_query (const gchar *rrname,
switch (rrtype)
{
case T_SRV:
- record = parse_res_srv (answer, end, &p);
+ record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
break;
case T_MX:
- record = parse_res_mx (answer, end, &p);
+ record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
break;
case T_SOA:
- record = parse_res_soa (answer, end, &p);
+ record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
break;
case T_NS:
- record = parse_res_ns (answer, end, &p);
+ record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
break;
case T_TXT:
- record = parse_res_txt (answer, p + rdlength, &p);
+ record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
break;
default:
- g_warn_if_reached ();
+ g_debug ("Unrecognised DNS record type %u", rrtype);
record = NULL;
break;
}
if (record != NULL)
records = g_list_prepend (records, record);
+
+ if (parsing_error != NULL)
+ break;
}
- if (records == NULL)
+ if (parsing_error != NULL)
+ {
+ g_propagate_prefixed_error (error, parsing_error, _("Failed to parse DNS response for “%s”: "), rrname);
+ g_list_free_full (records, (GDestroyNotify)g_variant_unref);
+ return NULL;
+ }
+ else if (records == NULL)
{
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
_("No DNS record of the requested type for “%s”"), rrname);
diff --git a/gio/gthreadedresolver.h b/gio/gthreadedresolver.h
index 5900d6a14..8d2ca19bf 100644
--- a/gio/gthreadedresolver.h
+++ b/gio/gthreadedresolver.h
@@ -42,6 +42,19 @@ typedef struct {
GLIB_AVAILABLE_IN_ALL
GType g_threaded_resolver_get_type (void) G_GNUC_CONST;
+/* Used for a private test API */
+#ifdef G_OS_UNIX
+GLIB_AVAILABLE_IN_ALL
+GList *g_resolver_records_from_res_query (const gchar *rrname,
+ gint rrtype,
+ const guint8 *answer,
+ gssize len,
+ gint herr,
+ GError **error);
+GLIB_AVAILABLE_IN_ALL
+gint g_resolver_record_type_to_rrtype (GResolverRecordType type);
+#endif
+
G_END_DECLS
#endif /* __G_RESOLVER_H__ */
diff --git a/gio/gtlscertificate.c b/gio/gtlscertificate.c
index 2c238120c..d0a326b27 100644
--- a/gio/gtlscertificate.c
+++ b/gio/gtlscertificate.c
@@ -50,7 +50,11 @@
* Since: 2.28
*/
-G_DEFINE_ABSTRACT_TYPE (GTlsCertificate, g_tls_certificate, G_TYPE_OBJECT)
+struct _GTlsCertificatePrivate {
+ gboolean pkcs12_properties_not_overridden;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GTlsCertificate, g_tls_certificate, G_TYPE_OBJECT)
enum
{
@@ -69,6 +73,8 @@ enum
PROP_ISSUER_NAME,
PROP_DNS_NAMES,
PROP_IP_ADDRESSES,
+ PROP_PKCS12_DATA,
+ PROP_PASSWORD,
};
static void
@@ -84,11 +90,11 @@ g_tls_certificate_get_property (GObject *object,
{
switch (prop_id)
{
+ /* Subclasses must override these properties but this allows older backends to not fatally error */
case PROP_PRIVATE_KEY:
case PROP_PRIVATE_KEY_PEM:
case PROP_PKCS11_URI:
case PROP_PRIVATE_KEY_PKCS11_URI:
- /* Subclasses must override this property but this allows older backends to not fatally error */
g_value_set_static_string (value, NULL);
break;
default:
@@ -102,11 +108,19 @@ g_tls_certificate_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
+ GTlsCertificate *cert = (GTlsCertificate*)object;
+ GTlsCertificatePrivate *priv = g_tls_certificate_get_instance_private (cert);
+
switch (prop_id)
{
case PROP_PKCS11_URI:
case PROP_PRIVATE_KEY_PKCS11_URI:
- /* Subclasses must override this property but this allows older backends to not fatally error */
+ /* Subclasses must override these properties but this allows older backends to not fatally error. */
+ break;
+ case PROP_PKCS12_DATA:
+ case PROP_PASSWORD:
+ /* We don't error on setting these properties however we track that they were not overridden. */
+ priv->pkcs12_properties_not_overridden = TRUE;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -122,6 +136,39 @@ g_tls_certificate_class_init (GTlsCertificateClass *class)
gobject_class->get_property = g_tls_certificate_get_property;
/**
+ * GTlsCertificate:pkcs12-data: (nullable)
+ *
+ * The PKCS #12 formatted data used to construct the object.
+ *
+ * See also: g_tls_certificate_new_from_pkcs12()
+ *
+ * Since: 2.72
+ */
+ g_object_class_install_property (gobject_class, PROP_PKCS12_DATA,
+ g_param_spec_boxed ("pkcs12-data",
+ P_("PKCS #12 data"),
+ P_("The PKCS #12 data used for construction"),
+ G_TYPE_BYTE_ARRAY,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * GTlsCertificate:password: (nullable)
+ *
+ * An optional password used when constructed with GTlsCertificate:pkcs12-data.
+ *
+ * Since: 2.72
+ */
+ g_object_class_install_property (gobject_class, PROP_PASSWORD,
+ g_param_spec_string ("password",
+ P_("Password"),
+ P_("Password used when constructing from bytes"),
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
* GTlsCertificate:certificate:
*
* The DER (binary) encoded representation of the certificate.
@@ -684,23 +731,138 @@ g_tls_certificate_new_from_pem (const gchar *data,
}
/**
- * g_tls_certificate_new_from_file:
- * @file: (type filename): file containing a PEM-encoded certificate to import
+ * g_tls_certificate_new_from_pkcs12:
+ * @data: (array length=length): DER-encoded PKCS #12 format certificate data
+ * @length: the length of @data
+ * @password: (nullable): optional password for encrypted certificate data
* @error: #GError for error reporting, or %NULL to ignore.
*
- * Creates a #GTlsCertificate from the PEM-encoded data in @file. The
- * returned certificate will be the first certificate found in @file. As
- * of GLib 2.44, if @file contains more certificates it will try to load
- * a certificate chain. All certificates will be verified in the order
- * found (top-level certificate should be the last one in the file) and
- * the #GTlsCertificate:issuer property of each certificate will be set
- * accordingly if the verification succeeds. If any certificate in the
- * chain cannot be verified, the first certificate in the file will
- * still be returned.
+ * Creates a #GTlsCertificate from the data in @data. It must contain
+ * a certificate and matching private key.
+ *
+ * If extra certificates are included they will be verified as a chain
+ * and the #GTlsCertificate:issuer property will be set.
+ * All other data will be ignored.
+ *
+ * You can pass as single password for all of the data which will be
+ * used both for the PKCS #12 container as well as encrypted
+ * private keys. If decryption fails it will error with
+ * %G_TLS_ERROR_BAD_CERTIFICATE_PASSWORD.
+ *
+ * This constructor requires support in the current #GTlsBackend.
+ * If support is missing it will error with
+ * %G_IO_ERROR_NOT_SUPPORTED.
+ *
+ * Other parsing failures will error with %G_TLS_ERROR_BAD_CERTIFICATE.
+ *
+ * Returns: the new certificate, or %NULL if @data is invalid
+ *
+ * Since: 2.72
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_pkcs12 (const guint8 *data,
+ gsize length,
+ const gchar *password,
+ GError **error)
+{
+ GObject *cert;
+ GTlsBackend *backend;
+ GByteArray *bytes;
+
+ g_return_val_if_fail (data != NULL || length == 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ backend = g_tls_backend_get_default ();
+
+ bytes = g_byte_array_new ();
+ g_byte_array_append (bytes, data, length);
+
+ cert = g_initable_new (g_tls_backend_get_certificate_type (backend),
+ NULL, error,
+ "pkcs12-data", bytes,
+ "password", password,
+ NULL);
+
+ g_byte_array_unref (bytes);
+
+ if (cert)
+ {
+ GTlsCertificatePrivate *priv = g_tls_certificate_get_instance_private (G_TLS_CERTIFICATE (cert));
+
+ if (priv->pkcs12_properties_not_overridden)
+ {
+ g_clear_object (&cert);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("The current TLS backend does not support PKCS #12"));
+ return NULL;
+ }
+ }
+
+ return G_TLS_CERTIFICATE (cert);
+}
+
+/**
+ * g_tls_certificate_new_from_file_with_password:
+ * @file: (type filename): file containing a certificate to import
+ * @password: (not nullable): password for PKCS #12 files
+ * @error: #GError for error reporting, or %NULL to ignore
+ *
+ * Creates a #GTlsCertificate from the data in @file.
*
* If @file cannot be read or parsed, the function will return %NULL and
- * set @error. Otherwise, this behaves like
- * g_tls_certificate_new_from_pem().
+ * set @error.
+ *
+ * Any unknown file types will error with %G_IO_ERROR_NOT_SUPPORTED.
+ * Currently only `.p12` and `.pfx` files are supported.
+ * See g_tls_certificate_new_from_pkcs12() for more details.
+ *
+ * Returns: the new certificate, or %NULL on error
+ *
+ * Since: 2.72
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_file_with_password (const gchar *file,
+ const gchar *password,
+ GError **error)
+{
+ GTlsCertificate *cert;
+ gchar *contents;
+ gsize length;
+
+ g_return_val_if_fail (file != NULL, NULL);
+ g_return_val_if_fail (password != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (!g_str_has_suffix (file, ".p12") && !g_str_has_suffix (file, ".pfx"))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "The file type of \"%s\" is unknown. Only .p12 and .pfx files are supported currently.", file);
+ return NULL;
+ }
+
+ if (!g_file_get_contents (file, &contents, &length, error))
+ return NULL;
+
+ cert = g_tls_certificate_new_from_pkcs12 ((guint8 *)contents, length, password, error);
+
+ g_free (contents);
+ return cert;
+}
+
+/**
+ * g_tls_certificate_new_from_file:
+ * @file: (type filename): file containing a certificate to import
+ * @error: #GError for error reporting, or %NULL to ignore
+ *
+ * Creates a #GTlsCertificate from the data in @file.
+ *
+ * As of 2.72, if the filename ends in `.p12` or `.pfx` the data is loaded by
+ * g_tls_certificate_new_from_pkcs12() otherwise it is loaded by
+ * g_tls_certificate_new_from_pem(). See those functions for
+ * exact details.
+ *
+ * If @file cannot be read or parsed, the function will return %NULL and
+ * set @error.
*
* Returns: the new certificate, or %NULL on error
*
@@ -708,16 +870,23 @@ g_tls_certificate_new_from_pem (const gchar *data,
*/
GTlsCertificate *
g_tls_certificate_new_from_file (const gchar *file,
- GError **error)
+ GError **error)
{
GTlsCertificate *cert;
gchar *contents;
gsize length;
+ g_return_val_if_fail (file != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
if (!g_file_get_contents (file, &contents, &length, error))
return NULL;
- cert = g_tls_certificate_new_from_pem (contents, length, error);
+ if (g_str_has_suffix (file, ".p12") || g_str_has_suffix (file, ".pfx"))
+ cert = g_tls_certificate_new_from_pkcs12 ((guint8 *)contents, length, NULL, error);
+ else
+ cert = g_tls_certificate_new_from_pem (contents, length, error);
+
g_free (contents);
return cert;
}
@@ -959,13 +1128,18 @@ g_tls_certificate_get_issuer (GTlsCertificate *cert)
* @trusted_ca is %NULL, that bit will never be set in the return
* value.
*
- * (All other #GTlsCertificateFlags values will always be set or unset
- * as appropriate.)
+ * GLib guarantees that if certificate verification fails, at least one
+ * error will be set in the return value, but it does not guarantee
+ * that all possible errors will be set. Accordingly, you may not safely
+ * decide to ignore any particular type of error. For example, it would
+ * be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if you want to allow
+ * expired certificates, because this could potentially be the only
+ * error flag set even if other problems exist with the certificate.
*
* Because TLS session context is not used, #GTlsCertificate may not
* perform as many checks on the certificates as #GTlsConnection would.
- * For example, certificate constraints cannot be honored, and some
- * revocation checks cannot be performed. The best way to verify TLS
+ * For example, certificate constraints may not be honored, and
+ * revocation checks may not be performed. The best way to verify TLS
* certificates used by a TLS connection is to let #GTlsConnection
* handle the verification.
*
diff --git a/gio/gtlscertificate.h b/gio/gtlscertificate.h
index 3b92b97fc..52e678bcb 100644
--- a/gio/gtlscertificate.h
+++ b/gio/gtlscertificate.h
@@ -63,7 +63,15 @@ GLIB_AVAILABLE_IN_ALL
GTlsCertificate *g_tls_certificate_new_from_pem (const gchar *data,
gssize length,
GError **error);
-
+GLIB_AVAILABLE_IN_2_72
+GTlsCertificate *g_tls_certificate_new_from_pkcs12 (const guint8 *data,
+ gsize length,
+ const gchar *password,
+ GError **error);
+GLIB_AVAILABLE_IN_2_72
+GTlsCertificate *g_tls_certificate_new_from_file_with_password (const gchar *file,
+ const gchar *password,
+ GError **error);
GLIB_AVAILABLE_IN_ALL
GTlsCertificate *g_tls_certificate_new_from_file (const gchar *file,
GError **error);
diff --git a/gio/gtlsclientconnection.c b/gio/gtlsclientconnection.c
index d0a740f4f..63dd6bee7 100644
--- a/gio/gtlsclientconnection.c
+++ b/gio/gtlsclientconnection.c
@@ -59,7 +59,21 @@ g_tls_client_connection_default_init (GTlsClientConnectionInterface *iface)
* ways indicated here will be rejected unless the application
* overrides the default via #GTlsConnection::accept-certificate.
*
+ * GLib guarantees that if certificate verification fails, at least one
+ * flag will be set, but it does not guarantee that all possible flags
+ * will be set. Accordingly, you may not safely decide to ignore any
+ * particular type of error. For example, it would be incorrect to mask
+ * %G_TLS_CERTIFICATE_EXPIRED if you want to allow expired certificates,
+ * because this could potentially be the only error flag set even if
+ * other problems exist with the certificate. Therefore, there is no
+ * safe way to use this property. This is not a horrible problem,
+ * though, because you should not be attempting to ignore validation
+ * errors anyway. If you really must ignore TLS certificate errors,
+ * connect to #GTlsConnection::accept-certificate.
+ *
* Since: 2.28
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
*/
g_object_interface_install_property (iface,
g_param_spec_flags ("validation-flags",
@@ -69,7 +83,8 @@ g_tls_client_connection_default_init (GTlsClientConnectionInterface *iface)
G_TLS_CERTIFICATE_VALIDATE_ALL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
- G_PARAM_STATIC_STRINGS));
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_DEPRECATED));
/**
* GTlsClientConnection:server-identity:
@@ -183,9 +198,15 @@ g_tls_client_connection_new (GIOStream *base_io_stream,
*
* Gets @conn's validation flags
*
+ * This function does not work as originally designed and is impossible
+ * to use correctly. See #GTlsClientConnection:validation-flags for more
+ * information.
+ *
* Returns: the validation flags
*
* Since: 2.28
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
*/
GTlsCertificateFlags
g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn)
@@ -207,7 +228,13 @@ g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn)
* checks performed when validating a server certificate. By default,
* %G_TLS_CERTIFICATE_VALIDATE_ALL is used.
*
+ * This function does not work as originally designed and is impossible
+ * to use correctly. See #GTlsClientConnection:validation-flags for more
+ * information.
+ *
* Since: 2.28
+ *
+ * Deprecated: 2.72: Do not attempt to ignore validation errors.
*/
void
g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn,
diff --git a/gio/gtlsclientconnection.h b/gio/gtlsclientconnection.h
index 29dbafcf4..f592fa808 100644
--- a/gio/gtlsclientconnection.h
+++ b/gio/gtlsclientconnection.h
@@ -59,9 +59,9 @@ GIOStream * g_tls_client_connection_new (GIOStream
GSocketConnectable *server_identity,
GError **error);
-GLIB_AVAILABLE_IN_ALL
+GLIB_DEPRECATED_IN_2_72
GTlsCertificateFlags g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn);
-GLIB_AVAILABLE_IN_ALL
+GLIB_DEPRECATED_IN_2_72
void g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn,
GTlsCertificateFlags flags);
GLIB_AVAILABLE_IN_ALL
diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c
index 0239489b7..f930ebabe 100644
--- a/gio/gtlsconnection.c
+++ b/gio/gtlsconnection.c
@@ -143,6 +143,19 @@ g_tls_connection_class_init (GTlsConnectionClass *klass)
* If no certificate database is set, then the default database will be
* used. See g_tls_backend_get_default_database().
*
+ * When using a non-default database, #GTlsConnection must fall back to using
+ * the #GTlsDatabase to perform certificate verification using
+ * g_tls_database_verify_chain(), which means certificate verification will
+ * not be able to make use of TLS session context. This may be less secure.
+ * For example, if you create your own #GTlsDatabase that just wraps the
+ * default #GTlsDatabase, you might expect that you have not changed anything,
+ * but this is not true because you may have altered the behavior of
+ * #GTlsConnection by causing it to use g_tls_database_verify_chain(). See the
+ * documentation of g_tls_database_verify_chain() for more details on specific
+ * security checks that may not be performed. Accordingly, setting a
+ * non-default database is discouraged except for specialty applications with
+ * unusual security requirements.
+ *
* Since: 2.30
*/
g_object_class_install_property (gobject_class, PROP_DATABASE,
@@ -248,6 +261,14 @@ g_tls_connection_class_init (GTlsConnectionClass *klass)
* #GTlsConnection::accept-certificate overrode the default
* behavior.
*
+ * GLib guarantees that if certificate verification fails, at least
+ * one error will be set, but it does not guarantee that all possible
+ * errors will be set. Accordingly, you may not safely decide to
+ * ignore any particular type of error. For example, it would be
+ * incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if you want to allow
+ * expired certificates, because this could potentially be the only
+ * error flag set even if other problems exist with the certificate.
+ *
* Since: 2.28
*/
g_object_class_install_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS,
@@ -339,6 +360,15 @@ g_tls_connection_class_init (GTlsConnectionClass *klass)
* signal handler. Otherwise, if no handler accepts the certificate,
* the handshake will fail with %G_TLS_ERROR_BAD_CERTIFICATE.
*
+ * GLib guarantees that if certificate verification fails, this signal
+ * will be emitted with at least one error will be set in @errors, but
+ * it does not guarantee that all possible errors will be set.
+ * Accordingly, you may not safely decide to ignore any particular
+ * type of error. For example, it would be incorrect to ignore
+ * %G_TLS_CERTIFICATE_EXPIRED if you want to allow expired
+ * certificates, because this could potentially be the only error flag
+ * set even if other problems exist with the certificate.
+ *
* For a server-side connection, @peer_cert is the certificate
* presented by the client, if this was requested via the server's
* #GTlsServerConnection:authentication_mode. On the server side,
@@ -470,6 +500,9 @@ g_tls_connection_get_use_system_certdb (GTlsConnection *conn)
* client-side connections, unless that bit is not set in
* #GTlsClientConnection:validation-flags).
*
+ * There are nonintuitive security implications when using a non-default
+ * database. See #GTlsConnection:database for details.
+ *
* Since: 2.30
*/
void
@@ -655,6 +688,8 @@ g_tls_connection_get_peer_certificate (GTlsConnection *conn)
* certificate, after the handshake has completed or failed. (It is
* not set during the emission of #GTlsConnection::accept-certificate.)
*
+ * See #GTlsConnection:peer-certificate-errors for more information.
+ *
* Returns: @conn's peer's certificate errors
*
* Since: 2.28
diff --git a/gio/gtlsdatabase.c b/gio/gtlsdatabase.c
index 2e5a264e9..7027b121c 100644
--- a/gio/gtlsdatabase.c
+++ b/gio/gtlsdatabase.c
@@ -469,7 +469,7 @@ g_tls_database_class_init (GTlsDatabaseClass *klass)
* certificate in the chain by its #GTlsCertificate:issuer property.
*
* @purpose describes the purpose (or usage) for which the certificate
- * is being used. Typically @purpose will be set to #G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER
+ * is being used. Typically @purpose will be set to %G_TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER
* which means that the certificate is being used to authenticate a server
* (and we are acting as the client).
*
@@ -485,13 +485,21 @@ g_tls_database_class_init (GTlsDatabaseClass *klass)
* used.
*
* If @chain is found to be valid, then the return value will be 0. If
- * @chain is found to be invalid, then the return value will indicate
- * the problems found. If the function is unable to determine whether
- * @chain is valid or not (eg, because @cancellable is triggered
- * before it completes) then the return value will be
- * %G_TLS_CERTIFICATE_GENERIC_ERROR and @error will be set
- * accordingly. @error is not set when @chain is successfully analyzed
- * but found to be invalid.
+ * @chain is found to be invalid, then the return value will indicate at
+ * least one problem found. If the function is unable to determine
+ * whether @chain is valid (for example, because @cancellable is
+ * triggered before it completes) then the return value will be
+ * %G_TLS_CERTIFICATE_GENERIC_ERROR and @error will be set accordingly.
+ * @error is not set when @chain is successfully analyzed but found to
+ * be invalid.
+ *
+ * GLib guarantees that if certificate verification fails, at least one
+ * error will be set in the return value, but it does not guarantee
+ * that all possible errors will be set. Accordingly, you may not safely
+ * decide to ignore any particular type of error. For example, it would
+ * be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if you want to allow
+ * expired certificates, because this could potentially be the only
+ * error flag set even if other problems exist with the certificate.
*
* Prior to GLib 2.48, GLib's default TLS backend modified @chain to
* represent the certification path built by #GTlsDatabase during
@@ -503,14 +511,14 @@ g_tls_database_class_init (GTlsDatabaseClass *klass)
*
* Because TLS session context is not used, #GTlsDatabase may not
* perform as many checks on the certificates as #GTlsConnection would.
- * For example, certificate constraints cannot be honored, and some
- * revocation checks cannot be performed. The best way to verify TLS
+ * For example, certificate constraints may not be honored, and
+ * revocation checks may not be performed. The best way to verify TLS
* certificates used by a TLS connection is to let #GTlsConnection
* handle the verification.
*
* The TLS backend may attempt to look up and add missing certificates
- * to the chain. Since GLib 2.70, this may involve HTTP requests to
- * download missing certificates.
+ * to the chain. This may involve HTTP requests to download missing
+ * certificates.
*
* This function can block. Use g_tls_database_verify_chain_async() to
* perform the verification operation asynchronously.
diff --git a/gio/gtrashportal.c b/gio/gtrashportal.c
index 03c933297..2f739600c 100644
--- a/gio/gtrashportal.c
+++ b/gio/gtrashportal.c
@@ -89,8 +89,12 @@ g_trash_portal_trash_file (GFile *file,
fd = g_open (path, O_RDWR | O_CLOEXEC | O_NOFOLLOW);
if (fd == -1 && errno == EISDIR)
- /* If it is a directory, fall back to O_PATH */
- fd = g_open (path, O_PATH | O_CLOEXEC | O_RDONLY | O_NOFOLLOW);
+ /* If it is a directory, fall back to O_PATH.
+ * Remove O_NOFOLLOW since
+ * a) we know it is a directory, not a symlink, and
+ * b) the portal reject this combination
+ */
+ fd = g_open (path, O_PATH | O_CLOEXEC | O_RDONLY);
errsv = errno;
diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c
index e9e2f75f0..e89aba6ca 100644
--- a/gio/gunixconnection.c
+++ b/gio/gunixconnection.c
@@ -24,7 +24,9 @@
#include <errno.h>
#include <string.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
/**
* SECTION:gunixconnection
@@ -39,9 +41,12 @@
* It contains functions to do some of the UNIX socket specific
* functionality like passing file descriptors.
*
- * Note that `<gio/gunixconnection.h>` belongs to the UNIX-specific
- * GIO interfaces, thus you have to use the `gio-unix-2.0.pc`
- * pkg-config file when using it.
+ * Since GLib 2.72, #GUnixConnection is available on all platforms. It requires
+ * underlying system support (such as Windows 10 with `AF_UNIX`) at run time.
+ *
+ * Before GLib 2.72, `<gio/gunixconnection.h>` belonged to the UNIX-specific GIO
+ * interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file when
+ * using it. This is no longer necessary since GLib 2.72.
*
* Since: 2.22
*/
@@ -86,6 +91,7 @@ g_unix_connection_send_fd (GUnixConnection *connection,
GCancellable *cancellable,
GError **error)
{
+#ifdef G_OS_UNIX
GSocketControlMessage *scm;
GSocket *socket;
@@ -114,6 +120,11 @@ g_unix_connection_send_fd (GUnixConnection *connection,
g_object_unref (scm);
return TRUE;
+#else
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Sending FD is not supported"));
+ return FALSE;
+#endif
}
/**
@@ -139,6 +150,7 @@ g_unix_connection_receive_fd (GUnixConnection *connection,
GCancellable *cancellable,
GError **error)
{
+#ifdef G_OS_UNIX
GSocketControlMessage **scms;
gint *fds, nfd, fd, nscm;
GUnixFDMessage *fdmsg;
@@ -221,6 +233,11 @@ g_unix_connection_receive_fd (GUnixConnection *connection,
}
return fd;
+#else
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Receiving FD is not supported"));
+ return -1;
+#endif
}
static void
diff --git a/gio/gunixcredentialsmessage.c b/gio/gunixcredentialsmessage.c
index 9e5c7d32e..e8ac5a73c 100644
--- a/gio/gunixcredentialsmessage.c
+++ b/gio/gunixcredentialsmessage.c
@@ -31,6 +31,14 @@
* g_unix_connection_receive_credentials(). To receive credentials of
* a foreign process connected to a socket, use
* g_socket_get_credentials().
+ *
+ * Since GLib 2.72, #GUnixCredentialMessage is available on all platforms. It
+ * requires underlying system support (such as Windows 10 with `AF_UNIX`) at run
+ * time.
+ *
+ * Before GLib 2.72, `<gio/gunixcredentialsmessage.h>` belonged to the UNIX-specific
+ * GIO interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file
+ * when using it. This is no longer necessary since GLib 2.72.
*/
#include "config.h"
@@ -40,7 +48,9 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include "gunixcredentialsmessage.h"
#include "gcredentials.h"
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index e0c8f6778..9c8ef5d66 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -1062,7 +1062,6 @@ _g_get_unix_mount_points (void)
if ((mount_fstype != NULL && g_strcmp0 ("supermount", mount_fstype) == 0) ||
((userspace_flags & MNT_MS_USER) &&
(g_strstr_len (mount_options, -1, "user_xattr") == NULL)) ||
- (g_strstr_len (mount_options, -1, "pamconsole") == NULL) ||
(userspace_flags & MNT_MS_USERS) ||
(userspace_flags & MNT_MS_OWNER))
{
@@ -1158,7 +1157,6 @@ _g_get_unix_mount_points (void)
#ifdef HAVE_HASMNTOPT
|| (hasmntopt (mntent, "user") != NULL
&& hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
- || hasmntopt (mntent, "pamconsole") != NULL
|| hasmntopt (mntent, "users") != NULL
|| hasmntopt (mntent, "owner") != NULL
#endif
@@ -1231,7 +1229,6 @@ _g_get_unix_mount_points (void)
#ifdef HAVE_HASMNTOPT
|| (hasmntopt (&mntent, "user") != NULL
&& hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
- || hasmntopt (&mntent, "pamconsole") != NULL
|| hasmntopt (&mntent, "users") != NULL
|| hasmntopt (&mntent, "owner") != NULL
#endif
@@ -1669,6 +1666,14 @@ g_unix_mount_for (const char *file_path,
return entry;
}
+static gpointer
+copy_mount_point_cb (gconstpointer src,
+ gpointer data)
+{
+ GUnixMountPoint *src_mount_point = (GUnixMountPoint *) src;
+ return g_unix_mount_point_copy (src_mount_point);
+}
+
/**
* g_unix_mount_points_get:
* @time_read: (out) (optional): guint64 to contain a timestamp.
@@ -1684,10 +1689,29 @@ g_unix_mount_for (const char *file_path,
GList *
g_unix_mount_points_get (guint64 *time_read)
{
+ static GList *mnt_pts_last = NULL;
+ static guint64 time_read_last = 0;
+ GList *mnt_pts = NULL;
+ guint64 time_read_now;
+ G_LOCK_DEFINE_STATIC (unix_mount_points);
+
+ G_LOCK (unix_mount_points);
+
+ time_read_now = get_mount_points_timestamp ();
+ if (time_read_now != time_read_last || mnt_pts_last == NULL)
+ {
+ time_read_last = time_read_now;
+ g_list_free_full (mnt_pts_last, (GDestroyNotify) g_unix_mount_point_free);
+ mnt_pts_last = _g_get_unix_mount_points ();
+ }
+ mnt_pts = g_list_copy_deep (mnt_pts_last, copy_mount_point_cb, NULL);
+
+ G_UNLOCK (unix_mount_points);
+
if (time_read)
- *time_read = get_mount_points_timestamp ();
+ *time_read = time_read_now;
- return _g_get_unix_mount_points ();
+ return mnt_pts;
}
/**
diff --git a/gio/gunixsocketaddress.c b/gio/gunixsocketaddress.c
index 69204e9b9..f80e8cc88 100644
--- a/gio/gunixsocketaddress.c
+++ b/gio/gunixsocketaddress.c
@@ -28,6 +28,9 @@
#include "glibintl.h"
#include "gnetworking.h"
+#ifdef G_OS_WIN32
+#include "giowin32-afunix.h"
+#endif
/**
* SECTION:gunixsocketaddress
@@ -45,9 +48,13 @@
* errors. You can use g_unix_socket_address_abstract_names_supported()
* to see if abstract names are supported.
*
- * Note that `<gio/gunixsocketaddress.h>` belongs to the UNIX-specific GIO
- * interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config file
- * when using it.
+ * Since GLib 2.72, #GUnixSocketAddress is available on all platforms. It
+ * requires underlying system support (such as Windows 10 with `AF_UNIX`) at
+ * run time.
+ *
+ * Before GLib 2.72, `<gio/gunixsocketaddress.h>` belonged to the UNIX-specific
+ * GIO interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file
+ * when using it. This is no longer necessary since GLib 2.72.
*/
/**
diff --git a/gio/gvolume.c b/gio/gvolume.c
index 0e14ddaf6..cb6d34f8b 100644
--- a/gio/gvolume.c
+++ b/gio/gvolume.c
@@ -66,13 +66,13 @@
* different kinds of identifiers, such as Hal UDIs, filesystem labels,
* traditional Unix devices (e.g. `/dev/sda2`), UUIDs. GIO uses predefined
* strings as names for the different kinds of identifiers:
- * #G_VOLUME_IDENTIFIER_KIND_UUID, #G_VOLUME_IDENTIFIER_KIND_LABEL, etc.
+ * %G_VOLUME_IDENTIFIER_KIND_UUID, %G_VOLUME_IDENTIFIER_KIND_LABEL, etc.
* Use g_volume_get_identifier() to obtain an identifier for a volume.
*
*
- * Note that #G_VOLUME_IDENTIFIER_KIND_HAL_UDI will only be available
+ * Note that %G_VOLUME_IDENTIFIER_KIND_HAL_UDI will only be available
* when the gvfs hal volume monitor is in use. Other volume monitors
- * will generally be able to provide the #G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE
+ * will generally be able to provide the %G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE
* identifier, which can be used to obtain a hal device by means of
* libhal_manager_find_device_string_match().
*/
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index cafd053b4..26ca6eff2 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -3359,7 +3359,7 @@ uwp_package_cb (gpointer user_data,
GPtrArray *supported_extgroups,
GPtrArray *supported_protocols)
{
- gint i, i_verb, i_ext;
+ guint i, i_verb, i_ext;
gint extensions_considered;
GWin32AppInfoApplication *app;
gchar *app_user_model_id_u8;
@@ -3459,7 +3459,7 @@ uwp_package_cb (gpointer user_data,
*/
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &ext))
{
- gint i_hverb;
+ guint i_hverb;
if (!ext)
continue;
@@ -3476,8 +3476,8 @@ uwp_package_cb (gpointer user_data,
}
if (app->verbs->len == 0 && extensions_considered > 0)
- g_warning ("Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs",
- full_package_name, app_user_model_id_u8, extensions_considered);
+ g_debug ("Unexpectedly, UWP app `%S' (AUMId `%s') supports %d extensions but has no verbs",
+ full_package_name, app_user_model_id_u8, extensions_considered);
for (i = 0; i < supported_protocols->len; i++)
{
@@ -3537,7 +3537,7 @@ uwp_package_cb (gpointer user_data,
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &url))
{
- gint i_hverb;
+ guint i_hverb;
if (!url)
continue;
@@ -4788,7 +4788,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
if (apppath)
{
gchar **p;
- gint p_index;
+ gsize p_index;
for (p = envp, p_index = 0; p[0]; p++, p_index++)
if ((p[0][0] == 'p' || p[0][0] == 'P') &&
@@ -4856,7 +4856,13 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
GVariant *platform_data;
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
- g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gint32) pid));
+ /* pid handles are never bigger than 2^24 as per
+ * https://docs.microsoft.com/en-us/windows/win32/sysinfo/kernel-objects,
+ * so truncating to `int32` is valid.
+ * The gsize cast is to silence a compiler warning
+ * about conversion from pointer to integer of
+ * different size. */
+ g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 ((gsize) pid));
platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
g_signal_emit_by_name (launch_context, "launched", info, platform_data);
@@ -5088,6 +5094,15 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
}
static gboolean
+g_win32_app_info_should_show (GAppInfo *appinfo)
+{
+ /* FIXME: This is a placeholder implementation to avoid crashes
+ * for now. It can be made more specific to @appinfo in future. */
+
+ return TRUE;
+}
+
+static gboolean
g_win32_app_info_launch (GAppInfo *appinfo,
GList *files,
GAppLaunchContext *launch_context,
@@ -5223,7 +5238,7 @@ g_win32_app_info_iface_init (GAppInfoIface *iface)
iface->supports_uris = g_win32_app_info_supports_uris;
iface->supports_files = g_win32_app_info_supports_files;
iface->launch_uris = g_win32_app_info_launch_uris;
-/* iface->should_show = g_win32_app_info_should_show;*/
+ iface->should_show = g_win32_app_info_should_show;
/* iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;*/
/* iface->set_as_default_for_extension = g_win32_app_info_set_as_default_for_extension;*/
/* iface->add_supports_type = g_win32_app_info_add_supports_type;*/
diff --git a/gio/gwin32packageparser.c b/gio/gwin32packageparser.c
index ee05bb1dd..58b99947c 100755
--- a/gio/gwin32packageparser.c
+++ b/gio/gwin32packageparser.c
@@ -96,7 +96,7 @@ static BOOL
WIN32_FROM_HRESULT (HRESULT hresult,
DWORD *win32_error_code)
{
- if ((hresult & 0xFFFF0000) == MAKE_HRESULT (SEVERITY_ERROR, FACILITY_WIN32, 0) ||
+ if ((hresult & (HRESULT) 0xFFFF0000) == MAKE_HRESULT (SEVERITY_ERROR, FACILITY_WIN32, 0) ||
hresult == S_OK)
{
*win32_error_code = HRESULT_CODE (hresult);
@@ -815,4 +815,4 @@ xml_parser_iteration (struct _xml_sax_state *sax,
}
return TRUE;
-} \ No newline at end of file
+}
diff --git a/gio/gwin32sid.c b/gio/gwin32sid.c
new file mode 100644
index 000000000..6112cf171
--- /dev/null
+++ b/gio/gwin32sid.c
@@ -0,0 +1,234 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2018 Руслан Ижбулатов
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Руслан Ижбулатов <lrn1986@gmail.com>
+ */
+
+#include "config.h"
+
+#include "gwin32sid.h"
+#include "gioerror.h"
+
+#include <sddl.h>
+
+/**
+ * _g_win32_sid_replace: (skip)
+ * @dest: A pointer to a SID storage
+ * @src: Existing SID
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a copy of the @src SID and puts that into @dest, after freeing
+ * existing SID in @dest (if any).
+ *
+ * The @src SID must be valid (use IsValidSid() to ensure that).
+ *
+ * Returns: TRUE on success, FALSE otherwise
+ */
+static gboolean
+_g_win32_sid_replace (SID **dest,
+ SID *src,
+ GError **error)
+{
+ DWORD sid_len;
+ SID *new_sid;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ g_return_val_if_fail (src != NULL, FALSE);
+ g_return_val_if_fail (dest && *dest == NULL, FALSE);
+
+ sid_len = GetLengthSid (src);
+ new_sid = g_malloc (sid_len);
+
+ if (!CopySid (sid_len, new_sid, src))
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to copy SID");
+
+ g_free (new_sid);
+ return FALSE;
+ }
+ else
+ {
+ g_free (*dest);
+ *dest = g_steal_pointer (&new_sid);
+
+ return TRUE;
+ }
+}
+
+/**
+ * _g_win32_token_get_sid: (skip)
+ * @token: A handle of an access token
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets user SID of the @token and returns a copy of that SID.
+ *
+ * Returns: A newly-allocated SID, or NULL in case of an error.
+ * Free the returned SID with g_free().
+ */
+static SID *
+_g_win32_token_get_sid (HANDLE token,
+ GError **error)
+{
+ TOKEN_USER *token_user = NULL;
+ DWORD n;
+ PSID psid;
+ SID *result = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (!GetTokenInformation (token, TokenUser, NULL, 0, &n)
+ && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to GetTokenInformation");
+
+ return NULL;
+ }
+
+ token_user = g_alloca (n);
+
+ if (!GetTokenInformation (token, TokenUser, token_user, n, &n))
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to GetTokenInformation");
+
+ return NULL;
+ }
+
+ psid = token_user->User.Sid;
+
+ if (!IsValidSid (psid))
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Invalid SID token");
+
+ return NULL;
+ }
+
+ _g_win32_sid_replace (&result, psid, error);
+
+ return result;
+}
+
+/**
+ * _g_win32_process_get_access_token_sid: (skip)
+ * @process_id: Identifier of a process to get an access token of
+ * (use 0 to get a token of the current process)
+ * @error: return location for a #GError, or %NULL
+ *
+ * Opens the process identified by @process_id and opens its token,
+ * then retrieves SID of the token user and returns a copy of that SID.
+ *
+ * Returns: A newly-allocated SID, or NULL in case of an error.
+ * Free the returned SID with g_free().
+ */
+SID *
+_g_win32_process_get_access_token_sid (DWORD process_id,
+ GError **error)
+{
+ HANDLE process_handle;
+ HANDLE process_token;
+ SID *result = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (process_id == 0)
+ process_handle = GetCurrentProcess ();
+ else
+ process_handle = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
+
+ if (process_handle == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "%s failed", process_id == 0 ? "GetCurrentProcess" : "OpenProcess");
+
+ return NULL;
+ }
+
+ if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token))
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "OpenProcessToken failed");
+
+ CloseHandle (process_handle);
+ return NULL;
+ }
+
+ result = _g_win32_token_get_sid (process_token, error);
+
+ CloseHandle (process_token);
+ CloseHandle (process_handle);
+
+ return result;
+}
+
+/**
+ * _g_win32_sid_to_string: (skip)
+ * @sid: a SID.
+ * @error: return location for a #GError, or %NULL
+ *
+ * Convert a SID to its string form.
+ *
+ * Returns: A newly-allocated string, or NULL in case of an error.
+ */
+gchar *
+_g_win32_sid_to_string (SID *sid, GError **error)
+{
+ gchar *tmp, *ret;
+
+ g_return_val_if_fail (sid != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (!ConvertSidToStringSidA (sid, &tmp))
+ {
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to ConvertSidToString");
+
+ return NULL;
+ }
+
+ ret = g_strdup (tmp);
+ LocalFree (tmp);
+ return ret;
+}
+
+/**
+ * _g_win32_current_process_sid_string: (skip)
+ * @error: return location for a #GError, or %NULL
+ *
+ * Get the current process SID, as a string.
+ *
+ * Returns: A newly-allocated string, or NULL in case of an error.
+ */
+gchar *
+_g_win32_current_process_sid_string (GError **error)
+{
+ SID *sid;
+ gchar *ret;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ sid = _g_win32_process_get_access_token_sid (0, error);
+ if (!sid)
+ return NULL;
+
+ ret = _g_win32_sid_to_string (sid, error);
+ g_free (sid);
+ return ret;
+}
diff --git a/gio/gwin32sid.h b/gio/gwin32sid.h
new file mode 100644
index 000000000..84c037799
--- /dev/null
+++ b/gio/gwin32sid.h
@@ -0,0 +1,40 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2018 Руслан Ижбулатов
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Руслан Ижбулатов <lrn1986@gmail.com>
+ */
+
+#ifndef __G_WIN32_SID_H__
+#define __G_WIN32_SID_H__
+
+#include <glib.h>
+#include <windows.h>
+
+G_BEGIN_DECLS
+
+SID * _g_win32_process_get_access_token_sid (DWORD process_id,
+ GError **error);
+
+gchar * _g_win32_sid_to_string (SID *sid,
+ GError **error);
+
+gchar * _g_win32_current_process_sid_string (GError **error);
+
+G_END_DECLS
+
+#endif /* __G_WIN32_SID_H__ */
diff --git a/gio/meson.build b/gio/meson.build
index 89c6dc6f8..9019104a4 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -74,15 +74,12 @@ if host_system != 'windows'
endif
endif
- # res_init()
- if cc.links('''#include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/nameser.h>
- #include <resolv.h>
+ # dn_comp()
+ if cc.links('''#include <resolv.h>
int main (int argc, char ** argv) {
- return res_init();
- }''', args : network_args, name : 'res_init()')
- glib_conf.set('HAVE_RES_INIT', 1)
+ return dn_comp(NULL, NULL, 0, NULL, NULL) == -1;
+ } ''', args : network_args, name : 'dn_comp()')
+ glib_conf.set('HAVE_DN_COMP', 1)
endif
# res_nclose()
@@ -341,12 +338,6 @@ local_sources = files(
platform_deps = []
internal_deps = []
-# TODO: internal_objects is a workaround for
-# <https://github.com/mesonbuild/meson/issues/3934> and
-# <https://github.com/mesonbuild/meson/issues/3937>. When we can depend
-# on a meson version where those are fixed, revert the commit that
-# introduced this workaround.
-internal_objects = []
appinfo_sources = []
contenttype_sources = []
portal_sources = []
@@ -363,13 +354,10 @@ if host_system != 'windows'
unix_sources = files(
'gfiledescriptorbased.c',
'giounix-private.c',
- 'gunixconnection.c',
- 'gunixcredentialsmessage.c',
'gunixfdlist.c',
'gunixfdmessage.c',
'gunixmount.c',
'gunixmounts.c',
- 'gunixsocketaddress.c',
'gunixvolume.c',
'gunixvolumemonitor.c',
'gunixinputstream.c',
@@ -393,20 +381,19 @@ if host_system != 'windows'
gio_unix_include_headers = files(
'gfiledescriptorbased.h',
- 'gunixconnection.h',
- 'gunixcredentialsmessage.h',
'gunixmounts.h',
'gunixfdlist.h',
'gunixfdmessage.h',
'gunixinputstream.h',
'gunixoutputstream.h',
- 'gunixsocketaddress.h',
)
if glib_have_cocoa
settings_sources += files('gnextstepsettingsbackend.m')
contenttype_sources += files('gosxcontenttype.m')
appinfo_sources += files('gosxappinfo.m')
+ framework_dep = dependency('appleframeworks', modules : ['Foundation', 'CoreFoundation', 'AppKit'])
+ platform_deps += [framework_dep]
if glib_have_os_x_9_or_later
unix_sources += files('gcocoanotificationbackend.m')
endif
@@ -419,7 +406,6 @@ if host_system != 'windows'
subdir('xdgmime')
internal_deps += [xdgmime_lib]
- internal_objects += [xdgmime_lib.extract_all_objects(recursive: false)]
install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.0/gio')
@@ -439,6 +425,7 @@ else
platform_deps += uwp_gio_deps
win32_sources += files(
+ 'gmemorymonitorwin32.c',
'gwin32registrykey.c',
'gwin32mount.c',
'gwin32volumemonitor.c',
@@ -449,6 +436,8 @@ else
'gwin32networkmonitor.c',
'gwin32networkmonitor.h',
'gwin32notificationbackend.c',
+ 'gwin32sid.c',
+ 'gwin32sid.h',
)
gio_win_rc = configure_file(
@@ -484,6 +473,8 @@ gio_sources = files(
'gdatagrambased.c',
'gdatainputstream.c',
'gdataoutputstream.c',
+ 'gdebugcontroller.c',
+ 'gdebugcontrollerdbus.c',
'gdrive.c',
'gdummyfile.c',
'gdummyproxyresolver.c',
@@ -582,6 +573,9 @@ gio_sources = files(
'gdtlsclientconnection.c',
'gdtlsserverconnection.c',
'gunionvolumemonitor.c',
+ 'gunixconnection.c',
+ 'gunixcredentialsmessage.c',
+ 'gunixsocketaddress.c',
'gvfs.c',
'gvolume.c',
'gvolumemonitor.c',
@@ -631,6 +625,8 @@ gio_headers = files(
'gdatagrambased.h',
'gdatainputstream.h',
'gdataoutputstream.h',
+ 'gdebugcontroller.h',
+ 'gdebugcontrollerdbus.h',
'gdrive.h',
'gemblem.h',
'gemblemedicon.h',
@@ -717,6 +713,9 @@ gio_headers = files(
'gdtlsconnection.h',
'gdtlsclientconnection.h',
'gdtlsserverconnection.h',
+ 'gunixconnection.h',
+ 'gunixcredentialsmessage.h',
+ 'gunixsocketaddress.h',
'gvfs.h',
'gvolume.h',
'gvolumemonitor.h',
@@ -758,20 +757,17 @@ gioenumtypes_dep = declare_dependency(sources : [gioenumtypes_h, glib_enumtypes_
if glib_conf.has('HAVE_SYS_INOTIFY_H') and have_func_inotify_init1
subdir('inotify')
internal_deps += [ inotify_lib ]
- internal_objects += [inotify_lib.extract_all_objects(recursive: false)]
endif
# kevent
if have_func_kqueue and have_func_kevent
subdir('kqueue')
internal_deps += [ kqueue_lib ]
- internal_objects += [kqueue_lib.extract_all_objects(recursive: false)]
endif
if host_system == 'windows'
subdir('win32')
internal_deps += [ giowin32_lib ]
- internal_objects += [giowin32_lib.extract_all_objects(recursive: false)]
endif
if have_bash
@@ -807,13 +803,13 @@ endif
libgio = library('gio-2.0',
gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources,
gio_dtrace_hdr, gio_dtrace_obj,
- objects : internal_objects,
version : library_version,
soversion : soversion,
darwin_versions : darwin_versions,
install : true,
include_directories : [configinc, gioinc],
# '$(gio_win32_res_ldflag)',
+ link_with: internal_deps,
dependencies : [libz_dep, libdl_dep, libmount_dep, libglib_dep,
libgobject_dep, libgmodule_dep, selinux_dep, xattr_dep,
platform_deps, network_libs, libsysprof_capture_dep],
@@ -835,8 +831,17 @@ libgio_dep = declare_dependency(link_with : libgio,
dependencies : [libgmodule_dep, libgobject_dep, gioenumtypes_dep],
include_directories : [gioinc])
+# Work around variables kwarg requiring Meson 0.56
+if meson.version().version_compare('>=0.56.0')
+ libgio_dep = declare_dependency(dependencies: libgio_dep,
+ variables: [
+ 'schemasdir=' + join_paths(glib_datadir, schemas_subdir),
+ 'giomoduledir=' + glib_giomodulesdir,
+ ]
+ )
+endif
+
pkg.generate(libgio,
- libraries_private : [osx_ldflags],
requires : ['glib-2.0', 'gobject-2.0'],
variables : ['datadir=' + join_paths('${prefix}', get_option('datadir')),
'schemasdir=' + join_paths('${datadir}', schemas_subdir),
@@ -872,7 +877,7 @@ if host_system == 'windows'
description : 'Windows specific headers for glib I/O library',
)
if meson.version().version_compare('>=0.54.0')
- meson.override_dependency('gio-win32-2.0', libgio_dep)
+ meson.override_dependency('gio-windows-2.0', libgio_dep)
endif
else
pkg.generate(requires : ['gobject-2.0', 'gio-2.0'],
diff --git a/gio/tests/actions.c b/gio/tests/actions.c
index 91fc08074..a41e989bd 100644
--- a/gio/tests/actions.c
+++ b/gio/tests/actions.c
@@ -115,33 +115,20 @@ test_name (void)
}
static gboolean
-strv_has_string (gchar **haystack,
- const gchar *needle)
-{
- guint n;
-
- for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
- {
- if (g_strcmp0 (haystack[n], needle) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-static gboolean
-strv_strv_cmp (gchar **a, gchar **b)
+strv_strv_cmp (const gchar * const *a,
+ const gchar * const *b)
{
guint n;
for (n = 0; a[n] != NULL; n++)
{
- if (!strv_has_string (b, a[n]))
+ if (!g_strv_contains (b, a[n]))
return FALSE;
}
for (n = 0; b[n] != NULL; n++)
{
- if (!strv_has_string (a, b[n]))
+ if (!g_strv_contains (a, b[n]))
return FALSE;
}
@@ -149,7 +136,7 @@ strv_strv_cmp (gchar **a, gchar **b)
}
static gboolean
-strv_set_equal (gchar **strv, ...)
+strv_set_equal (const gchar * const *strv, ...)
{
guint count;
va_list list;
@@ -164,7 +151,7 @@ strv_set_equal (gchar **strv, ...)
str = va_arg (list, const gchar *);
if (str == NULL)
break;
- if (!strv_has_string (strv, str))
+ if (!g_strv_contains (strv, str))
{
res = FALSE;
break;
@@ -215,7 +202,7 @@ test_simple_group (void)
g_assert_false (g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
actions = g_action_group_list_actions (G_ACTION_GROUP (group));
g_assert_cmpint (g_strv_length (actions), ==, 2);
- g_assert_true (strv_set_equal (actions, "foo", "bar", NULL));
+ g_assert_true (strv_set_equal ((const gchar * const *) actions, "foo", "bar", NULL));
g_strfreev (actions);
g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
@@ -275,10 +262,12 @@ test_stateful (void)
if (g_test_undefined ())
{
+ GVariant *new_state = g_variant_ref_sink (g_variant_new_int32 (123));
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*assertion*g_variant_is_of_type*failed*");
- g_simple_action_set_state (action, g_variant_new_int32 (123));
+ g_simple_action_set_state (action, new_state);
g_test_assert_expected_messages ();
+ g_variant_unref (new_state);
}
g_simple_action_set_state (action, g_variant_new_string ("hello"));
@@ -292,10 +281,12 @@ test_stateful (void)
if (g_test_undefined ())
{
+ GVariant *new_state = g_variant_ref_sink (g_variant_new_int32 (123));
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*assertion*!= NULL*failed*");
- g_simple_action_set_state (action, g_variant_new_int32 (123));
+ g_simple_action_set_state (action, new_state);
g_test_assert_expected_messages ();
+ g_variant_unref (new_state);
}
g_object_unref (action);
@@ -536,6 +527,8 @@ count_activation (const gchar *action)
count = GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
count++;
g_hash_table_insert (activation_counts, (gpointer)action, GINT_TO_POINTER (count));
+
+ g_main_context_wakeup (NULL);
}
static gint
@@ -593,7 +586,7 @@ compare_action_groups (GActionGroup *a, GActionGroup *b)
alist = g_action_group_list_actions (a);
blist = g_action_group_list_actions (b);
- equal = strv_strv_cmp (alist, blist);
+ equal = strv_strv_cmp ((const gchar * const *) alist, (const gchar * const *) blist);
for (i = 0; equal && alist[i]; i++)
{
@@ -743,6 +736,41 @@ call_describe (gpointer user_data)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void
+action_added_removed_cb (GActionGroup *action_group,
+ char *action_name,
+ gpointer user_data)
+{
+ guint *counter = user_data;
+
+ *counter = *counter + 1;
+ g_main_context_wakeup (NULL);
+}
+
+static void
+action_enabled_changed_cb (GActionGroup *action_group,
+ char *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ guint *counter = user_data;
+
+ *counter = *counter + 1;
+ g_main_context_wakeup (NULL);
+}
+
+static void
+action_state_changed_cb (GActionGroup *action_group,
+ char *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ guint *counter = user_data;
+
+ *counter = *counter + 1;
+ g_main_context_wakeup (NULL);
+}
+
+static void
test_dbus_export (void)
{
GDBusConnection *bus;
@@ -754,6 +782,8 @@ test_dbus_export (void)
GVariant *v;
guint id;
gchar **actions;
+ guint n_actions_added = 0, n_actions_enabled_changed = 0, n_actions_removed = 0, n_actions_state_changed = 0;
+ gulong added_signal_id, enabled_changed_signal_id, removed_signal_id, state_changed_signal_id;
loop = g_main_loop_new (NULL, FALSE);
@@ -770,13 +800,19 @@ test_dbus_export (void)
g_assert_no_error (error);
proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
+ added_signal_id = g_signal_connect (proxy, "action-added", G_CALLBACK (action_added_removed_cb), &n_actions_added);
+ enabled_changed_signal_id = g_signal_connect (proxy, "action-enabled-changed", G_CALLBACK (action_enabled_changed_cb), &n_actions_enabled_changed);
+ removed_signal_id = g_signal_connect (proxy, "action-removed", G_CALLBACK (action_added_removed_cb), &n_actions_removed);
+ state_changed_signal_id = g_signal_connect (proxy, "action-state-changed", G_CALLBACK (action_state_changed_cb), &n_actions_state_changed);
actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
g_assert_cmpint (g_strv_length (actions), ==, 0);
g_strfreev (actions);
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ /* Actions are queried from the bus asynchronously after the first
+ * list_actions() call. Wait for the expected signals then check again. */
+ while (n_actions_added < G_N_ELEMENTS (exported_entries))
+ g_main_context_iteration (NULL, TRUE);
actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
@@ -795,54 +831,56 @@ test_dbus_export (void)
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
/* test that various changes get propagated from group to proxy */
+ n_actions_added = 0;
action = g_simple_action_new_stateful ("italic", NULL, g_variant_new_boolean (FALSE));
g_simple_action_group_insert (group, G_ACTION (action));
g_object_unref (action);
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_added == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "cut"));
g_simple_action_set_enabled (action, FALSE);
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_enabled_changed == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "bold"));
g_simple_action_set_state (action, g_variant_new_boolean (FALSE));
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_state_changed == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
g_simple_action_group_remove (group, "italic");
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_removed == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
/* test that activations and state changes propagate the other way */
-
+ n_actions_state_changed = 0;
g_assert_cmpint (activation_count ("copy"), ==, 0);
g_action_group_activate_action (G_ACTION_GROUP (proxy), "copy", NULL);
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (activation_count ("copy") == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (activation_count ("copy"), ==, 1);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
+ n_actions_state_changed = 0;
g_assert_cmpint (activation_count ("bold"), ==, 0);
g_action_group_activate_action (G_ACTION_GROUP (proxy), "bold", NULL);
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_state_changed == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (activation_count ("bold"), ==, 1);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
@@ -850,10 +888,11 @@ test_dbus_export (void)
g_assert_true (g_variant_get_boolean (v));
g_variant_unref (v);
+ n_actions_state_changed = 0;
g_action_group_change_action_state (G_ACTION_GROUP (proxy), "bold", g_variant_new_boolean (FALSE));
- g_timeout_add (100, stop_loop, loop);
- g_main_loop_run (loop);
+ while (n_actions_state_changed == 0)
+ g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (activation_count ("bold"), ==, 1);
g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
@@ -863,6 +902,10 @@ test_dbus_export (void)
g_dbus_connection_unexport_action_group (bus, id);
+ g_signal_handler_disconnect (proxy, added_signal_id);
+ g_signal_handler_disconnect (proxy, enabled_changed_signal_id);
+ g_signal_handler_disconnect (proxy, removed_signal_id);
+ g_signal_handler_disconnect (proxy, state_changed_signal_id);
g_object_unref (proxy);
g_object_unref (group);
g_main_loop_unref (loop);
diff --git a/gio/tests/appmonitor.c b/gio/tests/appmonitor.c
index 725d91e5c..9db8c4dea 100644
--- a/gio/tests/appmonitor.c
+++ b/gio/tests/appmonitor.c
@@ -78,6 +78,11 @@ test_app_monitor (Fixture *fixture,
GAppInfoMonitor *monitor;
GMainLoop *loop;
+#ifdef G_OS_WIN32
+ g_test_skip (".desktop monitor on win32");
+ return;
+#endif
+
app_path = g_build_filename (fixture->applications_dir, "app.desktop", NULL);
/* FIXME: this shouldn't be required */
diff --git a/gio/tests/cert-tests/key-cert-password-123.p12 b/gio/tests/cert-tests/key-cert-password-123.p12
new file mode 100644
index 000000000..4da265fd6
--- /dev/null
+++ b/gio/tests/cert-tests/key-cert-password-123.p12
Binary files differ
diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py
index d3a09bae7..e76c6d243 100644
--- a/gio/tests/codegen.py
+++ b/gio/tests/codegen.py
@@ -27,10 +27,10 @@ import subprocess
import sys
import tempfile
import unittest
+import xml.etree.ElementTree as ET
import taptestrunner
-
# Disable line length warnings as wrapping the C code templates would be hard
# flake8: noqa: E501
@@ -38,6 +38,10 @@ import taptestrunner
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
+def on_win32():
+ return sys.platform.find('win') != -1
+
+
class TestCodegen(unittest.TestCase):
"""Integration test for running gdbus-codegen.
@@ -55,7 +59,7 @@ class TestCodegen(unittest.TestCase):
cwd = ""
def setUp(self):
- self.timeout_seconds = 10 # seconds per test
+ self.timeout_seconds = 100 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
@@ -281,6 +285,7 @@ class TestCodegen(unittest.TestCase):
with self.assertRaises(subprocess.CalledProcessError):
self.runCodegen()
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_empty_interface_header(self):
"""Test generating a header with an empty interface file."""
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--header")
@@ -304,6 +309,7 @@ G_END_DECLS
result.out.strip(),
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_empty_interface_body(self):
"""Test generating a body with an empty interface file."""
result = self.runCodegenWithInterface("", "--output", "/dev/stdout", "--body")
@@ -323,6 +329,7 @@ G_END_DECLS
result.out.strip(),
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering."""
xml_contents1 = """
@@ -382,6 +389,47 @@ G_END_DECLS
# The output should be the same.
self.assertEqual(result1.out, result2.out)
+ def test_generate_docbook(self):
+ """Test the basic functionality of the docbook generator."""
+ xml_contents = """
+ <node>
+ <interface name="org.project.Bar.Frobnicator">
+ <method name="RandomMethod"/>
+ </interface>
+ </node>
+ """
+ res = self.runCodegenWithInterface(
+ xml_contents,
+ "--generate-docbook",
+ "test",
+ )
+ self.assertEqual("", res.err)
+ self.assertEqual("", res.out)
+ with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
+ xml_data = f.readlines()
+ self.assertTrue(len(xml_data) != 0)
+
+ def test_generate_rst(self):
+ """Test the basic functionality of the rst generator."""
+ xml_contents = """
+ <node>
+ <interface name="org.project.Bar.Frobnicator">
+ <method name="RandomMethod"/>
+ </interface>
+ </node>
+ """
+ res = self.runCodegenWithInterface(
+ xml_contents,
+ "--generate-rst",
+ "test",
+ )
+ self.assertEqual("", res.err)
+ self.assertEqual("", res.out)
+ with open("test-org.project.Bar.Frobnicator.rst", "r") as f:
+ rst = f.readlines()
+ self.assertTrue(len(rst) != 0)
+
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_min_required_invalid(self):
"""Test running with an invalid --glib-min-required."""
with self.assertRaises(subprocess.CalledProcessError):
@@ -394,6 +442,7 @@ G_END_DECLS
"hello mum",
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_min_required_too_low(self):
"""Test running with a --glib-min-required which is too low (and hence
probably a typo)."""
@@ -402,6 +451,7 @@ G_END_DECLS
"", "--output", "/dev/stdout", "--body", "--glib-min-required", "2.6"
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_min_required_major_only(self):
"""Test running with a --glib-min-required which contains only a major version."""
result = self.runCodegenWithInterface(
@@ -417,6 +467,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_min_required_with_micro(self):
"""Test running with a --glib-min-required which contains a micro version."""
result = self.runCodegenWithInterface(
@@ -425,6 +476,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_max_allowed_too_low(self):
"""Test running with a --glib-max-allowed which is too low (and hence
probably a typo)."""
@@ -433,6 +485,7 @@ G_END_DECLS
"", "--output", "/dev/stdout", "--body", "--glib-max-allowed", "2.6"
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_max_allowed_major_only(self):
"""Test running with a --glib-max-allowed which contains only a major version."""
result = self.runCodegenWithInterface(
@@ -441,6 +494,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_max_allowed_with_micro(self):
"""Test running with a --glib-max-allowed which contains a micro version."""
result = self.runCodegenWithInterface(
@@ -449,6 +503,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_max_allowed_unstable(self):
"""Test running with a --glib-max-allowed which is unstable. It should
be rounded up to the next stable version number, and hence should not
@@ -466,6 +521,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip())
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_glib_max_allowed_less_than_min_required(self):
"""Test running with a --glib-max-allowed which is less than
--glib-min-required."""
@@ -481,6 +537,7 @@ G_END_DECLS
"2.64",
)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_unix_fd_types_and_annotations(self):
"""Test an interface with `h` arguments, no annotation, and GLib < 2.64.
@@ -539,6 +596,7 @@ G_END_DECLS
self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GUnixFDList"), 18)
+ @unittest.skipIf(on_win32(), "requires /dev/stdout")
def test_call_flags_and_timeout_method_args(self):
"""Test that generated method call functions have @call_flags and
@timeout_msec args if and only if GLib >= 2.64.
@@ -585,6 +643,40 @@ G_END_DECLS
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 2)
self.assertEqual(result.out.strip().count("gint timeout_msec,"), 2)
+ def test_generate_valid_docbook(self):
+ """Test the basic functionality of the docbook generator."""
+ xml_contents = """
+ <node>
+ <interface name="org.project.Bar.Frobnicator">
+ <!-- Resize:
+ @size: New partition size in bytes, 0 for maximal size.
+ @options: Options.
+ @since 2.7.2
+
+ Resizes the partition.
+
+ The partition will not change its position but might be slightly bigger
+ than requested due to sector counts and alignment (e.g. 1MiB).
+ If the requested size can't be allocated it results in an error.
+ The maximal size can automatically be set by using 0 as size.
+ -->
+ <method name="Resize">
+ <arg name="size" direction="in" type="t"/>
+ <arg name="options" direction="in" type="a{sv}"/>
+ </method>
+ </interface>
+ </node>
+ """
+ res = self.runCodegenWithInterface(
+ xml_contents,
+ "--generate-docbook",
+ "test",
+ )
+ self.assertEqual("", res.err)
+ self.assertEqual("", res.out)
+ with open("test-org.project.Bar.Frobnicator.xml", "r") as f:
+ self.assertTrue(ET.parse(f) is not None)
+
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())
diff --git a/gio/tests/contenttype.c b/gio/tests/contenttype.c
index db34f1da8..6cfd366ff 100644
--- a/gio/tests/contenttype.c
+++ b/gio/tests/contenttype.c
@@ -1,8 +1,6 @@
#include <gio/gio.h>
#include <string.h>
-#include "glib/glib-private.h"
-
#define g_assert_content_type_equals(s1, s2) \
do { \
const char *__s1 = (s1), *__s2 = (s2); \
@@ -18,9 +16,6 @@
static void
test_guess (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *res;
gchar *expected;
gchar *existing_directory;
@@ -35,7 +30,7 @@ test_guess (void)
existing_directory = (gchar *) g_getenv ("SYSTEMROOT");
if (existing_directory)
- existing_directory = g_strdup_printf ("%s/", existing_directory);
+ existing_directory = g_strdup_printf ("%s" G_DIR_SEPARATOR_S, existing_directory);
#else
existing_directory = g_strdup ("/etc/");
#endif
@@ -61,7 +56,8 @@ test_guess (void)
g_free (res);
g_free (expected);
- /* Sadly OSX just doesn't have as large and robust of a mime type database as Linux */
+ /* Sadly win32 & OSX just don't have as large and robust of a mime type database as Linux */
+#ifndef G_OS_WIN32
#ifndef __APPLE__
res = g_content_type_guess ("foo", data, sizeof (data) - 1, &uncertain);
expected = g_content_type_from_mime_type ("text/plain");
@@ -115,7 +111,7 @@ test_guess (void)
g_assert_false (uncertain);
g_free (res);
g_free (expected);
-#endif
+#endif /* __APPLE__ */
res = g_content_type_guess (NULL, (guchar *)"%!PS-Adobe-2.0 EPSF-1.2", 23, &uncertain);
expected = g_content_type_from_mime_type ("image/x-eps");
@@ -131,15 +127,12 @@ test_guess (void)
g_assert_false (uncertain);
g_free (res);
g_free (expected);
-#endif
+#endif /* G_OS_WIN32 */
}
static void
test_unknown (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *unknown;
gchar *str;
@@ -149,15 +142,11 @@ test_unknown (void)
g_assert_cmpstr (str, ==, "application/octet-stream");
g_free (str);
g_free (unknown);
-#endif
}
static void
test_subtype (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *plain;
gchar *xml;
@@ -169,7 +158,6 @@ test_subtype (void)
g_free (plain);
g_free (xml);
-#endif
}
static gint
@@ -183,10 +171,6 @@ find_mime (gconstpointer a, gconstpointer b)
static void
test_list (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
- (void) find_mime;
-#else
GList *types;
gchar *plain;
gchar *xml;
@@ -211,17 +195,20 @@ test_list (void)
g_free (plain);
g_free (xml);
-#endif
}
static void
test_executable (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *type;
+#ifdef G_OS_WIN32
+ type = g_content_type_from_mime_type ("application/vnd.microsoft.portable-executable");
+ /* FIXME: the MIME is not in the default `MIME\Database\Content Type` registry.
+ * g_assert_true (g_content_type_can_be_executable (type));
+ */
+ g_free (type);
+#else
type = g_content_type_from_mime_type ("application/x-executable");
g_assert_true (g_content_type_can_be_executable (type));
g_free (type);
@@ -229,19 +216,15 @@ test_executable (void)
type = g_content_type_from_mime_type ("text/plain");
g_assert_true (g_content_type_can_be_executable (type));
g_free (type);
-
+#endif
type = g_content_type_from_mime_type ("image/png");
g_assert_false (g_content_type_can_be_executable (type));
g_free (type);
-#endif
}
static void
test_description (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *type;
gchar *desc;
@@ -251,15 +234,11 @@ test_description (void)
g_free (desc);
g_free (type);
-#endif
}
static void
test_icon (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gchar *type;
GIcon *icon;
@@ -274,7 +253,9 @@ test_icon (void)
#ifdef __APPLE__
g_assert_true (g_strv_contains (names, "text-*"));
#else
+#ifndef G_OS_WIN32
g_assert_true (g_strv_contains (names, "text-plain"));
+#endif
g_assert_true (g_strv_contains (names, "text-x-generic"));
#endif
}
@@ -289,22 +270,23 @@ test_icon (void)
const gchar *const *names;
names = g_themed_icon_get_names (G_THEMED_ICON (icon));
+#ifdef G_OS_WIN32
+ g_assert_true (g_strv_contains (names, "text-x-generic"));
+#else
g_assert_true (g_strv_contains (names, "application-rtf"));
#ifndef __APPLE__
g_assert_true (g_strv_contains (names, "x-office-document"));
#endif
+#endif
}
g_object_unref (icon);
g_free (type);
-#endif
}
static void
test_symbolic_icon (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#elif !defined(G_OS_WIN32)
+#ifndef G_OS_WIN32
gchar *type;
GIcon *icon;
@@ -352,9 +334,6 @@ test_symbolic_icon (void)
static void
test_tree (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
const gchar *tests[] = {
"x-content/image-dcf",
"x-content/unix-software",
@@ -365,8 +344,8 @@ test_tree (void)
gchar **types;
gsize i;
-#ifdef __APPLE__
- g_test_skip ("The OSX backend does not implement g_content_type_guess_for_tree()");
+#if defined(__APPLE__) || defined(G_OS_WIN32)
+ g_test_skip ("The OSX & Windows backends do not implement g_content_type_guess_for_tree()");
return;
#endif
@@ -379,15 +358,11 @@ test_tree (void)
g_strfreev (types);
g_object_unref (file);
}
-#endif
}
static void
test_type_is_a_special_case (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
gboolean res;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=782311");
@@ -395,19 +370,15 @@ test_type_is_a_special_case (void)
/* Everything but the inode type is application/octet-stream */
res = g_content_type_is_a ("inode/directory", "application/octet-stream");
g_assert_false (res);
-#ifndef __APPLE__
+#if !defined(__APPLE__) && !defined(G_OS_WIN32)
res = g_content_type_is_a ("anything", "application/octet-stream");
g_assert_true (res);
#endif
-#endif
}
static void
test_guess_svg_from_data (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#else
const gchar svgfilecontent[] = "<svg xmlns=\"http://www.w3.org/2000/svg\"\
xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\
<rect x=\"10\" y=\"10\" height=\"100\" width=\"100\"\n\
@@ -426,15 +397,12 @@ test_guess_svg_from_data (void)
#endif
g_assert_false (uncertain);
g_free (res);
-#endif
}
static void
test_mime_from_content (void)
{
-#ifdef _GLIB_ADDRESS_SANITIZER
- g_test_incomplete ("FIXME: Leaks xdgmime internal data, see glib#2310");
-#elif defined(__APPLE__)
+#ifdef __APPLE__
gchar *mime_type;
mime_type = g_content_type_get_mime_type ("com.microsoft.bmp");
g_assert_cmpstr (mime_type, ==, "image/bmp");
diff --git a/gio/tests/converter-stream.c b/gio/tests/converter-stream.c
index cf1853ba0..31399a78e 100644
--- a/gio/tests/converter-stream.c
+++ b/gio/tests/converter-stream.c
@@ -310,9 +310,9 @@ test_expander (void)
G_CONVERTER_INPUT_AT_END,
&n_read, &n_written, NULL);
- g_assert (cres == G_CONVERTER_FINISHED);
- g_assert (n_read == 11);
- g_assert (n_written == 41030);
+ g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
+ g_assert_cmpuint (n_read, ==, 11);
+ g_assert_cmpuint (n_written, ==, 41030);
g_converter_reset (expander);
@@ -320,9 +320,9 @@ test_expander (void)
sizeof (unexpanded_data),
NULL);
cstream = g_converter_input_stream_new (mem, expander);
- g_assert (g_converter_input_stream_get_converter (G_CONVERTER_INPUT_STREAM (cstream)) == expander);
+ g_assert_true (g_converter_input_stream_get_converter (G_CONVERTER_INPUT_STREAM (cstream)) == expander);
g_object_get (cstream, "converter", &converter, NULL);
- g_assert (converter == expander);
+ g_assert_true (converter == expander);
g_object_unref (converter);
g_object_unref (mem);
@@ -334,7 +334,7 @@ test_expander (void)
res = g_input_stream_read (cstream,
ptr, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
break;
ptr += res;
@@ -347,9 +347,9 @@ test_expander (void)
mem_out = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
cstream_out = g_converter_output_stream_new (mem_out, expander);
- g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (cstream_out)) == expander);
+ g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (cstream_out)) == expander);
g_object_get (cstream_out, "converter", &converter, NULL);
- g_assert (converter == expander);
+ g_assert_true (converter == expander);
g_object_unref (converter);
g_object_unref (mem_out);
@@ -359,13 +359,13 @@ test_expander (void)
res = g_output_stream_write (cstream_out,
unexpanded_data + i, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
{
- g_assert (i == sizeof(unexpanded_data) -1);
+ g_assert_cmpuint (i, ==, sizeof(unexpanded_data) -1);
break;
}
- g_assert (res == 1);
+ g_assert_cmpint (res, ==, 1);
}
g_output_stream_close (cstream_out, NULL, NULL);
@@ -402,9 +402,9 @@ test_compressor (void)
expanded, 100*1000,
G_CONVERTER_INPUT_AT_END,
&n_read, &expanded_size, NULL);
- g_assert (cres == G_CONVERTER_FINISHED);
- g_assert (n_read == 11);
- g_assert (expanded_size == 41030);
+ g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
+ g_assert_cmpuint (n_read, ==, 11);
+ g_assert_cmpuint (expanded_size, ==, 41030);
compressor = g_compressor_converter_new ();
@@ -424,7 +424,7 @@ test_compressor (void)
res = g_input_stream_read (cstream,
ptr, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
break;
ptr += res;
@@ -448,13 +448,13 @@ test_compressor (void)
res = g_output_stream_write (cstream_out,
expanded + i, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
{
- g_assert (i == expanded_size -1);
+ g_assert_cmpuint (i, ==, expanded_size -1);
break;
}
- g_assert (res == 1);
+ g_assert_cmpint (res, ==, 1);
}
g_output_stream_close (cstream_out, NULL, NULL);
@@ -485,15 +485,15 @@ test_compressor (void)
res = g_input_stream_read (cstream,
ptr, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
break;
ptr += res;
total_read += res;
}
- g_assert (total_read == 1);
- g_assert (*converted == 5);
+ g_assert_cmpuint (total_read, ==, 1);
+ g_assert_cmpuint (*converted, ==, 5);
g_object_unref (cstream);
@@ -511,16 +511,16 @@ test_compressor (void)
res = g_input_stream_read (cstream,
ptr, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
break;
ptr += res;
total_read += res;
}
- g_assert (total_read == 2);
- g_assert (converted[0] == 5);
- g_assert (converted[1] == 5);
+ g_assert_cmpuint (total_read, ==, 2);
+ g_assert_cmpuint (converted[0], ==, 5);
+ g_assert_cmpuint (converted[1], ==, 5);
g_object_unref (cstream);
@@ -547,13 +547,13 @@ test_compressor (void)
break;
}
- g_assert (res != 0);
+ g_assert_cmpint (res, !=, 0);
ptr += res;
total_read += res;
}
- g_assert (total_read == 1);
- g_assert (converted[0] == 5);
+ g_assert_cmpuint (total_read, ==, 1);
+ g_assert_cmpuint (converted[0], ==, 5);
g_object_unref (cstream);
@@ -710,7 +710,7 @@ test_converter_leftover (void)
converted + total_read,
LEFTOVER_BUFSIZE - total_read,
NULL, &error);
- g_assert (res >= 0);
+ g_assert_cmpint (res, >=, 0);
if (res == 0)
break;
total_read += res;
@@ -763,10 +763,10 @@ test_roundtrip (gconstpointer data)
g_file_info_set_name (info, "foo");
g_object_set (compressor, "file-info", info, NULL);
info2 = g_zlib_compressor_get_file_info (G_ZLIB_COMPRESSOR (compressor));
- g_assert (info == info2);
+ g_assert_true (info == info2);
g_object_unref (info);
costream1 = g_converter_output_stream_new (ostream1, compressor);
- g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
+ g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
g_output_stream_splice (costream1, istream0, 0, NULL, &error);
g_assert_no_error (error);
@@ -829,7 +829,7 @@ test_charset (gconstpointer data)
conv = (GConverter *)g_charset_converter_new (test->charset_out, test->charset_in, NULL);
g_object_get (conv, "use-fallback", &fallback, NULL);
- g_assert (!fallback);
+ g_assert_false (fallback);
in = g_memory_input_stream_new_from_data (test->text_in, -1, NULL);
in2 = g_converter_input_stream_new (in, conv);
@@ -862,7 +862,7 @@ test_charset (gconstpointer data)
g_converter_reset (conv);
- g_assert (!g_charset_converter_get_use_fallback (G_CHARSET_CONVERTER (conv)));
+ g_assert_false (g_charset_converter_get_use_fallback (G_CHARSET_CONVERTER (conv)));
g_charset_converter_set_use_fallback (G_CHARSET_CONVERTER (conv), TRUE);
in = g_memory_input_stream_new_from_data (test->text_in, -1, NULL);
@@ -981,9 +981,9 @@ test_converter_pollable (void)
expanded, 100*1000,
G_CONVERTER_INPUT_AT_END,
&n_read, &expanded_size, NULL);
- g_assert (cres == G_CONVERTER_FINISHED);
- g_assert (n_read == 11);
- g_assert (expanded_size == 41030);
+ g_assert_cmpint (cres, ==, G_CONVERTER_FINISHED);
+ g_assert_cmpuint (n_read, ==, 11);
+ g_assert_cmpuint (expanded_size, ==, 41030);
expanded_end = expanded + expanded_size;
make_socketpair (&left, &right);
@@ -995,7 +995,7 @@ test_converter_pollable (void)
cstream = g_converter_input_stream_new (g_io_stream_get_input_stream (left),
compressor);
pollable_in = G_POLLABLE_INPUT_STREAM (cstream);
- g_assert (g_pollable_input_stream_can_poll (pollable_in));
+ g_assert_true (g_pollable_input_stream_can_poll (pollable_in));
socket_out = g_io_stream_get_output_stream (right);
@@ -1017,15 +1017,16 @@ test_converter_pollable (void)
}
else if (socket_out)
{
+ g_output_stream_close (socket_out, NULL, NULL);
g_object_unref (right);
socket_out = NULL;
}
/* Wait a few ticks to check for the pipe to propagate the
- * write. Finesses the race condition in the following test,
- * where is_readable fails because the write hasn't propagated,
- * but the read then succeeds because it has. */
- g_usleep (80L);
+ * write. We can’t wait on a GSource as that might affect the stream under
+ * test, so just poll. */
+ while (!g_pollable_input_stream_is_readable (pollable_in))
+ g_usleep (80L);
is_readable = g_pollable_input_stream_is_readable (pollable_in);
res = g_pollable_input_stream_read_nonblocking (pollable_in,
@@ -1038,7 +1039,10 @@ test_converter_pollable (void)
/* After closing the write end, we can't get WOULD_BLOCK any more */
if (!socket_out)
- g_assert_cmpint (res, !=, -1);
+ {
+ g_assert_no_error (error);
+ g_assert_cmpint (res, !=, -1);
+ }
if (res == -1)
{
@@ -1071,8 +1075,8 @@ test_converter_pollable (void)
cstream_out = g_converter_output_stream_new (mem_out, compressor);
g_object_unref (mem_out);
pollable_out = G_POLLABLE_OUTPUT_STREAM (cstream_out);
- g_assert (g_pollable_output_stream_can_poll (pollable_out));
- g_assert (g_pollable_output_stream_is_writable (pollable_out));
+ g_assert_true (g_pollable_output_stream_can_poll (pollable_out));
+ g_assert_true (g_pollable_output_stream_is_writable (pollable_out));
for (i = 0; i < expanded_size; i++)
{
@@ -1080,13 +1084,13 @@ test_converter_pollable (void)
res = g_pollable_output_stream_write_nonblocking (pollable_out,
expanded + i, 1,
NULL, &error);
- g_assert (res != -1);
+ g_assert_cmpint (res, !=, -1);
if (res == 0)
{
- g_assert (i == expanded_size -1);
+ g_assert_cmpuint (i, ==, expanded_size -1);
break;
}
- g_assert (res == 1);
+ g_assert_cmpint (res, ==, 1);
}
g_output_stream_close (cstream_out, NULL, NULL);
@@ -1127,7 +1131,7 @@ test_truncation (gconstpointer data)
ostream1 = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
compressor = G_CONVERTER (g_zlib_compressor_new (test->format, -1));
costream1 = g_converter_output_stream_new (ostream1, compressor);
- g_assert (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
+ g_assert_true (g_converter_output_stream_get_converter (G_CONVERTER_OUTPUT_STREAM (costream1)) == compressor);
g_output_stream_splice (costream1, istream0, 0, NULL, &error);
g_assert_no_error (error);
diff --git a/gio/tests/credentials.c b/gio/tests/credentials.c
index 2b0f1f787..070019f1c 100644
--- a/gio/tests/credentials.c
+++ b/gio/tests/credentials.c
@@ -25,6 +25,28 @@
#include <gio/gio.h>
#include <gio/gcredentialsprivate.h>
+#ifdef G_OS_WIN32
+
+static void
+test_basic (void)
+{
+ GCredentials *creds = g_credentials_new ();
+ gchar *stringified;
+ DWORD *pid;
+
+ stringified = g_credentials_to_string (creds);
+ g_test_message ("%s", stringified);
+ g_free (stringified);
+
+ pid = g_credentials_get_native (creds,
+ G_CREDENTIALS_TYPE_WIN32_PID);
+ g_assert_cmpuint (*pid, ==, GetCurrentProcessId ());
+
+ g_object_unref (creds);
+}
+
+#else
+
static void
test_basic (void)
{
@@ -177,6 +199,8 @@ test_basic (void)
g_object_unref (other);
}
+#endif /* !G_OS_WIN32 */
+
int
main (int argc,
char *argv[])
diff --git a/gio/tests/cxx.cpp b/gio/tests/cxx.cpp
new file mode 100644
index 000000000..1f28d0b27
--- /dev/null
+++ b/gio/tests/cxx.cpp
@@ -0,0 +1,26 @@
+/* Copyright (C) 2001 Sebastian Wilhelmi <wilhelmi@google.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* A trivial C++ program to be compiled in C++ mode, which
+ * smoketests that the GIO headers are valid C++ headers. */
+
+#include <gio/gio.h>
+
+int
+main ()
+{
+ return 0;
+}
diff --git a/gio/tests/debugcontroller.c b/gio/tests/debugcontroller.c
new file mode 100644
index 000000000..c20acd659
--- /dev/null
+++ b/gio/tests/debugcontroller.c
@@ -0,0 +1,396 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright © 2022 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Author: Philip Withnall <pwithnall@endlessos.org>
+ */
+
+#include <gio/gio.h>
+#include <locale.h>
+
+
+static void
+test_dbus_basic (void)
+{
+ GTestDBus *bus;
+ GDBusConnection *connection = NULL, *connection2 = NULL;
+ GDebugControllerDBus *controller = NULL;
+ gboolean old_value;
+ gboolean debug_enabled;
+ GError *local_error = NULL;
+
+ g_test_summary ("Smoketest for construction and setting of a #GDebugControllerDBus.");
+
+ /* Set up a test session bus and connection. */
+ bus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_test_dbus_up (bus);
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Create a controller for this process. */
+ controller = g_debug_controller_dbus_new (connection, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (controller);
+ g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller));
+
+ /* Try enabling and disabling debug output from within the process. */
+ old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller));
+
+ g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), TRUE);
+ g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)));
+
+ g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), FALSE);
+ g_assert_false (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)));
+
+ /* Reset the debug state and check using g_object_get(), to exercise that. */
+ g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value);
+
+ g_object_get (G_OBJECT (controller),
+ "debug-enabled", &debug_enabled,
+ "connection", &connection2,
+ NULL);
+ g_assert_true (debug_enabled == old_value);
+ g_assert_true (connection2 == connection);
+ g_clear_object (&connection2);
+
+ g_debug_controller_dbus_stop (controller);
+ while (g_main_context_iteration (NULL, FALSE));
+ g_assert_finalize_object (controller);
+ g_clear_object (&connection);
+
+ g_test_dbus_down (bus);
+ g_clear_object (&bus);
+}
+
+static void
+test_dbus_duplicate (void)
+{
+ GTestDBus *bus;
+ GDBusConnection *connection = NULL;
+ GDebugControllerDBus *controller1 = NULL, *controller2 = NULL;
+ GError *local_error = NULL;
+
+ g_test_summary ("Test that creating a second #GDebugControllerDBus on the same D-Bus connection fails.");
+
+ /* Set up a test session bus and connection. */
+ bus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_test_dbus_up (bus);
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Create a controller for this process. */
+ controller1 = g_debug_controller_dbus_new (connection, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (controller1);
+
+ /* And try creating a second one. */
+ controller2 = g_debug_controller_dbus_new (connection, NULL, &local_error);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS);
+ g_assert_null (controller2);
+ g_clear_error (&local_error);
+
+ g_debug_controller_dbus_stop (controller1);
+ while (g_main_context_iteration (NULL, FALSE));
+ g_assert_finalize_object (controller1);
+ g_clear_object (&connection);
+
+ g_test_dbus_down (bus);
+ g_clear_object (&bus);
+}
+
+static void
+async_result_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAsyncResult **result_out = user_data;
+
+ g_assert_null (*result_out);
+ *result_out = g_object_ref (result);
+
+ g_main_context_wakeup (g_main_context_get_thread_default ());
+}
+
+static gboolean
+authorize_false_cb (GDebugControllerDBus *debug_controller,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ return FALSE;
+}
+
+static gboolean
+authorize_true_cb (GDebugControllerDBus *debug_controller,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+static void
+notify_debug_enabled_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ guint *notify_count_out = user_data;
+
+ *notify_count_out = *notify_count_out + 1;
+}
+
+static void
+properties_changed_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ guint *properties_changed_count_out = user_data;
+
+ *properties_changed_count_out = *properties_changed_count_out + 1;
+ g_main_context_wakeup (g_main_context_get_thread_default ());
+}
+
+static void
+test_dbus_properties (void)
+{
+ GTestDBus *bus;
+ GDBusConnection *controller_connection = NULL;
+ GDBusConnection *remote_connection = NULL;
+ GDebugControllerDBus *controller = NULL;
+ gboolean old_value;
+ GAsyncResult *result = NULL;
+ GVariant *reply = NULL;
+ GVariant *debug_enabled_variant = NULL;
+ gboolean debug_enabled;
+ GError *local_error = NULL;
+ gulong handler_id;
+ gulong notify_id;
+ guint notify_count = 0;
+ guint properties_changed_id;
+ guint properties_changed_count = 0;
+
+ g_test_summary ("Test getting and setting properties on a #GDebugControllerDBus.");
+
+ /* Set up a test session bus and connection. Set up a separate second
+ * connection to simulate a remote peer. */
+ bus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_test_dbus_up (bus);
+
+ controller_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ remote_connection = g_dbus_connection_new_for_address_sync (g_test_dbus_get_bus_address (bus),
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+ NULL,
+ NULL,
+ &local_error);
+ g_assert_no_error (local_error);
+
+ /* Create a controller for this process. */
+ controller = g_debug_controller_dbus_new (controller_connection, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (controller);
+ g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller));
+
+ old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller));
+ notify_id = g_signal_connect (controller, "notify::debug-enabled", G_CALLBACK (notify_debug_enabled_cb), &notify_count);
+
+ properties_changed_id = g_dbus_connection_signal_subscribe (remote_connection,
+ g_dbus_connection_get_unique_name (controller_connection),
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ "/org/gtk/Debugging",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ properties_changed_cb,
+ &properties_changed_count,
+ NULL);
+
+ /* Get the debug status remotely. */
+ g_dbus_connection_call (remote_connection,
+ g_dbus_connection_get_unique_name (controller_connection),
+ "/org/gtk/Debugging",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)", "org.gtk.Debugging", "DebugEnabled"),
+ G_VARIANT_TYPE ("(v)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ async_result_cb,
+ &result);
+ g_assert_no_error (local_error);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
+ g_assert_no_error (local_error);
+ g_clear_object (&result);
+
+ g_variant_get (reply, "(v)", &debug_enabled_variant);
+ debug_enabled = g_variant_get_boolean (debug_enabled_variant);
+ g_assert_true (debug_enabled == old_value);
+ g_assert_cmpuint (notify_count, ==, 0);
+ g_assert_cmpuint (properties_changed_count, ==, 0);
+
+ g_clear_pointer (&debug_enabled_variant, g_variant_unref);
+ g_clear_pointer (&reply, g_variant_unref);
+
+ /* Set the debug status remotely. The first attempt should fail due to no
+ * authorisation handler being connected. The second should fail due to the
+ * now-connected handler returning %FALSE. The third attempt should
+ * succeed. */
+ g_dbus_connection_call (remote_connection,
+ g_dbus_connection_get_unique_name (controller_connection),
+ "/org/gtk/Debugging",
+ "org.gtk.Debugging",
+ "SetDebugEnabled",
+ g_variant_new ("(b)", !old_value),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ async_result_cb,
+ &result);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
+ g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED);
+ g_clear_object (&result);
+ g_clear_error (&local_error);
+
+ g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
+ g_assert_cmpuint (notify_count, ==, 0);
+ g_assert_cmpuint (properties_changed_count, ==, 0);
+
+ g_clear_pointer (&debug_enabled_variant, g_variant_unref);
+ g_clear_pointer (&reply, g_variant_unref);
+
+ /* Attach an authorisation handler and try again. */
+ handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_false_cb), NULL);
+
+ g_dbus_connection_call (remote_connection,
+ g_dbus_connection_get_unique_name (controller_connection),
+ "/org/gtk/Debugging",
+ "org.gtk.Debugging",
+ "SetDebugEnabled",
+ g_variant_new ("(b)", !old_value),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ async_result_cb,
+ &result);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
+ g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED);
+ g_clear_object (&result);
+ g_clear_error (&local_error);
+
+ g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
+ g_assert_cmpuint (notify_count, ==, 0);
+ g_assert_cmpuint (properties_changed_count, ==, 0);
+
+ g_clear_pointer (&debug_enabled_variant, g_variant_unref);
+ g_clear_pointer (&reply, g_variant_unref);
+
+ g_signal_handler_disconnect (controller, handler_id);
+ handler_id = 0;
+
+ /* Attach another signal handler which will grant access, and try again. */
+ handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_true_cb), NULL);
+
+ g_dbus_connection_call (remote_connection,
+ g_dbus_connection_get_unique_name (controller_connection),
+ "/org/gtk/Debugging",
+ "org.gtk.Debugging",
+ "SetDebugEnabled",
+ g_variant_new ("(b)", !old_value),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ async_result_cb,
+ &result);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
+ g_assert_no_error (local_error);
+ g_clear_object (&result);
+
+ g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == !old_value);
+ g_assert_cmpuint (notify_count, ==, 1);
+ g_assert_cmpuint (properties_changed_count, ==, 1);
+
+ g_clear_pointer (&debug_enabled_variant, g_variant_unref);
+ g_clear_pointer (&reply, g_variant_unref);
+
+ g_signal_handler_disconnect (controller, handler_id);
+ handler_id = 0;
+
+ /* Set the debug status locally. */
+ g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value);
+ g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
+ g_assert_cmpuint (notify_count, ==, 2);
+
+ while (properties_changed_count != 2)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_assert_cmpuint (properties_changed_count, ==, 2);
+
+ g_signal_handler_disconnect (controller, notify_id);
+ notify_id = 0;
+
+ g_dbus_connection_signal_unsubscribe (remote_connection, properties_changed_id);
+ properties_changed_id = 0;
+
+ g_debug_controller_dbus_stop (controller);
+ while (g_main_context_iteration (NULL, FALSE));
+ g_assert_finalize_object (controller);
+ g_clear_object (&controller_connection);
+ g_clear_object (&remote_connection);
+
+ g_test_dbus_down (bus);
+ g_clear_object (&bus);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ setlocale (LC_ALL, "");
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/debug-controller/dbus/basic", test_dbus_basic);
+ g_test_add_func ("/debug-controller/dbus/duplicate", test_dbus_duplicate);
+ g_test_add_func ("/debug-controller/dbus/properties", test_dbus_properties);
+
+ return g_test_run ();
+}
diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c
index 15dcd8f1c..4a5de6017 100644
--- a/gio/tests/desktop-app-info.c
+++ b/gio/tests/desktop-app-info.c
@@ -728,6 +728,20 @@ test_show_in (void)
assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop:../invalid/desktop");
}
+static void
+on_launch_started (GAppLaunchContext *context, GAppInfo *info, GVariant *platform_data, gpointer data)
+{
+ gboolean *invoked = data;
+
+ g_assert_true (G_IS_APP_LAUNCH_CONTEXT (context));
+ g_assert_true (G_IS_APP_INFO (info));
+ /* Our default context doesn't fill in any platform data */
+ g_assert_null (platform_data);
+
+ g_assert_false (*invoked);
+ *invoked = TRUE;
+}
+
/* Test g_desktop_app_info_launch_uris_as_manager() and
* g_desktop_app_info_launch_uris_as_manager_with_fds()
*/
@@ -738,6 +752,8 @@ test_launch_as_manager (void)
GError *error = NULL;
gboolean retval;
const gchar *path;
+ gboolean invoked = FALSE;
+ GAppLaunchContext *context;
if (g_getenv ("DISPLAY") == NULL || g_getenv ("DISPLAY")[0] == '\0')
{
@@ -754,23 +770,43 @@ test_launch_as_manager (void)
return;
}
- retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, NULL, 0,
+ context = g_app_launch_context_new ();
+ g_signal_connect (context, "launch-started",
+ G_CALLBACK (on_launch_started),
+ &invoked);
+ retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, context, 0,
NULL, NULL,
NULL, NULL,
&error);
g_assert_no_error (error);
g_assert_true (retval);
+ g_assert_true (invoked);
+ invoked = FALSE;
retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
- NULL, NULL, 0,
+ NULL, context, 0,
NULL, NULL,
NULL, NULL,
-1, -1, -1,
&error);
g_assert_no_error (error);
g_assert_true (retval);
+ g_assert_true (invoked);
g_object_unref (appinfo);
+ g_assert_finalize_object (context);
+}
+
+/* Test if Desktop-File Id is correctly formed */
+static void
+test_id (void)
+{
+ gchar *result;
+
+ result = run_apps ("default-for-type", "application/vnd.kde.okular-archive",
+ TRUE, FALSE, NULL, NULL, NULL);
+ g_assert_cmpstr (result, ==, "kde4-okular.desktop\n");
+ g_free (result);
}
int
@@ -794,6 +830,7 @@ main (int argc,
g_test_add_func ("/desktop-app-info/implements", test_implements);
g_test_add_func ("/desktop-app-info/show-in", test_show_in);
g_test_add_func ("/desktop-app-info/launch-as-manager", test_launch_as_manager);
+ g_test_add_func ("/desktop-app-info/id", test_id);
return g_test_run ();
}
diff --git a/gio/tests/file.c b/gio/tests/file.c
index 2f1b7b310..a849e83cf 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -3006,6 +3006,111 @@ test_build_attribute_list_for_copy (void)
g_clear_object (&tmpfile);
}
+typedef struct
+{
+ GError *error;
+ gboolean done;
+ gboolean res;
+} MoveAsyncData;
+
+static void
+test_move_async_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ MoveAsyncData *data = user_data;
+ GError *error = NULL;
+
+ data->res = g_file_move_finish (file, result, &error);
+ data->error = error;
+ data->done = TRUE;
+}
+
+typedef struct
+{
+ goffset total_num_bytes;
+} MoveAsyncProgressData;
+
+static void
+test_move_async_progress_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ MoveAsyncProgressData *data = user_data;
+ data->total_num_bytes = total_num_bytes;
+}
+
+/* Test that move_async() moves the file correctly */
+static void
+test_move_async (void)
+{
+ MoveAsyncData data = { 0 };
+ MoveAsyncProgressData progress_data = { 0 };
+ GFile *source;
+ GFileIOStream *iostream;
+ GOutputStream *ostream;
+ GFile *destination;
+ gchar *destination_path;
+ GError *error = NULL;
+ gboolean res;
+ const guint8 buffer[] = {1, 2, 3, 4, 5};
+
+ source = g_file_new_tmp ("g_file_move_XXXXXX", &iostream, NULL);
+
+ destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_target", NULL);
+ destination = g_file_new_for_path (destination_path);
+
+ g_assert_nonnull (source);
+ g_assert_nonnull (iostream);
+
+ res = g_file_query_exists (source, NULL);
+ g_assert_true (res);
+ res = g_file_query_exists (destination, NULL);
+ g_assert_false (res);
+
+ // Write a known amount of bytes to the file, so we can test the progress callback against it
+ ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
+ g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
+ g_assert_no_error (error);
+
+ g_file_move_async (source,
+ destination,
+ G_FILE_COPY_NONE,
+ 0,
+ NULL,
+ test_move_async_progress_cb,
+ &progress_data,
+ test_move_async_cb,
+ &data);
+
+ while (!data.done)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_assert_no_error (data.error);
+ g_assert_true (data.res);
+ g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
+
+ res = g_file_query_exists (source, NULL);
+ g_assert_false (res);
+ res = g_file_query_exists (destination, NULL);
+ g_assert_true (res);
+
+ res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+ g_object_unref (iostream);
+
+ res = g_file_delete (destination, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ g_object_unref (source);
+ g_object_unref (destination);
+
+ g_free (destination_path);
+}
+
int
main (int argc, char *argv[])
{
@@ -3049,6 +3154,7 @@ main (int argc, char *argv[])
g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors);
g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
+ g_test_add_func ("/file/move_async", test_move_async);
return g_test_run ();
}
diff --git a/gio/tests/g-file-info-filesystem-readonly.c b/gio/tests/g-file-info-filesystem-readonly.c
index 9b94c3220..ddf99da1e 100644
--- a/gio/tests/g-file-info-filesystem-readonly.c
+++ b/gio/tests/g-file-info-filesystem-readonly.c
@@ -135,6 +135,13 @@ test_filesystem_readonly (gconstpointer with_mount_monitor)
if (! g_file_set_contents (file_in_mount, "Example", -1, NULL))
{
g_test_skip ("Failed to create file needed to proceed further with the test");
+
+ g_free (dir_mountpoint);
+ g_free (file_in_mount);
+ g_free (dir_to_mount);
+ g_free (curdir);
+ g_free (fusermount);
+ g_free (bindfs);
return;
}
@@ -147,8 +154,18 @@ test_filesystem_readonly (gconstpointer with_mount_monitor)
{
gchar *skip_message = g_strdup_printf ("Failed to run bindfs to set up test: %s", error->message);
g_test_skip (skip_message);
+
g_free (skip_message);
g_clear_error (&error);
+
+ g_clear_object (&mount_monitor);
+ g_free (dir_mountpoint);
+ g_free (file_in_mount);
+ g_free (dir_to_mount);
+ g_free (curdir);
+ g_free (fusermount);
+ g_free (bindfs);
+
return;
}
@@ -170,6 +187,18 @@ test_filesystem_readonly (gconstpointer with_mount_monitor)
if (! g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
{
g_test_skip ("Failed to create readonly file needed to proceed further with the test");
+
+ g_clear_object (&file_info);
+ g_clear_object (&mounted_file);
+ g_free (file_in_mountpoint);
+ g_clear_object (&mount_monitor);
+ g_free (dir_mountpoint);
+ g_free (file_in_mount);
+ g_free (dir_to_mount);
+ g_free (curdir);
+ g_free (fusermount);
+ g_free (bindfs);
+
return;
}
diff --git a/gio/tests/g-file-info.c b/gio/tests/g-file-info.c
index fd0c64b55..59411c3a8 100644
--- a/gio/tests/g-file-info.c
+++ b/gio/tests/g-file-info.c
@@ -654,7 +654,11 @@ test_internal_enhanced_stdio (void)
g_assert_cmpuint (alsize_ps, <, 0x40000000);
g_assert_cmpuint (size_ps, >, G_GUINT64_CONSTANT (0xFFFFFFFF));
g_assert_cmpuint (statbuf_ps.st_size, >, 0);
+#if defined(_WIN64)
+ g_assert_cmpuint (statbuf_ps.st_size, ==, G_GUINT64_CONSTANT (0x10000000f));
+#else
g_assert_cmpuint (statbuf_ps.st_size, <=, 0xFFFFFFFF);
+#endif
g_object_unref (fi_ps);
g_object_unref (gf_ps);
diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c
index 18288f36d..eabfdd331 100644
--- a/gio/tests/gdbus-auth.c
+++ b/gio/tests/gdbus-auth.c
@@ -167,6 +167,7 @@ test_auth_mechanism (const gchar *allowed_client_mechanism,
GMainLoop *loop;
GThread *client_thread;
TestAuthData data;
+ guint timeout_id;
server = server_new_for_mechanism (allowed_server_mechanism);
@@ -177,7 +178,7 @@ test_auth_mechanism (const gchar *allowed_client_mechanism,
G_CALLBACK (test_auth_on_new_connection),
loop);
- g_timeout_add_seconds (5, test_auth_on_timeout, NULL);
+ timeout_id = g_timeout_add_seconds (5, test_auth_on_timeout, NULL);
data.allowed_client_mechanism = allowed_client_mechanism;
data.allowed_server_mechanism = allowed_server_mechanism;
@@ -195,6 +196,7 @@ test_auth_mechanism (const gchar *allowed_client_mechanism,
g_dbus_server_stop (server);
g_thread_join (client_thread);
+ g_source_remove (timeout_id);
while (g_main_context_iteration (NULL, FALSE));
g_main_loop_unref (loop);
diff --git a/gio/tests/gdbus-connection-flush.c b/gio/tests/gdbus-connection-flush.c
index 8c925825a..c1647651e 100644
--- a/gio/tests/gdbus-connection-flush.c
+++ b/gio/tests/gdbus-connection-flush.c
@@ -179,8 +179,8 @@ setup_client_cb (GObject *source,
f->client_conn = g_dbus_connection_new_finish (res, &f->error);
g_assert_no_error (f->error);
- g_assert (G_IS_DBUS_CONNECTION (f->client_conn));
- g_assert (f->client_conn == G_DBUS_CONNECTION (source));
+ g_assert_true (G_IS_DBUS_CONNECTION (f->client_conn));
+ g_assert_true (f->client_conn == G_DBUS_CONNECTION (source));
}
static void
@@ -192,8 +192,8 @@ setup_server_cb (GObject *source,
f->server_conn = g_dbus_connection_new_finish (res, &f->error);
g_assert_no_error (f->error);
- g_assert (G_IS_DBUS_CONNECTION (f->server_conn));
- g_assert (f->server_conn == G_DBUS_CONNECTION (source));
+ g_assert_true (G_IS_DBUS_CONNECTION (f->server_conn));
+ g_assert_true (f->server_conn == G_DBUS_CONNECTION (source));
}
static void
@@ -206,21 +206,21 @@ setup (Fixture *f,
ok = test_pipe (&f->server_istream, &f->client_real_ostream, &f->error);
g_assert_no_error (f->error);
- g_assert (G_IS_OUTPUT_STREAM (f->client_real_ostream));
- g_assert (G_IS_INPUT_STREAM (f->server_istream));
- g_assert (ok);
+ g_assert_true (G_IS_OUTPUT_STREAM (f->client_real_ostream));
+ g_assert_true (G_IS_INPUT_STREAM (f->server_istream));
+ g_assert_true (ok);
f->client_ostream = g_object_new (MY_TYPE_OUTPUT_STREAM,
"base-stream", f->client_real_ostream,
"close-base-stream", TRUE,
NULL);
- g_assert (G_IS_OUTPUT_STREAM (f->client_ostream));
+ g_assert_true (G_IS_OUTPUT_STREAM (f->client_ostream));
ok = test_pipe (&f->client_istream, &f->server_ostream, &f->error);
g_assert_no_error (f->error);
- g_assert (G_IS_OUTPUT_STREAM (f->server_ostream));
- g_assert (G_IS_INPUT_STREAM (f->client_istream));
- g_assert (ok);
+ g_assert_true (G_IS_OUTPUT_STREAM (f->server_ostream));
+ g_assert_true (G_IS_INPUT_STREAM (f->client_istream));
+ g_assert_true (ok);
f->client_stream = test_io_stream_new (f->client_istream, f->client_ostream);
f->server_stream = test_io_stream_new (f->server_istream, f->server_ostream);
@@ -244,13 +244,13 @@ flush_cb (GObject *source,
Fixture *f = user_data;
gboolean ok;
- g_assert (G_IS_DBUS_CONNECTION (source));
- g_assert (G_IS_DBUS_CONNECTION (f->client_conn));
+ g_assert_true (G_IS_DBUS_CONNECTION (source));
+ g_assert_true (G_IS_DBUS_CONNECTION (f->client_conn));
g_assert_cmpuint ((guintptr) f->client_conn, ==, (guintptr) G_DBUS_CONNECTION (source));
ok = g_dbus_connection_flush_finish (f->client_conn, res, &f->error);
g_assert_no_error (f->error);
- g_assert (ok);
+ g_assert_true (ok);
f->flushed = TRUE;
}
@@ -270,7 +270,7 @@ test_flush_busy (Fixture *f,
"com.example.Foo", "SomeSignal", NULL,
&f->error);
g_assert_no_error (f->error);
- g_assert (ok);
+ g_assert_true (ok);
/* wait for at least part of the message to have started writing -
* the write will block indefinitely in the worker thread
@@ -318,7 +318,7 @@ test_flush_idle (Fixture *f,
"com.example.Foo", "SomeSignal", NULL,
&f->error);
g_assert_no_error (f->error);
- g_assert (ok);
+ g_assert_true (ok);
/* wait for at least part of the message to have been written */
do {
@@ -368,10 +368,7 @@ main (int argc,
{
gint ret;
- /* FIXME: Add debug for https://gitlab.gnome.org/GNOME/glib/issues/1929 */
- g_setenv ("G_DBUS_DEBUG", "authentication", TRUE);
-
- g_test_init (&argc, &argv, NULL);
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
g_test_add ("/gdbus/connection/flush/busy", Fixture, NULL,
setup, test_flush_busy, teardown);
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
index 0410a880a..396b1a40a 100644
--- a/gio/tests/gdbus-connection.c
+++ b/gio/tests/gdbus-connection.c
@@ -416,7 +416,7 @@ test_connection_send (void)
/*
* Check that we never actually send a message if the GCancellable
- * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED
+ * is already cancelled - i.e. we should get G_IO_ERROR_CANCELLED
* when the actual connection is not up.
*/
ca = g_cancellable_new ();
diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c
index 4cdc24492..f66b1f74c 100644
--- a/gio/tests/gdbus-export.c
+++ b/gio/tests/gdbus-export.c
@@ -602,20 +602,6 @@ on_subtree_unregistered (gpointer user_data)
data->num_unregistered_subtree_calls++;
}
-static gboolean
-_g_strv_has_string (const gchar* const * haystack,
- const gchar *needle)
-{
- guint n;
-
- for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
- {
- if (g_strcmp0 (haystack[n], needle) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
/* -------------------- */
static gchar **
@@ -1306,11 +1292,11 @@ test_object_registration (void)
nodes = get_nodes_at (c, "/foo/boss");
g_assert (nodes != NULL);
g_assert_cmpint (g_strv_length (nodes), ==, 5);
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1p1"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker2"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "interns"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "executives"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "worker1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "worker1p1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "worker2"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "interns"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "executives"));
g_strfreev (nodes);
/* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */
g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5);
@@ -1322,7 +1308,7 @@ test_object_registration (void)
*/
nodes = get_nodes_at (c, "/foo/boss/executives");
g_assert (nodes != NULL);
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
g_assert_cmpint (g_strv_length (nodes), ==, 1);
g_strfreev (nodes);
g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0);
@@ -1332,11 +1318,11 @@ test_object_registration (void)
nodes = get_nodes_at (c, "/foo/boss/executives");
g_assert (nodes != NULL);
g_assert_cmpint (g_strv_length (nodes), ==, 5);
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "vp0"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "vp1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "evp0"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "evp1"));
/* check that /foo/boss/executives/non_subtree_object is not handled by the
* subtree handlers - we can do this because objects from subtree handlers
* has exactly one interface and non_subtree_object has two
@@ -1358,13 +1344,13 @@ test_object_registration (void)
nodes = get_nodes_at (c, "/foo/boss/executives");
g_assert (nodes != NULL);
g_assert_cmpint (g_strv_length (nodes), ==, 7);
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp2"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1"));
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp2"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "vp0"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "vp1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "vp2"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "evp0"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "evp1"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "evp2"));
g_strfreev (nodes);
/* This is to check that a bug (rather, class of bugs) in gdbusconnection.c's
@@ -1401,7 +1387,7 @@ test_object_registration (void)
nodes = get_nodes_at (c, "/foo/boss/executives");
g_assert (nodes != NULL);
g_assert_cmpint (g_strv_length (nodes), ==, 1);
- g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
g_strfreev (nodes);
g_assert (g_dbus_connection_unregister_object (c, boss_foo_reg_id));
diff --git a/gio/tests/gdbus-method-invocation.c b/gio/tests/gdbus-method-invocation.c
new file mode 100644
index 000000000..45fb67757
--- /dev/null
+++ b/gio/tests/gdbus-method-invocation.c
@@ -0,0 +1,406 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2022 Endless OS Foundation, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "gdbus-tests.h"
+
+static const GDBusArgInfo foo_get_fds_in_args =
+{
+ -1,
+ "type",
+ "s",
+ NULL
+};
+static const GDBusArgInfo * const foo_get_fds_in_arg_pointers[] = {&foo_get_fds_in_args, NULL};
+
+static const GDBusArgInfo foo_get_fds_out_args =
+{
+ -1,
+ "some_fd",
+ "h",
+ NULL
+};
+static const GDBusArgInfo * const foo_get_fds_out_arg_pointers[] = {&foo_get_fds_out_args, NULL};
+
+static const GDBusMethodInfo foo_method_info_wrong_return_type =
+{
+ -1,
+ "WrongReturnType",
+ NULL, /* in args */
+ NULL, /* out args */
+ NULL /* annotations */
+};
+static const GDBusMethodInfo foo_method_info_close_before_returning =
+{
+ -1,
+ "CloseBeforeReturning",
+ NULL, /* in args */
+ NULL, /* out args */
+ NULL /* annotations */
+};
+static const GDBusMethodInfo foo_method_info_get_fds =
+{
+ -1,
+ "GetFDs",
+ (GDBusArgInfo **) foo_get_fds_in_arg_pointers,
+ (GDBusArgInfo **) foo_get_fds_out_arg_pointers,
+ NULL /* annotations */
+};
+static const GDBusMethodInfo foo_method_info_return_error =
+{
+ -1,
+ "ReturnError",
+ NULL, /* in args */
+ NULL, /* out args */
+ NULL /* annotations */
+};
+static const GDBusMethodInfo * const foo_method_info_pointers[] = {
+ &foo_method_info_wrong_return_type,
+ &foo_method_info_close_before_returning,
+ &foo_method_info_get_fds,
+ &foo_method_info_return_error,
+ NULL
+};
+
+static const GDBusPropertyInfo foo_property_info[] =
+{
+ {
+ -1,
+ "InvalidType",
+ "s",
+ G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
+ NULL
+ },
+ {
+ -1,
+ "InvalidTypeNull",
+ "s",
+ G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
+ NULL
+ },
+ {
+ -1,
+ "InvalidValueType",
+ "s",
+ G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
+ NULL
+ },
+};
+static const GDBusPropertyInfo * const foo_property_info_pointers[] =
+{
+ &foo_property_info[0],
+ &foo_property_info[1],
+ &foo_property_info[2],
+ NULL
+};
+
+static const GDBusInterfaceInfo foo_interface_info =
+{
+ -1,
+ "org.example.Foo",
+ (GDBusMethodInfo **) &foo_method_info_pointers,
+ NULL, /* signals */
+ (GDBusPropertyInfo **) &foo_property_info_pointers,
+ NULL, /* annotations */
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_method_invocation_return_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ gboolean no_reply = g_dbus_message_get_flags (g_dbus_method_invocation_get_message (invocation)) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED;
+
+ if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
+ g_str_equal (method_name, "Get"))
+ {
+ const gchar *iface_name, *prop_name;
+
+ g_variant_get (parameters, "(&s&s)", &iface_name, &prop_name);
+ g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+
+ /* Do different things depending on the property name. */
+ if (g_str_equal (prop_name, "InvalidType"))
+ {
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Type of return value for property 'Get' call should be '(v)' but got '(s)'");
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "this type is invalid"));
+ }
+ else if (g_str_equal (prop_name, "InvalidTypeNull"))
+ {
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Type of return value for property 'Get' call should be '(v)' but got '()'");
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+ else if (g_str_equal (prop_name, "InvalidValueType"))
+ {
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Value returned from property 'Get' call for 'InvalidValueType' should be 's' but is 'u'");
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", g_variant_new_uint32 (123)));
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ g_test_assert_expected_messages ();
+ }
+ else if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
+ g_str_equal (method_name, "Set"))
+ {
+ const gchar *iface_name, *prop_name;
+ GVariant *value;
+
+ g_variant_get (parameters, "(&s&sv)", &iface_name, &prop_name, &value);
+ g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+
+ if (g_str_equal (prop_name, "InvalidType"))
+ {
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Type of return value for property 'Set' call should be '()' but got '(s)'");
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be unit"));
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ g_test_assert_expected_messages ();
+ g_variant_unref (value);
+ }
+ else if (g_str_equal (interface_name, "org.freedesktop.DBus.Properties") &&
+ g_str_equal (method_name, "GetAll"))
+ {
+ const gchar *iface_name;
+
+ g_variant_get (parameters, "(&s)", &iface_name);
+ g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Type of return value for property 'GetAll' call should be '(a{sv})' but got '(s)'");
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be a different type"));
+ }
+ else if (g_str_equal (interface_name, "org.example.Foo") &&
+ g_str_equal (method_name, "WrongReturnType"))
+ {
+ if (!no_reply)
+ g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_WARNING,
+ "Type of return value is incorrect: expected '()', got '(s)'");
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "should be a different type"));
+ }
+ else if (g_str_equal (interface_name, "org.example.Foo") &&
+ g_str_equal (method_name, "CloseBeforeReturning"))
+ {
+ g_dbus_connection_close (connection, NULL, NULL, NULL);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ }
+ else if (g_str_equal (interface_name, "org.example.Foo") &&
+ g_str_equal (method_name, "GetFDs"))
+ {
+ const gchar *action;
+ GUnixFDList *list = NULL;
+ GError *local_error = NULL;
+
+ g_variant_get (parameters, "(&s)", &action);
+
+ list = g_unix_fd_list_new ();
+ g_unix_fd_list_append (list, 1, &local_error);
+ g_assert_no_error (local_error);
+
+ if (g_str_equal (action, "WrongNumber"))
+ {
+ g_unix_fd_list_append (list, 1, &local_error);
+ g_assert_no_error (local_error);
+ }
+
+ if (g_str_equal (action, "Valid") ||
+ g_str_equal (action, "WrongNumber"))
+ g_dbus_method_invocation_return_value_with_unix_fd_list (invocation, g_variant_new ("(h)"), list);
+ else
+ g_assert_not_reached ();
+
+ g_object_unref (list);
+ }
+ else if (g_str_equal (interface_name, "org.example.Foo") &&
+ g_str_equal (method_name, "ReturnError"))
+ {
+ g_dbus_method_invocation_return_dbus_error (invocation, "org.example.Foo", "SomeError");
+ }
+ else
+ g_assert_not_reached ();
+}
+
+static void
+ensure_result_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusConnection *connection = G_DBUS_CONNECTION (source);
+ GVariant *reply;
+ guint *n_outstanding_calls = user_data;
+
+ reply = g_dbus_connection_call_finish (connection, result, NULL);
+
+ /* We don’t care what the reply is. */
+ g_clear_pointer (&reply, g_variant_unref);
+
+ g_assert_cmpint (*n_outstanding_calls, >, 0);
+ *n_outstanding_calls = *n_outstanding_calls - 1;
+}
+
+static void
+test_method_invocation_return (void)
+{
+ GDBusConnection *connection = NULL;
+ GError *local_error = NULL;
+ guint registration_id;
+ const GDBusInterfaceVTable vtable = {
+ test_method_invocation_return_method_call, NULL, NULL, { 0 }
+ };
+ guint n_outstanding_calls = 0;
+
+ g_test_summary ("Test calling g_dbus_method_invocation_return_*() in various ways");
+
+ /* Connect to the bus. */
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (connection);
+
+ /* Don’t exit the test when the server closes the connection in
+ * CloseBeforeReturning(). */
+ g_dbus_connection_set_exit_on_close (connection, FALSE);
+
+ /* Register an object which we can call methods on. */
+ registration_id = g_dbus_connection_register_object (connection,
+ "/foo",
+ (GDBusInterfaceInfo *) &foo_interface_info,
+ &vtable, NULL, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_cmpint (registration_id, !=, 0);
+
+ /* Test a variety of error cases */
+ {
+ const struct
+ {
+ const gchar *interface_name;
+ const gchar *method_name;
+ const gchar *parameters_string;
+ gboolean tests_undefined_behaviour;
+ }
+ calls[] =
+ {
+ { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidType')", TRUE },
+ { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidTypeNull')", TRUE },
+ { "org.freedesktop.DBus.Properties", "Get", "('org.example.Foo', 'InvalidValueType')", TRUE },
+ { "org.freedesktop.DBus.Properties", "Set", "('org.example.Foo', 'InvalidType', <'irrelevant'>)", TRUE },
+ { "org.freedesktop.DBus.Properties", "GetAll", "('org.example.Foo',)", TRUE },
+ { "org.example.Foo", "WrongReturnType", "()", TRUE },
+ { "org.example.Foo", "GetFDs", "('Valid',)", FALSE },
+ { "org.example.Foo", "GetFDs", "('WrongNumber',)", TRUE },
+ { "org.example.Foo", "ReturnError", "()", FALSE },
+ { "org.example.Foo", "CloseBeforeReturning", "()", FALSE },
+ };
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS (calls); i++)
+ {
+ if (calls[i].tests_undefined_behaviour && !g_test_undefined ())
+ {
+ g_test_message ("Skipping %s.%s", calls[i].interface_name, calls[i].method_name);
+ continue;
+ }
+ else
+ {
+ g_test_message ("Calling %s.%s", calls[i].interface_name, calls[i].method_name);
+ }
+
+ /* Call twice, once expecting a result and once not. Do the call which
+ * doesn’t expect a result first; message ordering should ensure that
+ * it’s completed by the time the second call completes, so we don’t
+ * have to account for it separately.
+ *
+ * That’s good, because the only way to get g_dbus_connection_call()
+ * to set %G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED is to not provide
+ * a callback function. */
+ n_outstanding_calls++;
+
+ g_dbus_connection_call (connection,
+ g_dbus_connection_get_unique_name (connection),
+ "/foo",
+ calls[i].interface_name,
+ calls[i].method_name,
+ g_variant_new_parsed (calls[i].parameters_string),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL, /* no callback */
+ NULL);
+
+ g_dbus_connection_call (connection,
+ g_dbus_connection_get_unique_name (connection),
+ "/foo",
+ calls[i].interface_name,
+ calls[i].method_name,
+ g_variant_new_parsed (calls[i].parameters_string),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ ensure_result_cb,
+ &n_outstanding_calls);
+ }
+ }
+
+ /* Wait until all the calls are complete. */
+ while (n_outstanding_calls > 0)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_dbus_connection_unregister_object (connection, registration_id);
+ g_object_unref (connection);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+
+ g_test_add_func ("/gdbus/method-invocation/return", test_method_invocation_return);
+
+ return session_bus_run ();
+}
diff --git a/gio/tests/gdbus-non-socket.c b/gio/tests/gdbus-non-socket.c
index 911aff262..7ddb55bfb 100644
--- a/gio/tests/gdbus-non-socket.c
+++ b/gio/tests/gdbus-non-socket.c
@@ -293,7 +293,7 @@ main (int argc,
{
gint ret;
- g_test_init (&argc, &argv, NULL);
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
g_test_add_func ("/gdbus/non-socket", test_non_socket);
diff --git a/gio/tests/gdbus-object-manager-example/meson.build b/gio/tests/gdbus-object-manager-example/meson.build
index f9c3bce26..ce0335e11 100644
--- a/gio/tests/gdbus-object-manager-example/meson.build
+++ b/gio/tests/gdbus-object-manager-example/meson.build
@@ -17,6 +17,22 @@ gdbus_example_objectmanager_generated = custom_target('objectmanager-gen',
'--symbol-decorator-define', 'HAVE_CONFIG_H',
'@INPUT@'])
+gdbus_example_objectmanager_rst_gen = custom_target('objectmanager-rst-gen',
+ input: gdbus_example_objectmanager_xml,
+ output: [
+ 'objectmanager-rst-gen-org.gtk.GDBus.Example.ObjectManager.Animal.rst',
+ 'objectmanager-rst-gen-org.gtk.GDBus.Example.ObjectManager.Cat.rst',
+ ],
+ command: [
+ python,
+ gdbus_codegen,
+ '--interface-prefix', 'org.gtk.GDBus.Example.ObjectManager.',
+ '--generate-rst', 'objectmanager-rst-gen',
+ '--output-directory', '@OUTDIR@',
+ '@INPUT@',
+ ],
+)
+
libgdbus_example_objectmanager = library('gdbus-example-objectmanager',
gdbus_example_objectmanager_generated,
c_args : test_c_args,
@@ -25,6 +41,9 @@ libgdbus_example_objectmanager = library('gdbus-example-objectmanager',
install_dir : installed_tests_execdir)
libgdbus_example_objectmanager_dep = declare_dependency(
- sources : gdbus_example_objectmanager_generated[0],
+ sources : [
+ gdbus_example_objectmanager_generated[0],
+ gdbus_example_objectmanager_rst_gen[0],
+ ],
link_with : libgdbus_example_objectmanager,
dependencies : [libgio_dep])
diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c
index 7e619c2ac..eed75acf4 100644
--- a/gio/tests/gdbus-proxy.c
+++ b/gio/tests/gdbus-proxy.c
@@ -701,7 +701,6 @@ test_basic (GDBusProxy *proxy)
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert_true (g_dbus_proxy_get_connection (proxy) == connection);
- g_assert_cmpint (g_dbus_proxy_get_flags (proxy), ==, G_DBUS_PROXY_FLAGS_NONE);
g_assert_null (g_dbus_proxy_get_interface_info (proxy));
g_assert_cmpstr (g_dbus_proxy_get_name (proxy), ==, "com.example.TestService");
g_assert_cmpstr (g_dbus_proxy_get_object_path (proxy), ==, "/com/example/TestObject");
@@ -720,7 +719,7 @@ test_basic (GDBusProxy *proxy)
g_assert_true (conn == connection);
g_assert_null (info);
- g_assert_cmpint (flags, ==, G_DBUS_PROXY_FLAGS_NONE);
+ g_assert_cmpint (flags, ==, g_dbus_proxy_get_flags (proxy));
g_assert_cmpstr (name, ==, "com.example.TestService");
g_assert_cmpstr (path, ==, "/com/example/TestObject");
g_assert_cmpstr (interface, ==, "com.example.Frob");
@@ -735,6 +734,16 @@ test_basic (GDBusProxy *proxy)
}
static void
+name_disappeared_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ gboolean *name_disappeared = user_data;
+ *name_disappeared = TRUE;
+ g_main_context_wakeup (NULL);
+}
+
+static void
kill_test_service (GDBusConnection *connection)
{
#ifdef G_OS_UNIX
@@ -742,6 +751,8 @@ kill_test_service (GDBusConnection *connection)
GVariant *ret;
GError *error = NULL;
const gchar *name = "com.example.TestService";
+ guint watch_id;
+ gboolean name_disappeared = FALSE;
ret = g_dbus_connection_call_sync (connection,
"org.freedesktop.DBus",
@@ -756,14 +767,25 @@ kill_test_service (GDBusConnection *connection)
&error);
g_variant_get (ret, "(u)", &pid);
g_variant_unref (ret);
+
+ /* Watch the name and wait until it’s disappeared. */
+ watch_id = g_bus_watch_name_on_connection (connection, name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL, name_disappeared_cb,
+ &name_disappeared, NULL);
kill (pid, SIGTERM);
+
+ while (!name_disappeared)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_bus_unwatch_name (watch_id);
#else
g_warning ("Can't kill com.example.TestService");
#endif
}
static void
-test_proxy (void)
+test_proxy_with_flags (GDBusProxyFlags flags)
{
GDBusProxy *proxy;
GDBusConnection *connection;
@@ -777,7 +799,7 @@ test_proxy (void)
g_assert_no_error (error);
error = NULL;
proxy = g_dbus_proxy_new_sync (connection,
- G_DBUS_PROXY_FLAGS_NONE,
+ flags,
NULL, /* GDBusInterfaceInfo */
"com.example.TestService", /* name */
"/com/example/TestObject", /* object path */
@@ -799,8 +821,6 @@ test_proxy (void)
kill_test_service (connection);
- _g_assert_property_notify (proxy, "g-name-owner");
-
owner = g_dbus_proxy_get_name_owner (proxy);
g_assert_null (owner);
g_free (owner);
@@ -809,6 +829,12 @@ test_proxy (void)
g_object_unref (connection);
}
+static void
+test_proxy (void)
+{
+ test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NONE);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
static void
@@ -930,6 +956,58 @@ test_wellknown_noauto (void)
g_source_remove (id);
}
+typedef enum {
+ ADD_MATCH,
+ REMOVE_MATCH,
+} AddOrRemove;
+
+static void
+add_or_remove_match_rule (GDBusConnection *connection,
+ AddOrRemove add_or_remove,
+ GVariant *match_rule)
+{
+ GDBusMessage *message = NULL;
+ GError *error = NULL;
+
+ message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
+ "/org/freedesktop/DBus", /* path */
+ "org.freedesktop.DBus", /* interface */
+ (add_or_remove == ADD_MATCH) ? "AddMatch" : "RemoveMatch");
+ g_dbus_message_set_body (message, match_rule);
+ g_dbus_connection_send_message (connection,
+ message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_clear_object (&message);
+}
+
+static void
+test_proxy_no_match_rule (void)
+{
+ GDBusConnection *connection = NULL;
+ GVariant *match_rule = NULL;
+
+ g_test_summary ("Test that G_DBUS_PROXY_FLAGS_NO_MATCH_RULE works");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1109");
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ /* Add a custom match rule which matches everything. */
+ match_rule = g_variant_ref_sink (g_variant_new ("(s)", "type='signal'"));
+ add_or_remove_match_rule (connection, ADD_MATCH, match_rule);
+
+ /* Run the tests. */
+ test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NO_MATCH_RULE);
+
+ /* Remove the match rule again. */
+ add_or_remove_match_rule (connection, REMOVE_MATCH, match_rule);
+
+ g_clear_pointer (&match_rule, g_variant_unref);
+ g_clear_object (&connection);
+}
+
int
main (int argc,
char *argv[])
@@ -950,6 +1028,7 @@ main (int argc,
g_test_add_func ("/gdbus/proxy/no-properties", test_no_properties);
g_test_add_func ("/gdbus/proxy/wellknown-noauto", test_wellknown_noauto);
g_test_add_func ("/gdbus/proxy/async", test_async);
+ g_test_add_func ("/gdbus/proxy/no-match-rule", test_proxy_no_match_rule);
ret = session_bus_run();
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
index 0681e7ec8..22628c078 100644
--- a/gio/tests/gdbus-test-codegen.c
+++ b/gio/tests/gdbus-test-codegen.c
@@ -23,8 +23,6 @@
#include <string.h>
#include <stdio.h>
-#include "glib/glib-private.h"
-
#include "gdbus-tests.h"
#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
@@ -869,7 +867,7 @@ check_bar_proxy (FooiGenBar *proxy,
"s", "a string",
"o", "/a/path",
"g", "asig",
- "ay", g_variant_new_parsed ("[byte 0x65, 0x67]"),
+ "ay", "eg",
"as", array_of_strings,
"ao", array_of_objpaths,
"ag", g_variant_new_parsed ("[@g 'ass', 'git']"),
@@ -1304,18 +1302,6 @@ static gpointer
check_proxies_in_thread (gpointer user_data)
{
GMainLoop *loop = user_data;
-#ifdef _GLIB_ADDRESS_SANITIZER
-
- /* Silence "Not available before 2.38" when using old API */
- G_GNUC_BEGIN_IGNORE_DEPRECATIONS
- g_test_incomplete ("FIXME: Leaks a GWeakRef, see glib#2312");
- G_GNUC_END_IGNORE_DEPRECATIONS
-
- (void) check_thread_proxies;
- (void) check_authorize_proxy;
- (void) check_bat_proxy;
- (void) check_bar_proxy;
-#else
GMainContext *thread_context;
GMainLoop *thread_loop;
GError *error;
@@ -1382,9 +1368,14 @@ check_proxies_in_thread (gpointer user_data)
g_object_unref (thread_proxy_1);
g_object_unref (thread_proxy_2);
+ /* Wait for the proxy signals to all be unsubscribed. */
+ while (g_main_context_iteration (thread_context, FALSE))
+ {
+ /* Nothing needs to be done here */
+ }
+
g_main_loop_unref (thread_loop);
g_main_context_unref (thread_context);
-#endif
/* this breaks out of the loop in main() (below) */
g_main_loop_quit (loop);
diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c
index 4519fda91..23dc1fde3 100644
--- a/gio/tests/gdbus-threading.c
+++ b/gio/tests/gdbus-threading.c
@@ -48,8 +48,9 @@ timeout_cb (gpointer user_data)
* unrefs complete first. This is typically used on the shared connection, to
* ensure it’s in a correct state before beginning the next test. */
static void
-assert_connection_has_one_ref (GDBusConnection *connection,
- GMainContext *context)
+(assert_connection_has_one_ref) (GDBusConnection *connection,
+ GMainContext *context,
+ const gchar *calling_function)
{
GSource *timeout_source = NULL;
TimeoutData data = { context, FALSE };
@@ -63,7 +64,8 @@ assert_connection_has_one_ref (GDBusConnection *connection,
while (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1 && !data.timed_out)
{
- g_debug ("refcount of %p is not right, sleeping", connection);
+ g_debug ("refcount of %p is not right (%u rather than 1) in %s(), sleeping",
+ connection, g_atomic_int_get (&G_OBJECT (connection)->ref_count), calling_function);
g_main_context_iteration (NULL, TRUE);
}
@@ -71,9 +73,14 @@ assert_connection_has_one_ref (GDBusConnection *connection,
g_source_unref (timeout_source);
if (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1)
- g_error ("connection %p had too many refs", connection);
+ g_error ("connection %p had too many refs (%u rather than 1) in %s()",
+ connection, g_atomic_int_get (&G_OBJECT (connection)->ref_count), calling_function);
}
+/* Macro wrapper to add in the calling function name */
+#define assert_connection_has_one_ref(connection, context) \
+ (assert_connection_has_one_ref) (connection, context, G_STRFUNC)
+
/* ---------------------------------------------------------------------------------------------------- */
/* Ensure that signal and method replies are delivered in the right thread */
/* ---------------------------------------------------------------------------------------------------- */
@@ -176,7 +183,7 @@ test_delivery_in_thread_func (gpointer _data)
/*
* Check that we never actually send a message if the GCancellable
- * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED
+ * is already cancelled - i.e. we should get G_IO_ERROR_CANCELLED
* when the actual connection is not up.
*/
ca = g_cancellable_new ();
diff --git a/gio/tests/gio-du.c b/gio/tests/gio-du.c
index 58d1797d2..253a21f70 100644
--- a/gio/tests/gio-du.c
+++ b/gio/tests/gio-du.c
@@ -79,7 +79,6 @@ main (int argc, char **argv)
#ifdef G_OS_WIN32
argv = g_win32_get_command_line ();
- argc = g_strv_length (argv);
#endif
setlocale (LC_ALL, "");
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index dad1623b7..35d958e4d 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -2365,21 +2365,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
}
static gboolean
-strv_has_string (gchar **haystack,
- const gchar *needle)
-{
- guint n;
-
- for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
- {
- if (g_strcmp0 (haystack[n], needle) == 0)
- return TRUE;
- }
- return FALSE;
-}
-
-static gboolean
-strv_set_equal (gchar **strv, ...)
+strv_set_equal (const gchar * const *strv, ...)
{
gsize count;
va_list list;
@@ -2394,7 +2380,7 @@ strv_set_equal (gchar **strv, ...)
str = va_arg (list, const gchar *);
if (str == NULL)
break;
- if (!strv_has_string (strv, str))
+ if (!g_strv_contains (strv, str))
{
res = FALSE;
break;
@@ -2422,8 +2408,8 @@ test_list_items (void)
children = g_settings_list_children (settings);
keys = g_settings_schema_list_keys (schema);
- g_assert_true (strv_set_equal (children, "basic-types", "complex-types", "localized", NULL));
- g_assert_true (strv_set_equal (keys, "greeting", "farewell", NULL));
+ g_assert_true (strv_set_equal ((const gchar * const *) children, "basic-types", "complex-types", "localized", NULL));
+ g_assert_true (strv_set_equal ((const gchar * const *) keys, "greeting", "farewell", NULL));
g_strfreev (children);
g_strfreev (keys);
@@ -2443,13 +2429,13 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
schemas = g_settings_list_schemas ();
G_GNUC_END_IGNORE_DEPRECATIONS
- g_assert_true (strv_set_equal ((gchar **)relocs,
+ g_assert_true (strv_set_equal (relocs,
"org.gtk.test.no-path",
"org.gtk.test.extends.base",
"org.gtk.test.extends.extended",
NULL));
- g_assert_true (strv_set_equal ((gchar **)schemas,
+ g_assert_true (strv_set_equal (schemas,
"org.gtk.test",
"org.gtk.test.basic-types",
"org.gtk.test.complex-types",
@@ -2658,7 +2644,7 @@ test_schema_list_keys (void)
keys = g_settings_schema_list_keys (schema);
- g_assert_true (strv_set_equal ((gchar **)keys,
+ g_assert_true (strv_set_equal ((const gchar * const *) keys,
"greeting",
"farewell",
NULL));
@@ -2969,7 +2955,7 @@ test_extended_schema (void)
settings = g_settings_new_with_path ("org.gtk.test.extends.extended", "/test/extendes/");
g_object_get (settings, "settings-schema", &schema, NULL);
keys = g_settings_schema_list_keys (schema);
- g_assert_true (strv_set_equal (keys, "int32", "string", "another-int32", NULL));
+ g_assert_true (strv_set_equal ((const gchar * const *) keys, "int32", "string", "another-int32", NULL));
g_strfreev (keys);
g_object_unref (settings);
g_settings_schema_unref (schema);
diff --git a/gio/tests/gsubprocess-testprog.c b/gio/tests/gsubprocess-testprog.c
index da3cf8d00..eee759dcd 100644
--- a/gio/tests/gsubprocess-testprog.c
+++ b/gio/tests/gsubprocess-testprog.c
@@ -5,8 +5,6 @@
#include <errno.h>
#ifdef G_OS_UNIX
#include <unistd.h>
-#include <gio/gunixinputstream.h>
-#include <gio/gunixoutputstream.h>
#else
#include <io.h>
#endif
@@ -151,6 +149,55 @@ write_to_fds (int argc, char **argv)
}
static int
+read_from_fd (int argc, char **argv)
+{
+ int fd;
+ const char expected_result[] = "Yay success!";
+ guint8 buf[sizeof (expected_result) + 1];
+ gsize bytes_read;
+ FILE *f;
+
+ if (argc != 3)
+ {
+ g_print ("Usage: %s read-from-fd FD\n", argv[0]);
+ return 1;
+ }
+
+ fd = atoi (argv[2]);
+ if (fd == 0)
+ {
+ g_warning ("Argument \"%s\" does not look like a valid nonzero file descriptor", argv[2]);
+ return 1;
+ }
+
+ f = fdopen (fd, "r");
+ if (f == NULL)
+ {
+ g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno));
+ return 1;
+ }
+
+ bytes_read = fread (buf, 1, sizeof (buf), f);
+ if (bytes_read != sizeof (expected_result))
+ {
+ g_warning ("Read %zu bytes, but expected %zu", bytes_read, sizeof (expected_result));
+ return 1;
+ }
+
+ if (memcmp (expected_result, buf, sizeof (expected_result)) != 0)
+ {
+ buf[sizeof (expected_result)] = '\0';
+ g_warning ("Expected \"%s\" but read \"%s\"", expected_result, (char *)buf);
+ return 1;
+ }
+
+ if (fclose (f) == -1)
+ g_assert_not_reached ();
+
+ return 0;
+}
+
+static int
env_mode (int argc, char **argv)
{
char **env;
@@ -242,6 +289,8 @@ main (int argc, char **argv)
return sleep_forever_mode (argc, argv);
else if (strcmp (mode, "write-to-fds") == 0)
return write_to_fds (argc, argv);
+ else if (strcmp (mode, "read-from-fd") == 0)
+ return read_from_fd (argc, argv);
else if (strcmp (mode, "env") == 0)
return env_mode (argc, argv);
else if (strcmp (mode, "cwd") == 0)
diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
index 084b77df3..fc5d4624e 100644
--- a/gio/tests/gsubprocess.c
+++ b/gio/tests/gsubprocess.c
@@ -5,6 +5,7 @@
#include <sys/wait.h>
#include <glib-unix.h>
#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
#include <gio/gfiledescriptorbased.h>
#include <unistd.h>
#include <fcntl.h>
@@ -26,6 +27,14 @@
#define SPLICELEN (TOTAL_HELLOS * strlen (HELLO_WORLD))
#endif
+
+
+#ifdef G_OS_WIN32
+#define TESTPROG "gsubprocess-testprog.exe"
+#else
+#define TESTPROG "gsubprocess-testprog"
+#endif
+
static GPtrArray *
get_test_subprocess_args (const char *mode,
...) G_GNUC_NULL_TERMINATED;
@@ -36,19 +45,12 @@ get_test_subprocess_args (const char *mode,
{
GPtrArray *ret;
char *path;
- const char *binname;
va_list args;
gpointer arg;
ret = g_ptr_array_new_with_free_func (g_free);
-#ifdef G_OS_WIN32
- binname = "gsubprocess-testprog.exe";
-#else
- binname = "gsubprocess-testprog";
-#endif
-
- path = g_test_build_filename (G_TEST_BUILT, binname, NULL);
+ path = g_test_build_filename (G_TEST_BUILT, TESTPROG, NULL);
g_ptr_array_add (ret, path);
g_ptr_array_add (ret, g_strdup (mode));
@@ -167,6 +169,31 @@ test_search_path (void)
g_object_unref (proc);
}
+
+static void
+test_search_path_from_envp (void)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ GSubprocessLauncher *launcher;
+ GSubprocess *proc;
+ const char *path;
+
+ path = g_test_get_dir (G_TEST_BUILT);
+
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_SEARCH_PATH_FROM_ENVP);
+ g_subprocess_launcher_setenv (launcher, "PATH", path, TRUE);
+
+ proc = g_subprocess_launcher_spawn (launcher, error, TESTPROG, "exit1", NULL);
+ g_assert_no_error (local_error);
+ g_object_unref (launcher);
+
+ g_subprocess_wait_check (proc, NULL, error);
+ g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1);
+ g_clear_error (error);
+
+ g_object_unref (proc);
+}
#endif
static void
@@ -1697,7 +1724,8 @@ test_child_setup (void)
}
static void
-test_pass_fd (void)
+do_test_pass_fd (GSubprocessFlags flags,
+ GSpawnChildSetupFunc child_setup)
{
GError *local_error = NULL;
GError **error = &local_error;
@@ -1722,9 +1750,11 @@ test_pass_fd (void)
needdup_fd_str = g_strdup_printf ("%d", needdup_pipefds[1] + 1);
args = get_test_subprocess_args ("write-to-fds", basic_fd_str, needdup_fd_str, NULL);
- launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+ launcher = g_subprocess_launcher_new (flags);
g_subprocess_launcher_take_fd (launcher, basic_pipefds[1], basic_pipefds[1]);
g_subprocess_launcher_take_fd (launcher, needdup_pipefds[1], needdup_pipefds[1] + 1);
+ if (child_setup != NULL)
+ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL);
proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
g_ptr_array_free (args, TRUE);
g_assert_no_error (local_error);
@@ -1754,6 +1784,206 @@ test_pass_fd (void)
g_object_unref (proc);
}
+static void
+test_pass_fd (void)
+{
+ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, NULL);
+}
+
+static void
+empty_child_setup (gpointer user_data)
+{
+}
+
+static void
+test_pass_fd_empty_child_setup (void)
+{
+ /* Using a child setup function forces gspawn to use fork/exec
+ * rather than posix_spawn.
+ */
+ do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, empty_child_setup);
+}
+
+static void
+test_pass_fd_inherit_fds (void)
+{
+ /* Try to test the optimized posix_spawn codepath instead of
+ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's
+ * posix_spawn codepath does not currently handle closing
+ * non-inherited fds. Note that using INHERIT_FDS means our testing of
+ * g_subprocess_launcher_take_fd() is less-comprehensive than when
+ * using G_SUBPROCESS_FLAGS_NONE.
+ */
+ do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL);
+}
+
+static void
+do_test_fd_conflation (GSubprocessFlags flags,
+ GSpawnChildSetupFunc child_setup,
+ gboolean test_child_err_report_fd)
+{
+ char success_message[] = "Yay success!";
+ GError *error = NULL;
+ GOutputStream *output_stream;
+ GSubprocessLauncher *launcher;
+ GSubprocess *proc;
+ GPtrArray *args;
+ int unused_pipefds[2];
+ int pipefds[2];
+ int fd_to_pass_to_child;
+ gsize bytes_written;
+ gboolean success;
+ char *fd_str;
+
+ /* This test must run in a new process because it is extremely sensitive to
+ * order of opened fds.
+ */
+ if (!g_test_subprocess ())
+ {
+ g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR);
+ g_test_trap_assert_passed ();
+ return;
+ }
+
+ g_unix_open_pipe (unused_pipefds, FD_CLOEXEC, &error);
+ g_assert_no_error (error);
+
+ g_unix_open_pipe (pipefds, FD_CLOEXEC, &error);
+ g_assert_no_error (error);
+
+ /* The fds should be sequential since we are in a new process. */
+ g_assert_cmpint (unused_pipefds[0] /* 3 */, ==, unused_pipefds[1] - 1);
+ g_assert_cmpint (unused_pipefds[1] /* 4 */, ==, pipefds[0] - 1);
+ g_assert_cmpint (pipefds[0] /* 5 */, ==, pipefds[1] /* 6 */ - 1);
+
+ /* Because GSubprocess allows arbitrary remapping of fds, it has to be careful
+ * to avoid fd conflation issues, e.g. it should properly handle 5 -> 4 and
+ * 4 -> 5 at the same time. GIO previously attempted to handle this by naively
+ * dup'ing the source fds, but this was not good enough because it was
+ * possible that the dup'ed result could still conflict with one of the target
+ * fds. For example:
+ *
+ * source_fd 5 -> target_fd 9, source_fd 3 -> target_fd 7
+ *
+ * dup(5) -> dup returns 8
+ * dup(3) -> dup returns 9
+ *
+ * After dup'ing, we wind up with: 8 -> 9, 9 -> 7. That means that after we
+ * dup2(8, 9), we have clobbered fd 9 before we dup2(9, 7). The end result is
+ * we have remapped 5 -> 9 as expected, but then remapped 5 -> 7 instead of
+ * 3 -> 7 as the application intended.
+ *
+ * This issue has been fixed in the simplest way possible, by passing a
+ * minimum fd value when using F_DUPFD_CLOEXEC that is higher than any of the
+ * target fds, to guarantee all source fds are different than all target fds,
+ * eliminating any possibility of conflation.
+ *
+ * Anyway, that is why we have the unused_pipefds here. We need to open fds in
+ * a certain order in order to trick older GSubprocess into conflating the
+ * fds. The primary goal of this test is to ensure this particular conflation
+ * issue is not reintroduced. See glib#2503.
+ *
+ * This test also has an alternate mode of operation where it instead tests
+ * for conflation with gspawn's child_err_report_fd, glib#2506.
+ *
+ * Be aware this test is necessarily extremely fragile. To reproduce these
+ * bugs, it relies on internals of gspawn and gmain that will likely change
+ * in the future, eventually causing this test to no longer test the bugs
+ * it was originally designed to test. That is OK! If the test fails, at
+ * least you know *something* is wrong.
+ */
+ if (test_child_err_report_fd)
+ fd_to_pass_to_child = pipefds[1] + 2 /* 8 */;
+ else
+ fd_to_pass_to_child = pipefds[1] + 3 /* 9 */;
+
+ launcher = g_subprocess_launcher_new (flags);
+ g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, fd_to_pass_to_child);
+ g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */);
+ if (child_setup != NULL)
+ g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL);
+ fd_str = g_strdup_printf ("%d", fd_to_pass_to_child);
+ args = get_test_subprocess_args ("read-from-fd", fd_str, NULL);
+ proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (proc);
+ g_ptr_array_free (args, TRUE);
+ g_object_unref (launcher);
+ g_free (fd_str);
+
+ /* Close the read ends of the pipes. */
+ close (unused_pipefds[0]);
+ close (pipefds[0]);
+
+ /* Also close the write end of the unused pipe. */
+ close (unused_pipefds[1]);
+
+ /* If doing our normal test:
+ *
+ * So now pipefds[0] should be inherited into the subprocess as
+ * pipefds[1] + 2, and unused_pipefds[0] should be inherited as
+ * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify
+ * that it reads the expected data. But older broken GIO will accidentally
+ * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess
+ * to hang trying to read from the wrong pipe.
+ *
+ * If testing conflation with child_err_report_fd:
+ *
+ * We are actually already done. The real test succeeded if we made it this
+ * far without hanging while spawning the child. But let's continue with our
+ * write and read anyway, to ensure things are good.
+ */
+ output_stream = g_unix_output_stream_new (pipefds[1], TRUE);
+ success = g_output_stream_write_all (output_stream,
+ success_message, sizeof (success_message),
+ &bytes_written,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (bytes_written, ==, sizeof (success_message));
+ g_assert_true (success);
+ g_object_unref (output_stream);
+
+ success = g_subprocess_wait_check (proc, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (proc);
+}
+
+static void
+test_fd_conflation (void)
+{
+ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL, FALSE);
+}
+
+static void
+test_fd_conflation_empty_child_setup (void)
+{
+ /* Using a child setup function forces gspawn to use fork/exec
+ * rather than posix_spawn.
+ */
+ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, FALSE);
+}
+
+static void
+test_fd_conflation_inherit_fds (void)
+{
+ /* Try to test the optimized posix_spawn codepath instead of
+ * fork/exec. Currently this requires using INHERIT_FDS since gspawn's
+ * posix_spawn codepath does not currently handle closing
+ * non-inherited fds.
+ */
+ do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL, FALSE);
+}
+
+static void
+test_fd_conflation_child_err_report_fd (void)
+{
+ /* Using a child setup function forces gspawn to use fork/exec
+ * rather than posix_spawn.
+ */
+ do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, TRUE);
+}
+
#endif
static void
@@ -1819,6 +2049,7 @@ main (int argc, char **argv)
g_test_add_func ("/gsubprocess/noop-stdin-inherit", test_noop_stdin_inherit);
#ifdef G_OS_UNIX
g_test_add_func ("/gsubprocess/search-path", test_search_path);
+ g_test_add_func ("/gsubprocess/search-path-from-envp", test_search_path_from_envp);
g_test_add_func ("/gsubprocess/signal", test_signal);
#endif
g_test_add_func ("/gsubprocess/exit1", test_exit1);
@@ -1890,7 +2121,13 @@ main (int argc, char **argv)
g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file);
g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd);
g_test_add_func ("/gsubprocess/child-setup", test_child_setup);
- g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd);
+ g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd);
+ g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup);
+ g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds);
+ g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation);
+ g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup);
+ g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds);
+ g_test_add_func ("/gsubprocess/fd-conflation/child-err-report-fd", test_fd_conflation_child_err_report_fd);
#endif
g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment);
diff --git a/gio/tests/memory-monitor.c b/gio/tests/memory-monitor.c
index 06eabefa2..bef6d0f2e 100644
--- a/gio/tests/memory-monitor.c
+++ b/gio/tests/memory-monitor.c
@@ -18,21 +18,6 @@
#include <gio/gio.h>
-static const char *
-get_level_string (GMemoryMonitorWarningLevel level)
-{
- GEnumClass *eclass;
- GEnumValue *value;
-
- eclass = G_ENUM_CLASS (g_type_class_peek (G_TYPE_MEMORY_MONITOR_WARNING_LEVEL));
- value = g_enum_get_value (eclass, level);
-
- if (value == NULL)
- return "unknown";
-
- return value->value_nick;
-}
-
static void
test_dup_default (void)
{
@@ -47,10 +32,9 @@ static void
warning_cb (GMemoryMonitor *m,
GMemoryMonitorWarningLevel level)
{
- const char *str;
-
- str = get_level_string (level);
- g_debug ("Warning level: %s (%d)", str , level);
+ char *str = g_enum_to_string (G_TYPE_MEMORY_MONITOR_WARNING_LEVEL, level);
+ g_message ("Warning level: %s (%d)", str , level);
+ g_free (str);
}
static void
diff --git a/gio/tests/memory-output-stream.c b/gio/tests/memory-output-stream.c
index b448516df..ec1644ed8 100644
--- a/gio/tests/memory-output-stream.c
+++ b/gio/tests/memory-output-stream.c
@@ -300,6 +300,25 @@ test_write_bytes (void)
g_bytes_unref (bytes2);
}
+static void
+test_write_null (void)
+{
+ GOutputStream *mo;
+ GError *error = NULL;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2471");
+
+ mo = g_memory_output_stream_new_resizable ();
+ g_output_stream_write_all (mo, NULL, 0, NULL, NULL, &error);
+ g_assert_no_error (error);
+
+ g_assert_cmpint (0, ==, g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)));
+
+ g_output_stream_close (mo, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (mo);
+}
+
/* Test that writev() works on #GMemoryOutputStream with a non-empty set of vectors. This
* covers the default writev() implementation around write(). */
static void
@@ -437,6 +456,7 @@ main (int argc,
g_test_add_func ("/memory-output-stream/get-data-size", test_data_size);
g_test_add_func ("/memory-output-stream/properties", test_properties);
g_test_add_func ("/memory-output-stream/write-bytes", test_write_bytes);
+ g_test_add_func ("/memory-output-stream/write-null", test_write_null);
g_test_add_func ("/memory-output-stream/writev", test_writev);
g_test_add_func ("/memory-output-stream/writev_nonblocking", test_writev_nonblocking);
g_test_add_func ("/memory-output-stream/steal_as_bytes", test_steal_as_bytes);
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index b563e8dde..3ed23a5f2 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -42,25 +42,42 @@ giotypefuncs_inc = custom_target(
# Test programs buildable on all platforms
gio_tests = {
- 'appmonitor' : {},
+ 'appmonitor' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
'async-close-output-stream' : {},
'async-splice-output-stream' : {},
'buffered-input-stream' : {},
'buffered-output-stream' : {},
'cancellable' : {},
'contexts' : {},
- 'contenttype' : {},
+ 'contenttype' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392 / https://gitlab.gnome.org/GNOME/glib/-/issues/1251
+ 'should_fail' : host_system == 'darwin',
+ },
'converter-stream' : {},
'credentials' : {},
+ 'cxx' : {
+ 'source' : ['cxx.cpp'],
+ },
'data-input-stream' : {},
'data-output-stream' : {},
- 'defaultvalue' : {'extra_sources' : [giotypefuncs_inc]},
'fileattributematcher' : {},
'filter-streams' : {},
- 'giomodule' : {},
- 'gsubprocess' : {},
+ 'giomodule' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
+ 'gsubprocess' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
'g-file' : {},
- 'g-file-info' : {},
+ 'g-file-info' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
'g-icon' : {},
'gdbus-addresses' : {},
'gdbus-message' : {},
@@ -81,7 +98,10 @@ gio_tests = {
'simple-async-result' : {},
'simple-proxy' : {},
'sleepy-stream' : {},
- 'socket' : {},
+ 'socket' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
'socket-listener' : {},
'socket-service' : {},
'srvtarget' : {},
@@ -95,7 +115,10 @@ gio_tests = {
'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']},
'tls-database' : {'extra_sources' : ['gtesttlsbackend.c']},
'tls-bindings' : {'extra_sources' : ['gtesttlsbackend.c']},
- 'gdbus-address-get-session' : {},
+ 'gdbus-address-get-session' : {
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
+ },
'win32-appinfo' : {},
}
@@ -148,16 +171,27 @@ else
}
endif
+have_dbus_daemon = find_program('dbus-daemon', required : false).found()
+if have_dbus_daemon
+ gio_tests += {
+ 'debugcontroller' : {},
+ 'defaultvalue' : {'extra_sources' : [giotypefuncs_inc]},
+ }
+endif
+
# Test programs buildable on UNIX only
if host_machine.system() != 'windows'
gio_tests += {
'file' : {},
'gdbus-peer' : {
'dependencies' : [libgdbus_example_objectmanager_dep],
- 'install_rpath' : installed_tests_execdir
+ 'install_rpath' : installed_tests_execdir,
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
},
'gdbus-peer-object-manager' : {},
'live-g-file' : {},
+ 'resolver-parsing' : {'dependencies' : [network_libs]},
'socket-address' : {},
'stream-rw_all' : {},
'unix-fd' : {},
@@ -219,8 +253,6 @@ if host_machine.system() != 'windows'
}
endif
- # Test programs that need to bring up a session bus (requires dbus-daemon)
- have_dbus_daemon = find_program('dbus-daemon', required : false).found()
if have_dbus_daemon
annotate_args = [
'--annotate', 'org.project.Bar', 'Key1', 'Value1',
@@ -306,6 +338,7 @@ if host_machine.system() != 'windows'
'suite' : ['slow'],
},
'gdbus-introspection' : {'extra_sources' : extra_sources},
+ 'gdbus-method-invocation' : {'extra_sources' : extra_sources},
'gdbus-names' : {'extra_sources' : extra_sources},
'gdbus-proxy' : {'extra_sources' : extra_sources},
'gdbus-proxy-threads' : {
@@ -572,12 +605,16 @@ endif
if not meson.is_cross_build() or meson.has_exe_wrapper()
+ compiler_type = '--compiler=@0@'.format(cc.get_id())
+
plugin_resources_c = custom_target('plugin-resources.c',
input : 'test4.gresource.xml',
output : 'plugin-resources.c',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
+ '--internal',
'--generate-source',
'--c-name', '_g_plugin',
'@INPUT@'])
@@ -600,9 +637,11 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : 'test.gresource.xml',
output : 'test.gresource',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--sourcedir=' + meson.current_build_dir(),
+ '--internal',
'@INPUT@'],
install_dir : installed_tests_execdir,
install : installed_tests_enabled)
@@ -611,8 +650,10 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : 'test3.gresource.xml',
output : 'test_resources2.c',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
+ '--internal',
'--generate',
'--c-name', '_g_test2',
'--manual-register',
@@ -622,8 +663,10 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : 'test3.gresource.xml',
output : 'test_resources2.h',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
+ '--internal',
'--generate',
'--c-name', '_g_test2',
'--manual-register',
@@ -634,9 +677,11 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
depends : big_test_resource,
output : 'test_resources.c',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--sourcedir=' + meson.current_build_dir(),
+ '--internal',
'--generate-source',
'--c-name', '_g_test1',
'@INPUT@'])
@@ -645,9 +690,11 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : '111_digit_test.gresource.xml',
output : 'digit_test_resources.c',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--sourcedir=' + meson.current_build_dir(),
+ '--internal',
'--generate-source',
'--manual-register',
'@INPUT@'])
@@ -656,8 +703,10 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : '111_digit_test.gresource.xml',
output : 'digit_test_resources.h',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
+ '--internal',
'--generate',
'--manual-register',
'@INPUT@'])
@@ -700,9 +749,11 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : 'test5.gresource.xml',
output : 'test5.gresource',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--sourcedir=' + meson.current_build_dir(),
+ '--internal',
'@INPUT@'],
install_dir : installed_tests_execdir,
install : installed_tests_enabled)
@@ -712,9 +763,11 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : 'test5.gresource.xml',
output : 'test_resources_binary.c',
command : [glib_compile_resources,
+ compiler_type,
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--sourcedir=' + meson.current_build_dir(),
+ '--internal',
'--generate-source',
'--external-data',
'--c-name', '_g_binary_test1',
@@ -725,17 +778,24 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
input : test_gresource_binary,
output : 'test_resources.o',
command : [ld,
+ '-z', 'noexecstack',
'-r',
'-b','binary',
'@INPUT@',
'-o','@OUTPUT@'])
# Rename symbol to match the one in the C file
+ if cc.get_id() == 'gcc' and host_system == 'windows'
+ underscore = '_'
+ else
+ underscore = ''
+ endif
test_resources_binary2 = custom_target('test_resources2.o',
input : test_resources_binary,
output : 'test_resources2.o',
command : [objcopy,
- '--add-symbol','_g_binary_test1_resource_data=.data:0',
+ '--strip-all',
+ '--add-symbol', underscore + '_g_binary_test1_resource_data=.data:0',
'@INPUT@',
'@OUTPUT@'])
@@ -748,6 +808,8 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
gio_tests += {
'resources' : {
'extra_sources' : resources_extra_sources,
+ # FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/1392
+ 'should_fail' : host_system == 'darwin',
},
}
endif
@@ -801,6 +863,7 @@ foreach test_name, extra_args : gio_tests
suite : suite,
is_parallel : extra_args.get('is_parallel', true),
depends : extra_args.get('depends', []),
+ should_fail : extra_args.get('should_fail', false),
)
endforeach
diff --git a/gio/tests/mock-resolver.c b/gio/tests/mock-resolver.c
index 3a7532134..397be2792 100644
--- a/gio/tests/mock-resolver.c
+++ b/gio/tests/mock-resolver.c
@@ -87,18 +87,50 @@ mock_resolver_set_ipv6_error (MockResolver *self, GError *error)
self->ipv6_error = g_error_copy (error);
}
+static gboolean lookup_by_name_cb (gpointer user_data);
+
+/* Core of the implementation of `lookup_by_name()` in the mock resolver.
+ *
+ * It creates a #GSource which will become ready with the resolver results. It
+ * will become ready either after a timeout, or as an idle callback. This
+ * simulates doing some actual network-based resolution work.
+ *
+ * A previous implementation of this did the work in a thread, but that made it
+ * hard to synchronise the timeouts with the #GResolver failure timeouts in the
+ * calling thread, as spawning a worker thread could be subject to non-trivial
+ * delays. */
static void
-do_lookup_by_name (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+do_lookup_by_name (MockResolver *self,
+ GTask *task,
+ GResolverNameLookupFlags flags)
{
- MockResolver *self = source_object;
- GResolverNameLookupFlags flags = GPOINTER_TO_UINT(task_data);
+ GSource *source = NULL;
+
+ g_task_set_task_data (task, GINT_TO_POINTER (flags), NULL);
+
+ if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
+ source = g_timeout_source_new (self->ipv4_delay_ms);
+ else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
+ source = g_timeout_source_new (self->ipv6_delay_ms);
+ else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
+ source = g_idle_source_new ();
+ else
+ g_assert_not_reached ();
+
+ g_source_set_callback (source, lookup_by_name_cb, g_object_ref (task), g_object_unref);
+ g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+}
+
+static gboolean
+lookup_by_name_cb (gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ MockResolver *self = g_task_get_source_object (task);
+ GResolverNameLookupFlags flags = GPOINTER_TO_INT (g_task_get_task_data (task));
if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
{
- g_usleep (self->ipv4_delay_ms * 1000);
if (self->ipv4_error)
g_task_return_error (task, g_error_copy (self->ipv4_error));
else
@@ -106,7 +138,6 @@ do_lookup_by_name (GTask *task,
}
else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
{
- g_usleep (self->ipv6_delay_ms * 1000);
if (self->ipv6_error)
g_task_return_error (task, g_error_copy (self->ipv6_error));
else
@@ -120,6 +151,8 @@ do_lookup_by_name (GTask *task,
}
else
g_assert_not_reached ();
+
+ return G_SOURCE_REMOVE;
}
static void
@@ -130,27 +163,65 @@ lookup_by_name_with_flags_async (GResolver *resolver,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GTask *task = g_task_new (resolver, cancellable, callback, user_data);
- g_task_set_task_data (task, GUINT_TO_POINTER(flags), NULL);
- g_task_run_in_thread (task, do_lookup_by_name);
+ MockResolver *self = MOCK_RESOLVER (resolver);
+ GTask *task = NULL;
+
+ task = g_task_new (resolver, cancellable, callback, user_data);
+ g_task_set_source_tag (task, lookup_by_name_with_flags_async);
+
+ do_lookup_by_name (self, task, flags);
+
g_object_unref (task);
}
+static void
+async_result_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAsyncResult **result_out = user_data;
+
+ g_assert (*result_out == NULL);
+ *result_out = g_object_ref (result);
+
+ g_main_context_wakeup (g_main_context_get_thread_default ());
+}
+
static GList *
-lookup_by_name (GResolver *resolver,
- const gchar *hostname,
- GCancellable *cancellable,
+lookup_by_name (GResolver *resolver,
+ const gchar *hostname,
+ GCancellable *cancellable,
GError **error)
{
+ MockResolver *self = MOCK_RESOLVER (resolver);
+ GMainContext *context = NULL;
GList *result = NULL;
- GTask *task = g_task_new (resolver, cancellable, NULL, NULL);
- g_task_set_task_data (task, GUINT_TO_POINTER (G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT), NULL);
- g_task_run_in_thread_sync (task, do_lookup_by_name);
- result = g_task_propagate_pointer (task, error);
- g_object_unref (task);
- return result;
-}
+ GAsyncResult *async_result = NULL;
+ GTask *task = NULL;
+ context = g_main_context_new ();
+ g_main_context_push_thread_default (context);
+
+ task = g_task_new (resolver, cancellable, async_result_cb, &async_result);
+ g_task_set_source_tag (task, lookup_by_name);
+
+ /* Set up the resolution job. */
+ do_lookup_by_name (self, task, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT);
+
+ /* Wait for it to complete synchronously. */
+ while (async_result == NULL)
+ g_main_context_iteration (context, TRUE);
+
+ result = g_task_propagate_pointer (G_TASK (async_result), error);
+ g_object_unref (async_result);
+
+ g_assert_finalize_object (task);
+
+ g_main_context_pop_thread_default (context);
+ g_main_context_unref (context);
+
+ return g_steal_pointer (&result);
+}
static GList *
lookup_by_name_with_flags_finish (GResolver *resolver,
diff --git a/gio/tests/modules/symbol-visibility.h b/gio/tests/modules/symbol-visibility.h
index f9f8826ce..e83894444 100644
--- a/gio/tests/modules/symbol-visibility.h
+++ b/gio/tests/modules/symbol-visibility.h
@@ -3,11 +3,15 @@
/* This is the same check that's done in configure to create config.h */
#ifdef _WIN32
-# ifdef _MSC_VER
-# define GLIB_TEST_EXPORT_SYMBOL __declspec(dllexport) extern
-# else
-# define GLIB_TEST_EXPORT_SYMBOL __attribute__((visibility("default"))) __declspec(dllexport) extern
-# endif
+#ifdef GLIB_STATIC_COMPILATION
+#define GLIB_TEST_EXPORT_SYMBOL extern
+#else
+#ifdef _MSC_VER
+#define GLIB_TEST_EXPORT_SYMBOL __declspec(dllexport) extern
+#else
+#define GLIB_TEST_EXPORT_SYMBOL __attribute__ ((visibility ("default"))) __declspec(dllexport) extern
+#endif
+#endif
/* Matches GCC and Clang */
#elif defined(__GNUC__) && (__GNUC__ >= 4)
# define GLIB_TEST_EXPORT_SYMBOL __attribute__((visibility("default"))) extern
diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c
index 946a8f25f..da4d7f7e0 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -20,7 +20,7 @@ test_basic (void)
g_object_get (address, "hostname", &hostname, "port", &port, "scheme", &scheme, NULL);
g_assert_cmpstr (hostname, ==, "www.gnome.org");
g_assert_cmpint (port, ==, 8080);
- g_assert (scheme == NULL);
+ g_assert_null (scheme);
g_free (hostname);
g_object_unref (address);
@@ -284,8 +284,8 @@ test_scope_id (GSocketConnectable *addr)
saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
g_assert_no_error (error);
- g_assert (saddr != NULL);
- g_assert (G_IS_INET_SOCKET_ADDRESS (saddr));
+ g_assert_nonnull (saddr);
+ g_assert_true (G_IS_INET_SOCKET_ADDRESS (saddr));
isaddr = G_INET_SOCKET_ADDRESS (saddr);
g_assert_cmpint (g_inet_socket_address_get_scope_id (isaddr), ==, SCOPE_ID_TEST_INDEX);
@@ -299,7 +299,7 @@ test_scope_id (GSocketConnectable *addr)
g_object_unref (saddr);
saddr = g_socket_address_enumerator_next (addr_enum, NULL, &error);
g_assert_no_error (error);
- g_assert (saddr == NULL);
+ g_assert_null (saddr);
g_object_unref (addr_enum);
#else
@@ -377,7 +377,7 @@ assert_socket_address_matches (GSocketAddress *a,
GInetSocketAddress *sa;
gchar *str; /* owned */
- g_assert (G_IS_INET_SOCKET_ADDRESS (a));
+ g_assert_true (G_IS_INET_SOCKET_ADDRESS (a));
sa = G_INET_SOCKET_ADDRESS (a);
g_assert_cmpint (g_inet_socket_address_get_port (sa), ==, expected_port);
@@ -593,7 +593,7 @@ got_addr (GObject *source_object,
}
else
{
- g_assert (G_IS_INET_SOCKET_ADDRESS (a));
+ g_assert_true (G_IS_INET_SOCKET_ADDRESS (a));
data->addrs = g_list_prepend (data->addrs, a);
if (!data->delay_ms)
@@ -768,16 +768,22 @@ sort_socket_addresses (gconstpointer a, gconstpointer b)
static void
assert_list_matches_expected (GList *result, GList *expected)
{
+ GList *result_copy = NULL;
+
g_assert_cmpint (g_list_length (result), ==, g_list_length (expected));
- /* Sort by ipv4 first which matches the expected list */
- result = g_list_sort (result, sort_socket_addresses);
+ /* Sort by ipv4 first which matches the expected list. Do this on a copy of
+ * @result to avoid modifying the original. */
+ result_copy = g_list_copy (result);
+ result = result_copy = g_list_sort (result_copy, sort_socket_addresses);
for (; result != NULL; result = result->next, expected = expected->next)
{
GInetAddress *address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (result->data));
g_assert_true (g_inet_address_equal (address, expected->data));
}
+
+ g_list_free (result_copy);
}
typedef struct {
@@ -855,6 +861,8 @@ test_happy_eyeballs_basic (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -879,6 +887,7 @@ test_happy_eyeballs_parallel (HappyEyeballsFixture *fixture,
/* Run again to ensure the cache from the previous one is correct */
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
data.addrs = NULL;
g_object_unref (enumerator2);
@@ -887,7 +896,9 @@ test_happy_eyeballs_parallel (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
g_object_unref (enumerator2);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -905,6 +916,8 @@ test_happy_eyeballs_slow_ipv4 (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -922,6 +935,8 @@ test_happy_eyeballs_slow_ipv6 (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -939,6 +954,8 @@ test_happy_eyeballs_very_slow_ipv6 (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -958,6 +975,8 @@ test_happy_eyeballs_slow_connection_and_ipv4 (HappyEyeballsFixture *fixture,
g_main_loop_run (fixture->loop);
assert_list_matches_expected (data.addrs, fixture->input_all_results);
+
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
}
static void
@@ -979,6 +998,7 @@ test_happy_eyeballs_ipv6_error_ipv4_first (HappyEyeballsFixture *fixture,
assert_list_matches_expected (data.addrs, fixture->input_ipv4_results);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_error_free (ipv6_error);
}
@@ -1001,6 +1021,7 @@ test_happy_eyeballs_ipv6_error_ipv6_first (HappyEyeballsFixture *fixture,
assert_list_matches_expected (data.addrs, fixture->input_ipv4_results);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_error_free (ipv6_error);
}
@@ -1026,6 +1047,7 @@ test_happy_eyeballs_ipv6_error_ipv4_very_slow (HappyEyeballsFixture *fixture,
assert_list_matches_expected (data.addrs, fixture->input_ipv4_results);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_error_free (ipv6_error);
}
@@ -1048,6 +1070,7 @@ test_happy_eyeballs_ipv4_error_ipv4_first (HappyEyeballsFixture *fixture,
assert_list_matches_expected (data.addrs, fixture->input_ipv6_results);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_error_free (ipv4_error);
}
@@ -1070,6 +1093,7 @@ test_happy_eyeballs_ipv4_error_ipv6_first (HappyEyeballsFixture *fixture,
assert_list_matches_expected (data.addrs, fixture->input_ipv6_results);
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
g_error_free (ipv4_error);
}
diff --git a/gio/tests/proxy-test.c b/gio/tests/proxy-test.c
index eec4bf7ca..d4b71a432 100644
--- a/gio/tests/proxy-test.c
+++ b/gio/tests/proxy-test.c
@@ -41,9 +41,14 @@
* connects to @server_addr anyway).
*
* The default GProxyResolver (GTestProxyResolver) looks at its URI
- * and returns [ "direct://" ] for "simple://" URIs, and [
- * proxy_a.uri, proxy_b.uri ] for other URIs. The other GProxyResolver
- * (GTestAltProxyResolver) always returns [ proxy_a.uri ].
+ * and returns [ "direct://" ] for "simple://" URIs, and
+ * [ proxy_a.uri, proxy_b.uri ] for most other URIs. It can also return
+ * invalid results for other URIs (empty://, invalid://,
+ * invalid-then-simple://, and simple-then-invalid://) to test error
+ * handling.
+ *
+ * The other GProxyResolver (GTestAltProxyResolver) always returns
+ * [ proxy_a.uri ].
*/
typedef struct {
@@ -134,6 +139,28 @@ g_test_proxy_resolver_lookup (GProxyResolver *resolver,
proxies[0] = g_strdup ("direct://");
proxies[1] = NULL;
}
+ else if (g_str_has_prefix (uri, "empty://"))
+ {
+ proxies[0] = g_strdup ("");
+ proxies[1] = NULL;
+ }
+ else if (g_str_has_prefix (uri, "invalid://"))
+ {
+ proxies[0] = g_strdup ("😼");
+ proxies[1] = NULL;
+ }
+ else if (g_str_has_prefix (uri, "invalid-then-simple://"))
+ {
+ proxies[0] = g_strdup ("😼");
+ proxies[1] = g_strdup ("direct://");
+ proxies[2] = NULL;
+ }
+ else if (g_str_has_prefix (uri, "simple-then-invalid://"))
+ {
+ proxies[0] = g_strdup ("direct://");
+ proxies[1] = g_strdup ("😼");
+ proxies[2] = NULL;
+ }
else
{
/* Proxy A can only deal with "alpha://" URIs, not
@@ -824,11 +851,8 @@ static void
teardown_test (gpointer fixture,
gconstpointer user_data)
{
- if (last_proxies)
- {
- g_strfreev (last_proxies);
- last_proxies = NULL;
- }
+ g_clear_pointer (&last_proxies, g_strfreev);
+
g_clear_error (&proxy_a.last_error);
g_clear_error (&proxy_b.last_error);
}
@@ -1092,6 +1116,118 @@ test_multiple_async (gpointer fixture,
}
static void
+test_invalid_uris_sync (gpointer fixture,
+ gconstpointer user_data)
+{
+ GSocketConnection *conn;
+ gchar *uri;
+ GError *error = NULL;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
+
+ /* The empty:// URI causes the proxy resolver to return an empty string. */
+ uri = g_strdup_printf ("empty://127.0.0.1:%u", server.server_port);
+ conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+ g_free (uri);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_assert_null (conn);
+ g_clear_error (&error);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* The invalid:// URI causes the proxy resolver to return a cat emoji. */
+ uri = g_strdup_printf ("invalid://127.0.0.1:%u", server.server_port);
+ conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+ g_free (uri);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_assert_null (conn);
+ g_clear_error (&error);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* If the proxy resolver returns an invalid URI before a valid URI,
+ * we should succeed.
+ */
+ uri = g_strdup_printf ("invalid-then-simple://127.0.0.1:%u", server.server_port);
+ conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+ g_free (uri);
+ g_assert_no_error (error);
+ do_echo_test (conn);
+ g_object_unref (conn);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* If the proxy resolver returns a valid URI before an invalid URI,
+ * we should succeed.
+ */
+ uri = g_strdup_printf ("simple-then-invalid://127.0.0.1:%u", server.server_port);
+ conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+ g_free (uri);
+ g_assert_no_error (error);
+ do_echo_test (conn);
+ g_object_unref (conn);
+ g_clear_pointer (&last_proxies, g_strfreev);
+}
+
+static void
+test_invalid_uris_async (gpointer fixture,
+ gconstpointer user_data)
+{
+ GSocketConnection *conn;
+ GError *error = NULL;
+ gchar *uri;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
+
+ /* The empty:// URI causes the proxy resolver to return an empty string. */
+ uri = g_strdup_printf ("empty://127.0.0.1:%u", server.server_port);
+ g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+ async_got_error, &error);
+ g_free (uri);
+ while (error == NULL)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_clear_error (&error);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* The invalid:// URI causes the proxy resolver to return a cat emoji. */
+ uri = g_strdup_printf ("invalid://127.0.0.1:%u", server.server_port);
+ g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+ async_got_error, &error);
+ g_free (uri);
+ while (error == NULL)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_clear_error (&error);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* If the proxy resolver returns an invalid URI before a valid URI,
+ * we should succeed.
+ */
+ uri = g_strdup_printf ("invalid-then-simple://127.0.0.1:%u", server.server_port);
+ conn = NULL;
+ g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+ async_got_conn, &conn);
+ g_free (uri);
+ while (conn == NULL)
+ g_main_context_iteration (NULL, TRUE);
+ do_echo_test (conn);
+ g_object_unref (conn);
+ g_clear_pointer (&last_proxies, g_strfreev);
+
+ /* If the proxy resolver returns a valid URI before an invalid URI,
+ * we should succeed.
+ */
+ uri = g_strdup_printf ("simple-then-invalid://127.0.0.1:%u", server.server_port);
+ conn = NULL;
+ g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+ async_got_conn, &conn);
+ g_free (uri);
+ while (conn == NULL)
+ g_main_context_iteration (NULL, TRUE);
+ do_echo_test (conn);
+ g_object_unref (conn);
+ g_clear_pointer (&last_proxies, g_strfreev);
+}
+
+static void
test_dns (gpointer fixture,
gconstpointer user_data)
{
@@ -1370,6 +1506,8 @@ main (int argc,
g_test_add_vtable ("/proxy/single_async", 0, NULL, setup_test, test_single_async, teardown_test);
g_test_add_vtable ("/proxy/multiple_sync", 0, NULL, setup_test, test_multiple_sync, teardown_test);
g_test_add_vtable ("/proxy/multiple_async", 0, NULL, setup_test, test_multiple_async, teardown_test);
+ g_test_add_vtable ("/proxy/invalid-uris-sync", 0, NULL, setup_test, test_invalid_uris_sync, teardown_test);
+ g_test_add_vtable ("/proxy/invalid-uris-async", 0, NULL, setup_test, test_invalid_uris_async, teardown_test);
g_test_add_vtable ("/proxy/dns", 0, NULL, setup_test, test_dns, teardown_test);
g_test_add_vtable ("/proxy/override", 0, NULL, setup_test, test_override, teardown_test);
g_test_add_func ("/proxy/enumerator-ports", test_proxy_enumerator_ports);
diff --git a/gio/tests/resolver-parsing.c b/gio/tests/resolver-parsing.c
new file mode 100644
index 000000000..d9cf05244
--- /dev/null
+++ b/gio/tests/resolver-parsing.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (c) 2021 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Patrick Griffis <pgriffis@igalia.com>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <gio/gnetworking.h>
+
+#define GIO_COMPILATION
+#include "gthreadedresolver.h"
+#undef GIO_COMPILATION
+
+#ifdef HAVE_DN_COMP
+static void
+dns_builder_add_uint8 (GByteArray *builder,
+ guint8 value)
+{
+ g_byte_array_append (builder, &value, 1);
+}
+
+static void
+dns_builder_add_uint16 (GByteArray *builder,
+ guint16 value)
+{
+ dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
+ dns_builder_add_uint8 (builder, (value) & 0xFF);
+}
+
+static void
+dns_builder_add_uint32 (GByteArray *builder,
+ guint32 value)
+{
+ dns_builder_add_uint8 (builder, (value >> 24) & 0xFF);
+ dns_builder_add_uint8 (builder, (value >> 16) & 0xFF);
+ dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
+ dns_builder_add_uint8 (builder, (value) & 0xFF);
+}
+
+static void
+dns_builder_add_length_prefixed_string (GByteArray *builder,
+ const char *string)
+{
+ guint8 length;
+
+ g_assert (strlen (string) <= G_MAXUINT8);
+
+ length = (guint8) strlen (string);
+ dns_builder_add_uint8 (builder, length);
+
+ /* Don't include trailing NUL */
+ g_byte_array_append (builder, (const guchar *)string, length);
+}
+
+static void
+dns_builder_add_domain (GByteArray *builder,
+ const char *string)
+{
+ int ret;
+ guchar buffer[256];
+
+ ret = dn_comp (string, buffer, sizeof (buffer), NULL, NULL);
+ g_assert (ret != -1);
+
+ g_byte_array_append (builder, buffer, ret);
+}
+
+/* Append an invalid domain name to the DNS response. This is implemented by
+ * appending a single label followed by a pointer back to that label. This is
+ * invalid regardless of any other context in the response as its expansion is
+ * infinite.
+ *
+ * See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
+ *
+ * In order to create a pointer to the label, the label’s final offset in the
+ * DNS response must be known. The current length of @builder, plus @offset, is
+ * used for this. Hence, @offset is the additional offset (in bytes) to add, and
+ * typically corresponds to the length of the parent #GByteArray that @builder
+ * will eventually be added to. Potentially plus 2 bytes for the rdlength, as
+ * per dns_builder_add_answer_data(). */
+static void
+dns_builder_add_invalid_domain (GByteArray *builder,
+ gsize offset)
+{
+ offset += builder->len;
+ g_assert ((offset & 0xc0) == 0);
+
+ dns_builder_add_uint8 (builder, 1);
+ dns_builder_add_uint8 (builder, 'f');
+ dns_builder_add_uint8 (builder, 0xc0 | offset);
+}
+
+static void
+dns_builder_add_answer_data (GByteArray *builder,
+ GByteArray *answer)
+{
+ dns_builder_add_uint16 (builder, answer->len); /* rdlength */
+ g_byte_array_append (builder, answer->data, answer->len);
+}
+
+static GByteArray *
+dns_header (void)
+{
+ GByteArray *answer = g_byte_array_sized_new (2046);
+
+ /* Start with a header, we ignore everything except ancount.
+ https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1 */
+ dns_builder_add_uint16 (answer, 0); /* ID */
+ dns_builder_add_uint16 (answer, 0); /* |QR| Opcode |AA|TC|RD|RA| Z | RCODE | */
+ dns_builder_add_uint16 (answer, 0); /* QDCOUNT */
+ dns_builder_add_uint16 (answer, 1); /* ANCOUNT (1 answer) */
+ dns_builder_add_uint16 (answer, 0); /* NSCOUNT */
+ dns_builder_add_uint16 (answer, 0); /* ARCOUNT */
+
+ return g_steal_pointer (&answer);
+}
+
+static void
+assert_query_fails (const gchar *rrname,
+ GResolverRecordType record_type,
+ GByteArray *answer)
+{
+ GList *records = NULL;
+ GError *local_error = NULL;
+
+ records = g_resolver_records_from_res_query (rrname,
+ g_resolver_record_type_to_rrtype (record_type),
+ answer->data,
+ answer->len,
+ 0,
+ &local_error);
+
+ g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL);
+ g_assert_null (records);
+ g_clear_error (&local_error);
+}
+
+static void
+assert_query_succeeds (const gchar *rrname,
+ GResolverRecordType record_type,
+ GByteArray *answer,
+ const gchar *expected_answer_variant_str)
+{
+ GList *records = NULL;
+ GVariant *answer_variant, *expected_answer_variant = NULL;
+ GError *local_error = NULL;
+
+ records = g_resolver_records_from_res_query (rrname,
+ g_resolver_record_type_to_rrtype (record_type),
+ answer->data,
+ answer->len,
+ 0,
+ &local_error);
+
+ g_assert_no_error (local_error);
+ g_assert_nonnull (records);
+
+ /* Test the results. */
+ answer_variant = records->data;
+ expected_answer_variant = g_variant_new_parsed (expected_answer_variant_str);
+ g_assert_cmpvariant (answer_variant, expected_answer_variant);
+
+ g_variant_unref (expected_answer_variant);
+ g_list_free_full (records, (GDestroyNotify) g_variant_unref);
+}
+#endif /* HAVE_DN_COMP */
+
+static void
+test_invalid_header (void)
+{
+ const struct
+ {
+ const guint8 *answer;
+ gsize answer_len;
+ GResolverError expected_error_code;
+ }
+ vectors[] =
+ {
+ /* No answer: */
+ { (const guint8 *) "", 0, G_RESOLVER_ERROR_NOT_FOUND },
+ /* Definitely too short to be a valid header: */
+ { (const guint8 *) "\x20", 1, G_RESOLVER_ERROR_INTERNAL },
+ /* One byte too short to be a valid header: */
+ { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 11, G_RESOLVER_ERROR_INTERNAL },
+ /* Valid header indicating no answers: */
+ { (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, G_RESOLVER_ERROR_NOT_FOUND },
+ };
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS (vectors); i++)
+ {
+ GList *records = NULL;
+ GError *local_error = NULL;
+
+ records = g_resolver_records_from_res_query ("example.org",
+ g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS),
+ vectors[i].answer,
+ vectors[i].answer_len,
+ 0,
+ &local_error);
+
+ g_assert_error (local_error, G_RESOLVER_ERROR, (gint) vectors[i].expected_error_code);
+ g_assert_null (records);
+ g_clear_error (&local_error);
+ }
+}
+
+static void
+test_unknown_record_type (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL;
+ GList *records = NULL;
+ GError *local_error = NULL;
+ const guint type_id = 20; /* ISDN, not supported anywhere */
+
+ /* An answer with an unsupported type chosen from
+ * https://en.wikipedia.org/wiki/List_of_DNS_record_types#[1]_Obsolete_record_types */
+ answer = dns_header ();
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, type_id);
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+ dns_builder_add_uint16 (answer, 0); /* rdlength */
+
+ records = g_resolver_records_from_res_query ("example.org",
+ type_id,
+ answer->data,
+ answer->len,
+ 0,
+ &local_error);
+
+ g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
+ g_assert_null (records);
+ g_clear_error (&local_error);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_mx_valid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *mx_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9 */
+ mx_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (mx_rdata, 0); /* preference */
+ dns_builder_add_domain (mx_rdata, "mail.example.org");
+ dns_builder_add_answer_data (answer, mx_rdata);
+ g_byte_array_unref (mx_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_MX, answer,
+ "(@q 0, 'mail.example.org')");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_mx_invalid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *mx_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
+ *
+ * Use an invalid domain to trigger parsing failure. */
+ mx_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (mx_rdata, 0); /* preference */
+ dns_builder_add_invalid_domain (mx_rdata, answer->len + 2);
+ dns_builder_add_answer_data (answer, mx_rdata);
+ g_byte_array_unref (mx_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_mx_invalid_too_short (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *mx_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
+ *
+ * Miss out the domain field to trigger failure */
+ mx_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (mx_rdata, 0); /* preference */
+ /* missing domain field */
+ dns_builder_add_answer_data (answer, mx_rdata);
+ g_byte_array_unref (mx_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_mx_invalid_too_short2 (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *mx_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
+ *
+ * Miss out all fields to trigger failure */
+ mx_rdata = g_byte_array_new ();
+ /* missing preference and domain fields */
+ dns_builder_add_answer_data (answer, mx_rdata);
+ g_byte_array_unref (mx_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_ns_valid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *ns_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11 */
+ ns_rdata = g_byte_array_new ();
+ dns_builder_add_domain (ns_rdata, "ns.example.org");
+ dns_builder_add_answer_data (answer, ns_rdata);
+ g_byte_array_unref (ns_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_NS, answer,
+ "('ns.example.org',)");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_ns_invalid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *ns_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11
+ *
+ * Use an invalid domain to trigger parsing failure. */
+ ns_rdata = g_byte_array_new ();
+ dns_builder_add_invalid_domain (ns_rdata, answer->len + 2);
+ dns_builder_add_answer_data (answer, ns_rdata);
+ g_byte_array_unref (ns_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_NS, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_soa_valid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *soa_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13 */
+ soa_rdata = g_byte_array_new ();
+ dns_builder_add_domain (soa_rdata, "mname.example.org");
+ dns_builder_add_domain (soa_rdata, "rname.example.org");
+ dns_builder_add_uint32 (soa_rdata, 0); /* serial */
+ dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
+ dns_builder_add_uint32 (soa_rdata, 0); /* retry */
+ dns_builder_add_uint32 (soa_rdata, 0); /* expire */
+ dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
+ dns_builder_add_answer_data (answer, soa_rdata);
+ g_byte_array_unref (soa_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SOA, answer,
+ "('mname.example.org', 'rname.example.org', @u 0, @u 0, @u 0, @u 0, @u 0)");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_soa_invalid_mname (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *soa_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
+ *
+ * Use an invalid domain to trigger parsing failure. */
+ soa_rdata = g_byte_array_new ();
+ dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* mname */
+ dns_builder_add_domain (soa_rdata, "rname.example.org");
+ dns_builder_add_uint32 (soa_rdata, 0); /* serial */
+ dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
+ dns_builder_add_uint32 (soa_rdata, 0); /* retry */
+ dns_builder_add_uint32 (soa_rdata, 0); /* expire */
+ dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
+ dns_builder_add_answer_data (answer, soa_rdata);
+ g_byte_array_unref (soa_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_soa_invalid_rname (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *soa_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
+ *
+ * Use an invalid domain to trigger parsing failure. */
+ soa_rdata = g_byte_array_new ();
+ dns_builder_add_domain (soa_rdata, "mname.example.org");
+ dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* rname */
+ dns_builder_add_uint32 (soa_rdata, 0); /* serial */
+ dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
+ dns_builder_add_uint32 (soa_rdata, 0); /* retry */
+ dns_builder_add_uint32 (soa_rdata, 0); /* expire */
+ dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
+ dns_builder_add_answer_data (answer, soa_rdata);
+ g_byte_array_unref (soa_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_soa_invalid_too_short (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *soa_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
+ *
+ * Miss out one of the fields to trigger a failure. */
+ soa_rdata = g_byte_array_new ();
+ dns_builder_add_domain (soa_rdata, "mname.example.org");
+ dns_builder_add_domain (soa_rdata, "rname.example.org");
+ dns_builder_add_uint32 (soa_rdata, 0); /* serial */
+ dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
+ dns_builder_add_uint32 (soa_rdata, 0); /* retry */
+ dns_builder_add_uint32 (soa_rdata, 0); /* expire */
+ /* missing minimum field */
+ dns_builder_add_answer_data (answer, soa_rdata);
+ g_byte_array_unref (soa_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_txt_valid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *txt_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
+ txt_rdata = g_byte_array_new ();
+ dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
+ dns_builder_add_answer_data (answer, txt_rdata);
+ g_byte_array_unref (txt_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
+ "(['some test content'],)");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_txt_valid_multiple_strings (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *txt_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
+ txt_rdata = g_byte_array_new ();
+ dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
+ dns_builder_add_length_prefixed_string (txt_rdata, "more test content");
+ dns_builder_add_answer_data (answer, txt_rdata);
+ g_byte_array_unref (txt_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
+ "(['some test content', 'more test content'],)");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_txt_invalid_empty (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *txt_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
+ *
+ * Provide zero character strings (i.e. an empty rdata section) to trigger
+ * failure. */
+ txt_rdata = g_byte_array_new ();
+ dns_builder_add_answer_data (answer, txt_rdata);
+ g_byte_array_unref (txt_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_txt_invalid_overflow (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *txt_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
+ *
+ * Use a character string whose length exceeds the remaining length in the
+ * answer record, to trigger failure. */
+ txt_rdata = g_byte_array_new ();
+ dns_builder_add_uint8 (txt_rdata, 10); /* length, but no content */
+ dns_builder_add_answer_data (answer, txt_rdata);
+ g_byte_array_unref (txt_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_srv_valid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *srv_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782 */
+ srv_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (srv_rdata, 0); /* priority */
+ dns_builder_add_uint16 (srv_rdata, 0); /* weight */
+ dns_builder_add_uint16 (srv_rdata, 0); /* port */
+ dns_builder_add_domain (srv_rdata, "target.example.org");
+ dns_builder_add_answer_data (answer, srv_rdata);
+ g_byte_array_unref (srv_rdata);
+
+ assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SRV, answer,
+ "(@q 0, @q 0, @q 0, 'target.example.org')");
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_srv_invalid (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *srv_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
+ *
+ * Use an invalid domain to trigger parsing failure. */
+ srv_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (srv_rdata, 0); /* priority */
+ dns_builder_add_uint16 (srv_rdata, 0); /* weight */
+ dns_builder_add_uint16 (srv_rdata, 0); /* port */
+ dns_builder_add_invalid_domain (srv_rdata, answer->len + 2);
+ dns_builder_add_answer_data (answer, srv_rdata);
+ g_byte_array_unref (srv_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_srv_invalid_too_short (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *srv_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
+ *
+ * Miss out the target field to trigger failure */
+ srv_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (srv_rdata, 0); /* priority */
+ dns_builder_add_uint16 (srv_rdata, 0); /* weight */
+ dns_builder_add_uint16 (srv_rdata, 0); /* port */
+ /* missing target field */
+ dns_builder_add_answer_data (answer, srv_rdata);
+ g_byte_array_unref (srv_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+static void
+test_srv_invalid_too_short2 (void)
+{
+#ifndef HAVE_DN_COMP
+ g_test_skip ("The dn_comp() function was not available.");
+ return;
+#else
+ GByteArray *answer = NULL, *srv_rdata = NULL;
+
+ answer = dns_header ();
+
+ /* Resource record */
+ dns_builder_add_domain (answer, "example.org");
+ dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
+ dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
+ dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
+
+ /* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
+ *
+ * Miss out the target and port fields to trigger failure */
+ srv_rdata = g_byte_array_new ();
+ dns_builder_add_uint16 (srv_rdata, 0); /* priority */
+ dns_builder_add_uint16 (srv_rdata, 0); /* weight */
+ /* missing port and target fields */
+ dns_builder_add_answer_data (answer, srv_rdata);
+ g_byte_array_unref (srv_rdata);
+
+ assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
+
+ g_byte_array_free (answer, TRUE);
+#endif
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+
+ g_test_add_func ("/gresolver/invalid-header", test_invalid_header);
+ g_test_add_func ("/gresolver/unknown-record-type", test_unknown_record_type);
+ g_test_add_func ("/gresolver/mx/valid", test_mx_valid);
+ g_test_add_func ("/gresolver/mx/invalid", test_mx_invalid);
+ g_test_add_func ("/gresolver/mx/invalid/too-short", test_mx_invalid_too_short);
+ g_test_add_func ("/gresolver/mx/invalid/too-short2", test_mx_invalid_too_short2);
+ g_test_add_func ("/gresolver/ns/valid", test_ns_valid);
+ g_test_add_func ("/gresolver/ns/invalid", test_ns_invalid);
+ g_test_add_func ("/gresolver/soa/valid", test_soa_valid);
+ g_test_add_func ("/gresolver/soa/invalid/mname", test_soa_invalid_mname);
+ g_test_add_func ("/gresolver/soa/invalid/rname", test_soa_invalid_rname);
+ g_test_add_func ("/gresolver/soa/invalid/too-short", test_soa_invalid_too_short);
+ g_test_add_func ("/gresolver/srv/valid", test_srv_valid);
+ g_test_add_func ("/gresolver/srv/invalid", test_srv_invalid);
+ g_test_add_func ("/gresolver/srv/invalid/too-short", test_srv_invalid_too_short);
+ g_test_add_func ("/gresolver/srv/invalid/too-short2", test_srv_invalid_too_short2);
+ g_test_add_func ("/gresolver/txt/valid", test_txt_valid);
+ g_test_add_func ("/gresolver/txt/valid/multiple-strings", test_txt_valid_multiple_strings);
+ g_test_add_func ("/gresolver/txt/invalid/empty", test_txt_invalid_empty);
+ g_test_add_func ("/gresolver/txt/invalid/overflow", test_txt_invalid_overflow);
+
+ return g_test_run ();
+}
diff --git a/gio/tests/resolver.c b/gio/tests/resolver.c
index 6e0c4d73b..d62a4fd18 100644
--- a/gio/tests/resolver.c
+++ b/gio/tests/resolver.c
@@ -44,12 +44,12 @@ static G_NORETURN void
usage (void)
{
fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
- fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA] rrname ...\n");
+ fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA|SRV] rrname ...\n");
fprintf (stderr, " resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
fprintf (stderr, " Use -s to do synchronous lookups.\n");
fprintf (stderr, " Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
fprintf (stderr, " The given NUMBER determines how many times the connectable will be enumerated.\n");
- fprintf (stderr, " Use -t with MX, TXT, NS or SOA to look up DNS records of those types.\n");
+ fprintf (stderr, " Use -t with MX, TXT, NS, SOA or SRV to look up DNS records of those types.\n");
exit (1);
}
@@ -233,6 +233,46 @@ print_resolved_txt (const char *rrname,
}
static void
+print_resolved_srv (const char *rrname,
+ GList *records,
+ GError *error)
+{
+ G_LOCK (response);
+ printf ("Domain: %s\n", rrname);
+ if (error)
+ {
+ printf ("Error: %s\n", error->message);
+ g_error_free (error);
+ }
+ else if (!records)
+ {
+ printf ("no SRV records\n");
+ }
+ else
+ {
+ GList *t;
+
+ for (t = records; t != NULL; t = t->next)
+ {
+ guint16 priority, weight, port;
+ const gchar *target;
+
+ g_variant_get (t->data, "(qqq&s)", &priority, &weight, &port, &target);
+
+ printf ("%s (priority %u, weight %u, port %u)\n",
+ target, (guint) priority, (guint) weight, (guint) port);
+ g_variant_unref (t->data);
+ }
+
+ g_list_free (records);
+ }
+ printf ("\n");
+
+ done_lookup ();
+ G_UNLOCK (response);
+}
+
+static void
print_resolved_soa (const char *rrname,
GList *records,
GError *error)
@@ -331,6 +371,9 @@ lookup_one_sync (const char *arg)
case G_RESOLVER_RECORD_TXT:
print_resolved_txt (arg, records, error);
break;
+ case G_RESOLVER_RECORD_SRV:
+ print_resolved_srv (arg, records, error);
+ break;
default:
g_warn_if_reached ();
break;
@@ -449,6 +492,9 @@ lookup_records_callback (GObject *source,
case G_RESOLVER_RECORD_TXT:
print_resolved_txt (arg, records, error);
break;
+ case G_RESOLVER_RECORD_SRV:
+ print_resolved_srv (arg, records, error);
+ break;
default:
g_warn_if_reached ();
break;
@@ -659,9 +705,11 @@ record_type_arg (const gchar *option_name,
record_type = G_RESOLVER_RECORD_SOA;
} else if (g_ascii_strcasecmp (value, "NS") == 0) {
record_type = G_RESOLVER_RECORD_NS;
+ } else if (g_ascii_strcasecmp (value, "SRV") == 0) {
+ record_type = G_RESOLVER_RECORD_SRV;
} else {
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- "Specify MX, TXT, NS or SOA for the special record lookup types");
+ "Specify MX, TXT, NS, SOA or SRV for the special record lookup types");
return FALSE;
}
diff --git a/gio/tests/resources.c b/gio/tests/resources.c
index 325775fd6..7ba5896a0 100644
--- a/gio/tests/resources.c
+++ b/gio/tests/resources.c
@@ -63,7 +63,7 @@ test_resource (GResource *resource)
"/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, G_RESOURCE_FLAGS_COMPRESSED);
@@ -81,7 +81,7 @@ test_resource (GResource *resource)
"/a_prefix/test2.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, 0);
@@ -90,7 +90,7 @@ test_resource (GResource *resource)
"/a_prefix/test2-alias.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, 0);
@@ -137,13 +137,13 @@ test_resource (GResource *resource)
"/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (in != NULL);
+ g_assert_nonnull (in);
g_assert_no_error (error);
success = g_input_stream_read_all (in, buffer, sizeof (buffer) - 1,
&size,
NULL, &error);
- g_assert (success);
+ g_assert_true (success);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
buffer[size] = 0;
@@ -175,7 +175,7 @@ test_resource (GResource *resource)
"/a_prefix/test2.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -186,7 +186,7 @@ test_resource (GResource *resource)
"/a_prefix/test2-alias.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -211,7 +211,7 @@ test_resource (GResource *resource)
"/a_prefix",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (children != NULL);
+ g_assert_nonnull (children);
g_assert_no_error (error);
g_assert_cmpint (g_strv_length (children), ==, 2);
g_strfreev (children);
@@ -221,7 +221,7 @@ test_resource (GResource *resource)
"/a_prefix/",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (children != NULL);
+ g_assert_nonnull (children);
g_assert_no_error (error);
g_assert_cmpint (g_strv_length (children), ==, 2);
g_strfreev (children);
@@ -238,7 +238,7 @@ test_resource (GResource *resource)
"/with/no/trailing/slash",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (children == NULL);
+ g_assert_null (children);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
}
@@ -250,12 +250,12 @@ test_resource_file (void)
GError *error = NULL;
resource = g_resource_load ("not-there", &error);
- g_assert (resource == NULL);
+ g_assert_null (resource);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
g_clear_error (&error);
resource = g_resource_load (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL), &error);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
test_resource (resource);
@@ -293,10 +293,10 @@ test_resource_file_path (void)
gchar *uri;
file = g_file_new_for_uri (test_uris[i].input);
- g_assert (file != NULL);
+ g_assert_nonnull (file);
uri = g_file_get_uri (file);
- g_assert (uri != NULL);
+ g_assert_nonnull (uri);
g_assert_cmpstr (uri, ==, test_uris[i].expected);
@@ -317,12 +317,12 @@ test_resource_data (void)
loaded_file = g_file_get_contents (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL),
&content, &content_size, NULL);
- g_assert (loaded_file);
+ g_assert_true (loaded_file);
data = g_bytes_new_take (content, content_size);
resource = g_resource_new_from_data (data, &error);
g_bytes_unref (data);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
test_resource (resource);
@@ -342,7 +342,7 @@ test_resource_data_unaligned (void)
loaded_file = g_file_get_contents (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL),
&content, &content_size, NULL);
- g_assert (loaded_file);
+ g_assert_true (loaded_file);
content_copy = g_new (char, content_size + 1);
memcpy (content_copy + 1, content, content_size);
@@ -352,7 +352,7 @@ test_resource_data_unaligned (void)
g_free (content);
resource = g_resource_new_from_data (data, &error);
g_bytes_unref (data);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
test_resource (resource);
@@ -413,13 +413,13 @@ test_resource_registered (void)
char buffer[128];
resource = g_resource_load (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL), &error);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
found = g_resources_get_info ("/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (!found);
+ g_assert_false (found);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
@@ -428,10 +428,10 @@ test_resource_registered (void)
found = g_resources_get_info ("/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
- g_assert (flags == (G_RESOURCE_FLAGS_COMPRESSED));
+ g_assert_cmpint (flags, ==, G_RESOURCE_FLAGS_COMPRESSED);
found = g_resources_get_info ("/empty.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
@@ -439,12 +439,12 @@ test_resource_registered (void)
g_assert_no_error (error);
g_assert_true (found);
g_assert_cmpint (size, ==, 0);
- g_assert (flags == (G_RESOURCE_FLAGS_COMPRESSED));
+ g_assert_cmpint (flags, ==, G_RESOURCE_FLAGS_COMPRESSED);
found = g_resources_get_info ("/a_prefix/test2.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpint (flags, ==, 0);
@@ -452,7 +452,7 @@ test_resource_registered (void)
found = g_resources_get_info ("/a_prefix/test2-alias.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, 0);
@@ -467,13 +467,13 @@ test_resource_registered (void)
in = g_resources_open_stream ("/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (in != NULL);
+ g_assert_nonnull (in);
g_assert_no_error (error);
success = g_input_stream_read_all (in, buffer, sizeof (buffer) - 1,
&size,
NULL, &error);
- g_assert (success);
+ g_assert_true (success);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
buffer[size] = 0;
@@ -510,7 +510,7 @@ test_resource_registered (void)
data = g_resources_lookup_data ("/a_prefix/test2.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -520,7 +520,7 @@ test_resource_registered (void)
data = g_resources_lookup_data ("/a_prefix/test2-alias.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -530,14 +530,14 @@ test_resource_registered (void)
children = g_resources_enumerate_children ("/not/here",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (children == NULL);
+ g_assert_null (children);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
children = g_resources_enumerate_children ("/a_prefix",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (children != NULL);
+ g_assert_nonnull (children);
g_assert_no_error (error);
g_assert_cmpint (g_strv_length (children), ==, 2);
g_strfreev (children);
@@ -548,7 +548,7 @@ test_resource_registered (void)
found = g_resources_get_info ("/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (!found);
+ g_assert_false (found);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
}
@@ -565,7 +565,7 @@ test_resource_automatic (void)
found = g_resources_get_info ("/auto_loaded/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpint (flags, ==, 0);
@@ -573,7 +573,7 @@ test_resource_automatic (void)
data = g_resources_lookup_data ("/auto_loaded/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -593,7 +593,7 @@ test_resource_manual (void)
found = g_resources_get_info ("/manual_loaded/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, 0);
@@ -601,7 +601,7 @@ test_resource_manual (void)
data = g_resources_lookup_data ("/manual_loaded/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -623,7 +623,7 @@ test_resource_manual2 (void)
"/manual_loaded/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -730,7 +730,7 @@ test_resource_module (void)
found = g_resources_get_info ("/resourceplugin/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (!found);
+ g_assert_false (found);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
@@ -739,7 +739,7 @@ test_resource_module (void)
found = g_resources_get_info ("/resourceplugin/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (found);
+ g_assert_true (found);
g_assert_no_error (error);
g_assert_cmpint (size, ==, 6);
g_assert_cmpuint (flags, ==, 0);
@@ -747,7 +747,7 @@ test_resource_module (void)
data = g_resources_lookup_data ("/resourceplugin/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&error);
- g_assert (data != NULL);
+ g_assert_nonnull (data);
g_assert_no_error (error);
size = g_bytes_get_size (data);
g_assert_cmpint (size, ==, 6);
@@ -759,9 +759,11 @@ test_resource_module (void)
found = g_resources_get_info ("/resourceplugin/test1.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE,
&size, &flags, &error);
- g_assert (!found);
+ g_assert_false (found);
g_assert_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND);
g_clear_error (&error);
+
+ g_clear_object (&module);
}
}
@@ -783,12 +785,12 @@ test_uri_query_info (void)
loaded_file = g_file_get_contents (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL),
&content, &content_size, NULL);
- g_assert (loaded_file);
+ g_assert_true (loaded_file);
data = g_bytes_new_take (content, content_size);
resource = g_resource_new_from_data (data, &error);
g_bytes_unref (data);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
g_resources_register (resource);
@@ -798,9 +800,9 @@ test_uri_query_info (void)
g_assert_no_error (error);
content_type = g_file_info_get_content_type (info);
- g_assert (content_type);
+ g_assert_nonnull (content_type);
mime_type = g_content_type_get_mime_type (content_type);
- g_assert (mime_type);
+ g_assert_nonnull (mime_type);
g_assert_cmpstr (mime_type, ==, "text/plain");
g_free (mime_type);
@@ -847,19 +849,19 @@ test_uri_file (void)
loaded_file = g_file_get_contents (g_test_get_filename (G_TEST_BUILT, "test.gresource", NULL),
&content, &content_size, NULL);
- g_assert (loaded_file);
+ g_assert_true (loaded_file);
data = g_bytes_new_take (content, content_size);
resource = g_resource_new_from_data (data, &error);
g_bytes_unref (data);
- g_assert (resource != NULL);
+ g_assert_nonnull (resource);
g_assert_no_error (error);
g_resources_register (resource);
file = g_file_new_for_uri ("resource://" "/a_prefix/test2-alias.txt");
- g_assert (g_file_get_path (file) == NULL);
+ g_assert_null (g_file_get_path (file));
name = g_file_get_parse_name (file);
g_assert_cmpstr (name, ==, "resource:///a_prefix/test2-alias.txt");
@@ -869,15 +871,15 @@ test_uri_file (void)
g_assert_cmpstr (name, ==, "resource:///a_prefix/test2-alias.txt");
g_free (name);
- g_assert (!g_file_is_native (file));
- g_assert (!g_file_has_uri_scheme (file, "http"));
- g_assert (g_file_has_uri_scheme (file, "resource"));
+ g_assert_false (g_file_is_native (file));
+ g_assert_false (g_file_has_uri_scheme (file, "http"));
+ g_assert_true (g_file_has_uri_scheme (file, "resource"));
scheme = g_file_get_uri_scheme (file);
g_assert_cmpstr (scheme, ==, "resource");
g_free (scheme);
file2 = g_file_dup (file);
- g_assert (g_file_equal (file, file2));
+ g_assert_true (g_file_equal (file, file2));
g_object_unref (file2);
parent = g_file_get_parent (file);
@@ -886,31 +888,31 @@ test_uri_file (void)
file2 = g_file_get_child_for_display_name (parent, "test2-alias.txt", &error);
g_assert_no_error (error);
- g_assert (g_file_equal (file, file2));
+ g_assert_true (g_file_equal (file, file2));
g_object_unref (file2);
info = g_file_enumerator_next_file (enumerator, NULL, &error);
g_assert_no_error (error);
- g_assert (info != NULL);
+ g_assert_nonnull (info);
g_object_unref (info);
info = g_file_enumerator_next_file (enumerator, NULL, &error);
g_assert_no_error (error);
- g_assert (info != NULL);
+ g_assert_nonnull (info);
g_object_unref (info);
info = g_file_enumerator_next_file (enumerator, NULL, &error);
g_assert_no_error (error);
- g_assert (info == NULL);
+ g_assert_null (info);
g_file_enumerator_close (enumerator, NULL, &error);
g_assert_no_error (error);
g_object_unref (enumerator);
file2 = g_file_new_for_uri ("resource://" "a_prefix/../a_prefix//test2-alias.txt");
- g_assert (g_file_equal (file, file2));
+ g_assert_true (g_file_equal (file, file2));
- g_assert (g_file_has_prefix (file, parent));
+ g_assert_true (g_file_has_prefix (file, parent));
name = g_file_get_relative_path (parent, file);
g_assert_cmpstr (name, ==, "test2-alias.txt");
@@ -929,9 +931,9 @@ test_uri_file (void)
stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
g_assert_no_error (error);
g_assert_cmpint (g_seekable_tell (G_SEEKABLE (stream)), ==, 0);
- g_assert (g_seekable_can_seek (G_SEEKABLE (G_SEEKABLE (stream))));
+ g_assert_true (g_seekable_can_seek (G_SEEKABLE (G_SEEKABLE (stream))));
ret = g_seekable_seek (G_SEEKABLE (stream), 1, G_SEEK_SET, NULL, &error);
- g_assert (ret);
+ g_assert_true (ret);
g_assert_no_error (error);
skipped = g_input_stream_skip (stream, 1, NULL, &error);
g_assert_cmpint (skipped, ==, 1);
@@ -939,7 +941,7 @@ test_uri_file (void)
memset (buf, 0, 1024);
ret = g_input_stream_read_all (stream, &buf, 1024, NULL, NULL, &error);
- g_assert (ret);
+ g_assert_true (ret);
g_assert_no_error (error);
g_assert_cmpstr (buf, ==, "st2\n");
info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (stream),
@@ -947,12 +949,12 @@ test_uri_file (void)
NULL,
&error);
g_assert_no_error (error);
- g_assert (info != NULL);
+ g_assert_nonnull (info);
g_assert_cmpint (g_file_info_get_size (info), ==, 6);
g_object_unref (info);
ret = g_input_stream_close (stream, NULL, &error);
- g_assert (ret);
+ g_assert_true (ret);
g_assert_no_error (error);
g_object_unref (stream);
diff --git a/gio/tests/socket.c b/gio/tests/socket.c
index ee38fabcd..4dae36b17 100644
--- a/gio/tests/socket.c
+++ b/gio/tests/socket.c
@@ -17,15 +17,21 @@
*/
#include <gio/gio.h>
+#include <glib/gstdio.h>
#include <gio/gcredentialsprivate.h>
+#include <gio/gunixconnection.h>
+
#ifdef G_OS_UNIX
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <gio/gnetworking.h>
-#include <gio/gunixconnection.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include "giowin32-afunix.h"
#endif
#include "gnetworkingprivate.h"
@@ -1327,7 +1333,23 @@ test_sockaddr (void)
g_object_unref (saddr);
}
-#ifdef G_OS_UNIX
+static void
+bind_win32_unixfd (int fd)
+{
+#ifdef G_OS_WIN32
+ gint len, ret;
+ struct sockaddr_un addr;
+
+ memset (&addr, 0, sizeof addr);
+ addr.sun_family = AF_UNIX;
+ len = g_snprintf (addr.sun_path, sizeof addr.sun_path, "%s" G_DIR_SEPARATOR_S "%d.sock", g_get_tmp_dir (), fd);
+ g_assert_cmpint (len, <=, sizeof addr.sun_path);
+ ret = bind (fd, (struct sockaddr *)&addr, sizeof addr);
+ g_assert_cmpint (ret, ==, 0);
+ g_remove (addr.sun_path);
+#endif
+}
+
static void
test_unix_from_fd (void)
{
@@ -1336,8 +1358,17 @@ test_unix_from_fd (void)
GSocket *s;
fd = socket (AF_UNIX, SOCK_STREAM, 0);
+#ifdef G_OS_WIN32
+ if (fd == -1)
+ {
+ g_test_skip ("AF_UNIX not supported on this Windows system.");
+ return;
+ }
+#endif
g_assert_cmpint (fd, !=, -1);
+ bind_win32_unixfd (fd);
+
error = NULL;
s = g_socket_new_from_fd (fd, &error);
g_assert_no_error (error);
@@ -1356,8 +1387,17 @@ test_unix_connection (void)
GSocketConnection *c;
fd = socket (AF_UNIX, SOCK_STREAM, 0);
+#ifdef G_OS_WIN32
+ if (fd == -1)
+ {
+ g_test_skip ("AF_UNIX not supported on this Windows system.");
+ return;
+ }
+#endif
g_assert_cmpint (fd, !=, -1);
+ bind_win32_unixfd (fd);
+
error = NULL;
s = g_socket_new_from_fd (fd, &error);
g_assert_no_error (error);
@@ -1367,6 +1407,7 @@ test_unix_connection (void)
g_object_unref (s);
}
+#ifdef G_OS_UNIX
static GSocketConnection *
create_connection_for_fd (int fd)
{
@@ -1466,6 +1507,7 @@ test_unix_connection_ancillary_data (void)
* g_unix_connection_receive_credentials().
*/
}
+#endif
static gboolean
postmortem_source_cb (GSocket *socket,
@@ -1490,6 +1532,14 @@ test_source_postmortem (void)
gboolean callback_visited = FALSE;
socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
+#ifdef G_OS_WIN32
+ if (error)
+ {
+ g_test_skip_printf ("AF_UNIX not supported on this Windows system: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+#endif
g_assert_no_error (error);
context = g_main_context_new ();
@@ -1513,8 +1563,6 @@ test_source_postmortem (void)
g_main_context_unref (context);
}
-#endif /* G_OS_UNIX */
-
static void
test_reuse_tcp (void)
{
@@ -2076,22 +2124,145 @@ client_setup_thread (gpointer user_data)
return NULL;
}
-#ifdef G_OS_UNIX
+#ifdef G_OS_WIN32
+/*
+ * _g_win32_socketpair:
+ *
+ * Create a pair of connected sockets, similar to POSIX/BSD socketpair().
+ *
+ * Windows does not (yet) provide a socketpair() function. However, since the
+ * introduction of AF_UNIX sockets, it is possible to implement a fairly close
+ * function.
+ */
+static gint
+_g_win32_socketpair (gint domain,
+ gint type,
+ gint protocol,
+ gint sv[2])
+{
+ struct sockaddr_un addr = { 0, };
+ socklen_t socklen;
+ SOCKET listener = INVALID_SOCKET;
+ SOCKET client = INVALID_SOCKET;
+ SOCKET server = INVALID_SOCKET;
+ gchar *path = NULL;
+ int tmpfd, rv = -1;
+ u_long arg, br;
+
+ g_return_val_if_fail (sv != NULL, -1);
+
+ addr.sun_family = AF_UNIX;
+ socklen = sizeof (addr);
+
+ tmpfd = g_file_open_tmp (NULL, &path, NULL);
+ if (tmpfd == -1)
+ {
+ WSASetLastError (WSAEACCES);
+ goto out;
+ }
+
+ g_close (tmpfd, NULL);
+
+ if (strlen (path) >= sizeof (addr.sun_path))
+ {
+ WSASetLastError (WSAEACCES);
+ goto out;
+ }
+
+ strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
+
+ listener = socket (domain, type, protocol);
+ if (listener == INVALID_SOCKET)
+ goto out;
+
+ if (DeleteFile (path) == 0)
+ {
+ if (GetLastError () != ERROR_FILE_NOT_FOUND)
+ goto out;
+ }
+
+ if (bind (listener, (struct sockaddr *) &addr, socklen) == SOCKET_ERROR)
+ goto out;
+
+ if (listen (listener, 1) == SOCKET_ERROR)
+ goto out;
+
+ client = socket (domain, type, protocol);
+ if (client == INVALID_SOCKET)
+ goto out;
+
+ arg = 1;
+ if (ioctlsocket (client, FIONBIO, &arg) == SOCKET_ERROR)
+ goto out;
+
+ if (connect (client, (struct sockaddr *) &addr, socklen) == SOCKET_ERROR &&
+ WSAGetLastError () != WSAEWOULDBLOCK)
+ goto out;
+
+ server = accept (listener, NULL, NULL);
+ if (server == INVALID_SOCKET)
+ goto out;
+
+ arg = 0;
+ if (ioctlsocket (client, FIONBIO, &arg) == SOCKET_ERROR)
+ goto out;
+
+ if (WSAIoctl (server, SIO_AF_UNIX_GETPEERPID,
+ NULL, 0U,
+ &arg, sizeof (arg), &br,
+ NULL, NULL) == SOCKET_ERROR || arg != GetCurrentProcessId ())
+ {
+ WSASetLastError (WSAEACCES);
+ goto out;
+ }
+
+ sv[0] = server;
+ server = INVALID_SOCKET;
+ sv[1] = client;
+ client = INVALID_SOCKET;
+ rv = 0;
+
+ out:
+ if (listener != INVALID_SOCKET)
+ closesocket (listener);
+ if (client != INVALID_SOCKET)
+ closesocket (client);
+ if (server != INVALID_SOCKET)
+ closesocket (server);
+
+ DeleteFile (path);
+ g_free (path);
+ return rv;
+}
+#endif /* G_OS_WIN32 */
+
static void
test_credentials_unix_socketpair (void)
{
gint fds[2];
gint status;
- GSocket *sock;
+ GSocket *sock[2];
GError *error = NULL;
GCredentials *creds;
+#ifdef G_OS_WIN32
+ status = _g_win32_socketpair (PF_UNIX, SOCK_STREAM, 0, fds);
+ if (status != 0)
+ {
+ g_test_skip ("AF_UNIX not supported on this Windows system.");
+ return;
+ }
+#else
status = socketpair (PF_UNIX, SOCK_STREAM, 0, fds);
+#endif
g_assert_cmpint (status, ==, 0);
- sock = g_socket_new_from_fd (fds[0], &error);
+ sock[0] = g_socket_new_from_fd (fds[0], &error);
+ g_assert_no_error (error);
+ sock[1] = g_socket_new_from_fd (fds[1], &error);
+ g_assert_no_error (error);
- creds = g_socket_get_credentials (sock, &error);
+ creds = g_socket_get_credentials (sock[0], &error);
if (creds != NULL)
{
gchar *str = g_credentials_to_string (creds);
@@ -2106,11 +2277,10 @@ test_credentials_unix_socketpair (void)
g_clear_error (&error);
}
- g_object_unref (sock);
- close (fds[1]);
+ g_object_unref (sock[0]);
+ g_object_unref (sock[1]);
}
#endif
-#endif
int
main (int argc,
@@ -2151,12 +2321,12 @@ main (int argc,
g_test_add_func ("/socket/timed_wait", test_timed_wait);
g_test_add_func ("/socket/fd_reuse", test_fd_reuse);
g_test_add_func ("/socket/address", test_sockaddr);
-#ifdef G_OS_UNIX
g_test_add_func ("/socket/unix-from-fd", test_unix_from_fd);
g_test_add_func ("/socket/unix-connection", test_unix_connection);
+#ifdef G_OS_UNIX
g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data);
- g_test_add_func ("/socket/source-postmortem", test_source_postmortem);
#endif
+ g_test_add_func ("/socket/source-postmortem", test_source_postmortem);
g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp);
g_test_add_func ("/socket/reuse/udp", test_reuse_udp);
g_test_add_data_func ("/socket/get_available/datagram", GUINT_TO_POINTER (G_SOCKET_TYPE_DATAGRAM),
@@ -2173,10 +2343,8 @@ main (int argc,
#if G_CREDENTIALS_SUPPORTED
g_test_add_func ("/socket/credentials/tcp_client", test_credentials_tcp_client);
g_test_add_func ("/socket/credentials/tcp_server", test_credentials_tcp_server);
-#ifdef G_OS_UNIX
g_test_add_func ("/socket/credentials/unix_socketpair", test_credentials_unix_socketpair);
#endif
-#endif
return g_test_run();
}
diff --git a/gio/tests/testfilemonitor.c b/gio/tests/testfilemonitor.c
index a52ade715..082f0db26 100644
--- a/gio/tests/testfilemonitor.c
+++ b/gio/tests/testfilemonitor.c
@@ -4,6 +4,17 @@
#include <stdlib.h>
#include <gio/gio.h>
+static gboolean
+skip_win32 (void)
+{
+#ifdef G_OS_WIN32
+ g_test_skip ("FIXME, test is broken on win32");
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
/* These tests were written for the inotify implementation.
* Other implementations may require slight adjustments in
* the tests, e.g. the length of timeouts
@@ -361,6 +372,9 @@ test_atomic_replace (Fixture *fixture,
GError *error = NULL;
TestData data;
+ if (skip_win32 ())
+ return;
+
data.step = 0;
data.events = NULL;
@@ -466,6 +480,9 @@ test_file_changes (Fixture *fixture,
GError *error = NULL;
TestData data;
+ if (skip_win32 ())
+ return;
+
data.step = 0;
data.events = NULL;
@@ -583,6 +600,9 @@ test_dir_monitor (Fixture *fixture,
GError *error = NULL;
TestData data;
+ if (skip_win32 ())
+ return;
+
data.step = 0;
data.events = NULL;
@@ -680,6 +700,9 @@ test_dir_non_existent (Fixture *fixture,
TestData data;
GError *error = NULL;
+ if (skip_win32 ())
+ return;
+
data.step = 0;
data.events = NULL;
@@ -789,6 +812,9 @@ test_cross_dir_moves (Fixture *fixture,
GError *error = NULL;
TestData data[2];
+ if (skip_win32 ())
+ return;
+
data[0].step = 0;
data[0].events = NULL;
@@ -960,6 +986,9 @@ test_file_hard_links (Fixture *fixture,
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755721");
+ if (skip_win32 ())
+ return;
+
#ifdef HAVE_LINK
g_test_message ("Running with hard link tests");
#else /* if !HAVE_LINK */
@@ -1007,6 +1036,57 @@ test_file_hard_links (Fixture *fixture,
g_object_unref (data.output_stream);
}
+static void
+test_finalize_in_callback (Fixture *fixture,
+ gconstpointer user_data)
+{
+ GFile *file = NULL;
+ guint i;
+
+ g_test_summary ("Test that finalization of a GFileMonitor in one of its "
+ "callbacks doesn’t cause a deadlock.");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1941");
+
+ file = g_file_get_child (fixture->tmp_dir, "race-file");
+
+ for (i = 0; i < 50; i++)
+ {
+ GFileMonitor *monitor = NULL;
+ GError *local_error = NULL;
+
+ /* Monitor the file. */
+ monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (monitor);
+
+ /* Create the file. */
+ g_file_replace_contents (file, "hello", 5, NULL, FALSE,
+ G_FILE_CREATE_NONE, NULL, NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Immediately drop the last ref to the monitor in the hope that this
+ * happens in the middle of the critical section in
+ * g_file_monitor_source_handle_event(), so that any cleanup at the end
+ * of that function is done with a now-finalised file monitor. */
+ g_object_unref (monitor);
+
+ /* Re-create the monitor and do the same again for deleting the file, to
+ * give a second chance at hitting the race condition. */
+ monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (monitor);
+
+ /* Delete the file. */
+ g_file_delete (file, NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Drop the ref again. */
+ g_object_unref (monitor);
+ }
+
+ g_object_unref (file);
+}
+
int
main (int argc, char *argv[])
{
@@ -1018,6 +1098,7 @@ main (int argc, char *argv[])
g_test_add ("/monitor/dir-not-existent", Fixture, NULL, setup, test_dir_non_existent, teardown);
g_test_add ("/monitor/cross-dir-moves", Fixture, NULL, setup, test_cross_dir_moves, teardown);
g_test_add ("/monitor/file/hard-links", Fixture, NULL, setup, test_file_hard_links, teardown);
+ g_test_add ("/monitor/finalize-in-callback", Fixture, NULL, setup, test_finalize_in_callback, teardown);
return g_test_run ();
}
diff --git a/gio/tests/tls-certificate.c b/gio/tests/tls-certificate.c
index 2995b1028..bae582357 100644
--- a/gio/tests/tls-certificate.c
+++ b/gio/tests/tls-certificate.c
@@ -559,7 +559,7 @@ dns_names (void)
g_assert_cmpuint (actual->len, ==, 1);
g_assert_true (g_ptr_array_find_with_equal_func (actual, expected, (GEqualFunc)g_bytes_equal, NULL));
- g_ptr_array_free (actual, FALSE);
+ g_ptr_array_unref (actual);
g_bytes_unref (expected);
g_object_unref (cert);
}
@@ -586,6 +586,45 @@ ip_addresses (void)
g_object_unref (cert);
}
+static void
+from_pkcs12 (void)
+{
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ const guint8 data[1] = { 0 };
+
+ /* This simply fails because our test backend doesn't support this
+ * property. This reflects using a backend that doesn't support it.
+ * The real test lives in glib-networking. */
+ cert = g_tls_certificate_new_from_pkcs12 (data, 1, NULL, &error);
+
+ g_assert_null (cert);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_error_free (error);
+}
+
+static void
+from_pkcs12_file (void)
+{
+ GTlsCertificate *cert;
+ GError *error = NULL;
+ char *path = g_test_build_filename (G_TEST_DIST, "cert-tests", "key-cert-password-123.p12", NULL);
+
+ /* Fails on our test backend, see from_pkcs12() above. */
+ cert = g_tls_certificate_new_from_file_with_password (path, "123", &error);
+ g_assert_null (cert);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_clear_error (&error);
+
+ /* Just for coverage. */
+ cert = g_tls_certificate_new_from_file (path, &error);
+ g_assert_null (cert);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_error_free (error);
+
+ g_free (path);
+}
+
int
main (int argc,
char *argv[])
@@ -656,6 +695,10 @@ main (int argc,
from_pkcs11_uri);
g_test_add_func ("/tls-certificate/pkcs11-uri-unsupported",
from_unsupported_pkcs11_uri);
+ g_test_add_func ("/tls-certificate/from_pkcs12",
+ from_pkcs12);
+ g_test_add_func ("/tls-certificate/from_pkcs12_file",
+ from_pkcs12_file);
g_test_add_func ("/tls-certificate/not-valid-before",
not_valid_before);
g_test_add_func ("/tls-certificate/not-valid-after",
diff --git a/gio/win32/gwin32fsmonitorutils.c b/gio/win32/gwin32fsmonitorutils.c
index b47124634..f2ab5472a 100644
--- a/gio/win32/gwin32fsmonitorutils.c
+++ b/gio/win32/gwin32fsmonitorutils.c
@@ -92,7 +92,7 @@ g_win32_fs_monitor_handle_event (GWin32FSMonitorPrivate *monitor,
monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME)
{
/* don't bother sending events, was already sent (rename) */
- fme = -1;
+ fme = (GFileMonitorEvent) -1;
}
else
fme = G_FILE_MONITOR_EVENT_MOVED_IN;
@@ -104,7 +104,7 @@ g_win32_fs_monitor_handle_event (GWin32FSMonitorPrivate *monitor,
break;
}
- if (fme != -1)
+ if (fme != (GFileMonitorEvent) -1)
return g_file_monitor_source_handle_event (monitor->fms,
fme,
filename,
diff --git a/gio/win32/gwinhttpfile.c b/gio/win32/gwinhttpfile.c
index 5b8dcfe0b..e73c87658 100644
--- a/gio/win32/gwinhttpfile.c
+++ b/gio/win32/gwinhttpfile.c
@@ -546,7 +546,7 @@ g_winhttp_file_query_info (GFile *file,
NULL))
{
gint64 cl;
- int n;
+ size_t n;
const char *gint64_format = "%"G_GINT64_FORMAT"%n";
wchar_t *gint64_format_w = g_utf8_to_utf16 (gint64_format, -1, NULL, NULL, NULL);
diff --git a/gio/win32/gwinhttpvfs.c b/gio/win32/gwinhttpvfs.c
index 03feaf983..4d5f51498 100644
--- a/gio/win32/gwinhttpvfs.c
+++ b/gio/win32/gwinhttpvfs.c
@@ -165,7 +165,7 @@ g_winhttp_vfs_get_file_for_uri (GVfs *vfs,
const char *uri)
{
GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
- int i;
+ gsize i;
GFile *ret = NULL;
/* If it matches one of "our" schemes, handle it */
@@ -192,7 +192,7 @@ g_winhttp_vfs_get_supported_uri_schemes (GVfs *vfs)
{
GWinHttpVfs *winhttp_vfs = G_WINHTTP_VFS (vfs);
const gchar * const *wrapped_vfs_uri_schemes = g_vfs_get_supported_uri_schemes (winhttp_vfs->wrapped_vfs);
- int i, n;
+ gsize i, n;
const gchar **retval;
n = 0;
diff --git a/gio/xdgmime/meson.build b/gio/xdgmime/meson.build
index d107f71bb..4c8a552a2 100644
--- a/gio/xdgmime/meson.build
+++ b/gio/xdgmime/meson.build
@@ -13,4 +13,5 @@ xdgmime_lib = static_library('xdgmime',
sources : xdgmime_sources,
include_directories : [configinc],
pic : true,
- c_args : [ '-DXDG_PREFIX=_gio_xdg' ] + glib_hidden_visibility_args)
+ c_args : [ '-DHAVE_CONFIG_H',
+ '-DXDG_PREFIX=_gio_xdg' ] + glib_hidden_visibility_args)
diff --git a/gio/xdgmime/xdgmime.c b/gio/xdgmime/xdgmime.c
index 9bb93f791..9ab676048 100644
--- a/gio/xdgmime/xdgmime.c
+++ b/gio/xdgmime/xdgmime.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmime.h"
#include "xdgmimeint.h"
@@ -41,6 +45,10 @@
#include <unistd.h>
#include <assert.h>
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
typedef struct XdgDirTimeList XdgDirTimeList;
typedef struct XdgCallbackList XdgCallbackList;
@@ -134,7 +142,8 @@ xdg_dir_time_list_free (XdgDirTimeList *list)
}
static int
-xdg_mime_init_from_directory (const char *directory)
+xdg_mime_init_from_directory (const char *directory,
+ void *user_data)
{
char *file_name;
struct stat st;
@@ -400,10 +409,11 @@ xdg_check_file (const char *file_path,
static int
xdg_check_dir (const char *directory,
- int *invalid_dir_list)
+ void *user_data)
{
int invalid, exists;
char *file_name;
+ int* invalid_dir_list = user_data;
assert (directory != NULL);
@@ -458,8 +468,7 @@ xdg_check_dirs (void)
for (list = dir_time_list; list; list = list->next)
list->checked = XDG_CHECKED_UNCHECKED;
- xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
- &invalid_dir_list);
+ xdg_run_command_on_dirs (xdg_check_dir, &invalid_dir_list);
if (invalid_dir_list)
return TRUE;
@@ -515,8 +524,7 @@ xdg_mime_init (void)
icon_list = _xdg_mime_icon_list_new ();
generic_icon_list = _xdg_mime_icon_list_new ();
- xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
- NULL);
+ xdg_run_command_on_dirs (xdg_mime_init_from_directory, NULL);
need_reread = FALSE;
}
@@ -549,8 +557,6 @@ xdg_mime_get_mime_type_for_data (const void *data,
return _xdg_binary_or_text_fallback(data, len);
}
-#ifdef NOT_USED_IN_GIO
-
const char *
xdg_mime_get_mime_type_for_file (const char *file_name,
struct stat *statbuf)
@@ -621,13 +627,13 @@ xdg_mime_get_mime_type_for_file (const char *file_name,
mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
mime_types, n);
+ if (!mime_type)
+ mime_type = _xdg_binary_or_text_fallback (data, bytes_read);
+
free (data);
fclose (file);
- if (mime_type)
- return mime_type;
-
- return _xdg_binary_or_text_fallback(data, bytes_read);
+ return mime_type;
}
const char *
@@ -646,8 +652,6 @@ xdg_mime_get_mime_type_from_file_name (const char *file_name)
return XDG_MIME_TYPE_UNKNOWN;
}
-#endif
-
int
xdg_mime_get_mime_types_from_file_name (const char *file_name,
const char *mime_types[],
@@ -661,8 +665,6 @@ xdg_mime_get_mime_types_from_file_name (const char *file_name,
return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
}
-#ifdef NOT_USED_IN_GIO
-
int
xdg_mime_is_valid_mime_type (const char *mime_type)
{
@@ -671,8 +673,6 @@ xdg_mime_is_valid_mime_type (const char *mime_type)
return _xdg_utf8_validate (mime_type);
}
-#endif
-
void
xdg_mime_shutdown (void)
{
@@ -888,19 +888,14 @@ xdg_mime_mime_type_subclass (const char *mime,
char **
xdg_mime_list_mime_parents (const char *mime)
{
- const char *umime;
const char **parents;
char **result;
int i, n;
- xdg_mime_init ();
-
if (_caches)
return _xdg_mime_cache_list_mime_parents (mime);
- umime = _xdg_mime_unalias_mime_type (mime);
-
- parents = _xdg_mime_parent_list_lookup (parent_list, umime);
+ parents = xdg_mime_get_mime_parents (mime);
if (!parents)
return NULL;
@@ -914,8 +909,6 @@ xdg_mime_list_mime_parents (const char *mime)
return result;
}
-#ifdef NOT_USED_IN_GIO
-
const char **
xdg_mime_get_mime_parents (const char *mime)
{
@@ -945,7 +938,6 @@ xdg_mime_dump (void)
_xdg_mime_cache_glob_dump ();
}
-#endif
/* Registers a function to be called every time the mime database reloads its files
*/
@@ -973,8 +965,6 @@ xdg_mime_register_reload_callback (XdgMimeCallback callback,
return callback_id - 1;
}
-#ifdef NOT_USED_IN_GIO
-
void
xdg_mime_remove_callback (int callback_id)
{
@@ -1000,8 +990,6 @@ xdg_mime_remove_callback (int callback_id)
}
}
-#endif
-
const char *
xdg_mime_get_icon (const char *mime)
{
diff --git a/gio/xdgmime/xdgmime.h b/gio/xdgmime/xdgmime.h
index b175de107..c5909967f 100644
--- a/gio/xdgmime/xdgmime.h
+++ b/gio/xdgmime/xdgmime.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
@@ -71,9 +73,9 @@ typedef void (*XdgMimeDestroy) (void *user_data);
#define xdg_mime_get_icon XDG_ENTRY(get_icon)
#define xdg_mime_get_generic_icon XDG_ENTRY(get_generic_icon)
-#define _xdg_mime_mime_type_subclass XDG_RESERVED_ENTRY(mime_type_subclass)
#define _xdg_mime_mime_type_equal XDG_RESERVED_ENTRY(mime_type_equal)
-#define _xdg_mime_unalias_mime_type XDG_RESERVED_ENTRY(unalias_mime_type)
+#define _xdg_mime_mime_type_subclass XDG_RESERVED_ENTRY(mime_type_subclass)
+#define _xdg_mime_unalias_mime_type XDG_RESERVED_ENTRY(unalias_mime_type)
#endif
extern const char xdg_mime_type_unknown[];
@@ -86,17 +88,13 @@ extern const char xdg_mime_type_textplain[];
const char *xdg_mime_get_mime_type_for_data (const void *data,
size_t len,
int *result_prio);
-#ifdef NOT_USED_IN_GIO
const char *xdg_mime_get_mime_type_for_file (const char *file_name,
struct stat *statbuf);
const char *xdg_mime_get_mime_type_from_file_name (const char *file_name);
-#endif
int xdg_mime_get_mime_types_from_file_name(const char *file_name,
const char *mime_types[],
int n_mime_types);
-#ifdef NOT_USED_IN_GIO
int xdg_mime_is_valid_mime_type (const char *mime_type);
-#endif
int xdg_mime_mime_type_equal (const char *mime_a,
const char *mime_b);
int xdg_mime_media_type_equal (const char *mime_a,
@@ -108,24 +106,18 @@ int xdg_mime_mime_type_subclass (const char *mime_a,
* instead, but notice that that function expects you to free
* the array it returns.
*/
-#ifdef NOT_USED_IN_GIO
const char **xdg_mime_get_mime_parents (const char *mime);
-#endif
char ** xdg_mime_list_mime_parents (const char *mime);
const char *xdg_mime_unalias_mime_type (const char *mime);
const char *xdg_mime_get_icon (const char *mime);
const char *xdg_mime_get_generic_icon (const char *mime);
int xdg_mime_get_max_buffer_extents (void);
void xdg_mime_shutdown (void);
-#ifdef NOT_USED_IN_GIO
void xdg_mime_dump (void);
-#endif
int xdg_mime_register_reload_callback (XdgMimeCallback callback,
void *data,
XdgMimeDestroy destroy);
-#ifdef NOT_USED_IN_GIO
void xdg_mime_remove_callback (int callback_id);
-#endif
void xdg_mime_set_dirs (const char * const *dirs);
diff --git a/gio/xdgmime/xdgmimealias.c b/gio/xdgmime/xdgmimealias.c
index bf95bc0d3..0fc51f94c 100644
--- a/gio/xdgmime/xdgmimealias.c
+++ b/gio/xdgmime/xdgmimealias.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmimealias.h"
#include "xdgmimeint.h"
@@ -161,8 +165,6 @@ _xdg_mime_alias_read_from_file (XdgAliasList *list,
}
-#ifdef NOT_USED_IN_GIO
-
void
_xdg_mime_alias_list_dump (XdgAliasList *list)
{
@@ -179,4 +181,4 @@ _xdg_mime_alias_list_dump (XdgAliasList *list)
}
}
-#endif
+
diff --git a/gio/xdgmime/xdgmimealias.h b/gio/xdgmime/xdgmimealias.h
index 46cbc99c3..6e0cfff3b 100644
--- a/gio/xdgmime/xdgmimealias.h
+++ b/gio/xdgmime/xdgmimealias.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_ALIAS_H__
@@ -44,7 +46,6 @@ XdgAliasList *_xdg_mime_alias_list_new (void);
void _xdg_mime_alias_list_free (XdgAliasList *list);
const char *_xdg_mime_alias_list_lookup (XdgAliasList *list,
const char *alias);
-#ifdef NOT_USED_IN_GIO
void _xdg_mime_alias_list_dump (XdgAliasList *list);
-#endif
+
#endif /* __XDG_MIME_ALIAS_H__ */
diff --git a/gio/xdgmime/xdgmimecache.c b/gio/xdgmime/xdgmimecache.c
index 769b57836..234e4b467 100644
--- a/gio/xdgmime/xdgmimecache.c
+++ b/gio/xdgmime/xdgmimecache.c
@@ -19,10 +19,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
@@ -39,7 +43,7 @@
#ifdef HAVE_MMAP
#include <sys/mman.h>
#else
-#warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
+#warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used.
#endif
#include <sys/stat.h>
@@ -68,6 +72,10 @@
#define MAP_FAILED ((void *) -1)
#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
#define MAJOR_VERSION 1
#define MINOR_VERSION_MIN 1
#define MINOR_VERSION_MAX 2
@@ -117,9 +125,9 @@ _xdg_mime_cache_new_from_file (const char *file_name)
int minor;
/* Open the file and map it into memory */
- do
+ do {
fd = open (file_name, O_RDONLY|_O_BINARY, 0);
- while (fd == -1 && errno == EINTR);
+ } while (fd == -1 && errno == EINTR);
if (fd < 0)
return NULL;
@@ -176,7 +184,7 @@ cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
- int i, j;
+ xdg_uint32_t i, j;
for (i = range_start; i < range_start + range_length; i++)
{
@@ -199,16 +207,9 @@ cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
}
else
{
- for (j = 0; j < data_length; j++)
- {
- if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
- {
- valid_matchlet = FALSE;
- break;
- }
- }
+ valid_matchlet = memcmp(cache->buffer + data_offset, (unsigned char *)data + i, data_length) == 0;
}
-
+
if (valid_matchlet)
return TRUE;
}
@@ -225,7 +226,7 @@ cache_magic_matchlet_compare (XdgMimeCache *cache,
xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
- int i;
+ xdg_uint32_t i;
if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
{
@@ -255,7 +256,7 @@ cache_magic_compare_to_data (XdgMimeCache *cache,
xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
- int i;
+ xdg_uint32_t i;
for (i = 0; i < n_matchlets; i++)
{
@@ -275,15 +276,13 @@ static const char *
cache_magic_lookup_data (XdgMimeCache *cache,
const void *data,
size_t len,
- int *prio,
- const char *mime_types[],
- int n_mime_types)
+ int *prio)
{
xdg_uint32_t list_offset;
xdg_uint32_t n_entries;
xdg_uint32_t offset;
- int j, n;
+ xdg_uint32_t j;
*prio = 0;
@@ -299,21 +298,6 @@ cache_magic_lookup_data (XdgMimeCache *cache,
data, len, prio);
if (match)
return match;
- else
- {
- xdg_uint32_t mimetype_offset;
- const char *non_match;
-
- mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
- non_match = cache->buffer + mimetype_offset;
-
- for (n = 0; n < n_mime_types; n++)
- {
- if (mime_types[n] &&
- _xdg_mime_mime_type_equal (mime_types[n], non_match))
- mime_types[n] = NULL;
- }
- }
}
return NULL;
@@ -428,12 +412,14 @@ cache_glob_lookup_literal (const char *file_name,
static int
cache_glob_lookup_fnmatch (const char *file_name,
MimeWeight mime_types[],
- int n_mime_types)
+ int n_mime_types,
+ int case_sensitive_check)
{
const char *mime_type;
const char *ptr;
- int i, j, n;
+ int i, n;
+ xdg_uint32_t j;
n = 0;
for (i = 0; _caches[i]; i++)
@@ -454,16 +440,19 @@ cache_glob_lookup_fnmatch (const char *file_name,
xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
+ int case_sensitive = weight & 0x100;
weight = weight & 0xff;
ptr = cache->buffer + offset;
mime_type = cache->buffer + mimetype_offset;
-
- /* FIXME: Not UTF-8 safe */
- if (fnmatch (ptr, file_name, 0) == 0)
+ if (case_sensitive_check || !case_sensitive)
{
- mime_types[n].mime = mime_type;
- mime_types[n].weight = weight;
- n++;
+ /* FIXME: Not UTF-8 safe */
+ if (fnmatch (ptr, file_name, 0) == 0)
+ {
+ mime_types[n].mime = mime_type;
+ mime_types[n].weight = weight;
+ n++;
+ }
}
}
@@ -492,7 +481,8 @@ cache_glob_node_lookup_suffix (XdgMimeCache *cache,
int weight;
int case_sensitive;
- int min, max, mid, n, i;
+ xdg_uint32_t i;
+ int min, max, mid, n;
character = file_name[len - 1];
@@ -578,8 +568,8 @@ cache_glob_lookup_suffix (const char *file_name,
n_entries = GET_UINT32 (cache->buffer, list_offset);
offset = GET_UINT32 (cache->buffer, list_offset + 4);
- n += cache_glob_node_lookup_suffix (cache,
- n_entries, offset,
+ n += cache_glob_node_lookup_suffix (cache,
+ n_entries, offset,
file_name, len,
ignore_case,
mime_types + n,
@@ -680,14 +670,16 @@ cache_glob_lookup_file_name (const char *file_name,
if (n < 2)
n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
- free (lower_case);
-
/* Last, try fnmatch */
+ if (n == 0)
+ n = cache_glob_lookup_fnmatch (lower_case, mimes, n_mimes, FALSE);
if (n < 2)
- n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n);
+ n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n, TRUE);
n = filter_out_dupes (mimes, n);
+ free (lower_case);
+
qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
if (n_mime_types < n)
@@ -743,8 +735,7 @@ cache_get_mime_type_for_data (const void *data,
if (cache->buffer == NULL)
continue;
- match = cache_magic_lookup_data (cache, data, len, &prio,
- mime_types, n_mime_types);
+ match = cache_magic_lookup_data (cache, data, len, &prio);
if (prio > priority)
{
priority = prio;
@@ -754,14 +745,27 @@ cache_get_mime_type_for_data (const void *data,
if (result_prio)
*result_prio = priority;
-
+
if (priority > 0)
- return mime_type;
+ {
+ /* Pick glob-result R where mime_type inherits from R */
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n] && _xdg_mime_cache_mime_type_subclass(mime_types[n], mime_type))
+ return mime_types[n];
+ }
+ if (n == 0)
+ {
+ /* No globs: return magic match */
+ return mime_type;
+ }
+ }
+ /* Pick first glob result, as fallback */
for (n = 0; n < n_mime_types; n++)
{
if (mime_types[n])
- return mime_types[n];
+ return mime_types[n];
}
return NULL;
@@ -775,8 +779,6 @@ _xdg_mime_cache_get_mime_type_for_data (const void *data,
return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
}
-#ifdef NOT_USED_IN_GIO
-
const char *
_xdg_mime_cache_get_mime_type_for_file (const char *file_name,
struct stat *statbuf)
@@ -844,7 +846,7 @@ _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
mime_types, n);
if (!mime_type)
- mime_type = _xdg_binary_or_text_fallback(data, bytes_read);
+ mime_type = _xdg_binary_or_text_fallback (data, bytes_read);
free (data);
fclose (file);
@@ -863,8 +865,6 @@ _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
return XDG_MIME_TYPE_UNKNOWN;
}
-#endif
-
int
_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
const char *mime_types[],
@@ -905,7 +905,8 @@ _xdg_mime_cache_mime_type_subclass (const char *mime,
{
const char *umime, *ubase;
- int i, j, min, max, med, cmp;
+ xdg_uint32_t j;
+ int i, min, max, med, cmp;
umime = _xdg_mime_cache_unalias_mime_type (mime);
ubase = _xdg_mime_cache_unalias_mime_type (base);
@@ -995,7 +996,8 @@ _xdg_mime_cache_unalias_mime_type (const char *mime)
char **
_xdg_mime_cache_list_mime_parents (const char *mime)
{
- int i, j, k, l, p;
+ int i, l, p;
+ xdg_uint32_t j, k;
char *all_parents[128]; /* we'll stop at 128 */
char **result;
@@ -1109,8 +1111,6 @@ _xdg_mime_cache_get_icon (const char *mime)
return cache_lookup_icon (mime, 32);
}
-#ifdef NOT_USED_IN_GIO
-
static void
dump_glob_node (XdgMimeCache *cache,
xdg_uint32_t offset,
@@ -1120,6 +1120,7 @@ dump_glob_node (XdgMimeCache *cache,
xdg_uint32_t mime_offset;
xdg_uint32_t n_children;
xdg_uint32_t child_offset;
+ xdg_uint32_t k;
int i;
character = GET_UINT32 (cache->buffer, offset);
@@ -1134,15 +1135,15 @@ dump_glob_node (XdgMimeCache *cache,
printf ("\n");
if (child_offset)
{
- for (i = 0; i < n_children; i++)
- dump_glob_node (cache, child_offset + 20 * i, depth + 1);
+ for (k = 0; k < n_children; k++)
+ dump_glob_node (cache, child_offset + 20 * k, depth + 1);
}
}
void
_xdg_mime_cache_glob_dump (void)
{
- int i, j;
+ xdg_uint32_t i, j;
for (i = 0; _caches[i]; i++)
{
XdgMimeCache *cache = _caches[i];
@@ -1161,4 +1162,4 @@ _xdg_mime_cache_glob_dump (void)
}
}
-#endif
+
diff --git a/gio/xdgmime/xdgmimecache.h b/gio/xdgmime/xdgmimecache.h
index 2723c5396..df25b2a57 100644
--- a/gio/xdgmime/xdgmimecache.h
+++ b/gio/xdgmime/xdgmimecache.h
@@ -19,7 +19,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_CACHE_H__
@@ -56,16 +58,12 @@ void _xdg_mime_cache_unref (XdgMimeCache *cache);
const char *_xdg_mime_cache_get_mime_type_for_data (const void *data,
size_t len,
int *result_prio);
-#ifdef NOT_USED_IN_GIO
const char *_xdg_mime_cache_get_mime_type_for_file (const char *file_name,
struct stat *statbuf);
-#endif
int _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
const char *mime_types[],
int n_mime_types);
-#ifdef NOT_USED_IN_GIO
const char *_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name);
-#endif
int _xdg_mime_cache_is_valid_mime_type (const char *mime_type);
int _xdg_mime_cache_mime_type_equal (const char *mime_a,
const char *mime_b);
@@ -78,8 +76,6 @@ const char *_xdg_mime_cache_unalias_mime_type (const char *mime);
int _xdg_mime_cache_get_max_buffer_extents (void);
const char *_xdg_mime_cache_get_icon (const char *mime);
const char *_xdg_mime_cache_get_generic_icon (const char *mime);
-#ifdef NOT_USED_IN_GIO
void _xdg_mime_cache_glob_dump (void);
-#endif
#endif /* __XDG_MIME_CACHE_H__ */
diff --git a/gio/xdgmime/xdgmimeglob.c b/gio/xdgmime/xdgmimeglob.c
index c18762e9a..d68435c89 100644
--- a/gio/xdgmime/xdgmimeglob.c
+++ b/gio/xdgmime/xdgmimeglob.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmimeglob.h"
#include "xdgmimeint.h"
@@ -86,7 +90,7 @@ _xdg_glob_list_new (void)
return new_element;
}
-/* Frees glob_list and all of it's children */
+/* Frees glob_list and all of its children */
static void
_xdg_glob_list_free (XdgGlobList *glob_list)
{
@@ -158,8 +162,6 @@ _xdg_glob_hash_node_new (void)
return glob_hash_node;
}
-#ifdef NOT_USED_IN_GIO
-
static void
_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
int depth)
@@ -179,8 +181,6 @@ _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
_xdg_glob_hash_node_dump (glob_hash_node->next, depth);
}
-#endif
-
static XdgGlobHashNode *
_xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
xdg_unichar_t *text,
@@ -603,8 +603,6 @@ _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
}
}
-#ifdef NOT_USED_IN_GIO
-
void
_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
{
@@ -641,7 +639,6 @@ _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
}
}
-#endif
void
_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
diff --git a/gio/xdgmime/xdgmimeglob.h b/gio/xdgmime/xdgmimeglob.h
index 79ccdc292..8b1fa3ad4 100644
--- a/gio/xdgmime/xdgmimeglob.h
+++ b/gio/xdgmime/xdgmimeglob.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_GLOB_H__
@@ -63,8 +65,6 @@ void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
int weight,
int case_sensitive);
XdgGlobType _xdg_glob_determine_type (const char *glob);
-#ifdef NOT_USED_IN_GIO
void _xdg_glob_hash_dump (XdgGlobHash *glob_hash);
-#endif
#endif /* __XDG_MIME_GLOB_H__ */
diff --git a/gio/xdgmime/xdgmimeicon.c b/gio/xdgmime/xdgmimeicon.c
index a2f4dd2a7..feb6c869c 100644
--- a/gio/xdgmime/xdgmimeicon.c
+++ b/gio/xdgmime/xdgmimeicon.c
@@ -19,10 +19,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmimeicon.h"
#include "xdgmimeint.h"
@@ -159,7 +163,6 @@ _xdg_mime_icon_read_from_file (XdgIconList *list,
sizeof (XdgIcon), icon_entry_cmp);
}
-#ifdef NOT_USED_IN_GIO
void
_xdg_mime_icon_list_dump (XdgIconList *list)
@@ -177,4 +180,4 @@ _xdg_mime_icon_list_dump (XdgIconList *list)
}
}
-#endif
+
diff --git a/gio/xdgmime/xdgmimeicon.h b/gio/xdgmime/xdgmimeicon.h
index 6141a8668..c416b3c5e 100644
--- a/gio/xdgmime/xdgmimeicon.h
+++ b/gio/xdgmime/xdgmimeicon.h
@@ -19,7 +19,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_ICON_H__
@@ -43,8 +45,6 @@ XdgIconList *_xdg_mime_icon_list_new (void);
void _xdg_mime_icon_list_free (XdgIconList *list);
const char *_xdg_mime_icon_list_lookup (XdgIconList *list,
const char *mime);
-#ifdef NOT_USED_IN_GIO
void _xdg_mime_icon_list_dump (XdgIconList *list);
-#endif
#endif /* __XDG_MIME_ICON_H__ */
diff --git a/gio/xdgmime/xdgmimeint.c b/gio/xdgmime/xdgmimeint.c
index 35c3635e2..5e4513c50 100644
--- a/gio/xdgmime/xdgmimeint.c
+++ b/gio/xdgmime/xdgmimeint.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmimeint.h"
#include <ctype.h>
@@ -189,7 +193,7 @@ const char *
_xdg_binary_or_text_fallback(const void *data, size_t len)
{
unsigned char *chardata;
- int i;
+ size_t i;
chardata = (unsigned char *) data;
for (i = 0; i < 128 && i < len; ++i)
diff --git a/gio/xdgmime/xdgmimeint.h b/gio/xdgmime/xdgmimeint.h
index c9270139e..9a8256d17 100644
--- a/gio/xdgmime/xdgmimeint.h
+++ b/gio/xdgmime/xdgmimeint.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_INT_H__
diff --git a/gio/xdgmime/xdgmimemagic.c b/gio/xdgmime/xdgmimemagic.c
index ea986652d..dcee0fd59 100644
--- a/gio/xdgmime/xdgmimemagic.c
+++ b/gio/xdgmime/xdgmimemagic.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <assert.h>
#include "xdgmimemagic.h"
@@ -318,7 +322,7 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file,
int c;
int end_of_file;
int indent = 0;
- int bytes_read;
+ size_t bytes_read;
assert (magic_file != NULL);
@@ -410,7 +414,7 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file,
return XDG_MIME_MAGIC_ERROR;
}
bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
- if (bytes_read != matchlet->value_length)
+ if (bytes_read != (size_t) matchlet->value_length)
{
_xdg_mime_magic_matchlet_free (matchlet);
if (feof (magic_file))
@@ -430,7 +434,7 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file,
return XDG_MIME_MAGIC_ERROR;
}
bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
- if (bytes_read != matchlet->value_length)
+ if (bytes_read != (size_t) matchlet->value_length)
{
_xdg_mime_magic_matchlet_free (matchlet);
if (feof (magic_file))
@@ -468,7 +472,7 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file,
_xdg_mime_magic_matchlet_free (matchlet);
return XDG_MIME_MAGIC_EOF;
}
- if (matchlet->range_length == -1)
+ if (matchlet->range_length == (unsigned int) -1)
{
_xdg_mime_magic_matchlet_free (matchlet);
return XDG_MIME_MAGIC_ERROR;
@@ -483,7 +487,7 @@ _xdg_mime_magic_parse_magic_line (FILE *magic_file,
if (matchlet->word_size > 1)
{
#if LITTLE_ENDIAN
- int i;
+ unsigned int i;
#endif
if (matchlet->value_length % matchlet->word_size != 0)
{
@@ -529,7 +533,7 @@ _xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
const void *data,
size_t len)
{
- int i, j;
+ unsigned int i, j;
for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++)
{
int valid_matchlet = TRUE;
diff --git a/gio/xdgmime/xdgmimemagic.h b/gio/xdgmime/xdgmimemagic.h
index c990acee8..eb06a81f8 100644
--- a/gio/xdgmime/xdgmimemagic.h
+++ b/gio/xdgmime/xdgmimemagic.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_MAGIC_H__
diff --git a/gio/xdgmime/xdgmimeparent.c b/gio/xdgmime/xdgmimeparent.c
index b06b749d4..89b48fc16 100644
--- a/gio/xdgmime/xdgmimeparent.c
+++ b/gio/xdgmime/xdgmimeparent.c
@@ -20,10 +20,14 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "xdgmimeparent.h"
#include "xdgmimeint.h"
@@ -196,7 +200,6 @@ _xdg_mime_parent_read_from_file (XdgParentList *list,
sizeof (XdgMimeParents), &parent_entry_cmp);
}
-#ifdef NOT_USED_IN_GIO
void
_xdg_mime_parent_list_dump (XdgParentList *list)
@@ -214,4 +217,4 @@ _xdg_mime_parent_list_dump (XdgParentList *list)
}
}
-#endif
+
diff --git a/gio/xdgmime/xdgmimeparent.h b/gio/xdgmime/xdgmimeparent.h
index e3cdad5bf..29f43bc1f 100644
--- a/gio/xdgmime/xdgmimeparent.h
+++ b/gio/xdgmime/xdgmimeparent.h
@@ -20,7 +20,9 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
*/
#ifndef __XDG_MIME_PARENT_H__
@@ -44,8 +46,6 @@ XdgParentList *_xdg_mime_parent_list_new (void);
void _xdg_mime_parent_list_free (XdgParentList *list);
const char **_xdg_mime_parent_list_lookup (XdgParentList *list,
const char *mime);
-#ifdef NOT_USED_IN_GIO
void _xdg_mime_parent_list_dump (XdgParentList *list);
-#endif
#endif /* __XDG_MIME_PARENT_H__ */