summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2021-10-29 10:31:47 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2021-10-29 10:31:47 +0900
commit171ab2d5613fb4a84f9c599300f4ddbdd5dc18f7 (patch)
tree5f829ef35765afca84a0cc556acaee9de428d5f8 /gio
parentc56d78b101fcc361d6cd5467c59aed4ec78c44af (diff)
downloadglib-171ab2d5613fb4a84f9c599300f4ddbdd5dc18f7.tar.gz
glib-171ab2d5613fb4a84f9c599300f4ddbdd5dc18f7.tar.bz2
glib-171ab2d5613fb4a84f9c599300f4ddbdd5dc18f7.zip
Imported Upstream version 2.67.2upstream/2.67.2
Diffstat (limited to 'gio')
-rw-r--r--gio/completion/gio15
-rw-r--r--gio/gapplicationimpl-dbus.c11
-rw-r--r--gio/gbufferedinputstream.c8
-rw-r--r--gio/gbufferedoutputstream.c2
-rw-r--r--gio/gcancellable.c1
-rw-r--r--gio/gcontextspecificgroup.c3
-rw-r--r--gio/gcredentials.c48
-rw-r--r--gio/gdatainputstream.c4
-rw-r--r--gio/gdbus-2.0/codegen/codegen.py86
-rw-r--r--gio/gdbus-2.0/codegen/codegen_main.py4
-rw-r--r--gio/gdbusaddress.c18
-rw-r--r--gio/gdbusconnection.c41
-rw-r--r--gio/gdbusconnection.h8
-rw-r--r--gio/gdbuserror.c9
-rw-r--r--gio/gdesktopappinfo.c45
-rw-r--r--gio/gfile.c12
-rw-r--r--gio/gfileattribute.c4
-rw-r--r--gio/gfileinfo.c29
-rw-r--r--gio/gicon.c2
-rw-r--r--gio/gio-tool-info.c15
-rw-r--r--gio/gio-tool-launch.c131
-rw-r--r--gio/gio-tool-trash.c170
-rw-r--r--gio/gio-tool.c3
-rw-r--r--gio/gio-tool.h1
-rw-r--r--gio/giomodule.c41
-rw-r--r--gio/gliststore.c4
-rw-r--r--gio/glocalfile.c59
-rw-r--r--gio/gosxappinfo.h2
-rw-r--r--gio/gosxappinfo.m31
-rw-r--r--gio/gresource.c46
-rw-r--r--gio/gsettingsschema.c6
-rw-r--r--gio/gsocket.c34
-rw-r--r--gio/gtlsdatabase.c2
-rw-r--r--gio/gvdb/gvdb-builder.c8
-rw-r--r--gio/gvdb/gvdb-reader.c2
-rwxr-xr-xgio/gwin32api-application-activation-manager.h126
-rwxr-xr-xgio/gwin32api-iterator.h125
-rwxr-xr-xgio/gwin32api-misc.h1
-rwxr-xr-xgio/gwin32api-package.h264
-rwxr-xr-xgio/gwin32api-storage.h339
-rw-r--r--gio/gwin32appinfo.c1117
-rwxr-xr-xgio/gwin32file-sync-stream.c508
-rwxr-xr-xgio/gwin32file-sync-stream.h44
-rw-r--r--gio/gwin32mount.c8
-rwxr-xr-xgio/gwin32packageparser.c815
-rwxr-xr-xgio/gwin32packageparser.h48
-rw-r--r--gio/gwin32registrykey.c2
-rw-r--r--gio/gwin32volumemonitor.c24
-rw-r--r--gio/inotify/inotify-kernel.c4
-rw-r--r--gio/meson.build9
-rw-r--r--gio/tests/socket.c218
-rw-r--r--gio/win32/gwinhttpvfs.c14
52 files changed, 4256 insertions, 315 deletions
diff --git a/gio/completion/gio b/gio/completion/gio
index 75473d255..c650475b9 100644
--- a/gio/completion/gio
+++ b/gio/completion/gio
@@ -22,7 +22,8 @@
# Check whether the suggestions have common prefix (i.e. suggestions won't be
# shown and prefix will be completed first)
-__has_common_prefix() {
+__gio_has_common_prefix() {
+ local i
for (( i = 1; i < ${#COMPREPLY[@]}; i++ )); do
if [[ "${COMPREPLY[i-1]:${#cur}:1}" != "${COMPREPLY[i]:${#cur}:1}" ]]; then
return 1 # False
@@ -49,6 +50,7 @@ __gio_location() {
# List volumes and mounts
local mounts=( )
+ local mount
while IFS=$'\n' read mount; do
# Do not care about local mounts
[[ "$mount" =~ ^"file:" ]] && continue
@@ -58,12 +60,13 @@ __gio_location() {
done < <(gio mount -li | sed -n -r 's/^ *(default_location|activation_root)=(.*)$/\2/p' | sort -u)
# Workaround to unescape dir name (e.g. "\ " -> " ")
- declare -a tmp="( ${dir} )"
- unescaped_dir="${tmp[0]}"
+ local -a tmp="( ${dir} )"
+ local unescaped_dir="${tmp[0]}"
# List files
local files=()
local names=()
+ local name size type
while IFS=$'\t' read name size type; do
# Escape name properly
local escaped_name="$(printf "%q" "$name")"
@@ -75,7 +78,7 @@ __gio_location() {
escaped_name="$escaped_name "
fi
- path="$dir$escaped_name"
+ local path="$dir$escaped_name"
# Use only matching paths
if [[ "$path" =~ ^"$cur" ]]; then
@@ -87,7 +90,7 @@ __gio_location() {
COMPREPLY=("${files[@]}" "${mounts[@]}")
# Workaround to show suggestions as basenames only
- if ! __has_common_prefix; then
+ if ! __gio_has_common_prefix; then
COMPREPLY=("${mounts[@]} ${names[@]}")
# Workaround to prevent overwriting suggestions, it adds empty
@@ -105,7 +108,7 @@ __gio_location() {
__gio() {
# Complete subcommands
if (( ${COMP_CWORD} == 1 )); then
- COMPREPLY=($(compgen -W "help version cat copy info list mime mkdir monitor mount move open rename remove save set trash tree" -- "${COMP_WORDS[1]}"))
+ COMPREPLY=($(compgen -W "help version cat copy info launch list mime mkdir monitor mount move open rename remove save set trash tree" -- "${COMP_WORDS[1]}"))
compopt +o nospace
return 0
fi
diff --git a/gio/gapplicationimpl-dbus.c b/gio/gapplicationimpl-dbus.c
index 6f02788a2..263001b8e 100644
--- a/gio/gapplicationimpl-dbus.c
+++ b/gio/gapplicationimpl-dbus.c
@@ -371,6 +371,7 @@ g_application_impl_attempt_primary (GApplicationImpl *impl,
GApplicationFlags app_flags;
GVariant *reply;
guint32 rval;
+ GError *local_error = NULL;
if (org_gtk_Application == NULL)
{
@@ -430,8 +431,14 @@ g_application_impl_attempt_primary (GApplicationImpl *impl,
if (!app_class->dbus_register (impl->app,
impl->session_bus,
impl->object_path,
- error))
- return FALSE;
+ &local_error))
+ {
+ g_return_val_if_fail (local_error != NULL, FALSE);
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return FALSE;
+ }
+
+ g_return_val_if_fail (local_error == NULL, FALSE);
if (impl->bus_name == NULL)
{
diff --git a/gio/gbufferedinputstream.c b/gio/gbufferedinputstream.c
index f5090d064..b0e609c0d 100644
--- a/gio/gbufferedinputstream.c
+++ b/gio/gbufferedinputstream.c
@@ -661,10 +661,10 @@ g_buffered_input_stream_real_fill (GBufferedInputStream *stream,
in_buffer = priv->end - priv->pos;
/* Never fill more than can fit in the buffer */
- count = MIN (count, priv->len - in_buffer);
+ count = MIN ((gsize) count, priv->len - in_buffer);
/* If requested length does not fit at end, compact */
- if (priv->len - priv->end < count)
+ if (priv->len - priv->end < (gsize) count)
compact_buffer (stream);
base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
@@ -1072,10 +1072,10 @@ g_buffered_input_stream_real_fill_async (GBufferedInputStream *stream,
in_buffer = priv->end - priv->pos;
/* Never fill more than can fit in the buffer */
- count = MIN (count, priv->len - in_buffer);
+ count = MIN ((gsize) count, priv->len - in_buffer);
/* If requested length does not fit at end, compact */
- if (priv->len - priv->end < count)
+ if (priv->len - priv->end < (gsize) count)
compact_buffer (stream);
task = g_task_new (stream, cancellable, callback, user_data);
diff --git a/gio/gbufferedoutputstream.c b/gio/gbufferedoutputstream.c
index 98bda501d..969bbae0b 100644
--- a/gio/gbufferedoutputstream.c
+++ b/gio/gbufferedoutputstream.c
@@ -208,7 +208,7 @@ g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
if (priv->buffer)
{
- size = MAX (size, priv->pos);
+ size = (priv->pos > 0) ? MAX (size, (gsize) priv->pos) : size;
buffer = g_malloc (size);
memcpy (buffer, priv->buffer, priv->pos);
diff --git a/gio/gcancellable.c b/gio/gcancellable.c
index 00df6998d..a084282ec 100644
--- a/gio/gcancellable.c
+++ b/gio/gcancellable.c
@@ -770,6 +770,7 @@ static GSourceFuncs cancellable_source_funcs =
cancellable_source_dispatch,
NULL,
(GSourceFunc)cancellable_source_closure_callback,
+ NULL,
};
/**
diff --git a/gio/gcontextspecificgroup.c b/gio/gcontextspecificgroup.c
index ffd1307e4..acad72767 100644
--- a/gio/gcontextspecificgroup.c
+++ b/gio/gcontextspecificgroup.c
@@ -73,7 +73,8 @@ g_context_specific_source_new (const gchar *name,
NULL,
NULL,
g_context_specific_source_dispatch,
- g_context_specific_source_finalize
+ g_context_specific_source_finalize,
+ NULL, NULL
};
GContextSpecificSource *css;
GSource *source;
diff --git a/gio/gcredentials.c b/gio/gcredentials.c
index 9423f9ef9..c5d3d46ac 100644
--- a/gio/gcredentials.c
+++ b/gio/gcredentials.c
@@ -196,7 +196,7 @@ g_credentials_init (GCredentials *credentials)
* Creates a new #GCredentials object with credentials matching the
* the current process.
*
- * Returns: A #GCredentials. Free with g_object_unref().
+ * Returns: (transfer full): A #GCredentials. Free with g_object_unref().
*
* Since: 2.26
*/
@@ -216,7 +216,7 @@ g_credentials_new (void)
* that can be used in logging and debug messages. The format of the
* returned string may change in future GLib release.
*
- * Returns: A string that should be freed with g_free().
+ * Returns: (transfer full): A string that should be freed with g_free().
*
* Since: 2.26
*/
@@ -233,18 +233,18 @@ g_credentials_to_string (GCredentials *credentials)
ret = g_string_new ("GCredentials:");
#if G_CREDENTIALS_USE_LINUX_UCRED
g_string_append (ret, "linux-ucred:");
- if (credentials->native.pid != -1)
+ if (credentials->native.pid != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
- if (credentials->native.uid != -1)
+ if (credentials->native.uid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
- if (credentials->native.gid != -1)
+ if (credentials->native.gid != (gid_t) -1)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
#elif G_CREDENTIALS_USE_APPLE_XUCRED
g_string_append (ret, "apple-xucred:");
g_string_append_printf (ret, "version=%u,", credentials->native.cr_version);
- if (credentials->native.cr_uid != -1)
+ if (credentials->native.cr_uid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cr_uid);
for (i = 0; i < credentials->native.cr_ngroups; i++)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cr_groups[i]);
@@ -252,28 +252,28 @@ g_credentials_to_string (GCredentials *credentials)
ret->str[ret->len - 1] = '\0';
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
g_string_append (ret, "freebsd-cmsgcred:");
- if (credentials->native.cmcred_pid != -1)
+ if (credentials->native.cmcred_pid != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_pid);
- if (credentials->native.cmcred_euid != -1)
+ if (credentials->native.cmcred_euid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_euid);
- if (credentials->native.cmcred_gid != -1)
+ if (credentials->native.cmcred_gid != (gid_t) -1)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_gid);
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
g_string_append (ret, "netbsd-unpcbid:");
- if (credentials->native.unp_pid != -1)
+ if (credentials->native.unp_pid != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_pid);
- if (credentials->native.unp_euid != -1)
+ if (credentials->native.unp_euid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_euid);
- if (credentials->native.unp_egid != -1)
+ if (credentials->native.unp_egid != (gid_t) -1)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_egid);
ret->str[ret->len - 1] = '\0';
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
g_string_append (ret, "openbsd-sockpeercred:");
- if (credentials->native.pid != -1)
+ if (credentials->native.pid != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
- if (credentials->native.uid != -1)
+ if (credentials->native.uid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
- if (credentials->native.gid != -1)
+ if (credentials->native.gid != (gid_t) -1)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.gid);
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
@@ -281,11 +281,11 @@ g_credentials_to_string (GCredentials *credentials)
g_string_append (ret, "solaris-ucred:");
{
id_t id;
- if ((id = ucred_getpid (credentials->native)) != -1)
+ if ((id = ucred_getpid (credentials->native)) != (id_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) id);
- if ((id = ucred_geteuid (credentials->native)) != -1)
+ if ((id = ucred_geteuid (credentials->native)) != (id_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) id);
- if ((id = ucred_getegid (credentials->native)) != -1)
+ if ((id = ucred_getegid (credentials->native)) != (id_t) -1)
g_string_append_printf (ret, "gid=%" G_GINT64_FORMAT ",", (gint64) id);
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
@@ -435,10 +435,10 @@ credentials_native_type_check (GCredentialsType requested_type,
* logged) to use this method if there is no #GCredentials support for
* the OS or if @native_type isn't supported by the OS.
*
- * Returns: The pointer to native credentials or %NULL if the
- * operation there is no #GCredentials support for the OS or if
- * @native_type isn't supported by the OS. Do not free the returned
- * data, it is owned by @credentials.
+ * Returns: (transfer none) (nullable): The pointer to native credentials or
+ * %NULL if there is no #GCredentials support for the OS or if @native_type
+ * isn't supported by the OS. Do not free the returned data, it is owned
+ * by @credentials.
*
* Since: 2.26
*/
@@ -507,7 +507,7 @@ g_credentials_set_native (GCredentials *credentials,
* OS or if the native credentials type does not contain information
* about the UNIX user.
*
- * Returns: The UNIX user identifier or -1 if @error is set.
+ * Returns: The UNIX user identifier or `-1` if @error is set.
*
* Since: 2.26
*/
@@ -572,7 +572,7 @@ g_credentials_get_unix_user (GCredentials *credentials,
* about the UNIX process ID (for example this is the case for
* %G_CREDENTIALS_TYPE_APPLE_XUCRED).
*
- * Returns: The UNIX process ID, or -1 if @error is set.
+ * Returns: The UNIX process ID, or `-1` if @error is set.
*
* Since: 2.36
*/
diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c
index 2e7750cb5..4440eb41f 100644
--- a/gio/gdatainputstream.c
+++ b/gio/gdatainputstream.c
@@ -631,7 +631,7 @@ scan_for_newline (GDataInputStream *stream,
GDataInputStreamPrivate *priv;
const char *buffer;
gsize start, end, peeked;
- int i;
+ gsize i;
gssize found_pos;
int newline_len;
gsize available, checked;
@@ -861,7 +861,7 @@ scan_for_chars (GDataInputStream *stream,
GBufferedInputStream *bstream;
const char *buffer;
gsize start, end, peeked;
- int i;
+ gsize i;
gsize available, checked;
const char *stop_char;
const char *stop_end;
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index 04bf44a71..9d28cb77d 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -392,7 +392,7 @@ class HeaderCodeGenerator:
self.outfile.write("G_GNUC_DEPRECATED ")
self.outfile.write(
"void %s_set_%s (%s *object, %svalue);\n"
- % (i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in,)
+ % (i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in)
)
self.outfile.write("\n")
@@ -1975,10 +1975,11 @@ class CodeGenerator:
)
self.write_gtkdoc_deprecated_and_since_and_close(i, self.outfile, 0)
self.outfile.write(
- "guint\n"
- "%s_override_properties (GObjectClass *klass, guint property_id_begin)\n"
- "{\n" % (i.name_lower)
+ "guint\n" "%s_override_properties (GObjectClass *klass" % (i.name_lower)
)
+ if len(i.properties) == 0:
+ self.outfile.write(" G_GNUC_UNUSED")
+ self.outfile.write(", guint property_id_begin)\n" "{\n")
for p in i.properties:
self.outfile.write(
' g_object_class_override_property (klass, property_id_begin++, "%s");\n'
@@ -2065,10 +2066,13 @@ class CodeGenerator:
self.outfile.write(
"static void\n"
- "%s_default_init (%sIface *iface)\n"
- "{\n" % (i.name_lower, i.camel_name)
+ "%s_default_init (%sIface *iface" % (i.name_lower, i.camel_name)
)
-
+ if len(i.methods) == 0 and len(i.signals) == 0 and len(i.properties) == 0:
+ self.outfile.write(" G_GNUC_UNUSED)\n")
+ else:
+ self.outfile.write(")\n")
+ self.outfile.write("{\n")
if len(i.methods) > 0:
self.outfile.write(
" /* GObject signals for incoming D-Bus method calls: */\n"
@@ -2443,7 +2447,7 @@ class CodeGenerator:
self.outfile.write(
"void\n"
"%s_set_%s (%s *object, %svalue)\n"
- "{\n" % (i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in,)
+ "{\n" % (i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in)
)
self.outfile.write(
' g_object_set (G_OBJECT (object), "%s", value, NULL);\n'
@@ -2781,7 +2785,7 @@ class CodeGenerator:
self.outfile.write(
"void\n"
"%s_complete_%s (\n"
- " %s *object,\n"
+ " %s *object G_GNUC_UNUSED,\n"
" GDBusMethodInvocation *invocation"
% (i.name_lower, m.name_lower, i.camel_name)
)
@@ -2906,12 +2910,19 @@ class CodeGenerator:
#
self.outfile.write(
"static void\n"
- "%s_proxy_get_property (GObject *object,\n"
- " guint prop_id,\n"
- " GValue *value,\n"
- " GParamSpec *pspec G_GNUC_UNUSED)\n"
- "{\n" % (i.name_lower)
+ "%s_proxy_get_property (GObject *object" % (i.name_lower)
)
+ if len(i.properties) == 0:
+ self.outfile.write(
+ " G_GNUC_UNUSED,\n"
+ " guint prop_id G_GNUC_UNUSED,\n"
+ " GValue *value G_GNUC_UNUSED,\n"
+ )
+ else:
+ self.outfile.write(
+ ",\n" " guint prop_id,\n" " GValue *value,\n"
+ )
+ self.outfile.write(" GParamSpec *pspec G_GNUC_UNUSED)\n" "{\n")
if len(i.properties) > 0:
self.outfile.write(
" const _ExtendedGDBusPropertyInfo *info;\n"
@@ -2961,14 +2972,20 @@ class CodeGenerator:
" }\n" % (i.name)
)
self.outfile.write("}\n" "\n")
- self.outfile.write(
- "static void\n"
- "%s_proxy_set_property (GObject *object,\n"
- " guint prop_id,\n"
- " const GValue *value,\n"
- " GParamSpec *pspec G_GNUC_UNUSED)\n"
- "{\n" % (i.name_lower)
- )
+ self.outfile.write("static void\n" "%s_proxy_set_property (" % (i.name_lower))
+ if len(i.properties) == 0:
+ self.outfile.write(
+ "GObject *object G_GNUC_UNUSED,\n"
+ " guint prop_id G_GNUC_UNUSED,\n"
+ " const GValue *value G_GNUC_UNUSED,\n"
+ )
+ else:
+ self.outfile.write(
+ "GObject *object,\n"
+ " guint prop_id,\n"
+ " const GValue *value,\n"
+ )
+ self.outfile.write(" GParamSpec *pspec G_GNUC_UNUSED)\n" "{\n")
if len(i.properties) > 0:
self.outfile.write(
" const _ExtendedGDBusPropertyInfo *info;\n"
@@ -3221,9 +3238,13 @@ class CodeGenerator:
self.outfile.write(
"static void\n"
- "%s_proxy_iface_init (%sIface *iface)\n"
- "{\n" % (i.name_lower, i.camel_name)
+ "%s_proxy_iface_init (%sIface *iface" % (i.name_lower, i.camel_name)
)
+ if len(i.properties) == 0:
+ self.outfile.write(" G_GNUC_UNUSED)\n")
+ else:
+ self.outfile.write(")\n")
+ self.outfile.write("{\n")
for p in i.properties:
self.outfile.write(
" iface->get_%s = %s_proxy_get_%s;\n"
@@ -3753,9 +3774,14 @@ class CodeGenerator:
self.outfile.write(
"static void\n"
- "%s_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton)\n"
- "{\n" % (i.name_lower)
+ "%s_skeleton_dbus_interface_flush (GDBusInterfaceSkeleton *_skeleton"
+ % (i.name_lower)
)
+ if len(i.properties) == 0:
+ self.outfile.write(" G_GNUC_UNUSED)\n")
+ else:
+ self.outfile.write(")\n")
+ self.outfile.write("{\n")
if len(i.properties) > 0:
self.outfile.write(
" %sSkeleton *skeleton = %s%s_SKELETON (_skeleton);\n"
@@ -4193,9 +4219,13 @@ class CodeGenerator:
self.outfile.write(
"static void\n"
- "%s_skeleton_iface_init (%sIface *iface)\n"
- "{\n" % (i.name_lower, i.camel_name)
+ "%s_skeleton_iface_init (%sIface *iface" % (i.name_lower, i.camel_name)
)
+ if len(i.signals) == 0 and len(i.properties) == 0:
+ self.outfile.write(" G_GNUC_UNUSED)\n")
+ else:
+ self.outfile.write(")\n")
+ self.outfile.write("{\n")
for s in i.signals:
self.outfile.write(
" iface->%s = _%s_on_signal_%s;\n"
diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py
index dc0177f0f..238d7dd12 100644
--- a/gio/gdbus-2.0/codegen/codegen_main.py
+++ b/gio/gdbus-2.0/codegen/codegen_main.py
@@ -343,7 +343,7 @@ def codegen_main():
parts = args.glib_min_required.split(".", 3)
glib_min_required = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
# Ignore micro component, but still validate it:
- _ = int(parts[2] if len(parts) > 2 else 0)
+ _ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
except (ValueError, IndexError):
print_error(
"Unrecognized --glib-min-required string ‘{}’".format(
@@ -365,7 +365,7 @@ def codegen_main():
parts = args.glib_max_allowed.split(".", 3)
glib_max_allowed = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
# Ignore micro component, but still validate it:
- _ = int(parts[2] if len(parts) > 2 else 0)
+ _ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
except (ValueError, IndexError):
print_error(
"Unrecognized --glib-max-allowed string ‘{}’".format(
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
index 3dd3cc84b..d26c4d25f 100644
--- a/gio/gdbusaddress.c
+++ b/gio/gdbusaddress.c
@@ -30,6 +30,7 @@
#include "gdbusaddress.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
+#include "glib-private.h"
#include "gnetworkaddress.h"
#include "gsocketclient.h"
#include "giostream.h"
@@ -903,11 +904,14 @@ g_dbus_address_get_stream (const gchar *address,
/**
* g_dbus_address_get_stream_finish:
* @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
- * @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
+ * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
* @error: Return location for error or %NULL.
*
* Finishes an operation started with g_dbus_address_get_stream().
*
+ * A server is not required to set a GUID, so @out_guid may be set to %NULL
+ * even on success.
+ *
* Returns: (transfer full): A #GIOStream or %NULL if @error is set.
*
* Since: 2.26
@@ -940,7 +944,7 @@ g_dbus_address_get_stream_finish (GAsyncResult *res,
/**
* g_dbus_address_get_stream_sync:
* @address: A valid D-Bus address.
- * @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
+ * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
* @cancellable: (nullable): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
@@ -949,6 +953,9 @@ g_dbus_address_get_stream_finish (GAsyncResult *res,
* of the D-Bus authentication conversation. @address must be in the
* [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
*
+ * A server is not required to set a GUID, so @out_guid may be set to %NULL
+ * even on success.
+ *
* This is a synchronous failable function. See
* g_dbus_address_get_stream() for the asynchronous version.
*
@@ -1279,6 +1286,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error)
{
+ gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
gchar *ret, *s = NULL;
const gchar *starter_bus;
GError *local_error;
@@ -1317,10 +1325,12 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
_g_dbus_debug_print_unlock ();
}
+ /* Don’t load the addresses from the environment if running as setuid, as they
+ * come from an unprivileged caller. */
switch (bus_type)
{
case G_BUS_TYPE_SYSTEM:
- ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
+ ret = !is_setuid ? g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS")) : NULL;
if (ret == NULL)
{
ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
@@ -1328,7 +1338,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
break;
case G_BUS_TYPE_SESSION:
- ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
+ ret = !is_setuid ? g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")) : NULL;
if (ret == NULL)
{
ret = get_session_address_platform_specific (&local_error);
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 65939a4d2..5c5ef1718 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -885,7 +885,7 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass)
*
* If you are constructing a #GDBusConnection and pass
* %G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER in the
- * #GDBusConnection:flags property then you MUST also set this
+ * #GDBusConnection:flags property then you **must** also set this
* property to a valid guid.
*
* If you are constructing a #GDBusConnection and pass
@@ -1102,7 +1102,7 @@ g_dbus_connection_init (GDBusConnection *connection)
* stream from a worker thread, so it is not safe to interact with
* the stream directly.
*
- * Returns: (transfer none): the stream used for IO
+ * Returns: (transfer none) (not nullable): the stream used for IO
*
* Since: 2.26
*/
@@ -2732,7 +2732,7 @@ g_dbus_connection_new (GIOStream *stream,
*
* Finishes an operation started with g_dbus_connection_new().
*
- * Returns: a #GDBusConnection or %NULL if @error is set. Free
+ * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set. Free
* with g_object_unref().
*
* Since: 2.26
@@ -2784,7 +2784,8 @@ g_dbus_connection_new_finish (GAsyncResult *res,
* This is a synchronous failable constructor. See
* g_dbus_connection_new() for the asynchronous version.
*
- * Returns: a #GDBusConnection or %NULL if @error is set. Free with g_object_unref().
+ * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set.
+ * Free with g_object_unref().
*
* Since: 2.26
*/
@@ -2875,8 +2876,8 @@ g_dbus_connection_new_for_address (const gchar *address,
*
* Finishes an operation started with g_dbus_connection_new_for_address().
*
- * Returns: a #GDBusConnection or %NULL if @error is set. Free with
- * g_object_unref().
+ * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set.
+ * Free with g_object_unref().
*
* Since: 2.26
*/
@@ -2927,8 +2928,8 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res,
* If @observer is not %NULL it may be used to control the
* authentication process.
*
- * Returns: a #GDBusConnection or %NULL if @error is set. Free with
- * g_object_unref().
+ * Returns: (transfer full): a #GDBusConnection or %NULL if @error is set.
+ * Free with g_object_unref().
*
* Since: 2.26
*/
@@ -3017,7 +3018,7 @@ g_dbus_connection_get_exit_on_close (GDBusConnection *connection)
* The GUID of the peer performing the role of server when
* authenticating. See #GDBusConnection:guid for more details.
*
- * Returns: The GUID. Do not free this string, it is owned by
+ * Returns: (not nullable): The GUID. Do not free this string, it is owned by
* @connection.
*
* Since: 2.26
@@ -5510,7 +5511,7 @@ register_with_closures_on_set_property (GDBusConnection *connection,
* Version of g_dbus_connection_register_object() using closures instead of a
* #GDBusInterfaceVTable for easier binding in other languages.
*
- * Returns: 0 if @error is set, otherwise a registration id (never 0)
+ * Returns: 0 if @error is set, otherwise a registration ID (never 0)
* that can be used with g_dbus_connection_unregister_object() .
*
* Since: 2.46
@@ -6139,8 +6140,8 @@ g_dbus_connection_call (GDBusConnection *connection,
*
* Finishes an operation started with g_dbus_connection_call().
*
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
+ * Returns: (transfer full): %NULL if @error is set. Otherwise a non-floating
+ * #GVariant tuple with return values. Free with g_variant_unref().
*
* Since: 2.26
*/
@@ -6206,8 +6207,8 @@ g_dbus_connection_call_finish (GDBusConnection *connection,
* g_dbus_connection_call() for the asynchronous version of
* this method.
*
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
+ * Returns: (transfer full): %NULL if @error is set. Otherwise a non-floating
+ * #GVariant tuple with return values. Free with g_variant_unref().
*
* Since: 2.26
*/
@@ -6309,8 +6310,8 @@ g_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection,
* access file descriptors if they are referenced in this way by a
* value of type %G_VARIANT_TYPE_HANDLE in the body of the message.
*
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
+ * Returns: (transfer full): %NULL if @error is set. Otherwise a non-floating
+ * #GVariant tuple with return values. Free with g_variant_unref().
*
* Since: 2.30
*/
@@ -6348,8 +6349,8 @@ g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection,
*
* This method is only available on UNIX.
*
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
+ * Returns: (transfer full): %NULL if @error is set. Otherwise a non-floating
+ * #GVariant tuple with return values. Free with g_variant_unref().
*
* Since: 2.30
*/
@@ -6859,8 +6860,8 @@ subtree_message_func (GDBusConnection *connection,
* See this [server][gdbus-subtree-server] for an example of how to use
* this method.
*
- * Returns: 0 if @error is set, otherwise a subtree registration id (never 0)
- * that can be used with g_dbus_connection_unregister_subtree() .
+ * Returns: 0 if @error is set, otherwise a subtree registration ID (never 0)
+ * that can be used with g_dbus_connection_unregister_subtree()
*
* Since: 2.26
*/
diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h
index 05ef384e4..4bd3e9a4b 100644
--- a/gio/gdbusconnection.h
+++ b/gio/gdbusconnection.h
@@ -432,11 +432,11 @@ gboolean g_dbus_connection_unregister_object (GDBusConnection
* specified (ie: to verify that the object path is valid).
*
* Hierarchies are not supported; the items that you return should not
- * contain the '/' character.
+ * contain the `/` character.
*
* The return value will be freed with g_strfreev().
*
- * Returns: A newly allocated array of strings for node names that are children of @object_path.
+ * Returns: (array zero-terminated=1) (transfer full): A newly allocated array of strings for node names that are children of @object_path.
*
* Since: 2.26
*/
@@ -472,7 +472,7 @@ typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection,
* remote introspector in the empty array case, but not in the %NULL
* case.
*
- * Returns: A %NULL-terminated array of pointers to #GDBusInterfaceInfo, or %NULL.
+ * Returns: (array zero-terminated=1) (nullable) (transfer full): A %NULL-terminated array of pointers to #GDBusInterfaceInfo, or %NULL.
*
* Since: 2.26
*/
@@ -497,7 +497,7 @@ typedef GDBusInterfaceInfo ** (*GDBusSubtreeIntrospectFunc) (GDBusConnection
* Subtrees are flat. @node, if non-%NULL, is always exactly one
* segment of the object path (ie: it never contains a slash).
*
- * Returns: A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods.
+ * Returns: (nullable): A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods.
*
* Since: 2.26
*/
diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c
index 4ad97bb6e..b3ea28f5a 100644
--- a/gio/gdbuserror.c
+++ b/gio/gdbuserror.c
@@ -512,8 +512,8 @@ g_dbus_error_is_remote_error (const GError *error)
* (e.g. g_dbus_connection_call_finish()) unless
* g_dbus_error_strip_remote_error() has been used on @error.
*
- * Returns: an allocated string or %NULL if the D-Bus error name
- * could not be found. Free with g_free().
+ * Returns: (nullable) (transfer full): an allocated string or %NULL if the
+ * D-Bus error name could not be found. Free with g_free().
*
* Since: 2.26
*/
@@ -600,7 +600,7 @@ g_dbus_error_get_remote_error (const GError *error)
* #GError instances for applications. Regular applications should not use
* it.
*
- * Returns: An allocated #GError. Free with g_error_free().
+ * Returns: (transfer full): An allocated #GError. Free with g_error_free().
*
* Since: 2.26
*/
@@ -810,7 +810,8 @@ g_dbus_error_strip_remote_error (GError *error)
* This function is typically only used in object mappings to put a
* #GError on the wire. Regular applications should not use it.
*
- * Returns: A D-Bus error name (never %NULL). Free with g_free().
+ * Returns: (transfer full) (not nullable): A D-Bus error name (never %NULL).
+ * Free with g_free().
*
* Since: 2.26
*/
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 6b9185447..c10e1fc18 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -305,6 +305,27 @@ desktop_file_dir_app_name_is_masked (DesktopFileDir *dir,
return FALSE;
}
+/* Not much to go on from https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+ * so validate it as a non-empty alphanumeric ASCII string with `-` and `_` allowed.
+ *
+ * Validation is important as the desktop IDs are used to construct filenames,
+ * and may be set by an unprivileged caller if running in a setuid program. */
+static gboolean
+validate_xdg_desktop (const gchar *desktop)
+{
+ gsize i;
+
+ for (i = 0; desktop[i] != '\0'; i++)
+ if (desktop[i] != '-' && desktop[i] != '_' &&
+ !g_ascii_isalnum (desktop[i]))
+ return FALSE;
+
+ if (i == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
static const gchar * const *
get_lowercase_current_desktops (void)
{
@@ -320,12 +341,22 @@ get_lowercase_current_desktops (void)
if (envvar)
{
gint i, j;
+ gsize tmp_len;
tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
+ tmp_len = g_strv_length (tmp);
for (i = 0; tmp[i]; i++)
- for (j = 0; tmp[i][j]; j++)
- tmp[i][j] = g_ascii_tolower (tmp[i][j]);
+ {
+ /* If the desktop is invalid, drop it and shift the following
+ * ones (and trailing %NULL) up. */
+ if (!validate_xdg_desktop (tmp[i]))
+ memmove (tmp + i, tmp + i + 1, tmp_len - i);
+
+ /* Convert to lowercase. */
+ for (j = 0; tmp[i][j]; j++)
+ tmp[i][j] = g_ascii_tolower (tmp[i][j]);
+ }
}
else
tmp = g_new0 (gchar *, 0 + 1);
@@ -344,6 +375,7 @@ get_current_desktops (const gchar *value)
if (g_once_init_enter (&result))
{
gchar **tmp;
+ gsize tmp_len, i;
if (!value)
value = g_getenv ("XDG_CURRENT_DESKTOP");
@@ -352,6 +384,15 @@ get_current_desktops (const gchar *value)
value = "";
tmp = g_strsplit (value, ":", 0);
+ tmp_len = g_strv_length (tmp);
+
+ for (i = 0; tmp[i]; i++)
+ {
+ /* If the desktop is invalid, drop it and shift the following
+ * ones (and trailing %NULL) up. */
+ if (!validate_xdg_desktop (tmp[i]))
+ memmove (tmp + i, tmp + i + 1, tmp_len - i);
+ }
g_once_init_leave (&result, tmp);
}
diff --git a/gio/gfile.c b/gio/gfile.c
index f92c07612..b3acc25f1 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -456,11 +456,14 @@ g_file_has_uri_scheme (GFile *file,
* ]|
* Common schemes include "file", "http", "ftp", etc.
*
+ * The scheme can be different from the one used to construct the #GFile,
+ * in that it might be replaced with one that is logically equivalent to the #GFile.
+ *
* This call does no blocking I/O.
*
- * Returns: a string containing the URI scheme for the given
- * #GFile. The returned string should be freed with g_free()
- * when no longer needed.
+ * 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.
*/
char *
g_file_get_uri_scheme (GFile *file)
@@ -611,7 +614,8 @@ g_file_peek_path (GFile *file)
*
* This call does no blocking I/O.
*
- * Returns: a string containing the #GFile's URI.
+ * 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.
*/
diff --git a/gio/gfileattribute.c b/gio/gfileattribute.c
index a38ac09d5..8075d1d53 100644
--- a/gio/gfileattribute.c
+++ b/gio/gfileattribute.c
@@ -274,8 +274,8 @@ valid_char (char c)
static char *
escape_byte_string (const char *str)
{
- size_t len;
- int num_invalid, i;
+ size_t i, len;
+ int num_invalid;
char *escaped_val, *p;
unsigned char c;
const char hex_digits[] = "0123456789abcdef";
diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c
index 1b008f79a..42cdc7a5d 100644
--- a/gio/gfileinfo.c
+++ b/gio/gfileinfo.c
@@ -318,7 +318,7 @@ static void
g_file_info_finalize (GObject *object)
{
GFileInfo *info;
- int i;
+ guint i;
GFileAttribute *attrs;
info = G_FILE_INFO (object);
@@ -376,7 +376,7 @@ g_file_info_copy_into (GFileInfo *src_info,
GFileInfo *dest_info)
{
GFileAttribute *source, *dest;
- int i;
+ guint i;
g_return_if_fail (G_IS_FILE_INFO (src_info));
g_return_if_fail (G_IS_FILE_INFO (dest_info));
@@ -439,7 +439,7 @@ g_file_info_set_attribute_mask (GFileInfo *info,
GFileAttributeMatcher *mask)
{
GFileAttribute *attr;
- int i;
+ guint i;
g_return_if_fail (G_IS_FILE_INFO (info));
@@ -491,7 +491,7 @@ void
g_file_info_clear_status (GFileInfo *info)
{
GFileAttribute *attrs;
- int i;
+ guint i;
g_return_if_fail (G_IS_FILE_INFO (info));
@@ -536,7 +536,7 @@ g_file_info_find_value (GFileInfo *info,
guint32 attr_id)
{
GFileAttribute *attrs;
- int i;
+ guint i;
i = g_file_info_find_place (info, attr_id);
attrs = (GFileAttribute *)info->attributes->data;
@@ -599,7 +599,7 @@ g_file_info_has_namespace (GFileInfo *info,
{
GFileAttribute *attrs;
guint32 ns_id;
- int i;
+ guint i;
g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
g_return_val_if_fail (name_space != NULL, FALSE);
@@ -636,7 +636,7 @@ g_file_info_list_attributes (GFileInfo *info,
GFileAttribute *attrs;
guint32 attribute;
guint32 ns_id = (name_space) ? lookup_namespace (name_space) : 0;
- int i;
+ guint i;
g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
@@ -694,7 +694,7 @@ g_file_info_remove_attribute (GFileInfo *info,
{
guint32 attr_id;
GFileAttribute *attrs;
- int i;
+ guint i;
g_return_if_fail (G_IS_FILE_INFO (info));
g_return_if_fail (attribute != NULL && *attribute != '\0');
@@ -734,6 +734,9 @@ g_file_info_get_attribute_data (GFileInfo *info,
{
GFileAttributeValue *value;
+ g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+ g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
value = g_file_info_find_value_by_name (info, attribute);
if (value == NULL)
return FALSE;
@@ -1072,7 +1075,7 @@ g_file_info_create_value (GFileInfo *info,
guint32 attr_id)
{
GFileAttribute *attrs;
- int i;
+ guint i;
if (info->mask != NO_ATTRIBUTE_MASK &&
!_g_file_attribute_matcher_matches_id (info->mask, attr_id))
@@ -2616,7 +2619,7 @@ matcher_matches_id (GFileAttributeMatcher *matcher,
guint32 id)
{
SubMatcher *sub_matchers;
- int i;
+ guint i;
if (matcher->sub_matchers)
{
@@ -2693,8 +2696,8 @@ g_file_attribute_matcher_enumerate_namespace (GFileAttributeMatcher *matcher,
const char *ns)
{
SubMatcher *sub_matchers;
- int ns_id;
- int i;
+ guint ns_id;
+ guint i;
g_return_val_if_fail (ns != NULL && *ns != '\0', FALSE);
@@ -2735,7 +2738,7 @@ g_file_attribute_matcher_enumerate_namespace (GFileAttributeMatcher *matcher,
const char *
g_file_attribute_matcher_enumerate_next (GFileAttributeMatcher *matcher)
{
- int i;
+ guint i;
SubMatcher *sub_matcher;
/* We return a NULL matcher for an empty match string, so handle this */
diff --git a/gio/gicon.c b/gio/gicon.c
index f1ba0e2dc..29fae1068 100644
--- a/gio/gicon.c
+++ b/gio/gicon.c
@@ -138,7 +138,7 @@ g_icon_to_string_tokenized (GIcon *icon, GString *s)
GPtrArray *tokens;
gint version;
GIconIface *icon_iface;
- int i;
+ guint i;
g_return_val_if_fail (icon != NULL, FALSE);
g_return_val_if_fail (G_IS_ICON (icon), FALSE);
diff --git a/gio/gio-tool-info.c b/gio/gio-tool-info.c
index 7cf568370..a06263545 100644
--- a/gio/gio-tool-info.c
+++ b/gio/gio-tool-info.c
@@ -182,7 +182,8 @@ show_info (GFile *file, GFileInfo *info)
gchar *root_string = NULL;
gchar *mount;
gchar *fs;
- gchar *options;
+ const gchar *options;
+ gchar *options_string = NULL;
device = g_strescape (g_unix_mount_get_device_path (entry), NULL);
root = g_unix_mount_get_root_path (entry);
@@ -194,16 +195,22 @@ show_info (GFile *file, GFileInfo *info)
}
mount = g_strescape (g_unix_mount_get_mount_path (entry), NULL);
fs = g_strescape (g_unix_mount_get_fs_type (entry), NULL);
- options = g_strescape (g_unix_mount_get_options (entry), NULL);
+
+ options = g_unix_mount_get_options (entry);
+ if (options != NULL)
+ {
+ options_string = g_strescape (options, NULL);
+ }
g_print (_("unix mount: %s%s %s %s %s\n"), device,
- root_string ? root_string : "", mount, fs, options);
+ root_string ? root_string : "", mount, fs,
+ options_string ? options_string : "");
g_free (device);
g_free (root_string);
g_free (mount);
g_free (fs);
- g_free (options);
+ g_free (options_string);
g_unix_mount_free (entry);
}
diff --git a/gio/gio-tool-launch.c b/gio/gio-tool-launch.c
new file mode 100644
index 000000000..08c91c68a
--- /dev/null
+++ b/gio/gio-tool-launch.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 Frederic Martinsons
+ *
+ * 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: Frederic Martinsons <frederic.martinsons@sigfox.com>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
+#include <gio/gdesktopappinfo.h>
+#endif
+
+#include <gi18n.h>
+
+#include "gio-tool.h"
+
+static const GOptionEntry entries[] = {
+ { NULL }
+};
+
+int
+handle_launch (int argc, char *argv[], gboolean do_help)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
+ int i;
+ GAppInfo *app = NULL;
+ GAppLaunchContext *app_context = NULL;
+ GKeyFile *keyfile = NULL;
+ GList *args = NULL;
+ char *desktop_file = NULL;
+#endif
+ int retval;
+
+ g_set_prgname ("gio launch");
+
+ /* Translators: commandline placeholder */
+ context = g_option_context_new (_("DESKTOP-FILE [FILE-ARG …]"));
+ g_option_context_set_help_enabled (context, FALSE);
+ g_option_context_set_summary (context,
+ _("Launch an application from a desktop file, passing optional filename arguments to it."));
+ g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+ if (do_help)
+ {
+ show_help (context, NULL);
+ g_option_context_free (context);
+ return 0;
+ }
+
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ show_help (context, error->message);
+ g_error_free (error);
+ g_option_context_free (context);
+ return 1;
+ }
+
+ if (argc < 2)
+ {
+ show_help (context, _("No desktop file given"));
+ g_option_context_free (context);
+ return 1;
+ }
+
+ g_option_context_free (context);
+
+#if !defined(G_OS_UNIX) || defined(HAVE_COCOA)
+ print_error (_("The launch command is not currently supported on this platform"));
+ retval = 1;
+#else
+ retval = 0;
+ desktop_file = argv[1];
+
+ /* Use keyfile api for loading desktop app in order to check for
+ * - not existing file.
+ * - invalid keyfile format.
+ */
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (keyfile, desktop_file, G_KEY_FILE_NONE, &error))
+ {
+ print_error (_("Unable to load ‘%s‘: %s"), desktop_file, error->message);
+ g_clear_error (&error);
+ retval = 1;
+ }
+ else
+ {
+ app = (GAppInfo*)g_desktop_app_info_new_from_keyfile (keyfile);
+ if (!app)
+ {
+ print_error (_("Unable to load application information for ‘%s‘"), desktop_file);
+ retval = 1;
+ }
+ else
+ {
+ for (i = 2; i < argc; i++)
+ {
+ args = g_list_append (args, g_file_new_for_commandline_arg (argv[i]));
+ }
+ app_context = g_app_launch_context_new ();
+ if (!g_app_info_launch (app, args, app_context, &error))
+ {
+ print_error (_("Unable to launch application ‘%s’: %s"), desktop_file, error->message);
+ g_clear_error (&error);
+ retval = 1;
+ }
+ g_list_free_full (args, g_object_unref);
+ g_clear_object (&app_context);
+ }
+ g_clear_object (&app);
+ }
+ g_key_file_free (keyfile);
+#endif
+ return retval;
+}
diff --git a/gio/gio-tool-trash.c b/gio/gio-tool-trash.c
index 3be63a9b7..fc17b4cba 100644
--- a/gio/gio-tool-trash.c
+++ b/gio/gio-tool-trash.c
@@ -27,9 +27,14 @@
static gboolean force = FALSE;
static gboolean empty = FALSE;
+static gboolean restore = FALSE;
+static gboolean list = FALSE;
static const GOptionEntry entries[] = {
{ "force", 'f', 0, G_OPTION_ARG_NONE, &force, N_("Ignore nonexistent files, never prompt"), NULL },
{ "empty", 0, 0, G_OPTION_ARG_NONE, &empty, N_("Empty the trash"), NULL },
+ { "list", 0, 0, G_OPTION_ARG_NONE, &list, N_("List files in the trash with their original locations"), NULL },
+ { "restore", 0, 0, G_OPTION_ARG_NONE, &restore, N_("Restore a file from trash to its original location (possibly "
+ "recreating the directory)"), NULL },
{ NULL }
};
@@ -75,6 +80,131 @@ delete_trash_file (GFile *file, gboolean del_file, gboolean del_children)
g_file_delete (file, NULL, NULL);
}
+static gboolean
+restore_trash (GFile *file,
+ gboolean force,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileInfo *info = NULL;
+ GFile *target = NULL;
+ GFile *dir_target = NULL;
+ gboolean ret = FALSE;
+ gchar *orig_path = NULL;
+ GError *local_error = NULL;
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NONE, cancellable, &local_error);
+ if (local_error)
+ {
+ g_propagate_error (error, local_error);
+ goto exit_func;
+ }
+
+ orig_path = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
+ if (!orig_path)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Unable to find original path"));
+ goto exit_func;
+ }
+
+ target = g_file_new_for_commandline_arg (orig_path);
+ g_free (orig_path);
+
+ dir_target = g_file_get_parent (target);
+ if (dir_target)
+ {
+ g_file_make_directory_with_parents (dir_target, cancellable, &local_error);
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ g_clear_error (&local_error);
+ }
+ else if (local_error != NULL)
+ {
+ g_propagate_prefixed_error (error, local_error, _("Unable to recreate original location: "));
+ goto exit_func;
+ }
+ }
+
+ if (!g_file_move (file,
+ target,
+ force ? G_FILE_COPY_OVERWRITE : G_FILE_COPY_NONE,
+ cancellable,
+ NULL,
+ NULL,
+ &local_error))
+ {
+ g_propagate_prefixed_error (error, local_error, _("Unable to move file to its original location: "));
+ goto exit_func;
+ }
+ ret = TRUE;
+
+exit_func:
+ g_clear_object (&target);
+ g_clear_object (&dir_target);
+ g_clear_object (&info);
+ return ret;
+}
+
+static gboolean
+trash_list (GFile *file,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileEnumerator *enumerator;
+ GFileInfo *info;
+ GError *local_error = NULL;
+ gboolean res;
+
+ enumerator = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable,
+ &local_error);
+ if (!enumerator)
+ {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ res = TRUE;
+ while ((info = g_file_enumerator_next_file (enumerator, cancellable, &local_error)) != NULL)
+ {
+ const char *name;
+ char *orig_path;
+ char *uri;
+ GFile* child;
+
+ name = g_file_info_get_name (info);
+ child = g_file_get_child (file, name);
+ uri = g_file_get_uri (child);
+ g_object_unref (child);
+ orig_path = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
+
+ g_print ("%s\t%s\n", uri, orig_path);
+
+ g_object_unref (info);
+ g_free (orig_path);
+ g_free (uri);
+ }
+
+ if (local_error)
+ {
+ g_propagate_error (error, local_error);
+ local_error = NULL;
+ res = FALSE;
+ }
+
+ if (!g_file_enumerator_close (enumerator, cancellable, &local_error))
+ {
+ print_file_error (file, local_error->message);
+ g_clear_error (&local_error);
+ res = FALSE;
+ }
+
+ return res;
+}
+
int
handle_trash (int argc, char *argv[], gboolean do_help)
{
@@ -92,7 +222,10 @@ handle_trash (int argc, char *argv[], gboolean do_help)
g_free (param);
g_option_context_set_help_enabled (context, FALSE);
g_option_context_set_summary (context,
- _("Move files or directories to the trash."));
+ _("Move/Restore files or directories to the trash."));
+ g_option_context_set_description (context,
+ _("Note: for --restore switch, if the original location of the trashed file \n"
+ "already exists, it will not be overwritten unless --force is set."));
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
if (do_help)
@@ -118,7 +251,20 @@ handle_trash (int argc, char *argv[], gboolean do_help)
{
file = g_file_new_for_commandline_arg (argv[i]);
error = NULL;
- if (!g_file_trash (file, NULL, &error))
+ if (restore)
+ {
+ if (!g_file_has_uri_scheme (file, "trash"))
+ {
+ print_file_error (file, _("Location given doesn't start with trash:///"));
+ retval = 1;
+ }
+ else if (!restore_trash (file, force, NULL, &error))
+ {
+ print_file_error (file, error->message);
+ retval = 1;
+ }
+ }
+ else if (!g_file_trash (file, NULL, &error))
{
if (!force ||
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -126,13 +272,25 @@ handle_trash (int argc, char *argv[], gboolean do_help)
print_file_error (file, error->message);
retval = 1;
}
- g_error_free (error);
}
+ g_clear_error (&error);
g_object_unref (file);
}
}
-
- if (empty)
+ else if (list)
+ {
+ GFile *file;
+ file = g_file_new_for_uri ("trash:");
+ trash_list (file, NULL, &error);
+ if (error)
+ {
+ print_file_error (file, error->message);
+ g_clear_error (&error);
+ retval = 1;
+ }
+ g_object_unref (file);
+ }
+ else if (empty)
{
GFile *file;
file = g_file_new_for_uri ("trash:");
@@ -140,7 +298,7 @@ handle_trash (int argc, char *argv[], gboolean do_help)
g_object_unref (file);
}
- if (argc == 1 && !empty)
+ if (argc == 1 && !empty && !list)
{
show_help (context, _("No locations given"));
g_option_context_free (context);
diff --git a/gio/gio-tool.c b/gio/gio-tool.c
index c0aec7492..ff82c638e 100644
--- a/gio/gio-tool.c
+++ b/gio/gio-tool.c
@@ -229,6 +229,7 @@ usage (void)
g_printerr (" cat %s\n", _("Concatenate files to standard output"));
g_printerr (" copy %s\n", _("Copy one or more files"));
g_printerr (" info %s\n", _("Show information about locations"));
+ g_printerr (" launch %s\n", _("Launch an application from a desktop file"));
g_printerr (" list %s\n", _("List the contents of locations"));
g_printerr (" mime %s\n", _("Get or set the handler for a mimetype"));
g_printerr (" mkdir %s\n", _("Create directories"));
@@ -312,6 +313,8 @@ main (int argc, char **argv)
return handle_copy (argc, argv, do_help);
else if (g_str_equal (command, "info"))
return handle_info (argc, argv, do_help);
+ else if (g_str_equal (command, "launch"))
+ return handle_launch (argc, argv, do_help);
else if (g_str_equal (command, "list"))
return handle_list (argc, argv, do_help);
else if (g_str_equal (command, "mime"))
diff --git a/gio/gio-tool.h b/gio/gio-tool.h
index 5064165a6..6cd1d946a 100644
--- a/gio/gio-tool.h
+++ b/gio/gio-tool.h
@@ -37,6 +37,7 @@ gboolean file_is_dir (GFile *file);
int handle_cat (int argc, char *argv[], gboolean do_help);
int handle_copy (int argc, char *argv[], gboolean do_help);
int handle_info (int argc, char *argv[], gboolean do_help);
+int handle_launch (int argc, char *argv[], gboolean do_help);
int handle_list (int argc, char *argv[], gboolean do_help);
int handle_mime (int argc, char *argv[], gboolean do_help);
int handle_mkdir (int argc, char *argv[], gboolean do_help);
diff --git a/gio/giomodule.c b/gio/giomodule.c
index e27f1ab76..4c4c75b90 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -30,6 +30,7 @@
#include "giomodule.h"
#include "giomodule-priv.h"
+#include "glib-private.h"
#include "glocalfilemonitor.h"
#include "gnativevolumemonitor.h"
#include "gproxyresolver.h"
@@ -477,9 +478,7 @@ g_io_modules_scan_all_in_directory_with_scope (const char *dirname,
filename = g_build_filename (dirname, "giomodule.cache", NULL);
- cache = g_hash_table_new_full (g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)g_strfreev);
-
+ cache = NULL;
cache_time = 0;
if (g_stat (filename, &statbuf) == 0 &&
g_file_get_contents (filename, &data, NULL, NULL))
@@ -523,6 +522,10 @@ g_io_modules_scan_all_in_directory_with_scope (const char *dirname,
while (g_ascii_isspace (*colon))
colon++;
+ if (G_UNLIKELY (!cache))
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify)g_strfreev);
+
extension_points = g_strsplit (colon, ",", -1);
g_hash_table_insert (cache, file, extension_points);
}
@@ -536,13 +539,15 @@ g_io_modules_scan_all_in_directory_with_scope (const char *dirname,
GIOExtensionPoint *extension_point;
GIOModule *module;
gchar *path;
- char **extension_points;
+ char **extension_points = NULL;
int i;
path = g_build_filename (dirname, name, NULL);
module = g_io_module_new (path);
- extension_points = g_hash_table_lookup (cache, name);
+ if (cache)
+ extension_points = g_hash_table_lookup (cache, name);
+
if (extension_points != NULL &&
g_stat (path, &statbuf) == 0 &&
statbuf.st_ctime <= cache_time)
@@ -577,7 +582,8 @@ g_io_modules_scan_all_in_directory_with_scope (const char *dirname,
g_dir_close (dir);
- g_hash_table_destroy (cache);
+ if (cache)
+ g_hash_table_destroy (cache);
g_free (filename);
}
@@ -807,6 +813,9 @@ _g_io_module_get_default_type (const gchar *extension_point,
return G_TYPE_INVALID;
}
+ /* It’s OK to query the environment here, even when running as setuid, because
+ * it only allows a choice between existing already-loaded modules. No new
+ * code is loaded based on the environment variable value. */
use_this = envvar ? g_getenv (envvar) : NULL;
if (g_strcmp0 (use_this, "help") == 0)
{
@@ -956,6 +965,9 @@ _g_io_module_get_default (const gchar *extension_point,
return NULL;
}
+ /* It’s OK to query the environment here, even when running as setuid, because
+ * it only allows a choice between existing already-loaded modules. No new
+ * code is loaded based on the environment variable value. */
use_this = envvar ? g_getenv (envvar) : NULL;
if (g_strcmp0 (use_this, "help") == 0)
{
@@ -1154,8 +1166,16 @@ static gchar *
get_gio_module_dir (void)
{
gchar *module_dir;
-
- module_dir = g_strdup (g_getenv ("GIO_MODULE_DIR"));
+ gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
+
+ /* If running as setuid, loading modules from an arbitrary directory
+ * controlled by the unprivileged user who is running the program could allow
+ * for execution of arbitrary code (in constructors in modules).
+ * Don’t allow it.
+ *
+ * If a setuid program somehow needs to load additional GIO modules, it should
+ * explicitly call g_io_modules_scan_all_in_directory(). */
+ module_dir = !is_setuid ? g_strdup (g_getenv ("GIO_MODULE_DIR")) : NULL;
if (module_dir == NULL)
{
#ifdef G_OS_WIN32
@@ -1187,13 +1207,14 @@ _g_io_modules_ensure_loaded (void)
if (!loaded_dirs)
{
+ gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
gchar *module_dir;
loaded_dirs = TRUE;
scope = g_io_module_scope_new (G_IO_MODULE_SCOPE_BLOCK_DUPLICATES);
- /* First load any overrides, extras */
- module_path = g_getenv ("GIO_EXTRA_MODULES");
+ /* First load any overrides, extras (but not if running as setuid!) */
+ module_path = !is_setuid ? g_getenv ("GIO_EXTRA_MODULES") : NULL;
if (module_path)
{
gchar **paths;
diff --git a/gio/gliststore.c b/gio/gliststore.c
index 3c2361e1b..da7d12ce5 100644
--- a/gio/gliststore.c
+++ b/gio/gliststore.c
@@ -269,7 +269,7 @@ g_list_store_insert (GListStore *store,
g_return_if_fail (G_IS_LIST_STORE (store));
g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (item), store->item_type));
- g_return_if_fail (position <= g_sequence_get_length (store->items));
+ g_return_if_fail (position <= (guint) g_sequence_get_length (store->items));
it = g_sequence_get_iter_at_pos (store->items, position);
g_sequence_insert_before (it, g_object_ref (item));
@@ -477,7 +477,7 @@ g_list_store_splice (GListStore *store,
if (n_additions)
{
- gint i;
+ guint i;
for (i = 0; i < n_additions; i++)
{
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 15738d8b8..f9603e053 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -2026,7 +2026,7 @@ g_local_file_trash (GFile *file,
display_name = g_filename_display_name (trashdir);
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errsv),
- _("Unable to create trash dir %s: %s"),
+ _("Unable to create trash directory %s: %s"),
display_name, g_strerror (errsv));
g_free (display_name);
g_free (trashdir);
@@ -2038,6 +2038,7 @@ g_local_file_trash (GFile *file,
{
uid_t uid;
char uid_str[32];
+ gboolean success;
uid = geteuid ();
g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid);
@@ -2093,6 +2094,7 @@ g_local_file_trash (GFile *file,
/* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
dirname = g_strdup_printf (".Trash-%s", uid_str);
trashdir = g_build_filename (topdir, dirname, NULL);
+ success = TRUE;
g_free (dirname);
tried_create = FALSE;
@@ -2108,8 +2110,7 @@ g_local_file_trash (GFile *file,
g_remove (trashdir);
/* Not a directory or not owned by user, ignore */
- g_free (trashdir);
- trashdir = NULL;
+ success = FALSE;
}
}
else
@@ -2124,18 +2125,28 @@ g_local_file_trash (GFile *file,
}
else
{
- g_free (trashdir);
- trashdir = NULL;
+ success = FALSE;
}
}
}
- if (trashdir == NULL)
+ if (!success)
{
- g_free (topdir);
- g_set_io_error (error,
- _("Unable to find or create trash directory for %s"),
- file, G_IO_ERROR_NOT_SUPPORTED);
+ gchar *trashdir_display_name = NULL, *file_display_name = NULL;
+
+ trashdir_display_name = g_filename_display_name (trashdir);
+ file_display_name = g_filename_display_name (local->filename);
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Unable to find or create trash directory %s to trash %s"),
+ trashdir_display_name, file_display_name);
+
+ g_free (trashdir_display_name);
+ g_free (file_display_name);
+
+ g_free (topdir);
+ g_free (trashdir);
+
return FALSE;
}
}
@@ -2144,21 +2155,33 @@ g_local_file_trash (GFile *file,
infodir = g_build_filename (trashdir, "info", NULL);
filesdir = g_build_filename (trashdir, "files", NULL);
- g_free (trashdir);
/* Make sure we have the subdirectories */
if ((g_mkdir (infodir, 0700) == -1 && errno != EEXIST) ||
(g_mkdir (filesdir, 0700) == -1 && errno != EEXIST))
{
+ gchar *trashdir_display_name = NULL, *file_display_name = NULL;
+
+ trashdir_display_name = g_filename_display_name (trashdir);
+ file_display_name = g_filename_display_name (local->filename);
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Unable to find or create trash directory %s to trash %s"),
+ trashdir_display_name, file_display_name);
+
+ g_free (trashdir_display_name);
+ g_free (file_display_name);
+
g_free (topdir);
+ g_free (trashdir);
g_free (infodir);
g_free (filesdir);
- g_set_io_error (error,
- _("Unable to find or create trash directory for %s"),
- file, G_IO_ERROR_NOT_SUPPORTED);
+
return FALSE;
}
+ g_free (trashdir);
+
basename = g_path_get_basename (local->filename);
i = 1;
trashname = NULL;
@@ -2842,22 +2865,24 @@ g_local_file_measure_size_of_contents (gint fd,
gboolean success = TRUE;
const gchar *name;
GDir *dir;
+ gint saved_errno;
#ifdef AT_FDCWD
{
- /* If this fails, we want to preserve the errno from fopendir() */
+ /* If this fails, we want to preserve the errno from fdopendir() */
DIR *dirp;
dirp = fdopendir (fd);
+ saved_errno = errno;
dir = dirp ? GLIB_PRIVATE_CALL(g_dir_new_from_dirp) (dirp) : NULL;
+ g_assert ((dirp == NULL) == (dir == NULL));
}
#else
dir = GLIB_PRIVATE_CALL(g_dir_open_with_errno) (dir_name->data, 0);
+ saved_errno = errno;
#endif
if (dir == NULL)
{
- gint saved_errno = errno;
-
#ifdef AT_FDCWD
close (fd);
#endif
diff --git a/gio/gosxappinfo.h b/gio/gosxappinfo.h
index 7beeaad16..793ce1fcd 100644
--- a/gio/gosxappinfo.h
+++ b/gio/gosxappinfo.h
@@ -43,7 +43,7 @@ GLIB_AVAILABLE_IN_2_52
GType g_osx_app_info_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_52
-char * g_osx_app_info_get_filename (GOsxAppInfo *info);
+const char *g_osx_app_info_get_filename (GOsxAppInfo *info);
GLIB_AVAILABLE_IN_2_52
GList * g_osx_app_info_get_all_for_scheme (const gchar *scheme);
diff --git a/gio/gosxappinfo.m b/gio/gosxappinfo.m
index 849d63575..03c373778 100644
--- a/gio/gosxappinfo.m
+++ b/gio/gosxappinfo.m
@@ -168,6 +168,7 @@ get_bundle_string_value (NSBundle *bundle,
static CFStringRef
create_cfstring_from_cstr (const gchar *cstr)
{
+ g_return_val_if_fail (cstr != NULL, NULL);
return CFStringCreateWithCString (NULL, cstr, kCFStringEncodingUTF8);
}
@@ -226,8 +227,8 @@ url_escape_hostname (const char *url)
}
static CFURLRef
-create_url_from_cstr (gchar *cstr,
- gboolean is_file)
+create_url_from_cstr (const gchar *cstr,
+ gboolean is_file)
{
gchar *puny_cstr;
CFStringRef str;
@@ -279,11 +280,17 @@ create_urlspec_for_appinfo (GOsxAppInfo *info,
GList *uris,
gboolean are_files)
{
- LSLaunchURLSpec *urlspec = g_new0 (LSLaunchURLSpec, 1);
- gchar *app_cstr = g_osx_app_info_get_filename (info);
+ LSLaunchURLSpec *urlspec = NULL;
+ const gchar *app_cstr;
+
+ g_return_val_if_fail (G_IS_OSX_APP_INFO (info), NULL);
+
+ urlspec = g_new0 (LSLaunchURLSpec, 1);
+ app_cstr = g_osx_app_info_get_filename (info);
+ g_assert (app_cstr != NULL);
/* Strip file:// from app url but ensure filesystem url */
- urlspec->appURL = create_url_from_cstr (app_cstr + 7, TRUE);
+ urlspec->appURL = create_url_from_cstr (app_cstr + strlen ("file://"), TRUE);
urlspec->launchFlags = kLSLaunchDefaults;
urlspec->itemURLs = create_url_list_from_glist (uris, are_files);
@@ -402,7 +409,7 @@ g_osx_app_info_get_executable (GAppInfo *appinfo)
return info->executable;
}
-char *
+const char *
g_osx_app_info_get_filename (GOsxAppInfo *info)
{
g_return_val_if_fail (info != NULL, NULL);
@@ -431,7 +438,8 @@ g_osx_app_info_get_icon (GAppInfo *appinfo)
if (!info->icon)
{
- gchar *icon_name, *app_uri, *icon_uri;
+ const gchar *app_uri;
+ gchar *icon_name, *icon_uri;
GFile *file;
icon_name = get_bundle_string_value (info->bundle, @"CFBundleIconFile");
@@ -439,7 +447,7 @@ g_osx_app_info_get_icon (GAppInfo *appinfo)
return NULL;
app_uri = g_osx_app_info_get_filename (info);
- icon_uri = g_strconcat (app_uri + 7, "/Contents/Resources/", icon_name,
+ icon_uri = g_strconcat (app_uri + strlen ("file://"), "/Contents/Resources/", icon_name,
g_str_has_suffix (icon_name, ".icns") ? NULL : ".icns", NULL);
g_free (icon_name);
@@ -459,9 +467,14 @@ g_osx_app_info_launch_internal (GAppInfo *appinfo,
GError **error)
{
GOsxAppInfo *info = G_OSX_APP_INFO (appinfo);
- LSLaunchURLSpec *urlspec = create_urlspec_for_appinfo (info, uris, are_files);
+ LSLaunchURLSpec *urlspec;
gint ret, success = TRUE;
+ g_return_val_if_fail (G_IS_OSX_APP_INFO (appinfo), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ urlspec = create_urlspec_for_appinfo (info, uris, are_files);
+
if ((ret = LSOpenFromURLSpec (urlspec, NULL)))
{
/* TODO: Better error codes */
diff --git a/gio/gresource.c b/gio/gresource.c
index b495d12ac..53933f9d2 100644
--- a/gio/gresource.c
+++ b/gio/gresource.c
@@ -32,6 +32,8 @@
#include <gio/gzlibdecompressor.h>
#include <gio/gconverterinputstream.h>
+#include "glib-private.h"
+
struct _GResource
{
int ref_count;
@@ -76,12 +78,16 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref)
* the xmllint executable, or xmllint must be in the `PATH`; otherwise
* the preprocessing step is skipped.
*
- * `to-pixdata` which will use the gdk-pixbuf-pixdata command to convert
- * images to the GdkPixdata format, which allows you to create pixbufs directly using the data inside
- * the resource file, rather than an (uncompressed) copy of it. For this, the gdk-pixbuf-pixdata
- * program must be in the PATH, or the `GDK_PIXBUF_PIXDATA` environment variable must be
- * set to the full path to the gdk-pixbuf-pixdata executable; otherwise the resource compiler will
- * abort.
+ * `to-pixdata` (deprecated since gdk-pixbuf 2.32) which will use the
+ * `gdk-pixbuf-pixdata` command to convert images to the #GdkPixdata format,
+ * which allows you to create pixbufs directly using the data inside the
+ * resource file, rather than an (uncompressed) copy of it. For this, the
+ * `gdk-pixbuf-pixdata` program must be in the `PATH`, or the
+ * `GDK_PIXBUF_PIXDATA` environment variable must be set to the full path to the
+ * `gdk-pixbuf-pixdata` executable; otherwise the resource compiler will abort.
+ * `to-pixdata` has been deprecated since gdk-pixbuf 2.32, as #GResource
+ * supports embedding modern image formats just as well. Instead of using it,
+ * embed a PNG or SVG file in your #GResource.
*
* `json-stripblanks` which will use the `json-glib-format` command to strip
* ignorable whitespace from the JSON file. For this to work, the
@@ -159,7 +165,7 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref)
* replace resources in the program or library, without recompiling, for debugging or quick hacking and testing
* purposes. Since GLib 2.50, it is possible to use the `G_RESOURCE_OVERLAYS` environment variable to selectively overlay
* resources with replacements from the filesystem. It is a %G_SEARCHPATH_SEPARATOR-separated list of substitutions to perform
- * during resource lookups.
+ * during resource lookups. It is ignored when running in a setuid process.
*
* A substitution has the form
*
@@ -330,10 +336,13 @@ g_resource_find_overlay (const gchar *path,
if (g_once_init_enter (&overlay_dirs))
{
+ gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
const gchar * const *result;
const gchar *envvar;
- envvar = g_getenv ("G_RESOURCE_OVERLAYS");
+ /* Don’t load overlays if setuid, as they could allow reading privileged
+ * files. */
+ envvar = !is_setuid ? g_getenv ("G_RESOURCE_OVERLAYS") : NULL;
if (envvar != NULL)
{
gchar **parts;
@@ -929,9 +938,10 @@ g_resource_enumerate_children (GResource *resource,
if (*path == 0)
{
- g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
- _("The resource at “%s” does not exist"),
- path);
+ if (error)
+ g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
+ _("The resource at “%s” does not exist"),
+ path);
return NULL;
}
@@ -968,9 +978,10 @@ g_resource_enumerate_children (GResource *resource,
if (children == NULL)
{
- g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
- _("The resource at “%s” does not exist"),
- path);
+ if (error)
+ g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
+ _("The resource at “%s” does not exist"),
+ path);
return NULL;
}
@@ -1237,9 +1248,10 @@ g_resources_enumerate_children (const gchar *path,
if (hash == NULL)
{
- g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
- _("The resource at “%s” does not exist"),
- path);
+ if (error)
+ g_set_error (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND,
+ _("The resource at “%s” does not exist"),
+ path);
return NULL;
}
else
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index cbc3fad27..26b9a65ad 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -18,6 +18,7 @@
#include "config.h"
+#include "glib-private.h"
#include "gsettingsschema-internal.h"
#include "gsettings.h"
@@ -343,6 +344,7 @@ initialise_schema_sources (void)
*/
if G_UNLIKELY (g_once_init_enter (&initialised))
{
+ gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) ();
const gchar * const *dirs;
const gchar *path;
gchar **extra_schema_dirs;
@@ -357,7 +359,9 @@ initialise_schema_sources (void)
try_prepend_data_dir (g_get_user_data_dir ());
- if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
+ /* Disallow loading extra schemas if running as setuid, as that could
+ * allow reading privileged files. */
+ if (!is_setuid && (path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
{
extra_schema_dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; extra_schema_dirs[i]; i++);
diff --git a/gio/gsocket.c b/gio/gsocket.c
index 0f8f9259f..69fc72825 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -624,6 +624,16 @@ g_socket (gint domain,
fcntl (fd, F_SETFD, flags);
}
}
+#else
+ if ((domain == AF_INET || domain == AF_INET6) && type == SOCK_DGRAM)
+ {
+ BOOL new_behavior = FALSE;
+ DWORD bytes_returned = 0;
+
+ /* Disable connection reset error on ICMP port unreachable. */
+ WSAIoctl (fd, SIO_UDP_CONNRESET, &new_behavior, sizeof (new_behavior),
+ NULL, 0, &bytes_returned, NULL, NULL);
+ }
#endif
return fd;
@@ -3790,6 +3800,9 @@ update_select_events (GSocket *socket)
GList *l;
WSAEVENT event;
+ if (socket->priv->closed)
+ return;
+
ensure_event (socket);
event_mask = 0;
@@ -3848,7 +3861,8 @@ update_condition_unlocked (GSocket *socket)
WSANETWORKEVENTS events;
GIOCondition condition;
- if (WSAEnumNetworkEvents (socket->priv->fd,
+ if (!socket->priv->closed &&
+ WSAEnumNetworkEvents (socket->priv->fd,
socket->priv->event,
&events) == 0)
{
@@ -5475,10 +5489,10 @@ g_socket_receive_message_with_timeout (GSocket *socket,
if (errsv == WSAEINTR)
continue;
+ win32_unset_event_mask (socket, FD_READ);
+
if (errsv == WSAEWOULDBLOCK)
{
- win32_unset_event_mask (socket, FD_READ);
-
if (timeout_us != 0)
{
if (!block_on_timeout (socket, G_IO_IN, timeout_us,
@@ -5965,10 +5979,11 @@ g_socket_get_credentials (GSocket *socket,
socklen_t optlen = sizeof (cred);
if (getsockopt (socket->priv->fd,
- 0,
+ SOL_LOCAL,
LOCAL_PEERCRED,
&cred,
- &optlen) == 0)
+ &optlen) == 0
+ && optlen != 0)
{
if (cred.cr_version == XUCRED_VERSION)
{
@@ -5993,6 +6008,15 @@ g_socket_get_credentials (GSocket *socket,
return NULL;
}
}
+ else if (optlen == 0 || errno == EINVAL)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Unable to read socket credentials: %s"),
+ "unsupported socket type");
+ return NULL;
+ }
}
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
{
diff --git a/gio/gtlsdatabase.c b/gio/gtlsdatabase.c
index 9341206f1..d7dcf4bbe 100644
--- a/gio/gtlsdatabase.c
+++ b/gio/gtlsdatabase.c
@@ -516,8 +516,6 @@ g_tls_database_verify_chain (GTlsDatabase *self,
GError **error)
{
g_return_val_if_fail (G_IS_TLS_DATABASE (self), G_TLS_CERTIFICATE_GENERIC_ERROR);
- g_return_val_if_fail (G_IS_TLS_DATABASE (self),
- G_TLS_CERTIFICATE_GENERIC_ERROR);
g_return_val_if_fail (G_IS_TLS_CERTIFICATE (chain),
G_TLS_CERTIFICATE_GENERIC_ERROR);
g_return_val_if_fail (purpose, G_TLS_CERTIFICATE_GENERIC_ERROR);
diff --git a/gio/gvdb/gvdb-builder.c b/gio/gvdb/gvdb-builder.c
index b8ecbe3d7..918ee43fd 100644
--- a/gio/gvdb/gvdb-builder.c
+++ b/gio/gvdb/gvdb-builder.c
@@ -206,7 +206,7 @@ item_to_index (GvdbItem *item)
if (item != NULL)
return item->assigned_index;
- return guint32_to_le (-1u);
+ return guint32_to_le ((guint32) -1);
}
typedef struct
@@ -234,7 +234,7 @@ file_builder_allocate (FileBuilder *fb,
if (size == 0)
return NULL;
- fb->offset += (-fb->offset) & (alignment - 1);
+ fb->offset += (guint64) (-fb->offset) & (alignment - 1);
chunk = g_slice_new (FileChunk);
chunk->offset = fb->offset;
chunk->size = size;
@@ -463,9 +463,11 @@ static GString *
file_builder_serialise (FileBuilder *fb,
struct gvdb_pointer root)
{
- struct gvdb_header header = { { 0, }, };
+ struct gvdb_header header;
GString *result;
+ memset (&header, 0, sizeof (header));
+
if (fb->byteswap)
{
header.signature[0] = GVDB_SWAPPED_SIGNATURE0;
diff --git a/gio/gvdb/gvdb-reader.c b/gio/gvdb/gvdb-reader.c
index 6bc4c6f36..820ce4c3d 100644
--- a/gio/gvdb/gvdb-reader.c
+++ b/gio/gvdb/gvdb-reader.c
@@ -379,7 +379,7 @@ gvdb_table_get_names (GvdbTable *table,
* a pass that fills in no additional items.
*
* This takes an O(n) algorithm and turns it into O(n*m) where m is
- * the depth of the tree, but in all sane cases the tree won't be very
+ * the depth of the tree, but typically the tree won't be very
* deep and the constant factor of this algorithm is lower (and the
* complexity of coding it, as well).
*/
diff --git a/gio/gwin32api-application-activation-manager.h b/gio/gwin32api-application-activation-manager.h
new file mode 100755
index 000000000..cf44b93e6
--- /dev/null
+++ b/gio/gwin32api-application-activation-manager.h
@@ -0,0 +1,126 @@
+#if NTDDI_VERSION < NTDDI_WIN8
+/* The following code is copied verbatim from MinGW-w64 shobjidl.h */
+/*
+ * IApplicationActivationManager interface
+ */
+typedef enum ACTIVATEOPTIONS {
+ AO_NONE = 0x0,
+ AO_DESIGNMODE = 0x1,
+ AO_NOERRORUI = 0x2,
+ AO_NOSPLASHSCREEN = 0x4
+} ACTIVATEOPTIONS;
+
+DEFINE_ENUM_FLAG_OPERATORS(ACTIVATEOPTIONS)
+
+#ifndef __IApplicationActivationManager_INTERFACE_DEFINED__
+#define __IApplicationActivationManager_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IApplicationActivationManager, 0x2e941141, 0x7f97, 0x4756, 0xba,0x1d, 0x9d,0xec,0xde,0x89,0x4a,0x3d);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("2e941141-7f97-4756-ba1d-9decde894a3d")
+IApplicationActivationManager : public IUnknown
+{
+ virtual HRESULT STDMETHODCALLTYPE ActivateApplication(
+ LPCWSTR appUserModelId,
+ LPCWSTR arguments,
+ ACTIVATEOPTIONS options,
+ DWORD *processId) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ActivateForFile(
+ LPCWSTR appUserModelId,
+ IShellItemArray *itemArray,
+ LPCWSTR verb,
+ DWORD *processId) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ActivateForProtocol(
+ LPCWSTR appUserModelId,
+ IShellItemArray *itemArray,
+ DWORD *processId) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IApplicationActivationManager, 0x2e941141, 0x7f97, 0x4756, 0xba,0x1d, 0x9d,0xec,0xde,0x89,0x4a,0x3d)
+#endif
+#else
+typedef struct IApplicationActivationManagerVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IApplicationActivationManager *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IApplicationActivationManager *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IApplicationActivationManager *This);
+
+ /*** IApplicationActivationManager methods ***/
+ HRESULT (STDMETHODCALLTYPE *ActivateApplication)(
+ IApplicationActivationManager *This,
+ LPCWSTR appUserModelId,
+ LPCWSTR arguments,
+ ACTIVATEOPTIONS options,
+ DWORD *processId);
+
+ HRESULT (STDMETHODCALLTYPE *ActivateForFile)(
+ IApplicationActivationManager *This,
+ LPCWSTR appUserModelId,
+ IShellItemArray *itemArray,
+ LPCWSTR verb,
+ DWORD *processId);
+
+ HRESULT (STDMETHODCALLTYPE *ActivateForProtocol)(
+ IApplicationActivationManager *This,
+ LPCWSTR appUserModelId,
+ IShellItemArray *itemArray,
+ DWORD *processId);
+
+ END_INTERFACE
+} IApplicationActivationManagerVtbl;
+
+interface IApplicationActivationManager {
+ CONST_VTBL IApplicationActivationManagerVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IApplicationActivationManager_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IApplicationActivationManager_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IApplicationActivationManager_Release(This) (This)->lpVtbl->Release(This)
+/*** IApplicationActivationManager methods ***/
+#define IApplicationActivationManager_ActivateApplication(This,appUserModelId,arguments,options,processId) (This)->lpVtbl->ActivateApplication(This,appUserModelId,arguments,options,processId)
+#define IApplicationActivationManager_ActivateForFile(This,appUserModelId,itemArray,verb,processId) (This)->lpVtbl->ActivateForFile(This,appUserModelId,itemArray,verb,processId)
+#define IApplicationActivationManager_ActivateForProtocol(This,appUserModelId,itemArray,processId) (This)->lpVtbl->ActivateForProtocol(This,appUserModelId,itemArray,processId)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IApplicationActivationManager_QueryInterface(IApplicationActivationManager* This,REFIID riid,void **ppvObject) {
+ return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IApplicationActivationManager_AddRef(IApplicationActivationManager* This) {
+ return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IApplicationActivationManager_Release(IApplicationActivationManager* This) {
+ return This->lpVtbl->Release(This);
+}
+/*** IApplicationActivationManager methods ***/
+static FORCEINLINE HRESULT IApplicationActivationManager_ActivateApplication(IApplicationActivationManager* This,LPCWSTR appUserModelId,LPCWSTR arguments,ACTIVATEOPTIONS options,DWORD *processId) {
+ return This->lpVtbl->ActivateApplication(This,appUserModelId,arguments,options,processId);
+}
+static FORCEINLINE HRESULT IApplicationActivationManager_ActivateForFile(IApplicationActivationManager* This,LPCWSTR appUserModelId,IShellItemArray *itemArray,LPCWSTR verb,DWORD *processId) {
+ return This->lpVtbl->ActivateForFile(This,appUserModelId,itemArray,verb,processId);
+}
+static FORCEINLINE HRESULT IApplicationActivationManager_ActivateForProtocol(IApplicationActivationManager* This,LPCWSTR appUserModelId,IShellItemArray *itemArray,DWORD *processId) {
+ return This->lpVtbl->ActivateForProtocol(This,appUserModelId,itemArray,processId);
+}
+#endif
+#endif
+
+#endif
+
+
+#endif /* __IApplicationActivationManager_INTERFACE_DEFINED__ */
+#endif /* NTDDI_VERSION < NTDDI_WIN8 */ \ No newline at end of file
diff --git a/gio/gwin32api-iterator.h b/gio/gwin32api-iterator.h
new file mode 100755
index 000000000..d4df8a7b8
--- /dev/null
+++ b/gio/gwin32api-iterator.h
@@ -0,0 +1,125 @@
+typedef interface IIterator IIterator;
+typedef interface IIterable IIterable;
+
+/* IIterator */
+typedef struct IIteratorVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IIterator *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IIterator *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IIterator *This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IIterator *This,
+ UINT32 *count,
+ IID **ids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IIterator *This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IIterator *This,
+ TrustLevel *trustLevel);
+
+ /*** IIterator methods ***/
+ HRESULT (STDMETHODCALLTYPE *get_Current)(
+ IIterator *This,
+ IUnknown **current);
+
+ HRESULT (STDMETHODCALLTYPE *get_HasCurrent)(
+ IIterator *This,
+ CHAR *hasCurrent);
+
+ HRESULT (STDMETHODCALLTYPE *MoveNext)(
+ IIterator *This,
+ CHAR *hasCurrent);
+
+ HRESULT (STDMETHODCALLTYPE *GetMany)(
+ IIterator *This,
+ UINT capacity,
+ void *value,
+ UINT *actual);
+
+ END_INTERFACE
+} IIteratorVtbl;
+
+interface IIterator {
+ CONST_VTBL IIteratorVtbl* lpVtbl;
+};
+
+/*** IUnknown methods ***/
+#define IIterator_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IIterator_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IIterator_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IIterator_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids)
+#define IIterator_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name)
+#define IIterator_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level)
+/*** IIterator methods ***/
+#define IIterator_get_Current(This,current) (This)->lpVtbl->get_Current(This,current)
+#define IIterator_get_HasCurrent(This,hasCurrent) (This)->lpVtbl->get_HasCurrent(This,hasCurrent)
+#define IIterator_MoveNext(This,hasCurrent) (This)->lpVtbl->MoveNext(This,hasCurrent)
+#define IIterator_GetMany(This,capacity,value,actual) (This)->lpVtbl->GetMany(This,capacity,value,actual)
+
+/* IIterable */
+typedef struct IIterableVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IIterable *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IIterable *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IIterable *This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IIterable *This,
+ UINT32 *count,
+ IID **ids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IIterable *This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IIterable *This,
+ TrustLevel *trustLevel);
+
+ /*** IIterable methods ***/
+ HRESULT (STDMETHODCALLTYPE *First)(
+ IIterable *This,
+ IIterator **first);
+
+ END_INTERFACE
+} IIterableVtbl;
+
+interface IIterable {
+ CONST_VTBL IIterableVtbl* lpVtbl;
+};
+
+/*** IUnknown methods ***/
+#define IIterable_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IIterable_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IIterable_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IIterable_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids)
+#define IIterable_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name)
+#define IIterable_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level)
+/*** IIterable methods ***/
+#define IIterable_First(This,retval) (This)->lpVtbl->First(This,retval)
diff --git a/gio/gwin32api-misc.h b/gio/gwin32api-misc.h
new file mode 100755
index 000000000..2b45d9abd
--- /dev/null
+++ b/gio/gwin32api-misc.h
@@ -0,0 +1 @@
+typedef interface IProcessorArchitecture IProcessorArchitecture;
diff --git a/gio/gwin32api-package.h b/gio/gwin32api-package.h
new file mode 100755
index 000000000..9842a86fa
--- /dev/null
+++ b/gio/gwin32api-package.h
@@ -0,0 +1,264 @@
+typedef interface IPackageManager IPackageManager;
+typedef interface IPackage IPackage;
+typedef interface IPackageId IPackageId;
+typedef interface IPackageVersion IPackageVersion;
+
+DEFINE_GUID(IID_IPackageManager, 0x9A7D4B65, 0x5E8F, 0x4FC7, 0xA2, 0xE5, 0x7F, 0x69, 0x25, 0xCB, 0x8B, 0x53);
+DEFINE_GUID(IID_IPackage, 0x163C792F, 0xBD75, 0x413C, 0xBF, 0x23, 0xB1, 0xFE, 0x7B, 0x95, 0xD8, 0x25);
+
+/* IPackageManager */
+typedef struct IPackageManagerVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IPackageManager *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IPackageManager *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IPackageManager *This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IPackageManager *This,
+ UINT32 *count,
+ IID **ids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IPackageManager *This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IPackageManager *This,
+ TrustLevel *trustLevel);
+
+ /*** IPackageManager methods ***/
+ HRESULT (STDMETHODCALLTYPE *stub_AddPackageAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_UpdatePackageAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_RemovePackageAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_StagePackageAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_RegisterPackageAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *FindPackages)(
+ IPackageManager *This,
+ IIterable **retval);
+
+ HRESULT (STDMETHODCALLTYPE *FindPackagesByUserSecurityId)(
+ IPackageManager *This,
+ HSTRING userSecurityId,
+ IIterable **retval);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByNamePublisher)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByUserSecurityIdNamePublisher)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindUsers)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_SetPackageState)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackageByPackageFullName)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_CleanupPackageForUserAsync)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByPackageFamilyName)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackagesByUserSecurityIdPackageFamilyName)(
+ IPackageManager *This);
+
+ HRESULT (STDMETHODCALLTYPE *stub_FindPackageByUserSecurityIdPackageFullName)(
+ IPackageManager *This);
+
+ END_INTERFACE
+} IPackageManagerVtbl;
+
+interface IPackageManager {
+ CONST_VTBL IPackageManagerVtbl* lpVtbl;
+};
+
+/*** IUnknown methods ***/
+#define IPackageManager_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IPackageManager_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IPackageManager_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IPackageManager_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids)
+#define IPackageManager_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name)
+#define IPackageManager_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level)
+/*** IPackageManager methods ***/
+#define IPackageManager_FindPackages(This,retval) (This)->lpVtbl->FindPackages(This,retval)
+#define IPackageManager_FindPackagesByUserSecurityId(This,userSecurityId,retval) (This)->lpVtbl->FindPackagesByUserSecurityId(This,userSecurityId,retval)
+
+/* IPackageId */
+typedef struct IPackageIdVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IPackageId *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IPackageId *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IPackageId *This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IPackageId *This,
+ UINT32 *count,
+ IID **ids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IPackageId *This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IPackageId *This,
+ TrustLevel *trustLevel);
+
+ /*** IPackageId methods ***/
+ HRESULT (STDMETHODCALLTYPE *get_Name)(
+ IPackageId *This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Version)(
+ IPackageId *This,
+ IPackageVersion *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Architecture)(
+ IPackageId *This,
+ IProcessorArchitecture *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_ResourceId)(
+ IPackageId *This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Publisher)(
+ IPackageId *This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_PublisherId)(
+ IPackageId *This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_FullName)(
+ IPackageId *This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_FamilyName)(
+ IPackageId *This,
+ HSTRING *value);
+
+ END_INTERFACE
+} IPackageIdVtbl;
+
+interface IPackageId {
+ CONST_VTBL IPackageIdVtbl* lpVtbl;
+};
+
+/*** IUnknown methods ***/
+#define IPackageId_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IPackageId_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IPackageId_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IPackageId_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids)
+#define IPackageId_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name)
+#define IPackageId_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level)
+/*** IPackageId methods ***/
+#define IPackageId_get_Name(This,value) (This)->lpVtbl->get_Name(This,value)
+#define IPackageId_get_Version(This,value) (This)->lpVtbl->get_Version(This,value)
+#define IPackageId_get_Architecture(This,value) (This)->lpVtbl->get_Architecture(This,value)
+#define IPackageId_get_ResourceId(This,value) (This)->lpVtbl->get_ResourceId(This,value)
+#define IPackageId_get_Publisher(This,value) (This)->lpVtbl->get_Publisher(This,value)
+#define IPackageId_get_PublisherId(This,value) (This)->lpVtbl->get_PublisherId(This,value)
+#define IPackageId_get_FullName(This,value) (This)->lpVtbl->get_FullName(This,value)
+#define IPackageId_get_FamilyName(This,value) (This)->lpVtbl->get_FamilyName(This,value)
+
+/* IPackage */
+typedef struct IPackageVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IPackage *This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IPackage *This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IPackage *This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IPackage *This,
+ UINT32 *count,
+ IID **ids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IPackage *This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IPackage *This,
+ TrustLevel *trustLevel);
+
+ /*** IPackage methods ***/
+ HRESULT (STDMETHODCALLTYPE *get_Id)(
+ IPackage *This,
+ IPackageId **value);
+
+ HRESULT (STDMETHODCALLTYPE *get_InstalledLocation)(
+ IPackage *This,
+ IUnknown **value);
+
+ HRESULT (STDMETHODCALLTYPE *get_IsFramework)(
+ IPackage *This,
+ CHAR *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Dependencies)(
+ IPackage *This,
+ void **value);
+
+ END_INTERFACE
+} IPackageVtbl;
+
+interface IPackage {
+ CONST_VTBL IPackageVtbl* lpVtbl;
+};
+
+/*** IUnknown methods ***/
+#define IPackage_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IPackage_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IPackage_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IPackage_GetIids(This,count,ids) (This)->lpVtbl->GetIids(This,count,ids)
+#define IPackage_GetRuntimeClassName(This,name) (This)->lpVtbl->GetRuntimeClassName(This,name)
+#define IPackage_GetTrustLevel(This,level) (This)->lpVtbl->GetTrustLevel(This,level)
+/*** IPackage methods ***/
+#define IPackage_get_Id(This,value) (This)->lpVtbl->get_Id(This,value)
+#define IPackage_get_InstalledLocation(This,value) (This)->lpVtbl->get_InstalledLocation(This,value)
+#define IPackage_get_IsFramework(This,value) (This)->lpVtbl->get_IsFramework(This,value)
+#define IPackage_get_Dependencies(This,value) (This)->lpVtbl->get_Dependencies(This,value)
diff --git a/gio/gwin32api-storage.h b/gio/gwin32api-storage.h
new file mode 100755
index 000000000..716a0a7a1
--- /dev/null
+++ b/gio/gwin32api-storage.h
@@ -0,0 +1,339 @@
+struct DateTime;
+
+typedef struct DateTime {
+ UINT64 UniversalTime;
+} DateTime;
+
+/* The following is copied verbatim from MinGW-w64 windows.storage.h */
+enum StorageItemTypes;
+enum FileAttributes;
+enum NameCollisionOption;
+enum StorageDeleteOption;
+
+typedef enum NameCollisionoption {
+ NameCollisionoption_GenerateUniqueName = 0,
+ NameCollisionoption_ReplaceExisting = 1,
+ NameCollisionoption_FailIfExists = 2
+} NameCollisionOption;
+
+typedef enum FileAttributes {
+ FileAttributes_Normal = 0,
+ FileAttributes_ReadOnly = 1,
+ FileAttributes_Directory = 2,
+ FileAttributes_Archive = 3,
+ FileAttributes_Temporary = 4
+} FileAttributes;
+
+typedef enum StorageItemTypes {
+ StorageItemTypes_None = 0,
+ StorageItemTypes_File = 1,
+ StorageItemTypes_Folder = 2
+} StorageItemTypes;
+
+typedef enum StorageDeleteOption {
+ StorageDeleteOption_Default = 0,
+ StorageDeleteOption_PermanentDelete = 1
+} StorageDeleteOption;
+
+#ifndef __IStorageItem_FWD_DEFINED__
+#define __IStorageItem_FWD_DEFINED__
+typedef interface IStorageItem IStorageItem;
+#endif
+
+/*
+ * IStorageItem interface
+ */
+#ifndef __IStorageItem_INTERFACE_DEFINED__
+#define __IStorageItem_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IStorageItem, 0x4207a996, 0xca2f, 0x42f7, 0xbd,0xe8, 0x8b,0x10,0x45,0x7a,0x7f,0x30);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("4207a996-ca2f-42f7-bde8-8b10457a7f30")
+IStorageItem : public IInspectable
+{
+ virtual HRESULT STDMETHODCALLTYPE RenameAsyncOverloadDefaultOptions(
+ HSTRING desiredName,
+ IInspectable **action) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE RenameAsync(
+ HSTRING desiredName,
+ NameCollisionOption option,
+ IInspectable **action) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE DeleteAsyncOverloadDefaultOptions(
+ IInspectable **action) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE DeleteAsync(
+ StorageDeleteOption option,
+ IInspectable **action) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetBasicPropertiesAsync(
+ IInspectable **action) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_Name(
+ HSTRING *value) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_Path(
+ HSTRING *value) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_Attributes(
+ FileAttributes *value) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE get_DateCreated(
+ DateTime *value) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE IsOfType(
+ StorageItemTypes itemType,
+ boolean *value) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IStorageItem, 0x4207a996, 0xca2f, 0x42f7, 0xbd,0xe8, 0x8b,0x10,0x45,0x7a,0x7f,0x30)
+#endif
+#else
+typedef struct IStorageItemVtbl {
+ BEGIN_INTERFACE
+
+ /*** IUnknown methods ***/
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+ IStorageItem* This,
+ REFIID riid,
+ void **ppvObject);
+
+ ULONG (STDMETHODCALLTYPE *AddRef)(
+ IStorageItem* This);
+
+ ULONG (STDMETHODCALLTYPE *Release)(
+ IStorageItem* This);
+
+ /*** IInspectable methods ***/
+ HRESULT (STDMETHODCALLTYPE *GetIids)(
+ IStorageItem* This,
+ ULONG *iidCount,
+ IID **iids);
+
+ HRESULT (STDMETHODCALLTYPE *GetRuntimeClassName)(
+ IStorageItem* This,
+ HSTRING *className);
+
+ HRESULT (STDMETHODCALLTYPE *GetTrustLevel)(
+ IStorageItem* This,
+ TrustLevel *trustLevel);
+
+ /*** IStorageItem methods ***/
+ HRESULT (STDMETHODCALLTYPE *RenameAsyncOverloadDefaultOptions)(
+ IStorageItem* This,
+ HSTRING desiredName,
+ IInspectable **action);
+
+ HRESULT (STDMETHODCALLTYPE *RenameAsync)(
+ IStorageItem* This,
+ HSTRING desiredName,
+ NameCollisionOption option,
+ IInspectable **action);
+
+ HRESULT (STDMETHODCALLTYPE *DeleteAsyncOverloadDefaultOptions)(
+ IStorageItem* This,
+ IInspectable **action);
+
+ HRESULT (STDMETHODCALLTYPE *DeleteAsync)(
+ IStorageItem* This,
+ StorageDeleteOption option,
+ IInspectable **action);
+
+ HRESULT (STDMETHODCALLTYPE *GetBasicPropertiesAsync)(
+ IStorageItem* This,
+ IInspectable **action);
+
+ HRESULT (STDMETHODCALLTYPE *get_Name)(
+ IStorageItem* This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Path)(
+ IStorageItem* This,
+ HSTRING *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_Attributes)(
+ IStorageItem* This,
+ FileAttributes *value);
+
+ HRESULT (STDMETHODCALLTYPE *get_DateCreated)(
+ IStorageItem* This,
+ DateTime *value);
+
+ HRESULT (STDMETHODCALLTYPE *IsOfType)(
+ IStorageItem* This,
+ StorageItemTypes itemType,
+ boolean *value);
+
+ END_INTERFACE
+} IStorageItemVtbl;
+interface IStorageItem {
+ CONST_VTBL IStorageItemVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IStorageItem_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IStorageItem_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IStorageItem_Release(This) (This)->lpVtbl->Release(This)
+/*** IInspectable methods ***/
+#define IStorageItem_GetIids(This,iidCount,iids) (This)->lpVtbl->GetIids(This,iidCount,iids)
+#define IStorageItem_GetRuntimeClassName(This,className) (This)->lpVtbl->GetRuntimeClassName(This,className)
+#define IStorageItem_GetTrustLevel(This,trustLevel) (This)->lpVtbl->GetTrustLevel(This,trustLevel)
+/*** IStorageItem methods ***/
+#define IStorageItem_RenameAsyncOverloadDefaultOptions(This,desiredName,action) (This)->lpVtbl->RenameAsyncOverloadDefaultOptions(This,desiredName,action)
+#define IStorageItem_RenameAsync(This,desiredName,option,action) (This)->lpVtbl->RenameAsync(This,desiredName,option,action)
+#define IStorageItem_DeleteAsyncOverloadDefaultOptions(This,action) (This)->lpVtbl->DeleteAsyncOverloadDefaultOptions(This,action)
+#define IStorageItem_DeleteAsync(This,option,action) (This)->lpVtbl->DeleteAsync(This,option,action)
+#define IStorageItem_GetBasicPropertiesAsync(This,action) (This)->lpVtbl->GetBasicPropertiesAsync(This,action)
+#define IStorageItem_get_Name(This,value) (This)->lpVtbl->get_Name(This,value)
+#define IStorageItem_get_Path(This,value) (This)->lpVtbl->get_Path(This,value)
+#define IStorageItem_get_Attributes(This,value) (This)->lpVtbl->get_Attributes(This,value)
+#define IStorageItem_get_DateCreated(This,value) (This)->lpVtbl->get_DateCreated(This,value)
+#define IStorageItem_IsOfType(This,itemType,value) (This)->lpVtbl->IsOfType(This,itemType,value)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IStorageItem_QueryInterface(IStorageItem* This,REFIID riid,void **ppvObject) {
+ return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IStorageItem_AddRef(IStorageItem* This) {
+ return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IStorageItem_Release(IStorageItem* This) {
+ return This->lpVtbl->Release(This);
+}
+/*** IInspectable methods ***/
+static FORCEINLINE HRESULT IStorageItem_GetIids(IStorageItem* This,ULONG *iidCount,IID **iids) {
+ return This->lpVtbl->GetIids(This,iidCount,iids);
+}
+static FORCEINLINE HRESULT IStorageItem_GetRuntimeClassName(IStorageItem* This,HSTRING *className) {
+ return This->lpVtbl->GetRuntimeClassName(This,className);
+}
+static FORCEINLINE HRESULT IStorageItem_GetTrustLevel(IStorageItem* This,TrustLevel *trustLevel) {
+ return This->lpVtbl->GetTrustLevel(This,trustLevel);
+}
+/*** IStorageItem methods ***/
+static FORCEINLINE HRESULT IStorageItem_RenameAsyncOverloadDefaultOptions(IStorageItem* This,HSTRING desiredName,IInspectable **action) {
+ return This->lpVtbl->RenameAsyncOverloadDefaultOptions(This,desiredName,action);
+}
+static FORCEINLINE HRESULT IStorageItem_RenameAsync(IStorageItem* This,HSTRING desiredName,NameCollisionOption option,IInspectable **action) {
+ return This->lpVtbl->RenameAsync(This,desiredName,option,action);
+}
+static FORCEINLINE HRESULT IStorageItem_DeleteAsyncOverloadDefaultOptions(IStorageItem* This,IInspectable **action) {
+ return This->lpVtbl->DeleteAsyncOverloadDefaultOptions(This,action);
+}
+static FORCEINLINE HRESULT IStorageItem_DeleteAsync(IStorageItem* This,StorageDeleteOption option,IInspectable **action) {
+ return This->lpVtbl->DeleteAsync(This,option,action);
+}
+static FORCEINLINE HRESULT IStorageItem_GetBasicPropertiesAsync(IStorageItem* This,IInspectable **action) {
+ return This->lpVtbl->GetBasicPropertiesAsync(This,action);
+}
+static FORCEINLINE HRESULT IStorageItem_get_Name(IStorageItem* This,HSTRING *value) {
+ return This->lpVtbl->get_Name(This,value);
+}
+static FORCEINLINE HRESULT IStorageItem_get_Path(IStorageItem* This,HSTRING *value) {
+ return This->lpVtbl->get_Path(This,value);
+}
+static FORCEINLINE HRESULT IStorageItem_get_Attributes(IStorageItem* This,FileAttributes *value) {
+ return This->lpVtbl->get_Attributes(This,value);
+}
+static FORCEINLINE HRESULT IStorageItem_get_DateCreated(IStorageItem* This,DateTime *value) {
+ return This->lpVtbl->get_DateCreated(This,value);
+}
+static FORCEINLINE HRESULT IStorageItem_IsOfType(IStorageItem* This,StorageItemTypes itemType,boolean *value) {
+ return This->lpVtbl->IsOfType(This,itemType,value);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IStorageItem_RenameAsyncOverloadDefaultOptions_Proxy(
+ IStorageItem* This,
+ HSTRING desiredName,
+ IInspectable **action);
+void __RPC_STUB IStorageItem_RenameAsyncOverloadDefaultOptions_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_RenameAsync_Proxy(
+ IStorageItem* This,
+ HSTRING desiredName,
+ NameCollisionOption option,
+ IInspectable **action);
+void __RPC_STUB IStorageItem_RenameAsync_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_DeleteAsyncOverloadDefaultOptions_Proxy(
+ IStorageItem* This,
+ IInspectable **action);
+void __RPC_STUB IStorageItem_DeleteAsyncOverloadDefaultOptions_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_DeleteAsync_Proxy(
+ IStorageItem* This,
+ StorageDeleteOption option,
+ IInspectable **action);
+void __RPC_STUB IStorageItem_DeleteAsync_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_GetBasicPropertiesAsync_Proxy(
+ IStorageItem* This,
+ IInspectable **action);
+void __RPC_STUB IStorageItem_GetBasicPropertiesAsync_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_get_Name_Proxy(
+ IStorageItem* This,
+ HSTRING *value);
+void __RPC_STUB IStorageItem_get_Name_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_get_Path_Proxy(
+ IStorageItem* This,
+ HSTRING *value);
+void __RPC_STUB IStorageItem_get_Path_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_get_Attributes_Proxy(
+ IStorageItem* This,
+ FileAttributes *value);
+void __RPC_STUB IStorageItem_get_Attributes_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_get_DateCreated_Proxy(
+ IStorageItem* This,
+ DateTime *value);
+void __RPC_STUB IStorageItem_get_DateCreated_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IStorageItem_IsOfType_Proxy(
+ IStorageItem* This,
+ StorageItemTypes itemType,
+ boolean *value);
+void __RPC_STUB IStorageItem_IsOfType_Stub(
+ IRpcStubBuffer* This,
+ IRpcChannelBuffer* pRpcChannelBuffer,
+ PRPC_MESSAGE pRpcMessage,
+ DWORD* pdwStubPhase);
+
+#endif /* __IStorageItem_INTERFACE_DEFINED__ */
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index 8871acc9d..b12f9e092 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -22,6 +22,8 @@
#include "config.h"
+#define COBJMACROS
+
#include <string.h>
#include "gcontenttype.h"
@@ -32,8 +34,16 @@
#include <glib/gstdio.h>
#include "glibintl.h"
#include <gio/gwin32registrykey.h>
+#include <shlobj.h>
+/* Contains the definitions from shlobj.h that are
+ * guarded as Windows8-or-newer and are unavailable
+ * to GLib, being only Windows7-or-newer.
+ */
+#include "gwin32api-application-activation-manager.h"
#include <windows.h>
+/* For SHLoadIndirectString() */
+#include <shlwapi.h>
#include <glib/gstdioprivate.h>
#include "giowin32-priv.h"
@@ -181,7 +191,10 @@ struct _GWin32AppInfoHandler {
/* Usually a class name in HKCR */
gunichar2 *handler_id;
- /* Registry object obtained by opening @handler_id. Can be used to watch this handler. */
+ /* Registry object obtained by opening @handler_id.
+ * Can be used to watch this handler.
+ * May be %NULL (for fake handlers that we made up).
+ */
GWin32RegistryKey *key;
/* @handler_id, in UTF-8, folded */
@@ -192,6 +205,13 @@ struct _GWin32AppInfoHandler {
/* Verbs that this handler supports */
GPtrArray *verbs; /* of GWin32AppInfoShellVerb */
+
+ /* AppUserModelID for a UWP application. When this is not NULL,
+ * this handler launches a UWP application.
+ * UWP applications are launched using a COM interface and have no commandlines,
+ * and the verbs will reflect that too.
+ */
+ gunichar2 *uwp_aumid;
};
struct _GWin32AppInfoShellVerb {
@@ -203,6 +223,11 @@ struct _GWin32AppInfoShellVerb {
/* User-friendly (localized) verb name. */
gchar *verb_displayname;
+ /* %TRUE if this verb is for a UWP app.
+ * It means that @command, @executable and @dll_function are %NULL.
+ */
+ gboolean is_uwp;
+
/* shell/verb/command */
gunichar2 *command;
@@ -255,6 +280,7 @@ struct _GWin32AppInfoApplication {
* key path for the application.
* For applications tracked by executable name this is the
* basename of the executable.
+ * For UWP apps this is the AppUserModelID.
* For fake applications this is the full filename of the
* executable (as far as it can be inferred from a command line,
* meaning that it can also be a basename, if that's
@@ -320,6 +346,9 @@ struct _GWin32AppInfoApplication {
/* Set to TRUE for applications that are machine-wide defaults (i.e. default
* browser) */
gboolean default_app;
+
+ /* Set to TRUE for UWP applications */
+ gboolean is_uwp;
};
#define G_TYPE_WIN32_APPINFO_URL_SCHEMA (g_win32_appinfo_url_schema_get_type ())
@@ -373,6 +402,7 @@ g_win32_appinfo_handler_dispose (GObject *object)
g_clear_object (&handler->key);
g_clear_object (&handler->icon);
g_clear_pointer (&handler->verbs, g_ptr_array_unref);
+ g_clear_pointer (&handler->uwp_aumid, g_free);
G_OBJECT_CLASS (g_win32_appinfo_handler_parent_class)->dispose (object);
}
@@ -576,6 +606,11 @@ static GHashTable *fake_apps = NULL;
*/
static GHashTable *handlers = NULL;
+/* Temporary (only exists while the registry is being scanned) table
+ * that maps GWin32RegistryKey objects (keeps a ref) to owned AUMId wchar strings.
+ */
+static GHashTable *uwp_handler_table = NULL;
+
/* Watch this whole subtree */
static GWin32RegistryKey *url_associations_key;
@@ -616,6 +651,9 @@ static GWin32RegistryKey *classes_root_key;
*/
#include "giowin32-private.c"
+/* for g_win32_package_parser_enum_packages() */
+#include "gwin32packageparser.h"
+
static void
read_handler_icon (GWin32RegistryKey *key,
GIcon **icon_out)
@@ -642,6 +680,12 @@ read_handler_icon (GWin32RegistryKey *key,
NULL,
NULL))
{
+ /* TODO: For UWP handlers this string is usually in @{...} form,
+ * see grab_registry_string() below. Right now this
+ * string is read as-is and the icon would silently fail to load.
+ * Also, right now handler icon is not used anywhere
+ * (only app icon is used).
+ */
if (default_type == G_WIN32_REGISTRY_VALUE_STR &&
default_value[0] != '\0')
*icon_out = g_themed_icon_new (default_value);
@@ -689,18 +733,18 @@ compare_verbs (gconstpointer a,
if (def != NULL)
{
if (_wcsicmp (ca->name, def) == 0)
- return 1;
- else if (_wcsicmp (cb->name, def) == 0)
return -1;
+ else if (_wcsicmp (cb->name, def) == 0)
+ return 1;
}
is_open_ca = is_open (ca->name);
is_open_cb = is_open (cb->name);
if (is_open_ca && !is_open_cb)
- return 1;
- else if (is_open_ca && !is_open_cb)
return -1;
+ else if (is_open_ca && !is_open_cb)
+ return 1;
return _wcsicmp (ca->name, cb->name);
}
@@ -735,7 +779,8 @@ typedef void (*verb_command_func) (gpointer handler_data1,
static gunichar2 * decide_which_id_to_use (const gunichar2 *program_id,
GWin32RegistryKey **return_key,
gchar **return_handler_id_u8,
- gchar **return_handler_id_u8_folded);
+ gchar **return_handler_id_u8_folded,
+ gunichar2 **return_uwp_aumid);
static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *schema,
const gchar *schema_u8,
@@ -743,7 +788,8 @@ static GWin32AppInfoURLSchema * get_schema_object (const gunichar2 *sche
static GWin32AppInfoHandler * get_handler_object (const gchar *handler_id_u8_folded,
GWin32RegistryKey *handler_key,
- const gunichar2 *handler_id);
+ const gunichar2 *handler_id,
+ const gunichar2 *uwp_aumid);
static GWin32AppInfoFileExtension *get_ext_object (const gunichar2 *ext,
const gchar *ext_u8,
@@ -768,6 +814,20 @@ static void handler_add_verb (gpointer ha
gboolean verb_is_preferred,
gboolean invent_new_verb_name);
+static void process_uwp_verbs (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app);
+
+static void uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred);
+
/* output_size is in *bytes*, not gunichar2s! */
static gboolean
build_registry_path (gunichar2 *output, gsize output_size, ...)
@@ -858,6 +918,12 @@ _g_win32_registry_key_build_and_new_w (GError **error, ...)
* @verbshell_prefix is the subkey of @program_id_key
* that contains the verbs. It is "Shell" initially,
* but grows with recursive invocations (for subcommands).
+ * @is_uwp points to a boolean, which
+ * indicates whether the function is being called for a UWP app.
+ * It might be switched from %TRUE to %FALSE on return,
+ * if the application turns out to not to be UWP on closer inspection.
+ * If the application is already known not to be UWP before the
+ * call, this pointer can be %NULL instead.
* Returns TRUE on success, FALSE on failure.
*/
static gboolean
@@ -865,7 +931,8 @@ get_verbs (GWin32RegistryKey *program_id_key,
const reg_verb **preferred_verb,
GList **verbs,
const gunichar2 *verbname_prefix,
- const gunichar2 *verbshell_prefix)
+ const gunichar2 *verbshell_prefix,
+ gboolean *is_uwp)
{
GWin32RegistrySubkeyIter iter;
GWin32RegistryKey *key;
@@ -930,7 +997,8 @@ get_verbs (GWin32RegistryKey *program_id_key,
* Essentially, we're flattening the command tree into a list.
*/
has_subcommands = FALSE;
- if (g_win32_registry_key_get_value_w (subkey,
+ if ((is_uwp == NULL || !(*is_uwp)) && /* Assume UWP apps don't have subcommands */
+ g_win32_registry_key_get_value_w (subkey,
NULL,
TRUE,
L"Subcommands",
@@ -940,8 +1008,9 @@ get_verbs (GWin32RegistryKey *program_id_key,
NULL) &&
subc_type == G_WIN32_REGISTRY_VALUE_STR)
{
- gunichar2 *new_nameprefix = g_malloc ((verbname_prefix_len + name_len + 1 + 1) * sizeof (gunichar2));
- gunichar2 *new_shellprefix = g_malloc ((verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1) * sizeof (gunichar2));
+ gboolean dummy = FALSE;
+ gunichar2 *new_nameprefix = g_new (gunichar2, verbname_prefix_len + name_len + 1 + 1);
+ gunichar2 *new_shellprefix = g_new (gunichar2, verbshell_prefix_len + 1 + name_len + 1 + shell_len + 1);
memcpy (&new_shellprefix[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
new_shellprefix[verbshell_prefix_len] = L'\\';
memcpy (&new_shellprefix[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
@@ -953,16 +1022,38 @@ get_verbs (GWin32RegistryKey *program_id_key,
memcpy (&new_nameprefix[verbname_prefix_len], name, (name_len) * sizeof (gunichar2));
new_nameprefix[verbname_prefix_len + name_len] = L'\\';
new_nameprefix[verbname_prefix_len + name_len + 1] = 0;
- has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix);
+ has_subcommands = get_verbs (program_id_key, &tmp, verbs, new_nameprefix, new_shellprefix, &dummy);
g_free (new_shellprefix);
g_free (new_nameprefix);
}
- g_clear_object (&subkey);
-
/* Presence of subcommands means that this key itself is not a command-key */
if (has_subcommands)
- continue;
+ {
+ g_clear_object (&subkey);
+ continue;
+ }
+
+ if (is_uwp != NULL && *is_uwp &&
+ !g_win32_registry_key_get_value_w (subkey,
+ NULL,
+ TRUE,
+ L"ActivatableClassId",
+ &subc_type,
+ NULL,
+ NULL,
+ NULL))
+ {
+ /* We expected a UWP app, but it lacks ActivatableClassId
+ * on a verb, which means that it does not behave like
+ * a UWP app should (msedge being an example - it's UWP,
+ * but has its own launchable exe file and a simple ID),
+ * so we have to treat it like a normal app.
+ */
+ *is_uwp = FALSE;
+ }
+
+ g_clear_object (&subkey);
/* We don't look at the command sub-key and its value (the actual command line) here.
* We save the registry path instead, and use it later in process_verbs_commands().
@@ -973,11 +1064,11 @@ get_verbs (GWin32RegistryKey *program_id_key,
* because it never has one - all verbshell prefixes end with "Shell", not "Shell\\")
*/
rverb = g_new0 (reg_verb, 1);
- rverb->name = g_malloc ((verbname_prefix_len + name_len + 1) * sizeof (gunichar2));
+ rverb->name = g_new (gunichar2, verbname_prefix_len + name_len + 1);
memcpy (&rverb->name[0], verbname_prefix, verbname_prefix_len * sizeof (gunichar2));
memcpy (&rverb->name[verbname_prefix_len], name, name_len * sizeof (gunichar2));
rverb->name[verbname_prefix_len + name_len] = 0;
- rverb->shellpath = g_malloc ((verbshell_prefix_len + 1 + name_len + 1) * sizeof (gunichar2));
+ rverb->shellpath = g_new (gunichar2, verbshell_prefix_len + 1 + name_len + 1);
memcpy (&rverb->shellpath[0], verbshell_prefix, verbshell_prefix_len * sizeof (gunichar2));
memcpy (&rverb->shellpath[verbshell_prefix_len], L"\\", sizeof (gunichar2));
memcpy (&rverb->shellpath[verbshell_prefix_len + 1], name, name_len * sizeof (gunichar2));
@@ -1049,31 +1140,41 @@ get_url_association (const gunichar2 *program_id,
const reg_verb *preferred_verb;
gchar *handler_id_u8;
gchar *handler_id_u8_folded;
+ gunichar2 *uwp_aumid;
+ gboolean is_uwp;
GWin32RegistryKey *handler_key;
if ((handler_id = decide_which_id_to_use (program_id,
&handler_key,
&handler_id_u8,
- &handler_id_u8_folded)) == NULL)
+ &handler_id_u8_folded,
+ &uwp_aumid)) == NULL)
return;
- if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
+ is_uwp = uwp_aumid != NULL;
+
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
{
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
g_clear_object (&handler_key);
+ g_clear_pointer (&uwp_aumid, g_free);
return;
}
+ if (!is_uwp && uwp_aumid != NULL)
+ g_clear_pointer (&uwp_aumid, g_free);
+
schema_rec = get_schema_object (schema,
schema_u8,
schema_u8_folded);
handler_rec = get_handler_object (handler_id_u8_folded,
handler_key,
- handler_id);
+ handler_id,
+ uwp_aumid);
if (is_user_choice || schema_rec->chosen_handler == NULL)
g_set_object (&schema_rec->chosen_handler, handler_rec);
@@ -1089,18 +1190,29 @@ get_url_association (const gunichar2 *program_id,
g_strdup (schema_rec->schema_u8_folded),
g_object_ref (handler_rec));
- process_verbs_commands (g_steal_pointer (&verbs),
- preferred_verb,
- HKCR,
- handler_id,
- TRUE,
- handler_add_verb,
- handler_rec,
- app);
+ if (uwp_aumid == NULL)
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+ else
+ process_uwp_verbs (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_rec,
+ app);
+
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
g_clear_pointer (&handler_id, g_free);
+ g_clear_pointer (&uwp_aumid, g_free);
}
/* Grabs a file extension association (from HKCR\.ext or similar).
@@ -1121,6 +1233,8 @@ get_file_ext (const gunichar2 *program_id,
GList *verbs;
gchar *handler_id_u8;
gchar *handler_id_u8_folded;
+ gunichar2 *uwp_aumid;
+ gboolean is_uwp;
GWin32RegistryKey *handler_key;
GWin32AppInfoFileExtension *file_extn;
gchar *file_extension_u8;
@@ -1129,7 +1243,8 @@ get_file_ext (const gunichar2 *program_id,
if ((handler_id = decide_which_id_to_use (program_id,
&handler_key,
&handler_id_u8,
- &handler_id_u8_folded)) == NULL)
+ &handler_id_u8_folded,
+ &uwp_aumid)) == NULL)
return;
if (!g_utf16_to_utf8_and_fold (file_extension,
@@ -1140,12 +1255,15 @@ get_file_ext (const gunichar2 *program_id,
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_pointer (&uwp_aumid, g_free);
g_clear_object (&handler_key);
return;
}
- if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell"))
+ is_uwp = uwp_aumid != NULL;
+
+ if (!get_verbs (handler_key, &preferred_verb, &verbs, L"", L"Shell", &is_uwp))
{
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
@@ -1153,15 +1271,20 @@ get_file_ext (const gunichar2 *program_id,
g_clear_object (&handler_key);
g_clear_pointer (&file_extension_u8, g_free);
g_clear_pointer (&file_extension_u8_folded, g_free);
+ g_clear_pointer (&uwp_aumid, g_free);
return;
}
+ if (!is_uwp && uwp_aumid != NULL)
+ g_clear_pointer (&uwp_aumid, g_free);
+
file_extn = get_ext_object (file_extension, file_extension_u8, file_extension_u8_folded);
handler_rec = get_handler_object (handler_id_u8_folded,
handler_key,
- handler_id);
+ handler_id,
+ uwp_aumid);
if (is_user_choice || file_extn->chosen_handler == NULL)
g_set_object (&file_extn->chosen_handler, handler_rec);
@@ -1179,18 +1302,28 @@ get_file_ext (const gunichar2 *program_id,
g_clear_pointer (&file_extension_u8_folded, g_free);
g_clear_object (&handler_key);
- process_verbs_commands (g_steal_pointer (&verbs),
- preferred_verb,
- HKCR,
- handler_id,
- TRUE,
- handler_add_verb,
- handler_rec,
- app);
+ if (uwp_aumid == NULL)
+ process_verbs_commands (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_add_verb,
+ handler_rec,
+ app);
+ else
+ process_uwp_verbs (g_steal_pointer (&verbs),
+ preferred_verb,
+ HKCR,
+ handler_id,
+ TRUE,
+ handler_rec,
+ app);
g_clear_pointer (&handler_id, g_free);
g_clear_pointer (&handler_id_u8, g_free);
g_clear_pointer (&handler_id_u8_folded, g_free);
+ g_clear_pointer (&uwp_aumid, g_free);
}
/* Returns either a @program_id or the string from
@@ -1205,12 +1338,15 @@ static gunichar2 *
decide_which_id_to_use (const gunichar2 *program_id,
GWin32RegistryKey **return_key,
gchar **return_handler_id_u8,
- gchar **return_handler_id_u8_folded)
+ gchar **return_handler_id_u8_folded,
+ gunichar2 **return_uwp_aumid)
{
GWin32RegistryKey *key;
+ GWin32RegistryKey *uwp_key;
GWin32RegistryValueType val_type;
gunichar2 *proxy_id;
gunichar2 *return_id;
+ gunichar2 *uwp_aumid;
gboolean got_value;
gchar *handler_id_u8;
gchar *handler_id_u8_folded;
@@ -1219,23 +1355,61 @@ decide_which_id_to_use (const gunichar2 *program_id,
if (return_key)
*return_key = NULL;
- /* Check the proxy first */
+ if (return_uwp_aumid)
+ *return_uwp_aumid = NULL;
+
key = g_win32_registry_key_get_child_w (classes_root_key, program_id, NULL);
if (key == NULL)
return NULL;
+ /* Check for UWP first */
+ uwp_aumid = NULL;
+ uwp_key = g_win32_registry_key_get_child_w (key, L"Application", NULL);
+
+ if (uwp_key != NULL)
+ {
+ got_value = g_win32_registry_key_get_value_w (uwp_key,
+ NULL,
+ TRUE,
+ L"AppUserModelID",
+ &val_type,
+ (void **) &uwp_aumid,
+ NULL,
+ NULL);
+ if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&uwp_aumid, g_free);
+
+ /* Other values in the Application key contain useful information
+ * (description, name, icon), but it's inconvenient to read
+ * it here (we don't have an app object *yet*). Store the key
+ * in a table instead, and look at it later.
+ */
+ if (uwp_aumid == NULL)
+ g_debug ("ProgramID %S looks like a UWP application, but isn't",
+ program_id);
+ else
+ g_hash_table_insert (uwp_handler_table, g_object_ref (uwp_key), g_wcsdup (uwp_aumid, -1));
+
+ g_object_unref (uwp_key);
+ }
+
+ /* Then check for proxy */
proxy_id = NULL;
- got_value = g_win32_registry_key_get_value_w (key,
- NULL,
- TRUE,
- L"",
- &val_type,
- (void **) &proxy_id,
- NULL,
- NULL);
- if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
- g_clear_pointer (&proxy_id, g_free);
+
+ if (uwp_aumid == NULL)
+ {
+ got_value = g_win32_registry_key_get_value_w (key,
+ NULL,
+ TRUE,
+ L"",
+ &val_type,
+ (void **) &proxy_id,
+ NULL,
+ NULL);
+ if (got_value && val_type != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&proxy_id, g_free);
+ }
return_id = NULL;
@@ -1273,8 +1447,13 @@ decide_which_id_to_use (const gunichar2 *program_id,
if (return_handler_id_u8)
*return_handler_id_u8 = g_steal_pointer (&handler_id_u8);
+ g_clear_pointer (&handler_id_u8, g_free);
if (return_handler_id_u8_folded)
*return_handler_id_u8_folded = g_steal_pointer (&handler_id_u8_folded);
+ g_clear_pointer (&handler_id_u8_folded, g_free);
+ if (return_uwp_aumid)
+ *return_uwp_aumid = g_steal_pointer (&uwp_aumid);
+ g_clear_pointer (&uwp_aumid, g_free);
if (return_id == NULL && return_key)
*return_key = g_steal_pointer (&key);
@@ -1418,6 +1597,75 @@ process_verbs_commands (GList *verbs,
g_list_free_full (verbs, reg_verb_free);
}
+static void
+process_uwp_verbs (GList *verbs,
+ const reg_verb *preferred_verb,
+ const gunichar2 *path_to_progid,
+ const gunichar2 *progid,
+ gboolean autoprefer_first_verb,
+ GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app)
+{
+ GList *i;
+
+ g_assert (verbs != NULL);
+
+ for (i = verbs; i; i = i->next)
+ {
+ const reg_verb *verb = (const reg_verb *) i->data;
+ GWin32RegistryKey *key;
+ gboolean got_value;
+ GWin32RegistryValueType val_type;
+ gunichar2 *acid;
+ gsize acid_len;
+
+ key = _g_win32_registry_key_build_and_new_w (NULL, path_to_progid, progid,
+ L"\\", verb->shellpath, NULL);
+
+ if (key == NULL)
+ {
+ g_debug ("%S%S\\%S does not exist",
+ path_to_progid, progid, verb->shellpath);
+ continue;
+ }
+
+ got_value = g_win32_registry_key_get_value_w (key,
+ g_win32_registry_get_os_dirs_w (),
+ TRUE,
+ L"ActivatableClassId",
+ &val_type,
+ (void **) &acid,
+ &acid_len,
+ NULL);
+
+ if (got_value &&
+ val_type == G_WIN32_REGISTRY_VALUE_STR &&
+ acid_len > sizeof (gunichar2))
+ {
+ /* TODO: default value of a shell subkey, if not empty,
+ * migh contain something like @{Some.Identifier_1234.456.678.789_some_words?ms-resource://Arbitrary.Path/Pointing/Somewhere}
+ * and it might be possible to turn it into a nice displayname.
+ */
+ uwp_handler_add_verb (handler_rec,
+ app,
+ verb->name,
+ NULL,
+ (preferred_verb && _wcsicmp (verb->name, preferred_verb->name) == 0) ||
+ (!preferred_verb && autoprefer_first_verb && i == verbs));
+ }
+ else
+ {
+ g_debug ("%S%S\\%S does not have an ActivatableClassId string value",
+ path_to_progid, progid, verb->shellpath);
+ }
+
+ g_clear_pointer (&acid, g_free);
+ g_clear_object (&key);
+ }
+
+ g_list_free_full (verbs, reg_verb_free);
+}
+
/* Looks up a schema object identified by
* @schema_u8_folded in the urls hash table.
* If such object doesn't exist,
@@ -1454,7 +1702,8 @@ get_schema_object (const gunichar2 *schema,
static GWin32AppInfoHandler *
get_handler_object (const gchar *handler_id_u8_folded,
GWin32RegistryKey *handler_key,
- const gunichar2 *handler_id)
+ const gunichar2 *handler_id,
+ const gunichar2 *uwp_aumid)
{
GWin32AppInfoHandler *handler_rec;
@@ -1464,10 +1713,14 @@ get_handler_object (const gchar *handler_id_u8_folded,
return handler_rec;
handler_rec = g_object_new (G_TYPE_WIN32_APPINFO_HANDLER, NULL);
- handler_rec->key = g_object_ref (handler_key);
+ if (handler_key)
+ handler_rec->key = g_object_ref (handler_key);
handler_rec->handler_id = g_wcsdup (handler_id, -1);
handler_rec->handler_id_folded = g_strdup (handler_id_u8_folded);
- read_handler_icon (handler_key, &handler_rec->icon);
+ if (uwp_aumid)
+ handler_rec->uwp_aumid = g_wcsdup (uwp_aumid, -1);
+ if (handler_key)
+ read_handler_icon (handler_key, &handler_rec->icon);
g_hash_table_insert (handlers, g_strdup (handler_id_u8_folded), handler_rec);
return handler_rec;
@@ -1497,6 +1750,7 @@ handler_add_verb (gpointer handler_data1,
shverb->verb_displayname = g_strdup (verb_displayname);
shverb->command = g_wcsdup (command_line, -1);
shverb->command_utf8 = g_strdup (command_line_utf8);
+ shverb->is_uwp = FALSE; /* This function is for non-UWP verbs only */
if (app_rec)
shverb->app = g_object_ref (app_rec);
@@ -1533,7 +1787,7 @@ generate_new_verb_name (GPtrArray *verbs,
GWin32AppInfoShellVerb *shverb;
gsize orig_len = g_utf16_len (verb);
gsize new_verb_name_len = orig_len + strlen (" ()") + 2 + 1;
- gunichar2 *new_verb_name = g_malloc (new_verb_name_len * sizeof (gunichar2));
+ gunichar2 *new_verb_name = g_new (gunichar2, new_verb_name_len);
*new_verb = NULL;
*new_displayname = NULL;
@@ -1636,6 +1890,75 @@ app_add_verb (gpointer handler_data1,
g_ptr_array_insert (app_rec->verbs, 0, shverb);
}
+static void
+uwp_app_add_verb (GWin32AppInfoApplication *app_rec,
+ const gunichar2 *verb,
+ const gchar *verb_displayname)
+{
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (app_rec->verbs, verb, &shverb);
+
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->app = g_object_ref (app_rec);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+
+ shverb->is_uwp = TRUE;
+
+ /* Strictly speaking, this is unnecessary, but
+ * let's make it clear that UWP verbs have no
+ * commands and executables.
+ */
+ shverb->command = NULL;
+ shverb->command_utf8 = NULL;
+ shverb->executable = NULL;
+ shverb->executable_basename = NULL;
+ shverb->executable_folded = NULL;
+ shverb->dll_function = NULL;
+
+ g_ptr_array_add (app_rec->verbs, shverb);
+}
+
+static void
+uwp_handler_add_verb (GWin32AppInfoHandler *handler_rec,
+ GWin32AppInfoApplication *app,
+ const gunichar2 *verb,
+ const gchar *verb_displayname,
+ gboolean verb_is_preferred)
+{
+ GWin32AppInfoShellVerb *shverb;
+
+ _verb_lookup (handler_rec->verbs, verb, &shverb);
+
+ if (shverb != NULL)
+ return;
+
+ shverb = g_object_new (G_TYPE_WIN32_APPINFO_SHELL_VERB, NULL);
+ shverb->verb_name = g_wcsdup (verb, -1);
+ shverb->verb_displayname = g_strdup (verb_displayname);
+
+ shverb->is_uwp = TRUE;
+
+ if (app)
+ shverb->app = g_object_ref (app);
+
+ shverb->command = NULL;
+ shverb->command_utf8 = NULL;
+ shverb->executable = NULL;
+ shverb->executable_basename = NULL;
+ shverb->executable_folded = NULL;
+ shverb->dll_function = NULL;
+
+ if (!verb_is_preferred)
+ g_ptr_array_add (handler_rec->verbs, shverb);
+ else
+ g_ptr_array_insert (handler_rec->verbs, 0, shverb);
+}
+
/* Looks up a file extension object identified by
* @ext_u8_folded in the extensions hash table.
* If such object doesn't exist,
@@ -1899,7 +2222,8 @@ get_app_object (GHashTable *app_hashmap,
const gchar *canonical_name_u8,
const gchar *canonical_name_folded,
gboolean user_specific,
- gboolean default_app)
+ gboolean default_app,
+ gboolean is_uwp)
{
GWin32AppInfoApplication *app;
@@ -1915,6 +2239,7 @@ get_app_object (GHashTable *app_hashmap,
app->no_open_with = FALSE;
app->user_specific = user_specific;
app->default_app = default_app;
+ app->is_uwp = is_uwp;
g_hash_table_insert (app_hashmap,
g_strdup (canonical_name_folded),
app);
@@ -1950,6 +2275,7 @@ read_capable_app (const gunichar2 *app_key_path,
GWin32RegistryKey *associations;
const reg_verb *preferred_verb;
GList *verbs = NULL;
+ gboolean verbs_in_root_key = TRUE;
appkey = NULL;
capabilities = NULL;
@@ -1964,11 +2290,14 @@ read_capable_app (const gunichar2 *app_key_path,
&app_key_path_u8_folded) ||
(appkey = g_win32_registry_key_new_w (app_key_path, NULL)) == NULL ||
(capabilities = g_win32_registry_key_get_child_w (appkey, L"Capabilities", NULL)) == NULL ||
- !get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell"))
+ !(get_verbs (appkey, &preferred_verb, &verbs, L"", L"Shell", NULL) ||
+ (verbs_in_root_key = FALSE) ||
+ get_verbs (capabilities, &preferred_verb, &verbs, L"", L"Shell", NULL)))
{
g_clear_pointer (&canonical_name_u8, g_free);
g_clear_pointer (&canonical_name_folded, g_free);
g_clear_object (&appkey);
+ g_clear_object (&capabilities);
g_clear_pointer (&app_key_path_u8, g_free);
g_clear_pointer (&app_key_path_u8_folded, g_free);
@@ -1980,12 +2309,13 @@ read_capable_app (const gunichar2 *app_key_path,
canonical_name_u8,
canonical_name_folded,
user_specific,
- default_app);
+ default_app,
+ FALSE);
process_verbs_commands (g_steal_pointer (&verbs),
preferred_verb,
L"", /* [ab]use the fact that two strings are simply concatenated */
- g_win32_registry_key_get_path_w (capabilities),
+ verbs_in_root_key ? app_key_path : g_win32_registry_key_get_path_w (capabilities),
FALSE,
app_add_verb,
app,
@@ -2328,7 +2658,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app,
GIcon *icon = NULL;
GWin32RegistryKey *supported_key;
- if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell"))
+ if (!get_verbs (incapable_app, &preferred_verb, &verbs, L"", L"Shell", NULL))
return;
app = get_app_object (apps_by_exe,
@@ -2336,6 +2666,7 @@ read_incapable_app (GWin32RegistryKey *incapable_app,
app_exe_basename_u8,
app_exe_basename_u8_folded,
FALSE,
+ FALSE,
FALSE);
process_verbs_commands (g_steal_pointer (&verbs),
@@ -2743,6 +3074,9 @@ link_handlers_to_unregistered_apps (void)
{
gsize vi;
+ if (handler->uwp_aumid != NULL)
+ continue;
+
for (vi = 0; vi < handler->verbs->len; vi++)
{
GWin32AppInfoShellVerb *handler_verb;
@@ -2770,6 +3104,9 @@ link_handlers_to_unregistered_apps (void)
GWin32AppInfoShellVerb *app_verb;
gsize ai;
+ if (app->is_uwp)
+ continue;
+
for (ai = 0; ai < app->verbs->len; ai++)
{
GWin32PrivateStat app_verb_exec_info;
@@ -2820,6 +3157,9 @@ link_handlers_to_unregistered_apps (void)
(gpointer *) &appexe_fld_basename,
(gpointer *) &app))
{
+ if (app->is_uwp)
+ continue;
+
/* Use basename because apps_by_exe only has basenames */
if (g_strcmp0 (handler_exe_basename, appexe_fld_basename) != 0)
continue;
@@ -2866,6 +3206,9 @@ link_handlers_to_fake_apps (void)
{
gsize vi;
+ if (handler->uwp_aumid != NULL)
+ continue;
+
for (vi = 0; vi < handler->verbs->len; vi++)
{
GWin32AppInfoShellVerb *handler_verb;
@@ -2885,6 +3228,7 @@ link_handlers_to_fake_apps (void)
handler_verb->executable,
handler_verb->executable_folded,
FALSE,
+ FALSE,
FALSE);
g_clear_pointer (&exename_utf16, g_free);
handler_verb->app = g_object_ref (app);
@@ -2916,6 +3260,9 @@ link_handlers_to_fake_apps (void)
{
gsize vi;
+ if (handler->uwp_aumid != NULL)
+ continue;
+
for (vi = 0; vi < handler->verbs->len; vi++)
{
GWin32AppInfoShellVerb *handler_verb;
@@ -2932,6 +3279,7 @@ link_handlers_to_fake_apps (void)
handler_verb->command_utf8,
command_utf8_folded,
FALSE,
+ FALSE,
FALSE);
g_clear_pointer (&command_utf8_folded, g_free);
handler_verb->app = g_object_ref (app);
@@ -2952,6 +3300,404 @@ link_handlers_to_fake_apps (void)
}
}
+static GWin32AppInfoHandler *
+find_uwp_handler_for_ext (GWin32AppInfoFileExtension *file_extn,
+ const gunichar2 *app_user_model_id)
+{
+ GHashTableIter handler_iter;
+ gchar *handler_id_fld;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&handler_iter, file_extn->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
+ {
+ if (handler->uwp_aumid == NULL)
+ continue;
+
+ if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static GWin32AppInfoHandler *
+find_uwp_handler_for_schema (GWin32AppInfoURLSchema *schema,
+ const gunichar2 *app_user_model_id)
+{
+ GHashTableIter handler_iter;
+ gchar *handler_id_fld;
+ GWin32AppInfoHandler *handler;
+
+ g_hash_table_iter_init (&handler_iter, schema->handlers);
+ while (g_hash_table_iter_next (&handler_iter,
+ (gpointer *) &handler_id_fld,
+ (gpointer *) &handler))
+ {
+ if (handler->uwp_aumid == NULL)
+ continue;
+
+ if (_wcsicmp (handler->uwp_aumid, app_user_model_id) == 0)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static gboolean
+uwp_package_cb (gpointer user_data,
+ const gunichar2 *full_package_name,
+ const gunichar2 *package_name,
+ const gunichar2 *app_user_model_id,
+ gboolean show_in_applist,
+ GPtrArray *supported_extgroups,
+ GPtrArray *supported_protocols)
+{
+ gint i, i_verb, i_ext;
+ gint extensions_considered;
+ GWin32AppInfoApplication *app;
+ gchar *app_user_model_id_u8;
+ gchar *app_user_model_id_u8_folded;
+ GHashTableIter iter;
+ GWin32AppInfoHandler *ext;
+ GWin32AppInfoHandler *url;
+
+ if (!g_utf16_to_utf8_and_fold (app_user_model_id,
+ -1,
+ &app_user_model_id_u8,
+ &app_user_model_id_u8_folded))
+ return TRUE;
+
+ app = get_app_object (apps_by_id,
+ app_user_model_id,
+ app_user_model_id_u8,
+ app_user_model_id_u8_folded,
+ TRUE,
+ FALSE,
+ TRUE);
+
+ extensions_considered = 0;
+
+ for (i = 0; i < supported_extgroups->len; i++)
+ {
+ GWin32PackageExtGroup *grp = (GWin32PackageExtGroup *) g_ptr_array_index (supported_extgroups, i);
+
+ extensions_considered += grp->extensions->len;
+
+ for (i_ext = 0; i_ext < grp->extensions->len; i_ext++)
+ {
+ wchar_t *ext = (wchar_t *) g_ptr_array_index (grp->extensions, i_ext);
+ gchar *ext_u8;
+ gchar *ext_u8_folded;
+ GWin32AppInfoFileExtension *file_extn;
+ GWin32AppInfoHandler *handler_rec;
+
+ if (!g_utf16_to_utf8_and_fold (ext,
+ -1,
+ &ext_u8,
+ &ext_u8_folded))
+ continue;
+
+ file_extn = get_ext_object (ext, ext_u8, ext_u8_folded);
+ g_free (ext_u8);
+ handler_rec = find_uwp_handler_for_ext (file_extn, app_user_model_id);
+
+ if (handler_rec == NULL)
+ {
+ /* Use AppUserModelId as the ID of the new fake handler */
+ handler_rec = get_handler_object (app_user_model_id_u8_folded,
+ NULL,
+ app_user_model_id,
+ app_user_model_id);
+ g_hash_table_insert (file_extn->handlers,
+ g_strdup (app_user_model_id_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ if (file_extn->chosen_handler == NULL)
+ g_set_object (&file_extn->chosen_handler, handler_rec);
+
+ /* This is somewhat wasteful, but for 100% correct handling
+ * we need to remember which extensions (handlers) support
+ * which verbs, and each handler gets its own copy of the
+ * verb object, since our design is handler-centric,
+ * not verb-centric. The app also gets a list of verbs,
+ * but without handlers it would have no idea which
+ * verbs can be used with which extensions.
+ */
+ for (i_verb = 0; i_verb < grp->verbs->len; i_verb++)
+ {
+ wchar_t *verb = NULL;
+
+ verb = (wchar_t *) g_ptr_array_index (grp->verbs, i_verb);
+ /* *_add_verb() functions are no-ops when a verb already exists,
+ * so we're free to call them as many times as we want.
+ */
+ uwp_handler_add_verb (handler_rec,
+ app,
+ verb,
+ NULL,
+ FALSE);
+ }
+
+ g_hash_table_insert (app->supported_exts,
+ g_steal_pointer (&ext_u8_folded),
+ g_object_ref (handler_rec));
+ }
+ }
+
+ g_hash_table_iter_init (&iter, app->supported_exts);
+
+ /* Pile up all handler verbs into the app too,
+ * for cases when we don't have a ref to a handler.
+ */
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &ext))
+ {
+ gint i_hverb;
+
+ if (!ext)
+ continue;
+
+ for (i_hverb = 0; i_hverb < ext->verbs->len; i_hverb++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+
+ handler_verb = _verb_idx (ext->verbs, i_hverb);
+ uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname);
+ if (handler_verb->app == NULL && handler_verb->is_uwp)
+ handler_verb->app = g_object_ref (app);
+ }
+ }
+
+ 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);
+
+ for (i = 0; i < supported_protocols->len; i++)
+ {
+ wchar_t *proto = (wchar_t *) g_ptr_array_index (supported_protocols, i);
+ gchar *proto_u8;
+ gchar *proto_u8_folded;
+ GWin32AppInfoURLSchema *schema_rec;
+ GWin32AppInfoHandler *handler_rec;
+
+ if (!g_utf16_to_utf8_and_fold (proto,
+ -1,
+ &proto_u8,
+ &proto_u8_folded))
+ continue;
+
+ schema_rec = get_schema_object (proto,
+ proto_u8,
+ proto_u8_folded);
+
+ g_free (proto_u8);
+
+ handler_rec = find_uwp_handler_for_schema (schema_rec, app_user_model_id);
+
+ if (handler_rec == NULL)
+ {
+ /* Use AppUserModelId as the ID of the new fake handler */
+ handler_rec = get_handler_object (app_user_model_id_u8_folded,
+ NULL,
+ app_user_model_id,
+ app_user_model_id);
+
+ g_hash_table_insert (schema_rec->handlers,
+ g_strdup (app_user_model_id_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ if (schema_rec->chosen_handler == NULL)
+ g_set_object (&schema_rec->chosen_handler, handler_rec);
+
+ /* Technically, UWP apps don't use verbs for URIs,
+ * but we only store an app field in verbs,
+ * so each UWP URI handler has to have one.
+ * Let's call it "open".
+ */
+ uwp_handler_add_verb (handler_rec,
+ app,
+ L"open",
+ NULL,
+ TRUE);
+
+ g_hash_table_insert (app->supported_urls,
+ g_steal_pointer (&proto_u8_folded),
+ g_object_ref (handler_rec));
+ }
+
+ g_hash_table_iter_init (&iter, app->supported_urls);
+
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &url))
+ {
+ gint i_hverb;
+
+ if (!url)
+ continue;
+
+ for (i_hverb = 0; i_hverb < url->verbs->len; i_hverb++)
+ {
+ GWin32AppInfoShellVerb *handler_verb;
+
+ handler_verb = _verb_idx (url->verbs, i_hverb);
+ uwp_app_add_verb (app, handler_verb->verb_name, handler_verb->verb_displayname);
+ if (handler_verb->app == NULL && handler_verb->is_uwp)
+ handler_verb->app = g_object_ref (app);
+ }
+ }
+
+ g_free (app_user_model_id_u8);
+ g_free (app_user_model_id_u8_folded);
+
+ return TRUE;
+}
+
+/* Calls SHLoadIndirectString() in a loop to resolve
+ * a string in @{...} format (also supports other indirect
+ * strings, but we aren't using it for those).
+ * Consumes the input, but may return it unmodified
+ * (not an indirect string). May return %NULL (the string
+ * is indirect, but the OS failed to load it).
+ */
+static gunichar2 *
+resolve_string (gunichar2 *at_string)
+{
+ HRESULT hr;
+ gunichar2 *result = NULL;
+ gsize result_size;
+ /* This value is arbitrary */
+ const gsize reasonable_size_limit = 8192;
+
+ if (at_string == NULL || at_string[0] != L'@')
+ return at_string;
+
+ /* In case of a no-op @at_string will be copied into the output,
+ * buffer so allocate at least that much.
+ */
+ result_size = wcslen (at_string) + 1;
+
+ while (TRUE)
+ {
+ result = g_renew (gunichar2, result, result_size);
+ /* Since there's no built-in way to detect too small buffer size,
+ * we do so by putting a sentinel at the end of the buffer.
+ * If it's 0 (result is always 0-terminated, even if the buffer
+ * is too small), then try larger buffer.
+ */
+ result[result_size - 1] = 0xff;
+ /* This string accepts size in characters, not bytes. */
+ hr = SHLoadIndirectString (at_string, result, result_size, NULL);
+ if (!SUCCEEDED (hr))
+ {
+ g_free (result);
+ g_free (at_string);
+ return NULL;
+ }
+ else if (result[result_size - 1] != 0 ||
+ result_size >= reasonable_size_limit)
+ {
+ /* Now that the length is known, allocate the exact amount */
+ gunichar2 *copy = g_wcsdup (result, -1);
+ g_free (result);
+ g_free (at_string);
+ return copy;
+ }
+
+ result_size *= 2;
+ }
+
+ g_assert_not_reached ();
+
+ return at_string;
+}
+
+static void
+grab_registry_string (GWin32RegistryKey *handler_appkey,
+ const gunichar2 *value_name,
+ gunichar2 **destination,
+ gchar **destination_u8)
+{
+ gunichar2 *value;
+ gsize value_size;
+ GWin32RegistryValueType vtype;
+ const gunichar2 *ms_resource_prefix = L"ms-resource:";
+ gsize ms_resource_prefix_len = wcslen (ms_resource_prefix);
+
+ /* Right now this function is not used without destination,
+ * enforce this. destination_u8 is optional.
+ */
+ g_assert (destination != NULL);
+
+ if (*destination != NULL)
+ return;
+
+ if (g_win32_registry_key_get_value_w (handler_appkey,
+ NULL,
+ TRUE,
+ value_name,
+ &vtype,
+ (void **) &value,
+ &value_size,
+ NULL) &&
+ vtype != G_WIN32_REGISTRY_VALUE_STR)
+ g_clear_pointer (&value, g_free);
+
+ /* There's no way for us to resolve "ms-resource:..." strings */
+ if (value != NULL &&
+ value_size >= ms_resource_prefix_len &&
+ memcmp (value,
+ ms_resource_prefix,
+ ms_resource_prefix_len * sizeof (gunichar2)) == 0)
+ g_clear_pointer (&value, g_free);
+
+ if (value == NULL)
+ return;
+
+ *destination = resolve_string (g_steal_pointer (&value));
+
+ if (*destination == NULL)
+ return;
+
+ if (destination_u8)
+ *destination_u8 = g_utf16_to_utf8 (*destination, -1, NULL, NULL, NULL);
+}
+
+static void
+read_uwp_handler_info (void)
+{
+ GHashTableIter iter;
+ GWin32RegistryKey *handler_appkey;
+ gunichar2 *aumid;
+
+ g_hash_table_iter_init (&iter, uwp_handler_table);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &handler_appkey, (gpointer *) &aumid))
+ {
+ gchar *aumid_u8_folded;
+ GWin32AppInfoApplication *app;
+
+ if (!g_utf16_to_utf8_and_fold (aumid,
+ -1,
+ NULL,
+ &aumid_u8_folded))
+ continue;
+
+ app = g_hash_table_lookup (apps_by_id, aumid_u8_folded);
+ g_clear_pointer (&aumid_u8_folded, g_free);
+
+ if (app == NULL)
+ continue;
+
+ grab_registry_string (handler_appkey, L"ApplicationDescription", &app->description, &app->description_u8);
+ grab_registry_string (handler_appkey, L"ApplicationName", &app->localized_pretty_name, &app->localized_pretty_name_u8);
+ /* TODO: ApplicationIcon value (usually also @{...}) resolves into
+ * an image (PNG only?) with implicit multiple variants (scale, size, etc).
+ */
+ }
+}
static void
update_registry_data (void)
@@ -2963,7 +3709,8 @@ update_registry_data (void)
GWin32RegistryKey *url_associations;
GWin32RegistryKey *file_exts;
GWin32RegistryKey *classes_root;
- DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, postproc_end;
+ DWORD collect_start, collect_end, alloc_end, capable_end, url_end, ext_end, exeapp_end, classes_end, uwp_end, postproc_end;
+ GError *error = NULL;
url_associations =
g_win32_registry_key_new_w (L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations",
@@ -3009,6 +3756,8 @@ update_registry_data (void)
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
handlers =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ uwp_handler_table =
+ g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free);
alloc_end = GetTickCount ();
for (i = 0; i < priority_capable_apps_keys->len; i++)
@@ -3033,6 +3782,16 @@ update_registry_data (void)
exeapp_end = GetTickCount ();
read_classes (classes_root);
classes_end = GetTickCount ();
+
+ if (!g_win32_package_parser_enum_packages (uwp_package_cb, NULL, &error))
+ {
+ g_debug ("Unable to get UWP apps: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ read_uwp_handler_info ();
+
+ uwp_end = GetTickCount ();
link_handlers_to_unregistered_apps ();
link_handlers_to_fake_apps ();
postproc_end = GetTickCount ();
@@ -3044,6 +3803,7 @@ update_registry_data (void)
"Reading extension assocs: %lums\n"
"Reading exe-only apps:...... %lums\n"
"Reading classes: %lums\n"
+ "Reading UWP apps: %lums\n"
"Postprocessing:..............%lums\n"
"TOTAL: %lums",
collect_end - collect_start,
@@ -3053,7 +3813,8 @@ update_registry_data (void)
ext_end - url_end,
exeapp_end - ext_end,
classes_end - exeapp_end,
- postproc_end - classes_end,
+ uwp_end - classes_end,
+ postproc_end - uwp_end,
postproc_end - collect_start);
g_clear_object (&classes_root);
@@ -3062,6 +3823,7 @@ update_registry_data (void)
g_ptr_array_free (capable_apps_keys, TRUE);
g_ptr_array_free (user_capable_apps_keys, TRUE);
g_ptr_array_free (priority_capable_apps_keys, TRUE);
+ g_hash_table_unref (uwp_handler_table);
return;
}
@@ -3251,17 +4013,11 @@ gio_win32_appinfo_init (gboolean do_wait)
if (!do_wait)
return;
- /* If any of the keys had a change, then we've already initiated
- * a tree re-build in keys_updated(). Just wait for it to finish.
+ /* Previously, we checked each of the watched keys here.
+ * Now we just look at the update counter, because each key
+ * has a change callback keys_updated, which increments this counter.
*/
- if ((url_associations_key && g_win32_registry_key_has_changed (url_associations_key)) ||
- (file_exts_key && g_win32_registry_key_has_changed (file_exts_key)) ||
- (user_clients_key && g_win32_registry_key_has_changed (user_clients_key)) ||
- (system_clients_key && g_win32_registry_key_has_changed (system_clients_key)) ||
- (applications_key && g_win32_registry_key_has_changed (applications_key)) ||
- (user_registered_apps_key && g_win32_registry_key_has_changed (user_registered_apps_key)) ||
- (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
- (classes_root_key && g_win32_registry_key_has_changed (classes_root_key)))
+ if (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0)
{
g_mutex_lock (&gio_win32_appinfo_mutex);
while (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0)
@@ -3345,7 +4101,7 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
i += 1;
}
- new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+ new_info->supported_types = g_new (gchar *, i + 1);
i = 0;
g_hash_table_iter_init (&iter, new_info->app->supported_exts);
@@ -3392,7 +4148,7 @@ g_win32_app_info_dup (GAppInfo *appinfo)
for (i = 0; info->supported_types[i]; i++)
break;
- new_info->supported_types = g_malloc (sizeof (gchar *) * (i + 1));
+ new_info->supported_types = g_new (gchar *, i + 1);
for (i = 0; info->supported_types[i]; i++)
new_info->supported_types[i] = g_strdup (info->supported_types[i]);
@@ -3461,7 +4217,9 @@ g_win32_app_info_get_name (GAppInfo *appinfo)
{
GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
- if (info->app && info->app->canonical_name_u8)
+ if (info->app && info->app->pretty_name_u8)
+ return info->app->pretty_name_u8;
+ else if (info->app && info->app->canonical_name_u8)
return info->app->canonical_name_u8;
else
return P_("Unnamed");
@@ -3502,7 +4260,7 @@ g_win32_app_info_get_executable (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- if (info->app->verbs->len > 0)
+ if (info->app->verbs->len > 0 && !info->app->is_uwp)
return _verb_idx (info->app->verbs, 0)->executable;
return NULL;
@@ -3516,7 +4274,7 @@ g_win32_app_info_get_commandline (GAppInfo *appinfo)
if (info->app == NULL)
return NULL;
- if (info->app->verbs->len > 0)
+ if (info->app->verbs->len > 0 && !info->app->is_uwp)
return _verb_idx (info->app->verbs, 0)->command_utf8;
return NULL;
@@ -3912,8 +4670,51 @@ get_appath_for_exe (const gchar *exe_basename)
static gboolean
+g_win32_app_info_launch_uwp_internal (GWin32AppInfo *info,
+ gboolean for_files,
+ IShellItemArray *items,
+ GWin32AppInfoShellVerb *shverb,
+ GError **error)
+{
+ DWORD pid;
+ IApplicationActivationManager* paam = NULL;
+ gboolean result = TRUE;
+ HRESULT hr;
+
+ hr = CoCreateInstance (&CLSID_ApplicationActivationManager, NULL, CLSCTX_INPROC_SERVER, &IID_IApplicationActivationManager, (void **) &paam);
+ if (FAILED (hr))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to create ApplicationActivationManager: 0x%lx", hr);
+ return FALSE;
+ }
+
+ if (items == NULL)
+ hr = IApplicationActivationManager_ActivateApplication (paam, (const wchar_t *) info->app->canonical_name, NULL, AO_NONE, &pid);
+ else if (for_files)
+ hr = IApplicationActivationManager_ActivateForFile (paam, (const wchar_t *) info->app->canonical_name, items, shverb->verb_name, &pid);
+ else
+ hr = IApplicationActivationManager_ActivateForProtocol (paam, (const wchar_t *) info->app->canonical_name, items, &pid);
+
+ if (FAILED (hr))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "The app %s failed to launch: 0x%lx",
+ g_win32_appinfo_application_get_some_name (info->app), hr);
+ result = FALSE;
+ }
+
+ IApplicationActivationManager_Release (paam);
+
+ return result;
+}
+
+
+static gboolean
g_win32_app_info_launch_internal (GWin32AppInfo *info,
- GList *objs,
+ GList *objs, /* non-UWP only */
+ gboolean for_files, /* UWP only */
+ IShellItemArray *items, /* UWP only */
GAppLaunchContext *launch_context,
GSpawnFlags spawn_flags,
GError **error)
@@ -3929,15 +4730,10 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
g_return_val_if_fail (info->app != NULL, FALSE);
argv = NULL;
-
- if (launch_context)
- envp = g_app_launch_context_get_environment (launch_context);
- else
- envp = g_get_environ ();
-
shverb = NULL;
- if (info->handler != NULL &&
+ if (!info->app->is_uwp &&
+ info->handler != NULL &&
info->handler->verbs->len > 0)
shverb = _verb_idx (info->handler->verbs, 0);
else if (info->app->verbs->len > 0)
@@ -3945,7 +4741,7 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
if (shverb == NULL)
{
- if (info->handler == NULL)
+ if (info->app->is_uwp || info->handler == NULL)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
P_("The app ‘%s’ in the application object has no verbs"),
g_win32_appinfo_application_get_some_name (info->app));
@@ -3958,6 +4754,18 @@ g_win32_app_info_launch_internal (GWin32AppInfo *info,
return FALSE;
}
+ if (info->app->is_uwp)
+ return g_win32_app_info_launch_uwp_internal (info,
+ for_files,
+ items,
+ shverb,
+ error);
+
+ if (launch_context)
+ envp = g_app_launch_context_get_environment (launch_context);
+ else
+ envp = g_get_environ ();
+
g_assert (shverb->command_utf8 != NULL);
command = shverb->command_utf8;
apppath = get_appath_for_exe (shverb->executable_basename);
@@ -4105,6 +4913,95 @@ g_win32_app_info_supports_files (GAppInfo *appinfo)
}
+static IShellItemArray *
+make_item_array (gboolean for_files,
+ GList *files_or_uris,
+ GError **error)
+{
+ ITEMIDLIST **item_ids;
+ IShellItemArray *items;
+ GList *p;
+ gsize count;
+ gsize i;
+ HRESULT hr;
+
+ count = g_list_length (files_or_uris);
+
+ items = NULL;
+ item_ids = g_new (ITEMIDLIST*, count);
+
+ for (i = 0, p = files_or_uris; p != NULL; p = p->next, i++)
+ {
+ wchar_t *file_or_uri_utf16;
+
+ if (!for_files)
+ file_or_uri_utf16 = g_utf8_to_utf16 ((gchar *) p->data, -1, NULL, NULL, error);
+ else
+ file_or_uri_utf16 = g_utf8_to_utf16 (g_file_peek_path (G_FILE (p->data)), -1, NULL, NULL, error);
+
+ if (file_or_uri_utf16 == NULL)
+ break;
+
+ if (for_files)
+ {
+ wchar_t *c;
+ gsize len;
+ gsize len_tail;
+
+ len = wcslen (file_or_uri_utf16);
+ /* Filenames *MUST* use single backslashes, else the call
+ * will fail. First convert all slashes to backslashes,
+ * then remove duplicates.
+ */
+ for (c = file_or_uri_utf16; for_files && *c != 0; c++)
+ {
+ if (*c == L'/')
+ *c = L'\\';
+ }
+ for (len_tail = 0, c = &file_or_uri_utf16[len - 1];
+ for_files && c > file_or_uri_utf16;
+ c--, len_tail++)
+ {
+ if (c[0] != L'\\' || c[-1] != L'\\')
+ continue;
+
+ memmove (&c[-1], &c[0], len_tail * sizeof (wchar_t));
+ }
+ }
+
+ hr = SHParseDisplayName (file_or_uri_utf16, NULL, &item_ids[i], 0, NULL);
+ g_free (file_or_uri_utf16);
+
+ if (FAILED (hr))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "File or URI `%S' cannot be parsed by SHParseDisplayName: 0x%lx", file_or_uri_utf16, hr);
+ break;
+ }
+ }
+
+ if (i == count)
+ {
+ hr = SHCreateShellItemArrayFromIDLists (count, (const ITEMIDLIST **) item_ids, &items);
+ if (FAILED (hr))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "SHCreateShellItemArrayFromIDLists() failed: 0x%lx", hr);
+ items = NULL;
+ }
+ }
+
+ count = i;
+
+ for (i = 0; i < count; i++)
+ CoTaskMemFree (item_ids[i]);
+
+ g_free (item_ids);
+
+ return items;
+}
+
+
static gboolean
g_win32_app_info_launch_uris (GAppInfo *appinfo,
GList *uris,
@@ -4114,6 +5011,26 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
gboolean res = FALSE;
gboolean do_files;
GList *objs;
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app != NULL && info->app->is_uwp)
+ {
+ IShellItemArray *items = NULL;
+
+ if (uris)
+ {
+ items = make_item_array (FALSE, uris, error);
+ if (items == NULL)
+ return res;
+ }
+
+ res = g_win32_app_info_launch_internal (info, NULL, FALSE, items, launch_context, 0, error);
+
+ if (items != NULL)
+ IShellItemArray_Release (items);
+
+ return res;
+ }
do_files = g_win32_app_info_supports_files (appinfo);
@@ -4142,8 +5059,10 @@ g_win32_app_info_launch_uris (GAppInfo *appinfo,
objs = g_list_reverse (objs);
- res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+ res = g_win32_app_info_launch_internal (info,
objs,
+ FALSE,
+ NULL,
launch_context,
G_SPAWN_SEARCH_PATH,
error);
@@ -4162,6 +5081,26 @@ g_win32_app_info_launch (GAppInfo *appinfo,
gboolean res = FALSE;
gboolean do_uris;
GList *objs;
+ GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+ if (info->app != NULL && info->app->is_uwp)
+ {
+ IShellItemArray *items = NULL;
+
+ if (files)
+ {
+ items = make_item_array (TRUE, files, error);
+ if (items == NULL)
+ return res;
+ }
+
+ res = g_win32_app_info_launch_internal (info, NULL, TRUE, items, launch_context, 0, error);
+
+ if (items != NULL)
+ IShellItemArray_Release (items);
+
+ return res;
+ }
do_uris = g_win32_app_info_supports_uris (appinfo);
@@ -4181,8 +5120,10 @@ g_win32_app_info_launch (GAppInfo *appinfo,
objs = g_list_reverse (objs);
- res = g_win32_app_info_launch_internal (G_WIN32_APP_INFO (appinfo),
+ res = g_win32_app_info_launch_internal (info,
objs,
+ TRUE,
+ NULL,
launch_context,
G_SPAWN_SEARCH_PATH,
error);
diff --git a/gio/gwin32file-sync-stream.c b/gio/gwin32file-sync-stream.c
new file mode 100755
index 000000000..bc3b60694
--- /dev/null
+++ b/gio/gwin32file-sync-stream.c
@@ -0,0 +1,508 @@
+/* gwin32file-sync-stream.c - a simple IStream implementation
+ *
+ * Copyright 2020 Руслан Ижбулатов
+ *
+ * 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 COM object that implements an IStream backed by a file HANDLE.
+ * Works just like `SHCreateStreamOnFileEx()`, but does not
+ * support locking, and doesn't force us to link to libshlwapi.
+ * Only supports synchronous access.
+ */
+#include "config.h"
+#define COBJMACROS
+#define INITGUID
+#include <windows.h>
+
+#include "gwin32file-sync-stream.h"
+
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_query_interface (IStream *self_ptr,
+ REFIID ref_interface_guid,
+ LPVOID *output_object_ptr);
+static ULONG STDMETHODCALLTYPE _file_sync_stream_release (IStream *self_ptr);
+static ULONG STDMETHODCALLTYPE _file_sync_stream_add_ref (IStream *self_ptr);
+
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_read (IStream *self_ptr,
+ void *output_data,
+ ULONG bytes_to_read,
+ ULONG *output_bytes_read);
+
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_write (IStream *self_ptr,
+ const void *data,
+ ULONG bytes_to_write,
+ ULONG *output_bytes_written);
+
+
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_clone (IStream *self_ptr,
+ IStream **output_clone_ptr);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_commit (IStream *self_ptr,
+ DWORD commit_flags);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_copy_to (IStream *self_ptr,
+ IStream *output_stream,
+ ULARGE_INTEGER bytes_to_copy,
+ ULARGE_INTEGER *output_bytes_read,
+ ULARGE_INTEGER *output_bytes_written);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_lock_region (IStream *self_ptr,
+ ULARGE_INTEGER lock_offset,
+ ULARGE_INTEGER lock_bytes,
+ DWORD lock_type);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_revert (IStream *self_ptr);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_seek (IStream *self_ptr,
+ LARGE_INTEGER move_distance,
+ DWORD origin,
+ ULARGE_INTEGER *output_new_position);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_set_size (IStream *self_ptr,
+ ULARGE_INTEGER new_size);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_stat (IStream *self_ptr,
+ STATSTG *output_stat,
+ DWORD flags);
+static HRESULT STDMETHODCALLTYPE _file_sync_stream_unlock_region (IStream *self_ptr,
+ ULARGE_INTEGER lock_offset,
+ ULARGE_INTEGER lock_bytes,
+ DWORD lock_type);
+
+static void _file_sync_stream_free (GWin32FileSyncStream *self);
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_query_interface (IStream *self_ptr,
+ REFIID ref_interface_guid,
+ LPVOID *output_object_ptr)
+{
+ *output_object_ptr = NULL;
+
+ if (IsEqualGUID (ref_interface_guid, &IID_IUnknown))
+ {
+ IUnknown_AddRef ((IUnknown *) self_ptr);
+ *output_object_ptr = self_ptr;
+ return S_OK;
+ }
+ else if (IsEqualGUID (ref_interface_guid, &IID_IStream))
+ {
+ IStream_AddRef (self_ptr);
+ *output_object_ptr = self_ptr;
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE
+_file_sync_stream_add_ref (IStream *self_ptr)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+
+ return ++self->ref_count;
+}
+
+static ULONG STDMETHODCALLTYPE
+_file_sync_stream_release (IStream *self_ptr)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+
+ int ref_count = --self->ref_count;
+
+ if (ref_count == 0)
+ _file_sync_stream_free (self);
+
+ return ref_count;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_read (IStream *self_ptr,
+ void *output_data,
+ ULONG bytes_to_read,
+ ULONG *output_bytes_read)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+ DWORD bytes_read;
+
+ if (!ReadFile (self->file_handle, output_data, bytes_to_read, &bytes_read, NULL))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ if (output_bytes_read)
+ *output_bytes_read = bytes_read;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_write (IStream *self_ptr,
+ const void *data,
+ ULONG bytes_to_write,
+ ULONG *output_bytes_written)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+ DWORD bytes_written;
+
+ if (!WriteFile (self->file_handle, data, bytes_to_write, &bytes_written, NULL))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ if (output_bytes_written)
+ *output_bytes_written = bytes_written;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_seek (IStream *self_ptr,
+ LARGE_INTEGER move_distance,
+ DWORD origin,
+ ULARGE_INTEGER *output_new_position)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+ LARGE_INTEGER new_position;
+ DWORD move_method;
+
+ switch (origin)
+ {
+ case STREAM_SEEK_SET:
+ move_method = FILE_BEGIN;
+ break;
+ case STREAM_SEEK_CUR:
+ move_method = FILE_CURRENT;
+ break;
+ case STREAM_SEEK_END:
+ move_method = FILE_END;
+ break;
+ default:
+ return E_INVALIDARG;
+ }
+
+ if (!SetFilePointerEx (self->file_handle, move_distance, &new_position, move_method))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ (*output_new_position).QuadPart = new_position.QuadPart;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_set_size (IStream *self_ptr,
+ ULARGE_INTEGER new_size)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+ FILE_END_OF_FILE_INFO info;
+
+ info.EndOfFile.QuadPart = new_size.QuadPart;
+
+ if (SetFileInformationByHandle (self->file_handle, FileEndOfFileInfo, &info, sizeof (info)))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_copy_to (IStream *self_ptr,
+ IStream *output_stream,
+ ULARGE_INTEGER bytes_to_copy,
+ ULARGE_INTEGER *output_bytes_read,
+ ULARGE_INTEGER *output_bytes_written)
+{
+ ULARGE_INTEGER counter;
+ ULARGE_INTEGER written_counter;
+ ULARGE_INTEGER read_counter;
+
+ counter.QuadPart = bytes_to_copy.QuadPart;
+ written_counter.QuadPart = 0;
+ read_counter.QuadPart = 0;
+
+ while (counter.QuadPart > 0)
+ {
+ HRESULT hr;
+ ULONG bytes_read;
+ ULONG bytes_written;
+ ULONG bytes_index;
+#define _INTERNAL_BUFSIZE 1024
+ BYTE buffer[_INTERNAL_BUFSIZE];
+#undef _INTERNAL_BUFSIZE
+ ULONG buffer_size = sizeof (buffer);
+ ULONG to_read = buffer_size;
+
+ if (counter.QuadPart < buffer_size)
+ to_read = (ULONG) counter.QuadPart;
+
+ /* Because MS SDK has a function IStream_Read() with 3 arguments */
+ hr = self_ptr->lpVtbl->Read (self_ptr, buffer, to_read, &bytes_read);
+ if (!SUCCEEDED (hr))
+ return hr;
+
+ read_counter.QuadPart += bytes_read;
+
+ if (bytes_read == 0)
+ break;
+
+ bytes_index = 0;
+
+ while (bytes_index < bytes_read)
+ {
+ /* Because MS SDK has a function IStream_Write() with 3 arguments */
+ hr = output_stream->lpVtbl->Write (output_stream, &buffer[bytes_index], bytes_read - bytes_index, &bytes_written);
+ if (!SUCCEEDED (hr))
+ return hr;
+
+ if (bytes_written == 0)
+ return __HRESULT_FROM_WIN32 (ERROR_WRITE_FAULT);
+
+ bytes_index += bytes_written;
+ written_counter.QuadPart += bytes_written;
+ }
+ }
+
+ if (output_bytes_read)
+ output_bytes_read->QuadPart = read_counter.QuadPart;
+ if (output_bytes_written)
+ output_bytes_written->QuadPart = written_counter.QuadPart;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_commit (IStream *self_ptr,
+ DWORD commit_flags)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+
+ if (!FlushFileBuffers (self->file_handle))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_revert (IStream *self_ptr)
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_lock_region (IStream *self_ptr,
+ ULARGE_INTEGER lock_offset,
+ ULARGE_INTEGER lock_bytes,
+ DWORD lock_type)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_unlock_region (IStream *self_ptr,
+ ULARGE_INTEGER lock_offset,
+ ULARGE_INTEGER lock_bytes,
+ DWORD lock_type)
+{
+ return STG_E_INVALIDFUNCTION;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_stat (IStream *self_ptr,
+ STATSTG *output_stat,
+ DWORD flags)
+{
+ GWin32FileSyncStream *self = (GWin32FileSyncStream *) self_ptr;
+ BOOL get_name = FALSE;
+ FILE_BASIC_INFO bi;
+ FILE_STANDARD_INFO si;
+
+ if (output_stat == NULL)
+ return STG_E_INVALIDPOINTER;
+
+ switch (flags)
+ {
+ case STATFLAG_DEFAULT:
+ get_name = TRUE;
+ break;
+ case STATFLAG_NONAME:
+ get_name = FALSE;
+ break;
+ default:
+ return STG_E_INVALIDFLAG;
+ }
+
+ if (!GetFileInformationByHandleEx (self->file_handle, FileBasicInfo, &bi, sizeof (bi)) ||
+ !GetFileInformationByHandleEx (self->file_handle, FileStandardInfo, &si, sizeof (si)))
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ output_stat->type = STGTY_STREAM;
+ output_stat->mtime.dwLowDateTime = bi.LastWriteTime.LowPart;
+ output_stat->mtime.dwHighDateTime = bi.LastWriteTime.HighPart;
+ output_stat->ctime.dwLowDateTime = bi.CreationTime.LowPart;
+ output_stat->ctime.dwHighDateTime = bi.CreationTime.HighPart;
+ output_stat->atime.dwLowDateTime = bi.LastAccessTime.LowPart;
+ output_stat->atime.dwHighDateTime = bi.LastAccessTime.HighPart;
+ output_stat->grfLocksSupported = 0;
+ memset (&output_stat->clsid, 0, sizeof (CLSID));
+ output_stat->grfStateBits = 0;
+ output_stat->reserved = 0;
+ output_stat->cbSize.QuadPart = si.EndOfFile.QuadPart;
+ output_stat->grfMode = self->stgm_mode;
+
+ if (get_name)
+ {
+ DWORD tries;
+ wchar_t *buffer;
+
+ /* Nothing in the documentation guarantees that the name
+ * won't change between two invocations (one - to get the
+ * buffer size, the other - to fill the buffer).
+ * Re-try up to 5 times in case the required buffer size
+ * doesn't match.
+ */
+ for (tries = 5; tries > 0; tries--)
+ {
+ DWORD buffer_size;
+ DWORD buffer_size2;
+ DWORD error;
+
+ buffer_size = GetFinalPathNameByHandleW (self->file_handle, NULL, 0, 0);
+
+ if (buffer_size == 0)
+ {
+ DWORD error = GetLastError ();
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ buffer = CoTaskMemAlloc (buffer_size);
+ buffer[buffer_size - 1] = 0;
+ buffer_size2 = GetFinalPathNameByHandleW (self->file_handle, buffer, buffer_size, 0);
+
+ if (buffer_size2 < buffer_size)
+ break;
+
+ error = GetLastError ();
+ CoTaskMemFree (buffer);
+ if (buffer_size2 == 0)
+ return __HRESULT_FROM_WIN32 (error);
+ }
+
+ if (tries == 0)
+ return __HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
+
+ output_stat->pwcsName = buffer;
+ }
+ else
+ output_stat->pwcsName = NULL;
+
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+_file_sync_stream_clone (IStream *self_ptr,
+ IStream **output_clone_ptr)
+{
+ return E_NOTIMPL;
+}
+
+static IStreamVtbl _file_sync_stream_vtbl = {
+ _file_sync_stream_query_interface,
+ _file_sync_stream_add_ref,
+ _file_sync_stream_release,
+ _file_sync_stream_read,
+ _file_sync_stream_write,
+ _file_sync_stream_seek,
+ _file_sync_stream_set_size,
+ _file_sync_stream_copy_to,
+ _file_sync_stream_commit,
+ _file_sync_stream_revert,
+ _file_sync_stream_lock_region,
+ _file_sync_stream_unlock_region,
+ _file_sync_stream_stat,
+ _file_sync_stream_clone,
+};
+
+static void
+_file_sync_stream_free (GWin32FileSyncStream *self)
+{
+ if (self->owns_handle)
+ CloseHandle (self->file_handle);
+
+ g_free (self);
+}
+
+/**
+ * g_win32_file_sync_stream_new:
+ * @handle: a Win32 HANDLE for a file.
+ * @owns_handle: %TRUE if newly-created stream owns the handle
+ * (and closes it when destroyed)
+ * @stgm_mode: a combination of [STGM constants](https://docs.microsoft.com/en-us/windows/win32/stg/stgm-constants)
+ * that specify the mode with which the stream
+ * is opened.
+ * @output_hresult: (out) (optional): a HRESULT from the internal COM calls.
+ * Will be `S_OK` on success.
+ *
+ * Creates an IStream object backed by a HANDLE.
+ *
+ * @stgm_mode should match the mode of the @handle, otherwise the stream might
+ * attempt to perform operations that the @handle does not allow. The implementation
+ * itself ignores these flags completely, they are only used to report
+ * the mode of the stream to third parties.
+ *
+ * The stream only does synchronous access and will never return `E_PENDING` on I/O.
+ *
+ * The returned stream object should be treated just like any other
+ * COM object, and released via `IUnknown_Release()`.
+ * its elements have been unreffed with g_object_unref().
+ *
+ * Returns: (nullable) (transfer full): a new IStream object on success, %NULL on failure.
+ **/
+IStream *
+g_win32_file_sync_stream_new (HANDLE file_handle,
+ gboolean owns_handle,
+ DWORD stgm_mode,
+ HRESULT *output_hresult)
+{
+ GWin32FileSyncStream *new_stream;
+ IStream *result;
+ HRESULT hr;
+
+ new_stream = g_new0 (GWin32FileSyncStream, 1);
+ new_stream->self.lpVtbl = &_file_sync_stream_vtbl;
+
+ hr = IUnknown_QueryInterface ((IUnknown *) new_stream, &IID_IStream, (void **) &result);
+ if (!SUCCEEDED (hr))
+ {
+ g_free (new_stream);
+
+ if (output_hresult)
+ *output_hresult = hr;
+
+ return NULL;
+ }
+
+ new_stream->stgm_mode = stgm_mode;
+ new_stream->file_handle = file_handle;
+ new_stream->owns_handle = owns_handle;
+
+ if (output_hresult)
+ *output_hresult = S_OK;
+
+ return result;
+}
diff --git a/gio/gwin32file-sync-stream.h b/gio/gwin32file-sync-stream.h
new file mode 100755
index 000000000..8e7f5fd59
--- /dev/null
+++ b/gio/gwin32file-sync-stream.h
@@ -0,0 +1,44 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2020 Руслан Ижбулатов <lrn1986@gmail.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/>.
+ *
+ */
+#ifndef __G_WIN32_FILE_SYNC_STREAM_H__
+#define __G_WIN32_FILE_SYNC_STREAM_H__
+
+#include <gio/gio.h>
+
+#ifdef G_PLATFORM_WIN32
+
+typedef struct _GWin32FileSyncStream GWin32FileSyncStream;
+
+struct _GWin32FileSyncStream
+{
+ IStream self;
+ ULONG ref_count;
+ HANDLE file_handle;
+ gboolean owns_handle;
+ DWORD stgm_mode;
+};
+
+IStream *g_win32_file_sync_stream_new (HANDLE file_handle,
+ gboolean owns_handle,
+ DWORD stgm_mode,
+ HRESULT *output_hresult);
+
+#endif /* G_PLATFORM_WIN32 */
+
+#endif /* __G_WIN32_FILE_SYNC_STREAM_H__ */ \ No newline at end of file
diff --git a/gio/gwin32mount.c b/gio/gwin32mount.c
index 5b4847a9d..83d8695a1 100644
--- a/gio/gwin32mount.c
+++ b/gio/gwin32mount.c
@@ -131,6 +131,9 @@ _g_win32_mount_new (GVolumeMonitor *volume_monitor,
{
GWin32Mount *mount;
const gchar *drive = path; //fixme
+ WCHAR *drive_utf16;
+
+ drive_utf16 = g_utf8_to_utf16 (drive, -1, NULL, NULL, NULL);
#if 0
/* No volume for mount: Ignore internal things */
@@ -141,7 +144,7 @@ _g_win32_mount_new (GVolumeMonitor *volume_monitor,
mount = g_object_new (G_TYPE_WIN32_MOUNT, NULL);
mount->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
mount->mount_path = g_strdup (path);
- mount->drive_type = GetDriveType (drive);
+ mount->drive_type = GetDriveTypeW (drive_utf16);
mount->can_eject = FALSE; /* TODO */
mount->name = _win32_get_displayname (drive);
@@ -151,6 +154,9 @@ _g_win32_mount_new (GVolumeMonitor *volume_monitor,
if (volume != NULL)
_g_win32_volume_set_mount (volume, mount);
#endif
+
+ g_free (drive_utf16);
+
return mount;
}
diff --git a/gio/gwin32packageparser.c b/gio/gwin32packageparser.c
new file mode 100755
index 000000000..745f1f522
--- /dev/null
+++ b/gio/gwin32packageparser.c
@@ -0,0 +1,815 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2020 Руслан Ижбулатов <lrn1986@gmail.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/>.
+ *
+ */
+
+/* Queries the system (Windows 8 or newer) for the list
+ * of UWP packages, parses their manifests and invokes
+ * a user-provided callback with the needed application
+ * info.
+ */
+
+#include "config.h"
+
+#define COBJMACROS
+#define INITGUID
+#include <windows.h>
+#include <winstring.h>
+#include <roapi.h>
+#include <stdio.h>
+#include <shlwapi.h>
+
+#include "gwin32api-storage.h"
+#include "gwin32api-misc.h"
+#include "gwin32api-iterator.h"
+#include "gwin32api-package.h"
+
+#include <xmllite.h>
+
+#include <glib.h>
+
+#include "gwin32file-sync-stream.h"
+#include "gwin32packageparser.h"
+
+#ifdef G_WINAPI_ONLY_APP
+#define LoadedRoActivateInstance RoActivateInstance
+#define LoadedWindowsCreateStringReference WindowsCreateStringReference
+#define LoadedWindowsDeleteString WindowsDeleteString
+#define sax_WindowsGetStringRawBuffer WindowsGetStringRawBuffer
+#define LoadedWindowsGetStringRawBuffer WindowsGetStringRawBuffer
+#define sax_CreateXmlReader CreateXmlReader
+#else
+typedef HRESULT (WINAPI *RoActivateInstance_func)(HSTRING activatableClassId, IInspectable **instance);
+typedef HRESULT (WINAPI *WindowsCreateStringReference_func)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);
+typedef HRESULT (WINAPI *WindowsDeleteString_func)(HSTRING string);
+typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_func)(HSTRING string, UINT32 *length);
+typedef HRESULT (STDAPICALLTYPE *CreateXmlReader_func)(REFIID riid, void **ppvObject, IMalloc *pMalloc);
+#define sax_WindowsGetStringRawBuffer sax->WindowsGetStringRawBuffer
+#define sax_CreateXmlReader sax->CreateXmlReader
+#endif
+
+static gssize
+g_utf16_len (const gunichar2 *str)
+{
+ gssize result;
+
+ for (result = 0; str[0] != 0; str++, result++)
+ ;
+
+ return result;
+}
+
+static gunichar2 *
+g_wcsdup (const gunichar2 *str, gssize str_len)
+{
+ gssize str_size;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (str_len == -1)
+ str_len = g_utf16_len (str);
+
+ g_assert (str_len <= G_MAXSIZE / sizeof (gunichar2) - 1);
+ str_size = (str_len + 1) * sizeof (gunichar2);
+
+ return g_memdup (str, str_size);
+}
+
+static BOOL
+WIN32_FROM_HRESULT (HRESULT hresult,
+ DWORD *win32_error_code)
+{
+ if ((hresult & 0xFFFF0000) == MAKE_HRESULT (SEVERITY_ERROR, FACILITY_WIN32, 0) ||
+ hresult == S_OK)
+ {
+ *win32_error_code = HRESULT_CODE (hresult);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GIOErrorEnum
+gio_error_from_hresult (HRESULT hresult)
+{
+ DWORD error;
+
+ if (WIN32_FROM_HRESULT (hresult, &error))
+ return g_io_error_from_errno (error);
+
+ return G_IO_ERROR_FAILED;
+}
+
+static void
+free_extgroup (GWin32PackageExtGroup *g)
+{
+ g_ptr_array_unref (g->verbs);
+ g_ptr_array_unref (g->extensions);
+ g_free (g);
+}
+
+struct _xml_sax_state
+{
+#ifndef G_WINAPI_ONLY_APP
+ WindowsGetStringRawBuffer_func WindowsGetStringRawBuffer;
+ CreateXmlReader_func CreateXmlReader;
+#endif
+
+ GWin32PackageParserCallback callback;
+ gpointer user_data;
+
+ const wchar_t *manifest_filename;
+ gsize package_index;
+ const wchar_t *wcs_full_name;
+ const wchar_t *wcs_name;
+ HSTRING package_family;
+
+ gboolean applist;
+ gboolean exit_early;
+
+ UINT64 in_package;
+ UINT64 in_applications;
+ UINT64 in_application;
+ UINT64 in_extensions;
+ UINT64 in_extension_protocol;
+ UINT64 in_extension_fta;
+ UINT64 in_fta_group;
+ UINT64 in_sfp;
+ UINT64 in_filetype;
+ UINT64 in_sv;
+ GPtrArray *supported_extensions;
+ GPtrArray *supported_protocols;
+ GPtrArray *supported_verbs;
+ GPtrArray *supported_extgroups;
+ wchar_t *application_usermodelid;
+};
+
+static gboolean parse_manifest_file (struct _xml_sax_state *sax);
+static gboolean xml_parser_iteration (struct _xml_sax_state *sax,
+ IXmlReader *xml_reader);
+static gboolean xml_parser_get_current_state (struct _xml_sax_state *sax,
+ IXmlReader *xml_reader,
+ const wchar_t **local_name,
+ const wchar_t **prefix,
+ const wchar_t **value);
+
+gboolean
+g_win32_package_parser_enum_packages (GWin32PackageParserCallback callback,
+ gpointer user_data,
+ GError **error)
+{
+ gboolean result = TRUE;
+ HRESULT hr;
+ HSTRING packagemanager_name = NULL;
+ HSTRING_HEADER packageanager_name_header;
+ const wchar_t *packman_id = L"Windows.Management.Deployment.PackageManager";
+ IInspectable *ii_pm = NULL;
+ IPackageManager *pm = NULL;
+ IIterable *packages_iterable = NULL;
+ IIterator *packages_iterator = NULL;
+ CHAR has_current;
+ gsize package_index;
+ const wchar_t *bslash_appmanifest = L"\\AppxManifest.xml";
+ struct _xml_sax_state sax_stack;
+ struct _xml_sax_state *sax = &sax_stack;
+
+#ifndef G_WINAPI_ONLY_APP
+ HMODULE xmllite = NULL;
+ HMODULE combase = NULL;
+ HMODULE winrt = NULL;
+ RoActivateInstance_func LoadedRoActivateInstance;
+ WindowsCreateStringReference_func LoadedWindowsCreateStringReference;
+ WindowsDeleteString_func LoadedWindowsDeleteString;
+ WindowsGetStringRawBuffer_func LoadedWindowsGetStringRawBuffer;
+ CreateXmlReader_func LoadedCreateXmlReader;
+
+ winrt = LoadLibraryW (L"api-ms-win-core-winrt-l1-1-0.dll");
+ if (winrt == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to load api-ms-win-core-winrt-l1-1-0.dll");
+ goto cleanup;
+ }
+
+ combase = LoadLibraryW (L"combase.dll");
+ if (combase == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to load combase.dll");
+ goto cleanup;
+ }
+
+ xmllite = LoadLibraryW (L"xmllite.dll");
+ if (xmllite == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "Failed to load xmllite.dll");
+ goto cleanup;
+ }
+
+ LoadedCreateXmlReader = (CreateXmlReader_func) GetProcAddress (xmllite, "CreateXmlReader");
+ if (LoadedCreateXmlReader == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "CreateXmlReader entry point is not found in xmllite.dll");
+ goto cleanup;
+ }
+
+ LoadedRoActivateInstance = (RoActivateInstance_func) GetProcAddress (winrt, "RoActivateInstance");
+ if (LoadedRoActivateInstance == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "RoActivateInstance entry point is not found in api-ms-win-core-winrt-l1-1-0.dll");
+ goto cleanup;
+ }
+
+ LoadedWindowsCreateStringReference = (WindowsCreateStringReference_func) GetProcAddress (combase, "WindowsCreateStringReference");
+ if (LoadedWindowsCreateStringReference == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "WindowsCreateStringReference entry point is not found in combase.dll");
+ goto cleanup;
+ }
+
+ LoadedWindowsDeleteString = (WindowsDeleteString_func) GetProcAddress (combase, "WindowsDeleteString");
+ if (LoadedWindowsDeleteString == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "WindowsDeleteString entry point is not found in combase.dll");
+ goto cleanup;
+ }
+
+ LoadedWindowsGetStringRawBuffer = (WindowsGetStringRawBuffer_func) GetProcAddress (combase, "WindowsGetStringRawBuffer");
+ if (LoadedWindowsGetStringRawBuffer == NULL)
+ {
+ result = FALSE;
+ g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+ "WindowsGetStringRawBuffer entry point is not found in combase.dll");
+ goto cleanup;
+ }
+#endif
+
+ /* This essentially locks current GLib thread into apartment COM model. */
+ hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
+ /* Can return S_FALSE if COM is already initialized,
+ * which is not an error, and we still need to uninitialize after that.
+ */
+ if (hr != S_OK && hr != S_FALSE)
+ {
+ result = FALSE;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
+ "CoInitializeEx(COINIT_APARTMENTTHREADED) failed with code 0x%lx", hr);
+ goto cleanup;
+ }
+
+#define canned_com_error_handler(function_name_literal, where_to_go) \
+ do \
+ { \
+ if (FAILED (hr)) \
+ { \
+ result = FALSE; \
+ g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
+ function_name_literal " failed with code 0x%lx", hr); \
+ goto where_to_go; \
+ } \
+ } while (0)
+
+ hr = LoadedWindowsCreateStringReference (packman_id, wcslen (packman_id), &packageanager_name_header, &packagemanager_name);
+ canned_com_error_handler ("WindowsCreateStringReference()", cleanup);
+
+ hr = LoadedRoActivateInstance (packagemanager_name, &ii_pm);
+ canned_com_error_handler ("RoActivateInstance()", cleanup);
+
+ hr = IInspectable_QueryInterface (ii_pm, &IID_IPackageManager, (void**) &pm);
+ canned_com_error_handler ("IInspectable_QueryInterface()", cleanup);
+
+ hr = IPackageManager_FindPackagesByUserSecurityId (pm, 0, &packages_iterable);
+ canned_com_error_handler ("IPackageManager_FindPackagesByUserSecurityId()", cleanup);
+
+ hr = IIterable_First (packages_iterable, &packages_iterator);
+ canned_com_error_handler ("IIterable_First()", cleanup);
+
+ hr = IIterator_get_HasCurrent (packages_iterator, &has_current);
+ canned_com_error_handler ("IIterator_get_HasCurrent()", cleanup);
+
+ for (package_index = 0; SUCCEEDED (hr) && has_current; package_index++)
+ {
+ IUnknown *item = NULL;
+ IPackage *ipackage = NULL;
+ IPackageId *ipackageid = NULL;
+ IUnknown *package_install_location = NULL;
+ IStorageItem *storage_item = NULL;
+ HSTRING path = NULL;
+ HSTRING name = NULL;
+ HSTRING full_name = NULL;
+ HSTRING package_family = NULL;
+ size_t manifest_filename_size;
+ const wchar_t *wcs_path;
+ const wchar_t *wcs_full_name;
+ const wchar_t *wcs_name;
+ wchar_t *manifest_filename = NULL;
+
+#define canned_com_error_handler_pkg(function_name_literal, where_to_go) \
+ do \
+ { \
+ if (FAILED (hr)) \
+ { \
+ result = FALSE; \
+ g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
+ function_name_literal " for package #%zu failed with code 0x%lx", package_index, hr); \
+ goto where_to_go; \
+ } \
+ } while (0)
+
+ hr = IIterator_get_Current (packages_iterator, &item);
+ canned_com_error_handler_pkg ("IIterator_get_Current()", package_cleanup);
+
+ hr = IUnknown_QueryInterface (item, &IID_IPackage, (void **) &ipackage);
+ canned_com_error_handler_pkg ("IUnknown_QueryInterface(IID_IPackage)", package_cleanup);
+
+ hr = IPackage_get_Id (ipackage, &ipackageid);
+ canned_com_error_handler_pkg ("IPackage_get_Id()", package_cleanup);
+
+ hr = IPackageId_get_FullName (ipackageid, &full_name);
+ canned_com_error_handler_pkg ("IPackageId_get_FullName()", package_cleanup);
+
+ hr = IPackageId_get_Name (ipackageid, &name);
+ canned_com_error_handler_pkg ("IPackageId_get_Name()", package_cleanup);
+
+ wcs_full_name = LoadedWindowsGetStringRawBuffer (full_name, NULL);
+ wcs_name = LoadedWindowsGetStringRawBuffer (name, NULL);
+
+#define canned_com_error_handler_pkg_named(function_name_literal, where_to_go) \
+ do \
+ { \
+ if (FAILED (hr)) \
+ { \
+ result = FALSE; \
+ g_set_error (error, G_IO_ERROR, gio_error_from_hresult (hr), \
+ function_name_literal " for package #%zu (`%S') failed with code 0x%lx", package_index, wcs_full_name, hr); \
+ goto where_to_go; \
+ } \
+ } while (0)
+
+ hr = IPackage_get_InstalledLocation (ipackage, &package_install_location);
+ canned_com_error_handler_pkg_named ("IPackage_get_InstalledLocation()", package_cleanup);
+
+ hr = IUnknown_QueryInterface (package_install_location, &IID_IStorageItem, (void **) &storage_item);
+ canned_com_error_handler_pkg_named ("IUnknown_QueryInterface(IID_IStorageItem)", package_cleanup);
+
+ hr = IPackageId_get_FamilyName (ipackageid, &package_family);
+ canned_com_error_handler_pkg_named ("IPackageId_get_FamilyName()", package_cleanup);
+
+ hr = IStorageItem_get_Path (storage_item, &path);
+ canned_com_error_handler_pkg_named ("IStorageItem_get_Path()", package_cleanup);
+
+ wcs_path = LoadedWindowsGetStringRawBuffer (path, NULL);
+ manifest_filename_size = wcslen (wcs_path) + wcslen (bslash_appmanifest);
+ manifest_filename = g_new (wchar_t, manifest_filename_size + 1);
+ memcpy (manifest_filename, wcs_path, manifest_filename_size * sizeof (wchar_t));
+ memcpy (&manifest_filename[wcslen (wcs_path)], bslash_appmanifest, (wcslen (bslash_appmanifest) + 1) * sizeof (wchar_t));
+
+ memset (sax, 0, sizeof (*sax));
+ sax->callback = callback;
+ sax->user_data = user_data;
+ sax->manifest_filename = manifest_filename;
+ sax->package_index = package_index;
+ sax->wcs_full_name = wcs_full_name;
+ sax->wcs_name = wcs_name;
+ sax->package_family = package_family;
+ sax->applist = TRUE;
+ sax->exit_early = FALSE;
+#ifndef G_WINAPI_ONLY_APP
+ sax->CreateXmlReader = LoadedCreateXmlReader;
+ sax->WindowsGetStringRawBuffer = LoadedWindowsGetStringRawBuffer;
+#endif
+ /* Result isn't checked. If we fail to parse the manifest,
+ * just try the next package, no need to bail out.
+ */
+ parse_manifest_file (sax);
+
+ hr = IIterator_MoveNext (packages_iterator, &has_current);
+ canned_com_error_handler_pkg_named ("IIterator_MoveNext()", package_cleanup);
+
+#undef canned_com_error_handler_pkg_named
+#undef canned_com_error_handler_pkg
+#undef canned_com_error_handler
+
+ package_cleanup:
+ g_clear_pointer (&manifest_filename, g_free);
+
+ if (path)
+ LoadedWindowsDeleteString (path);
+ if (storage_item)
+ (void) IStorageItem_Release (storage_item);
+ if (package_install_location)
+ (void) IUnknown_Release (package_install_location);
+ if (ipackage)
+ (void) IPackage_Release (ipackage);
+ if (item)
+ (void) IUnknown_Release (item);
+
+ if (package_family)
+ LoadedWindowsDeleteString (package_family);
+ if (name)
+ LoadedWindowsDeleteString (name);
+ if (full_name)
+ LoadedWindowsDeleteString (full_name);
+
+ if (ipackageid)
+ (void) IPackageId_Release (ipackageid);
+ if (sax->exit_early)
+ break;
+ }
+
+ cleanup:
+ if (packages_iterator)
+ (void) IIterator_Release (packages_iterator);
+ if (packages_iterable)
+ (void) IIterable_Release (packages_iterable);
+ if (pm)
+ (void) IPackageManager_Release (pm);
+ if (ii_pm)
+ (void) IInspectable_Release (ii_pm);
+
+ CoUninitialize ();
+
+#ifndef G_WINAPI_ONLY_APP
+ if (xmllite)
+ (void) FreeLibrary (xmllite);
+ if (combase)
+ (void) FreeLibrary (combase);
+ if (winrt)
+ (void) FreeLibrary (winrt);
+#endif
+
+ return result;
+}
+
+static gboolean
+parse_manifest_file (struct _xml_sax_state *sax)
+{
+ HRESULT hr;
+ HANDLE file_handle = INVALID_HANDLE_VALUE;
+ IStream *file_stream = NULL;
+ gboolean result;
+ IXmlReader *xml_reader;
+
+ file_handle = CreateFileW (sax->manifest_filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ {
+ g_warning ("Failed to open application manifest `%S' for package #%zu (`%S'): error code 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, GetLastError ());
+ return FALSE;
+ }
+
+ file_stream = g_win32_file_sync_stream_new (file_handle, TRUE, STGM_READ | STGM_SHARE_DENY_WRITE, &hr);
+ if (file_stream == NULL)
+ {
+ g_warning ("Failed to create an IStream for application manifest `%S' for package #%zu (`%S'): HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ CloseHandle (file_handle);
+ return FALSE;
+ }
+
+ /* file_stream owns it now */
+ file_handle = NULL;
+
+ hr = sax_CreateXmlReader (&IID_IXmlReader, (void **) &xml_reader, NULL);
+ /* Slightly incorrect - xml reader is not created for any particular file,
+ * in theory we could re-use the same xml reader instance for all files.
+ */
+ if (FAILED (hr))
+ {
+ g_warning ("CreateXmlReader() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ (void) IStream_Release (file_stream);
+ return FALSE;
+ }
+
+ hr = IXmlReader_SetInput (xml_reader, (IUnknown *) file_stream);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_SetInput() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ (void) IXmlReader_Release (xml_reader);
+ (void) IStream_Release (file_stream);
+ return FALSE;
+ }
+
+ sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup);
+
+ result = TRUE;
+
+ while (!sax->exit_early && result && !IXmlReader_IsEOF (xml_reader))
+ result = xml_parser_iteration (sax, xml_reader);
+
+ g_clear_pointer (&sax->application_usermodelid, g_free);
+ g_clear_pointer (&sax->supported_extensions, g_ptr_array_unref);
+ g_clear_pointer (&sax->supported_verbs, g_ptr_array_unref);
+ g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref);
+ g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref);
+
+ (void) IXmlReader_Release (xml_reader);
+ (void) IStream_Release (file_stream);
+
+ return result;
+}
+
+static gboolean
+xml_parser_get_current_state (struct _xml_sax_state *sax,
+ IXmlReader *xml_reader,
+ const wchar_t **local_name,
+ const wchar_t **prefix,
+ const wchar_t **value)
+{
+ HRESULT hr;
+ UINT xml_line_number;
+ UINT xml_line_position;
+
+ hr = IXmlReader_GetLineNumber (xml_reader, &xml_line_number);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_GetLineNumber() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ hr = IXmlReader_GetLinePosition (xml_reader, &xml_line_position);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_GetLinePosition() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ hr = IXmlReader_GetLocalName (xml_reader, local_name, NULL);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_GetLocalName() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ hr = IXmlReader_GetPrefix (xml_reader, prefix, NULL);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_GetPrefix() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ hr = IXmlReader_GetValue (xml_reader, value, NULL);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_GetValue() for application manifest `%S':%u (column %u) for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, xml_line_number, xml_line_position, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+xml_parser_iteration (struct _xml_sax_state *sax,
+ IXmlReader *xml_reader)
+{
+ HRESULT hr;
+ XmlNodeType xml_node_type;
+ const wchar_t *local_name;
+ const wchar_t *prefix;
+ const wchar_t *value;
+ BOOL is_visual_elements = FALSE;
+ BOOL is_extension = FALSE;
+ BOOL is_protocol = FALSE;
+ BOOL is_empty;
+ BOOL is_application = FALSE;
+ BOOL is_verb = FALSE;
+
+ hr = IXmlReader_Read (xml_reader, &xml_node_type);
+ if (FAILED (hr))
+ {
+ g_warning ("IXmlReader_Read() for application manifest `%S' for package #%zu (`%S') failed with HRESULT 0x%lx",
+ sax->manifest_filename, sax->package_index, sax->wcs_full_name, hr);
+ return FALSE;
+ }
+
+ if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value))
+ return FALSE;
+
+ switch (xml_node_type)
+ {
+ case XmlNodeType_Element:
+ is_empty = IXmlReader_IsEmptyElement (xml_reader);
+ g_assert (local_name != NULL);
+
+ if (!is_empty &&
+ _wcsicmp (local_name, L"Package") == 0 &&
+ prefix[0] == 0)
+ sax->in_package += 1;
+ else if (!is_empty &&
+ sax->in_package == 1 &&
+ _wcsicmp (local_name, L"Applications") == 0 &&
+ prefix[0] == 0)
+ sax->in_applications += 1;
+ else if (!is_empty &&
+ sax->in_applications == 1 &&
+ _wcsicmp (local_name, L"Application") == 0 &&
+ prefix[0] == 0)
+ {
+ sax->in_application += 1;
+ is_application = TRUE;
+ sax->applist = TRUE;
+ g_clear_pointer (&sax->application_usermodelid, g_free);
+ }
+ else if (sax->in_application == 1 &&
+ _wcsicmp (local_name, L"VisualElements") == 0 &&
+ (_wcsicmp (prefix, L"uap") == 0 || _wcsicmp (prefix, L"uap3") == 0))
+ is_visual_elements = TRUE;
+ else if (!is_empty &&
+ sax->in_application == 1 &&
+ _wcsicmp (local_name, L"Extensions") == 0 &&
+ prefix[0] == 0)
+ sax->in_extensions += 1;
+ else if (!is_empty &&
+ sax->in_application == 1 &&
+ _wcsicmp (local_name, L"Extension") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ is_extension = TRUE;
+ else if (sax->in_extension_protocol == 1 &&
+ _wcsicmp (local_name, L"Protocol") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ is_protocol = TRUE;
+ else if (!is_empty &&
+ sax->in_extension_fta == 1 &&
+ _wcsicmp (local_name, L"FileTypeAssociation") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_fta_group += 1;
+ else if (!is_empty &&
+ sax->in_fta_group == 1 &&
+ _wcsicmp (local_name, L"SupportedFileTypes") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_sfp += 1;
+ else if (!is_empty &&
+ sax->in_fta_group == 1 &&
+ _wcsicmp (local_name, L"SupportedVerbs") == 0 &&
+ _wcsicmp (prefix, L"uap2") == 0)
+ sax->in_sv += 1;
+ else if (!is_empty &&
+ sax->in_sfp == 1 &&
+ _wcsicmp (local_name, L"FileType") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_filetype += 1;
+ else if (!is_empty &&
+ sax->in_sv == 1 &&
+ _wcsicmp (local_name, L"Verb") == 0 &&
+ _wcsicmp (prefix, L"uap3") == 0)
+ is_verb = TRUE;
+
+ hr = IXmlReader_MoveToFirstAttribute (xml_reader);
+ while (hr == S_OK)
+ {
+ if (!xml_parser_get_current_state (sax, xml_reader, &local_name, &prefix, &value))
+ return FALSE;
+
+ g_assert (local_name != NULL);
+ g_assert (value != NULL);
+ g_assert (prefix != NULL);
+
+ if (is_application &&
+ sax->application_usermodelid == NULL &&
+ _wcsicmp (local_name, L"Id") == 0)
+ {
+ size_t id_len = 0;
+ size_t value_len = wcslen (value);
+ const wchar_t *wcs_package_family;
+ size_t wcs_package_family_len;
+
+ wcs_package_family = sax_WindowsGetStringRawBuffer (sax->package_family, NULL);
+ wcs_package_family_len = wcslen (wcs_package_family);
+ id_len += wcs_package_family_len + 1 + value_len;
+ sax->application_usermodelid = g_new (wchar_t, id_len + 1);
+ /* AppUserModelId = <family>!<id> */
+ memcpy (&sax->application_usermodelid[0], wcs_package_family, wcs_package_family_len * sizeof (wchar_t));
+ memcpy (&sax->application_usermodelid[wcs_package_family_len], L"!", sizeof (wchar_t));
+ memcpy (&sax->application_usermodelid[wcs_package_family_len + 1], value, (value_len + 1) * sizeof (wchar_t));
+ }
+ else if (is_visual_elements &&
+ _wcsicmp (local_name, L"AppListEntry") == 0 &&
+ _wcsicmp (value, L"none") == 0)
+ sax->applist = FALSE;
+ else if (is_extension &&
+ _wcsicmp (local_name, L"Category") == 0 &&
+ _wcsicmp (value, L"windows.protocol") == 0)
+ sax->in_extension_protocol += 1;
+ else if (is_extension &&
+ _wcsicmp (local_name, L"Category") == 0 &&
+ _wcsicmp (value, L"windows.fileTypeAssociation") == 0)
+ sax->in_extension_fta += 1;
+ else if (is_protocol &&
+ _wcsicmp (local_name, L"Name") == 0)
+ g_ptr_array_add (sax->supported_protocols, g_wcsdup (value, -1));
+ else if (is_verb &&
+ _wcsicmp (local_name, L"Id") == 0)
+ g_ptr_array_add (sax->supported_verbs, g_wcsdup (value, -1));
+
+ hr = IXmlReader_MoveToNextAttribute (xml_reader);
+ }
+ break;
+ case XmlNodeType_Text:
+ g_assert (value != NULL);
+
+ if (sax->in_filetype && value[0] != 0)
+ g_ptr_array_add (sax->supported_extensions, g_wcsdup (value, -1));
+ break;
+ case XmlNodeType_EndElement:
+ g_assert (local_name != NULL);
+
+ if (_wcsicmp (local_name, L"Package") == 0 &&
+ prefix[0] == 0)
+ sax->in_package -= 1;
+ else if (sax->in_package == 1 &&
+ _wcsicmp (local_name, L"Applications") == 0 &&
+ prefix[0] == 0)
+ sax->in_applications -= 1;
+ else if (sax->in_application == 1 &&
+ _wcsicmp (local_name, L"Extensions") == 0 &&
+ prefix[0] == 0)
+ sax->in_extensions -= 1;
+ else if (sax->in_extension_protocol == 1 &&
+ _wcsicmp (local_name, L"Extension") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_extension_protocol -= 1;
+ else if (sax->in_extension_fta == 1 &&
+ _wcsicmp (local_name, L"Extension") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_extension_fta -= 1;
+ else if (sax->in_fta_group == 1 &&
+ _wcsicmp (local_name, L"SupportedFileTypes") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_sfp -= 1;
+ else if (sax->in_sfp == 1 &&
+ _wcsicmp (local_name, L"FileType") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ sax->in_filetype -= 1;
+ else if (sax->in_fta_group == 1 &&
+ _wcsicmp (local_name, L"SupportedVerbs") == 0 &&
+ _wcsicmp (prefix, L"uap2") == 0)
+ sax->in_sv -= 1;
+ else if (sax->in_applications == 1 &&
+ _wcsicmp (local_name, L"Application") == 0 &&
+ prefix[0] == 0)
+ {
+ if (sax->application_usermodelid != NULL)
+ sax->exit_early = !sax->callback (sax->user_data, sax->wcs_full_name, sax->wcs_name,
+ sax->application_usermodelid, sax->applist,
+ sax->supported_extgroups, sax->supported_protocols);
+ g_clear_pointer (&sax->supported_extgroups, g_ptr_array_unref);
+ g_clear_pointer (&sax->supported_protocols, g_ptr_array_unref);
+ sax->supported_protocols = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ sax->supported_extgroups = g_ptr_array_new_full (0, (GDestroyNotify) free_extgroup);
+ sax->in_application -= 1;
+ }
+ else if (sax->in_extension_fta == 1 &&
+ _wcsicmp (local_name, L"FileTypeAssociation") == 0 &&
+ _wcsicmp (prefix, L"uap") == 0)
+ {
+ GWin32PackageExtGroup *new_group = g_new0 (GWin32PackageExtGroup, 1);
+ new_group->extensions = g_steal_pointer (&sax->supported_extensions);
+ sax->supported_extensions = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ new_group->verbs = g_steal_pointer (&sax->supported_verbs);
+ sax->supported_verbs = g_ptr_array_new_full (0, (GDestroyNotify) g_free);
+ g_ptr_array_add (sax->supported_extgroups, new_group);
+ sax->in_fta_group -= 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+} \ No newline at end of file
diff --git a/gio/gwin32packageparser.h b/gio/gwin32packageparser.h
new file mode 100755
index 000000000..f55e30c3f
--- /dev/null
+++ b/gio/gwin32packageparser.h
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2020 Руслан Ижбулатов <lrn1986@gmail.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/>.
+ *
+ */
+#ifndef __G_WIN32_PACKAGE_PARSER_H__
+#define __G_WIN32_PACKAGE_PARSER_H__
+
+#include <gio/gio.h>
+
+#ifdef G_PLATFORM_WIN32
+
+typedef struct _GWin32PackageExtGroup GWin32PackageExtGroup;
+
+struct _GWin32PackageExtGroup
+{
+ GPtrArray *verbs;
+ GPtrArray *extensions;
+};
+
+typedef gboolean (*GWin32PackageParserCallback)(gpointer user_data,
+ const gunichar2 *full_package_name,
+ const gunichar2 *package_name,
+ const gunichar2 *app_user_model_id,
+ gboolean show_in_applist,
+ GPtrArray *supported_extgroups,
+ GPtrArray *supported_protocols);
+
+gboolean g_win32_package_parser_enum_packages (GWin32PackageParserCallback callback,
+ gpointer user_data,
+ GError **error);
+
+#endif /* G_PLATFORM_WIN32 */
+
+#endif /* __G_WIN32_PACKAGE_PARSER_H__ */ \ No newline at end of file
diff --git a/gio/gwin32registrykey.c b/gio/gwin32registrykey.c
index 6b24fdd90..29895217d 100644
--- a/gio/gwin32registrykey.c
+++ b/gio/gwin32registrykey.c
@@ -2484,7 +2484,7 @@ g_win32_registry_key_watch (GWin32RegistryKey *key,
if (g_once_init_enter (&nt_notify_change_multiple_keys))
{
NtNotifyChangeMultipleKeysFunc func;
- HMODULE ntdll = GetModuleHandle ("ntdll.dll");
+ HMODULE ntdll = GetModuleHandleW (L"ntdll.dll");
if (ntdll != NULL)
func = (NtNotifyChangeMultipleKeysFunc) GetProcAddress (ntdll, "NtNotifyChangeMultipleKeys");
diff --git a/gio/gwin32volumemonitor.c b/gio/gwin32volumemonitor.c
index 83d6d3260..c6657a357 100644
--- a/gio/gwin32volumemonitor.c
+++ b/gio/gwin32volumemonitor.c
@@ -68,13 +68,13 @@ get_viewable_logical_drives (void)
DWORD no_drives;
gboolean hklm_present = FALSE;
- if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
- "Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Policies\\Explorer",
- 0, KEY_READ, &key) == ERROR_SUCCESS)
+ if (RegOpenKeyExW (HKEY_LOCAL_MACHINE,
+ L"Software\\Microsoft\\Windows\\"
+ L"CurrentVersion\\Policies\\Explorer",
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
{
- if (RegQueryValueEx (key, "NoDrives", NULL, &var_type,
- (LPBYTE) &no_drives, &no_drives_size) == ERROR_SUCCESS)
+ if (RegQueryValueExW (key, L"NoDrives", NULL, &var_type,
+ (LPBYTE) &no_drives, &no_drives_size) == ERROR_SUCCESS)
{
/* We need the bits that are set in viewable_drives, and
* unset in no_drives.
@@ -88,13 +88,13 @@ get_viewable_logical_drives (void)
/* If the key is present in HKLM then the one in HKCU should be ignored */
if (!hklm_present)
{
- if (RegOpenKeyEx (HKEY_CURRENT_USER,
- "Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Policies\\Explorer",
- 0, KEY_READ, &key) == ERROR_SUCCESS)
+ if (RegOpenKeyExW (HKEY_CURRENT_USER,
+ L"Software\\Microsoft\\Windows\\"
+ L"CurrentVersion\\Policies\\Explorer",
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
{
- if (RegQueryValueEx (key, "NoDrives", NULL, &var_type,
- (LPBYTE) &no_drives, &no_drives_size) == ERROR_SUCCESS)
+ if (RegQueryValueExW (key, L"NoDrives", NULL, &var_type,
+ (LPBYTE) &no_drives, &no_drives_size) == ERROR_SUCCESS)
{
viewable_drives = viewable_drives & ~no_drives;
}
diff --git a/gio/inotify/inotify-kernel.c b/gio/inotify/inotify-kernel.c
index 05cdcb1aa..1a30fc84d 100644
--- a/gio/inotify/inotify-kernel.c
+++ b/gio/inotify/inotify-kernel.c
@@ -372,8 +372,8 @@ ik_source_new (gboolean (* callback) (ik_event_t *event))
{
static GSourceFuncs source_funcs = {
NULL, NULL,
- ik_source_dispatch
- /* should have a finalize, but it will never happen */
+ ik_source_dispatch,
+ NULL, NULL, NULL
};
InotifyKernelSource *iks;
GSource *source;
diff --git a/gio/meson.build b/gio/meson.build
index f79d22053..e0a190cd6 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -12,7 +12,7 @@ gnetworking_h_conf = configuration_data()
gnetworking_h_nameser_compat_include = ''
-if host_system != 'windows' and not host_system.contains('android')
+if host_system not in ['windows', 'android']
# Don't check for C_IN on Android since it does not define it in public
# headers, we define it ourselves wherever necessary
if not cc.compiles('''#include <sys/types.h>
@@ -148,7 +148,7 @@ if host_system != 'windows'
endif
-if host_system.contains('android')
+if host_system == 'android'
# struct ip_mreq_source definition is broken on Android NDK <= r16
# See https://bugzilla.gnome.org/show_bug.cgi?id=740791
if not cc.compiles('''#include <netinet/in.h>
@@ -430,12 +430,16 @@ else
cc.find_library('dnsapi'),
iphlpapi_dep,
winsock2]
+ platform_deps += uwp_gio_deps
+
win32_sources += files(
'gwin32registrykey.c',
'gwin32mount.c',
'gwin32volumemonitor.c',
'gwin32inputstream.c',
'gwin32outputstream.c',
+ 'gwin32file-sync-stream.c',
+ 'gwin32packageparser.c',
'gwin32networkmonitor.c',
'gwin32networkmonitor.h',
'gwin32notificationbackend.c',
@@ -908,6 +912,7 @@ gio_tool_sources = [
'gio-tool-cat.c',
'gio-tool-copy.c',
'gio-tool-info.c',
+ 'gio-tool-launch.c',
'gio-tool-list.c',
'gio-tool-mime.c',
'gio-tool-mkdir.c',
diff --git a/gio/tests/socket.c b/gio/tests/socket.c
index d6eecdc38..683866ede 100644
--- a/gio/tests/socket.c
+++ b/gio/tests/socket.c
@@ -18,6 +18,7 @@
#include <gio/gio.h>
+#include <gio/gcredentialsprivate.h>
#ifdef G_OS_UNIX
#include <errno.h>
#include <sys/wait.h>
@@ -1898,6 +1899,216 @@ test_read_write (gconstpointer user_data)
g_object_unref (client);
}
+#if G_CREDENTIALS_SUPPORTED
+static gpointer client_setup_thread (gpointer user_data);
+
+static void
+test_credentials_tcp_client (void)
+{
+ const GSocketFamily family = G_SOCKET_FAMILY_IPV4;
+ IPTestData *data;
+ GError *error = NULL;
+ GSocket *client;
+ GSocketAddress *addr;
+ GCredentials *creds;
+
+ data = create_server (family, echo_server_thread, FALSE, &error);
+ if (error != NULL)
+ {
+ gchar *message = g_strdup_printf ("Failed to create server: %s", error->message);
+ g_test_skip (message);
+ g_free (message);
+ g_clear_error (&error);
+ return;
+ }
+
+ addr = g_socket_get_local_address (data->server, &error);
+ g_assert_no_error (error);
+
+ client = g_socket_new (family,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ g_socket_set_blocking (client, TRUE);
+ g_socket_set_timeout (client, 1);
+
+ g_socket_connect (client, addr, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (addr);
+
+ creds = g_socket_get_credentials (client, &error);
+ if (creds != NULL)
+ {
+ gchar *str = g_credentials_to_string (creds);
+ g_print ("Supported on this OS: %s\n", str);
+ g_free (str);
+ g_clear_object (&creds);
+ }
+ else
+ {
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_print ("Unsupported on this OS: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ g_socket_close (client, &error);
+ g_assert_no_error (error);
+
+ g_thread_join (data->thread);
+
+ g_socket_close (data->server, &error);
+ g_assert_no_error (error);
+
+ g_object_unref (data->server);
+ g_object_unref (client);
+
+ g_slice_free (IPTestData, data);
+}
+
+static void
+test_credentials_tcp_server (void)
+{
+ const GSocketFamily family = G_SOCKET_FAMILY_IPV4;
+ IPTestData *data;
+ GSocket *server;
+ GError *error = NULL;
+ GSocketAddress *addr = NULL;
+ GInetAddress *iaddr = NULL;
+ GSocket *sock = NULL;
+ GCredentials *creds;
+
+ data = g_slice_new0 (IPTestData);
+ data->family = family;
+ data->server = server = g_socket_new (family,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ if (error != NULL)
+ goto skip;
+
+ g_socket_set_blocking (server, TRUE);
+
+ iaddr = g_inet_address_new_loopback (family);
+ addr = g_inet_socket_address_new (iaddr, 0);
+
+ if (!g_socket_bind (server, addr, TRUE, &error))
+ goto skip;
+
+ if (!g_socket_listen (server, &error))
+ goto skip;
+
+ data->thread = g_thread_new ("client", client_setup_thread, data);
+
+ sock = g_socket_accept (server, NULL, &error);
+ g_assert_no_error (error);
+
+ creds = g_socket_get_credentials (sock, &error);
+ if (creds != NULL)
+ {
+ gchar *str = g_credentials_to_string (creds);
+ g_print ("Supported on this OS: %s\n", str);
+ g_free (str);
+ g_clear_object (&creds);
+ }
+ else
+ {
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_print ("Unsupported on this OS: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ goto beach;
+
+skip:
+ {
+ gchar *message = g_strdup_printf ("Failed to create server: %s", error->message);
+ g_test_skip (message);
+ g_free (message);
+
+ goto beach;
+ }
+beach:
+ {
+ g_clear_error (&error);
+
+ g_clear_object (&sock);
+ g_clear_object (&addr);
+ g_clear_object (&iaddr);
+
+ g_clear_pointer (&data->thread, g_thread_join);
+ g_clear_object (&data->server);
+ g_clear_object (&data->client);
+
+ g_slice_free (IPTestData, data);
+ }
+}
+
+static gpointer
+client_setup_thread (gpointer user_data)
+{
+ IPTestData *data = user_data;
+ GSocketAddress *addr;
+ GSocket *client;
+ GError *error = NULL;
+
+ addr = g_socket_get_local_address (data->server, &error);
+ g_assert_no_error (error);
+
+ data->client = client = g_socket_new (data->family,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ g_socket_set_blocking (client, TRUE);
+ g_socket_set_timeout (client, 1);
+
+ g_socket_connect (client, addr, NULL, &error);
+ g_assert_no_error (error);
+
+ g_object_unref (addr);
+
+ return NULL;
+}
+
+#ifdef G_OS_UNIX
+static void
+test_credentials_unix_socketpair (void)
+{
+ gint fds[2];
+ gint status;
+ GSocket *sock;
+ GError *error = NULL;
+ GCredentials *creds;
+
+ status = socketpair (PF_UNIX, SOCK_STREAM, 0, fds);
+ g_assert_cmpint (status, ==, 0);
+
+ sock = g_socket_new_from_fd (fds[0], &error);
+
+ creds = g_socket_get_credentials (sock, &error);
+ if (creds != NULL)
+ {
+ gchar *str = g_credentials_to_string (creds);
+ g_print ("Supported on this OS: %s\n", str);
+ g_free (str);
+ g_clear_object (&creds);
+ }
+ else
+ {
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+ g_print ("Unsupported on this OS: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (sock);
+ close (fds[1]);
+}
+#endif
+#endif
+
int
main (int argc,
char *argv[])
@@ -1954,6 +2165,13 @@ main (int argc,
test_read_write);
g_test_add_data_func ("/socket/read_writev", GUINT_TO_POINTER (TRUE),
test_read_write);
+#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/win32/gwinhttpvfs.c b/gio/win32/gwinhttpvfs.c
index 3dfac259e..03feaf983 100644
--- a/gio/win32/gwinhttpvfs.c
+++ b/gio/win32/gwinhttpvfs.c
@@ -39,20 +39,20 @@ static void
lookup_funcs (void)
{
HMODULE winhttp = NULL;
- char winhttp_dll[MAX_PATH + 100];
+ WCHAR winhttp_dll[MAX_PATH + 100];
int n;
if (lookup_done)
return;
- n = GetSystemDirectory (winhttp_dll, MAX_PATH);
+ n = GetSystemDirectoryW (winhttp_dll, MAX_PATH);
if (n > 0 && n < MAX_PATH)
{
- if (winhttp_dll[n-1] != '\\' &&
- winhttp_dll[n-1] != '/')
- strcat (winhttp_dll, "\\");
- strcat (winhttp_dll, "winhttp.dll");
- winhttp = LoadLibrary (winhttp_dll);
+ if (winhttp_dll[n-1] != L'\\' &&
+ winhttp_dll[n-1] != L'/')
+ wcscat (winhttp_dll, L"\\");
+ wcscat (winhttp_dll, L"winhttp.dll");
+ winhttp = LoadLibraryW (winhttp_dll);
}
if (winhttp != NULL)