Import Upstream version 2.72.4

This commit is contained in:
evinadmin 2023-07-04 11:23:22 +02:00
commit 4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions

1
gio/completion/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
!gio

View file

@ -0,0 +1,55 @@
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
__app() {
case "${COMP_CWORD}" in
1)
COMPREPLY=($(compgen -W "help version list-apps launch action list-actions" -- "${COMP_WORDS[1]}"))
return 0
;;
2)
case "${COMP_WORDS[1]}" in
launch|action|list-actions)
COMPREPLY=($(compgen -W "`gapplication list-apps`" -- "${COMP_WORDS[2]}"))
return 0
;;
*)
COMPREPLY=()
return 0
;;
esac
;;
esac
# Otherwise, what we will do is based on the command in ${COMP_WORDS[1]}
case "${COMP_WORDS[1]}" in
action)
# Word 3 is the action name. This is the only one we can help with.
if [ "${COMP_CWORD}" == 3 ]; then
COMPREPLY=($(compgen -W "`gapplication list-actions "${COMP_WORDS[2]}"`" -- "${COMP_WORDS[3]}"))
return 0
else
COMPREPLY=()
return 0
fi
;;
launch)
# Filenames...
COMPREPLY=($(compgen -A file "${COMP_WORDS[COMP_CWORD]}"))
return 0
;;
*)
# Nothing else should be out this far...
COMPREPLY=()
return 0
esac
}
####################################################################################################
complete -F __app gapplication

33
gio/completion/gdbus Normal file
View file

@ -0,0 +1,33 @@
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
__gdbus() {
local IFS=$'\n'
local cur=`_get_cword :`
local suggestions=$(gdbus complete "${COMP_LINE}" ${COMP_POINT})
COMPREPLY=($(compgen -W "$suggestions" -- "$cur"))
# Remove colon-word prefix from COMPREPLY items
case "$cur" in
*:*)
case "$COMP_WORDBREAKS" in
*:*)
local colon_word=${cur%${cur##*:}}
local i=${#COMPREPLY[*]}
while [ $((--i)) -ge 0 ]; do
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
done
;;
esac
;;
esac
}
####################################################################################################
complete -o nospace -F __gdbus gdbus

122
gio/completion/gio Normal file
View file

@ -0,0 +1,122 @@
#
# Copyright (C) 2018 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# licence, or (at your option) any later version.
#
# This 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/>.
#
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
# Check whether the suggestions have common prefix (i.e. suggestions won't be
# shown and prefix will be completed first)
__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
fi
done
return 0 # True
}
# Complete file location
__gio_location() {
# Prevent breaking on colons, we have to work with uris
local cur
_get_comp_words_by_ref -n : cur
# Resolve dirname for dir listing
local dir=""
if [[ $cur =~ "/"$ ]]; then
dir="$cur"
elif [[ $cur =~ "/" ]]; then
# Subtract basename because dirname cmd doesn't work well with schemes
dir=${cur%$(basename "$cur")}
fi
# List volumes and mounts
local mounts=( )
local mount
while IFS=$'\n' read mount; do
# Do not care about local mounts
[[ "$mount" =~ ^"file:" ]] && continue
# Use only matching mounts
[[ "$mount" =~ ^"$cur" && "$mount" != "$cur" ]] && mounts+=("$mount")
done < <(gio mount -li | sed -n -r 's/^ *(default_location|activation_root)=(.*)$/\2/p' | sort -u)
# Workaround to unescape dir name (e.g. "\ " -> " ")
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")"
# Append slash for directories and space for files
if [[ "$type" == "(directory)" ]]; then
escaped_name="$escaped_name/"
else
escaped_name="$escaped_name "
fi
local path="$dir$escaped_name"
# Use only matching paths
if [[ "$path" =~ ^"$cur" ]]; then
files+=("$path")
names+=("$escaped_name")
fi
done < <(gio list -hl "$unescaped_dir" 2> /dev/null)
COMPREPLY=("${files[@]}" "${mounts[@]}")
# Workaround to show suggestions as basenames only
if ! __gio_has_common_prefix; then
COMPREPLY=("${mounts[@]} ${names[@]}")
# Workaround to prevent overwriting suggestions, it adds empty
# suggestion, otherwise names with colons will be corrupted
COMPREPLY+=(" ")
return 0
fi
# Workaround to complete names with colons, it removes colon prefix from
# COMPREPLY
__ltrim_colon_completions "$cur"
}
__gio() {
# Complete subcommands
if (( ${COMP_CWORD} == 1 )); then
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
# Complete file locations
__gio_location
}
####################################################################################################
complete -o nospace -F __gio gio

58
gio/completion/gresource Normal file
View file

@ -0,0 +1,58 @@
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
__gresource() {
local choices coffset section
if [ ${COMP_CWORD} -gt 2 ]; then
if [ ${COMP_WORDS[1]} = --section ]; then
section=${COMP_WORDS[2]}
coffset=2
else
coffset=0
fi
else
coffset=0
fi
case "$((${COMP_CWORD}-$coffset))" in
1)
choices=$'--section \nhelp \nsections \nlist \ndetails \nextract '
;;
2)
case "${COMP_WORDS[$(($coffset+1))]}" in
--section)
return 0
;;
help)
choices=$'sections\nlist\ndetails\nextract'
;;
sections|list|details|extract)
COMPREPLY=($(compgen -f -- ${COMP_WORDS[${COMP_CWORD}]}))
return 0
;;
esac
;;
3)
case "${COMP_WORDS[$(($coffset+1))]}" in
list|details|extract)
choices="$(gresource list ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null | sed -e 's.$. .')"
;;
esac
;;
esac
local IFS=$'\n'
COMPREPLY=($(compgen -W "${choices}" -- "${COMP_WORDS[${COMP_CWORD}]}"))
}
####################################################################################################
complete -o nospace -F __gresource gresource

88
gio/completion/gsettings Normal file
View file

@ -0,0 +1,88 @@
# Check for bash
[ -z "$BASH_VERSION" ] && return
####################################################################################################
__gsettings() {
local choices coffset schemadir
if [ ${COMP_CWORD} -gt 2 ]; then
if [ ${COMP_WORDS[1]} = --schemadir ]; then
# this complexity is needed to perform correct tilde expansion
schemadir=$(eval "echo --schemadir ${COMP_WORDS[2]}")
coffset=2
else
coffset=0
fi
else
coffset=0
fi
case "$((${COMP_CWORD}-$coffset))" in
1)
choices=$'--schemadir\n--version\nhelp \nlist-schemas\nlist-relocatable-schemas\nlist-keys \nlist-children \nlist-recursively \nget \nrange \nset \nreset \nreset-recursively \nwritable \nmonitor \ndescribe '
;;
2)
case "${COMP_WORDS[$(($coffset+1))]}" in
--schemadir)
COMPREPLY=($(compgen -o dirnames -- ${COMP_WORDS[${COMP_CWORD}]}))
return 0
;;
help)
choices=$'list-schemas\nlist-relocatable-schemas\nlist-keys\nlist-children\nlist-recursively\nget\nrange\nset\nreset\nreset-recursively\nwritable\nmonitor'
;;
list-keys|list-children|list-recursively|reset-recursively)
choices="$(gsettings $schemadir list-schemas 2> /dev/null)"$'\n'"$(gsettings $schemadir list-relocatable-schemas 2> /dev/null | sed -e 's.$.:/.')"
;;
list-schemas)
COMPREPLY=($(compgen -W "--print-paths" -- ${COMP_WORDS[${COMP_CWORD}]}))
return 0
;;
get|range|set|reset|writable|monitor|describe)
choices="$(gsettings $schemadir list-schemas 2> /dev/null | sed -e 's.$. .')"$'\n'"$(gsettings $schemadir list-relocatable-schemas 2> /dev/null | sed -e 's.$.:/.')"
;;
esac
;;
3)
case "${COMP_WORDS[$(($coffset+1))]}" in
set)
choices="$(gsettings $schemadir list-keys ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null | sed -e 's.$. .')"
;;
get|range|reset|writable|monitor|describe)
choices="$(gsettings $schemadir list-keys ${COMP_WORDS[$(($coffset+2))]} 2> /dev/null)"
;;
esac
;;
4)
case "${COMP_WORDS[$(($coffset+2))]}" in
set)
range=($(gsettings $schemadir range ${COMP_WORDS[$(($coffset+2))]} ${COMP_WORDS[$(($coffset+3))]} 2> /dev/null))
case "${range[0]}" in
enum)
unset range[0]
;;
*)
unset range
;;
esac
local IFS=$'\n'
choices="${range[*]}"
;;
esac
;;
esac
local IFS=$'\n'
COMPREPLY=($(compgen -W "${choices}" -- "${COMP_WORDS[${COMP_CWORD}]}"))
}
####################################################################################################
complete -o nospace -F __gsettings gsettings

17
gio/data-to-c.py Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import sys
if len(sys.argv) < 4:
print("Usage: {0} <filename> <variable> <output>")
with open(sys.argv[1], "r", encoding="utf-8", errors="backslashreplace") as f:
in_data = f.read()
b = [r"\x{:02x}".format(ord(c)) for c in in_data]
out_data = 'const char {0}[] = "'.format(sys.argv[2])
out_data += "".join(b) + '";\n'
with open(sys.argv[3], "w") as f:
f.write(out_data)

76
gio/dbus-daemon.xml Normal file
View file

@ -0,0 +1,76 @@
<node>
<interface name="org.freedesktop.DBus">
<method name="Hello">
<arg direction="out" type="s" name="assigned_name"/>
</method>
<method name="RequestName">
<arg direction="in" type="s" name="name"/>
<arg direction="in" type="u" name="flags"/>
<arg direction="out" type="u" name="value"/>
</method>
<method name="ReleaseName">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="u" name="value"/>
</method>
<method name="StartServiceByName">
<arg direction="in" type="s" name="name"/>
<arg direction="in" type="u" name="flags"/>
<arg direction="out" type="u" name="value"/>
</method>
<method name="NameHasOwner">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="b" name="has_owner"/>
</method>
<method name="ListNames">
<arg direction="out" type="as" name="names"/>
</method>
<method name="ListActivatableNames">
<arg direction="out" type="as" name="activatable_names"/>
</method>
<method name="AddMatch">
<arg direction="in" type="s" name="rule"/>
</method>
<method name="RemoveMatch">
<arg direction="in" type="s" name="rule"/>
</method>
<method name="GetNameOwner">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="s" name="unique_name"/>
</method>
<method name="ListQueuedOwners">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="as" name="queued_owners"/>
</method>
<method name="GetConnectionUnixUser">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="u" name="uid"/>
</method>
<method name="GetConnectionUnixProcessID">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="u" name="pid"/>
</method>
<method name="GetConnectionSELinuxSecurityContext">
<arg direction="in" type="s" name="name"/>
<arg direction="out" type="ay" name="security_context"/>
</method>
<method name="UpdateActivationEnvironment">
<arg direction="in" type="a{ss}" name="environment"/>
</method>
<method name="ReloadConfig">
</method>
<method name="GetId">
<arg direction="out" type="s" name="unique_id"/>
</method>
<signal name="NameOwnerChanged">
<arg type="s" name="name"/>
<arg type="s" name="old_owner"/>
<arg type="s" name="new_owner"/>
</signal>
<signal name="NameLost">
<arg type="s" name="name"/>
</signal>
<signal name="NameAcquired">
<arg type="s" name="name"/>
</signal>
</interface>
</node>

235
gio/fam/gfamfilemonitor.c Normal file
View file

@ -0,0 +1,235 @@
/*
* Copyright © 2015 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include <gio/glocalfilemonitor.h>
#include <gio/giomodule.h>
#include "glib-private.h"
#include <glib-unix.h>
#include <fam.h>
static GMutex fam_lock;
static gboolean fam_initialised;
static FAMConnection fam_connection;
static GSource *fam_source;
#define G_TYPE_FAM_FILE_MONITOR (g_fam_file_monitor_get_type ())
#define G_FAM_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_FAM_FILE_MONITOR, GFamFileMonitor))
typedef GLocalFileMonitorClass GFamFileMonitorClass;
typedef struct
{
GLocalFileMonitor parent_instance;
FAMRequest request;
} GFamFileMonitor;
static GType g_fam_file_monitor_get_type (void);
G_DEFINE_DYNAMIC_TYPE (GFamFileMonitor, g_fam_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
static gboolean
g_fam_file_monitor_callback (gint fd,
GIOCondition condition,
gpointer user_data)
{
gint64 now = g_source_get_time (fam_source);
g_mutex_lock (&fam_lock);
while (FAMPending (&fam_connection))
{
const gchar *child;
FAMEvent ev;
if (FAMNextEvent (&fam_connection, &ev) != 1)
{
/* The daemon died. We're in a really bad situation now
* because we potentially have a bunch of request structures
* outstanding which no longer make any sense to anyone.
*
* The best thing that we can do is do nothing. Notification
* won't work anymore for this process.
*/
g_mutex_unlock (&fam_lock);
g_warning ("Lost connection to FAM (file monitoring) service. Expect no further file monitor events.");
return FALSE;
}
/* We expect ev.filename to be a relative path for children in a
* monitored directory, and an absolute path for a monitored file
* or the directory itself.
*/
if (ev.filename[0] != '/')
child = ev.filename;
else
child = NULL;
switch (ev.code)
{
case FAMAcknowledge:
g_source_unref (ev.userdata);
break;
case FAMChanged:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CHANGED, child, NULL, NULL, now);
break;
case FAMDeleted:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_DELETED, child, NULL, NULL, now);
break;
case FAMCreated:
g_file_monitor_source_handle_event (ev.userdata, G_FILE_MONITOR_EVENT_CREATED, child, NULL, NULL, now);
break;
default:
/* unknown type */
break;
}
}
g_mutex_unlock (&fam_lock);
return TRUE;
}
static gboolean
g_fam_file_monitor_is_supported (void)
{
g_mutex_lock (&fam_lock);
if (!fam_initialised)
{
fam_initialised = FAMOpen2 (&fam_connection, "GLib GIO") == 0;
if (fam_initialised)
{
#ifdef HAVE_FAM_NO_EXISTS
/* This is a gamin extension that avoids sending all the
* Exists event for dir monitors
*/
FAMNoExists (&fam_connection);
#endif
fam_source = g_unix_fd_source_new (FAMCONNECTION_GETFD (&fam_connection), G_IO_IN);
g_source_set_callback (fam_source, (GSourceFunc) g_fam_file_monitor_callback, NULL, NULL);
g_source_attach (fam_source, GLIB_PRIVATE_CALL(g_get_worker_context) ());
}
}
g_mutex_unlock (&fam_lock);
return fam_initialised;
}
static gboolean
g_fam_file_monitor_cancel (GFileMonitor *monitor)
{
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (monitor);
g_mutex_lock (&fam_lock);
g_assert (fam_initialised);
FAMCancelMonitor (&fam_connection, &gffm->request);
g_mutex_unlock (&fam_lock);
return TRUE;
}
static void
g_fam_file_monitor_start (GLocalFileMonitor *local_monitor,
const gchar *dirname,
const gchar *basename,
const gchar *filename,
GFileMonitorSource *source)
{
GFamFileMonitor *gffm = G_FAM_FILE_MONITOR (local_monitor);
g_mutex_lock (&fam_lock);
g_assert (fam_initialised);
g_source_ref ((GSource *) source);
if (dirname)
FAMMonitorDirectory (&fam_connection, dirname, &gffm->request, source);
else
FAMMonitorFile (&fam_connection, filename, &gffm->request, source);
g_mutex_unlock (&fam_lock);
}
static void
g_fam_file_monitor_init (GFamFileMonitor* monitor)
{
}
static void
g_fam_file_monitor_class_init (GFamFileMonitorClass *class)
{
GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (class);
class->is_supported = g_fam_file_monitor_is_supported;
class->start = g_fam_file_monitor_start;
file_monitor_class->cancel = g_fam_file_monitor_cancel;
}
static void
g_fam_file_monitor_class_finalize (GFamFileMonitorClass *class)
{
}
void
g_io_module_load (GIOModule *module)
{
g_type_module_use (G_TYPE_MODULE (module));
g_fam_file_monitor_register_type (G_TYPE_MODULE (module));
g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
g_io_extension_point_implement (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
G_TYPE_FAM_FILE_MONITOR, "fam", 10);
}
void
g_io_module_unload (GIOModule *module)
{
g_assert_not_reached ();
}
char **
g_io_module_query (void)
{
char *eps[] = {
G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
NULL
};
return g_strdupv (eps);
}

View file

@ -0,0 +1,8 @@
{
global:
g_io_module_load;
g_io_module_unload;
g_io_module_query;
local:
*;
};

42
gio/fam/meson.build Normal file
View file

@ -0,0 +1,42 @@
if not get_option('fam')
subdir_done()
endif
fam_dep = cc.find_library('fam')
fam_c_args = gio_c_args
if cc.has_function('FAMNoExists', dependencies : fam_dep)
fam_c_args += '-DHAVE_FAM_NO_EXISTS=1'
endif
deps = [
fam_dep,
libglib_dep,
libgobject_dep,
libgio_dep,
]
symbol_map = join_paths(meson.current_source_dir(), 'gfamfilemonitor.map')
fam_ldflags = cc.get_supported_link_arguments([
'-Wl,--version-script,' + symbol_map,
'-Wl,-no-undefined',
])
module = shared_module('giofam', 'gfamfilemonitor.c',
include_directories : [gmoduleinc],
dependencies : deps,
c_args : fam_c_args,
link_args : fam_ldflags,
link_depends : symbol_map,
install_dir : glib_giomodulesdir,
install : true,
)
if not meson.is_cross_build()
meson.add_install_script('../gio-querymodules-wrapper.py', gio_querymodules.full_path(), glib_giomodulesdir)
endif
if meson.version().version_compare('>=0.58')
env = environment()
env.prepend('GIO_EXTRA_MODULES', meson.current_build_dir())
meson.add_devenv(env)
endif

587
gio/gaction.c Normal file
View file

@ -0,0 +1,587 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gaction.h"
#include "glibintl.h"
#include <string.h>
G_DEFINE_INTERFACE (GAction, g_action, G_TYPE_OBJECT)
/**
* SECTION:gaction
* @title: GAction
* @short_description: An action interface
* @include: gio/gio.h
*
* #GAction represents a single named action.
*
* The main interface to an action is that it can be activated with
* g_action_activate(). This results in the 'activate' signal being
* emitted. An activation has a #GVariant parameter (which may be
* %NULL). The correct type for the parameter is determined by a static
* parameter type (which is given at construction time).
*
* An action may optionally have a state, in which case the state may be
* set with g_action_change_state(). This call takes a #GVariant. The
* correct type for the state is determined by a static state type
* (which is given at construction time).
*
* The state may have a hint associated with it, specifying its valid
* range.
*
* #GAction is merely the interface to the concept of an action, as
* described above. Various implementations of actions exist, including
* #GSimpleAction.
*
* In all cases, the implementing class is responsible for storing the
* name of the action, the parameter type, the enabled state, the
* optional state type and the state and emitting the appropriate
* signals when these change. The implementor is responsible for filtering
* calls to g_action_activate() and g_action_change_state() for type
* safety and for the state being enabled.
*
* Probably the only useful thing to do with a #GAction is to put it
* inside of a #GSimpleActionGroup.
**/
/**
* GAction:
*
* #GAction is an opaque data structure and can only be accessed
* using the following functions.
**/
/**
* GActionInterface:
* @get_name: the virtual function pointer for g_action_get_name()
* @get_parameter_type: the virtual function pointer for g_action_get_parameter_type()
* @get_state_type: the virtual function pointer for g_action_get_state_type()
* @get_state_hint: the virtual function pointer for g_action_get_state_hint()
* @get_enabled: the virtual function pointer for g_action_get_enabled()
* @get_state: the virtual function pointer for g_action_get_state()
* @change_state: the virtual function pointer for g_action_change_state()
* @activate: the virtual function pointer for g_action_activate(). Note that #GAction does not have an
* 'activate' signal but that implementations of it may have one.
*
* The virtual function table for #GAction.
*
* Since: 2.28
*/
void
g_action_default_init (GActionInterface *iface)
{
/**
* GAction:name:
*
* The name of the action. This is mostly meaningful for identifying
* the action once it has been added to a #GActionGroup. It is immutable.
*
* Since: 2.28
**/
g_object_interface_install_property (iface,
g_param_spec_string ("name",
P_("Action Name"),
P_("The name used to invoke the action"),
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* GAction:parameter-type:
*
* The type of the parameter that must be given when activating the
* action. This is immutable, and may be %NULL if no parameter is needed when
* activating the action.
*
* Since: 2.28
**/
g_object_interface_install_property (iface,
g_param_spec_boxed ("parameter-type",
P_("Parameter Type"),
P_("The type of GVariant passed to activate()"),
G_TYPE_VARIANT_TYPE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* GAction:enabled:
*
* If @action is currently enabled.
*
* If the action is disabled then calls to g_action_activate() and
* g_action_change_state() have no effect.
*
* Since: 2.28
**/
g_object_interface_install_property (iface,
g_param_spec_boolean ("enabled",
P_("Enabled"),
P_("If the action can be activated"),
TRUE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* GAction:state-type:
*
* The #GVariantType of the state that the action has, or %NULL if the
* action is stateless. This is immutable.
*
* Since: 2.28
**/
g_object_interface_install_property (iface,
g_param_spec_boxed ("state-type",
P_("State Type"),
P_("The type of the state kept by the action"),
G_TYPE_VARIANT_TYPE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* GAction:state:
*
* The state of the action, or %NULL if the action is stateless.
*
* Since: 2.28
**/
g_object_interface_install_property (iface,
g_param_spec_variant ("state",
P_("State"),
P_("The state the action is in"),
G_VARIANT_TYPE_ANY,
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
}
/**
* g_action_change_state:
* @action: a #GAction
* @value: the new state
*
* Request for the state of @action to be changed to @value.
*
* The action must be stateful and @value must be of the correct type.
* See g_action_get_state_type().
*
* This call merely requests a change. The action may refuse to change
* its state or may change its state to something other than @value.
* See g_action_get_state_hint().
*
* If the @value GVariant is floating, it is consumed.
*
* Since: 2.30
**/
void
g_action_change_state (GAction *action,
GVariant *value)
{
const GVariantType *state_type;
g_return_if_fail (G_IS_ACTION (action));
g_return_if_fail (value != NULL);
state_type = g_action_get_state_type (action);
g_return_if_fail (state_type != NULL);
g_return_if_fail (g_variant_is_of_type (value, state_type));
g_variant_ref_sink (value);
G_ACTION_GET_IFACE (action)
->change_state (action, value);
g_variant_unref (value);
}
/**
* g_action_get_state:
* @action: a #GAction
*
* Queries the current state of @action.
*
* If the action is not stateful then %NULL will be returned. If the
* action is stateful then the type of the return value is the type
* given by g_action_get_state_type().
*
* The return value (if non-%NULL) should be freed with
* g_variant_unref() when it is no longer required.
*
* Returns: (nullable) (transfer full): the current state of the action
*
* Since: 2.28
**/
GVariant *
g_action_get_state (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), NULL);
return G_ACTION_GET_IFACE (action)
->get_state (action);
}
/**
* g_action_get_name:
* @action: a #GAction
*
* Queries the name of @action.
*
* Returns: the name of the action
*
* Since: 2.28
**/
const gchar *
g_action_get_name (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), NULL);
return G_ACTION_GET_IFACE (action)
->get_name (action);
}
/**
* g_action_get_parameter_type:
* @action: a #GAction
*
* Queries the type of the parameter that must be given when activating
* @action.
*
* When activating the action using g_action_activate(), the #GVariant
* given to that function must be of the type returned by this function.
*
* In the case that this function returns %NULL, you must not give any
* #GVariant, but %NULL instead.
*
* Returns: (nullable): the parameter type
*
* Since: 2.28
**/
const GVariantType *
g_action_get_parameter_type (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), NULL);
return G_ACTION_GET_IFACE (action)
->get_parameter_type (action);
}
/**
* g_action_get_state_type:
* @action: a #GAction
*
* Queries the type of the state of @action.
*
* If the action is stateful (e.g. created with
* g_simple_action_new_stateful()) then this function returns the
* #GVariantType of the state. This is the type of the initial value
* given as the state. All calls to g_action_change_state() must give a
* #GVariant of this type and g_action_get_state() will return a
* #GVariant of the same type.
*
* If the action is not stateful (e.g. created with g_simple_action_new())
* then this function will return %NULL. In that case, g_action_get_state()
* will return %NULL and you must not call g_action_change_state().
*
* Returns: (nullable): the state type, if the action is stateful
*
* Since: 2.28
**/
const GVariantType *
g_action_get_state_type (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), NULL);
return G_ACTION_GET_IFACE (action)
->get_state_type (action);
}
/**
* g_action_get_state_hint:
* @action: a #GAction
*
* Requests a hint about the valid range of values for the state of
* @action.
*
* If %NULL is returned it either means that the action is not stateful
* or that there is no hint about the valid range of values for the
* state of the action.
*
* If a #GVariant array is returned then each item in the array is a
* possible value for the state. If a #GVariant pair (ie: two-tuple) is
* returned then the tuple specifies the inclusive lower and upper bound
* of valid values for the state.
*
* In any case, the information is merely a hint. It may be possible to
* have a state value outside of the hinted range and setting a value
* within the range may fail.
*
* The return value (if non-%NULL) should be freed with
* g_variant_unref() when it is no longer required.
*
* Returns: (nullable) (transfer full): the state range hint
*
* Since: 2.28
**/
GVariant *
g_action_get_state_hint (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), NULL);
return G_ACTION_GET_IFACE (action)
->get_state_hint (action);
}
/**
* g_action_get_enabled:
* @action: a #GAction
*
* Checks if @action is currently enabled.
*
* An action must be enabled in order to be activated or in order to
* have its state changed from outside callers.
*
* Returns: whether the action is enabled
*
* Since: 2.28
**/
gboolean
g_action_get_enabled (GAction *action)
{
g_return_val_if_fail (G_IS_ACTION (action), FALSE);
return G_ACTION_GET_IFACE (action)
->get_enabled (action);
}
/**
* g_action_activate:
* @action: a #GAction
* @parameter: (nullable): the parameter to the activation
*
* Activates the action.
*
* @parameter must be the correct type of parameter for the action (ie:
* the parameter type given at construction time). If the parameter
* type was %NULL then @parameter must also be %NULL.
*
* If the @parameter GVariant is floating, it is consumed.
*
* Since: 2.28
**/
void
g_action_activate (GAction *action,
GVariant *parameter)
{
g_return_if_fail (G_IS_ACTION (action));
if (parameter != NULL)
g_variant_ref_sink (parameter);
G_ACTION_GET_IFACE (action)
->activate (action, parameter);
if (parameter != NULL)
g_variant_unref (parameter);
}
/**
* g_action_name_is_valid:
* @action_name: a potential action name
*
* Checks if @action_name is valid.
*
* @action_name is valid if it consists only of alphanumeric characters,
* plus '-' and '.'. The empty string is not a valid action name.
*
* It is an error to call this function with a non-utf8 @action_name.
* @action_name must not be %NULL.
*
* Returns: %TRUE if @action_name is valid
*
* Since: 2.38
**/
gboolean
g_action_name_is_valid (const gchar *action_name)
{
gchar c;
gint i;
g_return_val_if_fail (action_name != NULL, FALSE);
for (i = 0; (c = action_name[i]); i++)
if (!g_ascii_isalnum (c) && c != '.' && c != '-')
return FALSE;
return i > 0;
}
/**
* g_action_parse_detailed_name:
* @detailed_name: a detailed action name
* @action_name: (out): the action name
* @target_value: (out): the target value, or %NULL for no target
* @error: a pointer to a %NULL #GError, or %NULL
*
* Parses a detailed action name into its separate name and target
* components.
*
* Detailed action names can have three formats.
*
* The first format is used to represent an action name with no target
* value and consists of just an action name containing no whitespace
* nor the characters ':', '(' or ')'. For example: "app.action".
*
* The second format is used to represent an action with a target value
* that is a non-empty string consisting only of alphanumerics, plus '-'
* and '.'. In that case, the action name and target value are
* separated by a double colon ("::"). For example:
* "app.action::target".
*
* The third format is used to represent an action with any type of
* target value, including strings. The target value follows the action
* name, surrounded in parens. For example: "app.action(42)". The
* target value is parsed using g_variant_parse(). If a tuple-typed
* value is desired, it must be specified in the same way, resulting in
* two sets of parens, for example: "app.action((1,2,3))". A string
* target can be specified this way as well: "app.action('target')".
* For strings, this third format must be used if * target value is
* empty or contains characters other than alphanumerics, '-' and '.'.
*
* Returns: %TRUE if successful, else %FALSE with @error set
*
* Since: 2.38
**/
gboolean
g_action_parse_detailed_name (const gchar *detailed_name,
gchar **action_name,
GVariant **target_value,
GError **error)
{
const gchar *target;
gsize target_len;
gsize base_len;
/* For historical (compatibility) reasons, this function accepts some
* cases of invalid action names as long as they don't interfere with
* the separation of the action from the target value.
*
* We decide which format we have based on which we see first between
* '::' '(' and '\0'.
*/
if (*detailed_name == '\0' || *detailed_name == ' ')
goto bad_fmt;
base_len = strcspn (detailed_name, ": ()");
target = detailed_name + base_len;
target_len = strlen (target);
switch (target[0])
{
case ' ':
case ')':
goto bad_fmt;
case ':':
if (target[1] != ':')
goto bad_fmt;
*target_value = g_variant_ref_sink (g_variant_new_string (target + 2));
break;
case '(':
{
if (target[target_len - 1] != ')')
goto bad_fmt;
*target_value = g_variant_parse (NULL, target + 1, target + target_len - 1, NULL, error);
if (*target_value == NULL)
goto bad_fmt;
}
break;
case '\0':
*target_value = NULL;
break;
}
*action_name = g_strndup (detailed_name, base_len);
return TRUE;
bad_fmt:
if (error)
{
if (*error == NULL)
g_set_error (error, G_VARIANT_PARSE_ERROR, G_VARIANT_PARSE_ERROR_FAILED,
"Detailed action name '%s' has invalid format", detailed_name);
else
g_prefix_error (error, "Detailed action name '%s' has invalid format: ", detailed_name);
}
return FALSE;
}
/**
* g_action_print_detailed_name:
* @action_name: a valid action name
* @target_value: (nullable): a #GVariant target value, or %NULL
*
* Formats a detailed action name from @action_name and @target_value.
*
* It is an error to call this function with an invalid action name.
*
* This function is the opposite of g_action_parse_detailed_name().
* It will produce a string that can be parsed back to the @action_name
* and @target_value by that function.
*
* See that function for the types of strings that will be printed by
* this function.
*
* Returns: a detailed format string
*
* Since: 2.38
**/
gchar *
g_action_print_detailed_name (const gchar *action_name,
GVariant *target_value)
{
g_return_val_if_fail (g_action_name_is_valid (action_name), NULL);
if (target_value == NULL)
return g_strdup (action_name);
if (g_variant_is_of_type (target_value, G_VARIANT_TYPE_STRING))
{
const gchar *str = g_variant_get_string (target_value, NULL);
if (g_action_name_is_valid (str))
return g_strconcat (action_name, "::", str, NULL);
}
{
GString *result = g_string_new (action_name);
g_string_append_c (result, '(');
g_variant_print_string (target_value, result, TRUE);
g_string_append_c (result, ')');
return g_string_free (result, FALSE);
}
}

98
gio/gaction.h Normal file
View file

@ -0,0 +1,98 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_ACTION_H__
#define __G_ACTION_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_ACTION (g_action_get_type ())
#define G_ACTION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_ACTION, GAction))
#define G_IS_ACTION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_ACTION))
#define G_ACTION_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
G_TYPE_ACTION, GActionInterface))
typedef struct _GActionInterface GActionInterface;
struct _GActionInterface
{
GTypeInterface g_iface;
/* virtual functions */
const gchar * (* get_name) (GAction *action);
const GVariantType * (* get_parameter_type) (GAction *action);
const GVariantType * (* get_state_type) (GAction *action);
GVariant * (* get_state_hint) (GAction *action);
gboolean (* get_enabled) (GAction *action);
GVariant * (* get_state) (GAction *action);
void (* change_state) (GAction *action,
GVariant *value);
void (* activate) (GAction *action,
GVariant *parameter);
};
GLIB_AVAILABLE_IN_2_30
GType g_action_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
const gchar * g_action_get_name (GAction *action);
GLIB_AVAILABLE_IN_ALL
const GVariantType * g_action_get_parameter_type (GAction *action);
GLIB_AVAILABLE_IN_ALL
const GVariantType * g_action_get_state_type (GAction *action);
GLIB_AVAILABLE_IN_ALL
GVariant * g_action_get_state_hint (GAction *action);
GLIB_AVAILABLE_IN_ALL
gboolean g_action_get_enabled (GAction *action);
GLIB_AVAILABLE_IN_ALL
GVariant * g_action_get_state (GAction *action);
GLIB_AVAILABLE_IN_ALL
void g_action_change_state (GAction *action,
GVariant *value);
GLIB_AVAILABLE_IN_ALL
void g_action_activate (GAction *action,
GVariant *parameter);
GLIB_AVAILABLE_IN_2_28
gboolean g_action_name_is_valid (const gchar *action_name);
GLIB_AVAILABLE_IN_2_38
gboolean g_action_parse_detailed_name (const gchar *detailed_name,
gchar **action_name,
GVariant **target_value,
GError **error);
GLIB_AVAILABLE_IN_2_38
gchar * g_action_print_detailed_name (const gchar *action_name,
GVariant *target_value);
G_END_DECLS
#endif /* __G_ACTION_H__ */

790
gio/gactiongroup.c Normal file
View file

@ -0,0 +1,790 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gactiongroup.h"
#include "gaction.h"
#include "glibintl.h"
#include "gmarshal-internal.h"
/**
* SECTION:gactiongroup
* @title: GActionGroup
* @short_description: A group of actions
* @include: gio/gio.h
* @see_also: #GAction
*
* #GActionGroup represents a group of actions. Actions can be used to
* expose functionality in a structured way, either from one part of a
* program to another, or to the outside world. Action groups are often
* used together with a #GMenuModel that provides additional
* representation data for displaying the actions to the user, e.g. in
* a menu.
*
* The main way to interact with the actions in a GActionGroup is to
* activate them with g_action_group_activate_action(). Activating an
* action may require a #GVariant parameter. The required type of the
* parameter can be inquired with g_action_group_get_action_parameter_type().
* Actions may be disabled, see g_action_group_get_action_enabled().
* Activating a disabled action has no effect.
*
* Actions may optionally have a state in the form of a #GVariant. The
* current state of an action can be inquired with
* g_action_group_get_action_state(). Activating a stateful action may
* change its state, but it is also possible to set the state by calling
* g_action_group_change_action_state().
*
* As typical example, consider a text editing application which has an
* option to change the current font to 'bold'. A good way to represent
* this would be a stateful action, with a boolean state. Activating the
* action would toggle the state.
*
* Each action in the group has a unique name (which is a string). All
* method calls, except g_action_group_list_actions() take the name of
* an action as an argument.
*
* The #GActionGroup API is meant to be the 'public' API to the action
* group. The calls here are exactly the interaction that 'external
* forces' (eg: UI, incoming D-Bus messages, etc.) are supposed to have
* with actions. 'Internal' APIs (ie: ones meant only to be accessed by
* the action group implementation) are found on subclasses. This is
* why you will find - for example - g_action_group_get_action_enabled()
* but not an equivalent set() call.
*
* Signals are emitted on the action group in response to state changes
* on individual actions.
*
* Implementations of #GActionGroup should provide implementations for
* the virtual functions g_action_group_list_actions() and
* g_action_group_query_action(). The other virtual functions should
* not be implemented - their "wrappers" are actually implemented with
* calls to g_action_group_query_action().
*/
/**
* GActionGroup:
*
* #GActionGroup is an opaque data structure and can only be accessed
* using the following functions.
**/
/**
* GActionGroupInterface:
* @has_action: the virtual function pointer for g_action_group_has_action()
* @list_actions: the virtual function pointer for g_action_group_list_actions()
* @get_action_parameter_type: the virtual function pointer for g_action_group_get_action_parameter_type()
* @get_action_state_type: the virtual function pointer for g_action_group_get_action_state_type()
* @get_action_state_hint: the virtual function pointer for g_action_group_get_action_state_hint()
* @get_action_enabled: the virtual function pointer for g_action_group_get_action_enabled()
* @get_action_state: the virtual function pointer for g_action_group_get_action_state()
* @change_action_state: the virtual function pointer for g_action_group_change_action_state()
* @query_action: the virtual function pointer for g_action_group_query_action()
* @activate_action: the virtual function pointer for g_action_group_activate_action()
* @change_action_state: the virtual function pointer for g_action_group_change_action_state()
* @action_added: the class closure for the #GActionGroup::action-added signal
* @action_removed: the class closure for the #GActionGroup::action-removed signal
* @action_enabled_changed: the class closure for the #GActionGroup::action-enabled-changed signal
* @action_state_changed: the class closure for the #GActionGroup::action-enabled-changed signal
*
* The virtual function table for #GActionGroup.
*
* Since: 2.28
**/
G_DEFINE_INTERFACE (GActionGroup, g_action_group, G_TYPE_OBJECT)
enum
{
SIGNAL_ACTION_ADDED,
SIGNAL_ACTION_REMOVED,
SIGNAL_ACTION_ENABLED_CHANGED,
SIGNAL_ACTION_STATE_CHANGED,
NR_SIGNALS
};
static guint g_action_group_signals[NR_SIGNALS];
static gboolean
g_action_group_real_has_action (GActionGroup *action_group,
const gchar *action_name)
{
return g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, NULL, NULL);
}
static gboolean
g_action_group_real_get_action_enabled (GActionGroup *action_group,
const gchar *action_name)
{
gboolean enabled = FALSE;
g_action_group_query_action (action_group, action_name, &enabled, NULL, NULL, NULL, NULL);
return enabled;
}
static const GVariantType *
g_action_group_real_get_action_parameter_type (GActionGroup *action_group,
const gchar *action_name)
{
const GVariantType *type = NULL;
g_action_group_query_action (action_group, action_name, NULL, &type, NULL, NULL, NULL);
return type;
}
static const GVariantType *
g_action_group_real_get_action_state_type (GActionGroup *action_group,
const gchar *action_name)
{
const GVariantType *type = NULL;
g_action_group_query_action (action_group, action_name, NULL, NULL, &type, NULL, NULL);
return type;
}
static GVariant *
g_action_group_real_get_action_state_hint (GActionGroup *action_group,
const gchar *action_name)
{
GVariant *hint = NULL;
g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, &hint, NULL);
return hint;
}
static GVariant *
g_action_group_real_get_action_state (GActionGroup *action_group,
const gchar *action_name)
{
GVariant *state = NULL;
g_action_group_query_action (action_group, action_name, NULL, NULL, NULL, NULL, &state);
return state;
}
static gboolean
g_action_group_real_query_action (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
GActionGroupInterface *iface = G_ACTION_GROUP_GET_IFACE (action_group);
/* we expect implementations to override this method, but we also
* allow for implementations that existed before this method was
* introduced to override the individual accessors instead.
*
* detect the case that neither has happened and report it.
*/
if G_UNLIKELY (iface->has_action == g_action_group_real_has_action ||
iface->get_action_enabled == g_action_group_real_get_action_enabled ||
iface->get_action_parameter_type == g_action_group_real_get_action_parameter_type ||
iface->get_action_state_type == g_action_group_real_get_action_state_type ||
iface->get_action_state_hint == g_action_group_real_get_action_state_hint ||
iface->get_action_state == g_action_group_real_get_action_state)
{
g_critical ("Class '%s' implements GActionGroup interface without overriding "
"query_action() method -- bailing out to avoid infinite recursion.",
G_OBJECT_TYPE_NAME (action_group));
return FALSE;
}
if (!(* iface->has_action) (action_group, action_name))
return FALSE;
if (enabled != NULL)
*enabled = (* iface->get_action_enabled) (action_group, action_name);
if (parameter_type != NULL)
*parameter_type = (* iface->get_action_parameter_type) (action_group, action_name);
if (state_type != NULL)
*state_type = (* iface->get_action_state_type) (action_group, action_name);
if (state_hint != NULL)
*state_hint = (* iface->get_action_state_hint) (action_group, action_name);
if (state != NULL)
*state = (* iface->get_action_state) (action_group, action_name);
return TRUE;
}
static void
g_action_group_default_init (GActionGroupInterface *iface)
{
iface->has_action = g_action_group_real_has_action;
iface->get_action_enabled = g_action_group_real_get_action_enabled;
iface->get_action_parameter_type = g_action_group_real_get_action_parameter_type;
iface->get_action_state_type = g_action_group_real_get_action_state_type;
iface->get_action_state_hint = g_action_group_real_get_action_state_hint;
iface->get_action_state = g_action_group_real_get_action_state;
iface->query_action = g_action_group_real_query_action;
/**
* GActionGroup::action-added:
* @action_group: the #GActionGroup that changed
* @action_name: the name of the action in @action_group
*
* Signals that a new action was just added to the group.
* This signal is emitted after the action has been added
* and is now visible.
*
* Since: 2.28
**/
g_action_group_signals[SIGNAL_ACTION_ADDED] =
g_signal_new (I_("action-added"),
G_TYPE_ACTION_GROUP,
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (GActionGroupInterface, action_added),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
G_TYPE_STRING);
/**
* GActionGroup::action-removed:
* @action_group: the #GActionGroup that changed
* @action_name: the name of the action in @action_group
*
* Signals that an action is just about to be removed from the group.
* This signal is emitted before the action is removed, so the action
* is still visible and can be queried from the signal handler.
*
* Since: 2.28
**/
g_action_group_signals[SIGNAL_ACTION_REMOVED] =
g_signal_new (I_("action-removed"),
G_TYPE_ACTION_GROUP,
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (GActionGroupInterface, action_removed),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
G_TYPE_STRING);
/**
* GActionGroup::action-enabled-changed:
* @action_group: the #GActionGroup that changed
* @action_name: the name of the action in @action_group
* @enabled: whether the action is enabled or not
*
* Signals that the enabled status of the named action has changed.
*
* Since: 2.28
**/
g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED] =
g_signal_new (I_("action-enabled-changed"),
G_TYPE_ACTION_GROUP,
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (GActionGroupInterface,
action_enabled_changed),
NULL, NULL,
_g_cclosure_marshal_VOID__STRING_BOOLEAN,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
g_signal_set_va_marshaller (g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED],
G_TYPE_FROM_INTERFACE (iface),
_g_cclosure_marshal_VOID__STRING_BOOLEANv);
/**
* GActionGroup::action-state-changed:
* @action_group: the #GActionGroup that changed
* @action_name: the name of the action in @action_group
* @value: the new value of the state
*
* Signals that the state of the named action has changed.
*
* Since: 2.28
**/
g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED] =
g_signal_new (I_("action-state-changed"),
G_TYPE_ACTION_GROUP,
G_SIGNAL_RUN_LAST |
G_SIGNAL_DETAILED |
G_SIGNAL_MUST_COLLECT,
G_STRUCT_OFFSET (GActionGroupInterface,
action_state_changed),
NULL, NULL,
_g_cclosure_marshal_VOID__STRING_VARIANT,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_VARIANT);
g_signal_set_va_marshaller (g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED],
G_TYPE_FROM_INTERFACE (iface),
_g_cclosure_marshal_VOID__STRING_VARIANTv);
}
/**
* g_action_group_list_actions:
* @action_group: a #GActionGroup
*
* Lists the actions contained within @action_group.
*
* The caller is responsible for freeing the list with g_strfreev() when
* it is no longer required.
*
* Returns: (transfer full): a %NULL-terminated array of the names of the
* actions in the group
*
* Since: 2.28
**/
gchar **
g_action_group_list_actions (GActionGroup *action_group)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
return G_ACTION_GROUP_GET_IFACE (action_group)
->list_actions (action_group);
}
/**
* g_action_group_has_action:
* @action_group: a #GActionGroup
* @action_name: the name of the action to check for
*
* Checks if the named action exists within @action_group.
*
* Returns: whether the named action exists
*
* Since: 2.28
**/
gboolean
g_action_group_has_action (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), FALSE);
return G_ACTION_GROUP_GET_IFACE (action_group)
->has_action (action_group, action_name);
}
/**
* g_action_group_get_action_parameter_type:
* @action_group: a #GActionGroup
* @action_name: the name of the action to query
*
* Queries the type of the parameter that must be given when activating
* the named action within @action_group.
*
* When activating the action using g_action_group_activate_action(),
* the #GVariant given to that function must be of the type returned
* by this function.
*
* In the case that this function returns %NULL, you must not give any
* #GVariant, but %NULL instead.
*
* The parameter type of a particular action will never change but it is
* possible for an action to be removed and for a new action to be added
* with the same name but a different parameter type.
*
* Returns: (nullable): the parameter type
*
* Since: 2.28
**/
const GVariantType *
g_action_group_get_action_parameter_type (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_parameter_type (action_group, action_name);
}
/**
* g_action_group_get_action_state_type:
* @action_group: a #GActionGroup
* @action_name: the name of the action to query
*
* Queries the type of the state of the named action within
* @action_group.
*
* If the action is stateful then this function returns the
* #GVariantType of the state. All calls to
* g_action_group_change_action_state() must give a #GVariant of this
* type and g_action_group_get_action_state() will return a #GVariant
* of the same type.
*
* If the action is not stateful then this function will return %NULL.
* In that case, g_action_group_get_action_state() will return %NULL
* and you must not call g_action_group_change_action_state().
*
* The state type of a particular action will never change but it is
* possible for an action to be removed and for a new action to be added
* with the same name but a different state type.
*
* Returns: (nullable): the state type, if the action is stateful
*
* Since: 2.28
**/
const GVariantType *
g_action_group_get_action_state_type (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_state_type (action_group, action_name);
}
/**
* g_action_group_get_action_state_hint:
* @action_group: a #GActionGroup
* @action_name: the name of the action to query
*
* Requests a hint about the valid range of values for the state of the
* named action within @action_group.
*
* If %NULL is returned it either means that the action is not stateful
* or that there is no hint about the valid range of values for the
* state of the action.
*
* If a #GVariant array is returned then each item in the array is a
* possible value for the state. If a #GVariant pair (ie: two-tuple) is
* returned then the tuple specifies the inclusive lower and upper bound
* of valid values for the state.
*
* In any case, the information is merely a hint. It may be possible to
* have a state value outside of the hinted range and setting a value
* within the range may fail.
*
* The return value (if non-%NULL) should be freed with
* g_variant_unref() when it is no longer required.
*
* Returns: (nullable) (transfer full): the state range hint
*
* Since: 2.28
**/
GVariant *
g_action_group_get_action_state_hint (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_state_hint (action_group, action_name);
}
/**
* g_action_group_get_action_enabled:
* @action_group: a #GActionGroup
* @action_name: the name of the action to query
*
* Checks if the named action within @action_group is currently enabled.
*
* An action must be enabled in order to be activated or in order to
* have its state changed from outside callers.
*
* Returns: whether or not the action is currently enabled
*
* Since: 2.28
**/
gboolean
g_action_group_get_action_enabled (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), FALSE);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_enabled (action_group, action_name);
}
/**
* g_action_group_get_action_state:
* @action_group: a #GActionGroup
* @action_name: the name of the action to query
*
* Queries the current state of the named action within @action_group.
*
* If the action is not stateful then %NULL will be returned. If the
* action is stateful then the type of the return value is the type
* given by g_action_group_get_action_state_type().
*
* The return value (if non-%NULL) should be freed with
* g_variant_unref() when it is no longer required.
*
* Returns: (nullable) (transfer full): the current state of the action
*
* Since: 2.28
**/
GVariant *
g_action_group_get_action_state (GActionGroup *action_group,
const gchar *action_name)
{
g_return_val_if_fail (G_IS_ACTION_GROUP (action_group), NULL);
return G_ACTION_GROUP_GET_IFACE (action_group)
->get_action_state (action_group, action_name);
}
/**
* g_action_group_change_action_state:
* @action_group: a #GActionGroup
* @action_name: the name of the action to request the change on
* @value: the new state
*
* Request for the state of the named action within @action_group to be
* changed to @value.
*
* The action must be stateful and @value must be of the correct type.
* See g_action_group_get_action_state_type().
*
* This call merely requests a change. The action may refuse to change
* its state or may change its state to something other than @value.
* See g_action_group_get_action_state_hint().
*
* If the @value GVariant is floating, it is consumed.
*
* Since: 2.28
**/
void
g_action_group_change_action_state (GActionGroup *action_group,
const gchar *action_name,
GVariant *value)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
g_return_if_fail (value != NULL);
G_ACTION_GROUP_GET_IFACE (action_group)
->change_action_state (action_group, action_name, value);
}
/**
* g_action_group_activate_action:
* @action_group: a #GActionGroup
* @action_name: the name of the action to activate
* @parameter: (nullable): parameters to the activation
*
* Activate the named action within @action_group.
*
* If the action is expecting a parameter, then the correct type of
* parameter must be given as @parameter. If the action is expecting no
* parameters then @parameter must be %NULL. See
* g_action_group_get_action_parameter_type().
*
* If the #GActionGroup implementation supports asynchronous remote
* activation over D-Bus, this call may return before the relevant
* D-Bus traffic has been sent, or any replies have been received. In
* order to block on such asynchronous activation calls,
* g_dbus_connection_flush() should be called prior to the code, which
* depends on the result of the action activation. Without flushing
* the D-Bus connection, there is no guarantee that the action would
* have been activated.
*
* The following code which runs in a remote app instance, shows an
* example of a "quit" action being activated on the primary app
* instance over D-Bus. Here g_dbus_connection_flush() is called
* before `exit()`. Without g_dbus_connection_flush(), the "quit" action
* may fail to be activated on the primary instance.
*
* |[<!-- language="C" -->
* // call "quit" action on primary instance
* g_action_group_activate_action (G_ACTION_GROUP (app), "quit", NULL);
*
* // make sure the action is activated now
* g_dbus_connection_flush (...);
*
* g_debug ("application has been terminated. exiting.");
*
* exit (0);
* ]|
*
* Since: 2.28
**/
void
g_action_group_activate_action (GActionGroup *action_group,
const gchar *action_name,
GVariant *parameter)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
G_ACTION_GROUP_GET_IFACE (action_group)
->activate_action (action_group, action_name, parameter);
}
/**
* g_action_group_action_added:
* @action_group: a #GActionGroup
* @action_name: the name of an action in the group
*
* Emits the #GActionGroup::action-added signal on @action_group.
*
* This function should only be called by #GActionGroup implementations.
*
* Since: 2.28
**/
void
g_action_group_action_added (GActionGroup *action_group,
const gchar *action_name)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
g_signal_emit (action_group,
g_action_group_signals[SIGNAL_ACTION_ADDED],
g_quark_try_string (action_name),
action_name);
}
/**
* g_action_group_action_removed:
* @action_group: a #GActionGroup
* @action_name: the name of an action in the group
*
* Emits the #GActionGroup::action-removed signal on @action_group.
*
* This function should only be called by #GActionGroup implementations.
*
* Since: 2.28
**/
void
g_action_group_action_removed (GActionGroup *action_group,
const gchar *action_name)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
g_signal_emit (action_group,
g_action_group_signals[SIGNAL_ACTION_REMOVED],
g_quark_try_string (action_name),
action_name);
}
/**
* g_action_group_action_enabled_changed:
* @action_group: a #GActionGroup
* @action_name: the name of an action in the group
* @enabled: whether or not the action is now enabled
*
* Emits the #GActionGroup::action-enabled-changed signal on @action_group.
*
* This function should only be called by #GActionGroup implementations.
*
* Since: 2.28
**/
void
g_action_group_action_enabled_changed (GActionGroup *action_group,
const gchar *action_name,
gboolean enabled)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
enabled = !!enabled;
g_signal_emit (action_group,
g_action_group_signals[SIGNAL_ACTION_ENABLED_CHANGED],
g_quark_try_string (action_name),
action_name,
enabled);
}
/**
* g_action_group_action_state_changed:
* @action_group: a #GActionGroup
* @action_name: the name of an action in the group
* @state: the new state of the named action
*
* Emits the #GActionGroup::action-state-changed signal on @action_group.
*
* This function should only be called by #GActionGroup implementations.
*
* Since: 2.28
**/
void
g_action_group_action_state_changed (GActionGroup *action_group,
const gchar *action_name,
GVariant *state)
{
g_return_if_fail (G_IS_ACTION_GROUP (action_group));
g_return_if_fail (action_name != NULL);
g_signal_emit (action_group,
g_action_group_signals[SIGNAL_ACTION_STATE_CHANGED],
g_quark_try_string (action_name),
action_name,
state);
}
/**
* g_action_group_query_action:
* @action_group: a #GActionGroup
* @action_name: the name of an action in the group
* @enabled: (out): if the action is presently enabled
* @parameter_type: (out) (transfer none) (optional): the parameter type, or %NULL if none needed
* @state_type: (out) (transfer none) (optional): the state type, or %NULL if stateless
* @state_hint: (out) (optional): the state hint, or %NULL if none
* @state: (out) (optional): the current state, or %NULL if stateless
*
* Queries all aspects of the named action within an @action_group.
*
* This function acquires the information available from
* g_action_group_has_action(), g_action_group_get_action_enabled(),
* g_action_group_get_action_parameter_type(),
* g_action_group_get_action_state_type(),
* g_action_group_get_action_state_hint() and
* g_action_group_get_action_state() with a single function call.
*
* This provides two main benefits.
*
* The first is the improvement in efficiency that comes with not having
* to perform repeated lookups of the action in order to discover
* different things about it. The second is that implementing
* #GActionGroup can now be done by only overriding this one virtual
* function.
*
* The interface provides a default implementation of this function that
* calls the individual functions, as required, to fetch the
* information. The interface also provides default implementations of
* those functions that call this function. All implementations,
* therefore, must override either this function or all of the others.
*
* If the action exists, %TRUE is returned and any of the requested
* fields (as indicated by having a non-%NULL reference passed in) are
* filled. If the action doesn't exist, %FALSE is returned and the
* fields may or may not have been modified.
*
* Returns: %TRUE if the action exists, else %FALSE
*
* Since: 2.32
**/
gboolean
g_action_group_query_action (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
return G_ACTION_GROUP_GET_IFACE (action_group)
->query_action (action_group, action_name, enabled, parameter_type, state_type, state_hint, state);
}

161
gio/gactiongroup.h Normal file
View file

@ -0,0 +1,161 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_ACTION_GROUP_H__
#define __G_ACTION_GROUP_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_ACTION_GROUP (g_action_group_get_type ())
#define G_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_ACTION_GROUP, GActionGroup))
#define G_IS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_ACTION_GROUP))
#define G_ACTION_GROUP_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
G_TYPE_ACTION_GROUP, GActionGroupInterface))
typedef struct _GActionGroupInterface GActionGroupInterface;
struct _GActionGroupInterface
{
GTypeInterface g_iface;
/* virtual functions */
gboolean (* has_action) (GActionGroup *action_group,
const gchar *action_name);
gchar ** (* list_actions) (GActionGroup *action_group);
gboolean (* get_action_enabled) (GActionGroup *action_group,
const gchar *action_name);
const GVariantType * (* get_action_parameter_type) (GActionGroup *action_group,
const gchar *action_name);
const GVariantType * (* get_action_state_type) (GActionGroup *action_group,
const gchar *action_name);
GVariant * (* get_action_state_hint) (GActionGroup *action_group,
const gchar *action_name);
GVariant * (* get_action_state) (GActionGroup *action_group,
const gchar *action_name);
void (* change_action_state) (GActionGroup *action_group,
const gchar *action_name,
GVariant *value);
void (* activate_action) (GActionGroup *action_group,
const gchar *action_name,
GVariant *parameter);
/* signals */
void (* action_added) (GActionGroup *action_group,
const gchar *action_name);
void (* action_removed) (GActionGroup *action_group,
const gchar *action_name);
void (* action_enabled_changed) (GActionGroup *action_group,
const gchar *action_name,
gboolean enabled);
void (* action_state_changed) (GActionGroup *action_group,
const gchar *action_name,
GVariant *state);
/* more virtual functions */
gboolean (* query_action) (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state);
};
GLIB_AVAILABLE_IN_ALL
GType g_action_group_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gboolean g_action_group_has_action (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
gchar ** g_action_group_list_actions (GActionGroup *action_group);
GLIB_AVAILABLE_IN_ALL
const GVariantType * g_action_group_get_action_parameter_type (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
const GVariantType * g_action_group_get_action_state_type (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
GVariant * g_action_group_get_action_state_hint (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
gboolean g_action_group_get_action_enabled (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
GVariant * g_action_group_get_action_state (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
void g_action_group_change_action_state (GActionGroup *action_group,
const gchar *action_name,
GVariant *value);
GLIB_AVAILABLE_IN_ALL
void g_action_group_activate_action (GActionGroup *action_group,
const gchar *action_name,
GVariant *parameter);
/* signals */
GLIB_AVAILABLE_IN_ALL
void g_action_group_action_added (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
void g_action_group_action_removed (GActionGroup *action_group,
const gchar *action_name);
GLIB_AVAILABLE_IN_ALL
void g_action_group_action_enabled_changed (GActionGroup *action_group,
const gchar *action_name,
gboolean enabled);
GLIB_AVAILABLE_IN_ALL
void g_action_group_action_state_changed (GActionGroup *action_group,
const gchar *action_name,
GVariant *state);
GLIB_AVAILABLE_IN_2_32
gboolean g_action_group_query_action (GActionGroup *action_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state);
G_END_DECLS
#endif /* __G_ACTION_GROUP_H__ */

608
gio/gactiongroupexporter.c Normal file
View file

@ -0,0 +1,608 @@
/*
* Copyright © 2010 Codethink Limited
* Copyright © 2011 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gactiongroupexporter.h"
#include "gdbusmethodinvocation.h"
#include "gremoteactiongroup.h"
#include "gdbusintrospection.h"
#include "gdbusconnection.h"
#include "gactiongroup.h"
#include "gdbuserror.h"
/**
* SECTION:gactiongroupexporter
* @title: GActionGroup exporter
* @include: gio/gio.h
* @short_description: Export GActionGroups on D-Bus
* @see_also: #GActionGroup, #GDBusActionGroup
*
* These functions support exporting a #GActionGroup on D-Bus.
* The D-Bus interface that is used is a private implementation
* detail.
*
* To access an exported #GActionGroup remotely, use
* g_dbus_action_group_get() to obtain a #GDBusActionGroup.
*/
static GVariant *
g_action_group_describe_action (GActionGroup *action_group,
const gchar *name)
{
const GVariantType *type;
GVariantBuilder builder;
gboolean enabled;
GVariant *state;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(bgav)"));
enabled = g_action_group_get_action_enabled (action_group, name);
g_variant_builder_add (&builder, "b", enabled);
if ((type = g_action_group_get_action_parameter_type (action_group, name)))
{
gchar *str = g_variant_type_dup_string (type);
g_variant_builder_add (&builder, "g", str);
g_free (str);
}
else
g_variant_builder_add (&builder, "g", "");
g_variant_builder_open (&builder, G_VARIANT_TYPE ("av"));
if ((state = g_action_group_get_action_state (action_group, name)))
{
g_variant_builder_add (&builder, "v", state);
g_variant_unref (state);
}
g_variant_builder_close (&builder);
return g_variant_builder_end (&builder);
}
/* Using XML saves us dozens of relocations vs. using the introspection
* structure types. We only need to burn cycles and memory if we
* actually use the exporter -- not in every single app using GIO.
*
* It's also a lot easier to read. :)
*
* For documentation of this interface, see
* https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
*/
const char org_gtk_Actions_xml[] =
"<node>"
" <interface name='org.gtk.Actions'>"
" <method name='List'>"
" <arg type='as' name='list' direction='out'/>"
" </method>"
" <method name='Describe'>"
" <arg type='s' name='action_name' direction='in'/>"
" <arg type='(bgav)' name='description' direction='out'/>"
" </method>"
" <method name='DescribeAll'>"
" <arg type='a{s(bgav)}' name='descriptions' direction='out'/>"
" </method>"
" <method name='Activate'>"
" <arg type='s' name='action_name' direction='in'/>"
" <arg type='av' name='parameter' direction='in'/>"
" <arg type='a{sv}' name='platform_data' direction='in'/>"
" </method>"
" <method name='SetState'>"
" <arg type='s' name='action_name' direction='in'/>"
" <arg type='v' name='value' direction='in'/>"
" <arg type='a{sv}' name='platform_data' direction='in'/>"
" </method>"
" <signal name='Changed'>"
" <arg type='as' name='removals'/>"
" <arg type='a{sb}' name='enable_changes'/>"
" <arg type='a{sv}' name='state_changes'/>"
" <arg type='a{s(bgav)}' name='additions'/>"
" </signal>"
" </interface>"
"</node>";
static GDBusInterfaceInfo *org_gtk_Actions;
typedef struct
{
GActionGroup *action_group;
GDBusConnection *connection;
GMainContext *context;
gchar *object_path;
GHashTable *pending_changes;
GSource *pending_source;
} GActionGroupExporter;
#define ACTION_ADDED_EVENT (1u<<0)
#define ACTION_REMOVED_EVENT (1u<<1)
#define ACTION_STATE_CHANGED_EVENT (1u<<2)
#define ACTION_ENABLED_CHANGED_EVENT (1u<<3)
static gboolean
g_action_group_exporter_dispatch_events (gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
GVariantBuilder removes;
GVariantBuilder enabled_changes;
GVariantBuilder state_changes;
GVariantBuilder adds;
GHashTableIter iter;
gpointer value;
gpointer key;
g_variant_builder_init (&removes, G_VARIANT_TYPE_STRING_ARRAY);
g_variant_builder_init (&enabled_changes, G_VARIANT_TYPE ("a{sb}"));
g_variant_builder_init (&state_changes, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_init (&adds, G_VARIANT_TYPE ("a{s(bgav)}"));
g_hash_table_iter_init (&iter, exporter->pending_changes);
while (g_hash_table_iter_next (&iter, &key, &value))
{
guint events = GPOINTER_TO_INT (value);
const gchar *name = key;
/* Adds and removes are incompatible with enabled or state
* changes, but we must report at least one event.
*/
g_assert (((events & (ACTION_ENABLED_CHANGED_EVENT | ACTION_STATE_CHANGED_EVENT)) == 0) !=
((events & (ACTION_REMOVED_EVENT | ACTION_ADDED_EVENT)) == 0));
if (events & ACTION_REMOVED_EVENT)
g_variant_builder_add (&removes, "s", name);
if (events & ACTION_ENABLED_CHANGED_EVENT)
{
gboolean enabled;
enabled = g_action_group_get_action_enabled (exporter->action_group, name);
g_variant_builder_add (&enabled_changes, "{sb}", name, enabled);
}
if (events & ACTION_STATE_CHANGED_EVENT)
{
GVariant *state;
state = g_action_group_get_action_state (exporter->action_group, name);
g_variant_builder_add (&state_changes, "{sv}", name, state);
g_variant_unref (state);
}
if (events & ACTION_ADDED_EVENT)
{
GVariant *description;
description = g_action_group_describe_action (exporter->action_group, name);
g_variant_builder_add (&adds, "{s@(bgav)}", name, description);
}
}
g_hash_table_remove_all (exporter->pending_changes);
g_dbus_connection_emit_signal (exporter->connection, NULL, exporter->object_path,
"org.gtk.Actions", "Changed",
g_variant_new ("(asa{sb}a{sv}a{s(bgav)})",
&removes, &enabled_changes,
&state_changes, &adds),
NULL);
exporter->pending_source = NULL;
return FALSE;
}
static void
g_action_group_exporter_flush_queue (GActionGroupExporter *exporter)
{
if (exporter->pending_source)
{
g_source_destroy (exporter->pending_source);
g_action_group_exporter_dispatch_events (exporter);
g_assert (exporter->pending_source == NULL);
}
}
static guint
g_action_group_exporter_get_events (GActionGroupExporter *exporter,
const gchar *name)
{
return (gsize) g_hash_table_lookup (exporter->pending_changes, name);
}
static void
g_action_group_exporter_set_events (GActionGroupExporter *exporter,
const gchar *name,
guint events)
{
gboolean have_events;
gboolean is_queued;
if (events != 0)
g_hash_table_insert (exporter->pending_changes, g_strdup (name), GINT_TO_POINTER (events));
else
g_hash_table_remove (exporter->pending_changes, name);
have_events = g_hash_table_size (exporter->pending_changes) > 0;
is_queued = exporter->pending_source != NULL;
if (have_events && !is_queued)
{
GSource *source;
source = g_idle_source_new ();
exporter->pending_source = source;
g_source_set_callback (source, g_action_group_exporter_dispatch_events, exporter, NULL);
g_source_set_static_name (source, "[gio] g_action_group_exporter_dispatch_events");
g_source_attach (source, exporter->context);
g_source_unref (source);
}
if (!have_events && is_queued)
{
g_source_destroy (exporter->pending_source);
exporter->pending_source = NULL;
}
}
static void
g_action_group_exporter_action_added (GActionGroup *action_group,
const gchar *action_name,
gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
guint event_mask;
event_mask = g_action_group_exporter_get_events (exporter, action_name);
/* The action is new, so we should not have any pending
* enabled-changed or state-changed signals for it.
*/
g_assert (~event_mask & (ACTION_STATE_CHANGED_EVENT |
ACTION_ENABLED_CHANGED_EVENT));
event_mask |= ACTION_ADDED_EVENT;
g_action_group_exporter_set_events (exporter, action_name, event_mask);
}
static void
g_action_group_exporter_action_removed (GActionGroup *action_group,
const gchar *action_name,
gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
guint event_mask;
event_mask = g_action_group_exporter_get_events (exporter, action_name);
/* If the add event for this is still queued then just cancel it since
* it's gone now.
*
* If the event was freshly added, there should not have been any
* enabled or state changed events.
*/
if (event_mask & ACTION_ADDED_EVENT)
{
g_assert (~event_mask & ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT));
event_mask &= ~ACTION_ADDED_EVENT;
}
/* Otherwise, queue a remove event. Drop any state or enabled changes
* that were queued before the remove. */
else
{
event_mask |= ACTION_REMOVED_EVENT;
event_mask &= ~(ACTION_STATE_CHANGED_EVENT | ACTION_ENABLED_CHANGED_EVENT);
}
g_action_group_exporter_set_events (exporter, action_name, event_mask);
}
static void
g_action_group_exporter_action_state_changed (GActionGroup *action_group,
const gchar *action_name,
GVariant *value,
gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
guint event_mask;
event_mask = g_action_group_exporter_get_events (exporter, action_name);
/* If it was removed, it must have been added back. Otherwise, why
* are we hearing about changes?
*/
g_assert (~event_mask & ACTION_REMOVED_EVENT ||
event_mask & ACTION_ADDED_EVENT);
/* If it is freshly added, don't also bother with the state change
* signal since the updated state will be sent as part of the pending
* add message.
*/
if (~event_mask & ACTION_ADDED_EVENT)
event_mask |= ACTION_STATE_CHANGED_EVENT;
g_action_group_exporter_set_events (exporter, action_name, event_mask);
}
static void
g_action_group_exporter_action_enabled_changed (GActionGroup *action_group,
const gchar *action_name,
gboolean enabled,
gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
guint event_mask;
event_mask = g_action_group_exporter_get_events (exporter, action_name);
/* Reasoning as above. */
g_assert (~event_mask & ACTION_REMOVED_EVENT ||
event_mask & ACTION_ADDED_EVENT);
if (~event_mask & ACTION_ADDED_EVENT)
event_mask |= ACTION_ENABLED_CHANGED_EVENT;
g_action_group_exporter_set_events (exporter, action_name, event_mask);
}
static void
org_gtk_Actions_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
GVariant *result = NULL;
g_action_group_exporter_flush_queue (exporter);
if (g_str_equal (method_name, "List"))
{
gchar **list;
list = g_action_group_list_actions (exporter->action_group);
result = g_variant_new ("(^as)", list);
g_strfreev (list);
}
else if (g_str_equal (method_name, "Describe"))
{
const gchar *name;
GVariant *desc;
g_variant_get (parameters, "(&s)", &name);
if (!g_action_group_has_action (exporter->action_group, name))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"The named action ('%s') does not exist.", name);
return;
}
desc = g_action_group_describe_action (exporter->action_group, name);
result = g_variant_new ("(@(bgav))", desc);
}
else if (g_str_equal (method_name, "DescribeAll"))
{
GVariantBuilder builder;
gchar **list;
gint i;
list = g_action_group_list_actions (exporter->action_group);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{s(bgav)}"));
for (i = 0; list[i]; i++)
{
const gchar *name = list[i];
GVariant *description;
description = g_action_group_describe_action (exporter->action_group, name);
g_variant_builder_add (&builder, "{s@(bgav)}", name, description);
}
result = g_variant_new ("(a{s(bgav)})", &builder);
g_strfreev (list);
}
else if (g_str_equal (method_name, "Activate"))
{
GVariant *parameter = NULL;
GVariant *platform_data;
GVariantIter *iter;
const gchar *name;
g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data);
g_variant_iter_next (iter, "v", &parameter);
g_variant_iter_free (iter);
if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
g_remote_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
name, parameter, platform_data);
else
g_action_group_activate_action (exporter->action_group, name, parameter);
if (parameter)
g_variant_unref (parameter);
g_variant_unref (platform_data);
}
else if (g_str_equal (method_name, "SetState"))
{
GVariant *platform_data;
const gchar *name;
GVariant *state;
g_variant_get (parameters, "(&sv@a{sv})", &name, &state, &platform_data);
if (G_IS_REMOTE_ACTION_GROUP (exporter->action_group))
g_remote_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (exporter->action_group),
name, state, platform_data);
else
g_action_group_change_action_state (exporter->action_group, name, state);
g_variant_unref (platform_data);
g_variant_unref (state);
}
else
g_assert_not_reached ();
g_dbus_method_invocation_return_value (invocation, result);
}
static void
g_action_group_exporter_free (gpointer user_data)
{
GActionGroupExporter *exporter = user_data;
g_signal_handlers_disconnect_by_func (exporter->action_group,
g_action_group_exporter_action_added, exporter);
g_signal_handlers_disconnect_by_func (exporter->action_group,
g_action_group_exporter_action_enabled_changed, exporter);
g_signal_handlers_disconnect_by_func (exporter->action_group,
g_action_group_exporter_action_state_changed, exporter);
g_signal_handlers_disconnect_by_func (exporter->action_group,
g_action_group_exporter_action_removed, exporter);
g_hash_table_unref (exporter->pending_changes);
if (exporter->pending_source)
g_source_destroy (exporter->pending_source);
g_main_context_unref (exporter->context);
g_object_unref (exporter->connection);
g_object_unref (exporter->action_group);
g_free (exporter->object_path);
g_slice_free (GActionGroupExporter, exporter);
}
/**
* g_dbus_connection_export_action_group:
* @connection: a #GDBusConnection
* @object_path: a D-Bus object path
* @action_group: a #GActionGroup
* @error: a pointer to a %NULL #GError, or %NULL
*
* Exports @action_group on @connection at @object_path.
*
* The implemented D-Bus API should be considered private. It is
* subject to change in the future.
*
* A given object path can only have one action group exported on it.
* If this constraint is violated, the export will fail and 0 will be
* returned (with @error set accordingly).
*
* You can unexport the action group using
* g_dbus_connection_unexport_action_group() with the return value of
* this function.
*
* The thread default main context is taken at the time of this call.
* All incoming action activations and state change requests are
* reported from this context. Any changes on the action group that
* cause it to emit signals must also come from this same context.
* Since incoming action activations and state change requests are
* rather likely to cause changes on the action group, this effectively
* limits a given action group to being exported from only one main
* context.
*
* Returns: the ID of the export (never zero), or 0 in case of failure
*
* Since: 2.32
**/
guint
g_dbus_connection_export_action_group (GDBusConnection *connection,
const gchar *object_path,
GActionGroup *action_group,
GError **error)
{
const GDBusInterfaceVTable vtable = {
org_gtk_Actions_method_call, NULL, NULL, { 0 }
};
GActionGroupExporter *exporter;
guint id;
if G_UNLIKELY (org_gtk_Actions == NULL)
{
GError *error = NULL;
GDBusNodeInfo *info;
info = g_dbus_node_info_new_for_xml (org_gtk_Actions_xml, &error);
if G_UNLIKELY (info == NULL)
g_error ("%s", error->message);
org_gtk_Actions = g_dbus_node_info_lookup_interface (info, "org.gtk.Actions");
g_assert (org_gtk_Actions != NULL);
g_dbus_interface_info_ref (org_gtk_Actions);
g_dbus_node_info_unref (info);
}
exporter = g_slice_new (GActionGroupExporter);
id = g_dbus_connection_register_object (connection, object_path, org_gtk_Actions, &vtable,
exporter, g_action_group_exporter_free, error);
if (id == 0)
{
g_slice_free (GActionGroupExporter, exporter);
return 0;
}
exporter->context = g_main_context_ref_thread_default ();
exporter->pending_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
exporter->pending_source = NULL;
exporter->action_group = g_object_ref (action_group);
exporter->connection = g_object_ref (connection);
exporter->object_path = g_strdup (object_path);
g_signal_connect (action_group, "action-added",
G_CALLBACK (g_action_group_exporter_action_added), exporter);
g_signal_connect (action_group, "action-removed",
G_CALLBACK (g_action_group_exporter_action_removed), exporter);
g_signal_connect (action_group, "action-state-changed",
G_CALLBACK (g_action_group_exporter_action_state_changed), exporter);
g_signal_connect (action_group, "action-enabled-changed",
G_CALLBACK (g_action_group_exporter_action_enabled_changed), exporter);
return id;
}
/**
* g_dbus_connection_unexport_action_group:
* @connection: a #GDBusConnection
* @export_id: the ID from g_dbus_connection_export_action_group()
*
* Reverses the effect of a previous call to
* g_dbus_connection_export_action_group().
*
* It is an error to call this function with an ID that wasn't returned
* from g_dbus_connection_export_action_group() or to call it with the
* same ID more than once.
*
* Since: 2.32
**/
void
g_dbus_connection_unexport_action_group (GDBusConnection *connection,
guint export_id)
{
g_dbus_connection_unregister_object (connection, export_id);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright © 2010 Codethink Limited
* Copyright © 2011 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_ACTION_GROUP_EXPORTER_H__
#define __G_ACTION_GROUP_EXPORTER_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
GLIB_AVAILABLE_IN_2_32
guint g_dbus_connection_export_action_group (GDBusConnection *connection,
const gchar *object_path,
GActionGroup *action_group,
GError **error);
GLIB_AVAILABLE_IN_2_32
void g_dbus_connection_unexport_action_group (GDBusConnection *connection,
guint export_id);
G_END_DECLS
#endif /* __G_ACTION_GROUP_EXPORTER_H__ */

285
gio/gactionmap.c Normal file
View file

@ -0,0 +1,285 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gsimpleaction.h"
#include "gactionmap.h"
#include "gaction.h"
/**
* SECTION:gactionmap
* @title: GActionMap
* @include: gio/gio.h
* @short_description: Interface for action containers
*
* The GActionMap interface is implemented by #GActionGroup
* implementations that operate by containing a number of
* named #GAction instances, such as #GSimpleActionGroup.
*
* One useful application of this interface is to map the
* names of actions from various action groups to unique,
* prefixed names (e.g. by prepending "app." or "win.").
* This is the motivation for the 'Map' part of the interface
* name.
*
* Since: 2.32
**/
/**
* GActionMap:
*
* #GActionMap is an opaque data structure and can only be accessed
* using the following functions.
**/
/**
* GActionMapInterface:
* @lookup_action: the virtual function pointer for g_action_map_lookup_action()
* @add_action: the virtual function pointer for g_action_map_add_action()
* @remove_action: the virtual function pointer for g_action_map_remove_action()
*
* The virtual function table for #GActionMap.
*
* Since: 2.32
**/
G_DEFINE_INTERFACE (GActionMap, g_action_map, G_TYPE_OBJECT)
static void
g_action_map_default_init (GActionMapInterface *iface)
{
}
/**
* g_action_map_lookup_action:
* @action_map: a #GActionMap
* @action_name: the name of an action
*
* Looks up the action with the name @action_name in @action_map.
*
* If no such action exists, returns %NULL.
*
* Returns: (nullable) (transfer none): a #GAction, or %NULL
*
* Since: 2.32
*/
GAction *
g_action_map_lookup_action (GActionMap *action_map,
const gchar *action_name)
{
return G_ACTION_MAP_GET_IFACE (action_map)
->lookup_action (action_map, action_name);
}
/**
* g_action_map_add_action:
* @action_map: a #GActionMap
* @action: a #GAction
*
* Adds an action to the @action_map.
*
* If the action map already contains an action with the same name
* as @action then the old action is dropped from the action map.
*
* The action map takes its own reference on @action.
*
* Since: 2.32
*/
void
g_action_map_add_action (GActionMap *action_map,
GAction *action)
{
G_ACTION_MAP_GET_IFACE (action_map)->add_action (action_map, action);
}
/**
* g_action_map_remove_action:
* @action_map: a #GActionMap
* @action_name: the name of the action
*
* Removes the named action from the action map.
*
* If no action of this name is in the map then nothing happens.
*
* Since: 2.32
*/
void
g_action_map_remove_action (GActionMap *action_map,
const gchar *action_name)
{
G_ACTION_MAP_GET_IFACE (action_map)->remove_action (action_map, action_name);
}
/**
* GActionEntry:
* @name: the name of the action
* @activate: the callback to connect to the "activate" signal of the
* action. Since GLib 2.40, this can be %NULL for stateful
* actions, in which case the default handler is used. For
* boolean-stated actions with no parameter, this is a
* toggle. For other state types (and parameter type equal
* to the state type) this will be a function that
* just calls @change_state (which you should provide).
* @parameter_type: the type of the parameter that must be passed to the
* activate function for this action, given as a single
* GVariant type string (or %NULL for no parameter)
* @state: the initial state for this action, given in
* [GVariant text format][gvariant-text]. The state is parsed
* with no extra type information, so type tags must be added to
* the string if they are necessary. Stateless actions should
* give %NULL here.
* @change_state: the callback to connect to the "change-state" signal
* of the action. All stateful actions should provide a
* handler here; stateless actions should not.
*
* This struct defines a single action. It is for use with
* g_action_map_add_action_entries().
*
* The order of the items in the structure are intended to reflect
* frequency of use. It is permissible to use an incomplete initialiser
* in order to leave some of the later values as %NULL. All values
* after @name are optional. Additional optional fields may be added in
* the future.
*
* See g_action_map_add_action_entries() for an example.
**/
/**
* g_action_map_add_action_entries:
* @action_map: a #GActionMap
* @entries: (array length=n_entries) (element-type GActionEntry): a pointer to
* the first item in an array of #GActionEntry structs
* @n_entries: the length of @entries, or -1 if @entries is %NULL-terminated
* @user_data: the user data for signal connections
*
* A convenience function for creating multiple #GSimpleAction instances
* and adding them to a #GActionMap.
*
* Each action is constructed as per one #GActionEntry.
*
* |[<!-- language="C" -->
* static void
* activate_quit (GSimpleAction *simple,
* GVariant *parameter,
* gpointer user_data)
* {
* exit (0);
* }
*
* static void
* activate_print_string (GSimpleAction *simple,
* GVariant *parameter,
* gpointer user_data)
* {
* g_print ("%s\n", g_variant_get_string (parameter, NULL));
* }
*
* static GActionGroup *
* create_action_group (void)
* {
* const GActionEntry entries[] = {
* { "quit", activate_quit },
* { "print-string", activate_print_string, "s" }
* };
* GSimpleActionGroup *group;
*
* group = g_simple_action_group_new ();
* g_action_map_add_action_entries (G_ACTION_MAP (group), entries, G_N_ELEMENTS (entries), NULL);
*
* return G_ACTION_GROUP (group);
* }
* ]|
*
* Since: 2.32
*/
void
g_action_map_add_action_entries (GActionMap *action_map,
const GActionEntry *entries,
gint n_entries,
gpointer user_data)
{
gint i;
g_return_if_fail (G_IS_ACTION_MAP (action_map));
g_return_if_fail (entries != NULL || n_entries == 0);
for (i = 0; n_entries == -1 ? entries[i].name != NULL : i < n_entries; i++)
{
const GActionEntry *entry = &entries[i];
const GVariantType *parameter_type;
GSimpleAction *action;
if (entry->parameter_type)
{
if (!g_variant_type_string_is_valid (entry->parameter_type))
{
g_critical ("g_action_map_add_entries: the type "
"string '%s' given as the parameter type for "
"action '%s' is not a valid GVariant type "
"string. This action will not be added.",
entry->parameter_type, entry->name);
return;
}
parameter_type = G_VARIANT_TYPE (entry->parameter_type);
}
else
parameter_type = NULL;
if (entry->state)
{
GError *error = NULL;
GVariant *state;
state = g_variant_parse (NULL, entry->state, NULL, NULL, &error);
if (state == NULL)
{
g_critical ("g_action_map_add_entries: GVariant could "
"not parse the state value given for action '%s' "
"('%s'): %s. This action will not be added.",
entry->name, entry->state, error->message);
g_error_free (error);
continue;
}
action = g_simple_action_new_stateful (entry->name,
parameter_type,
state);
g_variant_unref (state);
}
else
{
action = g_simple_action_new (entry->name,
parameter_type);
}
if (entry->activate != NULL)
g_signal_connect (action, "activate",
G_CALLBACK (entry->activate), user_data);
if (entry->change_state != NULL)
g_signal_connect (action, "change-state",
G_CALLBACK (entry->change_state), user_data);
g_action_map_add_action (action_map, G_ACTION (action));
g_object_unref (action);
}
}

95
gio/gactionmap.h Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_ACTION_MAP_H__
#define __G_ACTION_MAP_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_ACTION_MAP (g_action_map_get_type ())
#define G_ACTION_MAP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_ACTION_MAP, GActionMap))
#define G_IS_ACTION_MAP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_ACTION_MAP))
#define G_ACTION_MAP_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
G_TYPE_ACTION_MAP, GActionMapInterface))
typedef struct _GActionMapInterface GActionMapInterface;
typedef struct _GActionEntry GActionEntry;
struct _GActionMapInterface
{
GTypeInterface g_iface;
GAction * (* lookup_action) (GActionMap *action_map,
const gchar *action_name);
void (* add_action) (GActionMap *action_map,
GAction *action);
void (* remove_action) (GActionMap *action_map,
const gchar *action_name);
};
struct _GActionEntry
{
const gchar *name;
void (* activate) (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
const gchar *parameter_type;
const gchar *state;
void (* change_state) (GSimpleAction *action,
GVariant *value,
gpointer user_data);
/*< private >*/
gsize padding[3];
};
GLIB_AVAILABLE_IN_2_32
GType g_action_map_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_32
GAction * g_action_map_lookup_action (GActionMap *action_map,
const gchar *action_name);
GLIB_AVAILABLE_IN_2_32
void g_action_map_add_action (GActionMap *action_map,
GAction *action);
GLIB_AVAILABLE_IN_2_32
void g_action_map_remove_action (GActionMap *action_map,
const gchar *action_name);
GLIB_AVAILABLE_IN_2_32
void g_action_map_add_action_entries (GActionMap *action_map,
const GActionEntry *entries,
gint n_entries,
gpointer user_data);
G_END_DECLS
#endif /* __G_ACTION_MAP_H__ */

1520
gio/gappinfo.c Normal file

File diff suppressed because it is too large Load diff

349
gio/gappinfo.h Normal file
View file

@ -0,0 +1,349 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_APP_INFO_H__
#define __G_APP_INFO_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_APP_INFO (g_app_info_get_type ())
#define G_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_APP_INFO, GAppInfo))
#define G_IS_APP_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APP_INFO))
#define G_APP_INFO_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_APP_INFO, GAppInfoIface))
#define G_TYPE_APP_LAUNCH_CONTEXT (g_app_launch_context_get_type ())
#define G_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContext))
#define G_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
#define G_IS_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_APP_LAUNCH_CONTEXT))
#define G_IS_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_APP_LAUNCH_CONTEXT))
#define G_APP_LAUNCH_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
typedef struct _GAppLaunchContextClass GAppLaunchContextClass;
typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
/**
* GAppInfo:
*
* Information about an installed application and methods to launch
* it (with file arguments).
*/
/**
* GAppInfoIface:
* @g_iface: The parent interface.
* @dup: Copies a #GAppInfo.
* @equal: Checks two #GAppInfos for equality.
* @get_id: Gets a string identifier for a #GAppInfo.
* @get_name: Gets the name of the application for a #GAppInfo.
* @get_description: Gets a short description for the application described by the #GAppInfo.
* @get_executable: Gets the executable name for the #GAppInfo.
* @get_icon: Gets the #GIcon for the #GAppInfo.
* @launch: Launches an application specified by the #GAppInfo.
* @supports_uris: Indicates whether the application specified supports launching URIs.
* @supports_files: Indicates whether the application specified accepts filename arguments.
* @launch_uris: Launches an application with a list of URIs.
* @should_show: Returns whether an application should be shown (e.g. when getting a list of installed applications).
* [FreeDesktop.Org Startup Notification Specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt).
* @set_as_default_for_type: Sets an application as default for a given content type.
* @set_as_default_for_extension: Sets an application as default for a given file extension.
* @add_supports_type: Adds to the #GAppInfo information about supported file types.
* @can_remove_supports_type: Checks for support for removing supported file types from a #GAppInfo.
* @remove_supports_type: Removes a supported application type from a #GAppInfo.
* @can_delete: Checks if a #GAppInfo can be deleted. Since 2.20
* @do_delete: Deletes a #GAppInfo. Since 2.20
* @get_commandline: Gets the commandline for the #GAppInfo. Since 2.20
* @get_display_name: Gets the display name for the #GAppInfo. Since 2.24
* @set_as_last_used_for_type: Sets the application as the last used. See g_app_info_set_as_last_used_for_type().
* @get_supported_types: Retrieves the list of content types that @app_info claims to support.
* @launch_uris_async: Asynchronously launches an application with a list of URIs. (Since: 2.60)
* @launch_uris_finish: Finishes an operation started with @launch_uris_async. (Since: 2.60)
* Application Information interface, for operating system portability.
*/
typedef struct _GAppInfoIface GAppInfoIface;
struct _GAppInfoIface
{
GTypeInterface g_iface;
/* Virtual Table */
GAppInfo * (* dup) (GAppInfo *appinfo);
gboolean (* equal) (GAppInfo *appinfo1,
GAppInfo *appinfo2);
const char * (* get_id) (GAppInfo *appinfo);
const char * (* get_name) (GAppInfo *appinfo);
const char * (* get_description) (GAppInfo *appinfo);
const char * (* get_executable) (GAppInfo *appinfo);
GIcon * (* get_icon) (GAppInfo *appinfo);
gboolean (* launch) (GAppInfo *appinfo,
GList *files,
GAppLaunchContext *context,
GError **error);
gboolean (* supports_uris) (GAppInfo *appinfo);
gboolean (* supports_files) (GAppInfo *appinfo);
gboolean (* launch_uris) (GAppInfo *appinfo,
GList *uris,
GAppLaunchContext *context,
GError **error);
gboolean (* should_show) (GAppInfo *appinfo);
/* For changing associations */
gboolean (* set_as_default_for_type) (GAppInfo *appinfo,
const char *content_type,
GError **error);
gboolean (* set_as_default_for_extension) (GAppInfo *appinfo,
const char *extension,
GError **error);
gboolean (* add_supports_type) (GAppInfo *appinfo,
const char *content_type,
GError **error);
gboolean (* can_remove_supports_type) (GAppInfo *appinfo);
gboolean (* remove_supports_type) (GAppInfo *appinfo,
const char *content_type,
GError **error);
gboolean (* can_delete) (GAppInfo *appinfo);
gboolean (* do_delete) (GAppInfo *appinfo);
const char * (* get_commandline) (GAppInfo *appinfo);
const char * (* get_display_name) (GAppInfo *appinfo);
gboolean (* set_as_last_used_for_type) (GAppInfo *appinfo,
const char *content_type,
GError **error);
const char ** (* get_supported_types) (GAppInfo *appinfo);
void (* launch_uris_async) (GAppInfo *appinfo,
GList *uris,
GAppLaunchContext *context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* launch_uris_finish) (GAppInfo *appinfo,
GAsyncResult *result,
GError **error);
};
GLIB_AVAILABLE_IN_ALL
GType g_app_info_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GAppInfo * g_app_info_create_from_commandline (const char *commandline,
const char *application_name,
GAppInfoCreateFlags flags,
GError **error);
GLIB_AVAILABLE_IN_ALL
GAppInfo * g_app_info_dup (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_equal (GAppInfo *appinfo1,
GAppInfo *appinfo2);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_id (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_name (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_display_name (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_description (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_executable (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
const char *g_app_info_get_commandline (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
GIcon * g_app_info_get_icon (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_launch (GAppInfo *appinfo,
GList *files,
GAppLaunchContext *context,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_supports_uris (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_supports_files (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_launch_uris (GAppInfo *appinfo,
GList *uris,
GAppLaunchContext *context,
GError **error);
GLIB_AVAILABLE_IN_2_60
void g_app_info_launch_uris_async (GAppInfo *appinfo,
GList *uris,
GAppLaunchContext *context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_60
gboolean g_app_info_launch_uris_finish (GAppInfo *appinfo,
GAsyncResult *result,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_should_show (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_set_as_default_for_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_set_as_default_for_extension (GAppInfo *appinfo,
const char *extension,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_add_supports_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_can_remove_supports_type (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_remove_supports_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
GLIB_AVAILABLE_IN_2_34
const char **g_app_info_get_supported_types (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_can_delete (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_delete (GAppInfo *appinfo);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
const char *content_type,
GError **error);
GLIB_AVAILABLE_IN_ALL
GList * g_app_info_get_all (void);
GLIB_AVAILABLE_IN_ALL
GList * g_app_info_get_all_for_type (const char *content_type);
GLIB_AVAILABLE_IN_ALL
GList * g_app_info_get_recommended_for_type (const gchar *content_type);
GLIB_AVAILABLE_IN_ALL
GList * g_app_info_get_fallback_for_type (const gchar *content_type);
GLIB_AVAILABLE_IN_ALL
void g_app_info_reset_type_associations (const char *content_type);
GLIB_AVAILABLE_IN_ALL
GAppInfo *g_app_info_get_default_for_type (const char *content_type,
gboolean must_support_uris);
GLIB_AVAILABLE_IN_ALL
GAppInfo *g_app_info_get_default_for_uri_scheme (const char *uri_scheme);
GLIB_AVAILABLE_IN_ALL
gboolean g_app_info_launch_default_for_uri (const char *uri,
GAppLaunchContext *context,
GError **error);
GLIB_AVAILABLE_IN_2_50
void g_app_info_launch_default_for_uri_async (const char *uri,
GAppLaunchContext *context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_50
gboolean g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
GError **error);
/**
* GAppLaunchContext:
*
* Integrating the launch with the launching application. This is used to
* handle for instance startup notification and launching the new application
* on the same screen as the launching window.
*/
struct _GAppLaunchContext
{
GObject parent_instance;
/*< private >*/
GAppLaunchContextPrivate *priv;
};
struct _GAppLaunchContextClass
{
GObjectClass parent_class;
char * (* get_display) (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
char * (* get_startup_notify_id) (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
void (* launch_failed) (GAppLaunchContext *context,
const char *startup_notify_id);
void (* launched) (GAppLaunchContext *context,
GAppInfo *info,
GVariant *platform_data);
void (* launch_started) (GAppLaunchContext *context,
GAppInfo *info,
GVariant *platform_data);
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_app_launch_context_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GAppLaunchContext *g_app_launch_context_new (void);
GLIB_AVAILABLE_IN_2_32
void g_app_launch_context_setenv (GAppLaunchContext *context,
const char *variable,
const char *value);
GLIB_AVAILABLE_IN_2_32
void g_app_launch_context_unsetenv (GAppLaunchContext *context,
const char *variable);
GLIB_AVAILABLE_IN_2_32
char ** g_app_launch_context_get_environment (GAppLaunchContext *context);
GLIB_AVAILABLE_IN_ALL
char * g_app_launch_context_get_display (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
GLIB_AVAILABLE_IN_ALL
char * g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
GAppInfo *info,
GList *files);
GLIB_AVAILABLE_IN_ALL
void g_app_launch_context_launch_failed (GAppLaunchContext *context,
const char * startup_notify_id);
#define G_TYPE_APP_INFO_MONITOR (g_app_info_monitor_get_type ())
#define G_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_APP_INFO_MONITOR, GAppInfoMonitor))
#define G_IS_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_APP_INFO_MONITOR))
typedef struct _GAppInfoMonitor GAppInfoMonitor;
GLIB_AVAILABLE_IN_2_40
GType g_app_info_monitor_get_type (void);
GLIB_AVAILABLE_IN_2_40
GAppInfoMonitor * g_app_info_monitor_get (void);
G_END_DECLS
#endif /* __G_APP_INFO_H__ */

26
gio/gappinfoprivate.h Normal file
View file

@ -0,0 +1,26 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2013 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_APP_INFO_PRIVATE_H__
#define __G_APP_INFO_PRIVATE_H__
void g_app_info_monitor_fire (void);
#endif /* __G_APP_INFO_PRIVATE_H__ */

471
gio/gapplication-tool.c Normal file
View file

@ -0,0 +1,471 @@
/*
* Copyright © 2013 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <string.h>
#include <locale.h>
struct help_topic
{
const gchar *command;
const gchar *summary;
const gchar *description;
const gchar *synopsis;
};
struct help_substvar
{
const gchar *var;
const gchar *description;
};
static const struct help_topic topics[] = {
{ "help", N_("Print help"),
N_("Print help"),
N_("[COMMAND]")
},
{ "version", N_("Print version"),
N_("Print version information and exit"),
NULL
},
{ "list-apps", N_("List applications"),
N_("List the installed D-Bus activatable applications (by .desktop files)"),
NULL
},
{ "launch", N_("Launch an application"),
N_("Launch the application (with optional files to open)"),
N_("APPID [FILE…]")
},
{ "action", N_("Activate an action"),
N_("Invoke an action on the application"),
N_("APPID ACTION [PARAMETER]")
},
{ "list-actions", N_("List available actions"),
N_("List static actions for an application (from .desktop file)"),
N_("APPID")
}
};
static const struct help_substvar substvars[] = {
{ N_("COMMAND"), N_("The command to print detailed help for") },
{ N_("APPID"), N_("Application identifier in D-Bus format (eg: org.example.viewer)") },
{ N_("FILE"), N_("Optional relative or absolute filenames, or URIs to open") },
{ N_("ACTION"), N_("The action name to invoke") },
{ N_("PARAMETER"), N_("Optional parameter to the action invocation, in GVariant format") }
};
static int
app_help (gboolean requested,
const gchar *command)
{
const struct help_topic *topic = NULL;
GString *string;
string = g_string_new (NULL);
if (command)
{
gsize i;
for (i = 0; i < G_N_ELEMENTS (topics); i++)
if (g_str_equal (topics[i].command, command))
topic = &topics[i];
if (!topic)
{
g_string_printf (string, _("Unknown command %s\n\n"), command);
requested = FALSE;
}
}
g_string_append (string, _("Usage:\n"));
if (topic)
{
guint maxwidth;
gsize i;
g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication",
topic->command, topic->synopsis ? _(topic->synopsis) : "");
g_string_append_printf (string, "%s\n\n", _(topic->description));
if (topic->synopsis)
{
g_string_append (string, _("Arguments:\n"));
maxwidth = 0;
for (i = 0; i < G_N_ELEMENTS (substvars); i++)
if (strstr (topic->synopsis, substvars[i].var))
maxwidth = MAX(maxwidth, strlen (_(substvars[i].var)));
for (i = 0; i < G_N_ELEMENTS (substvars); i++)
if (strstr (topic->synopsis, substvars[i].var))
g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
_(substvars[i].var), _(substvars[i].description));
g_string_append (string, "\n");
}
}
else
{
guint maxwidth;
gsize i;
g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication", _("COMMAND"), _("[ARGS…]"));
g_string_append_printf (string, _("Commands:\n"));
maxwidth = 0;
for (i = 0; i < G_N_ELEMENTS (topics); i++)
maxwidth = MAX(maxwidth, strlen (topics[i].command));
for (i = 0; i < G_N_ELEMENTS (topics); i++)
g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
topics[i].command, _(topics[i].summary));
g_string_append (string, "\n");
/* Translators: do not translate 'help', but please translate 'COMMAND'. */
g_string_append_printf (string, _("Use “%s help COMMAND” to get detailed help.\n\n"), "gapplication");
}
if (requested)
g_print ("%s", string->str);
else
g_printerr ("%s\n", string->str);
g_string_free (string, TRUE);
return requested ? 0 : 1;
}
static gboolean
app_check_name (gchar **args,
const gchar *command)
{
if (args[0] == NULL)
{
g_printerr (_("%s command requires an application id to directly follow\n\n"), command);
return FALSE;
}
if (!g_dbus_is_name (args[0]))
{
g_printerr (_("invalid application id: “%s”\n"), args[0]);
return FALSE;
}
return TRUE;
}
static int
app_no_args (const gchar *command)
{
/* Translators: %s is replaced with a command name like 'list-actions' */
g_printerr (_("“%s” takes no arguments\n\n"), command);
return app_help (FALSE, command);
}
static int
app_version (gchar **args)
{
if (g_strv_length (args))
return app_no_args ("version");
g_print (PACKAGE_VERSION "\n");
return 0;
}
static int
app_list (gchar **args)
{
GList *apps;
if (g_strv_length (args))
return app_no_args ("list");
apps = g_app_info_get_all ();
while (apps)
{
GDesktopAppInfo *info = apps->data;
if (G_IS_DESKTOP_APP_INFO (info))
if (g_desktop_app_info_get_boolean (info, "DBusActivatable"))
{
const gchar *filename;
filename = g_app_info_get_id (G_APP_INFO (info));
if (g_str_has_suffix (filename, ".desktop"))
{
gchar *id;
id = g_strndup (filename, strlen (filename) - 8);
g_print ("%s\n", id);
g_free (id);
}
}
apps = g_list_delete_link (apps, apps);
g_object_unref (info);
}
return 0;
}
static gchar *
app_path_for_id (const gchar *app_id)
{
gchar *path;
gint i;
path = g_strconcat ("/", app_id, NULL);
for (i = 0; path[i]; i++)
{
if (path[i] == '.')
path[i] = '/';
if (path[i] == '-')
path[i] = '_';
}
return path;
}
static int
app_call (const gchar *app_id,
const gchar *method_name,
GVariant *parameters)
{
GDBusConnection *session;
GError *error = NULL;
gchar *object_path;
GVariant *result;
session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (!session)
{
g_variant_unref (g_variant_ref_sink (parameters));
g_printerr (_("unable to connect to D-Bus: %s\n"), error->message);
g_error_free (error);
return 1;
}
object_path = app_path_for_id (app_id);
result = g_dbus_connection_call_sync (session, app_id, object_path, "org.freedesktop.Application",
method_name, parameters, G_VARIANT_TYPE_UNIT,
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
g_free (object_path);
if (result)
{
g_variant_unref (result);
return 0;
}
else
{
g_printerr (_("error sending %s message to application: %s\n"), method_name, error->message);
g_error_free (error);
return 1;
}
}
static GVariant *
app_get_platform_data (void)
{
GVariantBuilder builder;
const gchar *startup_id;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
if ((startup_id = g_getenv ("DESKTOP_STARTUP_ID")))
g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_string (startup_id));
return g_variant_builder_end (&builder);
}
static int
app_action (gchar **args)
{
GVariantBuilder params;
const gchar *name;
if (!app_check_name (args, "action"))
return 1;
if (args[1] == NULL)
{
g_printerr (_("action name must be given after application id\n"));
return 1;
}
name = args[1];
if (!g_action_name_is_valid (name))
{
g_printerr (_("invalid action name: “%s”\n"
"action names must consist of only alphanumerics, “-” and “.”\n"), name);
return 1;
}
g_variant_builder_init (&params, G_VARIANT_TYPE ("av"));
if (args[2])
{
GError *error = NULL;
GVariant *parameter;
parameter = g_variant_parse (NULL, args[2], NULL, NULL, &error);
if (!parameter)
{
gchar *context;
context = g_variant_parse_error_print_context (error, args[2]);
g_printerr (_("error parsing action parameter: %s\n"), context);
g_variant_builder_clear (&params);
g_error_free (error);
g_free (context);
return 1;
}
g_variant_builder_add (&params, "v", parameter);
g_variant_unref (parameter);
if (args[3])
{
g_printerr (_("actions accept a maximum of one parameter\n"));
g_variant_builder_clear (&params);
return 1;
}
}
return app_call (args[0], "ActivateAction", g_variant_new ("(sav@a{sv})", name, &params, app_get_platform_data ()));
}
static int
app_activate (const gchar *app_id)
{
return app_call (app_id, "Activate", g_variant_new ("(@a{sv})", app_get_platform_data ()));
}
static int
app_launch (gchar **args)
{
GVariantBuilder files;
gint i;
if (!app_check_name (args, "launch"))
return 1;
if (args[1] == NULL)
return app_activate (args[0]);
g_variant_builder_init (&files, G_VARIANT_TYPE_STRING_ARRAY);
for (i = 1; args[i]; i++)
{
GFile *file;
/* "This operation never fails" */
file = g_file_new_for_commandline_arg (args[i]);
g_variant_builder_add_value (&files, g_variant_new_take_string (g_file_get_uri (file)));
g_object_unref (file);
}
return app_call (args[0], "Open", g_variant_new ("(as@a{sv})", &files, app_get_platform_data ()));
}
static int
app_list_actions (gchar **args)
{
const gchar * const *actions;
GDesktopAppInfo *app_info;
gchar *filename;
gint i;
if (!app_check_name (args, "list-actions"))
return 1;
if (args[1])
{
g_printerr (_("list-actions command takes only the application id"));
app_help (FALSE, "list-actions");
}
filename = g_strconcat (args[0], ".desktop", NULL);
app_info = g_desktop_app_info_new (filename);
g_free (filename);
if (app_info == NULL)
{
g_printerr (_("unable to find desktop file for application %s\n"), args[0]);
return 1;
}
actions = g_desktop_app_info_list_actions (app_info);
for (i = 0; actions[i]; i++)
g_print ("%s\n", actions[i]);
g_object_unref (app_info);
return 0;
}
int
main (int argc, char **argv)
{
setlocale (LC_ALL, "");
textdomain (GETTEXT_PACKAGE);
bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
if (argc < 2)
return app_help (TRUE, NULL);
if (g_str_equal (argv[1], "help"))
return app_help (TRUE, argv[2]);
if (g_str_equal (argv[1], "version"))
return app_version (argv + 2);
if (g_str_equal (argv[1], "list-apps"))
return app_list (argv + 2);
if (g_str_equal (argv[1], "launch"))
return app_launch (argv + 2);
if (g_str_equal (argv[1], "action"))
return app_action (argv + 2);
if (g_str_equal (argv[1], "list-actions"))
return app_list_actions (argv + 2);
g_printerr (_("unrecognised command: %s\n\n"), argv[1]);
return app_help (FALSE, NULL);
}

3136
gio/gapplication.c Normal file

File diff suppressed because it is too large Load diff

252
gio/gapplication.h Normal file
View file

@ -0,0 +1,252 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_APPLICATION_H__
#define __G_APPLICATION_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_APPLICATION (g_application_get_type ())
#define G_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_APPLICATION, GApplication))
#define G_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
G_TYPE_APPLICATION, GApplicationClass))
#define G_IS_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_APPLICATION))
#define G_IS_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_APPLICATION))
#define G_APPLICATION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
G_TYPE_APPLICATION, GApplicationClass))
typedef struct _GApplicationPrivate GApplicationPrivate;
typedef struct _GApplicationClass GApplicationClass;
struct _GApplication
{
/*< private >*/
GObject parent_instance;
GApplicationPrivate *priv;
};
struct _GApplicationClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* signals */
void (* startup) (GApplication *application);
void (* activate) (GApplication *application);
void (* open) (GApplication *application,
GFile **files,
gint n_files,
const gchar *hint);
int (* command_line) (GApplication *application,
GApplicationCommandLine *command_line);
/* vfuncs */
/**
* GApplicationClass::local_command_line:
* @application: a #GApplication
* @arguments: (inout) (array zero-terminated=1): array of command line arguments
* @exit_status: (out): exit status to fill after processing the command line.
*
* This virtual function is always invoked in the local instance. It
* gets passed a pointer to a %NULL-terminated copy of @argv and is
* expected to remove arguments that it handled (shifting up remaining
* arguments).
*
* The last argument to local_command_line() is a pointer to the @status
* variable which can used to set the exit status that is returned from
* g_application_run().
*
* See g_application_run() for more details on #GApplication startup.
*
* Returns: %TRUE if the commandline has been completely handled
*/
gboolean (* local_command_line) (GApplication *application,
gchar ***arguments,
int *exit_status);
void (* before_emit) (GApplication *application,
GVariant *platform_data);
void (* after_emit) (GApplication *application,
GVariant *platform_data);
void (* add_platform_data) (GApplication *application,
GVariantBuilder *builder);
void (* quit_mainloop) (GApplication *application);
void (* run_mainloop) (GApplication *application);
void (* shutdown) (GApplication *application);
gboolean (* dbus_register) (GApplication *application,
GDBusConnection *connection,
const gchar *object_path,
GError **error);
void (* dbus_unregister) (GApplication *application,
GDBusConnection *connection,
const gchar *object_path);
gint (* handle_local_options)(GApplication *application,
GVariantDict *options);
gboolean (* name_lost) (GApplication *application);
/*< private >*/
gpointer padding[7];
};
GLIB_AVAILABLE_IN_ALL
GType g_application_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gboolean g_application_id_is_valid (const gchar *application_id);
GLIB_AVAILABLE_IN_ALL
GApplication * g_application_new (const gchar *application_id,
GApplicationFlags flags);
GLIB_AVAILABLE_IN_ALL
const gchar * g_application_get_application_id (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_set_application_id (GApplication *application,
const gchar *application_id);
GLIB_AVAILABLE_IN_2_34
GDBusConnection * g_application_get_dbus_connection (GApplication *application);
GLIB_AVAILABLE_IN_2_34
const gchar * g_application_get_dbus_object_path (GApplication *application);
GLIB_AVAILABLE_IN_ALL
guint g_application_get_inactivity_timeout (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_set_inactivity_timeout (GApplication *application,
guint inactivity_timeout);
GLIB_AVAILABLE_IN_ALL
GApplicationFlags g_application_get_flags (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_set_flags (GApplication *application,
GApplicationFlags flags);
GLIB_AVAILABLE_IN_2_42
const gchar * g_application_get_resource_base_path (GApplication *application);
GLIB_AVAILABLE_IN_2_42
void g_application_set_resource_base_path (GApplication *application,
const gchar *resource_path);
GLIB_DEPRECATED
void g_application_set_action_group (GApplication *application,
GActionGroup *action_group);
GLIB_AVAILABLE_IN_2_40
void g_application_add_main_option_entries (GApplication *application,
const GOptionEntry *entries);
GLIB_AVAILABLE_IN_2_42
void g_application_add_main_option (GApplication *application,
const char *long_name,
char short_name,
GOptionFlags flags,
GOptionArg arg,
const char *description,
const char *arg_description);
GLIB_AVAILABLE_IN_2_40
void g_application_add_option_group (GApplication *application,
GOptionGroup *group);
GLIB_AVAILABLE_IN_2_56
void g_application_set_option_context_parameter_string (GApplication *application,
const gchar *parameter_string);
GLIB_AVAILABLE_IN_2_56
void g_application_set_option_context_summary (GApplication *application,
const gchar *summary);
GLIB_AVAILABLE_IN_2_56
void g_application_set_option_context_description (GApplication *application,
const gchar *description);
GLIB_AVAILABLE_IN_ALL
gboolean g_application_get_is_registered (GApplication *application);
GLIB_AVAILABLE_IN_ALL
gboolean g_application_get_is_remote (GApplication *application);
GLIB_AVAILABLE_IN_ALL
gboolean g_application_register (GApplication *application,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_application_hold (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_release (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_activate (GApplication *application);
GLIB_AVAILABLE_IN_ALL
void g_application_open (GApplication *application,
GFile **files,
gint n_files,
const gchar *hint);
GLIB_AVAILABLE_IN_ALL
int g_application_run (GApplication *application,
int argc,
char **argv);
GLIB_AVAILABLE_IN_2_32
void g_application_quit (GApplication *application);
GLIB_AVAILABLE_IN_2_32
GApplication * g_application_get_default (void);
GLIB_AVAILABLE_IN_2_32
void g_application_set_default (GApplication *application);
GLIB_AVAILABLE_IN_2_38
void g_application_mark_busy (GApplication *application);
GLIB_AVAILABLE_IN_2_38
void g_application_unmark_busy (GApplication *application);
GLIB_AVAILABLE_IN_2_44
gboolean g_application_get_is_busy (GApplication *application);
GLIB_AVAILABLE_IN_2_40
void g_application_send_notification (GApplication *application,
const gchar *id,
GNotification *notification);
GLIB_AVAILABLE_IN_2_40
void g_application_withdraw_notification (GApplication *application,
const gchar *id);
GLIB_AVAILABLE_IN_2_44
void g_application_bind_busy_property (GApplication *application,
gpointer object,
const gchar *property);
GLIB_AVAILABLE_IN_2_44
void g_application_unbind_busy_property (GApplication *application,
gpointer object,
const gchar *property);
G_END_DECLS
#endif /* __G_APPLICATION_H__ */

View file

@ -0,0 +1,840 @@
/*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gapplicationcommandline.h"
#include "glibintl.h"
#include "gfile.h"
#include <string.h>
#include <stdio.h>
#ifdef G_OS_UNIX
#include "gunixinputstream.h"
#endif
#ifdef G_OS_WIN32
#include <windows.h>
#undef environ
#include "gwin32inputstream.h"
#endif
/**
* SECTION:gapplicationcommandline
* @title: GApplicationCommandLine
* @short_description: A command-line invocation of an application
* @include: gio/gio.h
* @see_also: #GApplication
*
* #GApplicationCommandLine represents a command-line invocation of
* an application. It is created by #GApplication and emitted
* in the #GApplication::command-line signal and virtual function.
*
* The class contains the list of arguments that the program was invoked
* with. It is also possible to query if the commandline invocation was
* local (ie: the current process is running in direct response to the
* invocation) or remote (ie: some other process forwarded the
* commandline to this process).
*
* The GApplicationCommandLine object can provide the @argc and @argv
* parameters for use with the #GOptionContext command-line parsing API,
* with the g_application_command_line_get_arguments() function. See
* [gapplication-example-cmdline3.c][gapplication-example-cmdline3]
* for an example.
*
* The exit status of the originally-invoked process may be set and
* messages can be printed to stdout or stderr of that process. The
* lifecycle of the originally-invoked process is tied to the lifecycle
* of this object (ie: the process exits when the last reference is
* dropped).
*
* The main use for #GApplicationCommandLine (and the
* #GApplication::command-line signal) is 'Emacs server' like use cases:
* You can set the `EDITOR` environment variable to have e.g. git use
* your favourite editor to edit commit messages, and if you already
* have an instance of the editor running, the editing will happen
* in the running instance, instead of opening a new one. An important
* aspect of this use case is that the process that gets started by git
* does not return until the editing is done.
*
* Normally, the commandline is completely handled in the
* #GApplication::command-line handler. The launching instance exits
* once the signal handler in the primary instance has returned, and
* the return value of the signal handler becomes the exit status
* of the launching instance.
* |[<!-- language="C" -->
* static int
* command_line (GApplication *application,
* GApplicationCommandLine *cmdline)
* {
* gchar **argv;
* gint argc;
* gint i;
*
* argv = g_application_command_line_get_arguments (cmdline, &argc);
*
* g_application_command_line_print (cmdline,
* "This text is written back\n"
* "to stdout of the caller\n");
*
* for (i = 0; i < argc; i++)
* g_print ("argument %d: %s\n", i, argv[i]);
*
* g_strfreev (argv);
*
* return 0;
* }
* ]|
* The complete example can be found here:
* [gapplication-example-cmdline.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline.c)
*
* In more complicated cases, the handling of the commandline can be
* split between the launcher and the primary instance.
* |[<!-- language="C" -->
* static gboolean
* test_local_cmdline (GApplication *application,
* gchar ***arguments,
* gint *exit_status)
* {
* gint i, j;
* gchar **argv;
*
* argv = *arguments;
*
* if (argv[0] == NULL)
* {
* *exit_status = 0;
* return FALSE;
* }
*
* i = 1;
* while (argv[i])
* {
* if (g_str_has_prefix (argv[i], "--local-"))
* {
* g_print ("handling argument %s locally\n", argv[i]);
* g_free (argv[i]);
* for (j = i; argv[j]; j++)
* argv[j] = argv[j + 1];
* }
* else
* {
* g_print ("not handling argument %s locally\n", argv[i]);
* i++;
* }
* }
*
* *exit_status = 0;
*
* return FALSE;
* }
*
* static void
* test_application_class_init (TestApplicationClass *class)
* {
* G_APPLICATION_CLASS (class)->local_command_line = test_local_cmdline;
*
* ...
* }
* ]|
* In this example of split commandline handling, options that start
* with `--local-` are handled locally, all other options are passed
* to the #GApplication::command-line handler which runs in the primary
* instance.
*
* The complete example can be found here:
* [gapplication-example-cmdline2.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline2.c)
*
* If handling the commandline requires a lot of work, it may
* be better to defer it.
* |[<!-- language="C" -->
* static gboolean
* my_cmdline_handler (gpointer data)
* {
* GApplicationCommandLine *cmdline = data;
*
* // do the heavy lifting in an idle
*
* g_application_command_line_set_exit_status (cmdline, 0);
* g_object_unref (cmdline); // this releases the application
*
* return G_SOURCE_REMOVE;
* }
*
* static int
* command_line (GApplication *application,
* GApplicationCommandLine *cmdline)
* {
* // keep the application running until we are done with this commandline
* g_application_hold (application);
*
* g_object_set_data_full (G_OBJECT (cmdline),
* "application", application,
* (GDestroyNotify)g_application_release);
*
* g_object_ref (cmdline);
* g_idle_add (my_cmdline_handler, cmdline);
*
* return 0;
* }
* ]|
* In this example the commandline is not completely handled before
* the #GApplication::command-line handler returns. Instead, we keep
* a reference to the #GApplicationCommandLine object and handle it
* later (in this example, in an idle). Note that it is necessary to
* hold the application until you are done with the commandline.
*
* The complete example can be found here:
* [gapplication-example-cmdline3.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gapplication-example-cmdline3.c)
*/
/**
* GApplicationCommandLine:
*
* #GApplicationCommandLine is an opaque data structure and can only be accessed
* using the following functions.
*/
/**
* GApplicationCommandLineClass:
*
* The #GApplicationCommandLineClass-struct
* contains private data only.
*
* Since: 2.28
**/
enum
{
PROP_NONE,
PROP_ARGUMENTS,
PROP_OPTIONS,
PROP_PLATFORM_DATA,
PROP_IS_REMOTE
};
struct _GApplicationCommandLinePrivate
{
GVariant *platform_data;
GVariant *arguments;
GVariant *options;
GVariantDict *options_dict;
gchar *cwd; /* in GLib filename encoding, not UTF-8 */
gchar **environ;
gint exit_status;
};
G_DEFINE_TYPE_WITH_PRIVATE (GApplicationCommandLine, g_application_command_line, G_TYPE_OBJECT)
/* All subclasses represent remote invocations of some kind. */
#define IS_REMOTE(cmdline) (G_TYPE_FROM_INSTANCE (cmdline) != \
G_TYPE_APPLICATION_COMMAND_LINE)
static void
grok_platform_data (GApplicationCommandLine *cmdline)
{
GVariantIter iter;
const gchar *key;
GVariant *value;
g_variant_iter_init (&iter, cmdline->priv->platform_data);
while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
if (strcmp (key, "cwd") == 0)
{
if (!cmdline->priv->cwd)
cmdline->priv->cwd = g_variant_dup_bytestring (value, NULL);
}
else if (strcmp (key, "environ") == 0)
{
if (!cmdline->priv->environ)
cmdline->priv->environ =
g_variant_dup_bytestring_array (value, NULL);
}
else if (strcmp (key, "options") == 0)
{
if (!cmdline->priv->options)
cmdline->priv->options = g_variant_ref (value);
}
}
static void
g_application_command_line_real_print_literal (GApplicationCommandLine *cmdline,
const gchar *message)
{
g_print ("%s", message);
}
static void
g_application_command_line_real_printerr_literal (GApplicationCommandLine *cmdline,
const gchar *message)
{
g_printerr ("%s", message);
}
static GInputStream *
g_application_command_line_real_get_stdin (GApplicationCommandLine *cmdline)
{
#ifdef G_OS_UNIX
return g_unix_input_stream_new (0, FALSE);
#else
return g_win32_input_stream_new (GetStdHandle (STD_INPUT_HANDLE), FALSE);
#endif
}
static void
g_application_command_line_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
switch (prop_id)
{
case PROP_ARGUMENTS:
g_value_set_variant (value, cmdline->priv->arguments);
break;
case PROP_PLATFORM_DATA:
g_value_set_variant (value, cmdline->priv->platform_data);
break;
case PROP_IS_REMOTE:
g_value_set_boolean (value, IS_REMOTE (cmdline));
break;
default:
g_assert_not_reached ();
}
}
static void
g_application_command_line_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
switch (prop_id)
{
case PROP_ARGUMENTS:
g_assert (cmdline->priv->arguments == NULL);
cmdline->priv->arguments = g_value_dup_variant (value);
break;
case PROP_OPTIONS:
g_assert (cmdline->priv->options == NULL);
cmdline->priv->options = g_value_dup_variant (value);
break;
case PROP_PLATFORM_DATA:
g_assert (cmdline->priv->platform_data == NULL);
cmdline->priv->platform_data = g_value_dup_variant (value);
if (cmdline->priv->platform_data != NULL)
grok_platform_data (cmdline);
break;
default:
g_assert_not_reached ();
}
}
static void
g_application_command_line_finalize (GObject *object)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
if (cmdline->priv->options_dict)
g_variant_dict_unref (cmdline->priv->options_dict);
if (cmdline->priv->options)
g_variant_unref (cmdline->priv->options);
if (cmdline->priv->platform_data)
g_variant_unref (cmdline->priv->platform_data);
if (cmdline->priv->arguments)
g_variant_unref (cmdline->priv->arguments);
g_free (cmdline->priv->cwd);
g_strfreev (cmdline->priv->environ);
G_OBJECT_CLASS (g_application_command_line_parent_class)
->finalize (object);
}
static void
g_application_command_line_init (GApplicationCommandLine *cmdline)
{
cmdline->priv = g_application_command_line_get_instance_private (cmdline);
}
static void
g_application_command_line_constructed (GObject *object)
{
GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
if (IS_REMOTE (cmdline))
return;
/* In the local case, set cmd and environ */
if (!cmdline->priv->cwd)
cmdline->priv->cwd = g_get_current_dir ();
if (!cmdline->priv->environ)
cmdline->priv->environ = g_get_environ ();
}
static void
g_application_command_line_class_init (GApplicationCommandLineClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->get_property = g_application_command_line_get_property;
object_class->set_property = g_application_command_line_set_property;
object_class->finalize = g_application_command_line_finalize;
object_class->constructed = g_application_command_line_constructed;
class->printerr_literal = g_application_command_line_real_printerr_literal;
class->print_literal = g_application_command_line_real_print_literal;
class->get_stdin = g_application_command_line_real_get_stdin;
g_object_class_install_property (object_class, PROP_ARGUMENTS,
g_param_spec_variant ("arguments",
P_("Commandline arguments"),
P_("The commandline that caused this ::command-line signal emission"),
G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_OPTIONS,
g_param_spec_variant ("options",
P_("Options"),
P_("The options sent along with the commandline"),
G_VARIANT_TYPE_VARDICT, NULL, G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PLATFORM_DATA,
g_param_spec_variant ("platform-data",
P_("Platform data"),
P_("Platform-specific data for the commandline"),
G_VARIANT_TYPE ("a{sv}"), NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_IS_REMOTE,
g_param_spec_boolean ("is-remote",
P_("Is remote"),
P_("TRUE if this is a remote commandline"),
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
/**
* g_application_command_line_get_arguments:
* @cmdline: a #GApplicationCommandLine
* @argc: (out) (optional): the length of the arguments array, or %NULL
*
* Gets the list of arguments that was passed on the command line.
*
* The strings in the array may contain non-UTF-8 data on UNIX (such as
* filenames or arguments given in the system locale) but are always in
* UTF-8 on Windows.
*
* If you wish to use the return value with #GOptionContext, you must
* use g_option_context_parse_strv().
*
* The return value is %NULL-terminated and should be freed using
* g_strfreev().
*
* Returns: (array length=argc) (element-type filename) (transfer full)
* the string array containing the arguments (the argv)
*
* Since: 2.28
**/
gchar **
g_application_command_line_get_arguments (GApplicationCommandLine *cmdline,
int *argc)
{
gchar **argv;
gsize len;
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
argv = g_variant_dup_bytestring_array (cmdline->priv->arguments, &len);
if (argc)
*argc = len;
return argv;
}
/**
* g_application_command_line_get_options_dict:
* @cmdline: a #GApplicationCommandLine
*
* Gets the options there were passed to g_application_command_line().
*
* If you did not override local_command_line() then these are the same
* options that were parsed according to the #GOptionEntrys added to the
* application with g_application_add_main_option_entries() and possibly
* modified from your GApplication::handle-local-options handler.
*
* If no options were sent then an empty dictionary is returned so that
* you don't need to check for %NULL.
*
* Returns: (transfer none): a #GVariantDict with the options
*
* Since: 2.40
**/
GVariantDict *
g_application_command_line_get_options_dict (GApplicationCommandLine *cmdline)
{
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
if (!cmdline->priv->options_dict)
cmdline->priv->options_dict = g_variant_dict_new (cmdline->priv->options);
return cmdline->priv->options_dict;
}
/**
* g_application_command_line_get_stdin:
* @cmdline: a #GApplicationCommandLine
*
* Gets the stdin of the invoking process.
*
* The #GInputStream can be used to read data passed to the standard
* input of the invoking process.
* This doesn't work on all platforms. Presently, it is only available
* on UNIX when using a D-Bus daemon capable of passing file descriptors.
* If stdin is not available then %NULL will be returned. In the
* future, support may be expanded to other platforms.
*
* You must only call this function once per commandline invocation.
*
* Returns: (nullable) (transfer full): a #GInputStream for stdin
*
* Since: 2.34
**/
GInputStream *
g_application_command_line_get_stdin (GApplicationCommandLine *cmdline)
{
return G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)->get_stdin (cmdline);
}
/**
* g_application_command_line_get_cwd:
* @cmdline: a #GApplicationCommandLine
*
* Gets the working directory of the command line invocation.
* The string may contain non-utf8 data.
*
* It is possible that the remote application did not send a working
* directory, so this may be %NULL.
*
* The return value should not be modified or freed and is valid for as
* long as @cmdline exists.
*
* Returns: (nullable) (type filename): the current directory, or %NULL
*
* Since: 2.28
**/
const gchar *
g_application_command_line_get_cwd (GApplicationCommandLine *cmdline)
{
return cmdline->priv->cwd;
}
/**
* g_application_command_line_get_environ:
* @cmdline: a #GApplicationCommandLine
*
* Gets the contents of the 'environ' variable of the command line
* invocation, as would be returned by g_get_environ(), ie as a
* %NULL-terminated list of strings in the form 'NAME=VALUE'.
* The strings may contain non-utf8 data.
*
* The remote application usually does not send an environment. Use
* %G_APPLICATION_SEND_ENVIRONMENT to affect that. Even with this flag
* set it is possible that the environment is still not available (due
* to invocation messages from other applications).
*
* The return value should not be modified or freed and is valid for as
* long as @cmdline exists.
*
* See g_application_command_line_getenv() if you are only interested
* in the value of a single environment variable.
*
* Returns: (array zero-terminated=1) (element-type filename) (transfer none):
* the environment strings, or %NULL if they were not sent
*
* Since: 2.28
**/
const gchar * const *
g_application_command_line_get_environ (GApplicationCommandLine *cmdline)
{
return (const gchar **)cmdline->priv->environ;
}
/**
* g_application_command_line_getenv:
* @cmdline: a #GApplicationCommandLine
* @name: (type filename): the environment variable to get
*
* Gets the value of a particular environment variable of the command
* line invocation, as would be returned by g_getenv(). The strings may
* contain non-utf8 data.
*
* The remote application usually does not send an environment. Use
* %G_APPLICATION_SEND_ENVIRONMENT to affect that. Even with this flag
* set it is possible that the environment is still not available (due
* to invocation messages from other applications).
*
* The return value should not be modified or freed and is valid for as
* long as @cmdline exists.
*
* Returns: (nullable): the value of the variable, or %NULL if unset or unsent
*
* Since: 2.28
**/
const gchar *
g_application_command_line_getenv (GApplicationCommandLine *cmdline,
const gchar *name)
{
gint length = strlen (name);
gint i;
/* TODO: expand on windows */
if (cmdline->priv->environ)
for (i = 0; cmdline->priv->environ[i]; i++)
if (strncmp (cmdline->priv->environ[i], name, length) == 0 &&
cmdline->priv->environ[i][length] == '=')
return cmdline->priv->environ[i] + length + 1;
return NULL;
}
/**
* g_application_command_line_get_is_remote:
* @cmdline: a #GApplicationCommandLine
*
* Determines if @cmdline represents a remote invocation.
*
* Returns: %TRUE if the invocation was remote
*
* Since: 2.28
**/
gboolean
g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline)
{
return IS_REMOTE (cmdline);
}
/**
* g_application_command_line_print:
* @cmdline: a #GApplicationCommandLine
* @format: a printf-style format string
* @...: arguments, as per @format
*
* Formats a message and prints it using the stdout print handler in the
* invoking process.
*
* If @cmdline is a local invocation then this is exactly equivalent to
* g_print(). If @cmdline is remote then this is equivalent to calling
* g_print() in the invoking process.
*
* Since: 2.28
**/
void
g_application_command_line_print (GApplicationCommandLine *cmdline,
const gchar *format,
...)
{
gchar *message;
va_list ap;
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
g_return_if_fail (format != NULL);
va_start (ap, format);
message = g_strdup_vprintf (format, ap);
va_end (ap);
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
->print_literal (cmdline, message);
g_free (message);
}
/**
* g_application_command_line_printerr:
* @cmdline: a #GApplicationCommandLine
* @format: a printf-style format string
* @...: arguments, as per @format
*
* Formats a message and prints it using the stderr print handler in the
* invoking process.
*
* If @cmdline is a local invocation then this is exactly equivalent to
* g_printerr(). If @cmdline is remote then this is equivalent to
* calling g_printerr() in the invoking process.
*
* Since: 2.28
**/
void
g_application_command_line_printerr (GApplicationCommandLine *cmdline,
const gchar *format,
...)
{
gchar *message;
va_list ap;
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
g_return_if_fail (format != NULL);
va_start (ap, format);
message = g_strdup_vprintf (format, ap);
va_end (ap);
G_APPLICATION_COMMAND_LINE_GET_CLASS (cmdline)
->printerr_literal (cmdline, message);
g_free (message);
}
/**
* g_application_command_line_set_exit_status:
* @cmdline: a #GApplicationCommandLine
* @exit_status: the exit status
*
* Sets the exit status that will be used when the invoking process
* exits.
*
* The return value of the #GApplication::command-line signal is
* passed to this function when the handler returns. This is the usual
* way of setting the exit status.
*
* In the event that you want the remote invocation to continue running
* and want to decide on the exit status in the future, you can use this
* call. For the case of a remote invocation, the remote process will
* typically exit when the last reference is dropped on @cmdline. The
* exit status of the remote process will be equal to the last value
* that was set with this function.
*
* In the case that the commandline invocation is local, the situation
* is slightly more complicated. If the commandline invocation results
* in the mainloop running (ie: because the use-count of the application
* increased to a non-zero value) then the application is considered to
* have been 'successful' in a certain sense, and the exit status is
* always zero. If the application use count is zero, though, the exit
* status of the local #GApplicationCommandLine is used.
*
* Since: 2.28
**/
void
g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
int exit_status)
{
g_return_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline));
cmdline->priv->exit_status = exit_status;
}
/**
* g_application_command_line_get_exit_status:
* @cmdline: a #GApplicationCommandLine
*
* Gets the exit status of @cmdline. See
* g_application_command_line_set_exit_status() for more information.
*
* Returns: the exit status
*
* Since: 2.28
**/
int
g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline)
{
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), -1);
return cmdline->priv->exit_status;
}
/**
* g_application_command_line_get_platform_data:
* @cmdline: #GApplicationCommandLine
*
* Gets the platform data associated with the invocation of @cmdline.
*
* This is a #GVariant dictionary containing information about the
* context in which the invocation occurred. It typically contains
* information like the current working directory and the startup
* notification ID.
*
* For local invocation, it will be %NULL.
*
* Returns: (nullable): the platform data, or %NULL
*
* Since: 2.28
**/
GVariant *
g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline)
{
g_return_val_if_fail (G_IS_APPLICATION_COMMAND_LINE (cmdline), NULL);
if (cmdline->priv->platform_data)
return g_variant_ref (cmdline->priv->platform_data);
else
return NULL;
}
/**
* g_application_command_line_create_file_for_arg:
* @cmdline: a #GApplicationCommandLine
* @arg: (type filename): an argument from @cmdline
*
* Creates a #GFile corresponding to a filename that was given as part
* of the invocation of @cmdline.
*
* This differs from g_file_new_for_commandline_arg() in that it
* resolves relative pathnames using the current working directory of
* the invoking process rather than the local process.
*
* Returns: (transfer full): a new #GFile
*
* Since: 2.36
**/
GFile *
g_application_command_line_create_file_for_arg (GApplicationCommandLine *cmdline,
const gchar *arg)
{
g_return_val_if_fail (arg != NULL, NULL);
if (cmdline->priv->cwd)
return g_file_new_for_commandline_arg_and_cwd (arg, cmdline->priv->cwd);
g_warning ("Requested creation of GFile for commandline invocation that did not send cwd. "
"Using cwd of local process to resolve relative path names.");
return g_file_new_for_commandline_arg (arg);
}

View file

@ -0,0 +1,122 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2010 Codethink Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_APPLICATION_COMMAND_LINE_H__
#define __G_APPLICATION_COMMAND_LINE_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_APPLICATION_COMMAND_LINE (g_application_command_line_get_type ())
#define G_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLine))
#define G_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLineClass))
#define G_IS_APPLICATION_COMMAND_LINE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE))
#define G_IS_APPLICATION_COMMAND_LINE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
G_TYPE_APPLICATION_COMMAND_LINE))
#define G_APPLICATION_COMMAND_LINE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
G_TYPE_APPLICATION_COMMAND_LINE, \
GApplicationCommandLineClass))
typedef struct _GApplicationCommandLinePrivate GApplicationCommandLinePrivate;
typedef struct _GApplicationCommandLineClass GApplicationCommandLineClass;
struct _GApplicationCommandLine
{
/*< private >*/
GObject parent_instance;
GApplicationCommandLinePrivate *priv;
};
struct _GApplicationCommandLineClass
{
/*< private >*/
GObjectClass parent_class;
void (* print_literal) (GApplicationCommandLine *cmdline,
const gchar *message);
void (* printerr_literal) (GApplicationCommandLine *cmdline,
const gchar *message);
GInputStream * (* get_stdin) (GApplicationCommandLine *cmdline);
gpointer padding[11];
};
GLIB_AVAILABLE_IN_ALL
GType g_application_command_line_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gchar ** g_application_command_line_get_arguments (GApplicationCommandLine *cmdline,
int *argc);
GLIB_AVAILABLE_IN_2_40
GVariantDict * g_application_command_line_get_options_dict (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_2_36
GInputStream * g_application_command_line_get_stdin (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_ALL
const gchar * const * g_application_command_line_get_environ (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_ALL
const gchar * g_application_command_line_getenv (GApplicationCommandLine *cmdline,
const gchar *name);
GLIB_AVAILABLE_IN_ALL
const gchar * g_application_command_line_get_cwd (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_ALL
gboolean g_application_command_line_get_is_remote (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_ALL
void g_application_command_line_print (GApplicationCommandLine *cmdline,
const gchar *format,
...) G_GNUC_PRINTF(2, 3);
GLIB_AVAILABLE_IN_ALL
void g_application_command_line_printerr (GApplicationCommandLine *cmdline,
const gchar *format,
...) G_GNUC_PRINTF(2, 3);
GLIB_AVAILABLE_IN_ALL
int g_application_command_line_get_exit_status (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_ALL
void g_application_command_line_set_exit_status (GApplicationCommandLine *cmdline,
int exit_status);
GLIB_AVAILABLE_IN_ALL
GVariant * g_application_command_line_get_platform_data (GApplicationCommandLine *cmdline);
GLIB_AVAILABLE_IN_2_36
GFile * g_application_command_line_create_file_for_arg (GApplicationCommandLine *cmdline,
const gchar *arg);
G_END_DECLS
#endif /* __G_APPLICATION_COMMAND_LINE_H__ */

1003
gio/gapplicationimpl-dbus.c Normal file

File diff suppressed because it is too large Load diff

44
gio/gapplicationimpl.h Normal file
View file

@ -0,0 +1,44 @@
#include "giotypes.h"
typedef struct _GApplicationImpl GApplicationImpl;
typedef struct
{
gchar *name;
GVariantType *parameter_type;
gboolean enabled;
GVariant *state;
} RemoteActionInfo;
void g_application_impl_destroy (GApplicationImpl *impl);
GApplicationImpl * g_application_impl_register (GApplication *application,
const gchar *appid,
GApplicationFlags flags,
GActionGroup *exported_actions,
GRemoteActionGroup **remote_actions,
GCancellable *cancellable,
GError **error);
void g_application_impl_activate (GApplicationImpl *impl,
GVariant *platform_data);
void g_application_impl_open (GApplicationImpl *impl,
GFile **files,
gint n_files,
const gchar *hint,
GVariant *platform_data);
int g_application_impl_command_line (GApplicationImpl *impl,
const gchar *const *arguments,
GVariant *platform_data);
void g_application_impl_flush (GApplicationImpl *impl);
GDBusConnection * g_application_impl_get_dbus_connection (GApplicationImpl *impl);
const gchar * g_application_impl_get_dbus_object_path (GApplicationImpl *impl);
void g_application_impl_set_busy_state (GApplicationImpl *impl,
gboolean busy);

87
gio/gasynchelper.c Normal file
View file

@ -0,0 +1,87 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "gasynchelper.h"
/*< private >
* SECTION:gasynchelper
* @short_description: Asynchronous Helper Functions
* @include: gio/gio.h
* @see_also: #GAsyncResult
*
* Provides helper functions for asynchronous operations.
*
**/
#ifdef G_OS_WIN32
gboolean
_g_win32_overlap_wait_result (HANDLE hfile,
OVERLAPPED *overlap,
DWORD *transferred,
GCancellable *cancellable)
{
GPollFD pollfd[2];
gboolean result = FALSE;
gint num, npoll;
#if GLIB_SIZEOF_VOID_P == 8
pollfd[0].fd = (gint64)overlap->hEvent;
#else
pollfd[0].fd = (gint)overlap->hEvent;
#endif
pollfd[0].events = G_IO_IN;
num = 1;
if (g_cancellable_make_pollfd (cancellable, &pollfd[1]))
num++;
loop:
npoll = g_poll (pollfd, num, -1);
if (npoll <= 0)
/* error out, should never happen */
goto end;
if (g_cancellable_is_cancelled (cancellable))
{
/* CancelIO only cancels pending operations issued by the
* current thread and since we're doing only sync operations,
* this is safe.... */
/* CancelIoEx is only Vista+. Since we have only one overlap
* operation on this thread, we can just use: */
result = CancelIo (hfile);
g_warn_if_fail (result);
}
result = GetOverlappedResult (overlap->hEvent, overlap, transferred, FALSE);
if (result == FALSE &&
GetLastError () == ERROR_IO_INCOMPLETE &&
!g_cancellable_is_cancelled (cancellable))
goto loop;
end:
if (num > 1)
g_cancellable_release_fd (cancellable);
return result;
}
#endif

41
gio/gasynchelper.h Normal file
View file

@ -0,0 +1,41 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_ASYNC_HELPER_H__
#define __G_ASYNC_HELPER_H__
#include <gio/gio.h>
#ifdef G_OS_WIN32
#include <windows.h>
#endif
G_BEGIN_DECLS
#ifdef G_OS_WIN32
gboolean _g_win32_overlap_wait_result (HANDLE hfile,
OVERLAPPED *overlap,
DWORD *transferred,
GCancellable *cancellable);
#endif
G_END_DECLS
#endif /* __G_ASYNC_HELPER_H__ */

464
gio/gasyncinitable.c Normal file
View file

@ -0,0 +1,464 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "gasyncinitable.h"
#include "gasyncresult.h"
#include "gsimpleasyncresult.h"
#include "gtask.h"
#include "glibintl.h"
/**
* SECTION:gasyncinitable
* @short_description: Asynchronously failable object initialization interface
* @include: gio/gio.h
* @see_also: #GInitable
*
* This is the asynchronous version of #GInitable; it behaves the same
* in all ways except that initialization is asynchronous. For more details
* see the descriptions on #GInitable.
*
* A class may implement both the #GInitable and #GAsyncInitable interfaces.
*
* Users of objects implementing this are not intended to use the interface
* method directly; instead it will be used automatically in various ways.
* For C applications you generally just call g_async_initable_new_async()
* directly, or indirectly via a foo_thing_new_async() wrapper. This will call
* g_async_initable_init_async() under the cover, calling back with %NULL and
* a set %GError on failure.
*
* A typical implementation might look something like this:
*
* |[<!-- language="C" -->
* enum {
* NOT_INITIALIZED,
* INITIALIZING,
* INITIALIZED
* };
*
* static void
* _foo_ready_cb (Foo *self)
* {
* GList *l;
*
* self->priv->state = INITIALIZED;
*
* for (l = self->priv->init_results; l != NULL; l = l->next)
* {
* GTask *task = l->data;
*
* if (self->priv->success)
* g_task_return_boolean (task, TRUE);
* else
* g_task_return_new_error (task, ...);
* g_object_unref (task);
* }
*
* g_list_free (self->priv->init_results);
* self->priv->init_results = NULL;
* }
*
* static void
* foo_init_async (GAsyncInitable *initable,
* int io_priority,
* GCancellable *cancellable,
* GAsyncReadyCallback callback,
* gpointer user_data)
* {
* Foo *self = FOO (initable);
* GTask *task;
*
* task = g_task_new (initable, cancellable, callback, user_data);
* g_task_set_name (task, G_STRFUNC);
*
* switch (self->priv->state)
* {
* case NOT_INITIALIZED:
* _foo_get_ready (self);
* self->priv->init_results = g_list_append (self->priv->init_results,
* task);
* self->priv->state = INITIALIZING;
* break;
* case INITIALIZING:
* self->priv->init_results = g_list_append (self->priv->init_results,
* task);
* break;
* case INITIALIZED:
* if (!self->priv->success)
* g_task_return_new_error (task, ...);
* else
* g_task_return_boolean (task, TRUE);
* g_object_unref (task);
* break;
* }
* }
*
* static gboolean
* foo_init_finish (GAsyncInitable *initable,
* GAsyncResult *result,
* GError **error)
* {
* g_return_val_if_fail (g_task_is_valid (result, initable), FALSE);
*
* return g_task_propagate_boolean (G_TASK (result), error);
* }
*
* static void
* foo_async_initable_iface_init (gpointer g_iface,
* gpointer data)
* {
* GAsyncInitableIface *iface = g_iface;
*
* iface->init_async = foo_init_async;
* iface->init_finish = foo_init_finish;
* }
* ]|
*/
static void g_async_initable_real_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
static gboolean g_async_initable_real_init_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error);
typedef GAsyncInitableIface GAsyncInitableInterface;
G_DEFINE_INTERFACE (GAsyncInitable, g_async_initable, G_TYPE_OBJECT)
static void
g_async_initable_default_init (GAsyncInitableInterface *iface)
{
iface->init_async = g_async_initable_real_init_async;
iface->init_finish = g_async_initable_real_init_finish;
}
/**
* g_async_initable_init_async:
* @initable: a #GAsyncInitable.
* @io_priority: the [I/O priority][io-priority] of the operation
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: the data to pass to callback function
*
* Starts asynchronous initialization of the object implementing the
* interface. This must be done before any real use of the object after
* initial construction. If the object also implements #GInitable you can
* optionally call g_initable_init() instead.
*
* This method is intended for language bindings. If writing in C,
* g_async_initable_new_async() should typically be used instead.
*
* When the initialization is finished, @callback will be called. You can
* then call g_async_initable_init_finish() to get the result of the
* initialization.
*
* Implementations may also support cancellation. If @cancellable is not
* %NULL, then initialization can be cancelled by triggering the cancellable
* object from another thread. If the operation was cancelled, the error
* %G_IO_ERROR_CANCELLED will be returned. If @cancellable is not %NULL, and
* the object doesn't support cancellable initialization, the error
* %G_IO_ERROR_NOT_SUPPORTED will be returned.
*
* As with #GInitable, if the object is not initialized, or initialization
* returns with an error, then all operations on the object except
* g_object_ref() and g_object_unref() are considered to be invalid, and
* have undefined behaviour. They will often fail with g_critical() or
* g_warning(), but this must not be relied on.
*
* Callers should not assume that a class which implements #GAsyncInitable can
* be initialized multiple times; for more information, see g_initable_init().
* If a class explicitly supports being initialized multiple times,
* implementation requires yielding all subsequent calls to init_async() on the
* results of the first call.
*
* For classes that also support the #GInitable interface, the default
* implementation of this method will run the g_initable_init() function
* in a thread, so if you want to support asynchronous initialization via
* threads, just implement the #GAsyncInitable interface without overriding
* any interface methods.
*
* Since: 2.22
*/
void
g_async_initable_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GAsyncInitableIface *iface;
g_return_if_fail (G_IS_ASYNC_INITABLE (initable));
iface = G_ASYNC_INITABLE_GET_IFACE (initable);
(* iface->init_async) (initable, io_priority, cancellable, callback, user_data);
}
/**
* g_async_initable_init_finish:
* @initable: a #GAsyncInitable.
* @res: a #GAsyncResult.
* @error: a #GError location to store the error occurring, or %NULL to
* ignore.
*
* Finishes asynchronous initialization and returns the result.
* See g_async_initable_init_async().
*
* Returns: %TRUE if successful. If an error has occurred, this function
* will return %FALSE and set @error appropriately if present.
*
* Since: 2.22
*/
gboolean
g_async_initable_init_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error)
{
GAsyncInitableIface *iface;
g_return_val_if_fail (G_IS_ASYNC_INITABLE (initable), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
if (g_async_result_legacy_propagate_error (res, error))
return FALSE;
iface = G_ASYNC_INITABLE_GET_IFACE (initable);
return (* iface->init_finish) (initable, res, error);
}
static void
async_init_thread (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
GError *error = NULL;
if (g_initable_init (G_INITABLE (source_object), cancellable, &error))
g_task_return_boolean (task, TRUE);
else
g_task_return_error (task, error);
}
static void
g_async_initable_real_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (G_IS_INITABLE (initable));
task = g_task_new (initable, cancellable, callback, user_data);
g_task_set_source_tag (task, g_async_initable_real_init_async);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, async_init_thread);
g_object_unref (task);
}
static gboolean
g_async_initable_real_init_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error)
{
/* For backward compatibility we have to process GSimpleAsyncResults
* even though g_async_initable_real_init_async doesn't generate
* them any more.
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
if (G_IS_SIMPLE_ASYNC_RESULT (res))
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
if (g_simple_async_result_propagate_error (simple, error))
return FALSE;
else
return TRUE;
}
G_GNUC_END_IGNORE_DEPRECATIONS
g_return_val_if_fail (g_task_is_valid (res, initable), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
/**
* g_async_initable_new_async:
* @object_type: a #GType supporting #GAsyncInitable.
* @io_priority: the [I/O priority][io-priority] of the operation
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @callback: a #GAsyncReadyCallback to call when the initialization is
* finished
* @user_data: the data to pass to callback function
* @first_property_name: (nullable): the name of the first property, or %NULL if no
* properties
* @...: the value of the first property, followed by other property
* value pairs, and ended by %NULL.
*
* Helper function for constructing #GAsyncInitable object. This is
* similar to g_object_new() but also initializes the object asynchronously.
*
* When the initialization is finished, @callback will be called. You can
* then call g_async_initable_new_finish() to get the new object and check
* for any errors.
*
* Since: 2.22
*/
void
g_async_initable_new_async (GType object_type,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
const gchar *first_property_name,
...)
{
va_list var_args;
va_start (var_args, first_property_name);
g_async_initable_new_valist_async (object_type,
first_property_name, var_args,
io_priority, cancellable,
callback, user_data);
va_end (var_args);
}
/**
* g_async_initable_newv_async:
* @object_type: a #GType supporting #GAsyncInitable.
* @n_parameters: the number of parameters in @parameters
* @parameters: the parameters to use to construct the object
* @io_priority: the [I/O priority][io-priority] of the operation
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @callback: a #GAsyncReadyCallback to call when the initialization is
* finished
* @user_data: the data to pass to callback function
*
* Helper function for constructing #GAsyncInitable object. This is
* similar to g_object_newv() but also initializes the object asynchronously.
*
* When the initialization is finished, @callback will be called. You can
* then call g_async_initable_new_finish() to get the new object and check
* for any errors.
*
* Since: 2.22
* Deprecated: 2.54: Use g_object_new_with_properties() and
* g_async_initable_init_async() instead. See #GParameter for more information.
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
void
g_async_initable_newv_async (GType object_type,
guint n_parameters,
GParameter *parameters,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GObject *obj;
g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type));
obj = g_object_newv (object_type, n_parameters, parameters);
g_async_initable_init_async (G_ASYNC_INITABLE (obj),
io_priority, cancellable,
callback, user_data);
g_object_unref (obj); /* Passed ownership to async call */
}
G_GNUC_END_IGNORE_DEPRECATIONS
/**
* g_async_initable_new_valist_async:
* @object_type: a #GType supporting #GAsyncInitable.
* @first_property_name: the name of the first property, followed by
* the value, and other property value pairs, and ended by %NULL.
* @var_args: The var args list generated from @first_property_name.
* @io_priority: the [I/O priority][io-priority] of the operation
* @cancellable: optional #GCancellable object, %NULL to ignore.
* @callback: a #GAsyncReadyCallback to call when the initialization is
* finished
* @user_data: the data to pass to callback function
*
* Helper function for constructing #GAsyncInitable object. This is
* similar to g_object_new_valist() but also initializes the object
* asynchronously.
*
* When the initialization is finished, @callback will be called. You can
* then call g_async_initable_new_finish() to get the new object and check
* for any errors.
*
* Since: 2.22
*/
void
g_async_initable_new_valist_async (GType object_type,
const gchar *first_property_name,
va_list var_args,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GObject *obj;
g_return_if_fail (G_TYPE_IS_ASYNC_INITABLE (object_type));
obj = g_object_new_valist (object_type,
first_property_name,
var_args);
g_async_initable_init_async (G_ASYNC_INITABLE (obj),
io_priority, cancellable,
callback, user_data);
g_object_unref (obj); /* Passed ownership to async call */
}
/**
* g_async_initable_new_finish:
* @initable: the #GAsyncInitable from the callback
* @res: the #GAsyncResult from the callback
* @error: return location for errors, or %NULL to ignore
*
* Finishes the async construction for the various g_async_initable_new
* calls, returning the created object or %NULL on error.
*
* Returns: (type GObject.Object) (transfer full): a newly created #GObject,
* or %NULL on error. Free with g_object_unref().
*
* Since: 2.22
*/
GObject *
g_async_initable_new_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error)
{
if (g_async_initable_init_finish (initable, res, error))
return g_object_ref (G_OBJECT (initable));
else
return NULL;
}

130
gio/gasyncinitable.h Normal file
View file

@ -0,0 +1,130 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_ASYNC_INITABLE_H__
#define __G_ASYNC_INITABLE_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
#include <gio/ginitable.h>
G_BEGIN_DECLS
#define G_TYPE_ASYNC_INITABLE (g_async_initable_get_type ())
#define G_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitable))
#define G_IS_ASYNC_INITABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_INITABLE))
#define G_ASYNC_INITABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_INITABLE, GAsyncInitableIface))
#define G_TYPE_IS_ASYNC_INITABLE(type) (g_type_is_a ((type), G_TYPE_ASYNC_INITABLE))
/**
* GAsyncInitable:
*
* Interface for asynchronously initializable objects.
*
* Since: 2.22
**/
typedef struct _GAsyncInitableIface GAsyncInitableIface;
/**
* GAsyncInitableIface:
* @g_iface: The parent interface.
* @init_async: Starts initialization of the object.
* @init_finish: Finishes initialization of the object.
*
* Provides an interface for asynchronous initializing object such that
* initialization may fail.
*
* Since: 2.22
**/
struct _GAsyncInitableIface
{
GTypeInterface g_iface;
/* Virtual Table */
void (* init_async) (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* init_finish) (GAsyncInitable *initable,
GAsyncResult *res,
GError **error);
};
GLIB_AVAILABLE_IN_ALL
GType g_async_initable_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
void g_async_initable_init_async (GAsyncInitable *initable,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
gboolean g_async_initable_init_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_async_initable_new_async (GType object_type,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data,
const gchar *first_property_name,
...);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
GLIB_DEPRECATED_IN_2_54_FOR(g_object_new_with_properties and g_async_initable_init_async)
void g_async_initable_newv_async (GType object_type,
guint n_parameters,
GParameter *parameters,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
G_GNUC_END_IGNORE_DEPRECATIONS
GLIB_AVAILABLE_IN_ALL
void g_async_initable_new_valist_async (GType object_type,
const gchar *first_property_name,
va_list var_args,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GObject *g_async_initable_new_finish (GAsyncInitable *initable,
GAsyncResult *res,
GError **error);
G_END_DECLS
#endif /* __G_ASYNC_INITABLE_H__ */

237
gio/gasyncresult.c Normal file
View file

@ -0,0 +1,237 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "gasyncresult.h"
#include "gsimpleasyncresult.h"
#include "glibintl.h"
/**
* SECTION:gasyncresult
* @short_description: Asynchronous Function Results
* @include: gio/gio.h
* @see_also: #GTask
*
* Provides a base class for implementing asynchronous function results.
*
* Asynchronous operations are broken up into two separate operations
* which are chained together by a #GAsyncReadyCallback. To begin
* an asynchronous operation, provide a #GAsyncReadyCallback to the
* asynchronous function. This callback will be triggered when the
* operation has completed, and must be run in a later iteration of
* the [thread-default main context][g-main-context-push-thread-default]
* from where the operation was initiated. It will be passed a
* #GAsyncResult instance filled with the details of the operation's
* success or failure, the object the asynchronous function was
* started for and any error codes returned. The asynchronous callback
* function is then expected to call the corresponding "_finish()"
* function, passing the object the function was called for, the
* #GAsyncResult instance, and (optionally) an @error to grab any
* error conditions that may have occurred.
*
* The "_finish()" function for an operation takes the generic result
* (of type #GAsyncResult) and returns the specific result that the
* operation in question yields (e.g. a #GFileEnumerator for a
* "enumerate children" operation). If the result or error status of the
* operation is not needed, there is no need to call the "_finish()"
* function; GIO will take care of cleaning up the result and error
* information after the #GAsyncReadyCallback returns. You can pass
* %NULL for the #GAsyncReadyCallback if you don't need to take any
* action at all after the operation completes. Applications may also
* take a reference to the #GAsyncResult and call "_finish()" later;
* however, the "_finish()" function may be called at most once.
*
* Example of a typical asynchronous operation flow:
* |[<!-- language="C" -->
* void _theoretical_frobnitz_async (Theoretical *t,
* GCancellable *c,
* GAsyncReadyCallback cb,
* gpointer u);
*
* gboolean _theoretical_frobnitz_finish (Theoretical *t,
* GAsyncResult *res,
* GError **e);
*
* static void
* frobnitz_result_func (GObject *source_object,
* GAsyncResult *res,
* gpointer user_data)
* {
* gboolean success = FALSE;
*
* success = _theoretical_frobnitz_finish (source_object, res, NULL);
*
* if (success)
* g_printf ("Hurray!\n");
* else
* g_printf ("Uh oh!\n");
*
* ...
*
* }
*
* int main (int argc, void *argv[])
* {
* ...
*
* _theoretical_frobnitz_async (theoretical_data,
* NULL,
* frobnitz_result_func,
* NULL);
*
* ...
* }
* ]|
*
* The callback for an asynchronous operation is called only once, and is
* always called, even in the case of a cancelled operation. On cancellation
* the result is a %G_IO_ERROR_CANCELLED error.
*
* ## I/O Priority # {#io-priority}
*
* Many I/O-related asynchronous operations have a priority parameter,
* which is used in certain cases to determine the order in which
* operations are executed. They are not used to determine system-wide
* I/O scheduling. Priorities are integers, with lower numbers indicating
* higher priority. It is recommended to choose priorities between
* %G_PRIORITY_LOW and %G_PRIORITY_HIGH, with %G_PRIORITY_DEFAULT
* as a default.
*/
typedef GAsyncResultIface GAsyncResultInterface;
G_DEFINE_INTERFACE (GAsyncResult, g_async_result, G_TYPE_OBJECT)
static void
g_async_result_default_init (GAsyncResultInterface *iface)
{
}
/**
* g_async_result_get_user_data:
* @res: a #GAsyncResult.
*
* Gets the user data from a #GAsyncResult.
*
* Returns: (transfer full): the user data for @res.
**/
gpointer
g_async_result_get_user_data (GAsyncResult *res)
{
GAsyncResultIface *iface;
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
iface = G_ASYNC_RESULT_GET_IFACE (res);
return (* iface->get_user_data) (res);
}
/**
* g_async_result_get_source_object:
* @res: a #GAsyncResult
*
* Gets the source object from a #GAsyncResult.
*
* Returns: (transfer full) (nullable): a new reference to the source
* object for the @res, or %NULL if there is none.
*/
GObject *
g_async_result_get_source_object (GAsyncResult *res)
{
GAsyncResultIface *iface;
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
iface = G_ASYNC_RESULT_GET_IFACE (res);
return (* iface->get_source_object) (res);
}
/**
* g_async_result_legacy_propagate_error:
* @res: a #GAsyncResult
* @error: (out): a location to propagate the error to.
*
* If @res is a #GSimpleAsyncResult, this is equivalent to
* g_simple_async_result_propagate_error(). Otherwise it returns
* %FALSE.
*
* This can be used for legacy error handling in async *_finish()
* wrapper functions that traditionally handled #GSimpleAsyncResult
* error returns themselves rather than calling into the virtual method.
* This should not be used in new code; #GAsyncResult errors that are
* set by virtual methods should also be extracted by virtual methods,
* to enable subclasses to chain up correctly.
*
* Returns: %TRUE if @error is has been filled in with an error from
* @res, %FALSE if not.
*
* Since: 2.34
**/
gboolean
g_async_result_legacy_propagate_error (GAsyncResult *res,
GError **error)
{
/* This doesn't use a vmethod, because it's only for code that used
* to use GSimpleAsyncResult. (But it's a GAsyncResult method so
* that callers don't need to worry about GSimpleAsyncResult
* deprecation warnings in the future.)
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
if (G_IS_SIMPLE_ASYNC_RESULT (res))
{
return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
error);
}
else
return FALSE;
G_GNUC_END_IGNORE_DEPRECATIONS
}
/**
* g_async_result_is_tagged:
* @res: a #GAsyncResult
* @source_tag: an application-defined tag
*
* Checks if @res has the given @source_tag (generally a function
* pointer indicating the function @res was created by).
*
* Returns: %TRUE if @res has the indicated @source_tag, %FALSE if
* not.
*
* Since: 2.34
**/
gboolean
g_async_result_is_tagged (GAsyncResult *res,
gpointer source_tag)
{
GAsyncResultIface *iface;
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
iface = G_ASYNC_RESULT_GET_IFACE (res);
if (!iface->is_tagged)
return FALSE;
return (* iface->is_tagged) (res, source_tag);
}

85
gio/gasyncresult.h Normal file
View file

@ -0,0 +1,85 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_ASYNC_RESULT_H__
#define __G_ASYNC_RESULT_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_ASYNC_RESULT (g_async_result_get_type ())
#define G_ASYNC_RESULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_RESULT, GAsyncResult))
#define G_IS_ASYNC_RESULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_RESULT))
#define G_ASYNC_RESULT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_RESULT, GAsyncResultIface))
/**
* GAsyncResult:
*
* Holds results information for an asynchronous operation,
* usually passed directly to an asynchronous _finish() operation.
**/
typedef struct _GAsyncResultIface GAsyncResultIface;
/**
* GAsyncResultIface:
* @g_iface: The parent interface.
* @get_user_data: Gets the user data passed to the callback.
* @get_source_object: Gets the source object that issued the asynchronous operation.
* @is_tagged: Checks if a result is tagged with a particular source.
*
* Interface definition for #GAsyncResult.
**/
struct _GAsyncResultIface
{
GTypeInterface g_iface;
/* Virtual Table */
gpointer (* get_user_data) (GAsyncResult *res);
GObject * (* get_source_object) (GAsyncResult *res);
gboolean (* is_tagged) (GAsyncResult *res,
gpointer source_tag);
};
GLIB_AVAILABLE_IN_ALL
GType g_async_result_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
gpointer g_async_result_get_user_data (GAsyncResult *res);
GLIB_AVAILABLE_IN_ALL
GObject *g_async_result_get_source_object (GAsyncResult *res);
GLIB_AVAILABLE_IN_2_34
gboolean g_async_result_legacy_propagate_error (GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_2_34
gboolean g_async_result_is_tagged (GAsyncResult *res,
gpointer source_tag);
G_END_DECLS
#endif /* __G_ASYNC_RESULT_H__ */

1275
gio/gbufferedinputstream.c Normal file

File diff suppressed because it is too large Load diff

133
gio/gbufferedinputstream.h Normal file
View file

@ -0,0 +1,133 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Christian Kellner <gicmo@gnome.org>
*/
#ifndef __G_BUFFERED_INPUT_STREAM_H__
#define __G_BUFFERED_INPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gfilterinputstream.h>
G_BEGIN_DECLS
#define G_TYPE_BUFFERED_INPUT_STREAM (g_buffered_input_stream_get_type ())
#define G_BUFFERED_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStream))
#define G_BUFFERED_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
#define G_IS_BUFFERED_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_INPUT_STREAM))
#define G_IS_BUFFERED_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_INPUT_STREAM))
#define G_BUFFERED_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
/**
* GBufferedInputStream:
*
* Implements #GFilterInputStream with a sized input buffer.
**/
typedef struct _GBufferedInputStreamClass GBufferedInputStreamClass;
typedef struct _GBufferedInputStreamPrivate GBufferedInputStreamPrivate;
struct _GBufferedInputStream
{
GFilterInputStream parent_instance;
/*< private >*/
GBufferedInputStreamPrivate *priv;
};
struct _GBufferedInputStreamClass
{
GFilterInputStreamClass parent_class;
gssize (* fill) (GBufferedInputStream *stream,
gssize count,
GCancellable *cancellable,
GError **error);
/* Async ops: (optional in derived classes) */
void (* fill_async) (GBufferedInputStream *stream,
gssize count,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gssize (* fill_finish) (GBufferedInputStream *stream,
GAsyncResult *result,
GError **error);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_buffered_input_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GInputStream* g_buffered_input_stream_new (GInputStream *base_stream);
GLIB_AVAILABLE_IN_ALL
GInputStream* g_buffered_input_stream_new_sized (GInputStream *base_stream,
gsize size);
GLIB_AVAILABLE_IN_ALL
gsize g_buffered_input_stream_get_buffer_size (GBufferedInputStream *stream);
GLIB_AVAILABLE_IN_ALL
void g_buffered_input_stream_set_buffer_size (GBufferedInputStream *stream,
gsize size);
GLIB_AVAILABLE_IN_ALL
gsize g_buffered_input_stream_get_available (GBufferedInputStream *stream);
GLIB_AVAILABLE_IN_ALL
gsize g_buffered_input_stream_peek (GBufferedInputStream *stream,
void *buffer,
gsize offset,
gsize count);
GLIB_AVAILABLE_IN_ALL
const void* g_buffered_input_stream_peek_buffer (GBufferedInputStream *stream,
gsize *count);
GLIB_AVAILABLE_IN_ALL
gssize g_buffered_input_stream_fill (GBufferedInputStream *stream,
gssize count,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_buffered_input_stream_fill_async (GBufferedInputStream *stream,
gssize count,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
gssize g_buffered_input_stream_fill_finish (GBufferedInputStream *stream,
GAsyncResult *result,
GError **error);
GLIB_AVAILABLE_IN_ALL
int g_buffered_input_stream_read_byte (GBufferedInputStream *stream,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_BUFFERED_INPUT_STREAM_H__ */

755
gio/gbufferedoutputstream.c Normal file
View file

@ -0,0 +1,755 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Christian Kellner <gicmo@gnome.org>
*/
#include "config.h"
#include "gbufferedoutputstream.h"
#include "goutputstream.h"
#include "gseekable.h"
#include "gtask.h"
#include "string.h"
#include "gioerror.h"
#include "glibintl.h"
/**
* SECTION:gbufferedoutputstream
* @short_description: Buffered Output Stream
* @include: gio/gio.h
* @see_also: #GFilterOutputStream, #GOutputStream
*
* Buffered output stream implements #GFilterOutputStream and provides
* for buffered writes.
*
* By default, #GBufferedOutputStream's buffer size is set at 4 kilobytes.
*
* To create a buffered output stream, use g_buffered_output_stream_new(),
* or g_buffered_output_stream_new_sized() to specify the buffer's size
* at construction.
*
* To get the size of a buffer within a buffered input stream, use
* g_buffered_output_stream_get_buffer_size(). To change the size of a
* buffered output stream's buffer, use
* g_buffered_output_stream_set_buffer_size(). Note that the buffer's
* size cannot be reduced below the size of the data within the buffer.
**/
#define DEFAULT_BUFFER_SIZE 4096
struct _GBufferedOutputStreamPrivate {
guint8 *buffer;
gsize len;
goffset pos;
gboolean auto_grow;
};
enum {
PROP_0,
PROP_BUFSIZE,
PROP_AUTO_GROW
};
static void g_buffered_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void g_buffered_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void g_buffered_output_stream_finalize (GObject *object);
static gssize g_buffered_output_stream_write (GOutputStream *stream,
const void *buffer,
gsize count,
GCancellable *cancellable,
GError **error);
static gboolean g_buffered_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable,
GError **error);
static gboolean g_buffered_output_stream_close (GOutputStream *stream,
GCancellable *cancellable,
GError **error);
static void g_buffered_output_stream_flush_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data);
static gboolean g_buffered_output_stream_flush_finish (GOutputStream *stream,
GAsyncResult *result,
GError **error);
static void g_buffered_output_stream_close_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data);
static gboolean g_buffered_output_stream_close_finish (GOutputStream *stream,
GAsyncResult *result,
GError **error);
static void g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface);
static goffset g_buffered_output_stream_tell (GSeekable *seekable);
static gboolean g_buffered_output_stream_can_seek (GSeekable *seekable);
static gboolean g_buffered_output_stream_seek (GSeekable *seekable,
goffset offset,
GSeekType type,
GCancellable *cancellable,
GError **error);
static gboolean g_buffered_output_stream_can_truncate (GSeekable *seekable);
static gboolean g_buffered_output_stream_truncate (GSeekable *seekable,
goffset offset,
GCancellable *cancellable,
GError **error);
G_DEFINE_TYPE_WITH_CODE (GBufferedOutputStream,
g_buffered_output_stream,
G_TYPE_FILTER_OUTPUT_STREAM,
G_ADD_PRIVATE (GBufferedOutputStream)
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
g_buffered_output_stream_seekable_iface_init))
static void
g_buffered_output_stream_class_init (GBufferedOutputStreamClass *klass)
{
GObjectClass *object_class;
GOutputStreamClass *ostream_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = g_buffered_output_stream_get_property;
object_class->set_property = g_buffered_output_stream_set_property;
object_class->finalize = g_buffered_output_stream_finalize;
ostream_class = G_OUTPUT_STREAM_CLASS (klass);
ostream_class->write_fn = g_buffered_output_stream_write;
ostream_class->flush = g_buffered_output_stream_flush;
ostream_class->close_fn = g_buffered_output_stream_close;
ostream_class->flush_async = g_buffered_output_stream_flush_async;
ostream_class->flush_finish = g_buffered_output_stream_flush_finish;
ostream_class->close_async = g_buffered_output_stream_close_async;
ostream_class->close_finish = g_buffered_output_stream_close_finish;
g_object_class_install_property (object_class,
PROP_BUFSIZE,
g_param_spec_uint ("buffer-size",
P_("Buffer Size"),
P_("The size of the backend buffer"),
1,
G_MAXUINT,
DEFAULT_BUFFER_SIZE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT|
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
g_object_class_install_property (object_class,
PROP_AUTO_GROW,
g_param_spec_boolean ("auto-grow",
P_("Auto-grow"),
P_("Whether the buffer should automatically grow"),
FALSE,
G_PARAM_READWRITE|
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
}
/**
* g_buffered_output_stream_get_buffer_size:
* @stream: a #GBufferedOutputStream.
*
* Gets the size of the buffer in the @stream.
*
* Returns: the current size of the buffer.
**/
gsize
g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream)
{
g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), -1);
return stream->priv->len;
}
/**
* g_buffered_output_stream_set_buffer_size:
* @stream: a #GBufferedOutputStream.
* @size: a #gsize.
*
* Sets the size of the internal buffer to @size.
**/
void
g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
gsize size)
{
GBufferedOutputStreamPrivate *priv;
guint8 *buffer;
g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
priv = stream->priv;
if (size == priv->len)
return;
if (priv->buffer)
{
size = (priv->pos > 0) ? MAX (size, (gsize) priv->pos) : size;
buffer = g_malloc (size);
memcpy (buffer, priv->buffer, priv->pos);
g_free (priv->buffer);
priv->buffer = buffer;
priv->len = size;
/* Keep old pos */
}
else
{
priv->buffer = g_malloc (size);
priv->len = size;
priv->pos = 0;
}
g_object_notify (G_OBJECT (stream), "buffer-size");
}
/**
* g_buffered_output_stream_get_auto_grow:
* @stream: a #GBufferedOutputStream.
*
* Checks if the buffer automatically grows as data is added.
*
* Returns: %TRUE if the @stream's buffer automatically grows,
* %FALSE otherwise.
**/
gboolean
g_buffered_output_stream_get_auto_grow (GBufferedOutputStream *stream)
{
g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), FALSE);
return stream->priv->auto_grow;
}
/**
* g_buffered_output_stream_set_auto_grow:
* @stream: a #GBufferedOutputStream.
* @auto_grow: a #gboolean.
*
* Sets whether or not the @stream's buffer should automatically grow.
* If @auto_grow is true, then each write will just make the buffer
* larger, and you must manually flush the buffer to actually write out
* the data to the underlying stream.
**/
void
g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
gboolean auto_grow)
{
GBufferedOutputStreamPrivate *priv;
g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
priv = stream->priv;
auto_grow = auto_grow != FALSE;
if (priv->auto_grow != auto_grow)
{
priv->auto_grow = auto_grow;
g_object_notify (G_OBJECT (stream), "auto-grow");
}
}
static void
g_buffered_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GBufferedOutputStream *stream;
stream = G_BUFFERED_OUTPUT_STREAM (object);
switch (prop_id)
{
case PROP_BUFSIZE:
g_buffered_output_stream_set_buffer_size (stream, g_value_get_uint (value));
break;
case PROP_AUTO_GROW:
g_buffered_output_stream_set_auto_grow (stream, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_buffered_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GBufferedOutputStream *buffered_stream;
GBufferedOutputStreamPrivate *priv;
buffered_stream = G_BUFFERED_OUTPUT_STREAM (object);
priv = buffered_stream->priv;
switch (prop_id)
{
case PROP_BUFSIZE:
g_value_set_uint (value, priv->len);
break;
case PROP_AUTO_GROW:
g_value_set_boolean (value, priv->auto_grow);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_buffered_output_stream_finalize (GObject *object)
{
GBufferedOutputStream *stream;
GBufferedOutputStreamPrivate *priv;
stream = G_BUFFERED_OUTPUT_STREAM (object);
priv = stream->priv;
g_free (priv->buffer);
G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize (object);
}
static void
g_buffered_output_stream_init (GBufferedOutputStream *stream)
{
stream->priv = g_buffered_output_stream_get_instance_private (stream);
}
static void
g_buffered_output_stream_seekable_iface_init (GSeekableIface *iface)
{
iface->tell = g_buffered_output_stream_tell;
iface->can_seek = g_buffered_output_stream_can_seek;
iface->seek = g_buffered_output_stream_seek;
iface->can_truncate = g_buffered_output_stream_can_truncate;
iface->truncate_fn = g_buffered_output_stream_truncate;
}
/**
* g_buffered_output_stream_new:
* @base_stream: a #GOutputStream.
*
* Creates a new buffered output stream for a base stream.
*
* Returns: a #GOutputStream for the given @base_stream.
**/
GOutputStream *
g_buffered_output_stream_new (GOutputStream *base_stream)
{
GOutputStream *stream;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
"base-stream", base_stream,
NULL);
return stream;
}
/**
* g_buffered_output_stream_new_sized:
* @base_stream: a #GOutputStream.
* @size: a #gsize.
*
* Creates a new buffered output stream with a given buffer size.
*
* Returns: a #GOutputStream with an internal buffer set to @size.
**/
GOutputStream *
g_buffered_output_stream_new_sized (GOutputStream *base_stream,
gsize size)
{
GOutputStream *stream;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
"base-stream", base_stream,
"buffer-size", size,
NULL);
return stream;
}
static gboolean
flush_buffer (GBufferedOutputStream *stream,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStreamPrivate *priv;
GOutputStream *base_stream;
gboolean res;
gsize bytes_written;
gsize count;
priv = stream->priv;
bytes_written = 0;
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), FALSE);
res = g_output_stream_write_all (base_stream,
priv->buffer,
priv->pos,
&bytes_written,
cancellable,
error);
count = priv->pos - bytes_written;
if (count > 0)
memmove (priv->buffer, priv->buffer + bytes_written, count);
priv->pos -= bytes_written;
return res;
}
static gssize
g_buffered_output_stream_write (GOutputStream *stream,
const void *buffer,
gsize count,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStream *bstream;
GBufferedOutputStreamPrivate *priv;
gboolean res;
gsize n;
gsize new_size;
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
priv = bstream->priv;
n = priv->len - priv->pos;
if (priv->auto_grow && n < count)
{
new_size = MAX (priv->len * 2, priv->len + count);
g_buffered_output_stream_set_buffer_size (bstream, new_size);
}
else if (n == 0)
{
res = flush_buffer (bstream, cancellable, error);
if (res == FALSE)
return -1;
}
n = priv->len - priv->pos;
count = MIN (count, n);
memcpy (priv->buffer + priv->pos, buffer, count);
priv->pos += count;
return count;
}
static gboolean
g_buffered_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStream *bstream;
GOutputStream *base_stream;
gboolean res;
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
res = flush_buffer (bstream, cancellable, error);
if (res == FALSE)
return FALSE;
res = g_output_stream_flush (base_stream, cancellable, error);
return res;
}
static gboolean
g_buffered_output_stream_close (GOutputStream *stream,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStream *bstream;
GOutputStream *base_stream;
gboolean res;
bstream = G_BUFFERED_OUTPUT_STREAM (stream);
base_stream = G_FILTER_OUTPUT_STREAM (bstream)->base_stream;
res = flush_buffer (bstream, cancellable, error);
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
/* report the first error but still close the stream */
if (res)
res = g_output_stream_close (base_stream, cancellable, error);
else
g_output_stream_close (base_stream, cancellable, NULL);
}
return res;
}
static goffset
g_buffered_output_stream_tell (GSeekable *seekable)
{
GBufferedOutputStream *bstream;
GBufferedOutputStreamPrivate *priv;
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
goffset base_offset;
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
priv = bstream->priv;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
return 0;
base_stream_seekable = G_SEEKABLE (base_stream);
base_offset = g_seekable_tell (base_stream_seekable);
return base_offset + priv->pos;
}
static gboolean
g_buffered_output_stream_can_seek (GSeekable *seekable)
{
GOutputStream *base_stream;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
}
static gboolean
g_buffered_output_stream_seek (GSeekable *seekable,
goffset offset,
GSeekType type,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStream *bstream;
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
gboolean flushed;
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Seek not supported on base stream"));
return FALSE;
}
base_stream_seekable = G_SEEKABLE (base_stream);
flushed = flush_buffer (bstream, cancellable, error);
if (!flushed)
return FALSE;
return g_seekable_seek (base_stream_seekable, offset, type, cancellable, error);
}
static gboolean
g_buffered_output_stream_can_truncate (GSeekable *seekable)
{
GOutputStream *base_stream;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
return G_IS_SEEKABLE (base_stream) && g_seekable_can_truncate (G_SEEKABLE (base_stream));
}
static gboolean
g_buffered_output_stream_truncate (GSeekable *seekable,
goffset offset,
GCancellable *cancellable,
GError **error)
{
GBufferedOutputStream *bstream;
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
gboolean flushed;
bstream = G_BUFFERED_OUTPUT_STREAM (seekable);
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Truncate not supported on base stream"));
return FALSE;
}
base_stream_seekable = G_SEEKABLE (base_stream);
flushed = flush_buffer (bstream, cancellable, error);
if (!flushed)
return FALSE;
return g_seekable_truncate (base_stream_seekable, offset, cancellable, error);
}
/* ************************** */
/* Async stuff implementation */
/* ************************** */
/* TODO: This should be using the base class async ops, not threads */
typedef struct {
guint flush_stream : 1;
guint close_stream : 1;
} FlushData;
static void
free_flush_data (gpointer data)
{
g_slice_free (FlushData, data);
}
/* This function is used by all three (i.e.
* _write, _flush, _close) functions since
* all of them will need to flush the buffer
* and so closing and writing is just a special
* case of flushing + some addition stuff */
static void
flush_buffer_thread (GTask *task,
gpointer object,
gpointer task_data,
GCancellable *cancellable)
{
GBufferedOutputStream *stream;
GOutputStream *base_stream;
FlushData *fdata;
gboolean res;
GError *error = NULL;
stream = G_BUFFERED_OUTPUT_STREAM (object);
fdata = task_data;
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
res = flush_buffer (stream, cancellable, &error);
/* if flushing the buffer didn't work don't even bother
* to flush the stream but just report that error */
if (res && fdata->flush_stream)
res = g_output_stream_flush (base_stream, cancellable, &error);
if (fdata->close_stream)
{
/* if flushing the buffer or the stream returned
* an error report that first error but still try
* close the stream */
if (g_filter_output_stream_get_close_base_stream (G_FILTER_OUTPUT_STREAM (stream)))
{
if (res == FALSE)
g_output_stream_close (base_stream, cancellable, NULL);
else
res = g_output_stream_close (base_stream, cancellable, &error);
}
}
if (res == FALSE)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
}
static void
g_buffered_output_stream_flush_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data)
{
GTask *task;
FlushData *fdata;
fdata = g_slice_new0 (FlushData);
fdata->flush_stream = TRUE;
fdata->close_stream = FALSE;
task = g_task_new (stream, cancellable, callback, data);
g_task_set_source_tag (task, g_buffered_output_stream_flush_async);
g_task_set_task_data (task, fdata, free_flush_data);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, flush_buffer_thread);
g_object_unref (task);
}
static gboolean
g_buffered_output_stream_flush_finish (GOutputStream *stream,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
g_buffered_output_stream_close_async (GOutputStream *stream,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer data)
{
GTask *task;
FlushData *fdata;
fdata = g_slice_new0 (FlushData);
fdata->close_stream = TRUE;
task = g_task_new (stream, cancellable, callback, data);
g_task_set_source_tag (task, g_buffered_output_stream_close_async);
g_task_set_task_data (task, fdata, free_flush_data);
g_task_set_priority (task, io_priority);
g_task_run_in_thread (task, flush_buffer_thread);
g_object_unref (task);
}
static gboolean
g_buffered_output_stream_close_finish (GOutputStream *stream,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}

View file

@ -0,0 +1,86 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Christian Kellner <gicmo@gnome.org>
*/
#ifndef __G_BUFFERED_OUTPUT_STREAM_H__
#define __G_BUFFERED_OUTPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gfilteroutputstream.h>
G_BEGIN_DECLS
#define G_TYPE_BUFFERED_OUTPUT_STREAM (g_buffered_output_stream_get_type ())
#define G_BUFFERED_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStream))
#define G_BUFFERED_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
#define G_IS_BUFFERED_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_OUTPUT_STREAM))
#define G_IS_BUFFERED_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_OUTPUT_STREAM))
#define G_BUFFERED_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
/**
* GBufferedOutputStream:
*
* An implementation of #GFilterOutputStream with a sized buffer.
**/
typedef struct _GBufferedOutputStreamClass GBufferedOutputStreamClass;
typedef struct _GBufferedOutputStreamPrivate GBufferedOutputStreamPrivate;
struct _GBufferedOutputStream
{
GFilterOutputStream parent_instance;
/*< protected >*/
GBufferedOutputStreamPrivate *priv;
};
struct _GBufferedOutputStreamClass
{
GFilterOutputStreamClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_buffered_output_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GOutputStream* g_buffered_output_stream_new (GOutputStream *base_stream);
GLIB_AVAILABLE_IN_ALL
GOutputStream* g_buffered_output_stream_new_sized (GOutputStream *base_stream,
gsize size);
GLIB_AVAILABLE_IN_ALL
gsize g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream);
GLIB_AVAILABLE_IN_ALL
void g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
gsize size);
GLIB_AVAILABLE_IN_ALL
gboolean g_buffered_output_stream_get_auto_grow (GBufferedOutputStream *stream);
GLIB_AVAILABLE_IN_ALL
void g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
gboolean auto_grow);
G_END_DECLS
#endif /* __G_BUFFERED_OUTPUT_STREAM_H__ */

269
gio/gbytesicon.c Normal file
View file

@ -0,0 +1,269 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2013 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gbytesicon.h"
#include "gbytes.h"
#include "gicon.h"
#include "glibintl.h"
#include "gloadableicon.h"
#include "gmemoryinputstream.h"
#include "gtask.h"
#include "gioerror.h"
/**
* SECTION:gbytesicon
* @short_description: An icon stored in memory as a GBytes
* @include: gio/gio.h
* @see_also: #GIcon, #GLoadableIcon, #GBytes
*
* #GBytesIcon specifies an image held in memory in a common format (usually
* png) to be used as icon.
*
* Since: 2.38
**/
typedef GObjectClass GBytesIconClass;
struct _GBytesIcon
{
GObject parent_instance;
GBytes *bytes;
};
enum
{
PROP_0,
PROP_BYTES
};
static void g_bytes_icon_icon_iface_init (GIconIface *iface);
static void g_bytes_icon_loadable_icon_iface_init (GLoadableIconIface *iface);
G_DEFINE_TYPE_WITH_CODE (GBytesIcon, g_bytes_icon, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ICON, g_bytes_icon_icon_iface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, g_bytes_icon_loadable_icon_iface_init))
static void
g_bytes_icon_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GBytesIcon *icon = G_BYTES_ICON (object);
switch (prop_id)
{
case PROP_BYTES:
g_value_set_boxed (value, icon->bytes);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_bytes_icon_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GBytesIcon *icon = G_BYTES_ICON (object);
switch (prop_id)
{
case PROP_BYTES:
icon->bytes = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
g_bytes_icon_finalize (GObject *object)
{
GBytesIcon *icon;
icon = G_BYTES_ICON (object);
g_bytes_unref (icon->bytes);
G_OBJECT_CLASS (g_bytes_icon_parent_class)->finalize (object);
}
static void
g_bytes_icon_class_init (GBytesIconClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = g_bytes_icon_get_property;
gobject_class->set_property = g_bytes_icon_set_property;
gobject_class->finalize = g_bytes_icon_finalize;
/**
* GBytesIcon:bytes:
*
* The bytes containing the icon.
*/
g_object_class_install_property (gobject_class, PROP_BYTES,
g_param_spec_boxed ("bytes",
P_("bytes"),
P_("The bytes containing the icon"),
G_TYPE_BYTES,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
g_bytes_icon_init (GBytesIcon *bytes)
{
}
/**
* g_bytes_icon_new:
* @bytes: a #GBytes.
*
* Creates a new icon for a bytes.
*
* This cannot fail, but loading and interpreting the bytes may fail later on
* (for example, if g_loadable_icon_load() is called) if the image is invalid.
*
* Returns: (transfer full) (type GBytesIcon): a #GIcon for the given
* @bytes.
*
* Since: 2.38
**/
GIcon *
g_bytes_icon_new (GBytes *bytes)
{
g_return_val_if_fail (bytes != NULL, NULL);
return g_object_new (G_TYPE_BYTES_ICON, "bytes", bytes, NULL);
}
/**
* g_bytes_icon_get_bytes:
* @icon: a #GIcon.
*
* Gets the #GBytes associated with the given @icon.
*
* Returns: (transfer none): a #GBytes.
*
* Since: 2.38
**/
GBytes *
g_bytes_icon_get_bytes (GBytesIcon *icon)
{
g_return_val_if_fail (G_IS_BYTES_ICON (icon), NULL);
return icon->bytes;
}
static guint
g_bytes_icon_hash (GIcon *icon)
{
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
return g_bytes_hash (bytes_icon->bytes);
}
static gboolean
g_bytes_icon_equal (GIcon *icon1,
GIcon *icon2)
{
GBytesIcon *bytes1 = G_BYTES_ICON (icon1);
GBytesIcon *bytes2 = G_BYTES_ICON (icon2);
return g_bytes_equal (bytes1->bytes, bytes2->bytes);
}
static GVariant *
g_bytes_icon_serialize (GIcon *icon)
{
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
return g_variant_new ("(sv)", "bytes",
g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes_icon->bytes, TRUE));
}
static void
g_bytes_icon_icon_iface_init (GIconIface *iface)
{
iface->hash = g_bytes_icon_hash;
iface->equal = g_bytes_icon_equal;
iface->serialize = g_bytes_icon_serialize;
}
static GInputStream *
g_bytes_icon_load (GLoadableIcon *icon,
int size,
char **type,
GCancellable *cancellable,
GError **error)
{
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
if (type)
*type = NULL;
return g_memory_input_stream_new_from_bytes (bytes_icon->bytes);
}
static void
g_bytes_icon_load_async (GLoadableIcon *icon,
int size,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GBytesIcon *bytes_icon = G_BYTES_ICON (icon);
GTask *task;
task = g_task_new (icon, cancellable, callback, user_data);
g_task_set_source_tag (task, g_bytes_icon_load_async);
g_task_return_pointer (task, g_memory_input_stream_new_from_bytes (bytes_icon->bytes), g_object_unref);
g_object_unref (task);
}
static GInputStream *
g_bytes_icon_load_finish (GLoadableIcon *icon,
GAsyncResult *res,
char **type,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (res, icon), NULL);
if (type)
*type = NULL;
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
g_bytes_icon_loadable_icon_iface_init (GLoadableIconIface *iface)
{
iface->load = g_bytes_icon_load;
iface->load_async = g_bytes_icon_load_async;
iface->load_finish = g_bytes_icon_load_finish;
}

52
gio/gbytesicon.h Normal file
View file

@ -0,0 +1,52 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_BYTES_ICON_H__
#define __G_BYTES_ICON_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_BYTES_ICON (g_bytes_icon_get_type ())
#define G_BYTES_ICON(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_BYTES_ICON, GBytesIcon))
#define G_IS_BYTES_ICON(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_BYTES_ICON))
/**
* GBytesIcon:
*
* Gets an icon for a #GBytes. Implements #GLoadableIcon.
**/
GLIB_AVAILABLE_IN_2_38
GType g_bytes_icon_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_38
GIcon * g_bytes_icon_new (GBytes *bytes);
GLIB_AVAILABLE_IN_2_38
GBytes * g_bytes_icon_get_bytes (GBytesIcon *icon);
G_END_DECLS
#endif /* __G_BYTES_ICON_H__ */

821
gio/gcancellable.c Normal file
View file

@ -0,0 +1,821 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "glib.h"
#include <gioerror.h>
#include "glib-private.h"
#include "gcancellable.h"
#include "glibintl.h"
/**
* SECTION:gcancellable
* @short_description: Thread-safe Operation Cancellation Stack
* @include: gio/gio.h
*
* GCancellable is a thread-safe operation cancellation stack used
* throughout GIO to allow for cancellation of synchronous and
* asynchronous operations.
*/
enum {
CANCELLED,
LAST_SIGNAL
};
struct _GCancellablePrivate
{
/* Atomic so that g_cancellable_is_cancelled does not require holding the mutex. */
gboolean cancelled;
/* Access to fields below is protected by cancellable_mutex. */
guint cancelled_running : 1;
guint cancelled_running_waiting : 1;
guint fd_refcount;
GWakeup *wakeup;
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_PRIVATE (GCancellable, g_cancellable, G_TYPE_OBJECT)
static GPrivate current_cancellable;
static GMutex cancellable_mutex;
static GCond cancellable_cond;
static void
g_cancellable_finalize (GObject *object)
{
GCancellable *cancellable = G_CANCELLABLE (object);
if (cancellable->priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_free) (cancellable->priv->wakeup);
G_OBJECT_CLASS (g_cancellable_parent_class)->finalize (object);
}
static void
g_cancellable_class_init (GCancellableClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_cancellable_finalize;
/**
* GCancellable::cancelled:
* @cancellable: a #GCancellable.
*
* Emitted when the operation has been cancelled.
*
* Can be used by implementations of cancellable operations. If the
* operation is cancelled from another thread, the signal will be
* emitted in the thread that cancelled the operation, not the
* thread that is running the operation.
*
* Note that disconnecting from this signal (or any signal) in a
* multi-threaded program is prone to race conditions. For instance
* it is possible that a signal handler may be invoked even after
* a call to g_signal_handler_disconnect() for that handler has
* already returned.
*
* There is also a problem when cancellation happens right before
* connecting to the signal. If this happens the signal will
* unexpectedly not be emitted, and checking before connecting to
* the signal leaves a race condition where this is still happening.
*
* In order to make it safe and easy to connect handlers there
* are two helper functions: g_cancellable_connect() and
* g_cancellable_disconnect() which protect against problems
* like this.
*
* An example of how to us this:
* |[<!-- language="C" -->
* // Make sure we don't do unnecessary work if already cancelled
* if (g_cancellable_set_error_if_cancelled (cancellable, error))
* return;
*
* // Set up all the data needed to be able to handle cancellation
* // of the operation
* my_data = my_data_new (...);
*
* id = 0;
* if (cancellable)
* id = g_cancellable_connect (cancellable,
* G_CALLBACK (cancelled_handler)
* data, NULL);
*
* // cancellable operation here...
*
* g_cancellable_disconnect (cancellable, id);
*
* // cancelled_handler is never called after this, it is now safe
* // to free the data
* my_data_free (my_data);
* ]|
*
* Note that the cancelled signal is emitted in the thread that
* the user cancelled from, which may be the main thread. So, the
* cancellable signal should not do something that can block.
*/
signals[CANCELLED] =
g_signal_new (I_("cancelled"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GCancellableClass, cancelled),
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
}
static void
g_cancellable_init (GCancellable *cancellable)
{
cancellable->priv = g_cancellable_get_instance_private (cancellable);
}
/**
* g_cancellable_new:
*
* Creates a new #GCancellable object.
*
* Applications that want to start one or more operations
* that should be cancellable should create a #GCancellable
* and pass it to the operations.
*
* One #GCancellable can be used in multiple consecutive
* operations or in multiple concurrent operations.
*
* Returns: a #GCancellable.
**/
GCancellable *
g_cancellable_new (void)
{
return g_object_new (G_TYPE_CANCELLABLE, NULL);
}
/**
* g_cancellable_push_current:
* @cancellable: a #GCancellable object
*
* Pushes @cancellable onto the cancellable stack. The current
* cancellable can then be received using g_cancellable_get_current().
*
* This is useful when implementing cancellable operations in
* code that does not allow you to pass down the cancellable object.
*
* This is typically called automatically by e.g. #GFile operations,
* so you rarely have to call this yourself.
**/
void
g_cancellable_push_current (GCancellable *cancellable)
{
GSList *l;
g_return_if_fail (cancellable != NULL);
l = g_private_get (&current_cancellable);
l = g_slist_prepend (l, cancellable);
g_private_set (&current_cancellable, l);
}
/**
* g_cancellable_pop_current:
* @cancellable: a #GCancellable object
*
* Pops @cancellable off the cancellable stack (verifying that @cancellable
* is on the top of the stack).
**/
void
g_cancellable_pop_current (GCancellable *cancellable)
{
GSList *l;
l = g_private_get (&current_cancellable);
g_return_if_fail (l != NULL);
g_return_if_fail (l->data == cancellable);
l = g_slist_delete_link (l, l);
g_private_set (&current_cancellable, l);
}
/**
* g_cancellable_get_current:
*
* Gets the top cancellable from the stack.
*
* Returns: (nullable) (transfer none): a #GCancellable from the top
* of the stack, or %NULL if the stack is empty.
**/
GCancellable *
g_cancellable_get_current (void)
{
GSList *l;
l = g_private_get (&current_cancellable);
if (l == NULL)
return NULL;
return G_CANCELLABLE (l->data);
}
/**
* g_cancellable_reset:
* @cancellable: a #GCancellable object.
*
* Resets @cancellable to its uncancelled state.
*
* If cancellable is currently in use by any cancellable operation
* then the behavior of this function is undefined.
*
* Note that it is generally not a good idea to reuse an existing
* cancellable for more operations after it has been cancelled once,
* as this function might tempt you to do. The recommended practice
* is to drop the reference to a cancellable after cancelling it,
* and let it die with the outstanding async operations. You should
* create a fresh cancellable for further async operations.
**/
void
g_cancellable_reset (GCancellable *cancellable)
{
GCancellablePrivate *priv;
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
g_mutex_lock (&cancellable_mutex);
priv = cancellable->priv;
while (priv->cancelled_running)
{
priv->cancelled_running_waiting = TRUE;
g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
if (g_atomic_int_get (&priv->cancelled))
{
if (priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup);
g_atomic_int_set (&priv->cancelled, FALSE);
}
g_mutex_unlock (&cancellable_mutex);
}
/**
* g_cancellable_is_cancelled:
* @cancellable: (nullable): a #GCancellable or %NULL
*
* Checks if a cancellable job has been cancelled.
*
* Returns: %TRUE if @cancellable is cancelled,
* FALSE if called with %NULL or if item is not cancelled.
**/
gboolean
g_cancellable_is_cancelled (GCancellable *cancellable)
{
return cancellable != NULL && g_atomic_int_get (&cancellable->priv->cancelled);
}
/**
* g_cancellable_set_error_if_cancelled:
* @cancellable: (nullable): a #GCancellable or %NULL
* @error: #GError to append error state to
*
* If the @cancellable is cancelled, sets the error to notify
* that the operation was cancelled.
*
* Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not
*/
gboolean
g_cancellable_set_error_if_cancelled (GCancellable *cancellable,
GError **error)
{
if (g_cancellable_is_cancelled (cancellable))
{
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_CANCELLED,
_("Operation was cancelled"));
return TRUE;
}
return FALSE;
}
/**
* g_cancellable_get_fd:
* @cancellable: a #GCancellable.
*
* Gets the file descriptor for a cancellable job. This can be used to
* implement cancellable operations on Unix systems. The returned fd will
* turn readable when @cancellable is cancelled.
*
* You are not supposed to read from the fd yourself, just check for
* readable status. Reading to unset the readable status is done
* with g_cancellable_reset().
*
* After a successful return from this function, you should use
* g_cancellable_release_fd() to free up resources allocated for
* the returned file descriptor.
*
* See also g_cancellable_make_pollfd().
*
* Returns: A valid file descriptor. `-1` if the file descriptor
* is not supported, or on errors.
**/
int
g_cancellable_get_fd (GCancellable *cancellable)
{
GPollFD pollfd;
#ifndef G_OS_WIN32
gboolean retval G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
#endif
if (cancellable == NULL)
return -1;
#ifdef G_OS_WIN32
pollfd.fd = -1;
#else
retval = g_cancellable_make_pollfd (cancellable, &pollfd);
g_assert (retval);
#endif
return pollfd.fd;
}
/**
* g_cancellable_make_pollfd:
* @cancellable: (nullable): a #GCancellable or %NULL
* @pollfd: a pointer to a #GPollFD
*
* Creates a #GPollFD corresponding to @cancellable; this can be passed
* to g_poll() and used to poll for cancellation. This is useful both
* for unix systems without a native poll and for portability to
* windows.
*
* When this function returns %TRUE, you should use
* g_cancellable_release_fd() to free up resources allocated for the
* @pollfd. After a %FALSE return, do not call g_cancellable_release_fd().
*
* If this function returns %FALSE, either no @cancellable was given or
* resource limits prevent this function from allocating the necessary
* structures for polling. (On Linux, you will likely have reached
* the maximum number of file descriptors.) The suggested way to handle
* these cases is to ignore the @cancellable.
*
* You are not supposed to read from the fd yourself, just check for
* readable status. Reading to unset the readable status is done
* with g_cancellable_reset().
*
* Returns: %TRUE if @pollfd was successfully initialized, %FALSE on
* failure to prepare the cancellable.
*
* Since: 2.22
**/
gboolean
g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd)
{
g_return_val_if_fail (pollfd != NULL, FALSE);
if (cancellable == NULL)
return FALSE;
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
g_mutex_lock (&cancellable_mutex);
cancellable->priv->fd_refcount++;
if (cancellable->priv->wakeup == NULL)
{
cancellable->priv->wakeup = GLIB_PRIVATE_CALL (g_wakeup_new) ();
if (g_atomic_int_get (&cancellable->priv->cancelled))
GLIB_PRIVATE_CALL (g_wakeup_signal) (cancellable->priv->wakeup);
}
GLIB_PRIVATE_CALL (g_wakeup_get_pollfd) (cancellable->priv->wakeup, pollfd);
g_mutex_unlock (&cancellable_mutex);
return TRUE;
}
/**
* g_cancellable_release_fd:
* @cancellable: a #GCancellable
*
* Releases a resources previously allocated by g_cancellable_get_fd()
* or g_cancellable_make_pollfd().
*
* For compatibility reasons with older releases, calling this function
* is not strictly required, the resources will be automatically freed
* when the @cancellable is finalized. However, the @cancellable will
* block scarce file descriptors until it is finalized if this function
* is not called. This can cause the application to run out of file
* descriptors when many #GCancellables are used at the same time.
*
* Since: 2.22
**/
void
g_cancellable_release_fd (GCancellable *cancellable)
{
GCancellablePrivate *priv;
if (cancellable == NULL)
return;
g_return_if_fail (G_IS_CANCELLABLE (cancellable));
priv = cancellable->priv;
g_mutex_lock (&cancellable_mutex);
g_assert (priv->fd_refcount > 0);
priv->fd_refcount--;
if (priv->fd_refcount == 0)
{
GLIB_PRIVATE_CALL (g_wakeup_free) (priv->wakeup);
priv->wakeup = NULL;
}
g_mutex_unlock (&cancellable_mutex);
}
/**
* g_cancellable_cancel:
* @cancellable: (nullable): a #GCancellable object.
*
* Will set @cancellable to cancelled, and will emit the
* #GCancellable::cancelled signal. (However, see the warning about
* race conditions in the documentation for that signal if you are
* planning to connect to it.)
*
* This function is thread-safe. In other words, you can safely call
* it from a thread other than the one running the operation that was
* passed the @cancellable.
*
* If @cancellable is %NULL, this function returns immediately for convenience.
*
* The convention within GIO is that cancelling an asynchronous
* operation causes it to complete asynchronously. That is, if you
* cancel the operation from the same thread in which it is running,
* then the operation's #GAsyncReadyCallback will not be invoked until
* the application returns to the main loop.
**/
void
g_cancellable_cancel (GCancellable *cancellable)
{
GCancellablePrivate *priv;
if (cancellable == NULL || g_cancellable_is_cancelled (cancellable))
return;
priv = cancellable->priv;
g_mutex_lock (&cancellable_mutex);
if (g_atomic_int_get (&priv->cancelled))
{
g_mutex_unlock (&cancellable_mutex);
return;
}
g_atomic_int_set (&priv->cancelled, TRUE);
priv->cancelled_running = TRUE;
if (priv->wakeup)
GLIB_PRIVATE_CALL (g_wakeup_signal) (priv->wakeup);
g_mutex_unlock (&cancellable_mutex);
g_object_ref (cancellable);
g_signal_emit (cancellable, signals[CANCELLED], 0);
g_mutex_lock (&cancellable_mutex);
priv->cancelled_running = FALSE;
if (priv->cancelled_running_waiting)
g_cond_broadcast (&cancellable_cond);
priv->cancelled_running_waiting = FALSE;
g_mutex_unlock (&cancellable_mutex);
g_object_unref (cancellable);
}
/**
* g_cancellable_connect:
* @cancellable: A #GCancellable.
* @callback: The #GCallback to connect.
* @data: Data to pass to @callback.
* @data_destroy_func: (nullable): Free function for @data or %NULL.
*
* Convenience function to connect to the #GCancellable::cancelled
* signal. Also handles the race condition that may happen
* if the cancellable is cancelled right before connecting.
*
* @callback is called at most once, either directly at the
* time of the connect if @cancellable is already cancelled,
* or when @cancellable is cancelled in some thread.
*
* @data_destroy_func will be called when the handler is
* disconnected, or immediately if the cancellable is already
* cancelled.
*
* See #GCancellable::cancelled for details on how to use this.
*
* Since GLib 2.40, the lock protecting @cancellable is not held when
* @callback is invoked. This lifts a restriction in place for
* earlier GLib versions which now makes it easier to write cleanup
* code that unconditionally invokes e.g. g_cancellable_cancel().
*
* Returns: The id of the signal handler or 0 if @cancellable has already
* been cancelled.
*
* Since: 2.22
*/
gulong
g_cancellable_connect (GCancellable *cancellable,
GCallback callback,
gpointer data,
GDestroyNotify data_destroy_func)
{
gulong id;
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), 0);
g_mutex_lock (&cancellable_mutex);
if (g_atomic_int_get (&cancellable->priv->cancelled))
{
void (*_callback) (GCancellable *cancellable,
gpointer user_data);
g_mutex_unlock (&cancellable_mutex);
_callback = (void *)callback;
id = 0;
_callback (cancellable, data);
if (data_destroy_func)
data_destroy_func (data);
}
else
{
id = g_signal_connect_data (cancellable, "cancelled",
callback, data,
(GClosureNotify) data_destroy_func,
0);
g_mutex_unlock (&cancellable_mutex);
}
return id;
}
/**
* g_cancellable_disconnect:
* @cancellable: (nullable): A #GCancellable or %NULL.
* @handler_id: Handler id of the handler to be disconnected, or `0`.
*
* Disconnects a handler from a cancellable instance similar to
* g_signal_handler_disconnect(). Additionally, in the event that a
* signal handler is currently running, this call will block until the
* handler has finished. Calling this function from a
* #GCancellable::cancelled signal handler will therefore result in a
* deadlock.
*
* This avoids a race condition where a thread cancels at the
* same time as the cancellable operation is finished and the
* signal handler is removed. See #GCancellable::cancelled for
* details on how to use this.
*
* If @cancellable is %NULL or @handler_id is `0` this function does
* nothing.
*
* Since: 2.22
*/
void
g_cancellable_disconnect (GCancellable *cancellable,
gulong handler_id)
{
GCancellablePrivate *priv;
if (handler_id == 0 || cancellable == NULL)
return;
g_mutex_lock (&cancellable_mutex);
priv = cancellable->priv;
while (priv->cancelled_running)
{
priv->cancelled_running_waiting = TRUE;
g_cond_wait (&cancellable_cond, &cancellable_mutex);
}
g_signal_handler_disconnect (cancellable, handler_id);
g_mutex_unlock (&cancellable_mutex);
}
typedef struct {
GSource source;
GCancellable *cancellable;
gulong cancelled_handler;
/* Protected by cancellable_mutex: */
gboolean resurrected_during_cancellation;
} GCancellableSource;
/*
* The reference count of the GSource might be 0 at this point but it is not
* finalized yet and its dispose function did not run yet, or otherwise we
* would have disconnected the signal handler already and due to the signal
* emission lock it would be impossible to call the signal handler at that
* point. That is: at this point we either have a fully valid GSource, or
* it's not disposed or finalized yet and we can still resurrect it as needed.
*
* As such we first ensure that we have a strong reference to the GSource in
* here before calling any other GSource API.
*/
static void
cancellable_source_cancelled (GCancellable *cancellable,
gpointer user_data)
{
GSource *source = user_data;
GCancellableSource *cancellable_source = (GCancellableSource *) source;
g_mutex_lock (&cancellable_mutex);
/* Drop the reference added in cancellable_source_dispose(); see the comment there.
* The reference must be dropped after unlocking @cancellable_mutex since
* it could be the final reference, and the dispose function takes
* @cancellable_mutex. */
if (cancellable_source->resurrected_during_cancellation)
{
cancellable_source->resurrected_during_cancellation = FALSE;
g_mutex_unlock (&cancellable_mutex);
g_source_unref (source);
return;
}
g_source_ref (source);
g_mutex_unlock (&cancellable_mutex);
g_source_set_ready_time (source, 0);
g_source_unref (source);
}
static gboolean
cancellable_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
GCancellableSourceFunc func = (GCancellableSourceFunc)callback;
GCancellableSource *cancellable_source = (GCancellableSource *)source;
g_source_set_ready_time (source, -1);
return (*func) (cancellable_source->cancellable, user_data);
}
static void
cancellable_source_dispose (GSource *source)
{
GCancellableSource *cancellable_source = (GCancellableSource *)source;
g_mutex_lock (&cancellable_mutex);
if (cancellable_source->cancellable)
{
if (cancellable_source->cancellable->priv->cancelled_running)
{
/* There can be a race here: if thread A has called
* g_cancellable_cancel() and has got as far as committing to call
* cancellable_source_cancelled(), then thread B drops the final
* ref on the GCancellableSource before g_source_ref() is called in
* cancellable_source_cancelled(), then cancellable_source_dispose()
* will run through and the GCancellableSource will be finalised
* before cancellable_source_cancelled() gets to g_source_ref(). It
* will then be left in a state where its committed to using a
* dangling GCancellableSource pointer.
*
* Eliminate that race by resurrecting the #GSource temporarily, and
* then dropping that reference in cancellable_source_cancelled(),
* which should be guaranteed to fire because were inside a
* @cancelled_running block.
*/
g_source_ref (source);
cancellable_source->resurrected_during_cancellation = TRUE;
}
g_clear_signal_handler (&cancellable_source->cancelled_handler,
cancellable_source->cancellable);
g_clear_object (&cancellable_source->cancellable);
}
g_mutex_unlock (&cancellable_mutex);
}
static gboolean
cancellable_source_closure_callback (GCancellable *cancellable,
gpointer data)
{
GClosure *closure = data;
GValue params = G_VALUE_INIT;
GValue result_value = G_VALUE_INIT;
gboolean result;
g_value_init (&result_value, G_TYPE_BOOLEAN);
g_value_init (&params, G_TYPE_CANCELLABLE);
g_value_set_object (&params, cancellable);
g_closure_invoke (closure, &result_value, 1, &params, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
g_value_unset (&params);
return result;
}
static GSourceFuncs cancellable_source_funcs =
{
NULL,
NULL,
cancellable_source_dispatch,
NULL,
(GSourceFunc)cancellable_source_closure_callback,
NULL,
};
/**
* g_cancellable_source_new:
* @cancellable: (nullable): a #GCancellable, or %NULL
*
* Creates a source that triggers if @cancellable is cancelled and
* calls its callback of type #GCancellableSourceFunc. This is
* primarily useful for attaching to another (non-cancellable) source
* with g_source_add_child_source() to add cancellability to it.
*
* For convenience, you can call this with a %NULL #GCancellable,
* in which case the source will never trigger.
*
* The new #GSource will hold a reference to the #GCancellable.
*
* Returns: (transfer full): the new #GSource.
*
* Since: 2.28
*/
GSource *
g_cancellable_source_new (GCancellable *cancellable)
{
GSource *source;
GCancellableSource *cancellable_source;
source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource));
g_source_set_static_name (source, "GCancellable");
g_source_set_dispose_function (source, cancellable_source_dispose);
cancellable_source = (GCancellableSource *)source;
if (cancellable)
{
cancellable_source->cancellable = g_object_ref (cancellable);
/* We intentionally don't use g_cancellable_connect() here,
* because we don't want the "at most once" behavior.
*/
cancellable_source->cancelled_handler =
g_signal_connect (cancellable, "cancelled",
G_CALLBACK (cancellable_source_cancelled),
source);
if (g_cancellable_is_cancelled (cancellable))
g_source_set_ready_time (source, 0);
}
return source;
}

118
gio/gcancellable.h Normal file
View file

@ -0,0 +1,118 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CANCELLABLE_H__
#define __G_CANCELLABLE_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_CANCELLABLE (g_cancellable_get_type ())
#define G_CANCELLABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CANCELLABLE, GCancellable))
#define G_CANCELLABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CANCELLABLE, GCancellableClass))
#define G_IS_CANCELLABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CANCELLABLE))
#define G_IS_CANCELLABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CANCELLABLE))
#define G_CANCELLABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CANCELLABLE, GCancellableClass))
/**
* GCancellable:
*
* Allows actions to be cancelled.
*/
typedef struct _GCancellableClass GCancellableClass;
typedef struct _GCancellablePrivate GCancellablePrivate;
struct _GCancellable
{
GObject parent_instance;
/*< private >*/
GCancellablePrivate *priv;
};
struct _GCancellableClass
{
GObjectClass parent_class;
void (* cancelled) (GCancellable *cancellable);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_cancellable_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GCancellable *g_cancellable_new (void);
/* These are only safe to call inside a cancellable op */
GLIB_AVAILABLE_IN_ALL
gboolean g_cancellable_is_cancelled (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
gboolean g_cancellable_set_error_if_cancelled (GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
int g_cancellable_get_fd (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
gboolean g_cancellable_make_pollfd (GCancellable *cancellable,
GPollFD *pollfd);
GLIB_AVAILABLE_IN_ALL
void g_cancellable_release_fd (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
GSource * g_cancellable_source_new (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
GCancellable *g_cancellable_get_current (void);
GLIB_AVAILABLE_IN_ALL
void g_cancellable_push_current (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
void g_cancellable_pop_current (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
void g_cancellable_reset (GCancellable *cancellable);
GLIB_AVAILABLE_IN_ALL
gulong g_cancellable_connect (GCancellable *cancellable,
GCallback callback,
gpointer data,
GDestroyNotify data_destroy_func);
GLIB_AVAILABLE_IN_ALL
void g_cancellable_disconnect (GCancellable *cancellable,
gulong handler_id);
/* This is safe to call from another thread */
GLIB_AVAILABLE_IN_ALL
void g_cancellable_cancel (GCancellable *cancellable);
G_END_DECLS
#endif /* __G_CANCELLABLE_H__ */

472
gio/gcharsetconverter.c Normal file
View file

@ -0,0 +1,472 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "gcharsetconverter.h"
#include <errno.h>
#include "ginitable.h"
#include "gioerror.h"
#include "glibintl.h"
enum {
PROP_0,
PROP_FROM_CHARSET,
PROP_TO_CHARSET,
PROP_USE_FALLBACK
};
/**
* SECTION:gcharsetconverter
* @short_description: Convert between charsets
* @include: gio/gio.h
*
* #GCharsetConverter is an implementation of #GConverter based on
* GIConv.
*/
static void g_charset_converter_iface_init (GConverterIface *iface);
static void g_charset_converter_initable_iface_init (GInitableIface *iface);
/**
* GCharsetConverter:
*
* Conversions between character sets.
*/
struct _GCharsetConverter
{
GObject parent_instance;
char *from;
char *to;
GIConv iconv;
gboolean use_fallback;
guint n_fallback_errors;
};
G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER,
g_charset_converter_iface_init);
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
g_charset_converter_initable_iface_init))
static void
g_charset_converter_finalize (GObject *object)
{
GCharsetConverter *conv;
conv = G_CHARSET_CONVERTER (object);
g_free (conv->from);
g_free (conv->to);
if (conv->iconv)
g_iconv_close (conv->iconv);
G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object);
}
static void
g_charset_converter_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GCharsetConverter *conv;
conv = G_CHARSET_CONVERTER (object);
switch (prop_id)
{
case PROP_TO_CHARSET:
g_free (conv->to);
conv->to = g_value_dup_string (value);
break;
case PROP_FROM_CHARSET:
g_free (conv->from);
conv->from = g_value_dup_string (value);
break;
case PROP_USE_FALLBACK:
conv->use_fallback = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_charset_converter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GCharsetConverter *conv;
conv = G_CHARSET_CONVERTER (object);
switch (prop_id)
{
case PROP_TO_CHARSET:
g_value_set_string (value, conv->to);
break;
case PROP_FROM_CHARSET:
g_value_set_string (value, conv->from);
break;
case PROP_USE_FALLBACK:
g_value_set_boolean (value, conv->use_fallback);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_charset_converter_class_init (GCharsetConverterClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_charset_converter_finalize;
gobject_class->get_property = g_charset_converter_get_property;
gobject_class->set_property = g_charset_converter_set_property;
g_object_class_install_property (gobject_class,
PROP_TO_CHARSET,
g_param_spec_string ("to-charset",
P_("To Charset"),
P_("The character encoding to convert to"),
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_FROM_CHARSET,
g_param_spec_string ("from-charset",
P_("From Charset"),
P_("The character encoding to convert from"),
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_USE_FALLBACK,
g_param_spec_boolean ("use-fallback",
P_("Fallback enabled"),
P_("Use fallback (of form \\<hexval>) for invalid bytes"),
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
}
static void
g_charset_converter_init (GCharsetConverter *local)
{
}
/**
* g_charset_converter_new:
* @to_charset: destination charset
* @from_charset: source charset
* @error: #GError for error reporting, or %NULL to ignore.
*
* Creates a new #GCharsetConverter.
*
* Returns: a new #GCharsetConverter or %NULL on error.
*
* Since: 2.24
**/
GCharsetConverter *
g_charset_converter_new (const gchar *to_charset,
const gchar *from_charset,
GError **error)
{
GCharsetConverter *conv;
conv = g_initable_new (G_TYPE_CHARSET_CONVERTER,
NULL, error,
"to-charset", to_charset,
"from-charset", from_charset,
NULL);
return conv;
}
static void
g_charset_converter_reset (GConverter *converter)
{
GCharsetConverter *conv = G_CHARSET_CONVERTER (converter);
if (conv->iconv == NULL)
{
g_warning ("Invalid object, not initialized");
return;
}
g_iconv (conv->iconv, NULL, NULL, NULL, NULL);
conv->n_fallback_errors = 0;
}
static GConverterResult
g_charset_converter_convert (GConverter *converter,
const void *inbuf,
gsize inbuf_size,
void *outbuf,
gsize outbuf_size,
GConverterFlags flags,
gsize *bytes_read,
gsize *bytes_written,
GError **error)
{
GCharsetConverter *conv;
gsize res;
GConverterResult ret;
gchar *inbufp, *outbufp;
gsize in_left, out_left;
int errsv;
gboolean reset;
conv = G_CHARSET_CONVERTER (converter);
if (conv->iconv == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED,
_("Invalid object, not initialized"));
return G_CONVERTER_ERROR;
}
inbufp = (char *)inbuf;
outbufp = (char *)outbuf;
in_left = inbuf_size;
out_left = outbuf_size;
reset = FALSE;
/* if there is not input try to flush the data */
if (inbuf_size == 0)
{
if (flags & G_CONVERTER_INPUT_AT_END ||
flags & G_CONVERTER_FLUSH)
{
reset = TRUE;
}
else
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
_("Incomplete multibyte sequence in input"));
return G_CONVERTER_ERROR;
}
}
if (reset)
/* call g_iconv with NULL inbuf to cleanup shift state */
res = g_iconv (conv->iconv,
NULL, &in_left,
&outbufp, &out_left);
else
res = g_iconv (conv->iconv,
&inbufp, &in_left,
&outbufp, &out_left);
*bytes_read = inbufp - (char *)inbuf;
*bytes_written = outbufp - (char *)outbuf;
/* Don't report error if we converted anything */
if (res == (gsize) -1 && *bytes_read == 0)
{
errsv = errno;
switch (errsv)
{
case EINVAL:
/* Incomplete input text */
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
_("Incomplete multibyte sequence in input"));
break;
case E2BIG:
/* Not enough destination space */
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
_("Not enough space in destination"));
break;
case EILSEQ:
/* Invalid code sequence */
if (conv->use_fallback)
{
if (outbuf_size < 3)
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
_("Not enough space in destination"));
else
{
const char hex[] = "0123456789ABCDEF";
guint8 v = *(guint8 *)inbuf;
guint8 *out = (guint8 *)outbuf;
out[0] = '\\';
out[1] = hex[(v & 0xf0) >> 4];
out[2] = hex[(v & 0x0f) >> 0];
*bytes_read = 1;
*bytes_written = 3;
in_left--;
conv->n_fallback_errors++;
goto ok;
}
}
else
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
_("Invalid byte sequence in conversion input"));
break;
default:
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Error during conversion: %s"),
g_strerror (errsv));
break;
}
ret = G_CONVERTER_ERROR;
}
else
{
ok:
ret = G_CONVERTER_CONVERTED;
if (reset &&
(flags & G_CONVERTER_INPUT_AT_END))
ret = G_CONVERTER_FINISHED;
else if (reset &&
(flags & G_CONVERTER_FLUSH))
ret = G_CONVERTER_FLUSHED;
}
return ret;
}
/**
* g_charset_converter_set_use_fallback:
* @converter: a #GCharsetConverter
* @use_fallback: %TRUE to use fallbacks
*
* Sets the #GCharsetConverter:use-fallback property.
*
* Since: 2.24
*/
void
g_charset_converter_set_use_fallback (GCharsetConverter *converter,
gboolean use_fallback)
{
use_fallback = !!use_fallback;
if (converter->use_fallback != use_fallback)
{
converter->use_fallback = use_fallback;
g_object_notify (G_OBJECT (converter), "use-fallback");
}
}
/**
* g_charset_converter_get_use_fallback:
* @converter: a #GCharsetConverter
*
* Gets the #GCharsetConverter:use-fallback property.
*
* Returns: %TRUE if fallbacks are used by @converter
*
* Since: 2.24
*/
gboolean
g_charset_converter_get_use_fallback (GCharsetConverter *converter)
{
return converter->use_fallback;
}
/**
* g_charset_converter_get_num_fallbacks:
* @converter: a #GCharsetConverter
*
* Gets the number of fallbacks that @converter has applied so far.
*
* Returns: the number of fallbacks that @converter has applied
*
* Since: 2.24
*/
guint
g_charset_converter_get_num_fallbacks (GCharsetConverter *converter)
{
return converter->n_fallback_errors;
}
static void
g_charset_converter_iface_init (GConverterIface *iface)
{
iface->convert = g_charset_converter_convert;
iface->reset = g_charset_converter_reset;
}
static gboolean
g_charset_converter_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
GCharsetConverter *conv;
int errsv;
g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable), FALSE);
conv = G_CHARSET_CONVERTER (initable);
if (cancellable != NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Cancellable initialization not supported"));
return FALSE;
}
conv->iconv = g_iconv_open (conv->to, conv->from);
errsv = errno;
if (conv->iconv == (GIConv)-1)
{
if (errsv == EINVAL)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Conversion from character set “%s” to “%s” is not supported"),
conv->from, conv->to);
else
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Could not open converter from “%s” to “%s”"),
conv->from, conv->to);
return FALSE;
}
return TRUE;
}
static void
g_charset_converter_initable_iface_init (GInitableIface *iface)
{
iface->init = g_charset_converter_initable_init;
}

63
gio/gcharsetconverter.h Normal file
View file

@ -0,0 +1,63 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CHARSET_CONVERTER_H__
#define __G_CHARSET_CONVERTER_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gconverter.h>
G_BEGIN_DECLS
#define G_TYPE_CHARSET_CONVERTER (g_charset_converter_get_type ())
#define G_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverter))
#define G_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass))
#define G_IS_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CHARSET_CONVERTER))
#define G_IS_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CHARSET_CONVERTER))
#define G_CHARSET_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass))
typedef struct _GCharsetConverterClass GCharsetConverterClass;
struct _GCharsetConverterClass
{
GObjectClass parent_class;
};
GLIB_AVAILABLE_IN_ALL
GType g_charset_converter_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GCharsetConverter *g_charset_converter_new (const gchar *to_charset,
const gchar *from_charset,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_charset_converter_set_use_fallback (GCharsetConverter *converter,
gboolean use_fallback);
GLIB_AVAILABLE_IN_ALL
gboolean g_charset_converter_get_use_fallback (GCharsetConverter *converter);
GLIB_AVAILABLE_IN_ALL
guint g_charset_converter_get_num_fallbacks (GCharsetConverter *converter);
G_END_DECLS
#endif /* __G_CHARSET_CONVERTER_H__ */

View file

@ -0,0 +1,277 @@
/*
* Copyright © 2015 Patrick Griffis
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Patrick Griffis
*/
#include "config.h"
#import <Cocoa/Cocoa.h>
#include "gnotificationbackend.h"
#include "gapplication.h"
#include "gaction.h"
#include "gactiongroup.h"
#include "giomodule-priv.h"
#include "gnotification-private.h"
#include "gthemedicon.h"
#include "gfileicon.h"
#include "gfile.h"
#define G_TYPE_COCOA_NOTIFICATION_BACKEND (g_cocoa_notification_backend_get_type ())
#define G_COCOA_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_COCOA_NOTIFICATION_BACKEND, GCocoaNotificationBackend))
typedef struct _GCocoaNotificationBackend GCocoaNotificationBackend;
typedef GNotificationBackendClass GCocoaNotificationBackendClass;
struct _GCocoaNotificationBackend
{
GNotificationBackend parent;
};
GType g_cocoa_notification_backend_get_type (void);
G_DEFINE_TYPE_WITH_CODE (GCocoaNotificationBackend, g_cocoa_notification_backend, G_TYPE_NOTIFICATION_BACKEND,
_g_io_modules_ensure_extension_points_registered ();
g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa", 200));
static NSString *
nsstring_from_cstr (const char *cstr)
{
if (!cstr)
return nil;
return [[NSString alloc] initWithUTF8String:cstr];
}
static NSImage*
nsimage_from_gicon (GIcon *icon)
{
if (G_IS_FILE_ICON (icon))
{
NSImage *image = nil;
GFile *file;
char *path;
file = g_file_icon_get_file (G_FILE_ICON (icon));
path = g_file_get_path (file);
if (path)
{
NSString *str_path = nsstring_from_cstr (path);
image = [[NSImage alloc] initByReferencingFile:str_path];
[str_path release];
g_free (path);
}
return image;
}
else
{
g_warning ("This icon type is not handled by this NotificationBackend");
return nil;
}
}
static void
activate_detailed_action (const char * action)
{
char *name;
GVariant *target;
if (!g_str_has_prefix (action, "app."))
{
g_warning ("Notification action does not have \"app.\" prefix");
return;
}
if (g_action_parse_detailed_name (action, &name, &target, NULL))
{
g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
g_free (name);
if (target)
g_variant_unref (target);
}
}
@interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
@implementation GNotificationCenterDelegate
-(void) userNotificationCenter:(NSUserNotificationCenter*) center
didActivateNotification:(NSUserNotification*) notification
{
if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
{
const char *action = [[notification userInfo][@"default"] UTF8String];
if (action)
activate_detailed_action (action);
/* OSX Always activates the front window */
}
else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
{
const char *action = [[notification userInfo][@"button0"] UTF8String];
if (action)
activate_detailed_action (action);
}
[center removeDeliveredNotification:notification];
}
@end
static GNotificationCenterDelegate *cocoa_notification_delegate;
static gboolean
g_cocoa_notification_backend_is_supported (void)
{
NSBundle *bundle = [NSBundle mainBundle];
/* This is always actually supported, but without a bundle it does nothing */
if (![bundle bundleIdentifier])
return FALSE;
return TRUE;
}
static void
add_actions_to_notification (NSUserNotification *userNotification,
GNotification *notification)
{
guint n_buttons = g_notification_get_n_buttons (notification);
char *action = NULL, *label = NULL;
GVariant *target = NULL;
NSMutableDictionary *user_info = nil;
if (g_notification_get_default_action (notification, &action, &target))
{
char *detailed_name = g_action_print_detailed_name (action, target);
NSString *action_name = nsstring_from_cstr (detailed_name);
user_info = [[NSMutableDictionary alloc] init];
user_info[@"default"] = action_name;
[action_name release];
g_free (detailed_name);
g_clear_pointer (&action, g_free);
g_clear_pointer (&target, g_variant_unref);
}
if (n_buttons)
{
g_notification_get_button (notification, 0, &label, &action, &target);
if (label)
{
NSString *str_label = nsstring_from_cstr (label);
char *detailed_name = g_action_print_detailed_name (action, target);
NSString *action_name = nsstring_from_cstr (detailed_name);
if (!user_info)
user_info = [[NSMutableDictionary alloc] init];
user_info[@"button0"] = action_name;
userNotification.actionButtonTitle = str_label;
[str_label release];
[action_name release];
g_free (label);
g_free (action);
g_free (detailed_name);
g_clear_pointer (&target, g_variant_unref);
}
if (n_buttons > 1)
g_warning ("Only a single button is currently supported by this NotificationBackend");
}
userNotification.userInfo = user_info;
[user_info release];
}
static void
g_cocoa_notification_backend_send_notification (GNotificationBackend *backend,
const gchar *cstr_id,
GNotification *notification)
{
NSString *str_title = nil, *str_text = nil, *str_id = nil;
NSImage *content = nil;
const char *cstr;
GIcon *icon;
NSUserNotification *userNotification;
NSUserNotificationCenter *center;
if ((cstr = g_notification_get_title (notification)))
str_title = nsstring_from_cstr (cstr);
if ((cstr = g_notification_get_body (notification)))
str_text = nsstring_from_cstr (cstr);
if (cstr_id != NULL)
str_id = nsstring_from_cstr (cstr_id);
if ((icon = g_notification_get_icon (notification)))
content = nsimage_from_gicon (icon);
/* NOTE: There is no priority */
userNotification = [NSUserNotification new];
userNotification.title = str_title;
userNotification.informativeText = str_text;
userNotification.identifier = str_id;
userNotification.contentImage = content;
/* NOTE: Buttons only show up if your bundle has NSUserNotificationAlertStyle set to "alerts" */
add_actions_to_notification (userNotification, notification);
if (!cocoa_notification_delegate)
cocoa_notification_delegate = [[GNotificationCenterDelegate alloc] init];
center = [NSUserNotificationCenter defaultUserNotificationCenter];
center.delegate = cocoa_notification_delegate;
[center deliverNotification:userNotification];
[str_title release];
[str_text release];
[str_id release];
[content release];
[userNotification release];
}
static void
g_cocoa_notification_backend_withdraw_notification (GNotificationBackend *backend,
const gchar *cstr_id)
{
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
NSArray *notifications = [center deliveredNotifications];
NSString *str_id = nsstring_from_cstr (cstr_id);
for (NSUserNotification *notification in notifications)
{
if ([notification.identifier compare:str_id] == NSOrderedSame)
{
[center removeDeliveredNotification:notification];
break;
}
}
[str_id release];
}
static void
g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
{
}
static void
g_cocoa_notification_backend_class_init (GCocoaNotificationBackendClass *klass)
{
GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (klass);
backend_class->is_supported = g_cocoa_notification_backend_is_supported;
backend_class->send_notification = g_cocoa_notification_backend_send_notification;
backend_class->withdraw_notification = g_cocoa_notification_backend_withdraw_notification;
}

442
gio/gcontenttype-win32.c Normal file
View file

@ -0,0 +1,442 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "gcontenttype.h"
#include "gthemedicon.h"
#include "gicon.h"
#include "glibintl.h"
#include <windows.h>
static char *
get_registry_classes_key (const char *subdir,
const wchar_t *key_name)
{
wchar_t *wc_key;
HKEY reg_key = NULL;
DWORD key_type;
DWORD nbytes;
char *value_utf8;
value_utf8 = NULL;
nbytes = 0;
wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS &&
RegQueryValueExW (reg_key, key_name, 0,
&key_type, NULL, &nbytes) == ERROR_SUCCESS &&
(key_type == REG_SZ || key_type == REG_EXPAND_SZ))
{
wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
RegQueryValueExW (reg_key, key_name, 0,
&key_type, (LPBYTE) wc_temp, &nbytes);
wc_temp[nbytes/2] = '\0';
if (key_type == REG_EXPAND_SZ)
{
wchar_t dummy[1];
DWORD len = ExpandEnvironmentStringsW (wc_temp, dummy, 1);
if (len > 0)
{
wchar_t *wc_temp_expanded = g_new (wchar_t, len);
if (ExpandEnvironmentStringsW (wc_temp, wc_temp_expanded, len) == len)
value_utf8 = g_utf16_to_utf8 (wc_temp_expanded, -1, NULL, NULL, NULL);
g_free (wc_temp_expanded);
}
}
else
{
value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
}
g_free (wc_temp);
}
g_free (wc_key);
if (reg_key != NULL)
RegCloseKey (reg_key);
return value_utf8;
}
/*< private >*/
void
g_content_type_set_mime_dirs (const gchar * const *dirs)
{
/* noop on Windows */
}
/*< private >*/
const gchar * const *
g_content_type_get_mime_dirs (void)
{
const gchar * const *mime_dirs = { NULL };
return mime_dirs;
}
gboolean
g_content_type_equals (const gchar *type1,
const gchar *type2)
{
char *progid1, *progid2;
gboolean res;
g_return_val_if_fail (type1 != NULL, FALSE);
g_return_val_if_fail (type2 != NULL, FALSE);
if (g_ascii_strcasecmp (type1, type2) == 0)
return TRUE;
res = FALSE;
progid1 = get_registry_classes_key (type1, NULL);
progid2 = get_registry_classes_key (type2, NULL);
if (progid1 != NULL && progid2 != NULL &&
strcmp (progid1, progid2) == 0)
res = TRUE;
g_free (progid1);
g_free (progid2);
return res;
}
gboolean
g_content_type_is_a (const gchar *type,
const gchar *supertype)
{
gboolean res;
char *perceived_type;
char *perceived_supertype;
g_return_val_if_fail (type != NULL, FALSE);
g_return_val_if_fail (supertype != NULL, FALSE);
if (g_content_type_equals (type, supertype))
return TRUE;
perceived_type = get_registry_classes_key (type, L"PerceivedType");
perceived_supertype = get_registry_classes_key (supertype, L"PerceivedType");
res = perceived_type && perceived_supertype &&
strcmp (perceived_type, perceived_supertype) == 0;
g_free (perceived_type);
g_free (perceived_supertype);
return res;
}
gboolean
g_content_type_is_mime_type (const gchar *type,
const gchar *mime_type)
{
gchar *content_type;
gboolean ret;
g_return_val_if_fail (type != NULL, FALSE);
g_return_val_if_fail (mime_type != NULL, FALSE);
content_type = g_content_type_from_mime_type (mime_type);
ret = g_content_type_is_a (type, content_type);
g_free (content_type);
return ret;
}
gboolean
g_content_type_is_unknown (const gchar *type)
{
g_return_val_if_fail (type != NULL, FALSE);
return strcmp ("*", type) == 0;
}
gchar *
g_content_type_get_description (const gchar *type)
{
char *progid;
char *description;
g_return_val_if_fail (type != NULL, NULL);
progid = get_registry_classes_key (type, NULL);
if (progid)
{
description = get_registry_classes_key (progid, NULL);
g_free (progid);
if (description)
return description;
}
if (g_content_type_is_unknown (type))
return g_strdup (_("Unknown type"));
return g_strdup_printf (_("%s filetype"), type);
}
gchar *
g_content_type_get_mime_type (const gchar *type)
{
char *mime;
g_return_val_if_fail (type != NULL, NULL);
mime = get_registry_classes_key (type, L"Content Type");
if (mime)
return mime;
else if (g_content_type_is_unknown (type))
return g_strdup ("application/octet-stream");
else if (*type == '.')
return g_strdup_printf ("application/x-ext-%s", type+1);
else if (strcmp ("inode/directory", type) == 0)
return g_strdup (type);
/* TODO: Map "image" to "image/ *", etc? */
return g_strdup ("application/octet-stream");
}
G_LOCK_DEFINE_STATIC (_type_icons);
static GHashTable *_type_icons = NULL;
GIcon *
g_content_type_get_icon (const gchar *type)
{
GIcon *themed_icon;
char *name = NULL;
g_return_val_if_fail (type != NULL, NULL);
/* In the Registry icons are the default value of
HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
<type>: <value>
REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
REG_SZ: shimgvw.dll,3
*/
G_LOCK (_type_icons);
if (!_type_icons)
_type_icons = g_hash_table_new (g_str_hash, g_str_equal);
name = g_hash_table_lookup (_type_icons, type);
if (!name && type[0] == '.')
{
/* double lookup by extension */
gchar *key = get_registry_classes_key (type, NULL);
if (!key)
key = g_strconcat (type+1, "file\\DefaultIcon", NULL);
else
{
gchar *key2 = g_strconcat (key, "\\DefaultIcon", NULL);
g_free (key);
key = key2;
}
name = get_registry_classes_key (key, NULL);
if (name && strcmp (name, "%1") == 0)
{
g_free (name);
name = NULL;
}
if (name)
g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
g_free (key);
}
if (!name)
{
/* if no icon found, fall back to standard generic names */
if (strcmp (type, "inode/directory") == 0)
name = "folder";
else if (g_content_type_can_be_executable (type))
name = "system-run";
else
name = "text-x-generic";
g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
}
themed_icon = g_themed_icon_new (name);
G_UNLOCK (_type_icons);
return G_ICON (themed_icon);
}
GIcon *
g_content_type_get_symbolic_icon (const gchar *type)
{
return g_content_type_get_icon (type);
}
gchar *
g_content_type_get_generic_icon_name (const gchar *type)
{
return NULL;
}
gboolean
g_content_type_can_be_executable (const gchar *type)
{
g_return_val_if_fail (type != NULL, FALSE);
if (strcmp (type, ".exe") == 0 ||
strcmp (type, ".com") == 0 ||
strcmp (type, ".bat") == 0)
return TRUE;
/* TODO: Also look at PATHEXT, which lists the extensions for
* "scripts" in addition to those for true binary executables.
*
* (PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH for me
* right now, for instance). And in a sense, all associated file
* types are "executable" on Windows... You can just type foo.jpg as
* a command name in cmd.exe, and it will run the application
* associated with .jpg. Hard to say what this API actually means
* with "executable".
*/
return FALSE;
}
static gboolean
looks_like_text (const guchar *data,
gsize data_size)
{
gsize i;
guchar c;
for (i = 0; i < data_size; i++)
{
c = data[i];
if (g_ascii_iscntrl (c) && !g_ascii_isspace (c) && c != '\b')
return FALSE;
}
return TRUE;
}
gchar *
g_content_type_from_mime_type (const gchar *mime_type)
{
char *key, *content_type;
g_return_val_if_fail (mime_type != NULL, NULL);
/* This is a hack to allow directories to have icons in filechooser */
if (strcmp ("inode/directory", mime_type) == 0)
return g_strdup (mime_type);
key = g_strconcat ("MIME\\DataBase\\Content Type\\", mime_type, NULL);
content_type = get_registry_classes_key (key, L"Extension");
g_free (key);
return content_type ? g_steal_pointer (&content_type) : g_strdup ("*");
}
gchar *
g_content_type_guess (const gchar *filename,
const guchar *data,
gsize data_size,
gboolean *result_uncertain)
{
char *basename;
char *type;
char *dot;
size_t i;
type = NULL;
if (result_uncertain)
*result_uncertain = FALSE;
/* our test suite and potentially other code used -1 in the past, which is
* not documented and not allowed; guard against that */
g_return_val_if_fail (data_size != (gsize) -1, g_strdup ("*"));
if (filename)
{
i = strlen (filename);
if (i > 0 && filename[i - 1] == G_DIR_SEPARATOR)
{
type = g_strdup ("inode/directory");
if (result_uncertain)
*result_uncertain = TRUE;
}
else
{
basename = g_path_get_basename (filename);
dot = strrchr (basename, '.');
if (dot)
type = g_strdup (dot);
g_free (basename);
}
}
if (type)
return type;
if (data && looks_like_text (data, data_size))
return g_strdup (".txt");
return g_strdup ("*");
}
GList *
g_content_types_get_registered (void)
{
DWORD index;
wchar_t keyname[256];
DWORD key_len;
char *key_utf8;
GList *types;
types = NULL;
index = 0;
key_len = 256;
while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
index,
keyname,
&key_len,
NULL,
NULL,
NULL,
NULL) == ERROR_SUCCESS)
{
key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
if (key_utf8)
{
if (*key_utf8 == '.')
types = g_list_prepend (types, key_utf8);
else
g_free (key_utf8);
}
index++;
key_len = 256;
}
return g_list_reverse (types);
}
gchar **
g_content_type_guess_for_tree (GFile *root)
{
/* FIXME: implement */
return NULL;
}

1575
gio/gcontenttype.c Normal file

File diff suppressed because it is too large Load diff

82
gio/gcontenttype.h Normal file
View file

@ -0,0 +1,82 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CONTENT_TYPE_H__
#define __G_CONTENT_TYPE_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
GLIB_AVAILABLE_IN_ALL
gboolean g_content_type_equals (const gchar *type1,
const gchar *type2);
GLIB_AVAILABLE_IN_ALL
gboolean g_content_type_is_a (const gchar *type,
const gchar *supertype);
GLIB_AVAILABLE_IN_2_52
gboolean g_content_type_is_mime_type (const gchar *type,
const gchar *mime_type);
GLIB_AVAILABLE_IN_ALL
gboolean g_content_type_is_unknown (const gchar *type);
GLIB_AVAILABLE_IN_ALL
gchar * g_content_type_get_description (const gchar *type);
GLIB_AVAILABLE_IN_ALL
gchar * g_content_type_get_mime_type (const gchar *type);
GLIB_AVAILABLE_IN_ALL
GIcon * g_content_type_get_icon (const gchar *type);
GLIB_AVAILABLE_IN_2_34
GIcon * g_content_type_get_symbolic_icon (const gchar *type);
GLIB_AVAILABLE_IN_2_34
gchar * g_content_type_get_generic_icon_name (const gchar *type);
GLIB_AVAILABLE_IN_ALL
gboolean g_content_type_can_be_executable (const gchar *type);
GLIB_AVAILABLE_IN_ALL
gchar * g_content_type_from_mime_type (const gchar *mime_type);
GLIB_AVAILABLE_IN_ALL
gchar * g_content_type_guess (const gchar *filename,
const guchar *data,
gsize data_size,
gboolean *result_uncertain);
GLIB_AVAILABLE_IN_ALL
gchar ** g_content_type_guess_for_tree (GFile *root);
GLIB_AVAILABLE_IN_ALL
GList * g_content_types_get_registered (void);
/*< private >*/
#ifndef __GTK_DOC_IGNORE__
GLIB_AVAILABLE_IN_2_60
const gchar * const *g_content_type_get_mime_dirs (void);
GLIB_AVAILABLE_IN_2_60
void g_content_type_set_mime_dirs (const gchar * const *dirs);
#endif /* __GTK_DOC_IGNORE__ */
G_END_DECLS
#endif /* __G_CONTENT_TYPE_H__ */

34
gio/gcontenttypeprivate.h Normal file
View file

@ -0,0 +1,34 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CONTENT_TYPE_PRIVATE_H__
#define __G_CONTENT_TYPE_PRIVATE_H__
#include "gcontenttype.h"
G_BEGIN_DECLS
gsize _g_unix_content_type_get_sniff_len (void);
char * _g_unix_content_type_unalias (const char *type);
char **_g_unix_content_type_get_parents (const char *type);
G_END_DECLS
#endif /* __G_CONTENT_TYPE_PRIVATE_H__ */

274
gio/gcontextspecificgroup.c Normal file
View file

@ -0,0 +1,274 @@
/*
* Copyright © 2015 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gcontextspecificgroup.h"
#include <glib-object.h>
#include "glib-private.h"
typedef struct
{
GSource source;
GMutex lock;
gpointer instance;
GQueue pending;
} GContextSpecificSource;
static gboolean
g_context_specific_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
GContextSpecificSource *css = (GContextSpecificSource *) source;
guint signal_id;
g_mutex_lock (&css->lock);
g_assert (!g_queue_is_empty (&css->pending));
signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending));
if (g_queue_is_empty (&css->pending))
g_source_set_ready_time (source, -1);
g_mutex_unlock (&css->lock);
g_signal_emit (css->instance, signal_id, 0);
return TRUE;
}
static void
g_context_specific_source_finalize (GSource *source)
{
GContextSpecificSource *css = (GContextSpecificSource *) source;
g_mutex_clear (&css->lock);
g_queue_clear (&css->pending);
}
static GContextSpecificSource *
g_context_specific_source_new (const gchar *name,
gpointer instance)
{
static GSourceFuncs source_funcs = {
NULL,
NULL,
g_context_specific_source_dispatch,
g_context_specific_source_finalize,
NULL, NULL
};
GContextSpecificSource *css;
GSource *source;
source = g_source_new (&source_funcs, sizeof (GContextSpecificSource));
css = (GContextSpecificSource *) source;
g_source_set_name (source, name);
g_mutex_init (&css->lock);
g_queue_init (&css->pending);
css->instance = instance;
return css;
}
static gboolean
g_context_specific_group_change_state (gpointer user_data)
{
GContextSpecificGroup *group = user_data;
g_mutex_lock (&group->lock);
if (group->requested_state != group->effective_state)
{
(* group->requested_func) ();
group->effective_state = group->requested_state;
group->requested_func = NULL;
g_cond_broadcast (&group->cond);
}
g_mutex_unlock (&group->lock);
return FALSE;
}
/* this is not the most elegant way to deal with this, but it's probably
* the best. there are only two other things we could do, really:
*
* - run the start function (but not the stop function) from the user's
* thread under some sort of lock. we don't run the stop function
* from the user's thread to avoid the destroy-while-emitting problem
*
* - have some check-and-compare functionality similar to what
* gsettings does where we send an artificial event in case we notice
* a change during the potential race period (using stat, for
* example)
*/
static void
g_context_specific_group_request_state (GContextSpecificGroup *group,
gboolean requested_state,
GCallback requested_func)
{
if (requested_state != group->requested_state)
{
if (group->effective_state != group->requested_state)
{
/* abort the currently pending state transition */
g_assert (group->effective_state == requested_state);
group->requested_state = requested_state;
group->requested_func = NULL;
}
else
{
/* start a new state transition */
group->requested_state = requested_state;
group->requested_func = requested_func;
g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context) (),
g_context_specific_group_change_state, group);
}
}
/* we only block for positive transitions */
if (requested_state)
{
while (group->requested_state != group->effective_state)
g_cond_wait (&group->cond, &group->lock);
/* there is no way this could go back to FALSE because the object
* that we just created in this thread would have to have been
* destroyed again (from this thread) before that could happen.
*/
g_assert (group->effective_state);
}
}
gpointer
g_context_specific_group_get (GContextSpecificGroup *group,
GType type,
goffset context_offset,
GCallback start_func)
{
GContextSpecificSource *css;
GMainContext *context;
context = g_main_context_get_thread_default ();
if (!context)
context = g_main_context_default ();
g_mutex_lock (&group->lock);
if (!group->table)
group->table = g_hash_table_new (NULL, NULL);
css = g_hash_table_lookup (group->table, context);
if (!css)
{
gpointer instance;
instance = g_object_new (type, NULL);
css = g_context_specific_source_new (g_type_name (type), instance);
G_STRUCT_MEMBER (GMainContext *, instance, context_offset) = g_main_context_ref (context);
g_source_attach ((GSource *) css, context);
g_hash_table_insert (group->table, context, css);
}
else
g_object_ref (css->instance);
if (start_func)
g_context_specific_group_request_state (group, TRUE, start_func);
g_mutex_unlock (&group->lock);
return css->instance;
}
void
g_context_specific_group_remove (GContextSpecificGroup *group,
GMainContext *context,
gpointer instance,
GCallback stop_func)
{
GContextSpecificSource *css;
if (!context)
{
g_critical ("Removing %s with NULL context. This object was probably directly constructed from a "
"dynamic language. This is not a valid use of the API.", G_OBJECT_TYPE_NAME (instance));
return;
}
g_mutex_lock (&group->lock);
css = g_hash_table_lookup (group->table, context);
g_hash_table_remove (group->table, context);
g_assert (css);
/* stop only if we were the last one */
if (stop_func && g_hash_table_size (group->table) == 0)
g_context_specific_group_request_state (group, FALSE, stop_func);
g_mutex_unlock (&group->lock);
g_assert (css->instance == instance);
g_source_destroy ((GSource *) css);
g_source_unref ((GSource *) css);
g_main_context_unref (context);
}
void
g_context_specific_group_emit (GContextSpecificGroup *group,
guint signal_id)
{
g_mutex_lock (&group->lock);
if (group->table)
{
GHashTableIter iter;
gpointer value;
gpointer ptr;
ptr = GUINT_TO_POINTER (signal_id);
g_hash_table_iter_init (&iter, group->table);
while (g_hash_table_iter_next (&iter, NULL, &value))
{
GContextSpecificSource *css = value;
g_mutex_lock (&css->lock);
g_queue_remove (&css->pending, ptr);
g_queue_push_tail (&css->pending, ptr);
g_source_set_ready_time ((GSource *) css, 0);
g_mutex_unlock (&css->lock);
}
}
g_mutex_unlock (&group->lock);
}

View file

@ -0,0 +1,51 @@
/*
* Copyright © 2015 Canonical Limited
*
* 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: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_CONTEXT_SPECIFIC_GROUP_H__
#define __G_CONTEXT_SPECIFIC_GROUP_H__
#include <glib-object.h>
typedef struct
{
GHashTable *table;
GMutex lock;
GCond cond;
gboolean requested_state;
GCallback requested_func;
gboolean effective_state;
} GContextSpecificGroup;
gpointer
g_context_specific_group_get (GContextSpecificGroup *group,
GType type,
goffset context_offset,
GCallback start_func);
void
g_context_specific_group_remove (GContextSpecificGroup *group,
GMainContext *context,
gpointer instance,
GCallback stop_func);
void
g_context_specific_group_emit (GContextSpecificGroup *group,
guint signal_id);
#endif /* __G_CONTEXT_SPECIFIC_GROUP_H__ */

201
gio/gconverter.c Normal file
View file

@ -0,0 +1,201 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include "gconverter.h"
#include "glibintl.h"
/**
* SECTION:gconverter
* @short_description: Data conversion interface
* @include: gio/gio.h
* @see_also: #GInputStream, #GOutputStream
*
* #GConverter is implemented by objects that convert
* binary data in various ways. The conversion can be
* stateful and may fail at any place.
*
* Some example conversions are: character set conversion,
* compression, decompression and regular expression
* replace.
*
* Since: 2.24
**/
typedef GConverterIface GConverterInterface;
G_DEFINE_INTERFACE (GConverter, g_converter, G_TYPE_OBJECT)
static void
g_converter_default_init (GConverterInterface *iface)
{
}
/**
* g_converter_convert:
* @converter: a #GConverter.
* @inbuf: (array length=inbuf_size) (element-type guint8): the buffer
* containing the data to convert.
* @inbuf_size: the number of bytes in @inbuf
* @outbuf: (element-type guint8) (array length=outbuf_size): a buffer to write
* converted data in.
* @outbuf_size: the number of bytes in @outbuf, must be at least one
* @flags: a #GConverterFlags controlling the conversion details
* @bytes_read: (out): will be set to the number of bytes read from @inbuf on success
* @bytes_written: (out): will be set to the number of bytes written to @outbuf on success
* @error: location to store the error occurring, or %NULL to ignore
*
* This is the main operation used when converting data. It is to be called
* multiple times in a loop, and each time it will do some work, i.e.
* producing some output (in @outbuf) or consuming some input (from @inbuf) or
* both. If its not possible to do any work an error is returned.
*
* Note that a single call may not consume all input (or any input at all).
* Also a call may produce output even if given no input, due to state stored
* in the converter producing output.
*
* If any data was either produced or consumed, and then an error happens, then
* only the successful conversion is reported and the error is returned on the
* next call.
*
* A full conversion loop involves calling this method repeatedly, each time
* giving it new input and space output space. When there is no more input
* data after the data in @inbuf, the flag %G_CONVERTER_INPUT_AT_END must be set.
* The loop will be (unless some error happens) returning %G_CONVERTER_CONVERTED
* each time until all data is consumed and all output is produced, then
* %G_CONVERTER_FINISHED is returned instead. Note, that %G_CONVERTER_FINISHED
* may be returned even if %G_CONVERTER_INPUT_AT_END is not set, for instance
* in a decompression converter where the end of data is detectable from the
* data (and there might even be other data after the end of the compressed data).
*
* When some data has successfully been converted @bytes_read and is set to
* the number of bytes read from @inbuf, and @bytes_written is set to indicate
* how many bytes was written to @outbuf. If there are more data to output
* or consume (i.e. unless the %G_CONVERTER_INPUT_AT_END is specified) then
* %G_CONVERTER_CONVERTED is returned, and if no more data is to be output
* then %G_CONVERTER_FINISHED is returned.
*
* On error %G_CONVERTER_ERROR is returned and @error is set accordingly.
* Some errors need special handling:
*
* %G_IO_ERROR_NO_SPACE is returned if there is not enough space
* to write the resulting converted data, the application should
* call the function again with a larger @outbuf to continue.
*
* %G_IO_ERROR_PARTIAL_INPUT is returned if there is not enough
* input to fully determine what the conversion should produce,
* and the %G_CONVERTER_INPUT_AT_END flag is not set. This happens for
* example with an incomplete multibyte sequence when converting text,
* or when a regexp matches up to the end of the input (and may match
* further input). It may also happen when @inbuf_size is zero and
* there is no more data to produce.
*
* When this happens the application should read more input and then
* call the function again. If further input shows that there is no
* more data call the function again with the same data but with
* the %G_CONVERTER_INPUT_AT_END flag set. This may cause the conversion
* to finish as e.g. in the regexp match case (or, to fail again with
* %G_IO_ERROR_PARTIAL_INPUT in e.g. a charset conversion where the
* input is actually partial).
*
* After g_converter_convert() has returned %G_CONVERTER_FINISHED the
* converter object is in an invalid state where its not allowed
* to call g_converter_convert() anymore. At this time you can only
* free the object or call g_converter_reset() to reset it to the
* initial state.
*
* If the flag %G_CONVERTER_FLUSH is set then conversion is modified
* to try to write out all internal state to the output. The application
* has to call the function multiple times with the flag set, and when
* the available input has been consumed and all internal state has
* been produced then %G_CONVERTER_FLUSHED (or %G_CONVERTER_FINISHED if
* really at the end) is returned instead of %G_CONVERTER_CONVERTED.
* This is somewhat similar to what happens at the end of the input stream,
* but done in the middle of the data.
*
* This has different meanings for different conversions. For instance
* in a compression converter it would mean that we flush all the
* compression state into output such that if you uncompress the
* compressed data you get back all the input data. Doing this may
* make the final file larger due to padding though. Another example
* is a regexp conversion, where if you at the end of the flushed data
* have a match, but there is also a potential longer match. In the
* non-flushed case we would ask for more input, but when flushing we
* treat this as the end of input and do the match.
*
* Flushing is not always possible (like if a charset converter flushes
* at a partial multibyte sequence). Converters are supposed to try
* to produce as much output as possible and then return an error
* (typically %G_IO_ERROR_PARTIAL_INPUT).
*
* Returns: a #GConverterResult, %G_CONVERTER_ERROR on error.
*
* Since: 2.24
**/
GConverterResult
g_converter_convert (GConverter *converter,
const void *inbuf,
gsize inbuf_size,
void *outbuf,
gsize outbuf_size,
GConverterFlags flags,
gsize *bytes_read,
gsize *bytes_written,
GError **error)
{
GConverterIface *iface;
g_return_val_if_fail (G_IS_CONVERTER (converter), G_CONVERTER_ERROR);
g_return_val_if_fail (outbuf_size > 0, G_CONVERTER_ERROR);
*bytes_read = 0;
*bytes_written = 0;
iface = G_CONVERTER_GET_IFACE (converter);
return (* iface->convert) (converter,
inbuf, inbuf_size,
outbuf, outbuf_size,
flags,
bytes_read, bytes_written, error);
}
/**
* g_converter_reset:
* @converter: a #GConverter.
*
* Resets all internal state in the converter, making it behave
* as if it was just created. If the converter has any internal
* state that would produce output then that output is lost.
*
* Since: 2.24
**/
void
g_converter_reset (GConverter *converter)
{
GConverterIface *iface;
g_return_if_fail (G_IS_CONVERTER (converter));
iface = G_CONVERTER_GET_IFACE (converter);
(* iface->reset) (converter);
}

96
gio/gconverter.h Normal file
View file

@ -0,0 +1,96 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CONVERTER_H__
#define __G_CONVERTER_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_CONVERTER (g_converter_get_type ())
#define G_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_CONVERTER, GConverter))
#define G_IS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_CONVERTER))
#define G_CONVERTER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_CONVERTER, GConverterIface))
/**
* GConverter:
*
* Seek object for streaming operations.
*
* Since: 2.24
**/
typedef struct _GConverterIface GConverterIface;
/**
* GConverterIface:
* @g_iface: The parent interface.
* @convert: Converts data.
* @reset: Reverts the internal state of the converter to its initial state.
*
* Provides an interface for converting data from one type
* to another type. The conversion can be stateful
* and may fail at any place.
*
* Since: 2.24
**/
struct _GConverterIface
{
GTypeInterface g_iface;
/* Virtual Table */
GConverterResult (* convert) (GConverter *converter,
const void *inbuf,
gsize inbuf_size,
void *outbuf,
gsize outbuf_size,
GConverterFlags flags,
gsize *bytes_read,
gsize *bytes_written,
GError **error);
void (* reset) (GConverter *converter);
};
GLIB_AVAILABLE_IN_ALL
GType g_converter_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GConverterResult g_converter_convert (GConverter *converter,
const void *inbuf,
gsize inbuf_size,
void *outbuf,
gsize outbuf_size,
GConverterFlags flags,
gsize *bytes_read,
gsize *bytes_written,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_converter_reset (GConverter *converter);
G_END_DECLS
#endif /* __G_CONVERTER_H__ */

651
gio/gconverterinputstream.c Normal file
View file

@ -0,0 +1,651 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include "gconverterinputstream.h"
#include "gpollableinputstream.h"
#include "gcancellable.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "glibintl.h"
/**
* SECTION:gconverterinputstream
* @short_description: Converter Input Stream
* @include: gio/gio.h
* @see_also: #GInputStream, #GConverter
*
* Converter input stream implements #GInputStream and allows
* conversion of data of various types during reading.
*
* As of GLib 2.34, #GConverterInputStream implements
* #GPollableInputStream.
**/
#define INITIAL_BUFFER_SIZE 4096
typedef struct {
char *data;
gsize start;
gsize end;
gsize size;
} Buffer;
struct _GConverterInputStreamPrivate {
gboolean at_input_end;
gboolean finished;
gboolean need_input;
GConverter *converter;
Buffer input_buffer;
Buffer converted_buffer;
};
enum {
PROP_0,
PROP_CONVERTER
};
static void g_converter_input_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void g_converter_input_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void g_converter_input_stream_finalize (GObject *object);
static gssize g_converter_input_stream_read (GInputStream *stream,
void *buffer,
gsize count,
GCancellable *cancellable,
GError **error);
static gboolean g_converter_input_stream_can_poll (GPollableInputStream *stream);
static gboolean g_converter_input_stream_is_readable (GPollableInputStream *stream);
static gssize g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer,
gsize size,
GError **error);
static GSource *g_converter_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable);
static void g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GConverterInputStream,
g_converter_input_stream,
G_TYPE_FILTER_INPUT_STREAM,
G_ADD_PRIVATE (GConverterInputStream)
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
g_converter_input_stream_pollable_iface_init))
static void
g_converter_input_stream_class_init (GConverterInputStreamClass *klass)
{
GObjectClass *object_class;
GInputStreamClass *istream_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = g_converter_input_stream_get_property;
object_class->set_property = g_converter_input_stream_set_property;
object_class->finalize = g_converter_input_stream_finalize;
istream_class = G_INPUT_STREAM_CLASS (klass);
istream_class->read_fn = g_converter_input_stream_read;
g_object_class_install_property (object_class,
PROP_CONVERTER,
g_param_spec_object ("converter",
P_("Converter"),
P_("The converter object"),
G_TYPE_CONVERTER,
G_PARAM_READWRITE|
G_PARAM_CONSTRUCT_ONLY|
G_PARAM_STATIC_STRINGS));
}
static void
g_converter_input_stream_pollable_iface_init (GPollableInputStreamInterface *iface)
{
iface->can_poll = g_converter_input_stream_can_poll;
iface->is_readable = g_converter_input_stream_is_readable;
iface->read_nonblocking = g_converter_input_stream_read_nonblocking;
iface->create_source = g_converter_input_stream_create_source;
}
static void
g_converter_input_stream_finalize (GObject *object)
{
GConverterInputStreamPrivate *priv;
GConverterInputStream *stream;
stream = G_CONVERTER_INPUT_STREAM (object);
priv = stream->priv;
g_free (priv->input_buffer.data);
g_free (priv->converted_buffer.data);
if (priv->converter)
g_object_unref (priv->converter);
G_OBJECT_CLASS (g_converter_input_stream_parent_class)->finalize (object);
}
static void
g_converter_input_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GConverterInputStream *cstream;
cstream = G_CONVERTER_INPUT_STREAM (object);
switch (prop_id)
{
case PROP_CONVERTER:
cstream->priv->converter = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_converter_input_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GConverterInputStreamPrivate *priv;
GConverterInputStream *cstream;
cstream = G_CONVERTER_INPUT_STREAM (object);
priv = cstream->priv;
switch (prop_id)
{
case PROP_CONVERTER:
g_value_set_object (value, priv->converter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_converter_input_stream_init (GConverterInputStream *stream)
{
stream->priv = g_converter_input_stream_get_instance_private (stream);
}
/**
* g_converter_input_stream_new:
* @base_stream: a #GInputStream
* @converter: a #GConverter
*
* Creates a new converter input stream for the @base_stream.
*
* Returns: a new #GInputStream.
**/
GInputStream *
g_converter_input_stream_new (GInputStream *base_stream,
GConverter *converter)
{
GInputStream *stream;
g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
stream = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
"base-stream", base_stream,
"converter", converter,
NULL);
return stream;
}
static gsize
buffer_data_size (Buffer *buffer)
{
return buffer->end - buffer->start;
}
static gsize
buffer_tailspace (Buffer *buffer)
{
return buffer->size - buffer->end;
}
static char *
buffer_data (Buffer *buffer)
{
return buffer->data + buffer->start;
}
static void
buffer_consumed (Buffer *buffer,
gsize count)
{
buffer->start += count;
if (buffer->start == buffer->end)
buffer->start = buffer->end = 0;
}
static void
buffer_read (Buffer *buffer,
char *dest,
gsize count)
{
if (count != 0)
memcpy (dest, buffer->data + buffer->start, count);
buffer_consumed (buffer, count);
}
static void
compact_buffer (Buffer *buffer)
{
gsize in_buffer;
in_buffer = buffer_data_size (buffer);
memmove (buffer->data,
buffer->data + buffer->start,
in_buffer);
buffer->end -= buffer->start;
buffer->start = 0;
}
static void
grow_buffer (Buffer *buffer)
{
char *data;
gsize size, in_buffer;
if (buffer->size == 0)
size = INITIAL_BUFFER_SIZE;
else
size = buffer->size * 2;
data = g_malloc (size);
in_buffer = buffer_data_size (buffer);
if (in_buffer != 0)
memcpy (data,
buffer->data + buffer->start,
in_buffer);
g_free (buffer->data);
buffer->data = data;
buffer->end -= buffer->start;
buffer->start = 0;
buffer->size = size;
}
/* Ensures that the buffer can fit at_least_size bytes,
* *including* the current in-buffer data */
static void
buffer_ensure_space (Buffer *buffer,
gsize at_least_size)
{
gsize in_buffer, left_to_fill;
in_buffer = buffer_data_size (buffer);
if (in_buffer >= at_least_size)
return;
left_to_fill = buffer_tailspace (buffer);
if (in_buffer + left_to_fill >= at_least_size)
{
/* We fit in remaining space at end */
/* If the copy is small, compact now anyway so we can fill more */
if (in_buffer < 256)
compact_buffer (buffer);
}
else if (buffer->size >= at_least_size)
{
/* We fit, but only if we compact */
compact_buffer (buffer);
}
else
{
/* Need to grow buffer */
while (buffer->size < at_least_size)
grow_buffer (buffer);
}
}
static gssize
fill_input_buffer (GConverterInputStream *stream,
gsize at_least_size,
gboolean blocking,
GCancellable *cancellable,
GError **error)
{
GConverterInputStreamPrivate *priv;
GInputStream *base_stream;
gssize nread;
priv = stream->priv;
buffer_ensure_space (&priv->input_buffer, at_least_size);
base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
nread = g_pollable_stream_read (base_stream,
priv->input_buffer.data + priv->input_buffer.end,
buffer_tailspace (&priv->input_buffer),
blocking,
cancellable,
error);
if (nread > 0)
{
priv->input_buffer.end += nread;
priv->need_input = FALSE;
}
return nread;
}
static gssize
read_internal (GInputStream *stream,
void *buffer,
gsize count,
gboolean blocking,
GCancellable *cancellable,
GError **error)
{
GConverterInputStream *cstream;
GConverterInputStreamPrivate *priv;
gsize available, total_bytes_read;
gssize nread;
GConverterResult res;
gsize bytes_read;
gsize bytes_written;
GError *my_error;
GError *my_error2;
cstream = G_CONVERTER_INPUT_STREAM (stream);
priv = cstream->priv;
available = buffer_data_size (&priv->converted_buffer);
if (available > 0 &&
count <= available)
{
/* Converted data available, return that */
buffer_read (&priv->converted_buffer, buffer, count);
return count;
}
/* Full request not available, read all currently available and request
refill/conversion for more */
buffer_read (&priv->converted_buffer, buffer, available);
total_bytes_read = available;
buffer = (char *) buffer + available;
count -= available;
/* If there is no data to convert, and no pre-converted data,
do some i/o for more input */
if (buffer_data_size (&priv->input_buffer) == 0 &&
total_bytes_read == 0 &&
!priv->at_input_end)
{
nread = fill_input_buffer (cstream, count, blocking, cancellable, error);
if (nread < 0)
return -1;
if (nread == 0)
priv->at_input_end = TRUE;
}
/* First try to convert any available data (or state) directly to the user buffer: */
if (!priv->finished)
{
my_error = NULL;
res = g_converter_convert (priv->converter,
buffer_data (&priv->input_buffer),
buffer_data_size (&priv->input_buffer),
buffer, count,
priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
&bytes_read,
&bytes_written,
&my_error);
if (res != G_CONVERTER_ERROR)
{
total_bytes_read += bytes_written;
buffer_consumed (&priv->input_buffer, bytes_read);
if (res == G_CONVERTER_FINISHED)
priv->finished = TRUE; /* We're done converting */
}
else if (total_bytes_read == 0 &&
!g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_PARTIAL_INPUT) &&
!g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_NO_SPACE))
{
/* No previously read data and no "special" error, return error */
g_propagate_error (error, my_error);
return -1;
}
else
g_error_free (my_error);
}
/* We had some pre-converted data and/or we converted directly to the
user buffer */
if (total_bytes_read > 0)
return total_bytes_read;
/* If there is no more to convert, return EOF */
if (priv->finished)
{
g_assert (buffer_data_size (&priv->converted_buffer) == 0);
return 0;
}
/* There was "complexity" in the straight-to-buffer conversion,
* convert to our own buffer and write from that.
* At this point we didn't produce any data into @buffer.
*/
/* Ensure we have *some* initial target space */
buffer_ensure_space (&priv->converted_buffer, count);
while (TRUE)
{
g_assert (!priv->finished);
/* Try to convert to our buffer */
my_error = NULL;
res = g_converter_convert (priv->converter,
buffer_data (&priv->input_buffer),
buffer_data_size (&priv->input_buffer),
buffer_data (&priv->converted_buffer),
buffer_tailspace (&priv->converted_buffer),
priv->at_input_end ? G_CONVERTER_INPUT_AT_END : 0,
&bytes_read,
&bytes_written,
&my_error);
if (res != G_CONVERTER_ERROR)
{
priv->converted_buffer.end += bytes_written;
buffer_consumed (&priv->input_buffer, bytes_read);
/* Maybe we consumed without producing any output */
if (buffer_data_size (&priv->converted_buffer) == 0 && res != G_CONVERTER_FINISHED)
continue; /* Convert more */
if (res == G_CONVERTER_FINISHED)
priv->finished = TRUE;
total_bytes_read = MIN (count, buffer_data_size (&priv->converted_buffer));
buffer_read (&priv->converted_buffer, buffer, total_bytes_read);
g_assert (priv->finished || total_bytes_read > 0);
return total_bytes_read;
}
/* There was some kind of error filling our buffer */
if (g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_PARTIAL_INPUT) &&
!priv->at_input_end)
{
/* Need more data */
my_error2 = NULL;
nread = fill_input_buffer (cstream,
buffer_data_size (&priv->input_buffer) + 4096,
blocking,
cancellable,
&my_error2);
if (nread < 0)
{
/* Can't read any more data, return that error */
g_error_free (my_error);
g_propagate_error (error, my_error2);
priv->need_input = TRUE;
return -1;
}
else if (nread == 0)
{
/* End of file, try INPUT_AT_END */
priv->at_input_end = TRUE;
}
g_error_free (my_error);
continue;
}
if (g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_NO_SPACE))
{
/* Need more destination space, grow it
* Note: if we actually grow the buffer (as opposed to compacting it),
* this will double the size, not just add one byte. */
buffer_ensure_space (&priv->converted_buffer,
priv->converted_buffer.size + 1);
g_error_free (my_error);
continue;
}
/* Any other random error, return it */
g_propagate_error (error, my_error);
return -1;
}
g_assert_not_reached ();
}
static gssize
g_converter_input_stream_read (GInputStream *stream,
void *buffer,
gsize count,
GCancellable *cancellable,
GError **error)
{
return read_internal (stream, buffer, count, TRUE, cancellable, error);
}
static gboolean
g_converter_input_stream_can_poll (GPollableInputStream *stream)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
return (G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream)));
}
static gboolean
g_converter_input_stream_is_readable (GPollableInputStream *stream)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
GConverterInputStream *cstream = G_CONVERTER_INPUT_STREAM (stream);
if (buffer_data_size (&cstream->priv->converted_buffer))
return TRUE;
else if (buffer_data_size (&cstream->priv->input_buffer) &&
!cstream->priv->need_input)
return TRUE;
else
return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (base_stream));
}
static gssize
g_converter_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer,
gsize count,
GError **error)
{
return read_internal (G_INPUT_STREAM (stream), buffer, count,
FALSE, NULL, error);
}
static GSource *
g_converter_input_stream_create_source (GPollableInputStream *stream,
GCancellable *cancellable)
{
GInputStream *base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
GSource *base_source, *pollable_source;
if (g_pollable_input_stream_is_readable (stream))
base_source = g_timeout_source_new (0);
else
base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (base_stream), NULL);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}
/**
* g_converter_input_stream_get_converter:
* @converter_stream: a #GConverterInputStream
*
* Gets the #GConverter that is used by @converter_stream.
*
* Returns: (transfer none): the converter of the converter input stream
*
* Since: 2.24
*/
GConverter *
g_converter_input_stream_get_converter (GConverterInputStream *converter_stream)
{
return converter_stream->priv->converter;
}

View file

@ -0,0 +1,80 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CONVERTER_INPUT_STREAM_H__
#define __G_CONVERTER_INPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gfilterinputstream.h>
#include <gio/gconverter.h>
G_BEGIN_DECLS
#define G_TYPE_CONVERTER_INPUT_STREAM (g_converter_input_stream_get_type ())
#define G_CONVERTER_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStream))
#define G_CONVERTER_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStreamClass))
#define G_IS_CONVERTER_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CONVERTER_INPUT_STREAM))
#define G_IS_CONVERTER_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CONVERTER_INPUT_STREAM))
#define G_CONVERTER_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CONVERTER_INPUT_STREAM, GConverterInputStreamClass))
/**
* GConverterInputStream:
*
* An implementation of #GFilterInputStream that allows data
* conversion.
**/
typedef struct _GConverterInputStreamClass GConverterInputStreamClass;
typedef struct _GConverterInputStreamPrivate GConverterInputStreamPrivate;
struct _GConverterInputStream
{
GFilterInputStream parent_instance;
/*< private >*/
GConverterInputStreamPrivate *priv;
};
struct _GConverterInputStreamClass
{
GFilterInputStreamClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_converter_input_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GInputStream *g_converter_input_stream_new (GInputStream *base_stream,
GConverter *converter);
GLIB_AVAILABLE_IN_ALL
GConverter *g_converter_input_stream_get_converter (GConverterInputStream *converter_stream);
G_END_DECLS
#endif /* __G_CONVERTER_INPUT_STREAM_H__ */

View file

@ -0,0 +1,689 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include "gconverteroutputstream.h"
#include "gpollableoutputstream.h"
#include "gcancellable.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "glibintl.h"
/**
* SECTION:gconverteroutputstream
* @short_description: Converter Output Stream
* @include: gio/gio.h
* @see_also: #GOutputStream, #GConverter
*
* Converter output stream implements #GOutputStream and allows
* conversion of data of various types during reading.
*
* As of GLib 2.34, #GConverterOutputStream implements
* #GPollableOutputStream.
**/
#define INITIAL_BUFFER_SIZE 4096
typedef struct {
char *data;
gsize start;
gsize end;
gsize size;
} Buffer;
struct _GConverterOutputStreamPrivate {
gboolean at_output_end;
gboolean finished;
GConverter *converter;
Buffer output_buffer; /* To be converted and written */
Buffer converted_buffer; /* Already converted */
};
/* Buffering strategy:
*
* Each time we write we must at least consume some input, or
* return an error. Thus we start with writing all already
* converted data and *then* we start converting (reporting
* an error at any point in this).
*
* Its possible that what the user wrote is not enough data
* for the converter, so we must then buffer it in output_buffer
* and ask for more data, but we want to avoid this as much as
* possible, converting directly from the users buffer.
*/
enum {
PROP_0,
PROP_CONVERTER
};
static void g_converter_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void g_converter_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void g_converter_output_stream_finalize (GObject *object);
static gssize g_converter_output_stream_write (GOutputStream *stream,
const void *buffer,
gsize count,
GCancellable *cancellable,
GError **error);
static gboolean g_converter_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable,
GError **error);
static gboolean g_converter_output_stream_can_poll (GPollableOutputStream *stream);
static gboolean g_converter_output_stream_is_writable (GPollableOutputStream *stream);
static gssize g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
const void *buffer,
gsize size,
GError **error);
static GSource *g_converter_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable);
static void g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GConverterOutputStream,
g_converter_output_stream,
G_TYPE_FILTER_OUTPUT_STREAM,
G_ADD_PRIVATE (GConverterOutputStream)
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
g_converter_output_stream_pollable_iface_init))
static void
g_converter_output_stream_class_init (GConverterOutputStreamClass *klass)
{
GObjectClass *object_class;
GOutputStreamClass *istream_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = g_converter_output_stream_get_property;
object_class->set_property = g_converter_output_stream_set_property;
object_class->finalize = g_converter_output_stream_finalize;
istream_class = G_OUTPUT_STREAM_CLASS (klass);
istream_class->write_fn = g_converter_output_stream_write;
istream_class->flush = g_converter_output_stream_flush;
g_object_class_install_property (object_class,
PROP_CONVERTER,
g_param_spec_object ("converter",
P_("Converter"),
P_("The converter object"),
G_TYPE_CONVERTER,
G_PARAM_READWRITE|
G_PARAM_CONSTRUCT_ONLY|
G_PARAM_STATIC_STRINGS));
}
static void
g_converter_output_stream_pollable_iface_init (GPollableOutputStreamInterface *iface)
{
iface->can_poll = g_converter_output_stream_can_poll;
iface->is_writable = g_converter_output_stream_is_writable;
iface->write_nonblocking = g_converter_output_stream_write_nonblocking;
iface->create_source = g_converter_output_stream_create_source;
}
static void
g_converter_output_stream_finalize (GObject *object)
{
GConverterOutputStreamPrivate *priv;
GConverterOutputStream *stream;
stream = G_CONVERTER_OUTPUT_STREAM (object);
priv = stream->priv;
g_free (priv->output_buffer.data);
g_free (priv->converted_buffer.data);
if (priv->converter)
g_object_unref (priv->converter);
G_OBJECT_CLASS (g_converter_output_stream_parent_class)->finalize (object);
}
static void
g_converter_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GConverterOutputStream *cstream;
cstream = G_CONVERTER_OUTPUT_STREAM (object);
switch (prop_id)
{
case PROP_CONVERTER:
cstream->priv->converter = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_converter_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GConverterOutputStreamPrivate *priv;
GConverterOutputStream *cstream;
cstream = G_CONVERTER_OUTPUT_STREAM (object);
priv = cstream->priv;
switch (prop_id)
{
case PROP_CONVERTER:
g_value_set_object (value, priv->converter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_converter_output_stream_init (GConverterOutputStream *stream)
{
stream->priv = g_converter_output_stream_get_instance_private (stream);
}
/**
* g_converter_output_stream_new:
* @base_stream: a #GOutputStream
* @converter: a #GConverter
*
* Creates a new converter output stream for the @base_stream.
*
* Returns: a new #GOutputStream.
**/
GOutputStream *
g_converter_output_stream_new (GOutputStream *base_stream,
GConverter *converter)
{
GOutputStream *stream;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
stream = g_object_new (G_TYPE_CONVERTER_OUTPUT_STREAM,
"base-stream", base_stream,
"converter", converter,
NULL);
return stream;
}
static gsize
buffer_data_size (Buffer *buffer)
{
return buffer->end - buffer->start;
}
static gsize
buffer_tailspace (Buffer *buffer)
{
return buffer->size - buffer->end;
}
static char *
buffer_data (Buffer *buffer)
{
return buffer->data + buffer->start;
}
static void
buffer_consumed (Buffer *buffer,
gsize count)
{
buffer->start += count;
if (buffer->start == buffer->end)
buffer->start = buffer->end = 0;
}
static void
compact_buffer (Buffer *buffer)
{
gsize in_buffer;
in_buffer = buffer_data_size (buffer);
memmove (buffer->data,
buffer->data + buffer->start,
in_buffer);
buffer->end -= buffer->start;
buffer->start = 0;
}
static void
grow_buffer (Buffer *buffer)
{
char *data;
gsize size, in_buffer;
if (buffer->size == 0)
size = INITIAL_BUFFER_SIZE;
else
size = buffer->size * 2;
data = g_malloc (size);
in_buffer = buffer_data_size (buffer);
if (in_buffer != 0)
memcpy (data,
buffer->data + buffer->start,
in_buffer);
g_free (buffer->data);
buffer->data = data;
buffer->end -= buffer->start;
buffer->start = 0;
buffer->size = size;
}
/* Ensures that the buffer can fit at_least_size bytes,
* *including* the current in-buffer data */
static void
buffer_ensure_space (Buffer *buffer,
gsize at_least_size)
{
gsize in_buffer, left_to_fill;
in_buffer = buffer_data_size (buffer);
if (in_buffer >= at_least_size)
return;
left_to_fill = buffer_tailspace (buffer);
if (in_buffer + left_to_fill >= at_least_size)
{
/* We fit in remaining space at end */
/* If the copy is small, compact now anyway so we can fill more */
if (in_buffer < 256)
compact_buffer (buffer);
}
else if (buffer->size >= at_least_size)
{
/* We fit, but only if we compact */
compact_buffer (buffer);
}
else
{
/* Need to grow buffer */
while (buffer->size < at_least_size)
grow_buffer (buffer);
}
}
static void
buffer_append (Buffer *buffer,
const char *data,
gsize data_size)
{
buffer_ensure_space (buffer,
buffer_data_size (buffer) + data_size);
memcpy (buffer->data + buffer->end, data, data_size);
buffer->end += data_size;
}
static gboolean
flush_buffer (GConverterOutputStream *stream,
gboolean blocking,
GCancellable *cancellable,
GError **error)
{
GConverterOutputStreamPrivate *priv;
GOutputStream *base_stream;
gsize nwritten;
gsize available;
gboolean res;
priv = stream->priv;
base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
available = buffer_data_size (&priv->converted_buffer);
if (available > 0)
{
res = g_pollable_stream_write_all (base_stream,
buffer_data (&priv->converted_buffer),
available,
blocking,
&nwritten,
cancellable,
error);
buffer_consumed (&priv->converted_buffer, nwritten);
return res;
}
return TRUE;
}
static gssize
write_internal (GOutputStream *stream,
const void *buffer,
gsize count,
gboolean blocking,
GCancellable *cancellable,
GError **error)
{
GConverterOutputStream *cstream;
GConverterOutputStreamPrivate *priv;
gssize retval;
GConverterResult res;
gsize bytes_read;
gsize bytes_written;
GError *my_error;
const char *to_convert;
gsize to_convert_size, converted_bytes;
gboolean converting_from_buffer;
cstream = G_CONVERTER_OUTPUT_STREAM (stream);
priv = cstream->priv;
/* Write out all available pre-converted data and fail if
not possible */
if (!flush_buffer (cstream, blocking, cancellable, error))
return -1;
if (priv->finished)
return 0;
/* Convert as much as possible */
if (buffer_data_size (&priv->output_buffer) > 0)
{
converting_from_buffer = TRUE;
buffer_append (&priv->output_buffer, buffer, count);
to_convert = buffer_data (&priv->output_buffer);
to_convert_size = buffer_data_size (&priv->output_buffer);
}
else
{
converting_from_buffer = FALSE;
to_convert = buffer;
to_convert_size = count;
}
/* Ensure we have *some* initial target space */
buffer_ensure_space (&priv->converted_buffer, to_convert_size);
converted_bytes = 0;
while (!priv->finished && converted_bytes < to_convert_size)
{
/* Ensure we have *some* target space */
if (buffer_tailspace (&priv->converted_buffer) == 0)
grow_buffer (&priv->converted_buffer);
/* Try to convert to our buffer */
my_error = NULL;
res = g_converter_convert (priv->converter,
to_convert + converted_bytes,
to_convert_size - converted_bytes,
buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
buffer_tailspace (&priv->converted_buffer),
0,
&bytes_read,
&bytes_written,
&my_error);
if (res != G_CONVERTER_ERROR)
{
priv->converted_buffer.end += bytes_written;
converted_bytes += bytes_read;
if (res == G_CONVERTER_FINISHED)
priv->finished = TRUE;
}
else
{
/* No-space errors can be handled locally: */
if (g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_NO_SPACE))
{
/* Need more destination space, grow it
* Note: if we actually grow the buffer (as opposed to compacting it),
* this will double the size, not just add one byte. */
buffer_ensure_space (&priv->converted_buffer,
priv->converted_buffer.size + 1);
g_error_free (my_error);
continue;
}
if (converted_bytes > 0)
{
/* We got a conversion error, but we did convert some bytes before
that, so handle those before reporting the error */
g_error_free (my_error);
break;
}
if (g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_PARTIAL_INPUT))
{
/* Consume everything to buffer that we append to next time
we write */
if (!converting_from_buffer)
buffer_append (&priv->output_buffer, buffer, count);
/* in the converting_from_buffer case we already appended this */
g_error_free (my_error);
return count; /* consume everything */
}
/* Converted no data and got a normal error, return it */
g_propagate_error (error, my_error);
return -1;
}
}
if (converting_from_buffer)
{
buffer_consumed (&priv->output_buffer, converted_bytes);
retval = count;
}
else
retval = converted_bytes;
/* We now successfully consumed retval bytes, so we can't return an error,
even if writing this to the base stream fails. If it does we'll just
stop early and report this error when we try again on the next
write call. */
flush_buffer (cstream, blocking, cancellable, NULL);
return retval;
}
static gssize
g_converter_output_stream_write (GOutputStream *stream,
const void *buffer,
gsize count,
GCancellable *cancellable,
GError **error)
{
return write_internal (stream, buffer, count, TRUE, cancellable, error);
}
static gboolean
g_converter_output_stream_flush (GOutputStream *stream,
GCancellable *cancellable,
GError **error)
{
GConverterOutputStream *cstream;
GConverterOutputStreamPrivate *priv;
GConverterResult res;
GError *my_error;
gboolean is_closing;
gboolean flushed;
gsize bytes_read;
gsize bytes_written;
cstream = G_CONVERTER_OUTPUT_STREAM (stream);
priv = cstream->priv;
is_closing = g_output_stream_is_closing (stream);
/* Write out all available pre-converted data and fail if
not possible */
if (!flush_buffer (cstream, TRUE, cancellable, error))
return FALSE;
/* Ensure we have *some* initial target space */
buffer_ensure_space (&priv->converted_buffer, 1);
/* Convert whole buffer */
flushed = FALSE;
while (!priv->finished && !flushed)
{
/* Ensure we have *some* target space */
if (buffer_tailspace (&priv->converted_buffer) == 0)
grow_buffer (&priv->converted_buffer);
/* Try to convert to our buffer */
my_error = NULL;
res = g_converter_convert (priv->converter,
buffer_data (&priv->output_buffer),
buffer_data_size (&priv->output_buffer),
buffer_data (&priv->converted_buffer) + buffer_data_size (&priv->converted_buffer),
buffer_tailspace (&priv->converted_buffer),
is_closing ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_FLUSH,
&bytes_read,
&bytes_written,
&my_error);
if (res != G_CONVERTER_ERROR)
{
priv->converted_buffer.end += bytes_written;
buffer_consumed (&priv->output_buffer, bytes_read);
if (res == G_CONVERTER_FINISHED)
priv->finished = TRUE;
if (!is_closing &&
res == G_CONVERTER_FLUSHED)
{
/* Should not have returned FLUSHED with input left */
g_assert (buffer_data_size (&priv->output_buffer) == 0);
flushed = TRUE;
}
}
else
{
/* No-space errors can be handled locally: */
if (g_error_matches (my_error,
G_IO_ERROR,
G_IO_ERROR_NO_SPACE))
{
/* Need more destination space, grow it
* Note: if we actually grow the buffer (as opposed to compacting it),
* this will double the size, not just add one byte. */
buffer_ensure_space (&priv->converted_buffer,
priv->converted_buffer.size + 1);
g_error_free (my_error);
continue;
}
/* Any other error, including PARTIAL_INPUT can't be fixed by now
and is an error */
g_propagate_error (error, my_error);
return FALSE;
}
}
/* Now write all converted data to base stream */
if (!flush_buffer (cstream, TRUE, cancellable, error))
return FALSE;
return TRUE;
}
static gboolean
g_converter_output_stream_can_poll (GPollableOutputStream *stream)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
return (G_IS_POLLABLE_OUTPUT_STREAM (base_stream) &&
g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (base_stream)));
}
static gboolean
g_converter_output_stream_is_writable (GPollableOutputStream *stream)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (base_stream));
}
static gssize
g_converter_output_stream_write_nonblocking (GPollableOutputStream *stream,
const void *buffer,
gsize count,
GError **error)
{
return write_internal (G_OUTPUT_STREAM (stream), buffer, count, FALSE,
NULL, error);
}
static GSource *
g_converter_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable)
{
GOutputStream *base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
GSource *base_source, *pollable_source;
base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (base_stream), NULL);
pollable_source = g_pollable_source_new_full (stream, base_source,
cancellable);
g_source_unref (base_source);
return pollable_source;
}
/**
* g_converter_output_stream_get_converter:
* @converter_stream: a #GConverterOutputStream
*
* Gets the #GConverter that is used by @converter_stream.
*
* Returns: (transfer none): the converter of the converter output stream
*
* Since: 2.24
*/
GConverter *
g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream)
{
return converter_stream->priv->converter;
}

View file

@ -0,0 +1,80 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2009 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_CONVERTER_OUTPUT_STREAM_H__
#define __G_CONVERTER_OUTPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gfilteroutputstream.h>
#include <gio/gconverter.h>
G_BEGIN_DECLS
#define G_TYPE_CONVERTER_OUTPUT_STREAM (g_converter_output_stream_get_type ())
#define G_CONVERTER_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStream))
#define G_CONVERTER_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStreamClass))
#define G_IS_CONVERTER_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CONVERTER_OUTPUT_STREAM))
#define G_IS_CONVERTER_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CONVERTER_OUTPUT_STREAM))
#define G_CONVERTER_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CONVERTER_OUTPUT_STREAM, GConverterOutputStreamClass))
/**
* GConverterOutputStream:
*
* An implementation of #GFilterOutputStream that allows data
* conversion.
**/
typedef struct _GConverterOutputStreamClass GConverterOutputStreamClass;
typedef struct _GConverterOutputStreamPrivate GConverterOutputStreamPrivate;
struct _GConverterOutputStream
{
GFilterOutputStream parent_instance;
/*< private >*/
GConverterOutputStreamPrivate *priv;
};
struct _GConverterOutputStreamClass
{
GFilterOutputStreamClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_converter_output_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GOutputStream *g_converter_output_stream_new (GOutputStream *base_stream,
GConverter *converter);
GLIB_AVAILABLE_IN_ALL
GConverter *g_converter_output_stream_get_converter (GConverterOutputStream *converter_stream);
G_END_DECLS
#endif /* __G_CONVERTER_OUTPUT_STREAM_H__ */

705
gio/gcredentials.c Normal file
View file

@ -0,0 +1,705 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <gobject/gvaluecollector.h>
#include "gcredentials.h"
#include "gcredentialsprivate.h"
#include "gnetworking.h"
#include "gioerror.h"
#include "gioenumtypes.h"
#include "glibintl.h"
/**
* SECTION:gcredentials
* @short_description: An object containing credentials
* @include: gio/gio.h
*
* The #GCredentials type is a reference-counted wrapper for native
* credentials. This information is typically used for identifying,
* authenticating and authorizing other processes.
*
* Some operating systems supports looking up the credentials of the
* remote peer of a communication endpoint - see e.g.
* g_socket_get_credentials().
*
* Some operating systems supports securely sending and receiving
* credentials over a Unix Domain Socket, see
* #GUnixCredentialsMessage, g_unix_connection_send_credentials() and
* g_unix_connection_receive_credentials() for details.
*
* On Linux, the native credential type is a `struct ucred` - see the
* unix(7) man page for details. This corresponds to
* %G_CREDENTIALS_TYPE_LINUX_UCRED.
*
* On Apple operating systems (including iOS, tvOS, and macOS),
* the native credential type is a `struct xucred`.
* This corresponds to %G_CREDENTIALS_TYPE_APPLE_XUCRED.
*
* On FreeBSD, Debian GNU/kFreeBSD, and GNU/Hurd, the native
* credential type is a `struct cmsgcred`. This corresponds
* to %G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED.
*
* On NetBSD, the native credential type is a `struct unpcbid`.
* This corresponds to %G_CREDENTIALS_TYPE_NETBSD_UNPCBID.
*
* On OpenBSD, the native credential type is a `struct sockpeercred`.
* This corresponds to %G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED.
*
* On Solaris (including OpenSolaris and its derivatives), the native
* credential type is a `ucred_t`. This corresponds to
* %G_CREDENTIALS_TYPE_SOLARIS_UCRED.
*
* Since GLib 2.72, on Windows, the native credentials may contain the PID of a
* process. This corresponds to %G_CREDENTIALS_TYPE_WIN32_PID.
*/
/**
* GCredentials:
*
* The #GCredentials structure contains only private data and
* should only be accessed using the provided API.
*
* Since: 2.26
*/
struct _GCredentials
{
/*< private >*/
GObject parent_instance;
#if G_CREDENTIALS_USE_LINUX_UCRED
struct ucred native;
#elif G_CREDENTIALS_USE_APPLE_XUCRED
struct xucred native;
pid_t pid;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
struct cmsgcred native;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
struct unpcbid native;
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
struct sockpeercred native;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
ucred_t *native;
#elif G_CREDENTIALS_USE_WIN32_PID
DWORD native;
#else
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wcpp"
#warning Please add GCredentials support for your OS
#pragma GCC diagnostic pop
#endif
#endif
};
/**
* GCredentialsClass:
*
* Class structure for #GCredentials.
*
* Since: 2.26
*/
struct _GCredentialsClass
{
/*< private >*/
GObjectClass parent_class;
};
G_DEFINE_TYPE (GCredentials, g_credentials, G_TYPE_OBJECT)
static void
g_credentials_finalize (GObject *object)
{
#if G_CREDENTIALS_USE_SOLARIS_UCRED
GCredentials *credentials = G_CREDENTIALS (object);
ucred_free (credentials->native);
#endif
if (G_OBJECT_CLASS (g_credentials_parent_class)->finalize != NULL)
G_OBJECT_CLASS (g_credentials_parent_class)->finalize (object);
}
static void
g_credentials_class_init (GCredentialsClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_credentials_finalize;
}
static void
g_credentials_init (GCredentials *credentials)
{
#if G_CREDENTIALS_USE_LINUX_UCRED
credentials->native.pid = getpid ();
credentials->native.uid = geteuid ();
credentials->native.gid = getegid ();
#elif G_CREDENTIALS_USE_APPLE_XUCRED
gsize i;
credentials->native.cr_version = XUCRED_VERSION;
credentials->native.cr_uid = geteuid ();
credentials->native.cr_ngroups = 1;
credentials->native.cr_groups[0] = getegid ();
/* FIXME: In principle this could use getgroups() to fill in the rest
* of cr_groups, but then we'd have to handle the case where a process
* can have more than NGROUPS groups, if that's even possible. A macOS
* user would have to develop and test this.
*
* For now we fill it with -1 (meaning "no data"). */
for (i = 1; i < NGROUPS; i++)
credentials->native.cr_groups[i] = -1;
credentials->pid = -1;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
memset (&credentials->native, 0, sizeof (struct cmsgcred));
credentials->native.cmcred_pid = getpid ();
credentials->native.cmcred_euid = geteuid ();
credentials->native.cmcred_gid = getegid ();
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
credentials->native.unp_pid = getpid ();
credentials->native.unp_euid = geteuid ();
credentials->native.unp_egid = getegid ();
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
credentials->native.pid = getpid ();
credentials->native.uid = geteuid ();
credentials->native.gid = getegid ();
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
credentials->native = ucred_get (P_MYID);
#elif G_CREDENTIALS_USE_WIN32_PID
credentials->native = GetCurrentProcessId ();
#endif
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_new:
*
* Creates a new #GCredentials object with credentials matching the
* the current process.
*
* Returns: (transfer full): A #GCredentials. Free with g_object_unref().
*
* Since: 2.26
*/
GCredentials *
g_credentials_new (void)
{
return g_object_new (G_TYPE_CREDENTIALS, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_credentials_to_string:
* @credentials: A #GCredentials object.
*
* Creates a human-readable textual representation of @credentials
* that can be used in logging and debug messages. The format of the
* returned string may change in future GLib release.
*
* Returns: (transfer full): A string that should be freed with g_free().
*
* Since: 2.26
*/
gchar *
g_credentials_to_string (GCredentials *credentials)
{
GString *ret;
#if G_CREDENTIALS_USE_APPLE_XUCRED
glib_typeof (credentials->native.cr_ngroups) i;
#endif
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
ret = g_string_new ("GCredentials:");
#if G_CREDENTIALS_USE_LINUX_UCRED
g_string_append (ret, "linux-ucred:");
if (credentials->native.pid != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
if (credentials->native.uid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
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 != (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]);
if (ret->str[ret->len - 1] == ',')
ret->str[ret->len - 1] = '\0';
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
g_string_append (ret, "freebsd-cmsgcred:");
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 != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.cmcred_euid);
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 != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.unp_pid);
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 != (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 != (pid_t) -1)
g_string_append_printf (ret, "pid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.pid);
if (credentials->native.uid != (uid_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) credentials->native.uid);
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_SOLARIS_UCRED
g_string_append (ret, "solaris-ucred:");
{
id_t id;
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)) != (id_t) -1)
g_string_append_printf (ret, "uid=%" G_GINT64_FORMAT ",", (gint64) id);
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';
}
#elif G_CREDENTIALS_USE_WIN32_PID
g_string_append_printf (ret, "win32-pid:pid=%lu", credentials->native);
#else
g_string_append (ret, "unknown");
#endif
return g_string_free (ret, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
#if G_CREDENTIALS_USE_LINUX_UCRED
/*
* Check whether @native contains invalid data. If getsockopt SO_PEERCRED
* is used on a TCP socket, it succeeds but yields a credentials structure
* with pid 0, uid -1 and gid -1. Similarly, if SO_PASSCRED is used on a
* receiving Unix socket when the sending socket did not also enable
* SO_PASSCRED, it can succeed but yield a credentials structure with
* pid 0, uid /proc/sys/kernel/overflowuid and gid
* /proc/sys/kernel/overflowgid.
*/
static gboolean
linux_ucred_check_valid (struct ucred *native,
GError **error)
{
if (native->pid == 0
|| native->uid == (uid_t) -1
|| native->gid == (gid_t) -1)
{
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
_("GCredentials contains invalid data"));
return FALSE;
}
return TRUE;
}
#endif
/**
* g_credentials_is_same_user:
* @credentials: A #GCredentials.
* @other_credentials: A #GCredentials.
* @error: Return location for error or %NULL.
*
* Checks if @credentials and @other_credentials is the same user.
*
* This operation can fail if #GCredentials is not supported on the
* the OS.
*
* Returns: %TRUE if @credentials and @other_credentials has the same
* user, %FALSE otherwise or if @error is set.
*
* Since: 2.26
*/
gboolean
g_credentials_is_same_user (GCredentials *credentials,
GCredentials *other_credentials,
GError **error)
{
gboolean ret;
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
g_return_val_if_fail (G_IS_CREDENTIALS (other_credentials), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
ret = FALSE;
#if G_CREDENTIALS_USE_LINUX_UCRED
if (linux_ucred_check_valid (&credentials->native, NULL)
&& credentials->native.uid == other_credentials->native.uid)
ret = TRUE;
#elif G_CREDENTIALS_USE_APPLE_XUCRED
if (credentials->native.cr_version == other_credentials->native.cr_version &&
credentials->native.cr_uid == other_credentials->native.cr_uid)
ret = TRUE;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
if (credentials->native.cmcred_euid == other_credentials->native.cmcred_euid)
ret = TRUE;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
if (credentials->native.unp_euid == other_credentials->native.unp_euid)
ret = TRUE;
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
if (credentials->native.uid == other_credentials->native.uid)
ret = TRUE;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
if (ucred_geteuid (credentials->native) == ucred_geteuid (other_credentials->native))
ret = TRUE;
#else
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("GCredentials is not implemented on this OS"));
#endif
return ret;
}
static gboolean
credentials_native_type_check (GCredentialsType requested_type,
const char *op)
{
GEnumClass *enum_class;
GEnumValue *requested;
#if G_CREDENTIALS_SUPPORTED
GEnumValue *supported;
#endif
#if G_CREDENTIALS_SUPPORTED
if (requested_type == G_CREDENTIALS_NATIVE_TYPE)
return TRUE;
#endif
enum_class = g_type_class_ref (g_credentials_type_get_type ());
requested = g_enum_get_value (enum_class, requested_type);
#if G_CREDENTIALS_SUPPORTED
supported = g_enum_get_value (enum_class, G_CREDENTIALS_NATIVE_TYPE);
g_assert (supported);
g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
"but only %s is supported on this platform.",
op, op,
requested ? requested->value_name : "(unknown)",
supported->value_name);
#else
g_warning ("g_credentials_%s_native: Trying to %s credentials of type %s "
"but there is no support for GCredentials on this platform.",
op, op,
requested ? requested->value_name : "(unknown)");
#endif
g_type_class_unref (enum_class);
return FALSE;
}
/**
* g_credentials_get_native: (skip)
* @credentials: A #GCredentials.
* @native_type: The type of native credentials to get.
*
* Gets a pointer to native credentials of type @native_type from
* @credentials.
*
* It is a programming error (which will cause a warning to be
* 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: (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
*/
gpointer
g_credentials_get_native (GCredentials *credentials,
GCredentialsType native_type)
{
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), NULL);
if (!credentials_native_type_check (native_type, "get"))
return NULL;
#if G_CREDENTIALS_USE_SOLARIS_UCRED
return credentials->native;
#elif G_CREDENTIALS_SUPPORTED
return &credentials->native;
#else
g_assert_not_reached ();
#endif
}
/**
* g_credentials_set_native:
* @credentials: A #GCredentials.
* @native_type: The type of native credentials to set.
* @native: (not nullable): A pointer to native credentials.
*
* Copies the native credentials of type @native_type from @native
* into @credentials.
*
* It is a programming error (which will cause a warning to be
* logged) to use this method if there is no #GCredentials support for
* the OS or if @native_type isn't supported by the OS.
*
* Since: 2.26
*/
void
g_credentials_set_native (GCredentials *credentials,
GCredentialsType native_type,
gpointer native)
{
if (!credentials_native_type_check (native_type, "set"))
return;
#if G_CREDENTIALS_USE_SOLARIS_UCRED
memcpy (credentials->native, native, ucred_size ());
#elif G_CREDENTIALS_SUPPORTED
memcpy (&credentials->native, native, sizeof (credentials->native));
#else
g_assert_not_reached ();
#endif
}
/* ---------------------------------------------------------------------------------------------------- */
#ifdef G_OS_UNIX
/**
* g_credentials_get_unix_user:
* @credentials: A #GCredentials
* @error: Return location for error or %NULL.
*
* Tries to get the UNIX user identifier from @credentials. This
* method is only available on UNIX platforms.
*
* This operation can fail if #GCredentials is not supported on the
* OS or if the native credentials type does not contain information
* about the UNIX user.
*
* Returns: The UNIX user identifier or `-1` if @error is set.
*
* Since: 2.26
*/
uid_t
g_credentials_get_unix_user (GCredentials *credentials,
GError **error)
{
uid_t ret;
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
#if G_CREDENTIALS_USE_LINUX_UCRED
if (linux_ucred_check_valid (&credentials->native, error))
ret = credentials->native.uid;
else
ret = -1;
#elif G_CREDENTIALS_USE_APPLE_XUCRED
if (credentials->native.cr_version == XUCRED_VERSION)
{
ret = credentials->native.cr_uid;
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
/* No point in translating the part in parentheses... */
"%s (struct xucred cr_version %u != %u)",
_("There is no GCredentials support for your platform"),
credentials->native.cr_version,
XUCRED_VERSION);
ret = -1;
}
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
ret = credentials->native.cmcred_euid;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
ret = credentials->native.unp_euid;
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
ret = credentials->native.uid;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
ret = ucred_geteuid (credentials->native);
#else
ret = -1;
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("There is no GCredentials support for your platform"));
#endif
return ret;
}
/**
* g_credentials_get_unix_pid:
* @credentials: A #GCredentials
* @error: Return location for error or %NULL.
*
* Tries to get the UNIX process identifier from @credentials. This
* method is only available on UNIX platforms.
*
* This operation can fail if #GCredentials is not supported on the
* OS or if the native credentials type does not contain information
* about the UNIX process ID.
*
* Returns: The UNIX process ID, or `-1` if @error is set.
*
* Since: 2.36
*/
pid_t
g_credentials_get_unix_pid (GCredentials *credentials,
GError **error)
{
pid_t ret;
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
#if G_CREDENTIALS_USE_LINUX_UCRED
if (linux_ucred_check_valid (&credentials->native, error))
ret = credentials->native.pid;
else
ret = -1;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
ret = credentials->native.cmcred_pid;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
ret = credentials->native.unp_pid;
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
ret = credentials->native.pid;
#elif G_CREDENTIALS_USE_SOLARIS_UCRED
ret = ucred_getpid (credentials->native);
#elif G_CREDENTIALS_USE_WIN32_PID
ret = credentials->native;
#else
#if G_CREDENTIALS_USE_APPLE_XUCRED
ret = credentials->pid;
#else
ret = -1;
#endif
if (ret == -1)
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("GCredentials does not contain a process ID on this OS"));
#endif
return ret;
}
/**
* g_credentials_set_unix_user:
* @credentials: A #GCredentials.
* @uid: The UNIX user identifier to set.
* @error: Return location for error or %NULL.
*
* Tries to set the UNIX user identifier on @credentials. This method
* is only available on UNIX platforms.
*
* This operation can fail if #GCredentials is not supported on the
* OS or if the native credentials type does not contain information
* about the UNIX user. It can also fail if the OS does not allow the
* use of "spoofed" credentials.
*
* Returns: %TRUE if @uid was set, %FALSE if error is set.
*
* Since: 2.26
*/
gboolean
g_credentials_set_unix_user (GCredentials *credentials,
uid_t uid,
GError **error)
{
gboolean ret = FALSE;
g_return_val_if_fail (G_IS_CREDENTIALS (credentials), FALSE);
g_return_val_if_fail (uid != (uid_t) -1, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
#if G_CREDENTIALS_USE_LINUX_UCRED
credentials->native.uid = uid;
ret = TRUE;
#elif G_CREDENTIALS_USE_APPLE_XUCRED
credentials->native.cr_uid = uid;
ret = TRUE;
#elif G_CREDENTIALS_USE_FREEBSD_CMSGCRED
credentials->native.cmcred_euid = uid;
ret = TRUE;
#elif G_CREDENTIALS_USE_NETBSD_UNPCBID
credentials->native.unp_euid = uid;
ret = TRUE;
#elif G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
credentials->native.uid = uid;
ret = TRUE;
#elif !G_CREDENTIALS_SPOOFING_SUPPORTED
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Credentials spoofing is not possible on this OS"));
ret = FALSE;
#else
g_set_error_literal (error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("GCredentials is not implemented on this OS"));
ret = FALSE;
#endif
return ret;
}
#ifdef __APPLE__
void
_g_credentials_set_local_peerid (GCredentials *credentials,
pid_t pid)
{
g_return_if_fail (G_IS_CREDENTIALS (credentials));
g_return_if_fail (pid >= 0);
credentials->pid = pid;
}
#endif /* __APPLE__ */
#endif /* G_OS_UNIX */

85
gio/gcredentials.h Normal file
View file

@ -0,0 +1,85 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_CREDENTIALS_H__
#define __G_CREDENTIALS_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
#ifdef G_OS_UNIX
/* To get the uid_t type */
#include <unistd.h>
#include <sys/types.h>
#endif
G_BEGIN_DECLS
#define G_TYPE_CREDENTIALS (g_credentials_get_type ())
#define G_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CREDENTIALS, GCredentials))
#define G_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CREDENTIALS, GCredentialsClass))
#define G_CREDENTIALS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CREDENTIALS, GCredentialsClass))
#define G_IS_CREDENTIALS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CREDENTIALS))
#define G_IS_CREDENTIALS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CREDENTIALS))
typedef struct _GCredentialsClass GCredentialsClass;
GLIB_AVAILABLE_IN_ALL
GType g_credentials_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GCredentials *g_credentials_new (void);
GLIB_AVAILABLE_IN_ALL
gchar *g_credentials_to_string (GCredentials *credentials);
GLIB_AVAILABLE_IN_ALL
gpointer g_credentials_get_native (GCredentials *credentials,
GCredentialsType native_type);
GLIB_AVAILABLE_IN_ALL
void g_credentials_set_native (GCredentials *credentials,
GCredentialsType native_type,
gpointer native);
GLIB_AVAILABLE_IN_ALL
gboolean g_credentials_is_same_user (GCredentials *credentials,
GCredentials *other_credentials,
GError **error);
#ifdef G_OS_UNIX
GLIB_AVAILABLE_IN_2_36
pid_t g_credentials_get_unix_pid (GCredentials *credentials,
GError **error);
GLIB_AVAILABLE_IN_ALL
uid_t g_credentials_get_unix_user (GCredentials *credentials,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_credentials_set_unix_user (GCredentials *credentials,
uid_t uid,
GError **error);
#endif
G_END_DECLS
#endif /* __G_CREDENTIALS_H__ */

185
gio/gcredentialsprivate.h Normal file
View file

@ -0,0 +1,185 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __G_CREDENTIALS_PRIVATE_H__
#define __G_CREDENTIALS_PRIVATE_H__
#include "gio/gcredentials.h"
#include "gio/gnetworking.h"
/*
* G_CREDENTIALS_SUPPORTED:
*
* Defined to 1 if GCredentials works.
*/
#undef G_CREDENTIALS_SUPPORTED
/*
* G_CREDENTIALS_USE_LINUX_UCRED, etc.:
*
* Defined to 1 if GCredentials uses Linux `struct ucred`, etc.
*/
#undef G_CREDENTIALS_USE_LINUX_UCRED
#undef G_CREDENTIALS_USE_FREEBSD_CMSGCRED
#undef G_CREDENTIALS_USE_NETBSD_UNPCBID
#undef G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED
#undef G_CREDENTIALS_USE_SOLARIS_UCRED
#undef G_CREDENTIALS_USE_APPLE_XUCRED
#undef G_CREDENTIALS_USE_WIN32_PID
/*
* G_CREDENTIALS_NATIVE_TYPE:
*
* Defined to one of G_CREDENTIALS_TYPE_LINUX_UCRED, etc.
*/
#undef G_CREDENTIALS_NATIVE_TYPE
/*
* G_CREDENTIALS_NATIVE_SIZE:
*
* Defined to the size of the %G_CREDENTIALS_NATIVE_TYPE
*/
#undef G_CREDENTIALS_NATIVE_SIZE
/*
* G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED:
*
* Defined to 1 if we have a message-passing API in which credentials
* are attached to a particular message, such as `SCM_CREDENTIALS` on Linux
* or `SCM_CREDS` on FreeBSD.
*/
#undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
/*
* G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED:
*
* Defined to 1 if we have a `getsockopt()`-style API in which one end of
* a socket connection can directly query the credentials of the process
* that initiated the other end, such as `getsockopt SO_PEERCRED` on Linux
* or `getpeereid()` on multiple operating systems.
*/
#undef G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED
/*
* G_CREDENTIALS_SPOOFING_SUPPORTED:
*
* Defined to 1 if privileged processes can spoof their credentials when
* using the message-passing API.
*/
#undef G_CREDENTIALS_SPOOFING_SUPPORTED
/*
* G_CREDENTIALS_PREFER_MESSAGE_PASSING:
*
* Defined to 1 if the data structure transferred by the message-passing
* API is strictly more informative than the one transferred by the
* `getsockopt()`-style API, and hence should be preferred, even for
* protocols like D-Bus that are defined in terms of the credentials of
* the (process that opened the) socket, as opposed to the credentials
* of an individual message.
*/
#undef G_CREDENTIALS_PREFER_MESSAGE_PASSING
/*
* G_CREDENTIALS_HAS_PID:
*
* Defined to 1 if the %G_CREDENTIALS_NATIVE_TYPE contains the process ID.
*/
#undef G_CREDENTIALS_HAS_PID
#ifdef __linux__
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_LINUX_UCRED 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_LINUX_UCRED
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct ucred))
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 1
#elif defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \
defined(__GNU__) /* GNU Hurd */ || \
defined(__DragonFly__) /* DragonFly BSD */
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_FREEBSD_CMSGCRED 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_FREEBSD_CMSGCRED
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct cmsgcred))
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
/* GLib doesn't implement it yet, but FreeBSD's getsockopt()-style API
* is getpeereid(), which is not as informative as struct cmsgcred -
* it does not tell us the PID. As a result, libdbus prefers to use
* SCM_CREDS, and if we implement getpeereid() in future, we should
* do the same. */
#define G_CREDENTIALS_PREFER_MESSAGE_PASSING 1
#define G_CREDENTIALS_HAS_PID 1
#elif defined(__NetBSD__)
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_NETBSD_UNPCBID 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_NETBSD_UNPCBID
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct unpcbid))
/* #undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED */
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 1
#elif defined(__OpenBSD__)
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_OPENBSD_SOCKPEERCRED 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_OPENBSD_SOCKPEERCRED
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct sockpeercred))
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 1
#elif defined(__sun__) || defined(__illumos__) || defined (__OpenSolaris_kernel__)
#include <ucred.h>
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_SOLARIS_UCRED 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_SOLARIS_UCRED
#define G_CREDENTIALS_NATIVE_SIZE (ucred_size ())
#define G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED 1
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 1
#elif defined(__APPLE__)
#include <sys/ucred.h>
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_APPLE_XUCRED 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_APPLE_XUCRED
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (struct xucred))
#undef G_CREDENTIALS_UNIX_CREDENTIALS_MESSAGE_SUPPORTED
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_SPOOFING_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 0
void _g_credentials_set_local_peerid (GCredentials *credentials,
pid_t pid);
#elif defined(_WIN32)
#define G_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_USE_WIN32_PID 1
#define G_CREDENTIALS_NATIVE_TYPE G_CREDENTIALS_TYPE_WIN32_PID
#define G_CREDENTIALS_NATIVE_SIZE (sizeof (DWORD))
#define G_CREDENTIALS_SOCKET_GET_CREDENTIALS_SUPPORTED 1
#define G_CREDENTIALS_HAS_PID 1
#endif
#endif /* __G_CREDENTIALS_PRIVATE_H__ */

474
gio/gdatagrambased.c Normal file
View file

@ -0,0 +1,474 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright 2015 Collabora Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Philip Withnall <philip.withnall@collabora.co.uk>
*/
#include "config.h"
#include "gdatagrambased.h"
#include "gcancellable.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "gnetworkingprivate.h"
#include "gsocketaddress.h"
#include "glibintl.h"
/**
* SECTION:gdatagrambased
* @short_description: Low-level datagram communications interface
* @include: gio/gio.h
* @see_also: #GSocket, [<gnetworking.h>][gio-gnetworking.h]
*
* A #GDatagramBased is a networking interface for representing datagram-based
* communications. It is a more or less direct mapping of the core parts of the
* BSD socket API in a portable GObject interface. It is implemented by
* #GSocket, which wraps the UNIX socket API on UNIX and winsock2 on Windows.
*
* #GDatagramBased is entirely platform independent, and is intended to be used
* alongside higher-level networking APIs such as #GIOStream.
*
* It uses vectored scatter/gather I/O by default, allowing for many messages
* to be sent or received in a single call. Where possible, implementations of
* the interface should take advantage of vectored I/O to minimise processing
* or system calls. For example, #GSocket uses recvmmsg() and sendmmsg() where
* possible. Callers should take advantage of scatter/gather I/O (the use of
* multiple buffers per message) to avoid unnecessary copying of data to
* assemble or disassemble a message.
*
* Each #GDatagramBased operation has a timeout parameter which may be negative
* for blocking behaviour, zero for non-blocking behaviour, or positive for
* timeout behaviour. A blocking operation blocks until finished or there is an
* error. A non-blocking operation will return immediately with a
* %G_IO_ERROR_WOULD_BLOCK error if it cannot make progress. A timeout operation
* will block until the operation is complete or the timeout expires; if the
* timeout expires it will return what progress it made, or
* %G_IO_ERROR_TIMED_OUT if no progress was made. To know when a call would
* successfully run you can call g_datagram_based_condition_check() or
* g_datagram_based_condition_wait(). You can also use
* g_datagram_based_create_source() and attach it to a #GMainContext to get
* callbacks when I/O is possible.
*
* When running a non-blocking operation applications should always be able to
* handle getting a %G_IO_ERROR_WOULD_BLOCK error even when some other function
* said that I/O was possible. This can easily happen in case of a race
* condition in the application, but it can also happen for other reasons. For
* instance, on Windows a socket is always seen as writable until a write
* returns %G_IO_ERROR_WOULD_BLOCK.
*
* As with #GSocket, #GDatagramBaseds can be either connection oriented (for
* example, SCTP) or connectionless (for example, UDP). #GDatagramBaseds must be
* datagram-based, not stream-based. The interface does not cover connection
* establishment use methods on the underlying type to establish a connection
* before sending and receiving data through the #GDatagramBased API. For
* connectionless socket types the target/source address is specified or
* received in each I/O operation.
*
* Like most other APIs in GLib, #GDatagramBased is not inherently thread safe.
* To use a #GDatagramBased concurrently from multiple threads, you must
* implement your own locking.
*
* Since: 2.48
*/
G_DEFINE_INTERFACE (GDatagramBased, g_datagram_based, G_TYPE_OBJECT)
static void
g_datagram_based_default_init (GDatagramBasedInterface *iface)
{
/* Nothing here. */
}
/**
* g_datagram_based_receive_messages:
* @datagram_based: a #GDatagramBased
* @messages: (array length=num_messages): an array of #GInputMessage structs
* @num_messages: the number of elements in @messages
* @flags: an int containing #GSocketMsgFlags flags for the overall operation
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
* to block indefinitely
* @cancellable: (nullable): a %GCancellable
* @error: return location for a #GError
*
* Receive one or more data messages from @datagram_based in one go.
*
* @messages must point to an array of #GInputMessage structs and
* @num_messages must be the length of this array. Each #GInputMessage
* contains a pointer to an array of #GInputVector structs describing the
* buffers that the data received in each message will be written to.
*
* @flags modify how all messages are received. The commonly available
* arguments for this are available in the #GSocketMsgFlags enum, but the
* values there are the same as the system values, and the flags
* are passed in as-is, so you can pass in system-specific flags too. These
* flags affect the overall receive operation. Flags affecting individual
* messages are returned in #GInputMessage.flags.
*
* The other members of #GInputMessage are treated as described in its
* documentation.
*
* If @timeout is negative the call will block until @num_messages have been
* received, the connection is closed remotely (EOS), @cancellable is cancelled,
* or an error occurs.
*
* If @timeout is 0 the call will return up to @num_messages without blocking,
* or %G_IO_ERROR_WOULD_BLOCK if no messages are queued in the operating system
* to be received.
*
* If @timeout is positive the call will block on the same conditions as if
* @timeout were negative. If the timeout is reached
* before any messages are received, %G_IO_ERROR_TIMED_OUT is returned,
* otherwise it will return the number of messages received before timing out.
* (Note: This is effectively the behaviour of `MSG_WAITFORONE` with
* recvmmsg().)
*
* To be notified when messages are available, wait for the %G_IO_IN condition.
* Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
* g_datagram_based_receive_messages() even if you were previously notified of a
* %G_IO_IN condition.
*
* If the remote peer closes the connection, any messages queued in the
* underlying receive buffer will be returned, and subsequent calls to
* g_datagram_based_receive_messages() will return 0 (with no error set).
*
* If the connection is shut down or closed (by calling g_socket_close() or
* g_socket_shutdown() with @shutdown_read set, if its a #GSocket, for
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
*
* On error -1 is returned and @error is set accordingly. An error will only
* be returned if zero messages could be received; otherwise the number of
* messages successfully received before the error will be returned. If
* @cancellable is cancelled, %G_IO_ERROR_CANCELLED is returned as with any
* other error.
*
* Returns: number of messages received, or -1 on error. Note that the number
* of messages received may be smaller than @num_messages if @timeout is
* zero or positive, if the peer closed the connection, or if @num_messages
* was larger than `UIO_MAXIOV` (1024), in which case the caller may re-try
* to receive the remaining messages.
*
* Since: 2.48
*/
gint
g_datagram_based_receive_messages (GDatagramBased *datagram_based,
GInputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error)
{
GDatagramBasedInterface *iface;
gint retval;
GError *child_error = NULL;
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
g_return_val_if_fail (cancellable == NULL ||
G_IS_CANCELLABLE (cancellable), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
g_assert (iface->receive_messages != NULL);
retval = iface->receive_messages (datagram_based, messages, num_messages,
flags, timeout, cancellable, &child_error);
/* Postconditions. */
g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
g_return_val_if_fail (timeout == 0 ||
!g_error_matches (child_error, G_IO_ERROR,
G_IO_ERROR_WOULD_BLOCK), -1);
g_return_val_if_fail (timeout > 0 ||
!g_error_matches (child_error, G_IO_ERROR,
G_IO_ERROR_TIMED_OUT), -1);
g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
if (child_error != NULL)
g_propagate_error (error, child_error);
return retval;
}
/**
* g_datagram_based_send_messages:
* @datagram_based: a #GDatagramBased
* @messages: (array length=num_messages): an array of #GOutputMessage structs
* @num_messages: the number of elements in @messages
* @flags: an int containing #GSocketMsgFlags flags
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
* to block indefinitely
* @cancellable: (nullable): a %GCancellable
* @error: return location for a #GError
*
* Send one or more data messages from @datagram_based in one go.
*
* @messages must point to an array of #GOutputMessage structs and
* @num_messages must be the length of this array. Each #GOutputMessage
* contains an address to send the data to, and a pointer to an array of
* #GOutputVector structs to describe the buffers that the data to be sent
* for each message will be gathered from.
*
* @flags modify how the message is sent. The commonly available arguments
* for this are available in the #GSocketMsgFlags enum, but the
* values there are the same as the system values, and the flags
* are passed in as-is, so you can pass in system-specific flags too.
*
* The other members of #GOutputMessage are treated as described in its
* documentation.
*
* If @timeout is negative the call will block until @num_messages have been
* sent, @cancellable is cancelled, or an error occurs.
*
* If @timeout is 0 the call will send up to @num_messages without blocking,
* or will return %G_IO_ERROR_WOULD_BLOCK if there is no space to send messages.
*
* If @timeout is positive the call will block on the same conditions as if
* @timeout were negative. If the timeout is reached before any messages are
* sent, %G_IO_ERROR_TIMED_OUT is returned, otherwise it will return the number
* of messages sent before timing out.
*
* To be notified when messages can be sent, wait for the %G_IO_OUT condition.
* Note though that you may still receive %G_IO_ERROR_WOULD_BLOCK from
* g_datagram_based_send_messages() even if you were previously notified of a
* %G_IO_OUT condition. (On Windows in particular, this is very common due to
* the way the underlying APIs work.)
*
* If the connection is shut down or closed (by calling g_socket_close() or
* g_socket_shutdown() with @shutdown_write set, if its a #GSocket, for
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
*
* On error -1 is returned and @error is set accordingly. An error will only
* be returned if zero messages could be sent; otherwise the number of messages
* successfully sent before the error will be returned. If @cancellable is
* cancelled, %G_IO_ERROR_CANCELLED is returned as with any other error.
*
* Returns: number of messages sent, or -1 on error. Note that the number of
* messages sent may be smaller than @num_messages if @timeout is zero
* or positive, or if @num_messages was larger than `UIO_MAXIOV` (1024), in
* which case the caller may re-try to send the remaining messages.
*
* Since: 2.48
*/
gint
g_datagram_based_send_messages (GDatagramBased *datagram_based,
GOutputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error)
{
GDatagramBasedInterface *iface;
gint retval;
GError *child_error = NULL;
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), -1);
g_return_val_if_fail (num_messages == 0 || messages != NULL, -1);
g_return_val_if_fail (cancellable == NULL ||
G_IS_CANCELLABLE (cancellable), -1);
g_return_val_if_fail (error == NULL || *error == NULL, -1);
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
g_assert (iface->send_messages != NULL);
retval = iface->send_messages (datagram_based, messages, num_messages, flags,
timeout, cancellable, &child_error);
/* Postconditions. */
g_return_val_if_fail ((retval < 0) == (child_error != NULL), -1);
g_return_val_if_fail (timeout == 0 ||
!g_error_matches (child_error, G_IO_ERROR,
G_IO_ERROR_WOULD_BLOCK), -1);
g_return_val_if_fail (timeout > 0 ||
!g_error_matches (child_error, G_IO_ERROR,
G_IO_ERROR_TIMED_OUT), -1);
g_return_val_if_fail (retval < 0 || (guint) retval <= num_messages, -1);
g_return_val_if_fail (!(timeout < 0 && num_messages > 0) || retval != 0, -1);
if (child_error != NULL)
g_propagate_error (error, child_error);
return retval;
}
/**
* g_datagram_based_create_source:
* @datagram_based: a #GDatagramBased
* @condition: a #GIOCondition mask to monitor
* @cancellable: (nullable): a #GCancellable
*
* Creates a #GSource that can be attached to a #GMainContext to monitor for
* the availability of the specified @condition on the #GDatagramBased. The
* #GSource keeps a reference to the @datagram_based.
*
* The callback on the source is of the #GDatagramBasedSourceFunc type.
*
* It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
* conditions will always be reported in the callback if they are true.
*
* If non-%NULL, @cancellable can be used to cancel the source, which will
* cause the source to trigger, reporting the current condition (which is
* likely 0 unless cancellation happened at the same time as a condition
* change). You can check for this in the callback using
* g_cancellable_is_cancelled().
*
* Returns: (transfer full): a newly allocated #GSource
*
* Since: 2.48
*/
GSource *
g_datagram_based_create_source (GDatagramBased *datagram_based,
GIOCondition condition,
GCancellable *cancellable)
{
GDatagramBasedInterface *iface;
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), NULL);
g_return_val_if_fail (cancellable == NULL ||
G_IS_CANCELLABLE (cancellable), NULL);
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
g_assert (iface->create_source != NULL);
return iface->create_source (datagram_based, condition, cancellable);
}
/**
* g_datagram_based_condition_check:
* @datagram_based: a #GDatagramBased
* @condition: a #GIOCondition mask to check
*
* Checks on the readiness of @datagram_based to perform operations. The
* operations specified in @condition are checked for and masked against the
* currently-satisfied conditions on @datagram_based. The result is returned.
*
* %G_IO_IN will be set in the return value if data is available to read with
* g_datagram_based_receive_messages(), or if the connection is closed remotely
* (EOS); and if the datagram_based has not been closed locally using some
* implementation-specific method (such as g_socket_close() or
* g_socket_shutdown() with @shutdown_read set, if its a #GSocket).
*
* If the connection is shut down or closed (by calling g_socket_close() or
* g_socket_shutdown() with @shutdown_read set, if its a #GSocket, for
* example), all calls to this function will return %G_IO_ERROR_CLOSED.
*
* %G_IO_OUT will be set if it is expected that at least one byte can be sent
* using g_datagram_based_send_messages() without blocking. It will not be set
* if the datagram_based has been closed locally.
*
* %G_IO_HUP will be set if the connection has been closed locally.
*
* %G_IO_ERR will be set if there was an asynchronous error in transmitting data
* previously enqueued using g_datagram_based_send_messages().
*
* Note that on Windows, it is possible for an operation to return
* %G_IO_ERROR_WOULD_BLOCK even immediately after
* g_datagram_based_condition_check() has claimed that the #GDatagramBased is
* ready for writing. Rather than calling g_datagram_based_condition_check() and
* then writing to the #GDatagramBased if it succeeds, it is generally better to
* simply try writing right away, and try again later if the initial attempt
* returns %G_IO_ERROR_WOULD_BLOCK.
*
* It is meaningless to specify %G_IO_ERR or %G_IO_HUP in @condition; these
* conditions will always be set in the output if they are true. Apart from
* these flags, the output is guaranteed to be masked by @condition.
*
* This call never blocks.
*
* Returns: the #GIOCondition mask of the current state
*
* Since: 2.48
*/
GIOCondition
g_datagram_based_condition_check (GDatagramBased *datagram_based,
GIOCondition condition)
{
GDatagramBasedInterface *iface;
GIOCondition out;
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), 0);
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
g_assert (iface->condition_check != NULL);
out = iface->condition_check (datagram_based, condition);
/* Postconditions. G_IO_OUT and G_IO_HUP are mutually exclusive. G_IO_IN and
* G_IO_HUP are mutually exclusive. The return value must be a subset of
* (condition | G_IO_ERR | G_IO_HUP). */
g_return_val_if_fail ((out & (G_IO_OUT | G_IO_HUP)) != (G_IO_OUT | G_IO_HUP),
out & ~G_IO_OUT);
g_return_val_if_fail ((out & (G_IO_IN | G_IO_HUP)) != (G_IO_IN | G_IO_HUP),
out & ~G_IO_IN);
g_return_val_if_fail ((out & ~(condition | G_IO_ERR | G_IO_HUP)) == 0,
out & (condition | G_IO_ERR | G_IO_HUP));
return out;
}
/**
* g_datagram_based_condition_wait:
* @datagram_based: a #GDatagramBased
* @condition: a #GIOCondition mask to wait for
* @timeout: the maximum time (in microseconds) to wait, 0 to not block, or -1
* to block indefinitely
* @cancellable: (nullable): a #GCancellable
* @error: return location for a #GError
*
* Waits for up to @timeout microseconds for condition to become true on
* @datagram_based. If the condition is met, %TRUE is returned.
*
* If @cancellable is cancelled before the condition is met, or if @timeout is
* reached before the condition is met, then %FALSE is returned and @error is
* set appropriately (%G_IO_ERROR_CANCELLED or %G_IO_ERROR_TIMED_OUT).
*
* Returns: %TRUE if the condition was met, %FALSE otherwise
*
* Since: 2.48
*/
gboolean
g_datagram_based_condition_wait (GDatagramBased *datagram_based,
GIOCondition condition,
gint64 timeout,
GCancellable *cancellable,
GError **error)
{
GDatagramBasedInterface *iface;
gboolean out;
GError *child_error = NULL;
g_return_val_if_fail (G_IS_DATAGRAM_BASED (datagram_based), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable),
FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
iface = G_DATAGRAM_BASED_GET_IFACE (datagram_based);
g_assert (iface->condition_wait != NULL);
out = iface->condition_wait (datagram_based, condition, timeout,
cancellable, &child_error);
/* Postconditions. */
g_return_val_if_fail (out == (child_error == NULL), FALSE);
if (child_error != NULL)
g_propagate_error (error, child_error);
return out;
}

144
gio/gdatagrambased.h Normal file
View file

@ -0,0 +1,144 @@
/*
* Copyright 2015 Collabora Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Philip Withnall <philip.withnall@collabora.co.uk>
*/
#ifndef __G_DATAGRAM_BASED_H__
#define __G_DATAGRAM_BASED_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DATAGRAM_BASED (g_datagram_based_get_type ())
#define G_DATAGRAM_BASED(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_DATAGRAM_BASED, GDatagramBased))
#define G_IS_DATAGRAM_BASED(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_DATAGRAM_BASED))
#define G_DATAGRAM_BASED_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
G_TYPE_DATAGRAM_BASED, \
GDatagramBasedInterface))
#define G_TYPE_IS_DATAGRAM_BASED(type) (g_type_is_a ((type), \
G_TYPE_DATAGRAM_BASED))
/**
* GDatagramBased:
*
* Interface for socket-like objects with datagram semantics.
*
* Since: 2.48
*/
typedef struct _GDatagramBasedInterface GDatagramBasedInterface;
/**
* GDatagramBasedInterface:
* @g_iface: The parent interface.
* @receive_messages: Virtual method for g_datagram_based_receive_messages().
* @send_messages: Virtual method for g_datagram_based_send_messages().
* @create_source: Virtual method for g_datagram_based_create_source().
* @condition_check: Virtual method for g_datagram_based_condition_check().
* @condition_wait: Virtual method for
* g_datagram_based_condition_wait().
*
* Provides an interface for socket-like objects which have datagram semantics,
* following the Berkeley sockets API. The interface methods are thin wrappers
* around the corresponding virtual methods, and no pre-processing of inputs is
* implemented so implementations of this API must handle all functionality
* documented in the interface methods.
*
* Since: 2.48
*/
struct _GDatagramBasedInterface
{
GTypeInterface g_iface;
/* Virtual table */
gint (*receive_messages) (GDatagramBased *datagram_based,
GInputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error);
gint (*send_messages) (GDatagramBased *datagram_based,
GOutputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error);
GSource *(*create_source) (GDatagramBased *datagram_based,
GIOCondition condition,
GCancellable *cancellable);
GIOCondition (*condition_check) (GDatagramBased *datagram_based,
GIOCondition condition);
gboolean (*condition_wait) (GDatagramBased *datagram_based,
GIOCondition condition,
gint64 timeout,
GCancellable *cancellable,
GError **error);
};
GLIB_AVAILABLE_IN_2_48
GType
g_datagram_based_get_type (void);
GLIB_AVAILABLE_IN_2_48
gint
g_datagram_based_receive_messages (GDatagramBased *datagram_based,
GInputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_48
gint
g_datagram_based_send_messages (GDatagramBased *datagram_based,
GOutputMessage *messages,
guint num_messages,
gint flags,
gint64 timeout,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_48
GSource *
g_datagram_based_create_source (GDatagramBased *datagram_based,
GIOCondition condition,
GCancellable *cancellable);
GLIB_AVAILABLE_IN_2_48
GIOCondition
g_datagram_based_condition_check (GDatagramBased *datagram_based,
GIOCondition condition);
GLIB_AVAILABLE_IN_2_48
gboolean
g_datagram_based_condition_wait (GDatagramBased *datagram_based,
GIOCondition condition,
gint64 timeout,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DATAGRAM_BASED_H__ */

1477
gio/gdatainputstream.c Normal file

File diff suppressed because it is too large Load diff

180
gio/gdatainputstream.h Normal file
View file

@ -0,0 +1,180 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_DATA_INPUT_STREAM_H__
#define __G_DATA_INPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gbufferedinputstream.h>
G_BEGIN_DECLS
#define G_TYPE_DATA_INPUT_STREAM (g_data_input_stream_get_type ())
#define G_DATA_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStream))
#define G_DATA_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
#define G_IS_DATA_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_INPUT_STREAM))
#define G_IS_DATA_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_INPUT_STREAM))
#define G_DATA_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
/**
* GDataInputStream:
*
* An implementation of #GBufferedInputStream that allows for high-level
* data manipulation of arbitrary data (including binary operations).
**/
typedef struct _GDataInputStreamClass GDataInputStreamClass;
typedef struct _GDataInputStreamPrivate GDataInputStreamPrivate;
struct _GDataInputStream
{
GBufferedInputStream parent_instance;
/*< private >*/
GDataInputStreamPrivate *priv;
};
struct _GDataInputStreamClass
{
GBufferedInputStreamClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_data_input_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GDataInputStream * g_data_input_stream_new (GInputStream *base_stream);
GLIB_AVAILABLE_IN_ALL
void g_data_input_stream_set_byte_order (GDataInputStream *stream,
GDataStreamByteOrder order);
GLIB_AVAILABLE_IN_ALL
GDataStreamByteOrder g_data_input_stream_get_byte_order (GDataInputStream *stream);
GLIB_AVAILABLE_IN_ALL
void g_data_input_stream_set_newline_type (GDataInputStream *stream,
GDataStreamNewlineType type);
GLIB_AVAILABLE_IN_ALL
GDataStreamNewlineType g_data_input_stream_get_newline_type (GDataInputStream *stream);
GLIB_AVAILABLE_IN_ALL
guchar g_data_input_stream_read_byte (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gint16 g_data_input_stream_read_int16 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
guint16 g_data_input_stream_read_uint16 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gint32 g_data_input_stream_read_int32 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
guint32 g_data_input_stream_read_uint32 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gint64 g_data_input_stream_read_int64 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
guint64 g_data_input_stream_read_uint64 (GDataInputStream *stream,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
char * g_data_input_stream_read_line (GDataInputStream *stream,
gsize *length,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_30
char * g_data_input_stream_read_line_utf8 (GDataInputStream *stream,
gsize *length,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_data_input_stream_read_line_async (GDataInputStream *stream,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
char * g_data_input_stream_read_line_finish (GDataInputStream *stream,
GAsyncResult *result,
gsize *length,
GError **error);
GLIB_AVAILABLE_IN_2_30
char * g_data_input_stream_read_line_finish_utf8(GDataInputStream *stream,
GAsyncResult *result,
gsize *length,
GError **error);
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto)
char * g_data_input_stream_read_until (GDataInputStream *stream,
const gchar *stop_chars,
gsize *length,
GCancellable *cancellable,
GError **error);
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto_async)
void g_data_input_stream_read_until_async (GDataInputStream *stream,
const gchar *stop_chars,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_DEPRECATED_IN_2_56_FOR (g_data_input_stream_read_upto_finish)
char * g_data_input_stream_read_until_finish (GDataInputStream *stream,
GAsyncResult *result,
gsize *length,
GError **error);
GLIB_AVAILABLE_IN_ALL
char * g_data_input_stream_read_upto (GDataInputStream *stream,
const gchar *stop_chars,
gssize stop_chars_len,
gsize *length,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_data_input_stream_read_upto_async (GDataInputStream *stream,
const gchar *stop_chars,
gssize stop_chars_len,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
char * g_data_input_stream_read_upto_finish (GDataInputStream *stream,
GAsyncResult *result,
gsize *length,
GError **error);
G_END_DECLS
#endif /* __G_DATA_INPUT_STREAM_H__ */

598
gio/gdataoutputstream.c Normal file
View file

@ -0,0 +1,598 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include "gdataoutputstream.h"
#include "gseekable.h"
#include "gioenumtypes.h"
#include "gioerror.h"
#include "glibintl.h"
/**
* SECTION:gdataoutputstream
* @short_description: Data Output Stream
* @include: gio/gio.h
* @see_also: #GOutputStream
*
* Data output stream implements #GOutputStream and includes functions for
* writing data directly to an output stream.
*
**/
struct _GDataOutputStreamPrivate {
GDataStreamByteOrder byte_order;
};
enum {
PROP_0,
PROP_BYTE_ORDER
};
static void g_data_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void g_data_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void g_data_output_stream_seekable_iface_init (GSeekableIface *iface);
static goffset g_data_output_stream_tell (GSeekable *seekable);
static gboolean g_data_output_stream_can_seek (GSeekable *seekable);
static gboolean g_data_output_stream_seek (GSeekable *seekable,
goffset offset,
GSeekType type,
GCancellable *cancellable,
GError **error);
static gboolean g_data_output_stream_can_truncate (GSeekable *seekable);
static gboolean g_data_output_stream_truncate (GSeekable *seekable,
goffset offset,
GCancellable *cancellable,
GError **error);
G_DEFINE_TYPE_WITH_CODE (GDataOutputStream,
g_data_output_stream,
G_TYPE_FILTER_OUTPUT_STREAM,
G_ADD_PRIVATE (GDataOutputStream)
G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
g_data_output_stream_seekable_iface_init))
static void
g_data_output_stream_class_init (GDataOutputStreamClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = g_data_output_stream_get_property;
object_class->set_property = g_data_output_stream_set_property;
/**
* GDataOutputStream:byte-order:
*
* Determines the byte ordering that is used when writing
* multi-byte entities (such as integers) to the stream.
*/
g_object_class_install_property (object_class,
PROP_BYTE_ORDER,
g_param_spec_enum ("byte-order",
P_("Byte order"),
P_("The byte order"),
G_TYPE_DATA_STREAM_BYTE_ORDER,
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN,
G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_BLURB));
}
static void
g_data_output_stream_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GDataOutputStream *dstream;
dstream = G_DATA_OUTPUT_STREAM (object);
switch (prop_id)
{
case PROP_BYTE_ORDER:
g_data_output_stream_set_byte_order (dstream, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_data_output_stream_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GDataOutputStreamPrivate *priv;
GDataOutputStream *dstream;
dstream = G_DATA_OUTPUT_STREAM (object);
priv = dstream->priv;
switch (prop_id)
{
case PROP_BYTE_ORDER:
g_value_set_enum (value, priv->byte_order);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
g_data_output_stream_init (GDataOutputStream *stream)
{
stream->priv = g_data_output_stream_get_instance_private (stream);
stream->priv->byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
}
static void
g_data_output_stream_seekable_iface_init (GSeekableIface *iface)
{
iface->tell = g_data_output_stream_tell;
iface->can_seek = g_data_output_stream_can_seek;
iface->seek = g_data_output_stream_seek;
iface->can_truncate = g_data_output_stream_can_truncate;
iface->truncate_fn = g_data_output_stream_truncate;
}
/**
* g_data_output_stream_new:
* @base_stream: a #GOutputStream.
*
* Creates a new data output stream for @base_stream.
*
* Returns: #GDataOutputStream.
**/
GDataOutputStream *
g_data_output_stream_new (GOutputStream *base_stream)
{
GDataOutputStream *stream;
g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
stream = g_object_new (G_TYPE_DATA_OUTPUT_STREAM,
"base-stream", base_stream,
NULL);
return stream;
}
/**
* g_data_output_stream_set_byte_order:
* @stream: a #GDataOutputStream.
* @order: a %GDataStreamByteOrder.
*
* Sets the byte order of the data output stream to @order.
**/
void
g_data_output_stream_set_byte_order (GDataOutputStream *stream,
GDataStreamByteOrder order)
{
GDataOutputStreamPrivate *priv;
g_return_if_fail (G_IS_DATA_OUTPUT_STREAM (stream));
priv = stream->priv;
if (priv->byte_order != order)
{
priv->byte_order = order;
g_object_notify (G_OBJECT (stream), "byte-order");
}
}
/**
* g_data_output_stream_get_byte_order:
* @stream: a #GDataOutputStream.
*
* Gets the byte order for the stream.
*
* Returns: the #GDataStreamByteOrder for the @stream.
**/
GDataStreamByteOrder
g_data_output_stream_get_byte_order (GDataOutputStream *stream)
{
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
return stream->priv->byte_order;
}
/**
* g_data_output_stream_put_byte:
* @stream: a #GDataOutputStream.
* @data: a #guchar.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts a byte into the output stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_byte (GDataOutputStream *stream,
guchar data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 1,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_int16:
* @stream: a #GDataOutputStream.
* @data: a #gint16.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts a signed 16-bit integer into the output stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_int16 (GDataOutputStream *stream,
gint16 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GINT16_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GINT16_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 2,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_uint16:
* @stream: a #GDataOutputStream.
* @data: a #guint16.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts an unsigned 16-bit integer into the output stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_uint16 (GDataOutputStream *stream,
guint16 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GUINT16_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GUINT16_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 2,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_int32:
* @stream: a #GDataOutputStream.
* @data: a #gint32.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts a signed 32-bit integer into the output stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_int32 (GDataOutputStream *stream,
gint32 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GINT32_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GINT32_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 4,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_uint32:
* @stream: a #GDataOutputStream.
* @data: a #guint32.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts an unsigned 32-bit integer into the stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_uint32 (GDataOutputStream *stream,
guint32 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GUINT32_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GUINT32_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 4,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_int64:
* @stream: a #GDataOutputStream.
* @data: a #gint64.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts a signed 64-bit integer into the stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_int64 (GDataOutputStream *stream,
gint64 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GINT64_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GINT64_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 8,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_uint64:
* @stream: a #GDataOutputStream.
* @data: a #guint64.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts an unsigned 64-bit integer into the stream.
*
* Returns: %TRUE if @data was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_uint64 (GDataOutputStream *stream,
guint64 data,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
switch (stream->priv->byte_order)
{
case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
data = GUINT64_TO_BE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
data = GUINT64_TO_LE (data);
break;
case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
default:
break;
}
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
&data, 8,
&bytes_written,
cancellable, error);
}
/**
* g_data_output_stream_put_string:
* @stream: a #GDataOutputStream.
* @str: a string.
* @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
* @error: a #GError, %NULL to ignore.
*
* Puts a string into the output stream.
*
* Returns: %TRUE if @string was successfully added to the @stream.
**/
gboolean
g_data_output_stream_put_string (GDataOutputStream *stream,
const char *str,
GCancellable *cancellable,
GError **error)
{
gsize bytes_written;
g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
g_return_val_if_fail (str != NULL, FALSE);
return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
str, strlen (str),
&bytes_written,
cancellable, error);
}
static goffset
g_data_output_stream_tell (GSeekable *seekable)
{
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
return 0;
base_stream_seekable = G_SEEKABLE (base_stream);
return g_seekable_tell (base_stream_seekable);
}
static gboolean
g_data_output_stream_can_seek (GSeekable *seekable)
{
GOutputStream *base_stream;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
return G_IS_SEEKABLE (base_stream) && g_seekable_can_seek (G_SEEKABLE (base_stream));
}
static gboolean
g_data_output_stream_seek (GSeekable *seekable,
goffset offset,
GSeekType type,
GCancellable *cancellable,
GError **error)
{
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Seek not supported on base stream"));
return FALSE;
}
base_stream_seekable = G_SEEKABLE (base_stream);
return g_seekable_seek (base_stream_seekable, offset, type, cancellable, error);
}
static gboolean
g_data_output_stream_can_truncate (GSeekable *seekable)
{
GOutputStream *base_stream;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
return G_IS_SEEKABLE (base_stream) && g_seekable_can_truncate (G_SEEKABLE (base_stream));
}
static gboolean
g_data_output_stream_truncate (GSeekable *seekable,
goffset offset,
GCancellable *cancellable,
GError **error)
{
GOutputStream *base_stream;
GSeekable *base_stream_seekable;
base_stream = G_FILTER_OUTPUT_STREAM (seekable)->base_stream;
if (!G_IS_SEEKABLE (base_stream))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Truncate not supported on base stream"));
return FALSE;
}
base_stream_seekable = G_SEEKABLE (base_stream);
return g_seekable_truncate (base_stream_seekable, offset, cancellable, error);
}

125
gio/gdataoutputstream.h Normal file
View file

@ -0,0 +1,125 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2006-2007 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Larsson <alexl@redhat.com>
*/
#ifndef __G_DATA_OUTPUT_STREAM_H__
#define __G_DATA_OUTPUT_STREAM_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/gfilteroutputstream.h>
G_BEGIN_DECLS
#define G_TYPE_DATA_OUTPUT_STREAM (g_data_output_stream_get_type ())
#define G_DATA_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStream))
#define G_DATA_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
#define G_IS_DATA_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_OUTPUT_STREAM))
#define G_IS_DATA_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_OUTPUT_STREAM))
#define G_DATA_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
/**
* GDataOutputStream:
*
* An implementation of #GBufferedOutputStream that allows for high-level
* data manipulation of arbitrary data (including binary operations).
**/
typedef struct _GDataOutputStream GDataOutputStream;
typedef struct _GDataOutputStreamClass GDataOutputStreamClass;
typedef struct _GDataOutputStreamPrivate GDataOutputStreamPrivate;
struct _GDataOutputStream
{
GFilterOutputStream parent_instance;
/*< private >*/
GDataOutputStreamPrivate *priv;
};
struct _GDataOutputStreamClass
{
GFilterOutputStreamClass parent_class;
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
};
GLIB_AVAILABLE_IN_ALL
GType g_data_output_stream_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GDataOutputStream * g_data_output_stream_new (GOutputStream *base_stream);
GLIB_AVAILABLE_IN_ALL
void g_data_output_stream_set_byte_order (GDataOutputStream *stream,
GDataStreamByteOrder order);
GLIB_AVAILABLE_IN_ALL
GDataStreamByteOrder g_data_output_stream_get_byte_order (GDataOutputStream *stream);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_byte (GDataOutputStream *stream,
guchar data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_int16 (GDataOutputStream *stream,
gint16 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_uint16 (GDataOutputStream *stream,
guint16 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_int32 (GDataOutputStream *stream,
gint32 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_uint32 (GDataOutputStream *stream,
guint32 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_int64 (GDataOutputStream *stream,
gint64 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_uint64 (GDataOutputStream *stream,
guint64 data,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_data_output_stream_put_string (GDataOutputStream *stream,
const char *str,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DATA_OUTPUT_STREAM_H__ */

View file

@ -0,0 +1,4 @@
[flake8]
# We are generating long lines through templates
max-line-length = 120
exclude = __pycache__

3
gio/gdbus-2.0/codegen/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.pyc
config.py
gdbus-codegen

View file

@ -0,0 +1,29 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import os
builddir = os.environ.get("UNINSTALLED_GLIB_BUILDDIR")
if builddir is not None:
__path__.append(
os.path.abspath(os.path.join(builddir, "gio", "gdbus-2.0", "codegen"))
)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,480 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import re
from os import path
from . import utils
# ----------------------------------------------------------------------------------------------------
class DocbookCodeGenerator:
def __init__(self, ifaces):
self.ifaces = ifaces
self.generate_expand_dicts()
def print_method_prototype(self, i, m, in_synopsis):
max_method_len = 0
if in_synopsis:
for _m in i.methods:
max_method_len = max(len(_m.name), max_method_len)
else:
max_method_len = max(len(m.name), max_method_len)
max_signature_len = 0
if in_synopsis:
for _m in i.methods:
for a in _m.in_args:
max_signature_len = max(len(a.signature), max_signature_len)
for a in _m.out_args:
max_signature_len = max(len(a.signature), max_signature_len)
else:
for a in m.in_args:
max_signature_len = max(len(a.signature), max_signature_len)
for a in m.out_args:
max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis:
self.out.write(
'<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
% (
utils.dots_to_hyphens(i.name),
m.name,
m.name,
max_method_len - len(m.name),
"",
)
)
else:
self.out.write("%s%*s (" % (m.name, max_method_len - len(m.name), ""))
count = 0
for a in m.in_args:
if count > 0:
self.out.write(",\n%*s" % (max_method_len + 2, ""))
self.out.write(
"IN %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
for a in m.out_args:
if count > 0:
self.out.write(",\n%*s" % (max_method_len + 2, ""))
self.out.write(
"OUT %s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
self.out.write(");\n")
def print_signal_prototype(self, i, s, in_synopsis):
max_signal_len = 0
if in_synopsis:
for _s in i.signals:
max_signal_len = max(len(_s.name), max_signal_len)
else:
max_signal_len = max(len(s.name), max_signal_len)
max_signature_len = 0
if in_synopsis:
for _s in i.signals:
for a in _s.args:
max_signature_len = max(len(a.signature), max_signature_len)
else:
for a in s.args:
max_signature_len = max(len(a.signature), max_signature_len)
if in_synopsis:
self.out.write(
'<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
% (
utils.dots_to_hyphens(i.name),
s.name,
s.name,
max_signal_len - len(s.name),
"",
)
)
else:
self.out.write("%s%*s (" % (s.name, max_signal_len - len(s.name), ""))
count = 0
for a in s.args:
if count > 0:
self.out.write(",\n%*s" % (max_signal_len + 2, ""))
self.out.write(
"%s%*s %s"
% (a.signature, max_signature_len - len(a.signature), "", a.name)
)
count = count + 1
self.out.write(");\n")
def print_property_prototype(self, i, p, in_synopsis):
max_property_len = 0
if in_synopsis:
for _p in i.properties:
max_property_len = max(len(_p.name), max_property_len)
else:
max_property_len = max(len(p.name), max_property_len)
max_signature_len = 0
if in_synopsis:
for _p in i.properties:
max_signature_len = max(len(_p.signature), max_signature_len)
else:
max_signature_len = max(len(p.signature), max_signature_len)
if in_synopsis:
self.out.write(
'<link linkend="gdbus-property-%s.%s">%s</link>%*s'
% (
utils.dots_to_hyphens(i.name),
p.name,
p.name,
max_property_len - len(p.name),
"",
)
)
else:
self.out.write("%s%*s" % (p.name, max_property_len - len(p.name), ""))
if p.readable and p.writable:
access = "readwrite"
elif p.readable:
access = "readable "
else:
access = "writable "
self.out.write(" %s %s\n" % (access, p.signature))
def print_synopsis_methods(self, i):
self.out.write(' <refsynopsisdiv role="synopsis">\n')
self.out.write(' <title role="synopsis.title">Methods</title>\n')
self.out.write(" <synopsis>\n")
for m in i.methods:
self.print_method_prototype(i, m, in_synopsis=True)
self.out.write("</synopsis>\n")
self.out.write(" </refsynopsisdiv>\n")
def print_synopsis_signals(self, i):
self.out.write(' <refsect1 role="signal_proto">\n')
self.out.write(' <title role="signal_proto.title">Signals</title>\n')
self.out.write(" <synopsis>\n")
for s in i.signals:
self.print_signal_prototype(i, s, in_synopsis=True)
self.out.write("</synopsis>\n")
self.out.write(" </refsect1>\n")
def print_synopsis_properties(self, i):
self.out.write(' <refsect1 role="properties">\n')
self.out.write(' <title role="properties.title">Properties</title>\n')
self.out.write(" <synopsis>\n")
for p in i.properties:
self.print_property_prototype(i, p, in_synopsis=True)
self.out.write("</synopsis>\n")
self.out.write(" </refsect1>\n")
def print_method(self, i, m):
self.out.write(
'<refsect2 role="method" id="gdbus-method-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), m.name)
)
self.out.write(" <title>The %s() method</title>\n" % (m.name))
self.out.write(
' <indexterm zone="gdbus-method-%s.%s"><primary sortas="%s.%s">%s.%s()</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
m.name,
i.name_without_prefix,
m.name,
i.name,
m.name,
)
)
self.out.write("<programlisting>\n")
self.print_method_prototype(i, m, in_synopsis=False)
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(m.doc_string, True)))
if m.in_args or m.out_args:
self.out.write('<variablelist role="params">\n')
for a in m.in_args:
self.out.write("<varlistentry>\n")
self.out.write(
" <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n")
for a in m.out_args:
self.out.write("<varlistentry>\n")
self.out.write(
" <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n")
self.out.write("</variablelist>\n")
if len(m.since) > 0:
self.out.write('<para role="since">Since %s</para>\n' % (m.since))
if m.deprecated:
self.out.write(
"<warning><para>The %s() method is deprecated.</para></warning>"
% (m.name)
)
self.out.write("</refsect2>\n")
def print_signal(self, i, s):
self.out.write(
'<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), s.name)
)
self.out.write(' <title>The "%s" signal</title>\n' % (s.name))
self.out.write(
' <indexterm zone="gdbus-signal-%s.%s"><primary sortas="%s::%s">%s::%s</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
s.name,
i.name_without_prefix,
s.name,
i.name,
s.name,
)
)
self.out.write("<programlisting>\n")
self.print_signal_prototype(i, s, in_synopsis=False)
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(s.doc_string, True)))
if s.args:
self.out.write('<variablelist role="params">\n')
for a in s.args:
self.out.write("<varlistentry>\n")
self.out.write(
" <term><literal>%s <parameter>%s</parameter></literal>:</term>\n"
% (a.signature, a.name)
)
self.out.write(
" <listitem>%s</listitem>\n"
% (self.expand_paras(a.doc_string, True))
)
self.out.write("</varlistentry>\n")
self.out.write("</variablelist>\n")
if len(s.since) > 0:
self.out.write('<para role="since">Since %s</para>\n' % (s.since))
if s.deprecated:
self.out.write(
'<warning><para>The "%s" signal is deprecated.</para></warning>'
% (s.name)
)
self.out.write("</refsect2>\n")
def print_property(self, i, p):
self.out.write(
'<refsect2 role="property" id="gdbus-property-%s.%s">\n'
% (utils.dots_to_hyphens(i.name), p.name)
)
self.out.write(' <title>The "%s" property</title>\n' % (p.name))
self.out.write(
' <indexterm zone="gdbus-property-%s.%s"><primary sortas="%s:%s">%s:%s</primary></indexterm>\n'
% (
utils.dots_to_hyphens(i.name),
p.name,
i.name_without_prefix,
p.name,
i.name,
p.name,
)
)
self.out.write("<programlisting>\n")
self.print_property_prototype(i, p, in_synopsis=False)
self.out.write("</programlisting>\n")
self.out.write("%s\n" % (self.expand_paras(p.doc_string, True)))
if len(p.since) > 0:
self.out.write('<para role="since">Since %s</para>\n' % (p.since))
if p.deprecated:
self.out.write(
'<warning><para>The "%s" property is deprecated.</para></warning>'
% (p.name)
)
self.out.write("</refsect2>\n")
def expand(self, s, expandParamsAndConstants):
for key in self.expand_member_dict_keys:
s = s.replace(key, self.expand_member_dict[key])
for key in self.expand_iface_dict_keys:
s = s.replace(key, self.expand_iface_dict[key])
if expandParamsAndConstants:
# replace @foo with <parameter>foo</parameter>
s = re.sub(
"@[a-zA-Z0-9_]*",
lambda m: "<parameter>" + m.group(0)[1:] + "</parameter>",
s,
)
# replace e.g. %TRUE with <constant>TRUE</constant>
s = re.sub(
"%[a-zA-Z0-9_]*",
lambda m: "<constant>" + m.group(0)[1:] + "</constant>",
s,
)
return s
def expand_paras(self, s, expandParamsAndConstants):
s = self.expand(s, expandParamsAndConstants).strip()
res = []
if not s.startswith("<para>"):
res.append("<para>")
for line in s.split("\n"):
line = line.strip()
if not line:
line = "</para><para>"
res.append(line)
if not s.endswith("</para>"):
res.append("</para>")
return "\n".join(res)
def generate_expand_dicts(self):
self.expand_member_dict = {}
self.expand_iface_dict = {}
for i in self.ifaces:
key = "#%s" % (i.name)
value = '<link linkend="gdbus-interface-%s.top_of_page">%s</link>' % (
utils.dots_to_hyphens(i.name),
i.name,
)
self.expand_iface_dict[key] = value
for m in i.methods:
key = "%s.%s()" % (i.name, m.name)
value = '<link linkend="gdbus-method-%s.%s">%s()</link>' % (
utils.dots_to_hyphens(i.name),
m.name,
m.name,
)
self.expand_member_dict[key] = value
for s in i.signals:
key = "#%s::%s" % (i.name, s.name)
value = '<link linkend="gdbus-signal-%s.%s">"%s"</link>' % (
utils.dots_to_hyphens(i.name),
s.name,
s.name,
)
self.expand_member_dict[key] = value
for p in i.properties:
key = "#%s:%s" % (i.name, p.name)
value = '<link linkend="gdbus-property-%s.%s">"%s"</link>' % (
utils.dots_to_hyphens(i.name),
p.name,
p.name,
)
self.expand_member_dict[key] = value
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
# is evaluated before #org.foo.Iface:Media ...
self.expand_member_dict_keys = sorted(
self.expand_member_dict.keys(), reverse=True
)
self.expand_iface_dict_keys = sorted(
self.expand_iface_dict.keys(), reverse=True
)
def generate(self, docbook, outdir):
for i in self.ifaces:
self.out = open(path.join(outdir, "%s-%s.xml" % (docbook, i.name)), "w")
self.out.write("")
self.out.write('<?xml version="1.0" encoding="utf-8"?>\n')
self.out.write(
'<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'
)
self.out.write(
' "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'
)
self.out.write("]>\n")
self.out.write('<refentry id="gdbus-%s">\n' % (i.name))
self.out.write(" <refmeta>")
self.out.write(
' <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'
% (utils.dots_to_hyphens(i.name), i.name)
)
self.out.write(
' <indexterm zone="gdbus-interface-%s.top_of_page"><primary sortas="%s">%s</primary></indexterm>\n'
% (utils.dots_to_hyphens(i.name), i.name_without_prefix, i.name)
)
self.out.write(" </refmeta>")
self.out.write(" <refnamediv>")
self.out.write(" <refname>%s</refname>" % (i.name))
self.out.write(" <refpurpose>%s</refpurpose>" % (i.doc_string_brief))
self.out.write(" </refnamediv>")
if len(i.methods) > 0:
self.print_synopsis_methods(i)
if len(i.signals) > 0:
self.print_synopsis_signals(i)
if len(i.properties) > 0:
self.print_synopsis_properties(i)
self.out.write(
'<refsect1 role="desc" id="gdbus-interface-%s">\n'
% (utils.dots_to_hyphens(i.name))
)
self.out.write(' <title role="desc.title">Description</title>\n')
self.out.write(" %s\n" % (self.expand_paras(i.doc_string, True)))
if len(i.since) > 0:
self.out.write(' <para role="since">Since %s</para>\n' % (i.since))
if i.deprecated:
self.out.write(
"<warning><para>The %s interface is deprecated.</para></warning>"
% (i.name)
)
self.out.write("</refsect1>\n")
if len(i.methods) > 0:
self.out.write(
'<refsect1 role="details" id="gdbus-methods-%s">\n' % (i.name)
)
self.out.write(' <title role="details.title">Method Details</title>\n')
for m in i.methods:
self.print_method(i, m)
self.out.write("</refsect1>\n")
if len(i.signals) > 0:
self.out.write(
'<refsect1 role="details" id="gdbus-signals-%s">\n' % (i.name)
)
self.out.write(' <title role="details.title">Signal Details</title>\n')
for s in i.signals:
self.print_signal(i, s)
self.out.write("</refsect1>\n")
if len(i.properties) > 0:
self.out.write(
'<refsect1 role="details" id="gdbus-properties-%s">\n' % (i.name)
)
self.out.write(
' <title role="details.title">Property Details</title>\n'
)
for s in i.properties:
self.print_property(i, s)
self.out.write("</refsect1>\n")
self.out.write("</refentry>\n")
self.out.write("\n")

View file

@ -0,0 +1,500 @@
# -*- Mode: Python -*-
# coding=utf-8
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
# Copyright (C) 2018 Iñigo Martínez <inigomartinez@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/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import argparse
import os
import sys
from . import config
from . import dbustypes
from . import parser
from . import codegen
from . import codegen_docbook
from . import codegen_rst
from .utils import print_error, print_warning
def find_arg(arg_list, arg_name):
for a in arg_list:
if a.name == arg_name:
return a
return None
def find_method(iface, method):
for m in iface.methods:
if m.name == method:
return m
return None
def find_signal(iface, signal):
for m in iface.signals:
if m.name == signal:
return m
return None
def find_prop(iface, prop):
for m in iface.properties:
if m.name == prop:
return m
return None
def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
iface_obj = None
for i in iface_list:
if i.name == iface:
iface_obj = i
break
if iface_obj is None:
print_error('No interface "{}"'.format(iface))
target_obj = None
if method:
method_obj = find_method(iface_obj, method)
if method_obj is None:
print_error('No method "{}" on interface "{}"'.format(method, iface))
if arg:
arg_obj = find_arg(method_obj.in_args, arg)
if arg_obj is None:
arg_obj = find_arg(method_obj.out_args, arg)
if arg_obj is None:
print_error(
'No arg "{}" on method "{}" on interface "{}"'.format(
arg, method, iface
)
)
target_obj = arg_obj
else:
target_obj = method_obj
elif signal:
signal_obj = find_signal(iface_obj, signal)
if signal_obj is None:
print_error('No signal "{}" on interface "{}"'.format(signal, iface))
if arg:
arg_obj = find_arg(signal_obj.args, arg)
if arg_obj is None:
print_error(
'No arg "{}" on signal "{}" on interface "{}"'.format(
arg, signal, iface
)
)
target_obj = arg_obj
else:
target_obj = signal_obj
elif prop:
prop_obj = find_prop(iface_obj, prop)
if prop_obj is None:
print_error('No property "{}" on interface "{}"'.format(prop, iface))
target_obj = prop_obj
else:
target_obj = iface_obj
target_obj.annotations.insert(0, dbustypes.Annotation(key, value))
def apply_annotations(iface_list, annotation_list):
# apply annotations given on the command line
for (what, key, value) in annotation_list:
pos = what.find("::")
if pos != -1:
# signal
iface = what[0:pos]
signal = what[pos + 2 :]
pos = signal.find("[")
if pos != -1:
arg = signal[pos + 1 :]
signal = signal[0:pos]
pos = arg.find("]")
arg = arg[0:pos]
apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
else:
apply_annotation(
iface_list, iface, None, signal, None, None, key, value
)
else:
pos = what.find(":")
if pos != -1:
# property
iface = what[0:pos]
prop = what[pos + 1 :]
apply_annotation(iface_list, iface, None, None, prop, None, key, value)
else:
pos = what.find("()")
if pos != -1:
# method
combined = what[0:pos]
pos = combined.rfind(".")
iface = combined[0:pos]
method = combined[pos + 1 :]
pos = what.find("[")
if pos != -1:
arg = what[pos + 1 :]
pos = arg.find("]")
arg = arg[0:pos]
apply_annotation(
iface_list, iface, method, None, None, arg, key, value
)
else:
apply_annotation(
iface_list, iface, method, None, None, None, key, value
)
else:
# must be an interface
iface = what
apply_annotation(
iface_list, iface, None, None, None, None, key, value
)
def codegen_main():
arg_parser = argparse.ArgumentParser(
description="D-Bus code and documentation generator"
)
arg_parser.add_argument(
"files", metavar="FILE", nargs="+", help="D-Bus introspection XML file"
)
arg_parser.add_argument(
"--xml-files",
metavar="FILE",
action="append",
default=[],
help=argparse.SUPPRESS,
)
arg_parser.add_argument(
"--interface-prefix",
metavar="PREFIX",
default="",
help="String to strip from D-Bus interface names for code and docs",
)
arg_parser.add_argument(
"--c-namespace",
metavar="NAMESPACE",
default="",
help="The namespace to use for generated C code",
)
arg_parser.add_argument(
"--c-generate-object-manager",
action="store_true",
help="Generate a GDBusObjectManagerClient subclass when generating C code",
)
arg_parser.add_argument(
"--c-generate-autocleanup",
choices=["none", "objects", "all"],
default="objects",
help="Generate autocleanup support",
)
arg_parser.add_argument(
"--generate-docbook",
metavar="OUTFILES",
help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
)
arg_parser.add_argument(
"--generate-rst",
metavar="OUTFILES",
help="Generate reStructuredText in OUTFILES-org.Project.IFace.rst",
)
arg_parser.add_argument(
"--pragma-once",
action="store_true",
help='Use "pragma once" as the inclusion guard',
)
arg_parser.add_argument(
"--annotate",
nargs=3,
action="append",
metavar="WHAT KEY VALUE",
help="Add annotation (may be used several times)",
)
arg_parser.add_argument(
"--glib-min-required",
metavar="VERSION",
help="Minimum version of GLib to be supported by the outputted code "
"(default: 2.30)",
)
arg_parser.add_argument(
"--glib-max-allowed",
metavar="VERSION",
help="Maximum version of GLib to be used by the outputted code "
"(default: current GLib version)",
)
arg_parser.add_argument(
"--symbol-decorator",
help="Macro used to decorate a symbol in the outputted header, "
"possibly to export symbols",
)
arg_parser.add_argument(
"--symbol-decorator-header",
help="Additional header required for decorator specified by "
"--symbol-decorator",
)
arg_parser.add_argument(
"--symbol-decorator-define",
help="Additional define required for decorator specified by "
"--symbol-decorator",
)
group = arg_parser.add_mutually_exclusive_group()
group.add_argument(
"--generate-c-code", metavar="OUTFILES", help="Generate C code in OUTFILES.[ch]"
)
group.add_argument("--header", action="store_true", help="Generate C headers")
group.add_argument("--body", action="store_true", help="Generate C code")
group.add_argument(
"--interface-info-header",
action="store_true",
help="Generate GDBusInterfaceInfo C header",
)
group.add_argument(
"--interface-info-body",
action="store_true",
help="Generate GDBusInterfaceInfo C code",
)
group = arg_parser.add_mutually_exclusive_group()
group.add_argument(
"--output", metavar="FILE", help="Write output into the specified file"
)
group.add_argument(
"--output-directory",
metavar="OUTDIR",
default="",
help="Location to output generated files",
)
args = arg_parser.parse_args()
if len(args.xml_files) > 0:
print_warning(
'The "--xml-files" option is deprecated; use positional arguments instead'
)
if (
args.generate_c_code is not None
or args.generate_docbook is not None
or args.generate_rst is not None
) and args.output is not None:
print_error(
"Using --generate-c-code or --generate-docbook or --generate-rst and "
"--output at the same time is not allowed"
)
if args.generate_c_code:
header_name = args.generate_c_code + ".h"
h_file = os.path.join(args.output_directory, header_name)
args.header = True
c_file = os.path.join(args.output_directory, args.generate_c_code + ".c")
args.body = True
elif args.header:
if args.output is None:
print_error("Using --header requires --output")
h_file = args.output
header_name = os.path.basename(h_file)
elif args.body:
if args.output is None:
print_error("Using --body requires --output")
c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
elif args.interface_info_header:
if args.output is None:
print_error("Using --interface-info-header requires --output")
if args.c_generate_object_manager:
print_error(
"--c-generate-object-manager is incompatible with "
"--interface-info-header"
)
h_file = args.output
header_name = os.path.basename(h_file)
elif args.interface_info_body:
if args.output is None:
print_error("Using --interface-info-body requires --output")
if args.c_generate_object_manager:
print_error(
"--c-generate-object-manager is incompatible with "
"--interface-info-body"
)
c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
# Check the minimum GLib version. The minimum --glib-min-required is 2.30,
# because thats when gdbus-codegen was introduced. Support 1, 2 or 3
# component versions, but ignore the micro component if its present.
if args.glib_min_required:
try:
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) # noqa: F841
except (ValueError, IndexError):
print_error(
"Unrecognized --glib-min-required string {}".format(
args.glib_min_required
)
)
if glib_min_required < (2, 30):
print_error(
"Invalid --glib-min-required string {}: minimum "
"version is 2.30".format(args.glib_min_required)
)
else:
glib_min_required = (2, 30)
# And the maximum GLib version.
if args.glib_max_allowed:
try:
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) # noqa: F841
except (ValueError, IndexError):
print_error(
"Unrecognized --glib-max-allowed string {}".format(
args.glib_max_allowed
)
)
else:
glib_max_allowed = (config.MAJOR_VERSION, config.MINOR_VERSION)
# Only allow --symbol-decorator-define and --symbol-decorator-header if
# --symbol-decorator is used
if args.symbol_decorator is None:
if args.symbol_decorator_header or args.symbol_decorator_define:
print_error(
"--symbol-decorator-define and --symbol-decorator-header must "
"be used with --symbol-decorator"
)
# Round --glib-max-allowed up to the next stable release.
glib_max_allowed = (
glib_max_allowed[0],
glib_max_allowed[1] + (glib_max_allowed[1] % 2),
)
if glib_max_allowed < glib_min_required:
print_error(
"Invalid versions: --glib-min-required ({}) must be "
"less than or equal to --glib-max-allowed ({})".format(
glib_min_required, glib_max_allowed
)
)
all_ifaces = []
input_files_basenames = []
for fname in sorted(args.files + args.xml_files):
with open(fname, "rb") as f:
xml_data = f.read()
parsed_ifaces = parser.parse_dbus_xml(
xml_data, h_type_implies_unix_fd=(glib_min_required >= (2, 64))
)
all_ifaces.extend(parsed_ifaces)
input_files_basenames.append(os.path.basename(fname))
if args.annotate is not None:
apply_annotations(all_ifaces, args.annotate)
for i in all_ifaces:
i.post_process(args.interface_prefix, args.c_namespace)
docbook = args.generate_docbook
docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces)
if docbook:
docbook_gen.generate(docbook, args.output_directory)
rst = args.generate_rst
rst_gen = codegen_rst.RstCodeGenerator(all_ifaces)
if rst:
rst_gen.generate(rst, args.output_directory)
if args.header:
with open(h_file, "w") as outfile:
gen = codegen.HeaderCodeGenerator(
all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
args.c_generate_autocleanup,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile,
)
gen.generate()
if args.body:
with open(c_file, "w") as outfile:
gen = codegen.CodeGenerator(
all_ifaces,
args.c_namespace,
args.c_generate_object_manager,
header_name,
input_files_basenames,
docbook_gen,
glib_min_required,
args.symbol_decorator_define,
outfile,
)
gen.generate()
if args.interface_info_header:
with open(h_file, "w") as outfile:
gen = codegen.InterfaceInfoHeaderCodeGenerator(
all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
args.pragma_once,
glib_min_required,
args.symbol_decorator,
args.symbol_decorator_header,
outfile,
)
gen.generate()
if args.interface_info_body:
with open(c_file, "w") as outfile:
gen = codegen.InterfaceInfoBodyCodeGenerator(
all_ifaces,
args.c_namespace,
header_name,
input_files_basenames,
glib_min_required,
args.symbol_decorator_define,
outfile,
)
gen.generate()
sys.exit(0)
if __name__ == "__main__":
codegen_main()

View file

@ -0,0 +1,332 @@
# SPDX-FileCopyrightText: 2022 Emmanuele Bassi
#
# SPDX-License-Identifier: LGPL-2.1-or-later
import os
import re
from . import utils
# Disable line length warnings as wrapping the templates would be hard
# flake8: noqa: E501
class RstCodeGenerator:
"""Generates documentation in reStructuredText format."""
def __init__(self, ifaces):
self.ifaces = ifaces
self._generate_expand_dicts()
def _expand(self, s, expandParamsAndConstants):
"""Expands parameters and constant literals."""
res = []
for line in s.split("\n"):
line = line.strip()
if line == "":
res.append("")
continue
for key in self._expand_member_dict_keys:
line = line.replace(key, self._expand_member_dict[key])
for key in self._expand_iface_dict_keys:
line = line.replace(key, self._expand_iface_dict[key])
if expandParamsAndConstants:
# replace @foo with ``foo``
line = re.sub(
"@[a-zA-Z0-9_]*",
lambda m: "``" + m.group(0)[1:] + "``",
line,
)
# replace e.g. %TRUE with ``TRUE``
line = re.sub(
"%[a-zA-Z0-9_]*",
lambda m: "``" + m.group(0)[1:] + "``",
line,
)
res.append(line)
return "\n".join(res)
def _generate_expand_dicts(self):
"""Generates the dictionaries used to expand gtk-doc sigils."""
self._expand_member_dict = {}
self._expand_iface_dict = {}
for i in self.ifaces:
key = f"#{i.name}"
value = f"`{i.name}`_"
self._expand_iface_dict[key] = value
for m in i.methods:
key = "%s.%s()" % (i.name, m.name)
value = f"`{i.name}.{m.name}`_"
self._expand_member_dict[key] = value
for s in i.signals:
key = "#%s::%s" % (i.name, s.name)
value = f"`{i.name}::{s.name}`_"
self._expand_member_dict[key] = value
for p in i.properties:
key = "#%s:%s" % (i.name, p.name)
value = f"`{i.name}:{p.name}`_"
self._expand_member_dict[key] = value
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
# is evaluated before #org.foo.Iface:Media ...
self._expand_member_dict_keys = sorted(
self._expand_member_dict.keys(), reverse=True
)
self._expand_iface_dict_keys = sorted(
self._expand_iface_dict.keys(), reverse=True
)
def _generate_header(self, iface):
"""Generates the header and preamble of the document."""
header_len = len(iface.name)
res = [
f".. _{iface.name}:",
"",
"=" * header_len,
iface.name,
"=" * header_len,
"",
"-----------",
"Description",
"-----------",
"",
f".. _{iface.name} Description:",
"",
iface.doc_string_brief.strip(),
"",
self._expand(iface.doc_string, True),
"",
]
if iface.since:
res += [
f"Interface available since: {iface.since}.",
"",
]
if iface.deprecated:
res += [
".. warning::",
"",
" This interface is deprecated.",
"",
"",
]
res += [""]
return "\n".join(res)
def _generate_section(self, title, name):
"""Generates a section with the given title."""
res = [
"-" * len(title),
title,
"-" * len(title),
"",
f".. {name} {title}:",
"",
"",
]
return "\n".join(res)
def _generate_properties(self, iface):
"""Generates the properties section."""
res = []
for p in iface.properties:
title = f"{iface.name}:{p.name}"
if p.readable and p.writable:
access = "readwrite"
elif p.writable:
access = "writable"
else:
access = "readable"
res += [
title,
"^" * len(title),
"",
"::",
"",
f" {p.name} {access} {p.signature}",
"",
"",
self._expand(p.doc_string, True),
"",
]
if p.since:
res += [
f"Property available since: {p.since}.",
"",
]
if p.deprecated:
res += [
".. warning::",
"",
" This property is deprecated.",
"",
"",
]
res += [""]
return "\n".join(res)
def _generate_method_signature(self, method):
"""Generates the method signature as a code block."""
res = [
"::",
"",
]
n_in_args = len(method.in_args)
n_out_args = len(method.out_args)
if n_in_args == 0 and n_out_args == 0:
res += [
f" {method.name} ()",
]
else:
res += [
f" {method.name} (",
]
for idx, arg in enumerate(method.in_args):
if idx == n_in_args - 1 and n_out_args == 0:
res += [
f" IN {arg.name} {arg.signature}",
]
else:
res += [
f" IN {arg.name} {arg.signature},",
]
for idx, arg in enumerate(method.out_args):
if idx == n_out_args - 1:
res += [
f" OUT {arg.name} {arg.signature}",
]
else:
res += [
f" OUT {arg.name} {arg.signature},",
]
res += [
" )",
"",
]
res += [""]
return "\n".join(res)
def _generate_methods(self, iface):
"""Generates the methods section."""
res = []
for m in iface.methods:
title = f"{iface.name}.{m.name}"
res += [
title,
"^" * len(title),
"",
self._generate_method_signature(m),
"",
self._expand(m.doc_string, True),
"",
]
for a in m.in_args:
arg_desc = self._expand(a.doc_string, True)
res += [
f"{a.name}",
f" {arg_desc}",
"",
]
res += [""]
if m.since:
res += [
f"Method available since: {m.since}.",
"",
]
if m.deprecated:
res += [
".. warning::",
"",
" This method is deprecated.",
"",
"",
]
res += [""]
return "\n".join(res)
def _generate_signal_signature(self, signal):
"""Generates the signal signature."""
res = [
"::",
"",
]
n_args = len(signal.args)
if n_args == 0:
res += [
f" {signal.name} ()",
]
else:
res += [
f" {signal.name} (",
]
for idx, arg in enumerate(signal.args):
if idx == n_args - 1:
res += [
f" {arg.name} {arg.signature}",
]
else:
res += [
f" {arg.name} {arg.signature},",
]
res += [
" )",
"",
]
res += [""]
return "\n".join(res)
def _generate_signals(self, iface):
"""Generates the signals section."""
res = []
for s in iface.signals:
title = f"{iface.name}::{s.name}"
res += [
title,
"^" * len(title),
"",
self._generate_signal_signature(s),
"",
self._expand(s.doc_string, True),
"",
]
for a in s.args:
arg_desc = self._expand(a.doc_string, True)
res += [
f"{a.name}",
f" {arg_desc}",
"",
]
res += [""]
if s.since:
res += [
f"Signal available since: {s.since}.",
"",
]
if s.deprecated:
res += [
".. warning::",
"",
" This signal is deprecated.",
"",
"",
]
res += [""]
return "\n".join(res)
def generate(self, rst, outdir):
"""Generates the reStructuredText file for each interface."""
for i in self.ifaces:
with open(os.path.join(outdir, f"{rst}-{i.name}.rst"), "w") as outfile:
outfile.write(self._generate_header(i))
if len(i.properties) > 0:
outfile.write(self._generate_section("Properties", i.name))
outfile.write(self._generate_properties(i))
if len(i.methods) > 0:
outfile.write(self._generate_section("Methods", i.name))
outfile.write(self._generate_methods(i))
if len(i.signals) > 0:
outfile.write(self._generate_section("Signals", i.name))
outfile.write(self._generate_signals(i))

View file

@ -0,0 +1,24 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
VERSION = "@VERSION@"
MAJOR_VERSION = @MAJOR_VERSION@
MINOR_VERSION = @MINOR_VERSION@

View file

@ -0,0 +1,525 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
from . import utils
from .utils import print_error
class Annotation:
def __init__(self, key, value):
self.key = key
self.value = value
self.annotations = []
self.since = ""
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, container):
key = self.key
overridden_key = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_key):
self.key_lower = overridden_key.lower()
else:
if overridden_key:
key = overridden_key
self.key_lower = (
utils.camel_case_to_uscore(key)
.lower()
.replace("-", "_")
.replace(".", "_")
)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if len(self.since) == 0:
self.since = container.since
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Arg:
def __init__(self, name, signature):
self.name = name
self.signature = signature
self.annotations = []
self.doc_string = ""
self.since = ""
def post_process(self, interface_prefix, cns, cns_upper, cns_lower, arg_number):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if self.name is None:
self.name = "unnamed_arg%d" % arg_number
# default to GVariant
self.ctype_in_g = "GVariant *"
self.ctype_in = "GVariant *"
self.ctype_in_dup = "GVariant *"
self.ctype_out = "GVariant **"
self.gtype = "G_TYPE_VARIANT"
self.free_func = "g_variant_unref"
self.format_in = "@" + self.signature
self.format_out = "@" + self.signature
self.gvariant_get = "XXX"
self.gvalue_get = "g_value_get_variant"
self.array_annotation = ""
if not utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.ForceGVariant"
):
if self.signature == "b":
self.ctype_in_g = "gboolean "
self.ctype_in = "gboolean "
self.ctype_out = "gboolean *"
self.gtype = "G_TYPE_BOOLEAN"
self.free_func = None
self.format_in = "b"
self.format_out = "b"
self.gvariant_get = "g_variant_get_boolean"
self.gvalue_get = "g_value_get_boolean"
elif self.signature == "y":
self.ctype_in_g = "guchar "
self.ctype_in = "guchar "
self.ctype_out = "guchar *"
self.gtype = "G_TYPE_UCHAR"
self.free_func = None
self.format_in = "y"
self.format_out = "y"
self.gvariant_get = "g_variant_get_byte"
self.gvalue_get = "g_value_get_uchar"
elif self.signature == "n":
self.ctype_in_g = "gint "
self.ctype_in = "gint16 "
self.ctype_out = "gint16 *"
self.gtype = "G_TYPE_INT"
self.free_func = None
self.format_in = "n"
self.format_out = "n"
self.gvariant_get = "g_variant_get_int16"
self.gvalue_get = "g_value_get_int"
elif self.signature == "q":
self.ctype_in_g = "guint "
self.ctype_in = "guint16 "
self.ctype_out = "guint16 *"
self.gtype = "G_TYPE_UINT"
self.free_func = None
self.format_in = "q"
self.format_out = "q"
self.gvariant_get = "g_variant_get_uint16"
self.gvalue_get = "g_value_get_uint"
elif self.signature == "i":
self.ctype_in_g = "gint "
self.ctype_in = "gint "
self.ctype_out = "gint *"
self.gtype = "G_TYPE_INT"
self.free_func = None
self.format_in = "i"
self.format_out = "i"
self.gvariant_get = "g_variant_get_int32"
self.gvalue_get = "g_value_get_int"
elif self.signature == "u":
self.ctype_in_g = "guint "
self.ctype_in = "guint "
self.ctype_out = "guint *"
self.gtype = "G_TYPE_UINT"
self.free_func = None
self.format_in = "u"
self.format_out = "u"
self.gvariant_get = "g_variant_get_uint32"
self.gvalue_get = "g_value_get_uint"
elif self.signature == "x":
self.ctype_in_g = "gint64 "
self.ctype_in = "gint64 "
self.ctype_out = "gint64 *"
self.gtype = "G_TYPE_INT64"
self.free_func = None
self.format_in = "x"
self.format_out = "x"
self.gvariant_get = "g_variant_get_int64"
self.gvalue_get = "g_value_get_int64"
elif self.signature == "t":
self.ctype_in_g = "guint64 "
self.ctype_in = "guint64 "
self.ctype_out = "guint64 *"
self.gtype = "G_TYPE_UINT64"
self.free_func = None
self.format_in = "t"
self.format_out = "t"
self.gvariant_get = "g_variant_get_uint64"
self.gvalue_get = "g_value_get_uint64"
elif self.signature == "d":
self.ctype_in_g = "gdouble "
self.ctype_in = "gdouble "
self.ctype_out = "gdouble *"
self.gtype = "G_TYPE_DOUBLE"
self.free_func = None
self.format_in = "d"
self.format_out = "d"
self.gvariant_get = "g_variant_get_double"
self.gvalue_get = "g_value_get_double"
elif self.signature == "s":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "s"
self.format_out = "s"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "o":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "o"
self.format_out = "o"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "g":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "g"
self.format_out = "g"
self.gvariant_get = "g_variant_get_string"
self.gvalue_get = "g_value_get_string"
elif self.signature == "ay":
self.ctype_in_g = "const gchar *"
self.ctype_in = "const gchar *"
self.ctype_in_dup = "gchar *"
self.ctype_out = "gchar **"
self.gtype = "G_TYPE_STRING"
self.free_func = "g_free"
self.format_in = "^ay"
self.format_out = "^ay"
self.gvariant_get = "g_variant_get_bytestring"
self.gvalue_get = "g_value_get_string"
elif self.signature == "as":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^as"
self.format_out = "^as"
self.gvariant_get = "g_variant_get_strv"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
elif self.signature == "ao":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^ao"
self.format_out = "^ao"
self.gvariant_get = "g_variant_get_objv"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
elif self.signature == "aay":
self.ctype_in_g = "const gchar *const *"
self.ctype_in = "const gchar *const *"
self.ctype_in_dup = "gchar **"
self.ctype_out = "gchar ***"
self.gtype = "G_TYPE_STRV"
self.free_func = "g_strfreev"
self.format_in = "^aay"
self.format_out = "^aay"
self.gvariant_get = "g_variant_get_bytestring_array"
self.gvalue_get = "g_value_get_boxed"
self.array_annotation = "(array zero-terminated=1)"
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Method:
def __init__(self, name, h_type_implies_unix_fd=True):
self.name = name
self.h_type_implies_unix_fd = h_type_implies_unix_fd
self.in_args = []
self.out_args = []
self.annotations = []
self.doc_string = ""
self.since = ""
self.deprecated = False
self.unix_fd = False
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if len(self.since) == 0:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
arg_count = 0
for a in self.in_args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if self.h_type_implies_unix_fd and "h" in a.signature:
self.unix_fd = True
for a in self.out_args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if self.h_type_implies_unix_fd and "h" in a.signature:
self.unix_fd = True
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
if utils.lookup_annotation(self.annotations, "org.gtk.GDBus.C.UnixFD"):
self.unix_fd = True
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Signal:
def __init__(self, name):
self.name = name
self.args = []
self.annotations = []
self.doc_string = ""
self.since = ""
self.deprecated = False
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if len(self.since) == 0:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
arg_count = 0
for a in self.args:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
arg_count += 1
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
class Property:
def __init__(self, name, signature, access):
self.name = name
self.signature = signature
self.access = access
self.annotations = []
self.arg = Arg("value", self.signature)
self.arg.annotations = self.annotations
self.readable = False
self.writable = False
if self.access == "readwrite":
self.readable = True
self.writable = True
elif self.access == "read":
self.readable = True
elif self.access == "write":
self.writable = True
else:
print_error('Invalid access type "{}"'.format(self.access))
self.doc_string = ""
self.since = ""
self.deprecated = False
self.emits_changed_signal = True
def post_process(
self, interface_prefix, cns, cns_upper, cns_lower, containing_iface
):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if len(self.since) == 0:
self.since = containing_iface.since
name = self.name
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
self.name_lower = overridden_name.lower()
else:
if overridden_name:
name = overridden_name
self.name_lower = utils.camel_case_to_uscore(name).lower().replace("-", "_")
self.name_hyphen = self.name_lower.replace("_", "-")
# don't clash with the GType getter, e.g.:
# GType foo_bar_get_type (void); G_GNUC_CONST
if self.name_lower == "type":
self.name_lower = "type_"
# recalculate arg
self.arg.annotations = self.annotations
self.arg.post_process(interface_prefix, cns, cns_upper, cns_lower, 0)
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
# FIXME: for now we only support 'false' and 'const' on the signal itself,
# see #674913 and
# http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
# for details
if utils.lookup_annotation(
self.annotations, "org.freedesktop.DBus.Property.EmitsChangedSignal"
) in ("false", "const"):
self.emits_changed_signal = False
class Interface:
def __init__(self, name):
self.name = name
self.methods = []
self.signals = []
self.properties = []
self.annotations = []
self.doc_string = ""
self.doc_string_brief = ""
self.since = ""
self.deprecated = False
def post_process(self, interface_prefix, c_namespace):
if len(self.doc_string) == 0:
self.doc_string = utils.lookup_docs(self.annotations)
if len(self.doc_string_brief) == 0:
self.doc_string_brief = utils.lookup_brief_docs(self.annotations)
if len(self.since) == 0:
self.since = utils.lookup_since(self.annotations)
if len(c_namespace) > 0:
if utils.is_ugly_case(c_namespace):
cns = c_namespace.replace("_", "")
cns_upper = c_namespace.upper() + "_"
cns_lower = c_namespace.lower() + "_"
else:
cns = c_namespace
cns_upper = utils.camel_case_to_uscore(c_namespace).upper() + "_"
cns_lower = utils.camel_case_to_uscore(c_namespace).lower() + "_"
else:
cns = ""
cns_upper = ""
cns_lower = ""
overridden_name = utils.lookup_annotation(
self.annotations, "org.gtk.GDBus.C.Name"
)
if utils.is_ugly_case(overridden_name):
name = overridden_name.replace("_", "")
name_with_ns = cns + name
self.name_without_prefix = name
self.camel_name = name_with_ns
self.ns_upper = cns_upper
self.name_lower = cns_lower + overridden_name.lower()
self.name_upper = overridden_name.upper()
# print_error('handle Ugly_Case "{}"'.format(overridden_name))
else:
if overridden_name:
name = overridden_name
else:
name = self.name
if name.startswith(interface_prefix):
name = name[len(interface_prefix) :]
self.name_without_prefix = name
name = utils.strip_dots(name)
name_with_ns = utils.strip_dots(cns + "." + name)
self.camel_name = name_with_ns
self.ns_upper = cns_upper
self.name_lower = cns_lower + utils.camel_case_to_uscore(name)
self.name_upper = utils.camel_case_to_uscore(name).upper()
self.name_hyphen = self.name_upper.lower().replace("_", "-")
if (
utils.lookup_annotation(self.annotations, "org.freedesktop.DBus.Deprecated")
== "true"
):
self.deprecated = True
for m in self.methods:
m.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
for s in self.signals:
s.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
for p in self.properties:
p.post_process(interface_prefix, cns, cns_upper, cns_lower, self)
for a in self.annotations:
a.post_process(interface_prefix, cns, cns_upper, cns_lower, self)

View file

@ -0,0 +1,55 @@
#!/usr/bin/env @PYTHON@
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import os
import sys
srcdir = os.getenv('UNINSTALLED_GLIB_SRCDIR', None)
filedir = os.path.dirname(__file__)
if srcdir is not None:
path = os.path.join(srcdir, 'gio', 'gdbus-2.0')
elif os.path.basename(filedir) == 'bin':
# Make the prefix containing gdbus-codegen 'relocatable' at runtime by
# adding /some/prefix/bin/../share/glib-2.0 to the python path
path = os.path.join(filedir, '..', 'share', 'glib-2.0')
else:
# Assume that the modules we need are in the current directory and add the
# parent directory to the python path.
path = os.path.join(filedir, '..')
# Canonicalize, then do further testing
path = os.path.abspath(path)
# If the above path detection failed, use the hard-coded datadir. This can
# happen when, for instance, bindir and datadir are not in the same prefix or
# on Windows where we cannot make any guarantees about the directory structure.
#
# In these cases our installation cannot be relocatable, but at least we should
# be able to find the codegen module.
if not os.path.isfile(os.path.join(path, 'codegen', 'codegen_main.py')):
path = os.path.join('@DATADIR@', 'glib-2.0')
sys.path.insert(0, path)
from codegen import codegen_main
sys.exit(codegen_main.codegen_main())

View file

@ -0,0 +1,42 @@
gdbus_codegen_files = [
'__init__.py',
'codegen.py',
'codegen_main.py',
'codegen_docbook.py',
'codegen_rst.py',
'dbustypes.py',
'parser.py',
'utils.py',
]
gdbus_codegen_conf = configuration_data()
gdbus_codegen_conf.set('VERSION', glib_version)
gdbus_codegen_conf.set('MAJOR_VERSION', major_version)
gdbus_codegen_conf.set('MINOR_VERSION', minor_version)
gdbus_codegen_conf.set('PYTHON', python_name)
gdbus_codegen_conf.set('DATADIR', glib_datadir)
# Install gdbus-codegen executable
gdbus_codegen = configure_file(input : 'gdbus-codegen.in',
output : 'gdbus-codegen',
install_dir : get_option('bindir'),
configuration : gdbus_codegen_conf
)
# Provide tools for others when we're a subproject and they use the Meson GNOME module
meson.override_find_program('gdbus-codegen', gdbus_codegen)
codegen_dir = join_paths(glib_datadir, 'glib-2.0', 'codegen')
gdbus_codegen_built_files = []
gdbus_codegen_built_files += configure_file(input : 'config.py.in',
output : 'config.py',
install_dir : codegen_dir,
configuration : gdbus_codegen_conf)
foreach f : gdbus_codegen_files
# Copy these into the builddir so that gdbus-codegen can be used uninstalled
# and then install it too so that it can be used after installation
gdbus_codegen_built_files += configure_file(input : f, output : f,
install_dir : codegen_dir,
copy : true)
endforeach

View file

@ -0,0 +1,302 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import xml.parsers.expat
from . import dbustypes
from .utils import print_error
class DBusXMLParser:
STATE_TOP = "top"
STATE_NODE = "node"
STATE_INTERFACE = "interface"
STATE_METHOD = "method"
STATE_SIGNAL = "signal"
STATE_PROPERTY = "property"
STATE_ARG = "arg"
STATE_ANNOTATION = "annotation"
STATE_IGNORED = "ignored"
def __init__(self, xml_data, h_type_implies_unix_fd=True):
self._parser = xml.parsers.expat.ParserCreate()
self._parser.CommentHandler = self.handle_comment
self._parser.CharacterDataHandler = self.handle_char_data
self._parser.StartElementHandler = self.handle_start_element
self._parser.EndElementHandler = self.handle_end_element
self.parsed_interfaces = []
self._cur_object = None
self.state = DBusXMLParser.STATE_TOP
self.state_stack = []
self._cur_object = None
self._cur_object_stack = []
self.doc_comment_last_symbol = ""
self._h_type_implies_unix_fd = h_type_implies_unix_fd
self._parser.Parse(xml_data)
COMMENT_STATE_BEGIN = "begin"
COMMENT_STATE_PARAMS = "params"
COMMENT_STATE_BODY = "body"
COMMENT_STATE_SKIP = "skip"
def handle_comment(self, data):
comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
lines = data.split("\n")
symbol = ""
body = ""
in_para = False
params = {}
for line in lines:
orig_line = line
line = line.lstrip()
if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
if len(line) > 0:
colon_index = line.find(": ")
if colon_index == -1:
if line.endswith(":"):
symbol = line[0 : len(line) - 1]
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
else:
comment_state = DBusXMLParser.COMMENT_STATE_SKIP
else:
symbol = line[0:colon_index]
rest_of_line = line[colon_index + 2 :].strip()
if len(rest_of_line) > 0:
body += f"{rest_of_line}\n"
comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
if line.startswith("@"):
colon_index = line.find(": ")
if colon_index == -1:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if not in_para:
body += "\n"
in_para = True
body += f"{orig_line}\n"
else:
param = line[1:colon_index]
docs = line[colon_index + 2 :]
params[param] = docs
else:
comment_state = DBusXMLParser.COMMENT_STATE_BODY
if len(line) > 0:
if not in_para:
body += "\n"
in_para = True
body += orig_line + "\n"
elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
if len(line) > 0:
if not in_para:
in_para = True
body += orig_line + "\n"
else:
if in_para:
body += "\n"
in_para = False
if in_para:
body += "\n"
if symbol != "":
self.doc_comment_last_symbol = symbol
self.doc_comment_params = params
self.doc_comment_body = body
def handle_char_data(self, data):
# print 'char_data=%s'%data
pass
def handle_start_element(self, name, attrs):
old_state = self.state
old_cur_object = self._cur_object
if self.state == DBusXMLParser.STATE_IGNORED:
self.state = DBusXMLParser.STATE_IGNORED
elif self.state == DBusXMLParser.STATE_TOP:
if name == DBusXMLParser.STATE_NODE:
self.state = DBusXMLParser.STATE_NODE
else:
self.state = DBusXMLParser.STATE_IGNORED
elif self.state == DBusXMLParser.STATE_NODE:
if name == DBusXMLParser.STATE_INTERFACE:
self.state = DBusXMLParser.STATE_INTERFACE
iface = dbustypes.Interface(attrs["name"])
self._cur_object = iface
self.parsed_interfaces.append(iface)
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
self._cur_object.doc_string = self.doc_comment_body
if "short_description" in self.doc_comment_params:
short_description = self.doc_comment_params["short_description"]
self._cur_object.doc_string_brief = short_description
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params["since"].strip()
elif self.state == DBusXMLParser.STATE_INTERFACE:
if name == DBusXMLParser.STATE_METHOD:
self.state = DBusXMLParser.STATE_METHOD
method = dbustypes.Method(
attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
)
self._cur_object.methods.append(method)
self._cur_object = method
elif name == DBusXMLParser.STATE_SIGNAL:
self.state = DBusXMLParser.STATE_SIGNAL
signal = dbustypes.Signal(attrs["name"])
self._cur_object.signals.append(signal)
self._cur_object = signal
elif name == DBusXMLParser.STATE_PROPERTY:
self.state = DBusXMLParser.STATE_PROPERTY
prop = dbustypes.Property(attrs["name"], attrs["type"], attrs["access"])
self._cur_object.properties.append(prop)
self._cur_object = prop
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
self._cur_object.doc_string = self.doc_comment_body
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params["since"].strip()
elif self.state == DBusXMLParser.STATE_METHOD:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
arg_name = None
if "name" in attrs:
arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs["type"])
direction = attrs.get("direction", "in")
if direction == "in":
self._cur_object.in_args.append(arg)
elif direction == "out":
self._cur_object.out_args.append(arg)
else:
print_error('Invalid direction "{}"'.format(direction))
self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name:
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string is not None:
self._cur_object.doc_string = doc_string
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params[
"since"
].strip()
elif self.state == DBusXMLParser.STATE_SIGNAL:
if name == DBusXMLParser.STATE_ARG:
self.state = DBusXMLParser.STATE_ARG
arg_name = None
if "name" in attrs:
arg_name = attrs["name"]
arg = dbustypes.Arg(arg_name, attrs["type"])
self._cur_object.args.append(arg)
self._cur_object = arg
elif name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
# assign docs, if any
if self.doc_comment_last_symbol == old_cur_object.name:
if "name" in attrs and attrs["name"] in self.doc_comment_params:
doc_string = self.doc_comment_params[attrs["name"]]
if doc_string is not None:
self._cur_object.doc_string = doc_string
if "since" in self.doc_comment_params:
self._cur_object.since = self.doc_comment_params[
"since"
].strip()
elif self.state == DBusXMLParser.STATE_PROPERTY:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
elif self.state == DBusXMLParser.STATE_ARG:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
elif self.state == DBusXMLParser.STATE_ANNOTATION:
if name == DBusXMLParser.STATE_ANNOTATION:
self.state = DBusXMLParser.STATE_ANNOTATION
anno = dbustypes.Annotation(attrs["name"], attrs["value"])
self._cur_object.annotations.append(anno)
self._cur_object = anno
else:
self.state = DBusXMLParser.STATE_IGNORED
else:
print_error(
'Unhandled state "{}" while entering element with name "{}"'.format(
self.state, name
)
)
self.state_stack.append(old_state)
self._cur_object_stack.append(old_cur_object)
def handle_end_element(self, name):
self.state = self.state_stack.pop()
self._cur_object = self._cur_object_stack.pop()
def parse_dbus_xml(xml_data, h_type_implies_unix_fd):
parser = DBusXMLParser(xml_data, h_type_implies_unix_fd)
return parser.parsed_interfaces

View file

@ -0,0 +1,165 @@
# -*- Mode: Python -*-
# GDBus - GLib D-Bus Library
#
# Copyright (C) 2008-2011 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General
# Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
# Author: David Zeuthen <davidz@redhat.com>
import distutils.version
import os
import sys
# pylint: disable=too-few-public-methods
class Color:
"""ANSI Terminal colors"""
GREEN = "\033[1;32m"
BLUE = "\033[1;34m"
YELLOW = "\033[1;33m"
RED = "\033[1;31m"
END = "\033[0m"
def print_color(msg, color=Color.END, prefix="MESSAGE"):
"""Print a string with a color prefix"""
if os.isatty(sys.stderr.fileno()):
real_prefix = "{start}{prefix}{end}".format(
start=color, prefix=prefix, end=Color.END
)
else:
real_prefix = prefix
sys.stderr.write("{prefix}: {msg}\n".format(prefix=real_prefix, msg=msg))
def print_error(msg):
"""Print an error, and terminate"""
print_color(msg, color=Color.RED, prefix="ERROR")
sys.exit(1)
def print_warning(msg, fatal=False):
"""Print a warning, and optionally terminate"""
if fatal:
color = Color.RED
prefix = "ERROR"
else:
color = Color.YELLOW
prefix = "WARNING"
print_color(msg, color, prefix)
if fatal:
sys.exit(1)
def print_info(msg):
"""Print a message"""
print_color(msg, color=Color.GREEN, prefix="INFO")
def strip_dots(s):
ret = ""
force_upper = False
for c in s:
if c == ".":
force_upper = True
else:
if force_upper:
ret += c.upper()
force_upper = False
else:
ret += c
return ret
def dots_to_hyphens(s):
return s.replace(".", "-")
def camel_case_to_uscore(s):
ret = ""
insert_uscore = False
prev_was_lower = False
initial = True
for c in s:
# Keep initial underscores in camel case
if initial and c == "_":
ret += "_"
continue
initial = False
if c.isupper():
if prev_was_lower:
insert_uscore = True
prev_was_lower = False
else:
prev_was_lower = True
if insert_uscore:
ret += "_"
ret += c.lower()
insert_uscore = False
return ret
def is_ugly_case(s):
if s and s.find("_") > 0:
return True
return False
def lookup_annotation(annotations, key):
if annotations:
for a in annotations:
if a.key == key:
return a.value
return None
def lookup_docs(annotations):
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString")
if s is None:
return ""
else:
return s
def lookup_since(annotations):
s = lookup_annotation(annotations, "org.gtk.GDBus.Since")
if s is None:
return ""
else:
return s
def lookup_brief_docs(annotations):
s = lookup_annotation(annotations, "org.gtk.GDBus.DocString.Short")
if s is None:
return ""
else:
return s
def version_cmp_key(key):
# If the 'since' version is 'UNRELEASED', compare higher than anything else
# If it is empty put a 0 in its place as this will
# allow LooseVersion to work and will always compare lower.
if key[0] == "UNRELEASED":
v = "9999"
elif key[0]:
v = str(key[0])
else:
v = "0"
return (distutils.version.LooseVersion(v), key[1])

2641
gio/gdbus-tool.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
/*
* Copyright © 2010 Codethink Limited
* Copyright © 2011 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_DBUS_ACTION_GROUP_PRIVATE_H__
#define __G_DBUS_ACTION_GROUP_PRIVATE_H__
#include "gdbusactiongroup.h"
G_BEGIN_DECLS
gboolean
g_dbus_action_group_sync (GDBusActionGroup *group,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif

550
gio/gdbusactiongroup.c Normal file
View file

@ -0,0 +1,550 @@
/*
* Copyright © 2010 Codethink Limited
* Copyright © 2011 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "gdbusactiongroup-private.h"
#include "gremoteactiongroup.h"
#include "gdbusconnection.h"
#include "gactiongroup.h"
/**
* SECTION:gdbusactiongroup
* @title: GDBusActionGroup
* @short_description: A D-Bus GActionGroup implementation
* @include: gio/gio.h
* @see_also: [GActionGroup exporter][gio-GActionGroup-exporter]
*
* #GDBusActionGroup is an implementation of the #GActionGroup
* interface that can be used as a proxy for an action group
* that is exported over D-Bus with g_dbus_connection_export_action_group().
*/
/**
* GDBusActionGroup:
*
* #GDBusActionGroup is an opaque data structure and can only be accessed
* using the following functions.
*/
struct _GDBusActionGroup
{
GObject parent_instance;
GDBusConnection *connection;
gchar *bus_name;
gchar *object_path;
guint subscription_id;
GHashTable *actions;
/* The 'strict' flag indicates that the non-existence of at least one
* action has potentially been observed through the API. This means
* that we should always emit 'action-added' signals for all new
* actions.
*
* The user can observe the non-existence of an action by listing the
* actions or by performing a query (such as parameter type) on a
* non-existent action.
*
* If the user has no way of knowing that a given action didn't
* already exist then we can skip emitting 'action-added' signals
* since they have no way of knowing that it wasn't there from the
* start.
*/
gboolean strict;
};
typedef GObjectClass GDBusActionGroupClass;
typedef struct
{
gchar *name;
GVariantType *parameter_type;
gboolean enabled;
GVariant *state;
} ActionInfo;
static void
action_info_free (gpointer user_data)
{
ActionInfo *info = user_data;
g_free (info->name);
if (info->state)
g_variant_unref (info->state);
if (info->parameter_type)
g_variant_type_free (info->parameter_type);
g_slice_free (ActionInfo, info);
}
static ActionInfo *
action_info_new_from_iter (GVariantIter *iter)
{
const gchar *param_str;
ActionInfo *info;
gboolean enabled;
GVariant *state;
gchar *name;
if (!g_variant_iter_next (iter, "{s(b&g@av)}", &name,
&enabled, &param_str, &state))
return NULL;
info = g_slice_new (ActionInfo);
info->name = name;
info->enabled = enabled;
if (g_variant_n_children (state))
g_variant_get_child (state, 0, "v", &info->state);
else
info->state = NULL;
g_variant_unref (state);
if (param_str[0])
info->parameter_type = g_variant_type_copy ((GVariantType *) param_str);
else
info->parameter_type = NULL;
return info;
}
static void g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface);
static void g_dbus_action_group_iface_init (GActionGroupInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GDBusActionGroup, g_dbus_action_group, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_dbus_action_group_iface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_REMOTE_ACTION_GROUP, g_dbus_action_group_remote_iface_init))
static void
g_dbus_action_group_changed (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GDBusActionGroup *group = user_data;
GActionGroup *g_group = user_data;
/* make sure that we've been fully initialised */
if (group->actions == NULL)
return;
if (g_str_equal (signal_name, "Changed") &&
g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
{
/* Removes */
{
GVariantIter *iter;
const gchar *name;
g_variant_get_child (parameters, 0, "as", &iter);
while (g_variant_iter_next (iter, "&s", &name))
{
if (g_hash_table_lookup (group->actions, name))
{
g_hash_table_remove (group->actions, name);
g_action_group_action_removed (g_group, name);
}
}
g_variant_iter_free (iter);
}
/* Enable changes */
{
GVariantIter *iter;
const gchar *name;
gboolean enabled;
g_variant_get_child (parameters, 1, "a{sb}", &iter);
while (g_variant_iter_next (iter, "{&sb}", &name, &enabled))
{
ActionInfo *info;
info = g_hash_table_lookup (group->actions, name);
if (info && info->enabled != enabled)
{
info->enabled = enabled;
g_action_group_action_enabled_changed (g_group, name, enabled);
}
}
g_variant_iter_free (iter);
}
/* State changes */
{
GVariantIter *iter;
const gchar *name;
GVariant *state;
g_variant_get_child (parameters, 2, "a{sv}", &iter);
while (g_variant_iter_next (iter, "{&sv}", &name, &state))
{
ActionInfo *info;
info = g_hash_table_lookup (group->actions, name);
if (info && info->state && !g_variant_equal (state, info->state) &&
g_variant_is_of_type (state, g_variant_get_type (info->state)))
{
g_variant_unref (info->state);
info->state = g_variant_ref (state);
g_action_group_action_state_changed (g_group, name, state);
}
g_variant_unref (state);
}
g_variant_iter_free (iter);
}
/* Additions */
{
GVariantIter *iter;
ActionInfo *info;
g_variant_get_child (parameters, 3, "a{s(bgav)}", &iter);
while ((info = action_info_new_from_iter (iter)))
{
if (!g_hash_table_lookup (group->actions, info->name))
{
g_hash_table_insert (group->actions, info->name, info);
if (group->strict)
g_action_group_action_added (g_group, info->name);
}
else
action_info_free (info);
}
g_variant_iter_free (iter);
}
}
}
static void
g_dbus_action_group_describe_all_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusActionGroup *group= user_data;
GVariant *reply;
g_assert (group->actions == NULL);
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
g_assert (group->connection == (gpointer) source);
reply = g_dbus_connection_call_finish (group->connection, result, NULL);
if (reply != NULL)
{
GVariantIter *iter;
ActionInfo *action;
g_variant_get (reply, "(a{s(bgav)})", &iter);
while ((action = action_info_new_from_iter (iter)))
{
g_hash_table_insert (group->actions, action->name, action);
if (group->strict)
g_action_group_action_added (G_ACTION_GROUP (group), action->name);
}
g_variant_iter_free (iter);
g_variant_unref (reply);
}
g_object_unref (group);
}
static void
g_dbus_action_group_async_init (GDBusActionGroup *group)
{
if (group->subscription_id != 0)
return;
group->subscription_id =
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "DescribeAll", NULL,
G_VARIANT_TYPE ("(a{s(bgav)})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL,
g_dbus_action_group_describe_all_done, g_object_ref (group));
}
static gchar **
g_dbus_action_group_list_actions (GActionGroup *g_group)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
gchar **keys;
if (group->actions != NULL)
{
GHashTableIter iter;
gint n, i = 0;
gpointer key;
n = g_hash_table_size (group->actions);
keys = g_new (gchar *, n + 1);
g_hash_table_iter_init (&iter, group->actions);
while (g_hash_table_iter_next (&iter, &key, NULL))
keys[i++] = g_strdup (key);
g_assert_cmpint (i, ==, n);
keys[n] = NULL;
}
else
{
g_dbus_action_group_async_init (group);
keys = g_new0 (gchar *, 1);
}
group->strict = TRUE;
return keys;
}
static gboolean
g_dbus_action_group_query_action (GActionGroup *g_group,
const gchar *action_name,
gboolean *enabled,
const GVariantType **parameter_type,
const GVariantType **state_type,
GVariant **state_hint,
GVariant **state)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (g_group);
ActionInfo *info;
if (group->actions != NULL)
{
info = g_hash_table_lookup (group->actions, action_name);
if (info == NULL)
{
group->strict = TRUE;
return FALSE;
}
if (enabled)
*enabled = info->enabled;
if (parameter_type)
*parameter_type = info->parameter_type;
if (state_type)
*state_type = info->state ? g_variant_get_type (info->state) : NULL;
if (state_hint)
*state_hint = NULL;
if (state)
*state = info->state ? g_variant_ref (info->state) : NULL;
return TRUE;
}
else
{
g_dbus_action_group_async_init (group);
group->strict = TRUE;
return FALSE;
}
}
static void
g_dbus_action_group_activate_action_full (GRemoteActionGroup *remote,
const gchar *action_name,
GVariant *parameter,
GVariant *platform_data)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
if (parameter)
g_variant_builder_add (&builder, "v", parameter);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "Activate",
g_variant_new ("(sav@a{sv})", action_name, &builder, platform_data),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_change_action_state_full (GRemoteActionGroup *remote,
const gchar *action_name,
GVariant *value,
GVariant *platform_data)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (remote);
g_dbus_connection_call (group->connection, group->bus_name, group->object_path, "org.gtk.Actions", "SetState",
g_variant_new ("(sv@a{sv})", action_name, value, platform_data),
NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
static void
g_dbus_action_group_change_state (GActionGroup *group,
const gchar *action_name,
GVariant *value)
{
g_dbus_action_group_change_action_state_full (G_REMOTE_ACTION_GROUP (group),
action_name, value, g_variant_new ("a{sv}", NULL));
}
static void
g_dbus_action_group_activate (GActionGroup *group,
const gchar *action_name,
GVariant *parameter)
{
g_dbus_action_group_activate_action_full (G_REMOTE_ACTION_GROUP (group),
action_name, parameter, g_variant_new ("a{sv}", NULL));
}
static void
g_dbus_action_group_finalize (GObject *object)
{
GDBusActionGroup *group = G_DBUS_ACTION_GROUP (object);
if (group->subscription_id)
g_dbus_connection_signal_unsubscribe (group->connection, group->subscription_id);
if (group->actions)
g_hash_table_unref (group->actions);
g_object_unref (group->connection);
g_free (group->object_path);
g_free (group->bus_name);
G_OBJECT_CLASS (g_dbus_action_group_parent_class)
->finalize (object);
}
static void
g_dbus_action_group_init (GDBusActionGroup *group)
{
}
static void
g_dbus_action_group_class_init (GDBusActionGroupClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = g_dbus_action_group_finalize;
}
static void
g_dbus_action_group_remote_iface_init (GRemoteActionGroupInterface *iface)
{
iface->activate_action_full = g_dbus_action_group_activate_action_full;
iface->change_action_state_full = g_dbus_action_group_change_action_state_full;
}
static void
g_dbus_action_group_iface_init (GActionGroupInterface *iface)
{
iface->list_actions = g_dbus_action_group_list_actions;
iface->query_action = g_dbus_action_group_query_action;
iface->change_action_state = g_dbus_action_group_change_state;
iface->activate_action = g_dbus_action_group_activate;
}
/**
* g_dbus_action_group_get:
* @connection: A #GDBusConnection
* @bus_name: (nullable): the bus name which exports the action
* group or %NULL if @connection is not a message bus connection
* @object_path: the object path at which the action group is exported
*
* Obtains a #GDBusActionGroup for the action group which is exported at
* the given @bus_name and @object_path.
*
* The thread default main context is taken at the time of this call.
* All signals on the menu model (and any linked models) are reported
* with respect to this context. All calls on the returned menu model
* (and linked models) must also originate from this same context, with
* the thread default main context unchanged.
*
* This call is non-blocking. The returned action group may or may not
* already be filled in. The correct thing to do is connect the signals
* for the action group to monitor for changes and then to call
* g_action_group_list_actions() to get the initial list.
*
* Returns: (transfer full): a #GDBusActionGroup
*
* Since: 2.32
*/
GDBusActionGroup *
g_dbus_action_group_get (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path)
{
GDBusActionGroup *group;
g_return_val_if_fail (bus_name != NULL || g_dbus_connection_get_unique_name (connection) == NULL, NULL);
group = g_object_new (G_TYPE_DBUS_ACTION_GROUP, NULL);
group->connection = g_object_ref (connection);
group->bus_name = g_strdup (bus_name);
group->object_path = g_strdup (object_path);
return group;
}
gboolean
g_dbus_action_group_sync (GDBusActionGroup *group,
GCancellable *cancellable,
GError **error)
{
GVariant *reply;
g_assert (group->subscription_id == 0);
group->subscription_id =
g_dbus_connection_signal_subscribe (group->connection, group->bus_name, "org.gtk.Actions", "Changed", group->object_path,
NULL, G_DBUS_SIGNAL_FLAGS_NONE, g_dbus_action_group_changed, group, NULL);
reply = g_dbus_connection_call_sync (group->connection, group->bus_name, group->object_path, "org.gtk.Actions",
"DescribeAll", NULL, G_VARIANT_TYPE ("(a{s(bgav)})"),
G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
if (reply != NULL)
{
GVariantIter *iter;
ActionInfo *action;
g_assert (group->actions == NULL);
group->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, action_info_free);
g_variant_get (reply, "(a{s(bgav)})", &iter);
while ((action = action_info_new_from_iter (iter)))
g_hash_table_insert (group->actions, action->name, action);
g_variant_iter_free (iter);
g_variant_unref (reply);
}
return reply != NULL;
}

54
gio/gdbusactiongroup.h Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright © 2010 Codethink Limited
* Copyright © 2011 Canonical Limited
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#ifndef __G_DBUS_ACTION_GROUP_H__
#define __G_DBUS_ACTION_GROUP_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include "giotypes.h"
G_BEGIN_DECLS
#define G_TYPE_DBUS_ACTION_GROUP (g_dbus_action_group_get_type ())
#define G_DBUS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroup))
#define G_DBUS_ACTION_GROUP_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
#define G_IS_DBUS_ACTION_GROUP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
G_TYPE_DBUS_ACTION_GROUP))
#define G_IS_DBUS_ACTION_GROUP_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
G_TYPE_DBUS_ACTION_GROUP))
#define G_DBUS_ACTION_GROUP_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
G_TYPE_DBUS_ACTION_GROUP, GDBusActionGroupClass))
GLIB_AVAILABLE_IN_ALL
GType g_dbus_action_group_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_2_32
GDBusActionGroup * g_dbus_action_group_get (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path);
G_END_DECLS
#endif /* __G_DBUS_ACTION_GROUP_H__ */

1467
gio/gdbusaddress.c Normal file

File diff suppressed because it is too large Load diff

65
gio/gdbusaddress.h Normal file
View file

@ -0,0 +1,65 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_ADDRESS_H__
#define __G_DBUS_ADDRESS_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
GLIB_AVAILABLE_IN_2_36
gchar *g_dbus_address_escape_value (const gchar *string);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_is_address (const gchar *string);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_is_supported_address (const gchar *string,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_dbus_address_get_stream (const gchar *address,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GIOStream *g_dbus_address_get_stream_finish (GAsyncResult *res,
gchar **out_guid,
GError **error);
GLIB_AVAILABLE_IN_ALL
GIOStream *g_dbus_address_get_stream_sync (const gchar *address,
gchar **out_guid,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
gchar *g_dbus_address_get_for_bus_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_ADDRESS_H__ */

1394
gio/gdbusauth.c Normal file

File diff suppressed because it is too large Load diff

86
gio/gdbusauth.h Normal file
View file

@ -0,0 +1,86 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_H__
#define __G_DBUS_AUTH_H__
#if !defined (GIO_COMPILATION)
#error "gdbusauth.h is a private header file."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH (_g_dbus_auth_get_type ())
#define G_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH, GDBusAuth))
#define G_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH, GDBusAuthClass))
#define G_DBUS_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH, GDBusAuthClass))
#define G_IS_DBUS_AUTH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH))
#define G_IS_DBUS_AUTH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH))
typedef struct _GDBusAuth GDBusAuth;
typedef struct _GDBusAuthClass GDBusAuthClass;
typedef struct _GDBusAuthPrivate GDBusAuthPrivate;
struct _GDBusAuthClass
{
/*< private >*/
GObjectClass parent_class;
};
struct _GDBusAuth
{
GObject parent_instance;
GDBusAuthPrivate *priv;
};
GType _g_dbus_auth_get_type (void) G_GNUC_CONST;
GDBusAuth *_g_dbus_auth_new (GIOStream *stream);
/* TODO: need a way to set allowed authentication mechanisms */
/* TODO: need a way to convey credentials etc. */
/* TODO: need a way to convey negotiated features (e.g. returning flags from e.g. GDBusConnectionFeatures) */
/* TODO: need to expose encode()/decode() from the AuthMechanism (and whether it is needed at all) */
gboolean _g_dbus_auth_run_server (GDBusAuth *auth,
GDBusAuthObserver *observer,
const gchar *guid,
gboolean allow_anonymous,
gboolean require_same_user,
GDBusCapabilityFlags offered_capabilities,
GDBusCapabilityFlags *out_negotiated_capabilities,
GCredentials **out_received_credentials,
GCancellable *cancellable,
GError **error);
gchar *_g_dbus_auth_run_client (GDBusAuth *auth,
GDBusAuthObserver *observer,
GDBusCapabilityFlags offered_capabilities,
GDBusCapabilityFlags *out_negotiated_capabilities,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* __G_DBUS_AUTH_H__ */

334
gio/gdbusauthmechanism.c Normal file
View file

@ -0,0 +1,334 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include "gdbusauthmechanism.h"
#include "gcredentials.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
#include "giostream.h"
#include "glibintl.h"
/* ---------------------------------------------------------------------------------------------------- */
struct _GDBusAuthMechanismPrivate
{
GIOStream *stream;
GCredentials *credentials;
};
enum
{
PROP_0,
PROP_STREAM,
PROP_CREDENTIALS
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GDBusAuthMechanism, _g_dbus_auth_mechanism, G_TYPE_OBJECT)
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_finalize (GObject *object)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
if (mechanism->priv->stream != NULL)
g_object_unref (mechanism->priv->stream);
if (mechanism->priv->credentials != NULL)
g_object_unref (mechanism->priv->credentials);
G_OBJECT_CLASS (_g_dbus_auth_mechanism_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
switch (prop_id)
{
case PROP_STREAM:
g_value_set_object (value, mechanism->priv->stream);
break;
case PROP_CREDENTIALS:
g_value_set_object (value, mechanism->priv->credentials);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
_g_dbus_auth_mechanism_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GDBusAuthMechanism *mechanism = G_DBUS_AUTH_MECHANISM (object);
switch (prop_id)
{
case PROP_STREAM:
mechanism->priv->stream = g_value_dup_object (value);
break;
case PROP_CREDENTIALS:
mechanism->priv->credentials = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
_g_dbus_auth_mechanism_class_init (GDBusAuthMechanismClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = _g_dbus_auth_mechanism_get_property;
gobject_class->set_property = _g_dbus_auth_mechanism_set_property;
gobject_class->finalize = _g_dbus_auth_mechanism_finalize;
g_object_class_install_property (gobject_class,
PROP_STREAM,
g_param_spec_object ("stream",
P_("IO Stream"),
P_("The underlying GIOStream used for I/O"),
G_TYPE_IO_STREAM,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
/**
* GDBusAuthMechanism:credentials:
*
* If authenticating as a server, this property contains the
* received credentials, if any.
*
* If authenticating as a client, the property contains the
* credentials that were sent, if any.
*/
g_object_class_install_property (gobject_class,
PROP_CREDENTIALS,
g_param_spec_object ("credentials",
P_("Credentials"),
P_("The credentials of the remote peer"),
G_TYPE_CREDENTIALS,
G_PARAM_READABLE |
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK));
}
static void
_g_dbus_auth_mechanism_init (GDBusAuthMechanism *mechanism)
{
mechanism->priv = _g_dbus_auth_mechanism_get_instance_private (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */
GIOStream *
_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return mechanism->priv->stream;
}
GCredentials *
_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return mechanism->priv->credentials;
}
/* ---------------------------------------------------------------------------------------------------- */
const gchar *
_g_dbus_auth_mechanism_get_name (GType mechanism_type)
{
const gchar *name;
GDBusAuthMechanismClass *klass;
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), NULL);
klass = g_type_class_ref (mechanism_type);
g_assert (klass != NULL);
name = klass->get_name ();
//g_type_class_unref (klass);
return name;
}
gint
_g_dbus_auth_mechanism_get_priority (GType mechanism_type)
{
gint priority;
GDBusAuthMechanismClass *klass;
g_return_val_if_fail (g_type_is_a (mechanism_type, G_TYPE_DBUS_AUTH_MECHANISM), 0);
klass = g_type_class_ref (mechanism_type);
g_assert (klass != NULL);
priority = klass->get_priority ();
//g_type_class_unref (klass);
return priority;
}
/* ---------------------------------------------------------------------------------------------------- */
gboolean
_g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), FALSE);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism);
}
gchar *
_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->encode_data (mechanism, data, data_len, out_data_len);
}
gchar *
_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->decode_data (mechanism, data, data_len, out_data_len);
}
/* ---------------------------------------------------------------------------------------------------- */
GDBusAuthMechanismState
_g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_state (mechanism);
}
void
_g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_initiate (mechanism, initial_response, initial_response_len);
}
void
_g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_receive (mechanism, data, data_len);
}
gchar *
_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_data_send (mechanism, out_data_len);
}
gchar *
_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_get_reject_reason (mechanism);
}
void
_g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->server_shutdown (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */
GDBusAuthMechanismState
_g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism);
}
gchar *
_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism,
out_initial_response_len);
}
void
_g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len);
}
gchar *
_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len);
}
void
_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism));
G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */

152
gio/gdbusauthmechanism.h Normal file
View file

@ -0,0 +1,152 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_MECHANISM_H__
#define __G_DBUS_AUTH_MECHANISM_H__
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanism.h is a private header file."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM (_g_dbus_auth_mechanism_get_type ())
#define G_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanism))
#define G_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
#define G_DBUS_AUTH_MECHANISM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM, GDBusAuthMechanismClass))
#define G_IS_DBUS_AUTH_MECHANISM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM))
#define G_IS_DBUS_AUTH_MECHANISM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM))
typedef struct _GDBusAuthMechanism GDBusAuthMechanism;
typedef struct _GDBusAuthMechanismClass GDBusAuthMechanismClass;
typedef struct _GDBusAuthMechanismPrivate GDBusAuthMechanismPrivate;
typedef enum {
G_DBUS_AUTH_MECHANISM_STATE_INVALID,
G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA,
G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND,
G_DBUS_AUTH_MECHANISM_STATE_REJECTED,
G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED,
} GDBusAuthMechanismState;
struct _GDBusAuthMechanismClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* VTable */
/* TODO: server_initiate and client_initiate probably needs to have a
* GCredentials parameter...
*/
gint (*get_priority) (void);
const gchar *(*get_name) (void);
/* functions shared by server/client */
gboolean (*is_supported) (GDBusAuthMechanism *mechanism);
gchar *(*encode_data) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
gchar *(*decode_data) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
/* functions for server-side authentication */
GDBusAuthMechanismState (*server_get_state) (GDBusAuthMechanism *mechanism);
void (*server_initiate) (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
void (*server_data_receive) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *(*server_data_send) (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
gchar *(*server_get_reject_reason) (GDBusAuthMechanism *mechanism);
void (*server_shutdown) (GDBusAuthMechanism *mechanism);
/* functions for client-side authentication */
GDBusAuthMechanismState (*client_get_state) (GDBusAuthMechanism *mechanism);
gchar *(*client_initiate) (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
void (*client_data_receive) (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *(*client_data_send) (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
void (*client_shutdown) (GDBusAuthMechanism *mechanism);
};
struct _GDBusAuthMechanism
{
GObject parent_instance;
GDBusAuthMechanismPrivate *priv;
};
GType _g_dbus_auth_mechanism_get_type (void) G_GNUC_CONST;
gint _g_dbus_auth_mechanism_get_priority (GType mechanism_type);
const gchar *_g_dbus_auth_mechanism_get_name (GType mechanism_type);
GIOStream *_g_dbus_auth_mechanism_get_stream (GDBusAuthMechanism *mechanism);
GCredentials *_g_dbus_auth_mechanism_get_credentials (GDBusAuthMechanism *mechanism);
gboolean _g_dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism);
gchar *_g_dbus_auth_mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
gchar *_g_dbus_auth_mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
GDBusAuthMechanismState _g_dbus_auth_mechanism_server_get_state (GDBusAuthMechanism *mechanism);
void _g_dbus_auth_mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
void _g_dbus_auth_mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *_g_dbus_auth_mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
gchar *_g_dbus_auth_mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
void _g_dbus_auth_mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
GDBusAuthMechanismState _g_dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism);
gchar *_g_dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
void _g_dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_H__ */

View file

@ -0,0 +1,322 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include "gdbusauthmechanismanon.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
#include "glibintl.h"
struct _GDBusAuthMechanismAnonPrivate
{
gboolean is_client;
gboolean is_server;
GDBusAuthMechanismState state;
};
static gint mechanism_get_priority (void);
static const gchar *mechanism_get_name (void);
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
/* ---------------------------------------------------------------------------------------------------- */
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismAnon, _g_dbus_auth_mechanism_anon, G_TYPE_DBUS_AUTH_MECHANISM)
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_anon_finalize (GObject *object)
{
//GDBusAuthMechanismAnon *mechanism = G_DBUS_AUTH_MECHANISM_ANON (object);
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_mechanism_anon_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass)
{
GObjectClass *gobject_class;
GDBusAuthMechanismClass *mechanism_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = _g_dbus_auth_mechanism_anon_finalize;
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
mechanism_class->get_priority = mechanism_get_priority;
mechanism_class->get_name = mechanism_get_name;
mechanism_class->is_supported = mechanism_is_supported;
mechanism_class->encode_data = mechanism_encode_data;
mechanism_class->decode_data = mechanism_decode_data;
mechanism_class->server_get_state = mechanism_server_get_state;
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
static void
_g_dbus_auth_mechanism_anon_init (GDBusAuthMechanismAnon *mechanism)
{
mechanism->priv = _g_dbus_auth_mechanism_anon_get_instance_private (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */
static gint
mechanism_get_priority (void)
{
/* We prefer ANONYMOUS to most other mechanism (such as DBUS_COOKIE_SHA1) but not to EXTERNAL */
return 50;
}
static const gchar *
mechanism_get_name (void)
{
return "ANONYMOUS";
}
static gboolean
mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), FALSE);
return TRUE;
}
static gchar *
mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
static gchar *
mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static void
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
//g_debug ("ANONYMOUS: initial_response was '%s'", initial_response);
m->priv->is_server = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
static void
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static gchar *
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
m->priv->is_server = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gchar *
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
gchar *result;
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
/* just return our library name and version */
result = g_strdup ("GDBus 0.1");
*out_initial_response_len = strlen (result);
return result;
}
static void
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
m->priv->is_client = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */

View file

@ -0,0 +1,61 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_MECHANISM_ANON_H__
#define __G_DBUS_AUTH_MECHANISM_ANON_H__
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismanon.h is a private header file."
#endif
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_ANON (_g_dbus_auth_mechanism_anon_get_type ())
#define G_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnon))
#define G_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
#define G_DBUS_AUTH_MECHANISM_ANON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON, GDBusAuthMechanismAnonClass))
#define G_IS_DBUS_AUTH_MECHANISM_ANON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
#define G_IS_DBUS_AUTH_MECHANISM_ANON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_ANON))
typedef struct _GDBusAuthMechanismAnon GDBusAuthMechanismAnon;
typedef struct _GDBusAuthMechanismAnonClass GDBusAuthMechanismAnonClass;
typedef struct _GDBusAuthMechanismAnonPrivate GDBusAuthMechanismAnonPrivate;
struct _GDBusAuthMechanismAnonClass
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
};
struct _GDBusAuthMechanismAnon
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismAnonPrivate *priv;
};
GType _g_dbus_auth_mechanism_anon_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_ANON_H__ */

View file

@ -0,0 +1,443 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include <string.h>
#include "gdbusauthmechanismexternal.h"
#include "gcredentials.h"
#include "gdbuserror.h"
#include "gioenumtypes.h"
#include "glibintl.h"
#ifdef G_OS_WIN32
#include "gwin32sid.h"
#endif
struct _GDBusAuthMechanismExternalPrivate
{
gboolean is_client;
gboolean is_server;
GDBusAuthMechanismState state;
gboolean empty_data_sent;
};
static gint mechanism_get_priority (void);
static const gchar *mechanism_get_name (void);
static gboolean mechanism_is_supported (GDBusAuthMechanism *mechanism);
static gchar *mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static gchar *mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len);
static GDBusAuthMechanismState mechanism_server_get_state (GDBusAuthMechanism *mechanism);
static void mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len);
static void mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len);
static void mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len);
static gchar *mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
static void mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
/* ---------------------------------------------------------------------------------------------------- */
G_DEFINE_TYPE_WITH_PRIVATE (GDBusAuthMechanismExternal, _g_dbus_auth_mechanism_external, G_TYPE_DBUS_AUTH_MECHANISM)
/* ---------------------------------------------------------------------------------------------------- */
static void
_g_dbus_auth_mechanism_external_finalize (GObject *object)
{
//GDBusAuthMechanismExternal *mechanism = G_DBUS_AUTH_MECHANISM_EXTERNAL (object);
if (G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize != NULL)
G_OBJECT_CLASS (_g_dbus_auth_mechanism_external_parent_class)->finalize (object);
}
static void
_g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *klass)
{
GObjectClass *gobject_class;
GDBusAuthMechanismClass *mechanism_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = _g_dbus_auth_mechanism_external_finalize;
mechanism_class = G_DBUS_AUTH_MECHANISM_CLASS (klass);
mechanism_class->get_name = mechanism_get_name;
mechanism_class->get_priority = mechanism_get_priority;
mechanism_class->is_supported = mechanism_is_supported;
mechanism_class->encode_data = mechanism_encode_data;
mechanism_class->decode_data = mechanism_decode_data;
mechanism_class->server_get_state = mechanism_server_get_state;
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
static void
_g_dbus_auth_mechanism_external_init (GDBusAuthMechanismExternal *mechanism)
{
mechanism->priv = _g_dbus_auth_mechanism_external_get_instance_private (mechanism);
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
mechanism_is_supported (GDBusAuthMechanism *mechanism)
{
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), FALSE);
#if defined(G_OS_WIN32)
/* all that is required is current process SID */
return TRUE;
#else
/* This mechanism is only available if credentials has been exchanged */
if (_g_dbus_auth_mechanism_get_credentials (mechanism) != NULL)
return TRUE;
else
return FALSE;
#endif
}
static gint
mechanism_get_priority (void)
{
/* We prefer EXTERNAL to most other mechanism (DBUS_COOKIE_SHA1 and ANONYMOUS) */
return 100;
}
static const gchar *
mechanism_get_name (void)
{
return "EXTERNAL";
}
static gchar *
mechanism_encode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
static gchar *
mechanism_decode_data (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len,
gsize *out_data_len)
{
return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_server_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gboolean
data_matches_credentials (const gchar *data,
gsize data_len,
GCredentials *credentials)
{
gboolean match;
match = FALSE;
if (credentials == NULL)
goto out;
#if defined(G_OS_UNIX)
{
gint64 alleged_uid;
gchar *endp;
/* If we were unable to find out the uid, then nothing
* can possibly match it. */
if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
goto out;
/* An empty authorization identity means we want to be
* whatever identity the out-of-band credentials say we have
* (RFC 4422 appendix A.1). This effectively matches any uid. */
if (data == NULL || data_len == 0)
{
match = TRUE;
goto out;
}
/* on UNIX, this is the uid as a string in base 10 */
alleged_uid = g_ascii_strtoll (data, &endp, 10);
if (*endp == '\0')
{
if (g_credentials_get_unix_user (credentials, NULL) == alleged_uid)
{
match = TRUE;
}
}
}
#else
/* TODO: Dont know how to compare credentials on this OS. Please implement. */
#endif
out:
return match;
}
static void
mechanism_server_initiate (GDBusAuthMechanism *mechanism,
const gchar *initial_response,
gsize initial_response_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (!m->priv->is_server && !m->priv->is_client);
m->priv->is_server = TRUE;
if (initial_response != NULL)
{
if (data_matches_credentials (initial_response,
initial_response_len,
_g_dbus_auth_mechanism_get_credentials (mechanism)))
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
else
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
}
}
else
{
/* The initial-response optimization was not used, so we need to
* send an empty challenge to prompt the client to respond. */
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
}
}
static void
mechanism_server_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
if (data_matches_credentials (data,
data_len,
_g_dbus_auth_mechanism_get_credentials (mechanism)))
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
}
else
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
}
}
static gchar *
mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
if (out_data_len)
*out_data_len = 0;
if (m->priv->empty_data_sent)
{
/* We have already sent an empty data response.
Reject the connection. */
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
return NULL;
}
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
m->priv->empty_data_sent = TRUE;
return g_strdup ("");
}
static gchar *
mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_server_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_server && !m->priv->is_client);
m->priv->is_server = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */
static GDBusAuthMechanismState
mechanism_client_get_state (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), G_DBUS_AUTH_MECHANISM_STATE_INVALID);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, G_DBUS_AUTH_MECHANISM_STATE_INVALID);
return m->priv->state;
}
static gchar *
mechanism_client_initiate (GDBusAuthMechanism *mechanism,
gsize *out_initial_response_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
gchar *initial_response = NULL;
#if defined(G_OS_UNIX)
GCredentials *credentials;
#endif
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (!m->priv->is_server && !m->priv->is_client, NULL);
m->priv->is_client = TRUE;
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
*out_initial_response_len = 0;
/* return the uid */
#if defined(G_OS_UNIX)
credentials = _g_dbus_auth_mechanism_get_credentials (mechanism);
g_assert (credentials != NULL);
initial_response = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64) g_credentials_get_unix_user (credentials, NULL));
#elif defined(G_OS_WIN32)
initial_response = _g_win32_current_process_sid_string (NULL);
#else
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wcpp"
#warning Dont know how to send credentials on this OS. The EXTERNAL D-Bus authentication mechanism will not work.
#pragma GCC diagnostic pop
#endif
#endif
if (initial_response)
{
m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_ACCEPTED;
*out_initial_response_len = strlen (initial_response);
}
return initial_response;
}
static void
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
gsize data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
g_return_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA);
/* can never end up here because we are never in the WAITING_FOR_DATA state */
g_assert_not_reached ();
}
static gchar *
mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
g_return_val_if_fail (m->priv->is_client && !m->priv->is_server, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
/* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
g_assert_not_reached ();
return NULL;
}
static void
mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism));
g_return_if_fail (m->priv->is_client && !m->priv->is_server);
m->priv->is_client = FALSE;
}
/* ---------------------------------------------------------------------------------------------------- */

View file

@ -0,0 +1,61 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
#define __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismexternal.h is a private header file."
#endif
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL (_g_dbus_auth_mechanism_external_get_type ())
#define G_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternal))
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
#define G_DBUS_AUTH_MECHANISM_EXTERNAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL, GDBusAuthMechanismExternalClass))
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
#define G_IS_DBUS_AUTH_MECHANISM_EXTERNAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_EXTERNAL))
typedef struct _GDBusAuthMechanismExternal GDBusAuthMechanismExternal;
typedef struct _GDBusAuthMechanismExternalClass GDBusAuthMechanismExternalClass;
typedef struct _GDBusAuthMechanismExternalPrivate GDBusAuthMechanismExternalPrivate;
struct _GDBusAuthMechanismExternalClass
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
};
struct _GDBusAuthMechanismExternal
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismExternalPrivate *priv;
};
GType _g_dbus_auth_mechanism_external_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_EXTERNAL_H__ */

1290
gio/gdbusauthmechanismsha1.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_MECHANISM_SHA1_H__
#define __G_DBUS_AUTH_MECHANISM_SHA1_H__
#if !defined (GIO_COMPILATION)
#error "gdbusauthmechanismsha1.h is a private header file."
#endif
#include <gio/giotypes.h>
#include <gio/gdbusauthmechanism.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_MECHANISM_SHA1 (_g_dbus_auth_mechanism_sha1_get_type ())
#define G_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1))
#define G_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
#define G_DBUS_AUTH_MECHANISM_SHA1_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1, GDBusAuthMechanismSha1Class))
#define G_IS_DBUS_AUTH_MECHANISM_SHA1(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
#define G_IS_DBUS_AUTH_MECHANISM_SHA1_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_AUTH_MECHANISM_SHA1))
typedef struct _GDBusAuthMechanismSha1 GDBusAuthMechanismSha1;
typedef struct _GDBusAuthMechanismSha1Class GDBusAuthMechanismSha1Class;
typedef struct _GDBusAuthMechanismSha1Private GDBusAuthMechanismSha1Private;
struct _GDBusAuthMechanismSha1Class
{
/*< private >*/
GDBusAuthMechanismClass parent_class;
};
struct _GDBusAuthMechanismSha1
{
GDBusAuthMechanism parent_instance;
GDBusAuthMechanismSha1Private *priv;
};
GType _g_dbus_auth_mechanism_sha1_get_type (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __G_DBUS_AUTH_MECHANISM_SHA1_H__ */

315
gio/gdbusauthobserver.c Normal file
View file

@ -0,0 +1,315 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include "config.h"
#include "gdbusauthobserver.h"
#include "gcredentials.h"
#include "gioenumtypes.h"
#include "giostream.h"
#include "gdbusprivate.h"
#include "glibintl.h"
#include "gmarshal-internal.h"
/**
* SECTION:gdbusauthobserver
* @short_description: Object used for authenticating connections
* @include: gio/gio.h
*
* The #GDBusAuthObserver type provides a mechanism for participating
* in how a #GDBusServer (or a #GDBusConnection) authenticates remote
* peers. Simply instantiate a #GDBusAuthObserver and connect to the
* signals you are interested in. Note that new signals may be added
* in the future
*
* ## Controlling Authentication Mechanisms
*
* By default, a #GDBusServer or server-side #GDBusConnection will allow
* any authentication mechanism to be used. If you only
* want to allow D-Bus connections with the `EXTERNAL` mechanism,
* which makes use of credentials passing and is the recommended
* mechanism for modern Unix platforms such as Linux and the BSD family,
* you would use a signal handler like this:
*
* |[<!-- language="C" -->
* static gboolean
* on_allow_mechanism (GDBusAuthObserver *observer,
* const gchar *mechanism,
* gpointer user_data)
* {
* if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
* {
* return TRUE;
* }
*
* return FALSE;
* }
* ]|
*
* ## Controlling Authorization # {#auth-observer}
*
* By default, a #GDBusServer or server-side #GDBusConnection will accept
* connections from any successfully authenticated user (but not from
* anonymous connections using the `ANONYMOUS` mechanism). If you only
* want to allow D-Bus connections from processes owned by the same uid
* as the server, since GLib 2.68, you should use the
* %G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER flag. Its equivalent
* to the following signal handler:
*
* |[<!-- language="C" -->
* static gboolean
* on_authorize_authenticated_peer (GDBusAuthObserver *observer,
* GIOStream *stream,
* GCredentials *credentials,
* gpointer user_data)
* {
* gboolean authorized;
*
* authorized = FALSE;
* if (credentials != NULL)
* {
* GCredentials *own_credentials;
* own_credentials = g_credentials_new ();
* if (g_credentials_is_same_user (credentials, own_credentials, NULL))
* authorized = TRUE;
* g_object_unref (own_credentials);
* }
*
* return authorized;
* }
* ]|
*/
typedef struct _GDBusAuthObserverClass GDBusAuthObserverClass;
/**
* GDBusAuthObserverClass:
* @authorize_authenticated_peer: Signal class handler for the #GDBusAuthObserver::authorize-authenticated-peer signal.
*
* Class structure for #GDBusAuthObserverClass.
*
* Since: 2.26
*/
struct _GDBusAuthObserverClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
/* Signals */
gboolean (*authorize_authenticated_peer) (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials);
gboolean (*allow_mechanism) (GDBusAuthObserver *observer,
const gchar *mechanism);
};
/**
* GDBusAuthObserver:
*
* The #GDBusAuthObserver structure contains only private data and
* should only be accessed using the provided API.
*
* Since: 2.26
*/
struct _GDBusAuthObserver
{
GObject parent_instance;
};
enum
{
AUTHORIZE_AUTHENTICATED_PEER_SIGNAL,
ALLOW_MECHANISM_SIGNAL,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (GDBusAuthObserver, g_dbus_auth_observer, G_TYPE_OBJECT)
/* ---------------------------------------------------------------------------------------------------- */
static void
g_dbus_auth_observer_finalize (GObject *object)
{
G_OBJECT_CLASS (g_dbus_auth_observer_parent_class)->finalize (object);
}
static gboolean
g_dbus_auth_observer_authorize_authenticated_peer_real (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials)
{
return TRUE;
}
static gboolean
g_dbus_auth_observer_allow_mechanism_real (GDBusAuthObserver *observer,
const gchar *mechanism)
{
return TRUE;
}
static void
g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_dbus_auth_observer_finalize;
klass->authorize_authenticated_peer = g_dbus_auth_observer_authorize_authenticated_peer_real;
klass->allow_mechanism = g_dbus_auth_observer_allow_mechanism_real;
/**
* GDBusAuthObserver::authorize-authenticated-peer:
* @observer: The #GDBusAuthObserver emitting the signal.
* @stream: A #GIOStream for the #GDBusConnection.
* @credentials: (nullable): Credentials received from the peer or %NULL.
*
* Emitted to check if a peer that is successfully authenticated
* is authorized.
*
* Returns: %TRUE if the peer is authorized, %FALSE if not.
*
* Since: 2.26
*/
signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL] =
g_signal_new (I_("authorize-authenticated-peer"),
G_TYPE_DBUS_AUTH_OBSERVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GDBusAuthObserverClass, authorize_authenticated_peer),
_g_signal_accumulator_false_handled,
NULL, /* accu_data */
_g_cclosure_marshal_BOOLEAN__OBJECT_OBJECT,
G_TYPE_BOOLEAN,
2,
G_TYPE_IO_STREAM,
G_TYPE_CREDENTIALS);
g_signal_set_va_marshaller (signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL],
G_TYPE_FROM_CLASS (klass),
_g_cclosure_marshal_BOOLEAN__OBJECT_OBJECTv);
/**
* GDBusAuthObserver::allow-mechanism:
* @observer: The #GDBusAuthObserver emitting the signal.
* @mechanism: The name of the mechanism, e.g. `DBUS_COOKIE_SHA1`.
*
* Emitted to check if @mechanism is allowed to be used.
*
* Returns: %TRUE if @mechanism can be used to authenticate the other peer, %FALSE if not.
*
* Since: 2.34
*/
signals[ALLOW_MECHANISM_SIGNAL] =
g_signal_new (I_("allow-mechanism"),
G_TYPE_DBUS_AUTH_OBSERVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GDBusAuthObserverClass, allow_mechanism),
_g_signal_accumulator_false_handled,
NULL, /* accu_data */
_g_cclosure_marshal_BOOLEAN__STRING,
G_TYPE_BOOLEAN,
1,
G_TYPE_STRING);
g_signal_set_va_marshaller (signals[ALLOW_MECHANISM_SIGNAL],
G_TYPE_FROM_CLASS (klass),
_g_cclosure_marshal_BOOLEAN__STRINGv);
}
static void
g_dbus_auth_observer_init (GDBusAuthObserver *observer)
{
}
/**
* g_dbus_auth_observer_new:
*
* Creates a new #GDBusAuthObserver object.
*
* Returns: A #GDBusAuthObserver. Free with g_object_unref().
*
* Since: 2.26
*/
GDBusAuthObserver *
g_dbus_auth_observer_new (void)
{
return g_object_new (G_TYPE_DBUS_AUTH_OBSERVER, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
/**
* g_dbus_auth_observer_authorize_authenticated_peer:
* @observer: A #GDBusAuthObserver.
* @stream: A #GIOStream for the #GDBusConnection.
* @credentials: (nullable): Credentials received from the peer or %NULL.
*
* Emits the #GDBusAuthObserver::authorize-authenticated-peer signal on @observer.
*
* Returns: %TRUE if the peer is authorized, %FALSE if not.
*
* Since: 2.26
*/
gboolean
g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials)
{
gboolean denied;
denied = FALSE;
g_signal_emit (observer,
signals[AUTHORIZE_AUTHENTICATED_PEER_SIGNAL],
0,
stream,
credentials,
&denied);
return denied;
}
/**
* g_dbus_auth_observer_allow_mechanism:
* @observer: A #GDBusAuthObserver.
* @mechanism: The name of the mechanism, e.g. `DBUS_COOKIE_SHA1`.
*
* Emits the #GDBusAuthObserver::allow-mechanism signal on @observer.
*
* Returns: %TRUE if @mechanism can be used to authenticate the other peer, %FALSE if not.
*
* Since: 2.34
*/
gboolean
g_dbus_auth_observer_allow_mechanism (GDBusAuthObserver *observer,
const gchar *mechanism)
{
gboolean ret;
ret = FALSE;
g_signal_emit (observer,
signals[ALLOW_MECHANISM_SIGNAL],
0,
mechanism,
&ret);
return ret;
}

51
gio/gdbusauthobserver.h Normal file
View file

@ -0,0 +1,51 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_AUTH_OBSERVER_H__
#define __G_DBUS_AUTH_OBSERVER_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_AUTH_OBSERVER (g_dbus_auth_observer_get_type ())
#define G_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_AUTH_OBSERVER, GDBusAuthObserver))
#define G_IS_DBUS_AUTH_OBSERVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_AUTH_OBSERVER))
GLIB_AVAILABLE_IN_ALL
GType g_dbus_auth_observer_get_type (void) G_GNUC_CONST;
GLIB_AVAILABLE_IN_ALL
GDBusAuthObserver *g_dbus_auth_observer_new (void);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_auth_observer_authorize_authenticated_peer (GDBusAuthObserver *observer,
GIOStream *stream,
GCredentials *credentials);
GLIB_AVAILABLE_IN_2_34
gboolean g_dbus_auth_observer_allow_mechanism (GDBusAuthObserver *observer,
const gchar *mechanism);
G_END_DECLS
#endif /* _G_DBUS_AUTH_OBSERVER_H__ */

7580
gio/gdbusconnection.c Normal file

File diff suppressed because it is too large Load diff

684
gio/gdbusconnection.h Normal file
View file

@ -0,0 +1,684 @@
/* GDBus - GLib D-Bus Library
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#ifndef __G_DBUS_CONNECTION_H__
#define __G_DBUS_CONNECTION_H__
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
#error "Only <gio/gio.h> can be included directly."
#endif
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_DBUS_CONNECTION (g_dbus_connection_get_type ())
#define G_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection))
#define G_IS_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION))
GLIB_AVAILABLE_IN_ALL
GType g_dbus_connection_get_type (void) G_GNUC_CONST;
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
void g_bus_get (GBusType bus_type,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_bus_get_finish (GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_bus_get_sync (GBusType bus_type,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_new (GIOStream *stream,
const gchar *guid,
GDBusConnectionFlags flags,
GDBusAuthObserver *observer,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_dbus_connection_new_finish (GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_dbus_connection_new_sync (GIOStream *stream,
const gchar *guid,
GDBusConnectionFlags flags,
GDBusAuthObserver *observer,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_new_for_address (const gchar *address,
GDBusConnectionFlags flags,
GDBusAuthObserver *observer,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_dbus_connection_new_for_address_finish (GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar *address,
GDBusConnectionFlags flags,
GDBusAuthObserver *observer,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_start_message_processing (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_is_closed (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
GIOStream *g_dbus_connection_get_stream (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
const gchar *g_dbus_connection_get_guid (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
const gchar *g_dbus_connection_get_unique_name (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
GCredentials *g_dbus_connection_get_peer_credentials (GDBusConnection *connection);
GLIB_AVAILABLE_IN_2_34
guint32 g_dbus_connection_get_last_serial (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_set_exit_on_close (GDBusConnection *connection,
gboolean exit_on_close);
GLIB_AVAILABLE_IN_ALL
GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection);
GLIB_AVAILABLE_IN_2_60
GDBusConnectionFlags g_dbus_connection_get_flags (GDBusConnection *connection);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_close (GDBusConnection *connection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_close_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_close_sync (GDBusConnection *connection,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_flush (GDBusConnection *connection,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_flush_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_flush_sync (GDBusConnection *connection,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_send_message (GDBusConnection *connection,
GDBusMessage *message,
GDBusSendMessageFlags flags,
volatile guint32 *out_serial,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_send_message_with_reply (GDBusConnection *connection,
GDBusMessage *message,
GDBusSendMessageFlags flags,
gint timeout_msec,
volatile guint32 *out_serial,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GDBusMessage *g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
GDBusMessage *g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection,
GDBusMessage *message,
GDBusSendMessageFlags flags,
gint timeout_msec,
volatile guint32 *out_serial,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_emit_signal (GDBusConnection *connection,
const gchar *destination_bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
GError **error);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_call (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
GVariant *g_dbus_connection_call_finish (GDBusConnection *connection,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_ALL
GVariant *g_dbus_connection_call_sync (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GCancellable *cancellable,
GError **error);
GLIB_AVAILABLE_IN_2_30
void g_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GUnixFDList *fd_list,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_30
GVariant *g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection,
GUnixFDList **out_fd_list,
GAsyncResult *res,
GError **error);
GLIB_AVAILABLE_IN_2_30
GVariant *g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection *connection,
const gchar *bus_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
const GVariantType *reply_type,
GDBusCallFlags flags,
gint timeout_msec,
GUnixFDList *fd_list,
GUnixFDList **out_fd_list,
GCancellable *cancellable,
GError **error);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusInterfaceMethodCallFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name the method was invoked on.
* @method_name: The name of the method that was invoked.
* @parameters: A #GVariant tuple with parameters.
* @invocation: (transfer full): A #GDBusMethodInvocation object that must be used to return a value or error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @method_call function in #GDBusInterfaceVTable.
*
* Since: 2.26
*/
typedef void (*GDBusInterfaceMethodCallFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data);
/**
* GDBusInterfaceGetPropertyFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name for the property.
* @property_name: The name of the property to get the value of.
* @error: Return location for error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @get_property function in #GDBusInterfaceVTable.
*
* Returns: A #GVariant with the value for @property_name or %NULL if
* @error is set. If the returned #GVariant is floating, it is
* consumed - otherwise its reference count is decreased by one.
*
* Since: 2.26
*/
typedef GVariant *(*GDBusInterfaceGetPropertyFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data);
/**
* GDBusInterfaceSetPropertyFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that the method was invoked on.
* @interface_name: The D-Bus interface name for the property.
* @property_name: The name of the property to get the value of.
* @value: The value to set the property to.
* @error: Return location for error.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_object().
*
* The type of the @set_property function in #GDBusInterfaceVTable.
*
* Returns: %TRUE if the property was set to @value, %FALSE if @error is set.
*
* Since: 2.26
*/
typedef gboolean (*GDBusInterfaceSetPropertyFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GVariant *value,
GError **error,
gpointer user_data);
/**
* GDBusInterfaceVTable:
* @method_call: Function for handling incoming method calls.
* @get_property: Function for getting a property.
* @set_property: Function for setting a property.
*
* Virtual table for handling properties and method calls for a D-Bus
* interface.
*
* Since 2.38, if you want to handle getting/setting D-Bus properties
* asynchronously, give %NULL as your get_property() or set_property()
* function. The D-Bus call will be directed to your @method_call function,
* with the provided @interface_name set to "org.freedesktop.DBus.Properties".
*
* Ownership of the #GDBusMethodInvocation object passed to the
* method_call() function is transferred to your handler; you must
* call one of the methods of #GDBusMethodInvocation to return a reply
* (possibly empty), or an error. These functions also take ownership
* of the passed-in invocation object, so unless the invocation
* object has otherwise been referenced, it will be then be freed.
* Calling one of these functions may be done within your
* method_call() implementation but it also can be done at a later
* point to handle the method asynchronously.
*
* The usual checks on the validity of the calls is performed. For
* `Get` calls, an error is automatically returned if the property does
* not exist or the permissions do not allow access. The same checks are
* performed for `Set` calls, and the provided value is also checked for
* being the correct type.
*
* For both `Get` and `Set` calls, the #GDBusMethodInvocation
* passed to the @method_call handler can be queried with
* g_dbus_method_invocation_get_property_info() to get a pointer
* to the #GDBusPropertyInfo of the property.
*
* If you have readable properties specified in your interface info,
* you must ensure that you either provide a non-%NULL @get_property()
* function or provide implementations of both the `Get` and `GetAll`
* methods on org.freedesktop.DBus.Properties interface in your @method_call
* function. Note that the required return type of the `Get` call is
* `(v)`, not the type of the property. `GetAll` expects a return value
* of type `a{sv}`.
*
* If you have writable properties specified in your interface info,
* you must ensure that you either provide a non-%NULL @set_property()
* function or provide an implementation of the `Set` call. If implementing
* the call, you must return the value of type %G_VARIANT_TYPE_UNIT.
*
* Since: 2.26
*/
struct _GDBusInterfaceVTable
{
GDBusInterfaceMethodCallFunc method_call;
GDBusInterfaceGetPropertyFunc get_property;
GDBusInterfaceSetPropertyFunc set_property;
/*< private >*/
/* Padding for future expansion - also remember to update
* gdbusconnection.c:_g_dbus_interface_vtable_copy() when
* changing this.
*/
gpointer padding[8];
};
GLIB_AVAILABLE_IN_ALL
guint g_dbus_connection_register_object (GDBusConnection *connection,
const gchar *object_path,
GDBusInterfaceInfo *interface_info,
const GDBusInterfaceVTable *vtable,
gpointer user_data,
GDestroyNotify user_data_free_func,
GError **error);
GLIB_AVAILABLE_IN_2_46
guint g_dbus_connection_register_object_with_closures (GDBusConnection *connection,
const gchar *object_path,
GDBusInterfaceInfo *interface_info,
GClosure *method_call_closure,
GClosure *get_property_closure,
GClosure *set_property_closure,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_unregister_object (GDBusConnection *connection,
guint registration_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusSubtreeEnumerateFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @enumerate function in #GDBusSubtreeVTable.
*
* This function is called when generating introspection data and also
* when preparing to dispatch incoming messages in the event that the
* %G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is not
* specified (ie: to verify that the object path is valid).
*
* Hierarchies are not supported; the items that you return should not
* contain the `/` character.
*
* The return value will be freed with g_strfreev().
*
* 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
*/
typedef gchar** (*GDBusSubtreeEnumerateFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
gpointer user_data);
/**
* GDBusSubtreeIntrospectFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @node: A node that is a child of @object_path (relative to @object_path) or %NULL for the root of the subtree.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @introspect function in #GDBusSubtreeVTable.
*
* Subtrees are flat. @node, if non-%NULL, is always exactly one
* segment of the object path (ie: it never contains a slash).
*
* This function should return %NULL to indicate that there is no object
* at this node.
*
* If this function returns non-%NULL, the return value is expected to
* be a %NULL-terminated array of pointers to #GDBusInterfaceInfo
* structures describing the interfaces implemented by @node. This
* array will have g_dbus_interface_info_unref() called on each item
* before being freed with g_free().
*
* The difference between returning %NULL and an array containing zero
* items is that the standard DBus interfaces will returned to the
* remote introspector in the empty array case, but not in the %NULL
* case.
*
* Returns: (array zero-terminated=1) (nullable) (transfer full): A %NULL-terminated array of pointers to #GDBusInterfaceInfo, or %NULL.
*
* Since: 2.26
*/
typedef GDBusInterfaceInfo ** (*GDBusSubtreeIntrospectFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *node,
gpointer user_data);
/**
* GDBusSubtreeDispatchFunc:
* @connection: A #GDBusConnection.
* @sender: The unique bus name of the remote caller.
* @object_path: The object path that was registered with g_dbus_connection_register_subtree().
* @interface_name: The D-Bus interface name that the method call or property access is for.
* @node: A node that is a child of @object_path (relative to @object_path) or %NULL for the root of the subtree.
* @out_user_data: (nullable) (not optional): Return location for user data to pass to functions in the returned #GDBusInterfaceVTable.
* @user_data: The @user_data #gpointer passed to g_dbus_connection_register_subtree().
*
* The type of the @dispatch function in #GDBusSubtreeVTable.
*
* Subtrees are flat. @node, if non-%NULL, is always exactly one
* segment of the object path (ie: it never contains a slash).
*
* Returns: (nullable): A #GDBusInterfaceVTable or %NULL if you don't want to handle the methods.
*
* Since: 2.26
*/
typedef const GDBusInterfaceVTable * (*GDBusSubtreeDispatchFunc) (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *node,
gpointer *out_user_data,
gpointer user_data);
/**
* GDBusSubtreeVTable:
* @enumerate: Function for enumerating child nodes.
* @introspect: Function for introspecting a child node.
* @dispatch: Function for dispatching a remote call on a child node.
*
* Virtual table for handling subtrees registered with g_dbus_connection_register_subtree().
*
* Since: 2.26
*/
struct _GDBusSubtreeVTable
{
GDBusSubtreeEnumerateFunc enumerate;
GDBusSubtreeIntrospectFunc introspect;
GDBusSubtreeDispatchFunc dispatch;
/*< private >*/
/* Padding for future expansion - also remember to update
* gdbusconnection.c:_g_dbus_subtree_vtable_copy() when
* changing this.
*/
gpointer padding[8];
};
GLIB_AVAILABLE_IN_ALL
guint g_dbus_connection_register_subtree (GDBusConnection *connection,
const gchar *object_path,
const GDBusSubtreeVTable *vtable,
GDBusSubtreeFlags flags,
gpointer user_data,
GDestroyNotify user_data_free_func,
GError **error);
GLIB_AVAILABLE_IN_ALL
gboolean g_dbus_connection_unregister_subtree (GDBusConnection *connection,
guint registration_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusSignalCallback:
* @connection: A #GDBusConnection.
* @sender_name: (nullable): The unique bus name of the sender of the signal,
or %NULL on a peer-to-peer D-Bus connection.
* @object_path: The object path that the signal was emitted on.
* @interface_name: The name of the interface.
* @signal_name: The name of the signal.
* @parameters: A #GVariant tuple with parameters for the signal.
* @user_data: User data passed when subscribing to the signal.
*
* Signature for callback function used in g_dbus_connection_signal_subscribe().
*
* Since: 2.26
*/
typedef void (*GDBusSignalCallback) (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
guint g_dbus_connection_signal_subscribe (GDBusConnection *connection,
const gchar *sender,
const gchar *interface_name,
const gchar *member,
const gchar *object_path,
const gchar *arg0,
GDBusSignalFlags flags,
GDBusSignalCallback callback,
gpointer user_data,
GDestroyNotify user_data_free_func);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
guint subscription_id);
/* ---------------------------------------------------------------------------------------------------- */
/**
* GDBusMessageFilterFunction:
* @connection: (transfer none): A #GDBusConnection.
* @message: (transfer full): A locked #GDBusMessage that the filter function takes ownership of.
* @incoming: %TRUE if it is a message received from the other peer, %FALSE if it is
* a message to be sent to the other peer.
* @user_data: User data passed when adding the filter.
*
* Signature for function used in g_dbus_connection_add_filter().
*
* A filter function is passed a #GDBusMessage and expected to return
* a #GDBusMessage too. Passive filter functions that don't modify the
* message can simply return the @message object:
* |[
* static GDBusMessage *
* passive_filter (GDBusConnection *connection
* GDBusMessage *message,
* gboolean incoming,
* gpointer user_data)
* {
* // inspect @message
* return message;
* }
* ]|
* Filter functions that wants to drop a message can simply return %NULL:
* |[
* static GDBusMessage *
* drop_filter (GDBusConnection *connection
* GDBusMessage *message,
* gboolean incoming,
* gpointer user_data)
* {
* if (should_drop_message)
* {
* g_object_unref (message);
* message = NULL;
* }
* return message;
* }
* ]|
* Finally, a filter function may modify a message by copying it:
* |[
* static GDBusMessage *
* modifying_filter (GDBusConnection *connection
* GDBusMessage *message,
* gboolean incoming,
* gpointer user_data)
* {
* GDBusMessage *copy;
* GError *error;
*
* error = NULL;
* copy = g_dbus_message_copy (message, &error);
* // handle @error being set
* g_object_unref (message);
*
* // modify @copy
*
* return copy;
* }
* ]|
* If the returned #GDBusMessage is different from @message and cannot
* be sent on @connection (it could use features, such as file
* descriptors, not compatible with @connection), then a warning is
* logged to standard error. Applications can
* check this ahead of time using g_dbus_message_to_blob() passing a
* #GDBusCapabilityFlags value obtained from @connection.
*
* Returns: (transfer full) (nullable): A #GDBusMessage that will be freed with
* g_object_unref() or %NULL to drop the message. Passive filter
* functions can simply return the passed @message object.
*
* Since: 2.26
*/
typedef GDBusMessage *(*GDBusMessageFilterFunction) (GDBusConnection *connection,
GDBusMessage *message,
gboolean incoming,
gpointer user_data);
GLIB_AVAILABLE_IN_ALL
guint g_dbus_connection_add_filter (GDBusConnection *connection,
GDBusMessageFilterFunction filter_function,
gpointer user_data,
GDestroyNotify user_data_free_func);
GLIB_AVAILABLE_IN_ALL
void g_dbus_connection_remove_filter (GDBusConnection *connection,
guint filter_id);
/* ---------------------------------------------------------------------------------------------------- */
G_END_DECLS
#endif /* __G_DBUS_CONNECTION_H__ */

Some files were not shown because too many files have changed in this diff Show more