pax_global_header00006660000000000000000000000064132135607330014515gustar00rootroot0000000000000052 comment=26e7e4c1d9f898ef8222d1637019ebd24eafb52d EventDance-0.2.0/000077500000000000000000000000001321356073300135305ustar00rootroot00000000000000EventDance-0.2.0/.gitignore000066400000000000000000000004051321356073300155170ustar00rootroot00000000000000*.o *.lo *.la .libs/ .deps/ *.pc *.gir *.typelib INSTALL configure *.guess Makefile *.in *.m4 install-sh libtool ltmain.sh missing stamp-h1 *.make autom4te.cache/ compile config.h config.log config.status config.sub depcomp *.stamp *.bak vgdump test-driver EventDance-0.2.0/AUTHORS000066400000000000000000000000601321356073300145740ustar00rootroot00000000000000Eduardo Lima Mitev - Creator EventDance-0.2.0/COPYING000066400000000000000000000167261321356073300145770ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. EventDance-0.2.0/ChangeLog000066400000000000000000000000001321356073300152700ustar00rootroot00000000000000EventDance-0.2.0/Makefile.am000066400000000000000000000020231321356073300155610ustar00rootroot00000000000000SUBDIRS = \ evd \ tests \ examples if BUILD_GTK_DOC SUBDIRS += doc endif DIST_SUBDIRS = \ evd \ tests \ doc \ examples EXTRA_DIST = \ autogen.sh \ m4/introspection.m4 ACLOCAL_AMFLAGS = -I m4 DISTCHECK_CONFIGURE_FLAGS = --enable-introspection --enable-gtk-doc CLEANFILES = *~ MAINTAINERCLEANFILES = \ Makefile.in aclocal.m4 configure config.guess config.sub \ depcomp install-sh ltmain.sh missing mkinstalldirs config.h.in \ stamp-h.in compile gtk-doc.make DISTCLEANFILES = \ cscope.files cscope.out cscope.files: find src -name '*.[ch]' > $@ cscope.out: cscope.files cscope -b # test targets test: tests/Makefile make -C tests/ test dist-hook: @if test -d "$(srcdir)/.git"; \ then \ echo Creating ChangeLog && \ ( cd "$(top_srcdir)" && \ $(top_srcdir)/missing --run git log --stat ) > ChangeLog.tmp \ && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \ || ( rm -f ChangeLog.tmp ; \ echo Failed to generate ChangeLog >&2 ); \ else \ echo A git clone is required to generate a ChangeLog >&2; \ fi EventDance-0.2.0/NEWS000066400000000000000000000000001321356073300142150ustar00rootroot00000000000000EventDance-0.2.0/README000066400000000000000000000053361321356073300144170ustar00rootroot00000000000000EventDance 0.1 README ===================== Peer-to-peer inter-process communication library. EventDance is an open source library for interconnecting heterogeneous applications in a simple, secure and scalable fashion. It provides a nice API to send and receive data among distributed applications over different types of transports. This and other features like cryptography, make EventDance a perfect choice for peer-to-peer application development. EventDance currently requires: * GLib >= 2.28.0 * libsoup-2.4 >= 2.28.0 * gnutls >= 2.12.0 * uuid >= 2.16.0 * json-glib >= 0.14.0 If you are building the API reference you will also need: * GTK-Doc >= 1.11 If you are building the Introspection data you will also need: * GObject-Introspection >= 0.6.7 If you are building the Javascript support you will also need: * GJS >= 0.3 The official website is: (no official website yet) The EventDance blog is at: http://blogs.igalia.com/elima To subscribe to the EventDance mailing list, send mail to: (no mailing list yet) The official mailing list archive is: (no mailing-list archive yet) Bug reporting and tracking: (by now, just mail elima@igalia.com) EventDance is licensed under the terms of the GNU Lesser General Public License, version 3 or (at your option) later. See COPYING for more information. INSTALLATION ============ $ ./autogen.sh $ make # make install See the INSTALL file for details. Specific EventDance options to pass in autogen.sh: --enable-gtk-doc use gtk-doc to build API documentation (default=no). Requires gtk-doc present on system. --enable-introspection build the introspection data. Requires GObject-Introspection present on system. --enable-js enable server-side Javascript tests and examples. Requires GJS, and GObject-Introspection present on system and enabled in build options. --enable-tests enable automated unit and functional tests. Default is enabled. --enable-debug enable debug mode by adding -ggdb, -g3, -O0 and -Werror to CFLAGS. Default is disabled. VERSIONING ========== EventDance uses the common "Linux kernel" versioning system, where even-numbered minor versions are stable and odd-numbered minor versions are development snapshots. Different major versions break both API and ABI but are parallel installable. The same major version with differing minor version is expected to be ABI compatible with other minor versions; differing micro versions are meant just for bug fixing. On odd minor versions the newly added API might still change. The micro version indicates the origin of the release: even micro numbers are only used for released archives; odd micro numbers are only used on the repository. EventDance-0.2.0/autogen.sh000077500000000000000000000023461321356073300155360ustar00rootroot00000000000000#!/bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. PROJECT="EventDance" test "$srcdir" = "." || { echo "You must run this script in the top-level directory" exit 1 } GTKDOCIZE=`which gtkdocize` if test -z $GTKDOCIZE; then echo "*** No gtk-doc support ***" echo "EXTRA_DIST =" > gtk-doc.make else gtkdocize --copy || exit $? # we need to patch gtk-doc.make to support pretty output with # libtool 1.x. Should be fixed in the next version of gtk-doc. # To be more resilient with the various versions of gtk-doc one # can find, just sed gkt-doc.make rather than patch it. sed -e 's#) --mode=compile#) --tag=CC --mode=compile#' gtk-doc.make > gtk-doc.temp \ && mv gtk-doc.temp gtk-doc.make sed -e 's#) --mode=link#) --tag=CC --mode=link#' gtk-doc.make > gtk-doc.temp \ && mv gtk-doc.temp gtk-doc.make fi AUTORECONF=`which autoreconf` if test -z $AUTORECONF; then echo "*** No autoreconf found ***" exit 1 else ACLOCAL="${ACLOCAL-aclocal} $ACLOCAL_FLAGS" autoreconf -v --install || exit $? fi if test x$NOCONFIGURE = x; then ./configure "$@" else echo Skipping configure process. fi EventDance-0.2.0/configure.ac000066400000000000000000000105541321356073300160230ustar00rootroot00000000000000# # configure.ac # # EventDance, Peer-to-peer IPC library # # Copyright (C) 2009-2013, Igalia S.L. # # Authors: # Eduardo Lima Mitev # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # version 3, or (at your option) any later version as published by # the Free Software Foundation. # # 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 at http://www.gnu.org/licenses/lgpl-3.0.txt # for more details. # AC_PREREQ(2.59) m4_define([prj_name], [EventDance]) m4_define([prj_short_name], [evd]) m4_define([prj_home], [http://eventdance.org]) # package version number (not shared library version) # odd micro numbers indicate in-progress development # even micro numbers indicate released versions m4_define([prj_version_major], [0]) m4_define([prj_version_minor], [2]) m4_define([prj_version_micro], [0]) m4_define([prj_version], [prj_version_major.prj_version_minor.prj_version_micro]) m4_define([prj_api_version], [prj_version_major.prj_version_minor]) AC_INIT([prj_name], [prj_version], [prj_home]) AC_CONFIG_HEADERS(config.h) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE # EventDance specific definitions m4_define([prj_gir_namespace], [Evd]) EVD_VERSION="prj_version" AC_SUBST(EVD_VERSION) EVD_API_VERSION="prj_api_version" AC_SUBST(EVD_API_VERSION) EVD_API_NAME="prj_short_name-prj_api_version" AC_SUBST(EVD_API_NAME) # Check for programs AC_PROG_LIBTOOL AC_PROG_CC AC_PROG_INSTALL AM_PROG_CC_C_O # enable pkg-config PKG_PROG_PKG_CONFIG # Required libraries GLIB_REQUIRED=2.28.0 PKG_CHECK_MODULES(GLIB, gio-2.0 >= $GLIB_REQUIRED glib-2.0 >= $GLIB_REQUIRED gthread-2.0 >= $GLIB_REQUIRED gobject-2.0 >= $GLIB_REQUIRED) PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= $GLIB_REQUIRED], [HAVE_GIO_UNIX=yes], [HAVE_GIO_UNIX=no]) AM_CONDITIONAL(HAVE_GIO_UNIX, test x"$HAVE_GIO_UNIX" = x"yes") PKG_CHECK_MODULES(TLS, gnutls >= 3.0.0) PKG_CHECK_MODULES(SOUP, libsoup-2.4 >= 2.28.0) PKG_CHECK_MODULES(UUID, uuid >= 2.16.0) PKG_CHECK_MODULES(JSON, json-glib-1.0 >= 0.14.0) # GObject-Introspection check GOBJECT_INTROSPECTION_CHECK([0.6.7]) if test "x$found_introspection" = "xyes"; then EVD_GIR_NAMESPACE="prj_gir_namespace" AC_SUBST(EVD_GIR_NAMESPACE) EVD_GIR_API_NAME="prj_gir_namespace"-"prj_api_version" AC_SUBST(EVD_GIR_API_NAME) EVD_GIR_TARGET_NAME="prj_gir_namespace"_"prj_version_major"_"prj_version_minor"_gir AC_SUBST(EVD_GIR_TARGET_NAME) INTROSPECTION_GIRDIR=`$PKG_CONFIG --define-variable=prefix='${prefix}' --variable=girdir gobject-introspection-1.0` AC_SUBST(INTROSPECTION_GIRDIR) INTROSPECTION_TYPELIBDIR=`$PKG_CONFIG --define-variable=prefix='${prefix}' --variable=typelibdir gobject-introspection-1.0` AC_SUBST(INTROSPECTION_TYPELIBDIR) fi # Javascript support check JS_CHECK([0.3]) # GTK Doc GTK_DOC_CHECK([1.11]) AM_CONDITIONAL([BUILD_GTK_DOC], [test "x$enable_gtk_doc" = "xyes" || test ! -f "autogen.sh"]) # Silent build m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) # Tests AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests[=@<:@no/yes@:>@]], [Enable automated unit and functional tests [default=yes]]),, [enable_tests=yes]) AM_CONDITIONAL(ENABLE_TESTS, test x"${enable_tests}" = x"yes") # Debug AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug[=@<:@no/yes@:>@]], [Enable debug mode by adding -ggdb, -g3, -O0 and -Werror to CFLAGS [default=no]]),, [enable_debug=no]) AM_CONDITIONAL(ENABLE_DEBUG, test x"${enable_debug}" = x"yes") # Output files AC_OUTPUT([ Makefile evd/Makefile evd/evd-0.2.pc tests/Makefile doc/Makefile doc/reference/Makefile examples/Makefile ]) echo "" echo " EventDance $VERSION" echo " =====================" echo "" echo " Install prefix: ${prefix}" echo " Build introspection data: ${enable_introspection}" echo " Build API documentation: ${enable_gtk_doc}" echo " Enable debug mode: ${enable_debug}" echo " Enable automated tests: ${enable_tests}" echo " Enable Javascript tests: ${enable_js}" echo "" EventDance-0.2.0/doc/000077500000000000000000000000001321356073300142755ustar00rootroot00000000000000EventDance-0.2.0/doc/Makefile.am000066400000000000000000000000711321356073300163270ustar00rootroot00000000000000SUBDIRS=reference MAINTAINERCLEANFILES = \ Makefile.in EventDance-0.2.0/doc/reference/000077500000000000000000000000001321356073300162335ustar00rootroot00000000000000EventDance-0.2.0/doc/reference/.gitignore000066400000000000000000000002761321356073300202300ustar00rootroot00000000000000html/ tmpl/ xml/ *.hierarchy *.interfaces *.prerequisites *.signals *.types *.args *-overrides.txt *-unused.txt *-undocumented.txt *-undeclared.txt *-sections.txt *-decl.txt *-decl-list.txt EventDance-0.2.0/doc/reference/Makefile.am000066400000000000000000000057051321356073300202760ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in # We require automake 1.6 at least. AUTOMAKE_OPTIONS = 1.6 # This is a blank Makefile.am for using gtk-doc. # Copy this to your project's API docs directory and modify the variables to # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples # of using the various options. # The name of the module, e.g. 'glib'. DOC_MODULE=evd # The top-level SGML file. You can change this if you want to. DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml # The directory containing the source code. Relative to $(srcdir). # gtk-doc will search all .c & .h files beneath here for inline comments # documenting the functions and macros. # e.g. DOC_SOURCE_DIR=../../../gtk DOC_SOURCE_DIR=../../evd # Extra options to pass to gtkdoc-scangobj. Not normally needed. SCANGOBJ_OPTIONS= # Extra options to supply to gtkdoc-scan. # e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" SCAN_OPTIONS= # Extra options to supply to gtkdoc-mkdb. # e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space=evd # Extra options to supply to gtkdoc-mktmpl # e.g. MKTMPL_OPTIONS=--only-section-tmpl MKTMPL_OPTIONS= # Extra options to supply to gtkdoc-fixref. Not normally needed. # e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html FIXXREF_OPTIONS=\ --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib # Used for dependencies. The docs will be rebuilt if any of these change. # e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h # e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c HFILE_GLOB=$(top_srcdir)/evd/*.h CFILE_GLOB=$(top_srcdir)/evd/*.c # Header files to ignore when scanning. # e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h IGNORE_HFILES= EXTRA_HFILES= # Images to copy into HTML directory. # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png HTML_IMAGES= # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). # e.g. content_files=running.sgml building.sgml changes-2.0.sgml content_files= \ evd-overview.xml # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # These files must be listed here *and* in content_files # e.g. expand_content_files=running.sgml expand_content_files= \ evd-overview.xml # CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. # Only needed if you are using gtkdoc-scangobj to dynamically query widget # signals and properties. # e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/evd $(GLIB_CFLAGS) GTKDOC_LIBS=$(top_builddir)/evd/libevd-@EVD_API_VERSION@.la $(GLIB_LIBS) # This includes the standard gtk-doc make rules, copied by gtkdocize. include $(top_srcdir)/gtk-doc.make # Other files to distribute # e.g. EXTRA_DIST += version.xml.in EXTRA_DIST += MAINTAINERCLEANFILES = \ evd-overrides.txt evd-sections.txt evd.types Makefile.in maintainer-clean-local: rm -rf tmpl rm -rf html EventDance-0.2.0/doc/reference/evd-docs.xml000066400000000000000000000151371321356073300204700ustar00rootroot00000000000000 ]> EventDance - Documentation and Reference Manual Version &version; 2009-2012 ]]> Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the GNU Free Documentation License from the Free Software Foundation by visiting their Web site or by writing to:
The Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
EventDance Core Reference Abstract classes and interfaces Core Transports All transport classes implement EvdTransport interface. Utils and micellaneous Extending EventDance This section contains additional useful documentation for developers interested in extending the libraries. The Internals @TODO EventDance Objects Object Hierarchy Object Index Glossaries Index of all symbols Index of deprecated symbols License This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 3 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 Library General Public License for more details. You may obtain a copy of the GNU Library General Public License from the Free Software Foundation by visiting their Web site or by writing to:
Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307 USA
EventDance-0.2.0/doc/reference/evd-overview.xml000066400000000000000000000104221321356073300213760ustar00rootroot00000000000000 Overview EventDance is an open source library for interconnecting heterogeneous applications in a simple, secure and scalable fashion. It provides a nice API to send and receive data among distributed applications over different types of transports. This and other features like cryptography, make EventDance a perfect choice for peer-to-peer application development. Key design features This chapter discuss the basic design concepts behind EventDance. Not all features are implemented at the moment, but they surely will be at some point. Thus, by now some of the items in the following list should be considered a declaration of intentions, or goals. EventDance should be: Asynchronous All activity is driven by a main event loop, which no routine should block under any circumstance. If some blocking operation is to be performed, a separate thread shall be spawned for it. All network and disk IO is expected to be non-blocking, both in the core of EventDance and in programs using it. Secure Basic API should be provided for peer authentication and data privacy/integrity at a cryptographic level. Scalable All components should scale reasonably well under high-concurrency situations, and support graceful service degradation in case of excessive load. Any mechanism available in the platform to optimize responsiveness should be used to guarantee scalability. Introspection friendly Libraries are designed to work fine with GObject-Introspection. APIs are well-thought to be used in high level scripting languages like Javascript and Python. Thus, it's a design requirement to have introspection friendly and easy to use APIs, and to always have updated GIR information. Extensible Libraries should be designed so that developers can efficiently modify or extend its behavior, reusing as much code as possible. For that, relevant parts of libraries' internal logic is published as well. See chapter Extending EventDance for more information. Energy efficient This is relative to how synchronous operations and idle states are managed. Routines in the framework or in programs using it should avoid polling continuously for a condition change, unnecessarily feeding the event loop at intervals. Instead, these operations should be moved to a thread and blocked permanently when possible, until a notification of a condition change wakes up the thread. Basic concepts @TODO Architecture @TODO EventDance-0.2.0/evd-jhbuild.modules000066400000000000000000000075701321356073300173300ustar00rootroot00000000000000 EventDance-0.2.0/evd/000077500000000000000000000000001321356073300143065ustar00rootroot00000000000000EventDance-0.2.0/evd/.gitignore000066400000000000000000000000341321356073300162730ustar00rootroot00000000000000evd-marshal.c evd-marshal.h EventDance-0.2.0/evd/Makefile.am000066400000000000000000000124411321356073300163440ustar00rootroot00000000000000MAINTAINERCLEANFILES = \ Makefile.in DISTCLEANFILES = pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = @EVD_API_NAME@.pc library_includedir=$(includedir)/@EVD_API_NAME@ library_include_HEADERS = evd.h jslibdir = $(datadir)/@EVD_API_NAME@/js CLEANFILES = *~ @EVD_API_NAME@.pc AM_CFLAGS = $(GLIB_CFLAGS) -Wall \ -DPKGDATADIR="\"$(pkgdatadir)\"" \ -DJSLIBDIR="\"$(jslibdir)\"" \ -DEVD_COMPILATION if ENABLE_TESTS AM_CFLAGS += -DENABLE_TESTS endif if ENABLE_DEBUG AM_CFLAGS += -Werror -g3 -O0 -ggdb -D_GNU_SOURCE else AM_CFLAGS += -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS endif evd_marshal_list = $(addprefix $(srcdir)/, evd-marshal.list) evd-marshal.h: $(evd_marshal_list) glib-genmarshal --header \ --prefix=evd_marshal $(evd_marshal_list) > evd-marshal.h evd-marshal.c: $(evd_marshal_list) glib-genmarshal --body \ --prefix=evd_marshal $(evd_marshal_list) > evd-marshal.c BUILT_SOURCES = evd-marshal.c evd-marshal.h EXTRA_DIST = $(evd_marshal_list) DISTCLEANFILES += evd-marshal.c evd-marshal.h # libraries lib_LTLIBRARIES = lib@EVD_API_NAME@.la # libevd source_c = \ evd-error.c \ evd-utils.c \ evd-resolver.c \ evd-poll.c \ evd-socket.c \ evd-socket-input-stream.c \ evd-socket-output-stream.c \ evd-tls-input-stream.c \ evd-tls-output-stream.c \ evd-stream-throttle.c \ evd-buffered-input-stream.c \ evd-buffered-output-stream.c \ evd-throttled-input-stream.c \ evd-throttled-output-stream.c \ evd-json-filter.c \ evd-service.c \ evd-tls-common.c \ evd-tls-dh-generator.c \ evd-tls-session.c \ evd-tls-certificate.c \ evd-tls-privkey.c \ evd-tls-credentials.c \ evd-io-stream.c \ evd-connection.c \ evd-io-stream-group.c \ evd-http-connection.c \ evd-web-service.c \ evd-transport.c \ evd-peer.c \ evd-peer-manager.c \ evd-longpolling-server.c \ evd-websocket-protocol.c \ evd-websocket-server.c \ evd-websocket-client.c \ evd-connection-pool.c \ evd-reproxy.c \ evd-web-selector.c \ evd-web-transport-server.c \ evd-http-message.c \ evd-http-request.c \ evd-web-dir.c \ evd-ipc-mechanism.c \ evd-dbus-agent.c \ evd-dbus-bridge.c \ evd-dbus-daemon.c \ evd-jsonrpc.c \ evd-pki-privkey.c \ evd-pki-pubkey.c \ evd-daemon.c \ evd-http-chunked-decoder.c \ evd-jsonrpc-http-client.c \ evd-jsonrpc-http-server.c \ evd-promise.c source_h = \ evd.h \ evd-utils.h \ evd-socket.h \ evd-stream-throttle.h \ evd-buffered-input-stream.h \ evd-buffered-output-stream.h \ evd-throttled-input-stream.h \ evd-throttled-output-stream.h \ evd-service.h \ evd-tls-session.h \ evd-tls-certificate.h \ evd-tls-privkey.h \ evd-tls-credentials.h \ evd-io-stream.h \ evd-connection.h \ evd-io-stream-group.h \ evd-http-connection.h \ evd-web-service.h \ evd-transport.h \ evd-peer.h \ evd-peer-manager.h \ evd-longpolling-server.h \ evd-websocket-server.h \ evd-websocket-client.h \ evd-connection-pool.h \ evd-reproxy.h \ evd-web-selector.h \ evd-web-transport-server.h \ evd-http-message.h \ evd-http-request.h \ evd-web-dir.h \ evd-ipc-mechanism.h \ evd-dbus-bridge.h \ evd-dbus-daemon.h \ evd-jsonrpc.h \ evd-pki-common.h \ evd-pki-privkey.h \ evd-pki-pubkey.h \ evd-daemon.h \ evd-jsonrpc-http-client.h \ evd-jsonrpc-http-server.h \ evd-tls-common.h \ evd-promise.h source_h_priv = \ evd-poll.h \ evd-tls-dh-generator.h \ evd-websocket-protocol.h \ evd-resolver.h \ evd-socket-input-stream.h \ evd-socket-output-stream.h \ evd-tls-input-stream.h \ evd-tls-output-stream.h \ evd-json-filter.h \ evd-http-chunked-decoder.h \ evd-dbus-agent.h \ evd-error.h lib@EVD_API_NAME@_la_LIBADD = \ $(GLIB_LIBS) \ $(UUID_LIBS) \ $(SOUP_LIBS) \ $(TLS_LIBS) \ $(JSON_LIBS) lib@EVD_API_NAME@_la_CFLAGS = \ $(AM_CFLAGS) \ $(UUID_CFLAGS) \ $(SOUP_CFLAGS) \ $(TLS_CFLAGS) \ $(JSON_CFLAGS) if HAVE_GIO_UNIX lib@EVD_API_NAME@_la_LIBADD += \ $(GIO_UNIX_LIBS) lib@EVD_API_NAME@_la_CFLAGS += \ $(GIO_UNIX_CFLAGS) \ -DHAVE_GIO_UNIX \ -DHAVE_JS endif lib@EVD_API_NAME@_la_LDFLAGS = \ -version-info 0:1:0 \ -no-undefined lib@EVD_API_NAME@_la_SOURCES = \ $(source_c) \ $(source_h) nodist_lib@EVD_API_NAME@_la_SOURCES = \ evd-marshal.c \ evd-marshal.h evddir = $(includedir)/@EVD_API_NAME@ evd_HEADERS = \ $(source_h) jslib_DATA = \ js/evdWebTransport.js \ js/evdDBusBridge.js \ js/evdJsonrpc.js # introspection support if HAVE_INTROSPECTION -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) introspection_sources = $(evd_HEADERS) $(source_c) @EVD_GIR_API_NAME@.gir: lib@EVD_API_NAME@.la Makefile @EVD_GIR_TARGET_NAME@_INCLUDES = GObject-2.0 Gio-2.0 Soup-2.4 Json-1.0 @EVD_GIR_TARGET_NAME@_CFLAGS = $(INCLUDES) $(JSON_CFLAGS) @EVD_GIR_TARGET_NAME@_LIBS = @EVD_API_NAME@ uuid @EVD_GIR_TARGET_NAME@_FILES = $(addprefix $(srcdir)/,$(introspection_sources)) @EVD_GIR_TARGET_NAME@_SCANNERFLAGS = \ --warn-all \ --c-include='evd.h' \ --pkg-export=@EVD_API_NAME@ INTROSPECTION_GIRS += @EVD_GIR_API_NAME@.gir girdir = @INTROSPECTION_GIRDIR@ gir_DATA = $(INTROSPECTION_GIRS) typelibdir = @INTROSPECTION_TYPELIBDIR@ typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(dist_gir_DATA) $(typelib_DATA) DISTCLEANFILES += $(INTROSPECTION_GIRS) endif maintainer-clean-local: rm -rf tmp-introspect* EXTRA_DIST += \ $(source_h_priv) \ $(jslib_DATA) EventDance-0.2.0/evd/evd-0.2.pc.in000066400000000000000000000005771321356073300163230ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ datarootdir=@datarootdir@ localedir=@localedir@ Name: EventDance Description: An event distribution framework. Requires: glib-2.0 gio-2.0 gobject-2.0 libsoup-2.4 json-glib-1.0 gnutls uuid Version: @EVD_VERSION@ Libs: -L${libdir} -levd-@EVD_API_VERSION@ Cflags: -I${includedir}/evd-@EVD_API_VERSION@ EventDance-0.2.0/evd/evd-buffered-input-stream.c000066400000000000000000000402351321356073300214420ustar00rootroot00000000000000/* * evd-buffered-input-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-error.h" #include "evd-utils.h" #include "evd-buffered-input-stream.h" G_DEFINE_TYPE (EvdBufferedInputStream, evd_buffered_input_stream, G_TYPE_BUFFERED_INPUT_STREAM) #define EVD_BUFFERED_INPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_BUFFERED_INPUT_STREAM, \ EvdBufferedInputStreamPrivate)) /* private data */ struct _EvdBufferedInputStreamPrivate { GString *buffer; GSimpleAsyncResult *async_result; void *async_buffer; gsize requested_size; gssize actual_size; guint read_src_id; gboolean frozen; }; static void evd_buffered_input_stream_class_init (EvdBufferedInputStreamClass *class); static void evd_buffered_input_stream_init (EvdBufferedInputStream *self); static void evd_buffered_input_stream_finalize (GObject *obj); static gssize evd_buffered_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_buffered_input_stream_read_async (GInputStream *stream, void *buffer, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gssize evd_buffered_input_stream_read_finish (GInputStream *self, GAsyncResult *result, GError **error); static gboolean evd_buffered_input_stream_close (GInputStream *stream, GCancellable *cancellable, GError **error); static void evd_buffered_input_stream_class_init (EvdBufferedInputStreamClass *class) { GObjectClass *obj_class; GInputStreamClass *input_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_buffered_input_stream_finalize; input_stream_class = G_INPUT_STREAM_CLASS (class); input_stream_class->read_fn = evd_buffered_input_stream_read; input_stream_class->read_async = evd_buffered_input_stream_read_async; input_stream_class->read_finish = evd_buffered_input_stream_read_finish; input_stream_class->close_fn = evd_buffered_input_stream_close; g_type_class_add_private (obj_class, sizeof (EvdBufferedInputStreamPrivate)); } static void evd_buffered_input_stream_init (EvdBufferedInputStream *self) { EvdBufferedInputStreamPrivate *priv; priv = EVD_BUFFERED_INPUT_STREAM_GET_PRIVATE (self); self->priv = priv; priv->buffer = g_string_new (""); priv->async_result = NULL; priv->async_buffer = NULL; priv->requested_size = 0; priv->actual_size = 0; priv->read_src_id = 0; priv->frozen = FALSE; } static void evd_buffered_input_stream_finalize (GObject *obj) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (obj); if (self->priv->read_src_id != 0) g_source_remove (self->priv->read_src_id); g_string_free (self->priv->buffer, TRUE); if (self->priv->async_result != NULL) g_object_unref (self->priv->async_result); G_OBJECT_CLASS (evd_buffered_input_stream_parent_class)->finalize (obj); } static gssize evd_buffered_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (stream); gchar *buf; gssize read_from_buf = 0; gssize read_from_stream = 0; if (self->priv->frozen) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Resource temporarily unavailable"); return -1; } /* read from buffer first */ if (self->priv->buffer->len > 0) { read_from_buf = MIN (self->priv->buffer->len, size); g_memmove (buffer, self->priv->buffer->str, read_from_buf); size -= read_from_buf; buf = buffer + read_from_buf; } else { buf = buffer; } /* if not enough, read from base stream */ if (size > 0) { GInputStream *base_stream; GError *_error = NULL; base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (self)); read_from_stream = g_input_stream_read (base_stream, buf, size, cancellable, &_error); if (read_from_stream < 0) { if (read_from_buf > 0) { g_clear_error (&_error); read_from_stream = 0; } else { g_propagate_error (error, _error); } } } if (read_from_stream >= 0 && read_from_buf > 0) g_string_erase (self->priv->buffer, 0, read_from_buf); return read_from_stream + read_from_buf; } static gboolean do_read (gpointer user_data) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (user_data); gssize size; GError *error = NULL; self->priv->read_src_id = 0; if (self->priv->async_result == NULL) return FALSE; size = G_INPUT_STREAM_GET_CLASS (self)->read_fn (G_INPUT_STREAM (self), self->priv->async_buffer, self->priv->requested_size, NULL, &error); if (size < 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { g_error_free (error); return FALSE; } if (size != 0) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; if (size < 0) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } else { self->priv->actual_size += size; } g_input_stream_clear_pending (G_INPUT_STREAM (self)); g_simple_async_result_complete (res); g_object_unref (res); } return FALSE; } static void evd_buffered_input_stream_read_async (GInputStream *stream, void *buffer, gsize size, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (stream); self->priv->async_result = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, evd_buffered_input_stream_read_async); self->priv->async_buffer = buffer; self->priv->requested_size = size; self->priv->actual_size = 0; if (! self->priv->frozen) self->priv->read_src_id = evd_timeout_add (g_main_context_get_thread_default (), 0, io_priority, do_read, self); } static gssize evd_buffered_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (stream); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return self->priv->actual_size; else return -1; } static gboolean evd_buffered_input_stream_close (GInputStream *stream, GCancellable *cancellable, GError **error) { EvdBufferedInputStream *self = EVD_BUFFERED_INPUT_STREAM (stream); if (self->priv->read_src_id != 0) { g_source_remove (self->priv->read_src_id); self->priv->read_src_id = 0; } if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Buffered input stream closed during async operation"); g_simple_async_result_complete (res); g_object_unref (res); } return TRUE; } /* public methods */ EvdBufferedInputStream * evd_buffered_input_stream_new (GInputStream *base_stream) { EvdBufferedInputStream *self; g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_BUFFERED_INPUT_STREAM, "base-stream", base_stream, NULL); return self; } /** * evd_buffered_input_stream_unread: * @self: The #EvdConnection to unread data to. * @buffer: (transfer none): Buffer holding the data to be unread. Can contain nulls. * @size: Number of bytes to unread. * @cancellable: A #GCancellable object, or NULL. * @error: (out) (transfer full): A pointer to a #GError to return, or NULL. * * Stores @size bytes from @buffer in the local read buffer of the socket. Next calls * to read will first get data from the local buffer, before performing the actual read * operation. This is useful when one needs to do some action with a data just read, but doesn't * want to remove the data from the input stream of the socket. * * Normally, it would be used to write back data that was previously read, to made it available * in further calls to read. But in practice any data can be unread. * * This feature was implemented basically to provide type-of-stream detection on a socket * (e.g. a service selector). * * Returns: The actual number of bytes unread. **/ gssize evd_buffered_input_stream_unread (EvdBufferedInputStream *self, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { g_return_val_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self), -1); if (size == 0) return 0; g_return_val_if_fail (buffer != NULL, 0); if (self->priv->buffer->len + size > g_buffered_input_stream_get_buffer_size (G_BUFFERED_INPUT_STREAM (self))) { if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NO_SPACE, "Buffer is full"); return -1; } else { g_string_prepend_len (self->priv->buffer, buffer, size); if (! self->priv->frozen) evd_buffered_input_stream_thaw (self, G_PRIORITY_DEFAULT); return size; } } /** * evd_buffered_input_stream_read_str_sync: * @size: (inout): **/ gchar * evd_buffered_input_stream_read_str_sync (EvdBufferedInputStream *self, gssize *size, GError **error) { void *buf; gssize actual_size; gchar *data = NULL; g_return_val_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self), NULL); g_return_val_if_fail (size != NULL, NULL); if (*size == 0) return NULL; buf = g_slice_alloc ((*size) + 1); if ( (actual_size = g_input_stream_read (G_INPUT_STREAM (self), buf, *size, NULL, error)) >= 0) { if (actual_size > 0) { data = g_new (gchar, actual_size + 1); g_memmove (data, buf, actual_size); data[actual_size] = '\0'; } g_slice_free1 ((*size) + 1, buf); *size = actual_size; } else { (*size) = 0; } return data; } /** * evd_buffered_input_stream_read_str: * @callback: (scope async): the #GAsyncReadyCallback **/ void evd_buffered_input_stream_read_str (EvdBufferedInputStream *self, gsize size, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self)); self->priv->async_buffer = g_new0 (gchar, size + 1); g_input_stream_read_async (G_INPUT_STREAM (self), self->priv->async_buffer, size, io_priority, cancellable, callback, user_data); } /** * evd_buffered_input_stream_read_str_finish: * @size: (out): * * Returns: **/ gchar * evd_buffered_input_stream_read_str_finish (EvdBufferedInputStream *self, GAsyncResult *result, gssize *size, GError **error) { gchar *buf = NULL; gssize _size; g_return_val_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self), NULL); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); if ( (_size = g_input_stream_read_finish (G_INPUT_STREAM (self), result, error)) < 0) { g_free (self->priv->async_buffer); } else { buf = self->priv->async_buffer; } if (size != NULL) *size = _size; self->priv->async_buffer = NULL; return buf; } void evd_buffered_input_stream_freeze (EvdBufferedInputStream *self) { g_return_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self)); self->priv->frozen = TRUE; } void evd_buffered_input_stream_thaw (EvdBufferedInputStream *self, gint priority) { g_return_if_fail (EVD_IS_BUFFERED_INPUT_STREAM (self)); self->priv->frozen = FALSE; if (self->priv->async_result != NULL && self->priv->read_src_id == 0) self->priv->read_src_id = evd_timeout_add (g_main_context_get_thread_default (), 0, priority, do_read, self); } EventDance-0.2.0/evd/evd-buffered-input-stream.h000066400000000000000000000113541321356073300214470ustar00rootroot00000000000000/* * evd-buffered-input-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_BUFFERED_INPUT_STREAM_H__ #define __EVD_BUFFERED_INPUT_STREAM_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _EvdBufferedInputStream EvdBufferedInputStream; typedef struct _EvdBufferedInputStreamClass EvdBufferedInputStreamClass; typedef struct _EvdBufferedInputStreamPrivate EvdBufferedInputStreamPrivate; struct _EvdBufferedInputStream { GBufferedInputStream parent; EvdBufferedInputStreamPrivate *priv; }; struct _EvdBufferedInputStreamClass { GBufferedInputStreamClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_BUFFERED_INPUT_STREAM (evd_buffered_input_stream_get_type ()) #define EVD_BUFFERED_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_BUFFERED_INPUT_STREAM, EvdBufferedInputStream)) #define EVD_BUFFERED_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_BUFFERED_INPUT_STREAM, EvdBufferedInputStreamClass)) #define EVD_IS_BUFFERED_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_BUFFERED_INPUT_STREAM)) #define EVD_IS_BUFFERED_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_BUFFERED_INPUT_STREAM)) #define EVD_BUFFERED_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_BUFFERED_INPUT_STREAM, EvdBufferedInputStreamClass)) GType evd_buffered_input_stream_get_type (void) G_GNUC_CONST; EvdBufferedInputStream *evd_buffered_input_stream_new (GInputStream *base_stream); gssize evd_buffered_input_stream_unread (EvdBufferedInputStream *self, const void *buffer, gsize size, GCancellable *cancellable, GError **error); gchar *evd_buffered_input_stream_read_str_sync (EvdBufferedInputStream *self, gssize *size, GError **error); void evd_buffered_input_stream_read_str (EvdBufferedInputStream *stream, gsize count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar *evd_buffered_input_stream_read_str_finish (EvdBufferedInputStream *self, GAsyncResult *result, gssize *size, GError **error); void evd_buffered_input_stream_freeze (EvdBufferedInputStream *self); void evd_buffered_input_stream_thaw (EvdBufferedInputStream *self, gint priority); G_END_DECLS #endif /* __EVD_BUFFERED_INPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-buffered-output-stream.c000066400000000000000000000555401321356073300216500ustar00rootroot00000000000000/* * evd-buffered-output-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-error.h" #include "evd-utils.h" #include "evd-buffered-output-stream.h" G_DEFINE_TYPE (EvdBufferedOutputStream, evd_buffered_output_stream, G_TYPE_FILTER_OUTPUT_STREAM) #define EVD_BUFFERED_OUTPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_BUFFERED_OUTPUT_STREAM, \ EvdBufferedOutputStreamPrivate)) #define DEFAULT_BUFFER_SIZE 8192 /* private data */ struct _EvdBufferedOutputStreamPrivate { GString *buffer; gsize buffer_size; gboolean auto_grow; gboolean auto_flush; gboolean flushing; gint priority; GSimpleAsyncResult *async_result; gssize actual_size; }; /* properties */ enum { PROP_0, PROP_AUTO_FLUSH }; static void evd_buffered_output_stream_class_init (EvdBufferedOutputStreamClass *class); static void evd_buffered_output_stream_init (EvdBufferedOutputStream *self); static void evd_buffered_output_stream_finalize (GObject *obj); static void evd_buffered_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_buffered_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gssize evd_buffered_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_buffered_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize size, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gssize evd_buffered_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error); static gboolean evd_buffered_output_stream_flush (GOutputStream *stream, GCancellable *cancellable, GError **error); static void evd_buffered_output_stream_flush_async (GOutputStream *stream, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gboolean evd_buffered_output_stream_flush_finish (GOutputStream *stream, GAsyncResult *res, GError **error); static gboolean evd_buffered_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error); static void evd_buffered_output_stream_class_init (EvdBufferedOutputStreamClass *class) { GObjectClass *obj_class; GOutputStreamClass *output_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_buffered_output_stream_finalize; obj_class->get_property = evd_buffered_output_stream_get_property; obj_class->set_property = evd_buffered_output_stream_set_property; output_stream_class = G_OUTPUT_STREAM_CLASS (class); output_stream_class->write_fn = evd_buffered_output_stream_write; output_stream_class->flush = evd_buffered_output_stream_flush; output_stream_class->flush_async = evd_buffered_output_stream_flush_async; output_stream_class->flush_finish = evd_buffered_output_stream_flush_finish; output_stream_class->write_async = evd_buffered_output_stream_write_async; output_stream_class->write_finish = evd_buffered_output_stream_write_finish; output_stream_class->close_fn = evd_buffered_output_stream_close; g_object_class_install_property (obj_class, PROP_AUTO_FLUSH, g_param_spec_boolean ("auto-flush", "Auto flush", "Whether buffered data should be automaticallly flushed", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdBufferedOutputStreamPrivate)); } static void evd_buffered_output_stream_init (EvdBufferedOutputStream *self) { EvdBufferedOutputStreamPrivate *priv; priv = EVD_BUFFERED_OUTPUT_STREAM_GET_PRIVATE (self); self->priv = priv; priv->buffer = g_string_new (""); priv->buffer_size = DEFAULT_BUFFER_SIZE; priv->auto_grow = TRUE; priv->auto_flush = TRUE; priv->flushing = FALSE; priv->priority = G_PRIORITY_DEFAULT; priv->async_result = NULL; priv->actual_size = 0; } static void evd_buffered_output_stream_finalize (GObject *obj) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (obj); g_string_free (self->priv->buffer, TRUE); if (self->priv->async_result != NULL) g_object_unref (self->priv->async_result); G_OBJECT_CLASS (evd_buffered_output_stream_parent_class)->finalize (obj); } static void evd_buffered_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdBufferedOutputStream *self; self = EVD_BUFFERED_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_AUTO_FLUSH: self->priv->auto_flush = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_buffered_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdBufferedOutputStream *self; self = EVD_BUFFERED_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_AUTO_FLUSH: g_value_set_boolean (value, self->priv->auto_flush); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gsize evd_buffered_output_stream_fill (EvdBufferedOutputStream *self, const gchar *buf, gsize size) { gsize buf_size; buf_size = self->priv->buffer_size; if (self->priv->buffer->len + size > buf_size) { if (self->priv->auto_grow) self->priv->buffer_size = self->priv->buffer->len + size; else size = buf_size - self->priv->buffer->len; } if (size > 0) g_string_append_len (self->priv->buffer, buf, size); return size; } static gssize evd_buffered_output_stream_real_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { GOutputStream *base_stream; base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream)); return g_output_stream_write (base_stream, buffer, size, cancellable, error); } static gssize evd_buffered_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (stream); gssize actual_size; gsize buffered_size = 0; GError *_error = NULL; if (self->priv->buffer->len > 0 || ! self->priv->auto_flush) { actual_size = evd_buffered_output_stream_fill (self, buffer, size); } else { actual_size = evd_buffered_output_stream_real_write (stream, buffer, size, cancellable, &_error); if (actual_size < 0) { if (g_error_matches (_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { buffered_size = evd_buffered_output_stream_fill (self, buffer, size); actual_size = 0; g_clear_error (&_error); } else { g_propagate_error (error, _error); } } else if (actual_size < size) { buffered_size = evd_buffered_output_stream_fill (self, buffer + actual_size, size - actual_size); } } return actual_size + buffered_size; } static void evd_buffered_output_stream_write_async (GOutputStream *stream, const void *buffer, gsize size, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (stream); GError *error = NULL; gssize actual_size; GSimpleAsyncResult *res; res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_buffered_output_stream_write_async); actual_size = evd_buffered_output_stream_write (stream, buffer, size, cancellable, &error); if (actual_size < 0) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_output_stream_clear_pending (stream); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } if (actual_size == size) { g_simple_async_result_set_op_res_gssize (res, actual_size); g_output_stream_clear_pending (stream); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } else { /* there was not enough space in the buffer to hold all data */ /* @TODO: cache what was left unbuffered and add it after buffer is flushed */ self->priv->async_result = res; } } static gssize evd_buffered_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); if (! g_simple_async_result_propagate_error (res, error)) { return g_simple_async_result_get_op_res_gssize (res); } else { return -1; } } static void evd_buffered_output_stream_on_base_stream_flushed (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (user_data); GError *error = NULL; g_assert (! g_output_stream_has_pending (G_OUTPUT_STREAM (obj))); if (! g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), result, &error)) { if (self->priv->async_result != NULL) g_simple_async_result_take_error (self->priv->async_result, error); else g_error_free (error); } if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_complete_in_idle (res); g_object_unref (res); } g_output_stream_clear_pending (G_OUTPUT_STREAM (self)); g_object_unref (self); } static void evd_buffered_output_stream_flush_base_stream (EvdBufferedOutputStream *self, GCancellable *cancellable) { GOutputStream *base_stream; base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (self)); g_object_ref (self); g_output_stream_flush_async (base_stream, self->priv->priority, cancellable, evd_buffered_output_stream_on_base_stream_flushed, self); } static gboolean evd_buffered_output_stream_flush (GOutputStream *stream, GCancellable *cancellable, GError **error) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (stream); gsize size; gssize actual_size; GError *_error = NULL; size = self->priv->buffer->len; if (size == 0) return TRUE; actual_size = evd_buffered_output_stream_real_write (stream, self->priv->buffer->str, size, cancellable, &_error); if (actual_size < 0) { if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_from_error (res, _error); g_output_stream_clear_pending (stream); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } g_propagate_error (error, _error); return FALSE; } else if (actual_size > 0) { g_string_erase (self->priv->buffer, 0, actual_size); self->priv->actual_size += actual_size; if (self->priv->async_result != NULL) { gpointer source_tag; source_tag = g_simple_async_result_get_source_tag (self->priv->async_result); if (source_tag == evd_buffered_output_stream_write_async) { /* @TODO */ } else if (source_tag == evd_buffered_output_stream_flush_async && self->priv->buffer->len == 0) { self->priv->flushing = FALSE; evd_buffered_output_stream_flush_base_stream (self, cancellable); } } } return TRUE; } static void evd_buffered_output_stream_flush_async (GOutputStream *stream, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (stream); GError *error = NULL; if (! evd_buffered_output_stream_flush (stream, cancellable, &error)) { GSimpleAsyncResult *res; res = g_simple_async_result_new_from_error (G_OBJECT (self), callback, user_data, error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); g_error_free (error); return; } self->priv->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_buffered_output_stream_flush_async); if (self->priv->buffer->len > 0) self->priv->flushing = TRUE; else evd_buffered_output_stream_flush_base_stream (self, cancellable); } static gboolean evd_buffered_output_stream_flush_finish (GOutputStream *stream, GAsyncResult *res, GError **error) { g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (stream), evd_buffered_output_stream_flush_async), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } static gboolean evd_buffered_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error) { EvdBufferedOutputStream *self = EVD_BUFFERED_OUTPUT_STREAM (stream); if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Buffered output stream is closed"); g_simple_async_result_complete (res); g_object_unref (res); } return TRUE; } /* public methods */ EvdBufferedOutputStream * evd_buffered_output_stream_new (GOutputStream *base_stream) { EvdBufferedOutputStream *self; g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_BUFFERED_OUTPUT_STREAM, "base-stream", base_stream, NULL); return self; } gssize evd_buffered_output_stream_write_str_sync (EvdBufferedOutputStream *self, const gchar *buffer, GError **error) { g_return_val_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self), 0); if (buffer == NULL) return 0; return g_output_stream_write (G_OUTPUT_STREAM (self), buffer, strlen (buffer), NULL, error); } void evd_buffered_output_stream_write_str (EvdBufferedOutputStream *self, const gchar *buffer, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self)); g_output_stream_write_async (G_OUTPUT_STREAM (self), (void *) buffer, strlen (buffer), io_priority, cancellable, callback, user_data); } gssize evd_buffered_output_stream_write_str_finish (EvdBufferedOutputStream *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self), 0); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), 0); return g_output_stream_write_finish (G_OUTPUT_STREAM (self), result, error); } void evd_buffered_output_stream_set_auto_flush (EvdBufferedOutputStream *self, gboolean auto_flush) { g_return_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self)); self->priv->auto_flush = auto_flush; if (self->priv->auto_flush) evd_buffered_output_stream_notify_write (self); } gboolean evd_buffered_output_stream_get_auto_flush (EvdBufferedOutputStream *self) { g_return_val_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self), FALSE); return self->priv->auto_flush; } void evd_buffered_output_stream_notify_write (EvdBufferedOutputStream *self) { g_return_if_fail (EVD_IS_BUFFERED_OUTPUT_STREAM (self)); if ( self->priv->flushing || (self->priv->auto_flush && self->priv->buffer->len > 0) ) { evd_buffered_output_stream_flush (G_OUTPUT_STREAM (self), NULL, NULL); } } EventDance-0.2.0/evd/evd-buffered-output-stream.h000066400000000000000000000104611321356073300216460ustar00rootroot00000000000000/* * evd-buffered-output-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_BUFFERED_OUTPUT_STREAM_H__ #define __EVD_BUFFERED_OUTPUT_STREAM_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _EvdBufferedOutputStream EvdBufferedOutputStream; typedef struct _EvdBufferedOutputStreamClass EvdBufferedOutputStreamClass; typedef struct _EvdBufferedOutputStreamPrivate EvdBufferedOutputStreamPrivate; struct _EvdBufferedOutputStream { GFilterOutputStream parent; EvdBufferedOutputStreamPrivate *priv; }; struct _EvdBufferedOutputStreamClass { GFilterOutputStreamClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_BUFFERED_OUTPUT_STREAM (evd_buffered_output_stream_get_type ()) #define EVD_BUFFERED_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_BUFFERED_OUTPUT_STREAM, EvdBufferedOutputStream)) #define EVD_BUFFERED_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_BUFFERED_OUTPUT_STREAM, EvdBufferedOutputStreamClass)) #define EVD_IS_BUFFERED_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_BUFFERED_OUTPUT_STREAM)) #define EVD_IS_BUFFERED_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_BUFFERED_OUTPUT_STREAM)) #define EVD_BUFFERED_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_BUFFERED_OUTPUT_STREAM, EvdBufferedOutputStreamClass)) GType evd_buffered_output_stream_get_type (void) G_GNUC_CONST; EvdBufferedOutputStream *evd_buffered_output_stream_new (GOutputStream *base_stream); gssize evd_buffered_output_stream_write_str_sync (EvdBufferedOutputStream *self, const gchar *buffer, GError **error); void evd_buffered_output_stream_write_str (EvdBufferedOutputStream *self, const gchar *buffer, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gssize evd_buffered_output_stream_write_str_finish (EvdBufferedOutputStream *self, GAsyncResult *result, GError **error); void evd_buffered_output_stream_set_auto_flush (EvdBufferedOutputStream *self, gboolean auto_flush); gboolean evd_buffered_output_stream_get_auto_flush (EvdBufferedOutputStream *self); void evd_buffered_output_stream_notify_write (EvdBufferedOutputStream *self); G_END_DECLS #endif /* __EVD_BUFFERED_OUTPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-connection-pool.c000066400000000000000000000471451321356073300203470ustar00rootroot00000000000000/* * evd-connection-pool.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-connection-pool.h" #include "evd-utils.h" #include "evd-error.h" #include "evd-socket.h" G_DEFINE_TYPE (EvdConnectionPool, evd_connection_pool, EVD_TYPE_IO_STREAM_GROUP) #define EVD_CONNECTION_POOL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_CONNECTION_POOL, \ EvdConnectionPoolPrivate)) #define DEFAULT_MIN_CONNS 1 #define DEFAULT_MAX_CONNS 5 #define RETRY_TIMEOUT 500 /* in miliseconds */ #define TOTAL_SOCKETS(pool) (self->priv->connecting_sockets + \ g_queue_get_length (pool->priv->conns)) #define HAS_REQUESTS(pool) (g_queue_get_length (pool->priv->requests) > 0) /* private data */ struct _EvdConnectionPoolPrivate { gchar *target; guint min_conns; guint max_conns; GQueue *conns; GQueue *requests; GType connection_type; guint connecting_sockets; gboolean tls_autostart; EvdTlsCredentials *tls_cred; guint retry_src_id; }; /* properties */ enum { PROP_0, PROP_ADDRESS, PROP_CONNECTION_TYPE }; static void evd_connection_pool_class_init (EvdConnectionPoolClass *class); static void evd_connection_pool_init (EvdConnectionPool *self); static void evd_connection_pool_constructed (GObject *obj); static void evd_connection_pool_dispose (GObject *obj); static void evd_connection_pool_finalize (GObject *obj); static void evd_connection_pool_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_connection_pool_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gboolean group_add_stream (EvdIoStreamGroup *group, GIOStream *io_stream); static gboolean group_remove_stream (EvdIoStreamGroup *group, GIOStream *io_stream); static void evd_connection_pool_unref_request (GSimpleAsyncResult *result); static void evd_connection_pool_create_new_socket (EvdConnectionPool *self); static gboolean evd_connection_pool_create_min_conns (gpointer user_data); static void free_connection_in_queue (gpointer user_data); static void evd_connection_pool_class_init (EvdConnectionPoolClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIoStreamGroupClass *io_stream_group_class = EVD_IO_STREAM_GROUP_CLASS (class); obj_class->constructed = evd_connection_pool_constructed; obj_class->dispose = evd_connection_pool_dispose; obj_class->finalize = evd_connection_pool_finalize; obj_class->set_property = evd_connection_pool_set_property; obj_class->get_property = evd_connection_pool_get_property; io_stream_group_class->add = group_add_stream; io_stream_group_class->remove = group_remove_stream; g_object_class_install_property (obj_class, PROP_ADDRESS, g_param_spec_string ("address", "Address", "The target socket address to connect to", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_CONNECTION_TYPE, g_param_spec_gtype ("connection-type", "Connection type", "The GType of the connections handled by the pool", EVD_TYPE_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdConnectionPoolPrivate)); } static void evd_connection_pool_init (EvdConnectionPool *self) { EvdConnectionPoolPrivate *priv; priv = EVD_CONNECTION_POOL_GET_PRIVATE (self); self->priv = priv; priv->min_conns = DEFAULT_MIN_CONNS; priv->max_conns = DEFAULT_MAX_CONNS; priv->conns = g_queue_new (); priv->requests = g_queue_new (); priv->connecting_sockets = 0; priv->tls_autostart = FALSE; priv->tls_cred = NULL; priv->retry_src_id = 0; } static void evd_connection_pool_constructed (GObject *obj) { EvdConnectionPool *self = EVD_CONNECTION_POOL (obj); evd_connection_pool_create_min_conns (self); G_OBJECT_CLASS (evd_connection_pool_parent_class)->constructed (obj); } static void evd_connection_pool_dispose (GObject *obj) { EvdConnectionPool *self = EVD_CONNECTION_POOL (obj); if (self->priv->conns != NULL) { g_queue_free_full (self->priv->conns, free_connection_in_queue); self->priv->conns = NULL; } if (self->priv->requests != NULL) { g_queue_free_full (self->priv->requests, (GDestroyNotify) evd_connection_pool_unref_request); self->priv->requests = NULL; } G_OBJECT_CLASS (evd_connection_pool_parent_class)->dispose (obj); } static void evd_connection_pool_finalize (GObject *obj) { EvdConnectionPool *self = EVD_CONNECTION_POOL (obj); g_free (self->priv->target); if (self->priv->retry_src_id != 0) { g_source_remove (self->priv->retry_src_id); self->priv->retry_src_id = 0; } G_OBJECT_CLASS (evd_connection_pool_parent_class)->finalize (obj); } static void evd_connection_pool_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdConnectionPool *self; self = EVD_CONNECTION_POOL (obj); switch (prop_id) { case PROP_ADDRESS: if (self->priv->target != NULL) g_free (self->priv->target); self->priv->target = g_value_dup_string (value); break; case PROP_CONNECTION_TYPE: { GType conn_type; conn_type = g_value_get_gtype (value); if (g_type_is_a (conn_type, EVD_TYPE_CONNECTION)) self->priv->connection_type = conn_type; else g_warning ("Invalid connection type for EvdConnectionPool"); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_connection_pool_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdConnectionPool *self; self = EVD_CONNECTION_POOL (obj); switch (prop_id) { case PROP_ADDRESS: g_value_set_string (value, self->priv->target); break; case PROP_CONNECTION_TYPE: g_value_set_gtype (value, self->priv->connection_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void connection_on_close (EvdConnection *conn, gpointer user_data) { EvdConnectionPool *self = EVD_CONNECTION_POOL (user_data); evd_io_stream_group_remove (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); evd_connection_pool_create_min_conns (self); } static void free_connection_in_queue (gpointer user_data) { EvdIoStream *io_stream; g_assert (EVD_IS_CONNECTION (user_data)); io_stream = EVD_IO_STREAM (user_data); g_assert (EVD_IS_CONNECTION_POOL (evd_io_stream_get_group (io_stream))); g_signal_handlers_disconnect_by_func (io_stream, connection_on_close, evd_io_stream_get_group (io_stream)); g_object_unref (io_stream); } static void evd_connection_pool_finish_request (EvdConnectionPool *self, EvdConnection *conn, GSimpleAsyncResult *res) { g_object_ref (conn); g_simple_async_result_set_op_res_gpointer (res, conn, g_object_unref); g_simple_async_result_complete_in_idle (res); g_object_unref (res); evd_io_stream_group_remove (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); } static void connection_available (EvdConnectionPool *self, EvdConnection *conn) { if (HAS_REQUESTS (self)) { GSimpleAsyncResult *res; res = G_SIMPLE_ASYNC_RESULT (g_queue_pop_head (self->priv->requests)); evd_connection_pool_finish_request (self, conn, res); evd_connection_pool_create_min_conns (self); } else { g_signal_connect (conn, "close", G_CALLBACK (connection_on_close), self); g_queue_push_tail (self->priv->conns, g_object_ref (conn)); } } static void connection_on_tls_started (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdConnectionPool *self = EVD_CONNECTION_POOL (user_data); EvdConnection *conn = EVD_CONNECTION (obj); GError *error = NULL; if (evd_connection_starttls_finish (conn, res, &error)) { connection_available (self, conn); } else { /* @TODO: do proper logging */ g_print ("TLS upgrade error in EvdConnectionPool: %s\n", error->message); g_error_free (error); g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); } g_object_unref (self); } static void connection_starttls (EvdConnectionPool *self, EvdConnection *conn) { EvdTlsSession *tls_session; EvdTlsCredentials *tls_cred; tls_session = evd_connection_get_tls_session (conn); tls_cred = evd_connection_pool_get_tls_credentials (self); evd_tls_session_set_credentials (tls_session, tls_cred); g_object_ref (self); evd_connection_starttls (conn, EVD_TLS_MODE_CLIENT, NULL, connection_on_tls_started, self); } static gboolean group_add_stream (EvdIoStreamGroup *group, GIOStream *io_stream) { EvdConnectionPool *self = EVD_CONNECTION_POOL (group); EvdConnection *conn = EVD_CONNECTION (io_stream); if (! EVD_IO_STREAM_GROUP_CLASS (evd_connection_pool_parent_class)->add (group, io_stream)) { return FALSE; } if (self->priv->tls_autostart && ! evd_connection_get_tls_active (conn)) connection_starttls (self, conn); else connection_available (self, conn); return TRUE; } static gboolean group_remove_stream (EvdIoStreamGroup *group, GIOStream *io_stream) { EvdConnectionPool *self = EVD_CONNECTION_POOL (group); if (! EVD_IO_STREAM_GROUP_CLASS (evd_connection_pool_parent_class)->remove (group, io_stream)) { return FALSE; } g_signal_handlers_disconnect_by_func (io_stream, connection_on_close, self); if (g_queue_remove (self->priv->conns, io_stream)) g_object_unref (io_stream); return TRUE; } static void evd_connection_pool_unref_request (GSimpleAsyncResult *result) { g_simple_async_result_set_error (result, G_IO_ERROR, G_IO_ERROR_CLOSED, "Connection pool destroyed"); g_simple_async_result_complete (result); g_object_unref (result); } static gboolean evd_connection_pool_create_min_conns (gpointer user_data) { EvdConnectionPool *self = EVD_CONNECTION_POOL (user_data); self->priv->retry_src_id = 0; while (TOTAL_SOCKETS (self) < self->priv->min_conns) { evd_connection_pool_create_new_socket (self); } return FALSE; } static void evd_connection_pool_socket_on_connect (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdConnectionPool *self = EVD_CONNECTION_POOL (user_data); EvdSocket *socket = EVD_SOCKET (obj); GIOStream *io_stream; GError *error = NULL; self->priv->connecting_sockets--; if ( (io_stream = evd_socket_connect_finish (socket, res, &error)) != NULL) { /* remove any retry timoeut source */ if (self->priv->retry_src_id != 0) { g_source_remove (self->priv->retry_src_id); self->priv->retry_src_id = 0; } evd_io_stream_group_add (EVD_IO_STREAM_GROUP (self), io_stream); g_object_unref (io_stream); } else { /* @TODO: log properly */ g_print ("Connection pool error: %s\n", error->message); g_error_free (error); /* retry after a timeout */ self->priv->retry_src_id = evd_timeout_add (NULL, RETRY_TIMEOUT, G_PRIORITY_LOW, evd_connection_pool_create_min_conns, self); } g_object_unref (socket); g_object_unref (self); } static void evd_connection_pool_create_new_socket (EvdConnectionPool *self) { EvdSocket *socket; EvdConnectionPoolClass *class; socket = evd_socket_new (); class = EVD_CONNECTION_POOL_GET_CLASS (self); if (class->get_connection_type != NULL) { GType conn_type; conn_type = class->get_connection_type (self); if (g_type_is_a (conn_type, EVD_TYPE_CONNECTION)) self->priv->connection_type = conn_type; else g_warning ("Invalid connection type for EvdConnectionPool"); } g_object_set (socket, "io-stream-type", self->priv->connection_type, NULL); self->priv->connecting_sockets++; g_object_ref (self); evd_socket_connect_to (socket, self->priv->target, NULL, evd_connection_pool_socket_on_connect, self); } /* public methods */ EvdConnectionPool * evd_connection_pool_new (const gchar *address, GType connection_type) { EvdConnectionPool *self; g_return_val_if_fail (address != NULL, NULL); g_return_val_if_fail (g_type_is_a (connection_type, EVD_TYPE_CONNECTION), NULL); self = g_object_new (EVD_TYPE_CONNECTION_POOL, "address", address, "connection-type", connection_type, NULL); return self; } gboolean evd_connection_pool_has_free_connections (EvdConnectionPool *self) { g_return_val_if_fail (EVD_IS_CONNECTION_POOL (self), FALSE); return g_queue_get_length (self->priv->conns) > 0; } void evd_connection_pool_get_connection (EvdConnectionPool *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_CONNECTION_POOL (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_connection_pool_get_connection); if (g_queue_get_length (self->priv->conns) > 0) { EvdConnection *conn; conn = EVD_CONNECTION (g_queue_pop_head (self->priv->conns)); evd_connection_pool_finish_request (self, conn, res); g_object_unref (conn); evd_connection_pool_create_min_conns (self); } else { g_queue_push_tail (self->priv->requests, res); evd_connection_pool_create_min_conns (self); } } /** * evd_connection_pool_get_connection_finish: * * Returns: (transfer full): **/ EvdConnection * evd_connection_pool_get_connection_finish (EvdConnectionPool *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_CONNECTION_POOL (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_connection_pool_get_connection), NULL); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { EvdConnection *conn; conn = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); g_object_ref (conn); return conn; } else { return NULL; } } gboolean evd_connection_pool_recycle (EvdConnectionPool *self, EvdConnection *conn) { g_return_val_if_fail (EVD_IS_CONNECTION_POOL (self), FALSE); g_return_val_if_fail (EVD_IS_CONNECTION (conn), FALSE); if (g_io_stream_is_closed (G_IO_STREAM (conn))) return FALSE; if (TOTAL_SOCKETS (self) >= self->priv->max_conns) return FALSE; return evd_io_stream_group_add (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); } void evd_connection_pool_set_tls_autostart (EvdConnectionPool *self, gboolean autostart) { g_return_if_fail (EVD_IS_CONNECTION_POOL (self)); self->priv->tls_autostart = autostart; if (autostart) { gpointer item; EvdConnection *conn; item = g_queue_pop_head (self->priv->conns); while (item != NULL) { conn = EVD_CONNECTION (item); connection_starttls (self, conn); g_object_unref (conn); item = g_queue_pop_head (self->priv->conns); } } } gboolean evd_connection_pool_get_tls_autostart (EvdConnectionPool *self) { g_return_val_if_fail (EVD_IS_CONNECTION_POOL (self), FALSE); return self->priv->tls_autostart; } void evd_connection_pool_set_tls_credentials (EvdConnectionPool *self, EvdTlsCredentials *credentials) { g_return_if_fail (EVD_IS_CONNECTION_POOL (self)); g_return_if_fail (EVD_IS_TLS_CREDENTIALS (credentials)); if (self->priv->tls_cred != NULL) g_object_unref (self->priv->tls_cred); self->priv->tls_cred = credentials; g_object_ref (self->priv->tls_cred); } /** * evd_connection_pool_get_tls_credentials: * * Returns: (transfer none): **/ EvdTlsCredentials * evd_connection_pool_get_tls_credentials (EvdConnectionPool *self) { g_return_val_if_fail (EVD_IS_CONNECTION_POOL (self), NULL); if (self->priv->tls_cred == NULL) self->priv->tls_cred = evd_tls_credentials_new (); return self->priv->tls_cred; } EventDance-0.2.0/evd/evd-connection-pool.h000066400000000000000000000102551321356073300203440ustar00rootroot00000000000000/* * evd-connection-pool.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_CONNECTION_POOL_H__ #define __EVD_CONNECTION_POOL_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-io-stream-group.h" #include "evd-connection.h" #include "evd-tls-credentials.h" G_BEGIN_DECLS typedef struct _EvdConnectionPool EvdConnectionPool; typedef struct _EvdConnectionPoolClass EvdConnectionPoolClass; typedef struct _EvdConnectionPoolPrivate EvdConnectionPoolPrivate; struct _EvdConnectionPool { EvdIoStreamGroup parent; EvdConnectionPoolPrivate *priv; }; struct _EvdConnectionPoolClass { EvdIoStreamGroupClass parent_class; GType (* get_connection_type) (EvdConnectionPool *self); /* padding for future expansion */ void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_CONNECTION_POOL (evd_connection_pool_get_type ()) #define EVD_CONNECTION_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_CONNECTION_POOL, EvdConnectionPool)) #define EVD_CONNECTION_POOL_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_CONNECTION_POOL, EvdConnectionPoolClass)) #define EVD_IS_CONNECTION_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_CONNECTION_POOL)) #define EVD_IS_CONNECTION_POOL_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_CONNECTION_POOL)) #define EVD_CONNECTION_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_CONNECTION_POOL, EvdConnectionPoolClass)) GType evd_connection_pool_get_type (void) G_GNUC_CONST; EvdConnectionPool *evd_connection_pool_new (const gchar *address, GType connection_type); gboolean evd_connection_pool_has_free_connections (EvdConnectionPool *self); void evd_connection_pool_get_connection (EvdConnectionPool *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); EvdConnection *evd_connection_pool_get_connection_finish (EvdConnectionPool *self, GAsyncResult *result, GError **error); gboolean evd_connection_pool_recycle (EvdConnectionPool *self, EvdConnection *conn); void evd_connection_pool_set_tls_autostart (EvdConnectionPool *self, gboolean autostart); gboolean evd_connection_pool_get_tls_autostart (EvdConnectionPool *self); void evd_connection_pool_set_tls_credentials (EvdConnectionPool *self, EvdTlsCredentials *credentials); EvdTlsCredentials *evd_connection_pool_get_tls_credentials (EvdConnectionPool *self); G_END_DECLS #endif /* __EVD_CONNECTION_POOL_H__ */ EventDance-0.2.0/evd/evd-connection.c000066400000000000000000001200601321356073300173640ustar00rootroot00000000000000/* * evd-connection.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifdef HAVE_GIO_UNIX #include #endif #include "evd-connection.h" #include "evd-error.h" #include "evd-utils.h" #include "evd-marshal.h" #include "evd-socket-input-stream.h" #include "evd-socket-output-stream.h" #include "evd-buffered-input-stream.h" #include "evd-buffered-output-stream.h" #include "evd-throttled-input-stream.h" #include "evd-throttled-output-stream.h" #include "evd-tls-input-stream.h" #include "evd-tls-output-stream.h" G_DEFINE_TYPE (EvdConnection, evd_connection, EVD_TYPE_IO_STREAM) #define EVD_CONNECTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_CONNECTION, \ EvdConnectionPrivate)) #define CLOSED(conn) (g_io_stream_is_closed (G_IO_STREAM (conn))) #define READ_PENDING(conn) (conn->priv->buf_input_stream != NULL && \ g_input_stream_has_pending (G_INPUT_STREAM (conn->priv->buf_input_stream))) #define TLS_SESSION(conn) (evd_connection_get_tls_session (conn)) /* private data */ struct _EvdConnectionPrivate { EvdSocket *socket; EvdSocketInputStream *socket_input_stream; EvdSocketOutputStream *socket_output_stream; EvdTlsInputStream *tls_input_stream; EvdTlsOutputStream *tls_output_stream; EvdBufferedInputStream *buf_input_stream; EvdBufferedOutputStream *buf_output_stream; EvdThrottledInputStream *throt_input_stream; EvdThrottledOutputStream *throt_output_stream; GIOCondition cond; gboolean delayed_close; gboolean close_locked; gint read_src_id; gint write_src_id; gint close_src_id; gboolean tls_handshaking; gboolean tls_active; EvdTlsSession *tls_session; GSimpleAsyncResult *async_result; gboolean connected; gboolean closing; gchar *remote_addr_st; }; /* signals */ enum { SIGNAL_WRITE, SIGNAL_LAST }; static guint evd_connection_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_SOCKET, PROP_TLS_SESSION, PROP_TLS_ACTIVE }; static void evd_connection_class_init (EvdConnectionClass *class); static void evd_connection_init (EvdConnection *self); static void evd_connection_finalize (GObject *obj); static void evd_connection_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_connection_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static GInputStream *evd_connection_get_input_stream (GIOStream *stream); static GOutputStream *evd_connection_get_output_stream (GIOStream *stream); static void on_group_changed (EvdIoStream *io_stream, EvdIoStreamGroup *new_group, EvdIoStreamGroup *old_group); static gboolean evd_connection_close_internal (GIOStream *stream, GCancellable *cancellable, GError **error); static void evd_connection_close_in_idle (EvdConnection *self); static gboolean evd_connection_close_in_idle_cb (gpointer user_data); static void evd_connection_setup_streams (EvdConnection *self); static void evd_connection_teardown_streams (EvdConnection *self); static void evd_connection_socket_on_status_changed (EvdSocket *socket, EvdSocketState new_status, EvdSocketState old_status, gpointer user_data); static void evd_connection_socket_on_error (EvdSocket *socket, guint32 error_domain, gint error_code, gchar *message, gpointer user_data); static void evd_connection_delay_read (EvdThrottledInputStream *stream, guint wait, gpointer user_data); static void evd_connection_delay_write (EvdThrottledOutputStream *stream, guint wait, gpointer user_data); static void evd_connection_class_init (EvdConnectionClass *class) { GObjectClass *obj_class; GIOStreamClass *io_stream_class; EvdIoStreamClass *evd_io_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_connection_finalize; obj_class->get_property = evd_connection_get_property; obj_class->set_property = evd_connection_set_property; io_stream_class = G_IO_STREAM_CLASS (class); io_stream_class->get_input_stream = evd_connection_get_input_stream; io_stream_class->get_output_stream = evd_connection_get_output_stream; io_stream_class->close_fn = evd_connection_close_internal; evd_io_stream_class = EVD_IO_STREAM_CLASS (class); evd_io_stream_class->group_changed = on_group_changed; evd_connection_signals[SIGNAL_WRITE] = g_signal_new ("write", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdConnectionClass, write), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_object_class_install_property (obj_class, PROP_SOCKET, g_param_spec_object ("socket", "The connection's socket", "The socket this HTTP connection will use", EVD_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TLS_SESSION, g_param_spec_object ("tls", "The SSL/TLS session", "The underlaying SSL/TLS session object", EVD_TYPE_TLS_SESSION, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TLS_ACTIVE, g_param_spec_boolean ("tls-active", "Tells whether SSL/TLS is active", "Returns TRUE if connection has SSL/TLS active, FALSE otherwise. SSL/TLS is activated by calling 'starttls' on a connection", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdConnectionPrivate)); } static void evd_connection_init (EvdConnection *self) { EvdConnectionPrivate *priv; priv = EVD_CONNECTION_GET_PRIVATE (self); self->priv = priv; priv->tls_handshaking = FALSE; priv->delayed_close = FALSE; priv->close_locked = FALSE; priv->read_src_id = 0; priv->write_src_id = 0; priv->close_src_id = 0; priv->async_result = NULL; priv->tls_session = NULL; priv->tls_active = FALSE; priv->connected = FALSE; priv->closing = FALSE; priv->cond = 0; priv->remote_addr_st = NULL; } static void evd_connection_finalize (GObject *obj) { EvdConnection *self = EVD_CONNECTION (obj); evd_connection_teardown_streams (self); if (self->priv->tls_session != NULL) g_object_unref (self->priv->tls_session); if (self->priv->async_result != NULL) g_object_unref (self->priv->async_result); g_free (self->priv->remote_addr_st); G_OBJECT_CLASS (evd_connection_parent_class)->finalize (obj); } static void evd_connection_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdConnection *self; self = EVD_CONNECTION (obj); switch (prop_id) { case PROP_SOCKET: evd_connection_set_socket (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_connection_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdConnection *self; self = EVD_CONNECTION (obj); switch (prop_id) { case PROP_SOCKET: g_value_set_object (value, evd_connection_get_socket (self)); break; case PROP_TLS_SESSION: g_value_set_object (value, evd_connection_get_tls_session (self)); break; case PROP_TLS_ACTIVE: g_value_set_boolean (value, evd_connection_get_tls_active (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static GInputStream * evd_connection_get_input_stream (GIOStream *stream) { EvdConnection *self = EVD_CONNECTION (stream); return G_INPUT_STREAM (self->priv->buf_input_stream); } static GOutputStream * evd_connection_get_output_stream (GIOStream *stream) { EvdConnection *self = EVD_CONNECTION (stream); return G_OUTPUT_STREAM (self->priv->buf_output_stream); } static void on_group_changed (EvdIoStream *io_stream, EvdIoStreamGroup *new_group, EvdIoStreamGroup *old_group) { EvdConnection *self = EVD_CONNECTION (io_stream); EvdStreamThrottle *input_throttle; EvdStreamThrottle *output_throttle; if (old_group != NULL) { g_object_get (old_group, "input-throttle", &input_throttle, "output-throttle", &output_throttle, NULL); evd_throttled_input_stream_remove_throttle (self->priv->throt_input_stream, input_throttle); evd_throttled_output_stream_remove_throttle (self->priv->throt_output_stream, output_throttle); g_object_unref (input_throttle); g_object_unref (output_throttle); } if (new_group != NULL) { g_object_get (new_group, "input-throttle", &input_throttle, "output-throttle", &output_throttle, NULL); evd_throttled_input_stream_add_throttle (self->priv->throt_input_stream, input_throttle); evd_throttled_output_stream_add_throttle (self->priv->throt_output_stream, output_throttle); g_object_unref (input_throttle); g_object_unref (output_throttle); } } static gboolean evd_connection_close_internal (GIOStream *stream, GCancellable *cancellable, GError **error) { EvdConnection *self = EVD_CONNECTION (stream); gboolean result = TRUE; GError *_error = NULL; if (self->priv->closing) return TRUE; self->priv->closing = TRUE; self->priv->connected = FALSE; if (self->priv->close_src_id != 0) { g_source_remove (self->priv->close_src_id); self->priv->close_src_id = 0; g_object_unref (self); } if (self->priv->read_src_id != 0) { g_source_remove (self->priv->read_src_id); self->priv->read_src_id = 0; g_object_unref (self); } if (self->priv->write_src_id != 0) { g_source_remove (self->priv->write_src_id); self->priv->write_src_id = 0; g_object_unref (self); } if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; if (self->priv->tls_handshaking) g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Connection closed during TLS handshake"); g_simple_async_result_complete (res); g_object_unref (res); } self->priv->tls_handshaking = FALSE; self->priv->tls_active = FALSE; if (self->priv->tls_output_stream != NULL) { g_output_stream_clear_pending (G_OUTPUT_STREAM (self->priv->tls_output_stream)); if (! g_output_stream_close (G_OUTPUT_STREAM (self->priv->tls_output_stream), NULL, &_error)) { result = FALSE; } } g_signal_handlers_disconnect_by_func (self->priv->throt_input_stream, evd_connection_delay_read, self); g_signal_handlers_disconnect_by_func (self->priv->throt_output_stream, evd_connection_delay_write, self); g_output_stream_clear_pending (G_OUTPUT_STREAM (self->priv->buf_output_stream)); if (! g_output_stream_close (G_OUTPUT_STREAM (self->priv->buf_output_stream), NULL, _error == NULL ? &_error : NULL)) { result = FALSE; } g_input_stream_clear_pending (G_INPUT_STREAM (self->priv->buf_input_stream)); if (! g_input_stream_close (G_INPUT_STREAM (self->priv->buf_input_stream), NULL, (_error == NULL) ? &_error : NULL)) { result = FALSE; } g_signal_handlers_disconnect_by_func (self->priv->socket, evd_connection_socket_on_status_changed, self); g_signal_handlers_disconnect_by_func (self->priv->socket, evd_connection_socket_on_error, self); evd_socket_set_notify_condition_callback (self->priv->socket, NULL, NULL); evd_socket_close (self->priv->socket, (_error == NULL) ? &_error : NULL); if (_error != NULL) g_propagate_error (error, _error); G_IO_STREAM_CLASS (evd_connection_parent_class)->close_fn (stream, cancellable, error); self->priv->closing = FALSE; return result; } static void evd_connection_socket_on_status_changed (EvdSocket *socket, EvdSocketState new_status, EvdSocketState old_status, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (new_status == EVD_SOCKET_STATE_CONNECTED) { self->priv->connected = TRUE; evd_buffered_output_stream_set_auto_flush (self->priv->buf_output_stream, TRUE); } else if (new_status == EVD_SOCKET_STATE_CLOSED) { if (self->priv->connected) evd_connection_close_in_idle (self); } } static void evd_connection_socket_on_error (EvdSocket *socket, guint32 error_domain, gint error_code, gchar *message, gpointer user_data) { /* @TODO: report these errors through an own 'error' signal */ } static gboolean evd_connection_close_in_idle_cb (gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); GError *error = NULL; self->priv->close_src_id = 0; g_io_stream_clear_pending (G_IO_STREAM (self)); if (! g_io_stream_close (G_IO_STREAM (self), NULL, &error)) { /* @TODO: handle error */ g_debug ("error closing connection: %s", error->message); g_error_free (error); } g_object_unref (self); return FALSE; } static void evd_connection_close_in_idle (EvdConnection *self) { self->priv->connected = FALSE; if (self->priv->close_src_id == 0) { g_object_ref (self); self->priv->close_src_id = evd_timeout_add (NULL, 0, evd_socket_get_priority (self->priv->socket), evd_connection_close_in_idle_cb, self); } } static void evd_connection_tls_handshake (EvdConnection *self) { GError *error = NULL; GIOCondition direction; gint result; GSimpleAsyncResult *res; direction = evd_tls_session_get_direction (TLS_SESSION (self)); if ( (direction == G_IO_IN && self->priv->read_src_id != 0) || (direction == G_IO_OUT && self->priv->write_src_id != 0) ) return; result = evd_tls_session_handshake (TLS_SESSION (self), &error); if (result == 0) return; self->priv->tls_handshaking = FALSE; res = self->priv->async_result; self->priv->async_result = NULL; if (result < 0) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_simple_async_result_complete_in_idle (res); g_object_unref (res); if (result > 0) { evd_buffered_output_stream_set_auto_flush (self->priv->buf_output_stream, TRUE); evd_buffered_input_stream_thaw (self->priv->buf_input_stream, evd_socket_get_priority (self->priv->socket)); } else { g_object_ref (self); evd_connection_close_in_idle_cb (self); } } static void evd_connection_manage_read_condition (EvdConnection *self) { if (self->priv->tls_handshaking) evd_connection_tls_handshake (self); else evd_buffered_input_stream_thaw (self->priv->buf_input_stream, evd_socket_get_priority (self->priv->socket)); } static void evd_connection_manage_write_condition (EvdConnection *self) { if (self->priv->tls_handshaking) evd_connection_tls_handshake (self); else evd_buffered_output_stream_notify_write (self->priv->buf_output_stream); if (! CLOSED (self) && self->priv->tls_output_stream != NULL) evd_buffered_output_stream_notify_write (EVD_BUFFERED_OUTPUT_STREAM (self->priv->tls_output_stream)); if (! self->priv->tls_handshaking && evd_connection_get_max_writable (self) > 0) { g_signal_emit (self, evd_connection_signals[SIGNAL_WRITE], 0, NULL); } } static gboolean evd_connection_read_wait_timeout (gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (! CLOSED (self)) { self->priv->read_src_id = 0; evd_connection_manage_read_condition (self); if (self->priv->delayed_close && ! READ_PENDING (self)) { g_object_ref (self); evd_connection_close_in_idle_cb (self); } } g_object_unref (self); return FALSE; } static gboolean evd_connection_write_wait_timeout (gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (! CLOSED (self)) { self->priv->write_src_id = 0; evd_connection_manage_write_condition (self); } g_object_unref (self); return FALSE; } static void evd_connection_socket_on_condition (EvdSocket *socket, GIOCondition condition, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (CLOSED (self)) return; self->priv->cond = condition; if ( (condition & G_IO_IN) > 0 && self->priv->read_src_id == 0) evd_connection_manage_read_condition (self); if (condition & G_IO_HUP) { if (self->priv->close_locked || self->priv->read_src_id != 0 || READ_PENDING (self)) { self->priv->delayed_close = TRUE; } else { evd_connection_close_in_idle (self); } } else if ( (condition & G_IO_OUT) > 0 && self->priv->write_src_id == 0) { evd_connection_manage_write_condition (self); } } static void evd_connection_socket_input_stream_drained (GInputStream *stream, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (CLOSED (self)) return; if (self->priv->delayed_close && ! self->priv->close_locked) { evd_connection_close_in_idle (self); } else { GError *error = NULL; self->priv->cond &= ~G_IO_IN; if (! evd_socket_watch_condition (self->priv->socket, ~self->priv->cond, &error)) { /* @TODO: handle error */ g_warning ("Undandled error: watch socket condition error: %s", error->message); g_error_free (error); } } } static void evd_connection_socket_output_stream_filled (GOutputStream *stream, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); GError *error = NULL; if (CLOSED (self)) return; self->priv->cond &= ~G_IO_OUT; if (! evd_socket_watch_condition (self->priv->socket, ~self->priv->cond, &error)) { g_warning ("Undandled error: watch socket condition error: %s", error->message); g_error_free (error); } } static void evd_connection_delay_read (EvdThrottledInputStream *stream, guint wait, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (self->priv->read_src_id == 0) { g_object_ref (self); self->priv->read_src_id = evd_timeout_add (NULL, wait, evd_socket_get_priority (self->priv->socket), evd_connection_read_wait_timeout, self); } } static void evd_connection_delay_write (EvdThrottledOutputStream *stream, guint wait, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); if (self->priv->write_src_id == 0) { g_object_ref (self); self->priv->write_src_id = evd_timeout_add (NULL, wait, evd_socket_get_priority (self->priv->socket), evd_connection_write_wait_timeout, self); } } static void evd_connection_setup_streams (EvdConnection *self) { EvdStreamThrottle *input_throttle; EvdStreamThrottle *output_throttle; EvdIoStreamGroup *group; /* socket input stream */ self->priv->socket_input_stream = evd_socket_input_stream_new (self->priv->socket); g_signal_connect (self->priv->socket_input_stream, "drained", G_CALLBACK (evd_connection_socket_input_stream_drained), self); /* socket output stream */ self->priv->socket_output_stream = evd_socket_output_stream_new (self->priv->socket); g_signal_connect (self->priv->socket_output_stream, "filled", G_CALLBACK (evd_connection_socket_output_stream_filled), self); /* throttled input stream */ input_throttle = evd_io_stream_get_input_throttle (EVD_IO_STREAM (self)); self->priv->throt_input_stream = evd_throttled_input_stream_new ( G_INPUT_STREAM (self->priv->socket_input_stream)); evd_throttled_input_stream_add_throttle (self->priv->throt_input_stream, input_throttle); g_signal_connect (self->priv->throt_input_stream, "delay-read", G_CALLBACK (evd_connection_delay_read), self); /* throttled output stream */ output_throttle = evd_io_stream_get_output_throttle (EVD_IO_STREAM (self)); self->priv->throt_output_stream = evd_throttled_output_stream_new ( G_OUTPUT_STREAM (self->priv->socket_output_stream)); evd_throttled_output_stream_add_throttle (self->priv->throt_output_stream, output_throttle); g_signal_connect (self->priv->throt_output_stream, "delay-write", G_CALLBACK (evd_connection_delay_write), self); group = evd_io_stream_get_group (EVD_IO_STREAM (self)); if (group != NULL) { EvdStreamThrottle *input_throttle; EvdStreamThrottle *output_throttle; g_object_get (group, "input-throttle", &input_throttle, "output-throttle", &output_throttle, NULL); evd_throttled_input_stream_add_throttle (self->priv->throt_input_stream, input_throttle); evd_throttled_output_stream_add_throttle (self->priv->throt_output_stream, output_throttle); g_object_unref (input_throttle); g_object_unref (output_throttle); } /* buffered input stream */ self->priv->buf_input_stream = evd_buffered_input_stream_new (G_INPUT_STREAM (self->priv->throt_input_stream)); /* buffered output stream */ self->priv->buf_output_stream = evd_buffered_output_stream_new (G_OUTPUT_STREAM (self->priv->throt_output_stream)); if (evd_socket_get_status (self->priv->socket) != EVD_SOCKET_STATE_CONNECTED) { self->priv->connected = FALSE; evd_buffered_input_stream_freeze (self->priv->buf_input_stream); } else { self->priv->connected = TRUE; } evd_buffered_output_stream_set_auto_flush (self->priv->buf_output_stream, self->priv->connected); } static void evd_connection_teardown_streams (EvdConnection *self) { g_object_unref (self->priv->buf_input_stream); g_object_unref (self->priv->buf_output_stream); if (self->priv->tls_input_stream != NULL) { g_object_unref (self->priv->tls_input_stream); self->priv->tls_input_stream = NULL; } if (self->priv->tls_output_stream != NULL) { g_object_unref (self->priv->tls_output_stream); self->priv->tls_output_stream = NULL; } g_object_unref (self->priv->throt_input_stream); g_object_unref (self->priv->throt_output_stream); g_object_unref (self->priv->socket_input_stream); g_object_unref (self->priv->socket_output_stream); } static void evd_connection_shutdown_on_flush (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdConnection *self = EVD_CONNECTION (user_data); g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), res, NULL); if (self->priv->tls_active) evd_tls_session_close (self->priv->tls_session, NULL); evd_socket_shutdown (evd_connection_get_socket (self), TRUE, TRUE, NULL); g_object_unref (self); } /* public methods */ EvdConnection * evd_connection_new (EvdSocket *socket) { EvdConnection *self; g_return_val_if_fail (EVD_IS_SOCKET (socket), NULL); self = g_object_new (EVD_TYPE_CONNECTION, "socket", socket, NULL); return self; } void evd_connection_set_socket (EvdConnection *self, EvdSocket *socket) { g_return_if_fail (EVD_IS_CONNECTION (self)); g_return_if_fail (EVD_IS_SOCKET (socket)); if (socket == self->priv->socket) return; if (self->priv->socket != NULL) { g_signal_handlers_disconnect_by_func (self->priv->socket, evd_connection_socket_on_status_changed, self); g_signal_handlers_disconnect_by_func (self->priv->socket, evd_connection_socket_on_error, self); evd_socket_set_notify_condition_callback (self->priv->socket, NULL, NULL); } self->priv->socket = socket; g_signal_connect (self->priv->socket, "state-changed", G_CALLBACK (evd_connection_socket_on_status_changed), self); g_signal_connect (self->priv->socket, "error", G_CALLBACK (evd_connection_socket_on_error), self); evd_socket_set_notify_condition_callback (self->priv->socket, evd_connection_socket_on_condition, self); /* reset TLS if active */ if (self->priv->tls_active) { self->priv->tls_active = FALSE; self->priv->tls_handshaking = FALSE; g_assert (EVD_IS_TLS_INPUT_STREAM (self->priv->tls_input_stream)); g_object_unref (self->priv->tls_input_stream); self->priv->tls_input_stream = NULL; g_assert (EVD_IS_TLS_INPUT_STREAM (self->priv->tls_output_stream)); g_object_unref (self->priv->tls_output_stream); self->priv->tls_output_stream = NULL; } if (self->priv->socket_input_stream == NULL) { /* create streams for the first time */ evd_connection_setup_streams (self); } else if (CLOSED (self)) { /* this is nasty but there is no way to reset the GIO streams after they are closed, so we have to re-create the entire pipeline. */ evd_connection_teardown_streams (self); evd_connection_setup_streams (self); /* reset 'closed' state of the GIOStream */ g_object_set (self, "closed", FALSE, NULL); } else { /* update new socket in socket input streams */ evd_socket_input_stream_set_socket (self->priv->socket_input_stream, self->priv->socket); evd_socket_output_stream_set_socket (self->priv->socket_output_stream, self->priv->socket); } evd_connection_socket_on_condition (self->priv->socket, evd_socket_get_condition (self->priv->socket), self); /* cache remote address as string to have if available for logging, even after socket closed. */ g_free (self->priv->remote_addr_st); self->priv->remote_addr_st = evd_connection_get_remote_address_as_string (self, NULL); } /** * evd_connection_get_socket: * * Returns: (transfer none): The #EvdSocket object **/ EvdSocket * evd_connection_get_socket (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), NULL); return self->priv->socket; } /** * evd_connection_get_tls_session: * * Returns: (transfer none): The #EvdTlsSession object **/ EvdTlsSession * evd_connection_get_tls_session (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), NULL); if (self->priv->tls_session == NULL) self->priv->tls_session = evd_tls_session_new (); return self->priv->tls_session; } /** * evd_connection_starttls: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_connection_starttls (EvdConnection *self, EvdTlsMode mode, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EvdTlsSession *session; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_CONNECTION (self)); g_return_if_fail (mode == EVD_TLS_MODE_CLIENT || mode == EVD_TLS_MODE_SERVER); /* @TODO: use cancellable object for something */ res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_connection_starttls); if (! self->priv->connected) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "The connection has been closed"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } if (self->priv->tls_active) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_BUSY, "SSL/TLS was already started"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; self->priv->tls_active = TRUE; session = TLS_SESSION (self); g_object_set (session, "mode", mode, NULL); g_assert (self->priv->tls_input_stream == NULL); self->priv->tls_input_stream = evd_tls_input_stream_new (session, G_INPUT_STREAM (self->priv->throt_input_stream)); g_filter_input_stream_set_close_base_stream ( G_FILTER_INPUT_STREAM (self->priv->buf_input_stream), FALSE); g_object_unref (self->priv->buf_input_stream); self->priv->buf_input_stream = evd_buffered_input_stream_new ( G_INPUT_STREAM (self->priv->tls_input_stream)); g_assert (self->priv->tls_output_stream == NULL); self->priv->tls_output_stream = evd_tls_output_stream_new (session, G_OUTPUT_STREAM (self->priv->throt_output_stream)); g_filter_output_stream_set_close_base_stream ( G_FILTER_OUTPUT_STREAM (self->priv->buf_output_stream), FALSE); g_object_unref (self->priv->buf_output_stream); self->priv->buf_output_stream = evd_buffered_output_stream_new ( G_OUTPUT_STREAM (self->priv->tls_output_stream)); evd_buffered_input_stream_freeze (self->priv->buf_input_stream); evd_buffered_output_stream_set_auto_flush (self->priv->buf_output_stream, FALSE); self->priv->tls_handshaking = TRUE; if (mode == EVD_TLS_MODE_CLIENT && self->priv->cond & G_IO_OUT) evd_connection_tls_handshake (self); } gboolean evd_connection_starttls_finish (EvdConnection *self, GAsyncResult *result, GError **error) { gboolean res; g_return_val_if_fail (EVD_IS_CONNECTION (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_connection_starttls), FALSE); res = ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); return res; } gboolean evd_connection_get_tls_active (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), FALSE); return self->priv->tls_active; } gsize evd_connection_get_max_readable (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), 0); if ((self->priv->cond & G_IO_IN) == 0 || self->priv->throt_input_stream == NULL || g_io_stream_is_closed (G_IO_STREAM (self))) { return 0; } else { return evd_throttled_input_stream_get_max_readable (self->priv->throt_input_stream, NULL); } } gsize evd_connection_get_max_writable (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), 0); if ((self->priv->cond & G_IO_OUT) == 0 || self->priv->throt_output_stream == NULL || g_io_stream_is_closed (G_IO_STREAM (self))) { return 0; } else { return evd_throttled_output_stream_get_max_writable (self->priv->throt_output_stream, NULL); } } gboolean evd_connection_is_connected (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), FALSE); return self->priv->connected; } gint evd_connection_get_priority (EvdConnection *self) { g_return_val_if_fail (EVD_IS_CONNECTION (self), 0); return evd_socket_get_priority (self->priv->socket); } void evd_connection_lock_close (EvdConnection *self) { g_return_if_fail (EVD_IS_CONNECTION (self)); self->priv->close_locked = TRUE; } void evd_connection_unlock_close (EvdConnection *self) { g_return_if_fail (EVD_IS_CONNECTION (self)); self->priv->close_locked = FALSE; if (self->priv->delayed_close) evd_connection_close_in_idle (self); } /** * evd_connection_flush_and_shutdown: * @cancellable: (allow-none): * **/ void evd_connection_flush_and_shutdown (EvdConnection *self, GCancellable *cancellable) { GOutputStream *stream; g_return_if_fail (EVD_IS_CONNECTION (self)); stream = g_io_stream_get_output_stream (G_IO_STREAM (self)); g_object_ref (self); g_output_stream_flush_async (stream, evd_connection_get_priority (self), cancellable, evd_connection_shutdown_on_flush, self); } gchar * evd_connection_get_remote_address_as_string (EvdConnection *self, GError **error) { GSocket *socket; GSocketAddress *sock_addr; gchar *addr_str = NULL; GSocketFamily family; g_return_val_if_fail (EVD_IS_CONNECTION (self), NULL); if (self->priv->remote_addr_st != NULL) return g_strdup (self->priv->remote_addr_st); socket = evd_socket_get_socket (self->priv->socket); if (! G_IS_SOCKET (socket)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "The connection's socket is not initialized or has already been closed"); return NULL; } sock_addr = g_socket_get_remote_address (socket, error); if (sock_addr == NULL) return NULL; family = g_socket_address_get_family (sock_addr); if (family == G_SOCKET_FAMILY_IPV4 || family == G_SOCKET_FAMILY_IPV6) { /* inet socket address */ GInetAddress *inet_addr; inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sock_addr)); addr_str = g_inet_address_to_string (inet_addr); } else #ifdef HAVE_GIO_UNIX { /* unix socket address */ GUnixSocketAddress *unix_addr = G_UNIX_SOCKET_ADDRESS (sock_addr); addr_str = g_strdup (g_unix_socket_address_get_path (unix_addr)); } #else { g_error_set (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Socket address family not supported"); } #endif g_object_unref (sock_addr); return addr_str; } EventDance-0.2.0/evd/evd-connection.h000066400000000000000000000106011321356073300173700ustar00rootroot00000000000000/* * evd-connection.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_CONNECTION_H__ #define __EVD_CONNECTION_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-io-stream.h" #include "evd-socket.h" #include "evd-tls-session.h" #include "evd-io-stream-group.h" G_BEGIN_DECLS typedef struct _EvdConnection EvdConnection; typedef struct _EvdConnectionClass EvdConnectionClass; typedef struct _EvdConnectionPrivate EvdConnectionPrivate; struct _EvdConnection { EvdIoStream parent; EvdConnectionPrivate *priv; }; struct _EvdConnectionClass { EvdIoStreamClass parent_class; /* signal prototypes */ void (* close) (EvdConnection *self); void (* write) (EvdConnection *self); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_CONNECTION (evd_connection_get_type ()) #define EVD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_CONNECTION, EvdConnection)) #define EVD_CONNECTION_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_CONNECTION, EvdConnectionClass)) #define EVD_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_CONNECTION)) #define EVD_IS_CONNECTION_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_CONNECTION)) #define EVD_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_CONNECTION, EvdConnectionClass)) GType evd_connection_get_type (void) G_GNUC_CONST; EvdConnection *evd_connection_new (EvdSocket *socket); void evd_connection_set_socket (EvdConnection *self, EvdSocket *socket); EvdSocket *evd_connection_get_socket (EvdConnection *self); EvdTlsSession *evd_connection_get_tls_session (EvdConnection *self); void evd_connection_starttls (EvdConnection *self, EvdTlsMode mode, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_connection_starttls_finish (EvdConnection *self, GAsyncResult *result, GError **error); gboolean evd_connection_get_tls_active (EvdConnection *self); gsize evd_connection_get_max_readable (EvdConnection *self); gsize evd_connection_get_max_writable (EvdConnection *self); gboolean evd_connection_is_connected (EvdConnection *self); gint evd_connection_get_priority (EvdConnection *self); void evd_connection_lock_close (EvdConnection *self); void evd_connection_unlock_close (EvdConnection *self); void evd_connection_flush_and_shutdown (EvdConnection *self, GCancellable *cancellable); gchar * evd_connection_get_remote_address_as_string (EvdConnection *self, GError **error); G_END_DECLS #endif /* __EVD_CONNECTION_H__ */ EventDance-0.2.0/evd/evd-daemon.c000066400000000000000000000221371321356073300164760ustar00rootroot00000000000000/* * evd-daemon.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include #include #include #include #include #include #include #include "evd-daemon.h" #include "evd-utils.h" #include "evd-error.h" G_DEFINE_TYPE (EvdDaemon, evd_daemon, G_TYPE_OBJECT) #define EVD_DAEMON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_DAEMON, \ EvdDaemonPrivate)) /* private data */ struct _EvdDaemonPrivate { GMainLoop *main_loop; gboolean daemonize; gboolean daemonized; gint exit_code; gchar *pid_file; }; static EvdDaemon *evd_daemon_default = NULL; static void evd_daemon_class_init (EvdDaemonClass *class); static void evd_daemon_init (EvdDaemon *self); static void evd_daemon_finalize (GObject *obj); static void evd_daemon_class_init (EvdDaemonClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_daemon_finalize; g_type_class_add_private (obj_class, sizeof (EvdDaemonPrivate)); } static void evd_daemon_init (EvdDaemon *self) { EvdDaemonPrivate *priv; priv = EVD_DAEMON_GET_PRIVATE (self); self->priv = priv; priv->main_loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); priv->daemonize = FALSE; priv->daemonized = FALSE; priv->exit_code = 0; priv->pid_file = NULL; } static void evd_daemon_finalize (GObject *obj) { EvdDaemon *self = EVD_DAEMON (obj); g_main_loop_unref (self->priv->main_loop); g_free (self->priv->pid_file); G_OBJECT_CLASS (evd_daemon_parent_class)->finalize (obj); if (evd_daemon_default == self) evd_daemon_default = NULL; } static void evd_daemon_on_user_interrupt (gint sig) { signal (SIGINT, NULL); signal (SIGTERM, NULL); if (evd_daemon_default != NULL) evd_daemon_quit (evd_daemon_default, -sig); } /* public methods */ /** * evd_daemon_get_default: * @argc: (allow-none): * @argv: (allow-none): * * Returns: (transfer full): **/ EvdDaemon * evd_daemon_get_default (gint *argc, gchar **argv[]) { if (evd_daemon_default == NULL) evd_daemon_default = evd_daemon_new (argc, argv); else g_object_ref (evd_daemon_default); return evd_daemon_default; } /** * evd_daemon_new: * @argv: (allow-none): * * Returns: (transfer full): **/ EvdDaemon * evd_daemon_new (gint *argc, gchar **argv[]) { EvdDaemon *self; gboolean daemonize = FALSE; GOptionContext *context; const GOptionEntry entries[] = { { "daemonize", 'D', 0, G_OPTION_ARG_NONE, &daemonize, NULL, NULL }, { NULL } }; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, NULL); g_option_context_set_help_enabled (context, FALSE); g_option_context_set_ignore_unknown_options (context, TRUE); g_option_context_parse (context, argc, argv, NULL); /* lets ignore any parsing error */ g_option_context_free (context); self = g_object_new (EVD_TYPE_DAEMON, NULL); self->priv->daemonize = daemonize; if (evd_daemon_default == NULL) evd_daemon_default = self; return self; } gint evd_daemon_run (EvdDaemon *self, GError **error) { g_return_val_if_fail (EVD_IS_DAEMON (self), -1); g_return_val_if_fail (! g_main_loop_is_running (self->priv->main_loop), -1); /* daemonize */ if (! self->priv->daemonized && self->priv->daemonize) { if (! evd_daemon_daemonize (self, error)) return -1; } /* write PID file */ if (self->priv->pid_file != NULL) { const gint BUF_SIZE = 20; gint pid; gchar buf[BUF_SIZE]; pid = getpid (); g_snprintf (buf, BUF_SIZE - 1, "%d\n", pid); buf[BUF_SIZE - 1] = '\0'; if (! g_file_set_contents (self->priv->pid_file, buf, -1, error)) return -1; /* force PID-file permissions to 00644 */ if (chmod (self->priv->pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) { gchar *err_st; err_st = strerror (errno); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to set permissions on PID file: %s", err_st); g_free (err_st); return -1; } } /* hook SIGINT and SIGTERM if this is the default daemon */ if (self == evd_daemon_default) { signal (SIGINT, evd_daemon_on_user_interrupt); signal (SIGTERM, evd_daemon_on_user_interrupt); } /* finally, run the main loop */ g_main_loop_run (self->priv->main_loop); return self->priv->exit_code; } void evd_daemon_quit (EvdDaemon *self, gint exit_code) { g_return_if_fail (EVD_IS_DAEMON (self)); g_main_loop_quit (self->priv->main_loop); } gboolean evd_daemon_daemonize (EvdDaemon *self, GError **error) { pid_t pid, sid; gchar *err_st; errno = 0; /* already a daemon */ if (self->priv->daemonized || getppid () == 1) return TRUE; /* Fork off the parent process */ pid = fork (); if (pid < 0) goto error; /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) exit (0); /* At this point we are executing as the child process */ /* Change the file mode mask */ umask (0); /* Create a new SID for the child process */ sid = setsid (); if (sid < 0) goto error; /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if ((chdir ("/")) < 0) goto error; /* Redirect standard files to /dev/null */ if (freopen ("/dev/null", "r", stdin) == NULL || freopen ("/dev/null", "w", stdout) == NULL || freopen ("/dev/null", "w", stderr) == NULL) { goto error; } self->priv->daemonized = TRUE; return TRUE; error: err_st = strerror (errno); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to daemonize process: %s", err_st); g_free (err_st); return FALSE; } /** * evd_daemon_set_timeout: * @function: (scope notified): * * Returns: **/ guint evd_daemon_set_timeout (EvdDaemon *self, guint timeout, GSourceFunc function, gpointer user_data) { g_return_val_if_fail (EVD_IS_DAEMON (self), 0); return evd_timeout_add (g_main_loop_get_context (self->priv->main_loop), timeout, G_PRIORITY_DEFAULT, function, user_data); } gboolean evd_daemon_set_user_id (EvdDaemon *self, gint user_id, GError **error) { g_return_val_if_fail (EVD_IS_DAEMON (self), FALSE); errno = 0; if (setuid (user_id) != 0) { g_set_error (error, EVD_ERRNO_ERROR, errno, "%s", strerror (errno)); return FALSE; } return TRUE; } gboolean evd_daemon_set_user (EvdDaemon *self, const gchar *username, GError **error) { struct passwd *buf; g_return_val_if_fail (EVD_IS_DAEMON (self), FALSE); g_return_val_if_fail (username != NULL, FALSE); errno = 0; buf = getpwnam (username); if (buf == NULL) { if (errno != 0) g_set_error (error, EVD_ERRNO_ERROR, errno, "%s", strerror (errno)); else g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "User %s not found", username); return FALSE; } return evd_daemon_set_user_id (self, buf->pw_uid, error); } void evd_daemon_set_pid_file (EvdDaemon *self, const gchar *pid_file) { g_return_if_fail (EVD_IS_DAEMON (self)); if (g_main_loop_is_running (self->priv->main_loop)) { g_warning ("Ignoring PID file change because daemon is already running"); return; } g_free (self->priv->pid_file); self->priv->pid_file = NULL; if (pid_file != NULL) self->priv->pid_file = g_strdup (pid_file); } const gchar * evd_daemon_get_pid_file (EvdDaemon *self) { g_return_val_if_fail (EVD_IS_DAEMON (self), NULL); return self->priv->pid_file; } EventDance-0.2.0/evd/evd-daemon.h000066400000000000000000000067261321356073300165110ustar00rootroot00000000000000/* * evd-daemon.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_DAEMON_H__ #define __EVD_DAEMON_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef struct _EvdDaemon EvdDaemon; typedef struct _EvdDaemonClass EvdDaemonClass; typedef struct _EvdDaemonPrivate EvdDaemonPrivate; struct _EvdDaemon { GObject parent; EvdDaemonPrivate *priv; }; struct _EvdDaemonClass { GObjectClass parent_class; /* virtual methods */ /* signal prototypes */ }; #define EVD_TYPE_DAEMON (evd_daemon_get_type ()) #define EVD_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_DAEMON, EvdDaemon)) #define EVD_DAEMON_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_DAEMON, EvdDaemonClass)) #define EVD_IS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_DAEMON)) #define EVD_IS_DAEMON_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_DAEMON)) #define EVD_DAEMON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_DAEMON, EvdDaemonClass)) GType evd_daemon_get_type (void) G_GNUC_CONST; EvdDaemon * evd_daemon_get_default (gint *argc, gchar **argv[]); EvdDaemon * evd_daemon_new (gint *argc, gchar **argv[]); gint evd_daemon_run (EvdDaemon *self, GError **error); void evd_daemon_quit (EvdDaemon *self, gint exit_code); gboolean evd_daemon_daemonize (EvdDaemon *self, GError **error); guint evd_daemon_set_timeout (EvdDaemon *self, guint timeout, GSourceFunc function, gpointer user_data); gboolean evd_daemon_set_user_id (EvdDaemon *self, gint user_id, GError **error); gboolean evd_daemon_set_user (EvdDaemon *self, const gchar *username, GError **error); void evd_daemon_set_pid_file (EvdDaemon *self, const gchar *pid_file); const gchar * evd_daemon_get_pid_file (EvdDaemon *self); G_END_DECLS #endif /* __EVD_DAEMON_H__ */ EventDance-0.2.0/evd/evd-dbus-agent.c000066400000000000000000001253161321356073300172670ustar00rootroot00000000000000/* * evd-dbus-agent.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-dbus-agent.h" #include "evd-utils.h" #define OBJECT_DATA_KEY "org.eventdance.lib.DBusAgent.OBJ_DATA" typedef struct { GObject *obj; GHashTable *conns; guint conn_counter; GHashTable *proxies; guint proxy_counter; GHashTable *owned_names; GHashTable *reg_objs; GHashTable *reg_objs_by_id; GHashTable *addr_aliases; EvdDBusAgentVTable *vtable; gpointer vtable_user_data; } ObjectData; typedef struct { GDBusConnection *conn; gint ref_count; gboolean reuse; gchar *addr; } ConnData; typedef struct { ObjectData *obj_data; ConnData *conn_data; } ObjConnData; typedef struct { ObjectData *obj_data; guint32 conn_id; guint32 proxy_id; GDBusProxy *proxy; GSimpleAsyncResult *async_res; } ProxyData; typedef struct { ObjectData *obj_data; guint32 conn_id; guint owner_id; GDBusConnection *dbus_conn; } NameOwnerData; typedef struct { ObjectData *obj_data; guint32 conn_id; gchar *reg_str_id; GDBusConnection *dbus_conn; gchar *obj_path; GDBusInterfaceInfo *iface_info; guint reg_id; guint64 serial; GHashTable *invocations; } RegObjData; static GHashTable *conn_cache = NULL; static void evd_dbus_agent_on_object_connection_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, gpointer user_data); static void evd_dbus_agent_on_proxy_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data); static void evd_dbus_agent_on_proxy_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv *invalidated_properties, gpointer user_data); static void evd_dbus_agent_method_called (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data); static gboolean evd_dbus_agent_foreach_remove_proxy (gpointer key, gpointer value, gpointer user_data); static gboolean evd_dbus_agent_foreach_remove_owned_names (gpointer key, gpointer value, gpointer user_data); static gboolean evd_dbus_agent_foreach_remove_reg_obj (gpointer key, gpointer value, gpointer user_data); static const GDBusInterfaceVTable evd_dbus_agent_iface_vtable = { evd_dbus_agent_method_called, NULL, NULL }; static ObjectData * evd_dbus_agent_get_object_data (GObject *obj) { return (ObjectData *) g_object_get_data (G_OBJECT (obj), OBJECT_DATA_KEY); } static ConnData * evd_dbus_agent_conn_data_new (const gchar *addr, gboolean reuse) { ConnData *conn_data; conn_data = g_slice_new (ConnData); conn_data->conn = NULL; conn_data->ref_count = 0; conn_data->addr = g_strdup (addr); conn_data->reuse = reuse; return conn_data; } static void evd_dbus_agent_conn_data_ref (ConnData *conn_data) { conn_data->ref_count++; } static void evd_dbus_agent_conn_data_unref (ConnData *conn_data) { conn_data->ref_count--; if (conn_data->ref_count <= 0) { if (conn_cache != NULL) g_hash_table_remove (conn_cache, conn_data->addr); if (conn_data->conn != NULL) { g_dbus_connection_close (conn_data->conn, NULL, NULL, NULL); g_object_unref (conn_data->conn); } g_free (conn_data->addr); g_slice_free (ConnData, conn_data); } } static void evd_dbus_agent_free_obj_conn_data (gpointer data) { ObjectData *obj_data; ObjConnData *obj_conn_data = (ObjConnData *) data; obj_data = obj_conn_data->obj_data; /* remove all proxies created over this connection */ g_hash_table_foreach_remove (obj_data->proxies, evd_dbus_agent_foreach_remove_proxy, obj_conn_data->conn_data->conn); /* unown all names owned over this connection */ g_hash_table_foreach_remove (obj_data->owned_names, evd_dbus_agent_foreach_remove_owned_names, obj_conn_data->conn_data->conn); /* remove all objects registered over this connection */ g_hash_table_foreach_remove (obj_data->reg_objs_by_id, evd_dbus_agent_foreach_remove_reg_obj, obj_conn_data->conn_data->conn); g_signal_handlers_disconnect_by_func (obj_conn_data->conn_data->conn, G_CALLBACK (evd_dbus_agent_on_object_connection_closed), obj_data); evd_dbus_agent_conn_data_unref (obj_conn_data->conn_data); g_slice_free (ObjConnData, obj_conn_data); } static void evd_dbus_agent_free_proxy_data (gpointer data) { ProxyData *proxy_data = (ProxyData *) data; if (proxy_data->proxy != NULL) { g_signal_handlers_disconnect_by_func (proxy_data->proxy, evd_dbus_agent_on_proxy_signal, proxy_data); g_signal_handlers_disconnect_by_func (proxy_data->proxy, evd_dbus_agent_on_proxy_properties_changed, proxy_data); g_object_unref (proxy_data->proxy); } g_slice_free (ProxyData, proxy_data); } static void evd_dbus_agent_free_name_owner_data (gpointer data) { NameOwnerData *owner_data = (NameOwnerData *) data; g_bus_unown_name (owner_data->owner_id); g_slice_free (NameOwnerData, owner_data); } static void evd_dbus_agent_free_reg_obj_data (gpointer data) { ObjectData *obj_data; RegObjData *reg_obj_data = (RegObjData *) data; obj_data = reg_obj_data->obj_data; g_dbus_connection_unregister_object (reg_obj_data->dbus_conn, reg_obj_data->reg_id); g_hash_table_remove (obj_data->reg_objs, ®_obj_data->reg_str_id); g_free (reg_obj_data->obj_path); g_dbus_interface_info_unref (reg_obj_data->iface_info); g_object_unref (reg_obj_data->dbus_conn); g_hash_table_destroy (reg_obj_data->invocations); g_slice_free (RegObjData, reg_obj_data); } static void evd_dbus_agent_on_object_destroyed (gpointer user_data, GObject *where_the_object_was) { ObjectData *data = (ObjectData *) user_data; g_assert (data != NULL); g_hash_table_destroy (data->conns); g_hash_table_destroy (data->proxies); g_hash_table_destroy (data->owned_names); g_hash_table_destroy (data->addr_aliases); g_hash_table_destroy (data->reg_objs_by_id); g_hash_table_destroy (data->reg_objs); g_slice_free (ObjectData, data); } static ObjectData * evd_dbus_agent_setup_object_data (GObject *obj) { ObjectData *data; data = g_slice_new0 (ObjectData); g_object_weak_ref (obj, evd_dbus_agent_on_object_destroyed, data); g_object_set_data (G_OBJECT (obj), OBJECT_DATA_KEY, data); data->obj = obj; data->conns = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, evd_dbus_agent_free_obj_conn_data); data->conn_counter = 0; data->proxies = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, evd_dbus_agent_free_proxy_data); data->proxy_counter = 0; data->owned_names = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, evd_dbus_agent_free_name_owner_data); data->reg_objs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); data->reg_objs_by_id = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, evd_dbus_agent_free_reg_obj_data); data->addr_aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); return data; } static void evd_dbus_agent_on_object_connection_closed (GDBusConnection *conn, gboolean remote_peer_vanished, GError *error, gpointer user_data) { ObjectData *data = (ObjectData *) user_data; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, data->conns); while (g_hash_table_iter_next (&iter, &key, &value)) { ConnData *conn_data; conn_data = (ConnData *) value; if (conn_data->conn == conn) { /* @TODO: notify object that connection dropped */ g_hash_table_remove (data->conns, (guint *) key); break; } } } static ConnData * evd_dbus_agent_search_conn_in_global_cache (const gchar *addr, gboolean reuse) { if (conn_cache != NULL) return (ConnData *) g_hash_table_lookup (conn_cache, addr); else return NULL; } static void evd_dbus_agent_cache_conn_in_global_cache (ConnData *conn_data) { if (conn_cache == NULL) conn_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_hash_table_insert (conn_cache, conn_data->addr, conn_data); } static guint * evd_dbus_agent_bind_connection_to_object (ObjectData *obj_data, ObjConnData *obj_conn_data) { guint *conn_id; obj_data->conn_counter ++; conn_id = g_new (guint, 1); *conn_id = obj_data->conn_counter; evd_dbus_agent_conn_data_ref (obj_conn_data->conn_data); g_hash_table_insert (obj_data->conns, conn_id, obj_conn_data); g_signal_connect (obj_conn_data->conn_data->conn, "closed", G_CALLBACK (evd_dbus_agent_on_object_connection_closed), obj_data); g_object_ref (obj_conn_data->conn_data->conn); return conn_id; } static void evd_dbus_agent_on_new_dbus_connection (GObject *obj, GAsyncResult *res, gpointer user_data) { GDBusConnection *dbus_conn; GError *error = NULL; GSimpleAsyncResult *result; ObjectData *obj_data; ConnData *conn_data; ObjConnData *obj_conn_data; result = G_SIMPLE_ASYNC_RESULT (user_data); obj_conn_data = (ObjConnData *) g_simple_async_result_get_op_res_gpointer (result); obj_data = obj_conn_data->obj_data; conn_data = obj_conn_data->conn_data; if ( (dbus_conn = g_dbus_connection_new_for_address_finish (res, &error)) == NULL) { evd_dbus_agent_conn_data_unref (conn_data); g_slice_free (ObjConnData, obj_conn_data); g_simple_async_result_set_from_error (result, error); g_error_free (error); } else { guint *conn_id; conn_data->conn = dbus_conn; conn_id = evd_dbus_agent_bind_connection_to_object (obj_data, obj_conn_data); if (conn_data->reuse) evd_dbus_agent_cache_conn_in_global_cache (conn_data); g_simple_async_result_set_op_res_gpointer (result, conn_id, NULL); } g_simple_async_result_complete (result); g_object_unref (result); } static void evd_dbus_agent_on_new_dbus_proxy (GObject *obj, GAsyncResult *res, gpointer user_data) { GSimpleAsyncResult *result; GDBusProxy *proxy; GError *error = NULL; ProxyData *proxy_data; proxy_data = (ProxyData *) user_data; result = proxy_data->async_res; if ( (proxy = g_dbus_proxy_new_finish (res, &error)) != NULL) { ObjectData *obj_data; GDBusProxyFlags flags; guint *proxy_id; obj_data = proxy_data->obj_data; obj_data->proxy_counter++; proxy_data->proxy_id = obj_data->proxy_counter; proxy_data->proxy = proxy; g_hash_table_insert (obj_data->proxies, &proxy_data->proxy_id, proxy_data); proxy_id = g_new (guint, 1); *proxy_id = obj_data->proxy_counter; g_simple_async_result_set_op_res_gpointer (result, proxy_id, g_free); flags = g_dbus_proxy_get_flags (proxy); if ( (flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS) == 0) { g_signal_connect (proxy, "g-signal", G_CALLBACK (evd_dbus_agent_on_proxy_signal), proxy_data); } if ( (flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) == 0) { g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (evd_dbus_agent_on_proxy_properties_changed), proxy_data); } } else { g_simple_async_result_set_from_error (proxy_data->async_res, error); g_error_free (error); evd_dbus_agent_free_proxy_data (proxy_data); } g_simple_async_result_complete (result); g_object_unref (result); } static ProxyData * evd_dbus_agent_find_proxy_data_by_proxy (ObjectData *data, GDBusProxy *proxy, guint *proxy_id) { ProxyData *proxy_data; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, data->proxies); while (g_hash_table_iter_next (&iter, &key, &value)) { proxy_data = (ProxyData *) value; if (proxy_data->proxy == proxy) { *proxy_id = * (guint *) key; return proxy_data; } } return NULL; } static void evd_dbus_agent_on_proxy_signal (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, gpointer user_data) { ObjectData *data; ProxyData *proxy_data = (ProxyData *) user_data; g_assert (proxy_data != NULL); data = proxy_data->obj_data; g_assert (data != NULL); if (data->vtable != NULL && data->vtable->proxy_signal != NULL) { data->vtable->proxy_signal (data->obj, proxy_data->conn_id, proxy_data->proxy_id, signal_name, parameters, data->vtable_user_data); } } static void evd_dbus_agent_on_proxy_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv *invalidated_properties, gpointer user_data) { ObjectData *data = (ObjectData *) user_data; ProxyData *proxy_data; guint proxy_id = 0; proxy_data = evd_dbus_agent_find_proxy_data_by_proxy (data, proxy, &proxy_id); g_assert (proxy_data != NULL); if (data->vtable != NULL && data->vtable->proxy_properties_changed != NULL) { data->vtable->proxy_properties_changed (data->obj, proxy_data->conn_id, proxy_id, changed_properties, invalidated_properties, data->vtable_user_data); } } static void evd_dbus_agent_method_called (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GObject *obj = G_OBJECT (user_data); ObjectData *data; gchar *key; RegObjData *reg_obj_data; data = evd_dbus_agent_get_object_data (obj); g_assert (data != NULL); key = g_strdup_printf ("%p-%s<%s>", connection, object_path, interface_name); reg_obj_data = g_hash_table_lookup (data->reg_objs, key); g_free (key); g_assert (reg_obj_data != NULL); if (data->vtable && data->vtable->method_call != NULL) { /* cache the method invocation object, bound to the serial */ reg_obj_data->serial++; g_hash_table_insert (reg_obj_data->invocations, (gint64 *) ®_obj_data->serial, invocation); data->vtable->method_call (obj, reg_obj_data->conn_id, sender, method_name, reg_obj_data->reg_id, parameters, reg_obj_data->serial, data->vtable_user_data); } else { /* return error to invocation, no way to handle it */ g_dbus_method_invocation_return_error_literal (invocation, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Method not handled"); } } static gboolean evd_dbus_agent_foreach_remove_proxy (gpointer key, gpointer value, gpointer user_data) { ProxyData *proxy_data = (ProxyData *) value; GDBusConnection *conn = G_DBUS_CONNECTION (user_data); GDBusConnection *proxy_conn; proxy_conn = g_dbus_proxy_get_connection (proxy_data->proxy); return proxy_conn == conn; } static gboolean evd_dbus_agent_foreach_remove_owned_names (gpointer key, gpointer value, gpointer user_data) { NameOwnerData *name_owner_data = (NameOwnerData *) value; GDBusConnection *conn = G_DBUS_CONNECTION (user_data); return name_owner_data->dbus_conn == conn; } static gboolean evd_dbus_agent_foreach_remove_reg_obj (gpointer key, gpointer value, gpointer user_data) { RegObjData *reg_obj_data = (RegObjData *) value; GDBusConnection *conn = G_DBUS_CONNECTION (user_data); return reg_obj_data->dbus_conn == conn; } static RegObjData * evd_dbus_agent_get_registered_object_data (GObject *object, guint registration_id, GError **error) { ObjectData *data; RegObjData *reg_obj_data; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (registration_id > 0, NULL); data = evd_dbus_agent_get_object_data (object); if (data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Object is invalid"); return NULL; } reg_obj_data = g_hash_table_lookup (data->reg_objs_by_id, ®istration_id); if (reg_obj_data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object registration id '%u' is invalid", registration_id); return NULL; } return reg_obj_data; } /* public methods */ void evd_dbus_agent_create_address_alias (GObject *object, const gchar *address, const gchar *alias) { ObjectData *data; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (address != NULL); data = evd_dbus_agent_get_object_data (object); if (data == NULL) data = evd_dbus_agent_setup_object_data (object); g_hash_table_insert (data->addr_aliases, g_strdup (alias), g_strdup (address)); } void evd_dbus_agent_new_connection (GObject *object, const gchar *address, gboolean reuse, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; ObjectData *data; gchar *addr; ObjConnData *obj_conn_data; ConnData *conn_data; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (address != NULL); data = evd_dbus_agent_get_object_data (object); if (data == NULL) data = evd_dbus_agent_setup_object_data (object); obj_conn_data = g_slice_new (ObjConnData); obj_conn_data->obj_data = data; res = g_simple_async_result_new (object, callback, user_data, evd_dbus_agent_new_connection); /* if 'address' is an alias, dereference it */ if ( (addr = g_hash_table_lookup (data->addr_aliases, address)) == NULL) addr = g_strdup (address); else addr = g_strdup (addr); if (reuse) { /* lookup the connection in global cache */ conn_data = evd_dbus_agent_search_conn_in_global_cache (addr, reuse); if (conn_data != NULL) { guint *conn_id; obj_conn_data->conn_data = conn_data; conn_id = evd_dbus_agent_bind_connection_to_object (data, obj_conn_data); g_simple_async_result_set_op_res_gpointer (res, conn_id, NULL); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } } conn_data = evd_dbus_agent_conn_data_new (addr, reuse); obj_conn_data->conn_data = conn_data; g_simple_async_result_set_op_res_gpointer (res, obj_conn_data, NULL); g_dbus_connection_new_for_address (addr, G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, NULL, cancellable, evd_dbus_agent_on_new_dbus_connection, res); g_free (addr); } guint evd_dbus_agent_new_connection_finish (GObject *object, GAsyncResult *result, GError **error) { g_return_val_if_fail (G_IS_OBJECT (object), 0); g_return_val_if_fail (g_simple_async_result_is_valid (result, object, evd_dbus_agent_new_connection), 0); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { guint *conn_id; conn_id = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); return *conn_id; } else { return 0; } } gboolean evd_dbus_agent_close_connection (GObject *object, guint connection_id, GError **error) { GDBusConnection *conn; if ( (conn = evd_dbus_agent_get_connection (object, connection_id, error)) != NULL) { ObjectData *data; data = evd_dbus_agent_get_object_data (object); g_hash_table_remove (data->conns, &connection_id); return TRUE; } else { return FALSE; } } /** * evd_dbus_agent_get_connection: * * Returns: (transfer none): **/ GDBusConnection * evd_dbus_agent_get_connection (GObject *obj, guint connection_id, GError **error) { ObjectData *data; ObjConnData *obj_conn_data; data = evd_dbus_agent_get_object_data (obj); if (data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Object is invalid"); return NULL; } obj_conn_data = (ObjConnData *) (g_hash_table_lookup (data->conns, &connection_id)); if (obj_conn_data != NULL) { return obj_conn_data->conn_data->conn; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object doesn't hold specified connection"); return NULL; } } void evd_dbus_agent_new_proxy (GObject *object, guint connection_id, GDBusProxyFlags flags, const gchar *name, const gchar *object_path, const gchar *iface_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GDBusConnection *conn; GSimpleAsyncResult *res; GError *error = NULL; ProxyData *proxy_data; g_return_if_fail (G_IS_OBJECT (object)); g_return_if_fail (connection_id > 0); res = g_simple_async_result_new (object, callback, user_data, evd_dbus_agent_new_proxy); if ( (conn = evd_dbus_agent_get_connection (object, connection_id, &error)) != NULL) { ObjectData *data; data = evd_dbus_agent_get_object_data (object); g_simple_async_result_set_op_res_gpointer (res, data, NULL); proxy_data = g_slice_new (ProxyData); proxy_data->obj_data = data; proxy_data->conn_id = connection_id; proxy_data->async_res = res; proxy_data->proxy = NULL; g_dbus_proxy_new (conn, flags, NULL, name, object_path, iface_name, cancellable, evd_dbus_agent_on_new_dbus_proxy, proxy_data); } else { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } } guint evd_dbus_agent_new_proxy_finish (GObject *object, GAsyncResult *result, GError **error) { g_return_val_if_fail (G_IS_OBJECT (object), 0); g_return_val_if_fail (g_simple_async_result_is_valid (result, object, evd_dbus_agent_new_proxy), 0); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { guint *proxy_id; proxy_id = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); return *proxy_id; } else { return 0; } } gboolean evd_dbus_agent_close_proxy (GObject *object, guint proxy_id, GError **error) { g_return_val_if_fail (G_IS_OBJECT (object), FALSE); g_return_val_if_fail (proxy_id > 0, FALSE); if (evd_dbus_agent_get_proxy (object, proxy_id, error) != NULL) { ObjectData *data; data = evd_dbus_agent_get_object_data (object); g_hash_table_remove (data->proxies, &proxy_id); return TRUE; } else { return FALSE; } } /** * evd_dbus_agent_get_proxy: * * Returns: (transfer none): **/ GDBusProxy * evd_dbus_agent_get_proxy (GObject *object, guint proxy_id, GError **error) { ObjectData *data; ProxyData *proxy_data; g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (proxy_id > 0, NULL); data = evd_dbus_agent_get_object_data (object); if (data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Object is invalid"); return NULL; } proxy_data = (ProxyData *) (g_hash_table_lookup (data->proxies, &proxy_id)); if (proxy_data != NULL) { return proxy_data->proxy; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object doesn't hold specified proxy"); return NULL; } } void evd_dbus_agent_set_object_vtable (GObject *object, EvdDBusAgentVTable *vtable, gpointer user_data) { ObjectData *data; g_return_if_fail (G_IS_OBJECT (object)); data = evd_dbus_agent_get_object_data (object); if (data == NULL) data = evd_dbus_agent_setup_object_data (object); data->vtable = vtable; data->vtable_user_data = user_data; } guint evd_dbus_agent_register_object (GObject *object, guint connection_id, const gchar *object_path, GDBusInterfaceInfo *interface_info, GError **error) { GDBusConnection *dbus_conn; guint reg_id = 0; g_return_val_if_fail (G_IS_OBJECT (object), 0); g_return_val_if_fail (connection_id > 0, 0); g_return_val_if_fail (object_path != NULL, 0); g_return_val_if_fail (interface_info != NULL, 0); if ( (dbus_conn = evd_dbus_agent_get_connection (object, connection_id, error)) == NULL) { return 0; } reg_id = g_dbus_connection_register_object (dbus_conn, object_path, interface_info, &evd_dbus_agent_iface_vtable, object, NULL, error); if (reg_id > 0) { gchar *key; ObjectData *data; RegObjData *reg_obj_data; data = evd_dbus_agent_get_object_data (object); g_assert (data != NULL); key = g_strdup_printf ("%p-%s<%s>", dbus_conn, object_path, interface_info->name); reg_obj_data = g_slice_new (RegObjData); reg_obj_data->obj_path = g_strdup (object_path); reg_obj_data->dbus_conn = dbus_conn; g_object_ref (dbus_conn); reg_obj_data->reg_id = reg_id; reg_obj_data->serial = 0; reg_obj_data->reg_str_id = key; reg_obj_data->obj_data = data; reg_obj_data->conn_id = connection_id; reg_obj_data->iface_info = g_dbus_interface_info_ref (interface_info); reg_obj_data->invocations = g_hash_table_new_full (g_int64_hash, g_int64_equal, NULL, NULL); g_hash_table_insert (data->reg_objs, key, reg_obj_data); g_hash_table_insert (data->reg_objs_by_id, ®_obj_data->reg_id, reg_obj_data); } return reg_id; } gboolean evd_dbus_agent_unregister_object (GObject *object, guint registration_id, GError **error) { if (evd_dbus_agent_get_registered_object_data (object, registration_id, error) == NULL) { return FALSE; } else { ObjectData *data; data = evd_dbus_agent_get_object_data (object); g_hash_table_remove (data->reg_objs_by_id, ®istration_id); return TRUE; } } GDBusInterfaceInfo * evd_dbus_agent_get_registered_object_interface (GObject *object, guint registration_id, GError **error) { RegObjData *reg_obj_data; reg_obj_data = evd_dbus_agent_get_registered_object_data (object, registration_id, error); if (reg_obj_data == NULL) return NULL; else return reg_obj_data->iface_info; } /** * evd_dbus_agent_get_method_invocation: * * Returns: (transfer none): **/ GDBusMethodInvocation * evd_dbus_agent_get_method_invocation (GObject *object, guint registration_id, guint64 serial, GError **error) { RegObjData *reg_obj_data; GDBusMethodInvocation *invocation; reg_obj_data = evd_dbus_agent_get_registered_object_data (object, registration_id, error); if (reg_obj_data == NULL) return NULL; invocation = (GDBusMethodInvocation *) g_hash_table_lookup (reg_obj_data->invocations, &serial); if (invocation == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Method invocation serial '%" G_GUINT64_FORMAT "' is invalid", serial); return NULL; } return invocation; } gboolean evd_dbus_agent_method_call_return (GObject *object, guint registration_id, guint64 serial, GVariant *return_parameters, GError **error) { ObjectData *data; RegObjData *reg_obj_data; GDBusMethodInvocation *invocation; g_return_val_if_fail (G_IS_OBJECT (object), FALSE); g_return_val_if_fail (registration_id > 0, FALSE); g_return_val_if_fail (return_parameters != NULL, FALSE); data = evd_dbus_agent_get_object_data (object); if (data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object is invalid"); return FALSE; } reg_obj_data = g_hash_table_lookup (data->reg_objs_by_id, ®istration_id); if (reg_obj_data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object registration id '%u' is invalid", registration_id); return FALSE; } invocation = g_hash_table_lookup (reg_obj_data->invocations, &serial); if (invocation == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "No method call with serial '%" G_GUINT64_FORMAT "'", serial); return FALSE; } g_dbus_method_invocation_return_value (invocation, return_parameters); g_hash_table_remove (reg_obj_data->invocations, &serial); return TRUE; } gboolean evd_dbus_agent_emit_signal (GObject *object, guint registration_id, const gchar *signal_name, GVariant *parameters, GError **error) { ObjectData *data; RegObjData *reg_obj_data; g_return_val_if_fail (G_IS_OBJECT (object), FALSE); g_return_val_if_fail (registration_id > 0, FALSE); g_return_val_if_fail (signal_name != NULL, FALSE); data = evd_dbus_agent_get_object_data (object); if (data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object is invalid"); return FALSE; } reg_obj_data = g_hash_table_lookup (data->reg_objs_by_id, ®istration_id); if (reg_obj_data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object registration id '%u' is invalid", registration_id); return FALSE; } return g_dbus_connection_emit_signal (reg_obj_data->dbus_conn, NULL, reg_obj_data->obj_path, reg_obj_data->iface_info->name, signal_name, parameters, error); } static void evd_dbus_agent_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { NameOwnerData *owner_data = (NameOwnerData *) user_data; ObjectData *obj_data; obj_data = owner_data->obj_data; if (obj_data->vtable != NULL && obj_data->vtable->name_acquired != NULL) { obj_data->vtable->name_acquired (obj_data->obj, owner_data->conn_id, owner_data->owner_id, obj_data->vtable_user_data); } } static void evd_dbus_agent_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { NameOwnerData *owner_data = (NameOwnerData *) user_data; ObjectData *obj_data; obj_data = owner_data->obj_data; if (obj_data->vtable != NULL && obj_data->vtable->name_lost != NULL) { obj_data->vtable->name_lost (obj_data->obj, owner_data->conn_id, owner_data->owner_id, obj_data->vtable_user_data); } } guint evd_dbus_agent_own_name (GObject *object, guint connection_id, const gchar *name, GBusNameOwnerFlags flags, GError **error) { GDBusConnection *conn; ObjectData *obj_data; NameOwnerData *owner_data; conn = evd_dbus_agent_get_connection (object, connection_id, error); if (conn == NULL) return 0; obj_data = evd_dbus_agent_get_object_data (object); owner_data = g_slice_new (NameOwnerData); owner_data->obj_data = obj_data; owner_data->conn_id = connection_id; owner_data->dbus_conn = conn; owner_data->owner_id = g_bus_own_name_on_connection (conn, name, flags, evd_dbus_agent_name_acquired, evd_dbus_agent_name_lost, owner_data, NULL); g_hash_table_insert (obj_data->owned_names, &owner_data->owner_id, owner_data); return owner_data->owner_id; } gboolean evd_dbus_agent_unown_name (GObject *object, guint owner_id, GError **error) { ObjectData *obj_data; g_return_val_if_fail (G_IS_OBJECT (object), FALSE); g_return_val_if_fail (owner_id > 0, FALSE); obj_data = evd_dbus_agent_get_object_data (object); if (obj_data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Object is invalid"); return FALSE; } g_hash_table_remove (obj_data->owned_names, &owner_id); return TRUE; } EventDance-0.2.0/evd/evd-dbus-agent.h000066400000000000000000000242341321356073300172710ustar00rootroot00000000000000/* * evd-dbus-agent.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_DBUS_AGENT_H__ #define __EVD_DBUS_AGENT_H__ #include #include G_BEGIN_DECLS typedef void (* EvdDBusAgentProxyPropertiesChangedCb) (GObject *object, guint connection_id, guint proxy_id, GVariant *changed_properties, GStrv *invalidated_properties, gpointer user_data); typedef void (* EvdDBusAgentProxySignalCb) (GObject *object, guint connection_id, guint proxy_id, const gchar *signal_name, GVariant *parameters, gpointer user_data); typedef void (* EvdDBusAgentMethodCallCb) (GObject *object, guint connection_id, const gchar *sender, const gchar *method_name, guint registration_id, GVariant *parameters, guint64 serial, gpointer user_data); typedef void (* EvdDBusAgentNameAcquiredCb) (GObject *object, guint connection_id, guint owning_id, gpointer user_data); typedef void (* EvdDBusAgentNameLostCb) (GObject *object, guint connection_id, guint owning_id, gpointer user_data); typedef struct { EvdDBusAgentProxySignalCb proxy_signal; EvdDBusAgentProxyPropertiesChangedCb proxy_properties_changed; EvdDBusAgentMethodCallCb method_call; EvdDBusAgentNameAcquiredCb name_acquired; EvdDBusAgentNameLostCb name_lost; } EvdDBusAgentVTable; void evd_dbus_agent_create_address_alias (GObject *object, const gchar *address, const gchar *alias); void evd_dbus_agent_new_connection (GObject *object, const gchar *addr, gboolean reuse, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); guint evd_dbus_agent_new_connection_finish (GObject *object, GAsyncResult *result, GError **error); gboolean evd_dbus_agent_close_connection (GObject *object, guint connection_id, GError **error); GDBusConnection * evd_dbus_agent_get_connection (GObject *obj, guint connection_id, GError **error); void evd_dbus_agent_new_proxy (GObject *object, guint conn_id, GDBusProxyFlags flags, const gchar *name, const gchar *object_path, const gchar *iface_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); guint evd_dbus_agent_new_proxy_finish (GObject *object, GAsyncResult *result, GError **error); gboolean evd_dbus_agent_close_proxy (GObject *object, guint proxy_id, GError **error); GDBusProxy * evd_dbus_agent_get_proxy (GObject *obj, guint proxy_id, GError **error); guint evd_dbus_agent_own_name (GObject *object, guint connection_id, const gchar *name, GBusNameOwnerFlags flags, GError **error); gboolean evd_dbus_agent_unown_name (GObject *object, guint owner_id, GError **error); guint evd_dbus_agent_register_object (GObject *object, guint connection_id, const gchar *object_path, GDBusInterfaceInfo *interface_info, GError **error); gboolean evd_dbus_agent_unregister_object (GObject *object, guint registration_id, GError **error); GDBusInterfaceInfo * evd_dbus_agent_get_registered_object_interface (GObject *object, guint registration_id, GError **error); GDBusMethodInvocation * evd_dbus_agent_get_method_invocation (GObject *object, guint registration_id, guint64 serial, GError **error); void evd_dbus_agent_set_object_vtable (GObject *object, EvdDBusAgentVTable *vtable, gpointer user_data); gboolean evd_dbus_agent_method_call_return (GObject *object, guint registration_id, guint64 serial, GVariant *return_parameters, GError **error); gboolean evd_dbus_agent_emit_signal (GObject *object, guint registration_id, const gchar *signal_name, GVariant *signal_parameters, GError **error); G_END_DECLS #endif /* __EVD_DBUS_AGENT_H__ */ EventDance-0.2.0/evd/evd-dbus-bridge.c000066400000000000000000001401721321356073300174220ustar00rootroot00000000000000/* * evd-dbus-bridge.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-dbus-bridge.h" #include "evd-utils.h" #include "evd-dbus-agent.h" G_DEFINE_TYPE (EvdDBusBridge, evd_dbus_bridge, EVD_TYPE_IPC_MECHANISM) #define EVD_DBUS_BRIDGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_DBUS_BRIDGE, \ EvdDBusBridgePrivate)) enum EvdDBusBridgeCmd { EVD_DBUS_BRIDGE_CMD_NONE, EVD_DBUS_BRIDGE_CMD_ERROR, EVD_DBUS_BRIDGE_CMD_REPLY, EVD_DBUS_BRIDGE_CMD_NEW_CONNECTION, EVD_DBUS_BRIDGE_CMD_CLOSE_CONNECTION, EVD_DBUS_BRIDGE_CMD_OWN_NAME, EVD_DBUS_BRIDGE_CMD_UNOWN_NAME, EVD_DBUS_BRIDGE_CMD_NAME_ACQUIRED, EVD_DBUS_BRIDGE_CMD_NAME_LOST, EVD_DBUS_BRIDGE_CMD_REGISTER_OBJECT, EVD_DBUS_BRIDGE_CMD_UNREGISTER_OBJECT, EVD_DBUS_BRIDGE_CMD_NEW_PROXY, EVD_DBUS_BRIDGE_CMD_CLOSE_PROXY, EVD_DBUS_BRIDGE_CMD_CALL_METHOD, EVD_DBUS_BRIDGE_CMD_CALL_METHOD_RETURN, EVD_DBUS_BRIDGE_CMD_EMIT_SIGNAL, EVD_DBUS_BRIDGE_CMD_PAD0, EVD_DBUS_BRIDGE_CMD_PAD1, EVD_DBUS_BRIDGE_CMD_PAD2, EVD_DBUS_BRIDGE_CMD_PAD3, EVD_DBUS_BRIDGE_CMD_PAD4, EVD_DBUS_BRIDGE_CMD_LAST }; enum EvdDBusBridgeErr { EVD_DBUS_BRIDGE_ERR_FAILED, EVD_DBUS_BRIDGE_ERR_INVALID_MSG, EVD_DBUS_BRIDGE_ERR_UNKNOW_COMMAND, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, EVD_DBUS_BRIDGE_ERR_CONNECTION_FAILED, EVD_DBUS_BRIDGE_ERR_ALREADY_REGISTERED, EVD_DBUS_BRIDGE_ERR_PROXY_FAILED, EVD_DBUS_BRIDGE_ERR_UNKNOWN_METHOD, EVD_DBUS_BRIDGE_ERR_PAD0, EVD_DBUS_BRIDGE_ERR_PAD1, EVD_DBUS_BRIDGE_ERR_PAD2, EVD_DBUS_BRIDGE_ERR_PAD3, EVD_DBUS_BRIDGE_ERR_PAD4, EVD_DBUS_BRIDGE_ERR_LAST }; /* private data */ struct _EvdDBusBridgePrivate { EvdDBusAgentVTable agent_vtable; #ifdef ENABLE_TESTS EvdDBusBridgeSendMsgCb send_msg_callback; gpointer send_msg_user_data; #endif }; typedef struct { EvdDBusBridge *bridge; GObject *obj; guint8 cmd; guint64 serial; guint32 conn_id; gint32 subject; gchar *args; gint err_code; } MsgClosure; static void evd_dbus_bridge_class_init (EvdDBusBridgeClass *class); static void evd_dbus_bridge_init (EvdDBusBridge *self); static void evd_dbus_bridge_finalize (GObject *obj); static void evd_dbus_bridge_dispose (GObject *obj); static void evd_dbus_bridge_send (EvdDBusBridge *self, GObject *obj, guint8 cmd, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args); static void evd_dbus_bridge_on_proxy_signal (GObject *obj, guint conn_id, guint proxy_id, const gchar *signal_name, GVariant *parameters, gpointer user_data); static void evd_dbus_bridge_on_proxy_props_changed (GObject *obj, guint conn_id, guint proxy_uuid, GVariant *changed_properties, GStrv *invalidated_properties, gpointer user_data); static void evd_dbus_bridge_on_name_acquired (GObject *object, guint conn_id, guint owner_id, gpointer user_data); static void evd_dbus_bridge_on_name_lost (GObject *object, guint conn_id, guint owner_id, gpointer user_data); static void evd_dbus_bridge_on_reg_obj_call_method (GObject *object, guint conn_id, const gchar *sender, const gchar *method_name, guint registration_id, GVariant *parameters, guint64 serial, gpointer user_data); static void transport_on_new_peer (EvdIpcMechanism *ipc_mechanism, EvdTransport *transport, EvdPeer *peer); static void transport_on_receive (EvdIpcMechanism *self, EvdTransport *transport, EvdPeer *peer, const guchar *data, gsize size); #ifndef ENABLE_TESTS void evd_dbus_bridge_process_msg (EvdDBusBridge *self, GObject *object, const gchar *msg, gsize length); #endif /* ENABLE_TESTS */ static void evd_dbus_bridge_class_init (EvdDBusBridgeClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIpcMechanismClass *ipc_mechanism_class = EVD_IPC_MECHANISM_CLASS (class); obj_class->dispose = evd_dbus_bridge_dispose; obj_class->finalize = evd_dbus_bridge_finalize; ipc_mechanism_class->transport_receive = transport_on_receive; ipc_mechanism_class->transport_new_peer = transport_on_new_peer; g_type_class_add_private (obj_class, sizeof (EvdDBusBridgePrivate)); } static void evd_dbus_bridge_init (EvdDBusBridge *self) { EvdDBusBridgePrivate *priv; priv = EVD_DBUS_BRIDGE_GET_PRIVATE (self); self->priv = priv; priv->agent_vtable.proxy_signal = evd_dbus_bridge_on_proxy_signal; priv->agent_vtable.proxy_properties_changed = evd_dbus_bridge_on_proxy_props_changed; priv->agent_vtable.method_call = evd_dbus_bridge_on_reg_obj_call_method; priv->agent_vtable.name_acquired = evd_dbus_bridge_on_name_acquired; priv->agent_vtable.name_lost = evd_dbus_bridge_on_name_lost; } static void evd_dbus_bridge_dispose (GObject *obj) { G_OBJECT_CLASS (evd_dbus_bridge_parent_class)->dispose (obj); } static void evd_dbus_bridge_finalize (GObject *obj) { G_OBJECT_CLASS (evd_dbus_bridge_parent_class)->finalize (obj); } static gchar * escape_json_for_args (const gchar *json) { gchar *escaped_json1; gchar *escaped_json2; escaped_json1 = g_strescape (json, "\b\f\n\r\t\'"); escaped_json2 = g_strescape (escaped_json1, "\b\f\n\r\t\'"); g_free (escaped_json1); return escaped_json2; } static MsgClosure * evd_dbus_bridge_new_msg_closure (EvdDBusBridge *self, GObject *obj, guint8 cmd, guint64 serial, guint32 conn_id, gint32 subject, const gchar *args, gint err_code) { MsgClosure *closure; closure = g_slice_new (MsgClosure); closure->bridge = self; closure->obj = obj; closure->cmd = cmd; closure->serial = serial; closure->conn_id = conn_id; closure->subject = subject; closure->args = g_strdup (args); closure->err_code = err_code; return closure; } static void evd_dbus_bridge_free_msg_closure (MsgClosure *closure) { g_free (closure->args); g_slice_free (MsgClosure, closure); } static void evd_dbus_bridge_on_proxy_signal (GObject *obj, guint conn_id, guint proxy_id, const gchar *signal_name, GVariant *parameters, gpointer user_data) { EvdDBusBridge *self = EVD_DBUS_BRIDGE (user_data); gchar *json; gchar *escaped_json; gchar *args; const gchar *signature; json = json_gvariant_serialize_data (parameters, NULL); escaped_json = escape_json_for_args (json); signature = g_variant_get_type_string (parameters); args = g_strdup_printf ("\\\"%s\\\",\\\"%s\\\",\\\"%s\\\"", signal_name, escaped_json, signature); evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_EMIT_SIGNAL, 0, conn_id, proxy_id, args); g_free (args); g_free (escaped_json); g_free (json); } static void evd_dbus_bridge_on_proxy_props_changed (GObject *obj, guint conn_id, guint proxy_id, GVariant *changed_properties, GStrv *invalidated_properties, gpointer user_data) { /* @TODO */ } static void evd_dbus_bridge_on_name_acquired (GObject *object, guint conn_id, guint owner_id, gpointer user_data) { EvdDBusBridge *self = EVD_DBUS_BRIDGE (user_data); evd_dbus_bridge_send (self, object, EVD_DBUS_BRIDGE_CMD_NAME_ACQUIRED, 0, conn_id, owner_id, ""); } static void evd_dbus_bridge_on_name_lost (GObject *object, guint conn_id, guint owner_id, gpointer user_data) { EvdDBusBridge *self = EVD_DBUS_BRIDGE (user_data); evd_dbus_bridge_send (self, object, EVD_DBUS_BRIDGE_CMD_NAME_LOST, 0, conn_id, owner_id, ""); } static void evd_dbus_bridge_on_reg_obj_call_method (GObject *obj, guint conn_id, const gchar *sender, const gchar *method_name, guint registration_id, GVariant *parameters, guint64 serial, gpointer user_data) { EvdDBusBridge *self = EVD_DBUS_BRIDGE (user_data); gchar *json; gchar *escaped_json; gchar *args; const gchar *signature; json = json_gvariant_serialize_data (parameters, NULL); escaped_json = escape_json_for_args (json); signature = g_variant_get_type_string (parameters); args = g_strdup_printf ("\\\"%s\\\",\\\"%s\\\",\\\"%s\\\",0,0", method_name, escaped_json, signature); evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_CALL_METHOD, serial, conn_id, registration_id, args); g_free (args); g_free (escaped_json); g_free (json); } static void evd_dbus_bridge_send (EvdDBusBridge *self, GObject *obj, guint8 cmd, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { gchar *json; json = g_strdup_printf ("[%u,%" G_GUINT64_FORMAT ",%u,%u,\"[%s]\"]", cmd, serial, conn_id, subject, args); if (EVD_IS_PEER (obj)) { GError *error = NULL; if (! evd_peer_send_text (EVD_PEER (obj), json, &error)) { g_warning ("error sending DBus msg to peer: %s", error->message); g_error_free (error); } } #ifdef ENABLE_TESTS if (self->priv->send_msg_callback != NULL) { self->priv->send_msg_callback (self, obj, json, self->priv->send_msg_user_data); } #endif g_free (json); } static gboolean evd_dbus_bridge_on_idle_send (gpointer user_data) { MsgClosure *closure = (MsgClosure *) user_data; evd_dbus_bridge_send (closure->bridge, closure->obj, closure->cmd, closure->serial, closure->conn_id, closure->subject, closure->args); evd_dbus_bridge_free_msg_closure (closure); return FALSE; } static void evd_dbus_bridge_send_in_idle (EvdDBusBridge *self, GObject *obj, guint8 cmd, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { MsgClosure *closure; closure = evd_dbus_bridge_new_msg_closure (self, obj, cmd, serial, conn_id, subject, args, 0); evd_timeout_add (NULL, 0, G_PRIORITY_DEFAULT, evd_dbus_bridge_on_idle_send, closure); } static void evd_dbus_bridge_send_error (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, gint err_code, const gchar *err_msg) { gchar *args; if (err_msg != NULL) args = g_strdup_printf ("%d,\\\"%s\\\"", err_code, err_msg); else args = g_strdup_printf ("%d", err_code); evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_ERROR, serial, conn_id, subject, args); g_free (args); } static gboolean evd_dbus_bridge_on_send_error_idle (gpointer user_data) { MsgClosure *closure = (MsgClosure *) user_data; evd_dbus_bridge_send_error (closure->bridge, closure->obj, closure->serial, closure->conn_id, closure->subject, closure->err_code, closure->args); evd_dbus_bridge_free_msg_closure (closure); return FALSE; } static void evd_dbus_bridge_send_error_in_idle (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, gint err_code, const gchar *err_msg) { MsgClosure *closure; closure = evd_dbus_bridge_new_msg_closure (self, obj, EVD_DBUS_BRIDGE_CMD_ERROR, serial, conn_id, subject, err_msg, err_code); evd_timeout_add (NULL, 0, G_PRIORITY_DEFAULT, evd_dbus_bridge_on_send_error_idle, closure); } static void evd_dbus_bridge_on_new_connection (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdDBusBridge *self; MsgClosure *closure = (MsgClosure *) user_data; guint conn_id; GError *error = NULL; self = closure->bridge; if ( (conn_id = evd_dbus_agent_new_connection_finish (obj, res, &error)) == 0) { evd_dbus_bridge_send_error (self, obj, closure->serial, 0, closure->subject, EVD_DBUS_BRIDGE_ERR_CONNECTION_FAILED, error->message); g_error_free (error); } else { gchar *args; args = g_strdup_printf ("%u", (guint32) conn_id); evd_dbus_bridge_send (closure->bridge, closure->obj, EVD_DBUS_BRIDGE_CMD_REPLY, closure->serial, 0, closure->subject, args); g_free (args); } evd_dbus_bridge_free_msg_closure (closure); } static void evd_dbus_bridge_new_connection (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, const gchar *args) { gchar *addr; gboolean reuse; MsgClosure *closure; GVariant *variant_args; variant_args = json_gvariant_deserialize_data (args, -1, "(sb)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error_in_idle (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(sb)", &addr, &reuse); closure = evd_dbus_bridge_new_msg_closure (self, obj, EVD_DBUS_BRIDGE_CMD_NEW_CONNECTION, serial, conn_id, 0, NULL, 0); evd_dbus_agent_new_connection (obj, addr, reuse, NULL, evd_dbus_bridge_on_new_connection, closure); g_free (addr); g_variant_unref (variant_args); } static void evd_dbus_bridge_close_connection (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject) { GError *error = NULL; /* @TODO: validate that 'subject' is 0 */ if (evd_dbus_agent_close_connection (obj, conn_id, &error)) { evd_dbus_bridge_send_in_idle (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, ""); } else { evd_dbus_bridge_send_error_in_idle (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, error->message); g_error_free (error); } } static void evd_dbus_bridge_own_name (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GVariant *variant_args; gchar *name; guint32 flags; GDBusConnection *dbus_conn; GError *error = NULL; guint owning_id; gchar *st_args; variant_args = json_gvariant_deserialize_data (args, -1, "(su)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(su)", &name, &flags); dbus_conn = evd_dbus_agent_get_connection (obj, conn_id, &error); if (dbus_conn == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, NULL); g_error_free (error); goto free; } owning_id = evd_dbus_agent_own_name (obj, conn_id, name, flags, &error); st_args = g_strdup_printf ("%u", owning_id); evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, st_args); g_free (st_args); free: g_free (name); g_variant_unref (variant_args); } static void evd_dbus_bridge_unown_name (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GError *error = NULL; if (evd_dbus_agent_unown_name (obj, subject, &error)) { evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, ""); } else { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); } } static void evd_dbus_bridge_register_object (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GVariant *variant_args; gchar *object_path; gchar *iface_data; gchar *node_data; guint reg_id = 0; GDBusNodeInfo *node_info = NULL; GError *error = NULL; variant_args = json_gvariant_deserialize_data (args, -1, "(ss)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(ss)", &object_path, &iface_data); /* create interface info */ node_data = g_strdup_printf ("%s", iface_data); node_info = g_dbus_node_info_new_for_xml (node_data, &error); if (node_info != NULL && node_info->interfaces != NULL) { GDBusInterfaceInfo *iface_info; iface_info = node_info->interfaces[0]; reg_id = evd_dbus_agent_register_object (obj, conn_id, object_path, iface_info, &error); if (reg_id > 0) { gchar *args; args = g_strdup_printf ("%u", reg_id); evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, args); g_free (args); } else { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_ALREADY_REGISTERED, NULL); g_error_free (error); } } else { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); } if (node_info != NULL) g_dbus_node_info_unref (node_info); g_free (node_data); g_free (iface_data); g_free (object_path); g_variant_unref (variant_args); } static void evd_dbus_bridge_unregister_object (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GError *error = NULL; if (evd_dbus_agent_unregister_object (obj, subject, &error)) { evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, ""); } else { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, NULL); g_error_free (error); } } static void evd_dbus_bridge_on_new_proxy (GObject *obj, GAsyncResult *res, gpointer user_data) { MsgClosure *closure = (MsgClosure *) user_data; guint proxy_id; GError *error = NULL; if ( (proxy_id = evd_dbus_agent_new_proxy_finish (obj, res, &error)) > 0) { gchar *args; args = g_strdup_printf ("%u", proxy_id); evd_dbus_bridge_send (closure->bridge, closure->obj, EVD_DBUS_BRIDGE_CMD_REPLY, closure->serial, closure->conn_id, closure->subject, args); g_free (args); } else { evd_dbus_bridge_send_error (closure->bridge, closure->obj, closure->serial, closure->conn_id, closure->subject, EVD_DBUS_BRIDGE_ERR_PROXY_FAILED, error->message); g_error_free (error); } evd_dbus_bridge_free_msg_closure (closure); } static void evd_dbus_bridge_new_proxy (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GVariant *variant_args; guint flags; gchar *name; gchar *obj_path; gchar *iface_name; MsgClosure *closure; variant_args = json_gvariant_deserialize_data (args, -1, "(sssu)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(sssu)", &name, &obj_path, &iface_name, &flags); closure = evd_dbus_bridge_new_msg_closure (self, obj, EVD_DBUS_BRIDGE_CMD_NEW_PROXY, serial, conn_id, subject, NULL, 0); evd_dbus_agent_new_proxy (obj, conn_id, flags, name, obj_path, iface_name, NULL, evd_dbus_bridge_on_new_proxy, closure); g_free (iface_name); g_free (obj_path); g_free (name); g_variant_unref (variant_args); } static void evd_dbus_bridge_close_proxy (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GError *error = NULL; if (evd_dbus_agent_close_proxy (obj, subject, &error)) { evd_dbus_bridge_send (self, obj, EVD_DBUS_BRIDGE_CMD_REPLY, serial, conn_id, subject, ""); } else { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, NULL); g_error_free (error); } } static void evd_dbus_proxy_on_call_method_return (GObject *obj, GAsyncResult *res, gpointer user_data) { MsgClosure *closure = (MsgClosure *) user_data; GVariant *ret_variant; GError *error = NULL; ret_variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (obj), res, &error); if (ret_variant != NULL) { gchar *json; gchar *escaped_json; gchar *args; json = json_gvariant_serialize_data (ret_variant, NULL); escaped_json = escape_json_for_args (json); args = g_strdup_printf ("\\\"%s\\\"", escaped_json); evd_dbus_bridge_send (closure->bridge, closure->obj, EVD_DBUS_BRIDGE_CMD_CALL_METHOD_RETURN, closure->serial, closure->conn_id, closure->subject, args); g_free (args); g_free (escaped_json); g_free (json); g_variant_unref (ret_variant); } else { gint err_code; gchar *err_msg = NULL; /* @TODO: organize this in a method to convert from DBus error to bridge error */ if (error->code == G_DBUS_ERROR_INVALID_ARGS) err_code = EVD_DBUS_BRIDGE_ERR_INVALID_ARGS; else if (error->code == G_DBUS_ERROR_UNKNOWN_METHOD) err_code = EVD_DBUS_BRIDGE_ERR_UNKNOWN_METHOD; else { err_code = EVD_DBUS_BRIDGE_ERR_FAILED; err_msg = error->message; } evd_dbus_bridge_send_error (closure->bridge, closure->obj, closure->serial, closure->conn_id, closure->subject, err_code, err_msg); g_error_free (error); } evd_dbus_bridge_free_msg_closure (closure); } static void evd_dbus_bridge_call_method (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 subject, guint32 conn_id, const gchar *args) { GVariant *variant_args; gchar *method_name; gchar *method_args; guint call_flags; gint timeout; gchar *signature; GDBusProxy *proxy; MsgClosure *closure; GVariant *params; variant_args = json_gvariant_deserialize_data (args, -1, "(ssgui)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(ssgui)", &method_name, &method_args, &signature, &call_flags, &timeout); params = json_gvariant_deserialize_data (method_args, -1, signature, NULL); if (params == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); goto out; } proxy = evd_dbus_agent_get_proxy (obj, subject, NULL); if (proxy == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, NULL); goto out; } closure = evd_dbus_bridge_new_msg_closure (self, obj, EVD_DBUS_BRIDGE_CMD_CALL_METHOD, serial, conn_id, subject, args, 0); g_dbus_proxy_call (proxy, method_name, params, call_flags, timeout, NULL, evd_dbus_proxy_on_call_method_return, closure); out: g_free (signature); g_free (method_args); g_free (method_name); g_variant_unref (variant_args); } static gchar * evd_dbus_bridge_get_method_signature_from_reg_object (GObject *obj, guint reg_id, guint64 serial) { gchar *signature; GString *sig_str; GDBusMethodInvocation *invocation; const GDBusMethodInfo *method_info; invocation = evd_dbus_agent_get_method_invocation (obj, reg_id, serial, NULL); if (invocation == NULL) return NULL; method_info = g_dbus_method_invocation_get_method_info (invocation); sig_str = g_string_new ("("); if (method_info->out_args != NULL) { gint i = 0; while (method_info->out_args[i] != NULL) { g_string_append (sig_str, method_info->out_args[i]->signature); i++; } } g_string_append (sig_str, ")"); signature = sig_str->str; g_string_free (sig_str, FALSE); return signature; } static void evd_dbus_bridge_call_method_return (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GVariant *variant_args = NULL; gchar *return_args = NULL; gchar *signature = NULL; GVariant *return_variant; gboolean invalid_args = FALSE; signature = evd_dbus_bridge_get_method_signature_from_reg_object (obj, subject, serial); if (signature == NULL) { invalid_args = TRUE; goto out; } variant_args = json_gvariant_deserialize_data (args, -1, "(s)", NULL); if (variant_args == NULL) { invalid_args = TRUE; goto out; } g_variant_get (variant_args, "(s)", &return_args); return_variant = json_gvariant_deserialize_data (return_args, -1, signature, NULL); if (return_variant == NULL) { invalid_args = TRUE; goto out; } if (! evd_dbus_agent_method_call_return (obj, subject, serial, return_variant, NULL)) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_SUBJECT, NULL); } out: if (invalid_args) evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); g_free (signature); g_free (return_args); if (variant_args != NULL) g_variant_unref (variant_args); } static void evd_dbus_bridge_emit_signal (EvdDBusBridge *self, GObject *obj, guint64 serial, guint32 conn_id, guint32 subject, const gchar *args) { GVariant *variant_args; gchar *signal_name; gchar *signal_args; gchar *signature; GVariant *signal_args_variant; GError *error = NULL; variant_args = json_gvariant_deserialize_data (args, -1, "(sss)", NULL); if (variant_args == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); return; } g_variant_get (variant_args, "(sss)", &signal_name, &signal_args, &signature); signal_args_variant = json_gvariant_deserialize_data (signal_args, -1, signature, NULL); if (signal_args_variant == NULL) { evd_dbus_bridge_send_error (self, obj, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_INVALID_ARGS, NULL); goto out; } else { g_variant_ref_sink (signal_args_variant); } if (! evd_dbus_agent_emit_signal (obj, subject, signal_name, signal_args_variant, &error)) { gint err_code; gchar *err_msg = NULL; if (error->code == G_DBUS_ERROR_INVALID_ARGS) err_code = EVD_DBUS_BRIDGE_ERR_INVALID_ARGS; else { err_code = EVD_DBUS_BRIDGE_ERR_FAILED; err_msg = error->message; } evd_dbus_bridge_send_error (self, obj, serial, conn_id, subject, err_code, err_msg); g_error_free (error); } out: if (signal_args_variant != NULL) g_variant_unref (signal_args_variant); g_free (signature); g_free (signal_args); g_free (signal_name); g_variant_unref (variant_args); } static void transport_on_new_peer (EvdIpcMechanism *ipc_mechanism, EvdTransport *transport, EvdPeer *peer) { EvdDBusBridge *self = EVD_DBUS_BRIDGE (ipc_mechanism); evd_dbus_agent_set_object_vtable (G_OBJECT (peer), &self->priv->agent_vtable, self); } static void transport_on_receive (EvdIpcMechanism *ipc_mechanism, EvdTransport *transport, EvdPeer *peer, const guchar *data, gsize size) { evd_dbus_bridge_process_msg (EVD_DBUS_BRIDGE (ipc_mechanism), G_OBJECT (peer), (const gchar *) data, size); } /* public methods */ EvdDBusBridge * evd_dbus_bridge_new (void) { EvdDBusBridge *self; self = g_object_new (EVD_TYPE_DBUS_BRIDGE, NULL); return self; } void evd_dbus_bridge_process_msg (EvdDBusBridge *self, GObject *object, const gchar *msg, gsize length) { GVariant *variant_msg; guint8 cmd; guint64 serial; guint32 conn_id; guint32 subject; gchar *args; variant_msg = json_gvariant_deserialize_data (msg, length, "(ytuus)", NULL); if (variant_msg == NULL) { evd_dbus_bridge_send_error_in_idle (self, object, 0, 0, 0, EVD_DBUS_BRIDGE_ERR_INVALID_MSG, NULL); return; } g_variant_get (variant_msg, "(ytuus)", &cmd, &serial, &conn_id, &subject, &args); switch (cmd) { case EVD_DBUS_BRIDGE_CMD_NEW_CONNECTION: evd_dbus_bridge_new_connection (self, object, serial, conn_id, args); break; case EVD_DBUS_BRIDGE_CMD_CLOSE_CONNECTION: evd_dbus_bridge_close_connection (self, object, serial, conn_id, subject); break; case EVD_DBUS_BRIDGE_CMD_OWN_NAME: evd_dbus_bridge_own_name (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_UNOWN_NAME: evd_dbus_bridge_unown_name (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_REGISTER_OBJECT: evd_dbus_bridge_register_object (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_UNREGISTER_OBJECT: evd_dbus_bridge_unregister_object (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_NEW_PROXY: evd_dbus_bridge_new_proxy (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_CLOSE_PROXY: evd_dbus_bridge_close_proxy (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_CALL_METHOD: evd_dbus_bridge_call_method (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_CALL_METHOD_RETURN: evd_dbus_bridge_call_method_return (self, object, serial, conn_id, subject, args); break; case EVD_DBUS_BRIDGE_CMD_EMIT_SIGNAL: evd_dbus_bridge_emit_signal (self, object, serial, conn_id, subject, args); break; default: evd_dbus_bridge_send_error_in_idle (self, object, serial, conn_id, 0, EVD_DBUS_BRIDGE_ERR_UNKNOW_COMMAND, NULL); break; } g_free (args); g_variant_unref (variant_msg); } #ifdef ENABLE_TESTS void evd_dbus_bridge_track_object (EvdDBusBridge *self, GObject *object) { evd_dbus_agent_set_object_vtable (object, &self->priv->agent_vtable, self); } void evd_dbus_bridge_set_send_msg_callback (EvdDBusBridge *self, EvdDBusBridgeSendMsgCb callback, gpointer user_data) { g_return_if_fail (EVD_IS_DBUS_BRIDGE (self)); self->priv->send_msg_callback = callback; self->priv->send_msg_user_data = user_data; } #endif /* ENABLE_TESTS */ EventDance-0.2.0/evd/evd-dbus-bridge.h000066400000000000000000000063541321356073300174320ustar00rootroot00000000000000/* * evd-dbus-bridge.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_DBUS_BRIDGE_H__ #define __EVD_DBUS_BRIDGE_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-ipc-mechanism.h" G_BEGIN_DECLS typedef struct _EvdDBusBridge EvdDBusBridge; typedef struct _EvdDBusBridgeClass EvdDBusBridgeClass; typedef struct _EvdDBusBridgePrivate EvdDBusBridgePrivate; struct _EvdDBusBridge { EvdIpcMechanism parent; EvdDBusBridgePrivate *priv; }; struct _EvdDBusBridgeClass { EvdIpcMechanismClass parent_class; }; #define EVD_TYPE_DBUS_BRIDGE (evd_dbus_bridge_get_type ()) #define EVD_DBUS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_DBUS_BRIDGE, EvdDBusBridge)) #define EVD_DBUS_BRIDGE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_DBUS_BRIDGE, EvdDBusBridgeClass)) #define EVD_IS_DBUS_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_DBUS_BRIDGE)) #define EVD_IS_DBUS_BRIDGE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_DBUS_BRIDGE)) #define EVD_DBUS_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_DBUS_BRIDGE, EvdDBusBridgeClass)) GType evd_dbus_bridge_get_type (void) G_GNUC_CONST; EvdDBusBridge * evd_dbus_bridge_new (void); #ifdef ENABLE_TESTS /* only for testing purposes, DO NOT use in your programs */ typedef void (* EvdDBusBridgeSendMsgCb) (EvdDBusBridge *self, GObject *object, const gchar *json, gpointer user_data); void evd_dbus_bridge_process_msg (EvdDBusBridge *self, GObject *object, const gchar *msg, gsize length); void evd_dbus_bridge_set_send_msg_callback (EvdDBusBridge *self, EvdDBusBridgeSendMsgCb callback, gpointer user_data); void evd_dbus_bridge_track_object (EvdDBusBridge *self, GObject *object); #endif /* ENABLE_TESTS */ G_END_DECLS #endif /* __EVD_DBUS_BRIDGE_H__ */ EventDance-0.2.0/evd/evd-dbus-daemon.c000066400000000000000000000201321321356073300174220ustar00rootroot00000000000000/* * evd-dbus-daemon.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-dbus-daemon.h" #include "evd-error.h" #include #define EVD_DBUS_DAEMON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_DBUS_DAEMON, \ EvdDBusDaemonPrivate)) /* private data */ struct _EvdDBusDaemonPrivate { GPid pid; gchar *addr; gchar *config_file; }; /* properties */ enum { PROP_0, PROP_CONFIG_FILE, PROP_ADDRESS }; static void evd_dbus_daemon_class_init (EvdDBusDaemonClass *class); static void evd_dbus_daemon_init (EvdDBusDaemon *self); static void evd_dbus_daemon_finalize (GObject *obj); static void evd_dbus_daemon_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_dbus_daemon_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_dbus_daemon_initable_iface_init (GInitableIface *iface); static gboolean evd_dbus_daemon_initable_init (GInitable *initable, GCancellable *cancellable, GError **error); G_DEFINE_TYPE_WITH_CODE (EvdDBusDaemon, evd_dbus_daemon, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, evd_dbus_daemon_initable_iface_init)); static void evd_dbus_daemon_initable_iface_init (GInitableIface *iface) { iface->init = evd_dbus_daemon_initable_init; } static void evd_dbus_daemon_class_init (EvdDBusDaemonClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_dbus_daemon_finalize; obj_class->get_property = evd_dbus_daemon_get_property; obj_class->set_property = evd_dbus_daemon_set_property; g_object_class_install_property (obj_class, PROP_CONFIG_FILE, g_param_spec_string ("config-file", "DBus configuration file", "Filename of the configuration file to pass to the DBus daemon", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_ADDRESS, g_param_spec_string ("address", "Address", "DBus daemon address", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdDBusDaemonPrivate)); } static void evd_dbus_daemon_init (EvdDBusDaemon *self) { EvdDBusDaemonPrivate *priv; priv = EVD_DBUS_DAEMON_GET_PRIVATE (self); self->priv = priv; priv->config_file = NULL; priv->pid = 0; priv->addr = NULL; } static void evd_dbus_daemon_finalize (GObject *obj) { EvdDBusDaemon *self = EVD_DBUS_DAEMON (obj); g_free (self->priv->config_file); g_free (self->priv->addr); if (self->priv->pid > 0) { kill (self->priv->pid, SIGTERM); g_spawn_close_pid (self->priv->pid); } G_OBJECT_CLASS (evd_dbus_daemon_parent_class)->finalize (obj); } static void evd_dbus_daemon_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdDBusDaemon *self; self = EVD_DBUS_DAEMON (obj); switch (prop_id) { case PROP_CONFIG_FILE: self->priv->config_file = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_dbus_daemon_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdDBusDaemon *self; self = EVD_DBUS_DAEMON (obj); switch (prop_id) { case PROP_CONFIG_FILE: g_value_take_string (value, self->priv->config_file); break; case PROP_ADDRESS: g_value_set_string (value, self->priv->addr); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static GPid spawn_dbus_daemon (const gchar *config_file, gint *stdout_fd, GError **error) { gchar *cmdline; gchar **argv = NULL; GPid pid; cmdline = g_strdup_printf ("dbus-daemon --config-file %s --print-address --nofork", config_file); argv = g_strsplit (cmdline, " ", 0); g_free (cmdline); if (! g_spawn_async_with_pipes (".", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL, stdout_fd, NULL, error)) { pid = -1; } g_strfreev (argv); return pid; } static gboolean evd_dbus_daemon_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { EvdDBusDaemon *self = EVD_DBUS_DAEMON (initable); gint stdout_fd; self->priv->pid = spawn_dbus_daemon (self->priv->config_file, &stdout_fd, error); if (self->priv->pid <= 0) { return FALSE; } else { gchar buf[256] = { 0, }; gchar **lines; gssize size; errno = 0; size = read (stdout_fd, buf, 256); if (size >= 0) { lines = g_strsplit (buf, "\n", 0); self->priv->addr = g_strdup (lines[0]); g_strfreev (lines); return TRUE; } else { g_set_error (error, EVD_ERRNO_ERROR, errno, "Failed to D-Bus daemon address from stdout: %s", strerror (errno)); return FALSE; } } } /* public methods */ EvdDBusDaemon * evd_dbus_daemon_new (const gchar *config_file, GError **error) { g_return_val_if_fail (config_file != NULL, NULL); return EVD_DBUS_DAEMON (g_initable_new (EVD_TYPE_DBUS_DAEMON, NULL, error, "config-file", config_file, NULL)); } EventDance-0.2.0/evd/evd-dbus-daemon.h000066400000000000000000000042731321356073300174370ustar00rootroot00000000000000/* * evd-dbus-daemon.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_DBUS_DAEMON_H__ #define __EVD_DBUS_DAEMON_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _EvdDBusDaemon EvdDBusDaemon; typedef struct _EvdDBusDaemonClass EvdDBusDaemonClass; typedef struct _EvdDBusDaemonPrivate EvdDBusDaemonPrivate; struct _EvdDBusDaemon { GObject parent; EvdDBusDaemonPrivate *priv; }; struct _EvdDBusDaemonClass { GObjectClass parent_class; /* virtual methods */ /* signal prototypes */ }; #define EVD_TYPE_DBUS_DAEMON (evd_dbus_daemon_get_type ()) #define EVD_DBUS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_DBUS_DAEMON, EvdDBusDaemon)) #define EVD_DBUS_DAEMON_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_DBUS_DAEMON, EvdDBusDaemonClass)) #define EVD_IS_DBUS_DAEMON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_DBUS_DAEMON)) #define EVD_IS_DBUS_DAEMON_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_DBUS_DAEMON)) #define EVD_DBUS_DAEMON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_DBUS_DAEMON, EvdDBusDaemonClass)) GType evd_dbus_daemon_get_type (void) G_GNUC_CONST; EvdDBusDaemon * evd_dbus_daemon_new (const gchar *config_file, GError **error); G_END_DECLS #endif /* __EVD_DBUS_DAEMON_H__ */ EventDance-0.2.0/evd/evd-error.c000066400000000000000000000022611321356073300163600ustar00rootroot00000000000000/* * evd-error.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-error.h" /** * evd_error_propagate_gnutls: * * Since: 0.2.0 **/ gboolean evd_error_propagate_gnutls (gint gnutls_error_code, GError **error) { if (gnutls_error_code == GNUTLS_E_SUCCESS) { return FALSE; } else { g_set_error_literal (error, EVD_GNUTLS_ERROR, gnutls_error_code, gnutls_strerror (gnutls_error_code)); return TRUE; } } EventDance-0.2.0/evd/evd-error.h000066400000000000000000000026531321356073300163720ustar00rootroot00000000000000/* * evd-error.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_ERROR_H__ #define __EVD_ERROR_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include #define EVD_GNUTLS_ERROR_DOMAIN_STR "org.eventdance.lib.Gnutls.ErrorDomain" #define EVD_GNUTLS_ERROR g_quark_from_string (EVD_GNUTLS_ERROR_DOMAIN_STR) #define EVD_ERRNO_ERROR_DOMAIN_STR "org.eventdance.lib.Errno.ErrorDomain" #define EVD_ERRNO_ERROR g_quark_from_string (EVD_ERRNO_ERROR_DOMAIN_STR) gboolean evd_error_propagate_gnutls (gint gnutls_error_code, GError **error); #endif /* __EVD_ERROR_H__ */ EventDance-0.2.0/evd/evd-http-chunked-decoder.c000066400000000000000000000173201321356073300212320ustar00rootroot00000000000000/* * evd-http-chunked-decoder.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include "evd-http-chunked-decoder.h" #define EVD_HTTP_CHUNKED_DECODER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_HTTP_CHUNKED_DECODER, \ EvdHttpChunkedDecoderPrivate)) /* private data */ struct _EvdHttpChunkedDecoderPrivate { gsize chunk_left; guint status; gchar hdr_buf[10]; gsize hdr_buf_len; guint crlf_pos; }; enum { READING_CHUNK_HEADER, READING_CONTENT, READING_CRLF_1, READING_CRLF_2 }; static void evd_http_chunked_decoder_class_init (EvdHttpChunkedDecoderClass *class); static void evd_http_chunked_decoder_init (EvdHttpChunkedDecoder *self); static void converter_iface_init (GConverterIface *iface); static 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); static void reset (GConverter *converter); G_DEFINE_TYPE_WITH_CODE (EvdHttpChunkedDecoder, evd_http_chunked_decoder, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, converter_iface_init)) static void evd_http_chunked_decoder_class_init (EvdHttpChunkedDecoderClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); g_type_class_add_private (obj_class, sizeof (EvdHttpChunkedDecoderPrivate)); } static void converter_iface_init (GConverterIface *iface) { iface->reset = reset; iface->convert = convert; } static void evd_http_chunked_decoder_init (EvdHttpChunkedDecoder *self) { EvdHttpChunkedDecoderPrivate *priv; priv = EVD_HTTP_CHUNKED_DECODER_GET_PRIVATE (self); self->priv = priv; reset (G_CONVERTER (self)); } static 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) { EvdHttpChunkedDecoder *self = EVD_HTTP_CHUNKED_DECODER (converter); GConverterResult result = G_CONVERTER_CONVERTED; gsize bw = 0; const gchar *_inbuf = (const gchar *) inbuf; char c; gboolean done = FALSE; gsize pos = 0; while (! done && pos < inbuf_size && bw < outbuf_size) { c = _inbuf[pos]; switch (self->priv->status) { case READING_CHUNK_HEADER: { if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c == 32) ) { pos++; self->priv->hdr_buf[ self->priv->hdr_buf_len ] = c; self->priv->hdr_buf_len++; } else if (self->priv->hdr_buf_len > 0) { self->priv->hdr_buf[ self->priv->hdr_buf_len ] = '\0'; sscanf (self->priv->hdr_buf, "%x", (guint *) &self->priv->chunk_left); self->priv->hdr_buf_len = 0; self->priv->status = READING_CRLF_1; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to parse chunk-size of chunked encoded content"); result = G_CONVERTER_ERROR; done = TRUE; } break; } case READING_CRLF_1: case READING_CRLF_2: { gboolean err = FALSE; if (self->priv->crlf_pos == 0) { if (c == '\r') self->priv->crlf_pos++; else { g_debug ("kaka: %d", c); err = TRUE; } } else { if (c == '\n') { self->priv->crlf_pos = 0; if (self->priv->status == READING_CRLF_1) { if (self->priv->chunk_left == 0) { result = G_CONVERTER_FINISHED; done = TRUE; } else self->priv->status = READING_CONTENT; } else self->priv->status = READING_CHUNK_HEADER; } else err = TRUE; } if (err) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to parse chunked encoded content"); result = G_CONVERTER_ERROR; done = TRUE; } else { pos++; } break; } case READING_CONTENT: { gsize move_size; move_size = MIN (self->priv->chunk_left, inbuf_size - pos); move_size = MIN (move_size, outbuf_size - bw); g_memmove (outbuf + bw, inbuf + pos, move_size); pos += move_size; bw += move_size; self->priv->chunk_left -= move_size; if (self->priv->chunk_left == 0) self->priv->status = READING_CRLF_2; break; } } } if (bytes_read != NULL) *bytes_read = pos; if (bytes_written != NULL) *bytes_written = bw; if (result != G_CONVERTER_ERROR && flags & G_CONVERTER_FLUSH) result = G_CONVERTER_FLUSHED; return result; } static void reset (GConverter *converter) { EvdHttpChunkedDecoder *self = EVD_HTTP_CHUNKED_DECODER (converter); self->priv->chunk_left = 0; self->priv->status = READING_CHUNK_HEADER; self->priv->hdr_buf_len = 0; self->priv->crlf_pos = 0; } /* public methods */ EvdHttpChunkedDecoder * evd_http_chunked_decoder_new (void) { return g_object_new (EVD_TYPE_HTTP_CHUNKED_DECODER, NULL); } EventDance-0.2.0/evd/evd-http-chunked-decoder.h000066400000000000000000000042171321356073300212400ustar00rootroot00000000000000/* * evd-http-chunked-decoder.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_HTTP_CHUNKED_DECODER_H__ #define __EVD_HTTP_CHUNKED_DECODER_H__ #include G_BEGIN_DECLS typedef struct _EvdHttpChunkedDecoder EvdHttpChunkedDecoder; typedef struct _EvdHttpChunkedDecoderClass EvdHttpChunkedDecoderClass; typedef struct _EvdHttpChunkedDecoderPrivate EvdHttpChunkedDecoderPrivate; struct _EvdHttpChunkedDecoder { GObject parent; EvdHttpChunkedDecoderPrivate *priv; }; struct _EvdHttpChunkedDecoderClass { GObjectClass parent_class; }; #define EVD_TYPE_HTTP_CHUNKED_DECODER (evd_http_chunked_decoder_get_type ()) #define EVD_HTTP_CHUNKED_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_HTTP_CHUNKED_DECODER, EvdHttpChunkedDecoder)) #define EVD_HTTP_CHUNKED_DECODER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_HTTP_CHUNKED_DECODER, EvdHttpChunkedDecoderClass)) #define EVD_IS_HTTP_CHUNKED_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_HTTP_CHUNKED_DECODER)) #define EVD_IS_HTTP_CHUNKED_DECODER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_HTTP_CHUNKED_DECODER)) #define EVD_HTTP_CHUNKED_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_HTTP_CHUNKED_DECODER, EvdHttpChunkedDecoderClass)) GType evd_http_chunked_decoder_get_type (void) G_GNUC_CONST; EvdHttpChunkedDecoder * evd_http_chunked_decoder_new (void); G_END_DECLS #endif /* __EVD_HTTP_CHUNKED_DECODER_H__ */ EventDance-0.2.0/evd/evd-http-connection.c000066400000000000000000001335531321356073300203540ustar00rootroot00000000000000/* * evd-http-connection.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-http-connection.h" #include "evd-error.h" #include "evd-buffered-input-stream.h" #include "evd-http-chunked-decoder.h" G_DEFINE_TYPE (EvdHttpConnection, evd_http_connection, EVD_TYPE_CONNECTION) #define EVD_HTTP_CONNECTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_HTTP_CONNECTION, \ EvdHttpConnectionPrivate)) #define HEADER_BLOCK_SIZE 256 #define MAX_HEADERS_SIZE 16 * 1024 #define CONTENT_BLOCK_SIZE 4096 /* private data */ struct _EvdHttpConnectionPrivate { GSimpleAsyncResult *async_result; GString *buf; gchar *last_buf_block; gint priority; gint last_headers_pos; SoupHTTPVersion http_ver; goffset content_len; SoupEncoding encoding; gsize content_read; EvdHttpRequest *current_request; gboolean keepalive; GConverter *chunked_decoder; }; /* properties */ enum { PROP_0 }; struct EvdHttpConnectionResponseHeaders { SoupMessageHeaders *headers; SoupHTTPVersion version; guint status_code; gchar *reason_phrase; }; struct ContentReadData { gssize size; gboolean more; }; static void evd_http_connection_class_init (EvdHttpConnectionClass *class); static void evd_http_connection_init (EvdHttpConnection *self); static void evd_http_connection_finalize (GObject *obj); static void evd_http_connection_dispose (GObject *obj); static gboolean evd_http_connection_close (GIOStream *stream, GCancellable *cancellable, GError **error); static void evd_http_connection_read_headers_block (EvdHttpConnection *self); static void evd_http_connection_read_content_block (EvdHttpConnection *self, void *buf, gsize size); static void evd_http_connection_class_init (EvdHttpConnectionClass *class) { GObjectClass *obj_class; GIOStreamClass *io_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_http_connection_dispose; obj_class->finalize = evd_http_connection_finalize; io_stream_class = G_IO_STREAM_CLASS (class); io_stream_class->close_fn = evd_http_connection_close; g_type_class_add_private (obj_class, sizeof (EvdHttpConnectionPrivate)); } static void evd_http_connection_init (EvdHttpConnection *self) { EvdHttpConnectionPrivate *priv; priv = EVD_HTTP_CONNECTION_GET_PRIVATE (self); self->priv = priv; priv->async_result = NULL; priv->buf = g_string_new (""); priv->last_headers_pos = 0; priv->http_ver = SOUP_HTTP_1_1; priv->encoding = SOUP_ENCODING_UNRECOGNIZED; priv->content_len = 0; priv->keepalive = FALSE; priv->chunked_decoder = G_CONVERTER (evd_http_chunked_decoder_new ()); priv->last_buf_block = NULL; } static void evd_http_connection_dispose (GObject *obj) { EvdHttpConnection *self = EVD_HTTP_CONNECTION (obj); if (self->priv->current_request != NULL) { g_object_unref (self->priv->current_request); self->priv->current_request = NULL; } if (self->priv->async_result != NULL) { g_simple_async_result_set_error (self->priv->async_result, G_IO_ERROR, G_IO_ERROR_FAILED, "HTTP connection destroyed while an operation was pending"); g_simple_async_result_complete (self->priv->async_result); g_object_unref (self->priv->async_result); self->priv->async_result = NULL; } G_OBJECT_CLASS (evd_http_connection_parent_class)->dispose (obj); } static void evd_http_connection_finalize (GObject *obj) { EvdHttpConnection *self = EVD_HTTP_CONNECTION (obj); g_string_free (self->priv->buf, TRUE); g_object_unref (self->priv->chunked_decoder); if (self->priv->last_buf_block != NULL) g_slice_free1 (CONTENT_BLOCK_SIZE, self->priv->last_buf_block); G_OBJECT_CLASS (evd_http_connection_parent_class)->finalize (obj); } static gboolean evd_http_connection_close (GIOStream *stream, GCancellable *cancellable, GError **error) { EvdHttpConnection *self = EVD_HTTP_CONNECTION (stream); gboolean result; result = G_IO_STREAM_CLASS (evd_http_connection_parent_class)->close_fn (stream, cancellable, error); if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Connection closed during async operation"); g_simple_async_result_complete (res); g_object_unref (res); } return result; } static void evd_http_connection_response_headers_destroy (gpointer data) { struct EvdHttpConnectionResponseHeaders *response; response = (struct EvdHttpConnectionResponseHeaders *) data; if (response->headers != NULL) soup_message_headers_free (response->headers); g_free (response->reason_phrase); g_free (response); } static SoupURI * evd_http_connection_build_uri (EvdHttpConnection *self, const gchar *path, SoupMessageHeaders *headers) { gchar *scheme; const gchar *host; gchar *uri_str; SoupURI *uri; if (evd_connection_get_tls_active (EVD_CONNECTION (self))) scheme = g_strdup ("https"); else scheme = g_strdup ("http"); host = soup_message_headers_get_one (headers, "host"); uri_str = g_strconcat (scheme, "://", host, path, NULL); uri = soup_uri_new (uri_str); g_free (uri_str); g_free (scheme); return uri; } static void evd_http_connection_on_read_headers (EvdHttpConnection *self, GString *buf) { GSimpleAsyncResult *res; gpointer source_tag; if (self->priv->async_result == NULL) return; g_io_stream_clear_pending (G_IO_STREAM (self)); res = self->priv->async_result; self->priv->async_result = NULL; source_tag = g_simple_async_result_get_source_tag (res); if (source_tag == evd_http_connection_read_request_headers) { SoupMessageHeaders *headers; gchar *method = NULL; gchar *path = NULL; SoupHTTPVersion version; headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); if (soup_headers_parse_request (buf->str, buf->len - 2, headers, &method, &path, &version) && method != NULL && version <= SOUP_HTTP_1_1) { EvdHttpRequest *request; SoupURI *uri; const gchar *conn_header; uri = evd_http_connection_build_uri (self, path, headers); request = g_object_new (EVD_TYPE_HTTP_REQUEST, "version", version, "headers", headers, "method", method, "uri", uri, NULL); soup_uri_free (uri); evd_http_connection_set_current_request (self, request); g_simple_async_result_set_op_res_gpointer (res, request, g_object_unref); self->priv->encoding = soup_message_headers_get_encoding (headers); self->priv->content_len = soup_message_headers_get_content_length (headers); /* detect if is keep-alive */ conn_header = soup_message_headers_get_one (headers, "Connection"); self->priv->keepalive = (version == SOUP_HTTP_1_0 && conn_header != NULL && g_strstr_len (conn_header, -1, "keep-alive") != NULL) || (version == SOUP_HTTP_1_1 && conn_header != NULL && g_strstr_len (conn_header, -1, "close") == NULL); } else { soup_message_headers_free (headers); g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to parse HTTP request headers"); } g_free (method); g_free (path); } else if (source_tag == evd_http_connection_read_response_headers) { struct EvdHttpConnectionResponseHeaders *response; response = g_new0 (struct EvdHttpConnectionResponseHeaders, 1); response->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); if (soup_headers_parse_response (buf->str, buf->len - 2, response->headers, &response->version, &response->status_code, &response->reason_phrase)) { g_simple_async_result_set_op_res_gpointer (res, response, evd_http_connection_response_headers_destroy); self->priv->encoding = soup_message_headers_get_encoding (response->headers); self->priv->content_len = soup_message_headers_get_content_length (response->headers); } else { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to parse HTTP response headers"); } } g_simple_async_result_complete_in_idle (res); g_object_unref (res); } static gint evd_http_connection_find_end_headers_mark (const GString *buf, gint last_pos) { gint i; i = last_pos; while (i < buf->len) { if (buf->str[i] != '\r' && buf->str[i] != '\n') { i = i + 4; } else if (buf->str[i+1] != '\r' && buf->str[i+1] != '\n') { i = i - 3; } else if (buf->str[i+2] == '\r' && buf->str[i+3] == '\n') { if (buf->str[i] == '\r' && buf->str[i+1] == '\n') return i + 4; else i = i + 4; } else { i = i - 1; } } return -1; } static void evd_http_connection_on_read_headers_block (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdHttpConnection *self = EVD_HTTP_CONNECTION (user_data); GError *error = NULL; gssize size; if ( (size = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error)) >= 0) { gint pos; gint extra; extra = HEADER_BLOCK_SIZE - size; if (extra > 0) g_string_truncate (self->priv->buf, self->priv->buf->len - extra); if ( (pos = evd_http_connection_find_end_headers_mark (self->priv->buf, self->priv->last_headers_pos)) > 0) { void *unread_buf; gsize unread_size; unread_size = self->priv->buf->len - pos; if (unread_size > 0) { /* unread data beyond HTTP headers, back to the stream */ unread_buf = self->priv->buf->str + pos; if (evd_buffered_input_stream_unread (EVD_BUFFERED_INPUT_STREAM (obj), unread_buf, unread_size, NULL, &error) >= 0) { g_string_set_size (self->priv->buf, pos); } } evd_http_connection_on_read_headers (self, self->priv->buf); self->priv->last_headers_pos = 0; g_string_set_size (self->priv->buf, 0); } else if (self->priv->buf->len < MAX_HEADERS_SIZE) { self->priv->last_headers_pos = self->priv->buf->len - 3; evd_http_connection_read_headers_block (self); } else { g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "HTTP headers are too long"); } } if (error != NULL) { if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_from_error (res, error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } g_error_free (error); } g_object_unref (self); } static void evd_http_connection_read_headers_block (EvdHttpConnection *self) { GInputStream *stream; void *buf; gsize new_block_size; new_block_size = MIN (MAX_HEADERS_SIZE, self->priv->buf->len + HEADER_BLOCK_SIZE) - self->priv->buf->len; if (new_block_size <= 0) { /* @TODO: handle error, max header size reached */ g_warning ("Unhandled error: max header size reached"); return; } g_string_set_size (self->priv->buf, self->priv->buf->len + new_block_size); buf = self->priv->buf->str + self->priv->buf->len - new_block_size; stream = g_io_stream_get_input_stream (G_IO_STREAM (self)); g_object_ref (self); g_input_stream_read_async (stream, buf, new_block_size, evd_connection_get_priority (EVD_CONNECTION (self)), NULL, evd_http_connection_on_read_headers_block, self); } static void evd_http_connection_read_headers_async (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data, gpointer source_tag) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, source_tag); if (! g_io_stream_set_pending (G_IO_STREAM (self), &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->keepalive = FALSE; self->priv->async_result = res; g_string_set_size (self->priv->buf, 0); self->priv->last_headers_pos = 12; evd_http_connection_read_headers_block (self); } static void evd_http_connection_read_next_content_block (EvdHttpConnection *self) { gsize new_block_size; gchar *buf; new_block_size = CONTENT_BLOCK_SIZE; if (self->priv->encoding == SOUP_ENCODING_CHUNKED) { if (self->priv->last_buf_block == NULL) self->priv->last_buf_block = g_slice_alloc (CONTENT_BLOCK_SIZE); buf = self->priv->last_buf_block; } else { if (self->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH) new_block_size = MIN (self->priv->content_len - self->priv->content_read, CONTENT_BLOCK_SIZE); /* enlarge buffer, if necessary */ if (self->priv->buf->len < self->priv->content_read + new_block_size) g_string_set_size (self->priv->buf, self->priv->content_read + new_block_size); buf = self->priv->buf->str + self->priv->buf->len - new_block_size; } evd_http_connection_read_content_block (self, buf, new_block_size); } static gboolean evd_http_connection_process_read_content (EvdHttpConnection *self, gsize size, gboolean *done, GError **error) { gboolean result = TRUE; if (self->priv->encoding == SOUP_ENCODING_CHUNKED) { gchar outbuf[1024 + 1] = { 0, }; GConverterResult result; gsize total_bytes_read = 0; gsize bytes_read = 0; gsize bytes_written = 0; do { result = g_converter_convert (self->priv->chunked_decoder, self->priv->last_buf_block + total_bytes_read, size - total_bytes_read, outbuf, 1024, G_CONVERTER_NO_FLAGS, &bytes_read, &bytes_written, error); total_bytes_read += bytes_read; g_string_append_len (self->priv->buf, outbuf, bytes_written); self->priv->content_read += bytes_written; } while (result != G_CONVERTER_ERROR && result != G_CONVERTER_FINISHED && total_bytes_read < size); if (result == G_CONVERTER_FINISHED) { g_converter_reset (self->priv->chunked_decoder); *done = TRUE; } else if (result == G_CONVERTER_ERROR) { result = FALSE; g_converter_reset (self->priv->chunked_decoder); *done = TRUE; } } else { self->priv->content_read += size; /* are we done reading? */ if (! evd_connection_is_connected (EVD_CONNECTION (self)) || ( (self->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && self->priv->content_read >= self->priv->content_len) && (self->priv->encoding != SOUP_ENCODING_EOF) && (self->priv->encoding != SOUP_ENCODING_UNRECOGNIZED)) ) { *done = TRUE; } } return result; } static void evd_http_connection_on_read_content_block (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdHttpConnection *self = EVD_HTTP_CONNECTION (user_data); GError *error = NULL; gssize size; gboolean done = FALSE; gpointer source_tag; if ( (size = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error)) > 0) { if (! evd_http_connection_process_read_content (self, size, &done, &error)) { g_simple_async_result_set_from_error (self->priv->async_result, error); g_error_free (error); } } else if (size == 0) { done = TRUE; } else { g_simple_async_result_set_from_error (self->priv->async_result, error); g_error_free (error); done = TRUE; } source_tag = g_simple_async_result_get_source_tag (self->priv->async_result); if (source_tag == evd_http_connection_read_all_content) { if (done) g_string_set_size (self->priv->buf, self->priv->content_read); else evd_http_connection_read_next_content_block (self); } else if (source_tag == evd_http_connection_read_content) { if (size >= 0) { struct ContentReadData *data; data = g_new0 (struct ContentReadData, 1); data->size = size; data->more = ! done; g_simple_async_result_set_op_res_gpointer (self->priv->async_result, data, g_free); } done = TRUE; } if (done) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_io_stream_clear_pending (G_IO_STREAM (self)); g_simple_async_result_complete (res); g_object_unref (res); } g_object_unref (self); } static void evd_http_connection_read_content_block (EvdHttpConnection *self, void *buf, gsize size) { GInputStream *stream; stream = g_io_stream_get_input_stream (G_IO_STREAM (self)); g_object_ref (self); g_input_stream_read_async (stream, buf, size, evd_connection_get_priority (EVD_CONNECTION (self)), NULL, evd_http_connection_on_read_content_block, self); } static void evd_http_connection_on_write_request_headers (GObject *obj, GAsyncResult *res, gpointer user_data) { gssize size; GError *error = NULL; EvdHttpConnection *self = EVD_HTTP_CONNECTION (user_data); GSimpleAsyncResult *_res; g_io_stream_clear_pending (G_IO_STREAM (self)); if (self->priv->async_result == NULL) { /* this happens if the connection was closed while asynchronously writing request headers */ goto out; } _res = self->priv->async_result; self->priv->async_result = NULL; size = g_output_stream_write_finish (G_OUTPUT_STREAM (obj), res, &error); if (size < 0) { g_simple_async_result_set_from_error (_res, error); g_error_free (error); } g_simple_async_result_complete (_res); g_object_unref (_res); out: g_object_unref (self); } static gboolean evd_http_connection_write_chunk (EvdHttpConnection *self, const gchar *buffer, gsize size, GError **error) { gchar *chunk_hdr; GError *_error = NULL; gboolean result = TRUE; self->priv->encoding = SOUP_ENCODING_EOF; chunk_hdr = g_strdup_printf ("%x\r\n", (guint) size); result = evd_http_connection_write_content (self, chunk_hdr, strlen (chunk_hdr), TRUE, &_error); if (result && size > 0) result = evd_http_connection_write_content (self, buffer, size, TRUE, _error == NULL ? &_error : NULL); if (result) result = evd_http_connection_write_content (self, "\r\n", 2, TRUE, _error == NULL ? &_error : NULL); self->priv->encoding = SOUP_ENCODING_CHUNKED; g_free (chunk_hdr); if (_error != NULL) g_propagate_error (error, _error); return result; } /* public methods */ EvdHttpConnection * evd_http_connection_new (EvdSocket *socket) { EvdHttpConnection *self; g_return_val_if_fail (EVD_IS_SOCKET (socket), NULL); self = g_object_new (EVD_TYPE_HTTP_CONNECTION, "socket", socket, NULL); return self; } /** * evd_http_connection_read_response_headers: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_http_connection_read_response_headers (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { evd_http_connection_read_headers_async (self, cancellable, callback, user_data, evd_http_connection_read_response_headers); } /** * evd_http_connection_read_response_headers_finish: * @result: The #GAsyncResult object passed to the callback. * @version: (out) (allow-none): * @status_code: (out) (allow-none): * @reason_phrase: (out) (allow-none): * @error: (out) (allow-none): * * Returns: (transfer full) (type Soup.MessageHeaders): **/ SoupMessageHeaders * evd_http_connection_read_response_headers_finish (EvdHttpConnection *self, GAsyncResult *result, SoupHTTPVersion *version, guint *status_code, gchar **reason_phrase, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_http_connection_read_response_headers), FALSE); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { struct EvdHttpConnectionResponseHeaders *response; SoupMessageHeaders *headers = NULL; response = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); headers = response->headers; response->headers = NULL; if (version != NULL) *version = response->version; if (status_code != NULL) *status_code = response->status_code; if (reason_phrase != NULL) { *reason_phrase = response->reason_phrase; response->reason_phrase = NULL; } return headers; } else { return NULL; } } /** * evd_http_connection_read_request_headers: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_http_connection_read_request_headers (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { evd_http_connection_read_headers_async (self, cancellable, callback, user_data, evd_http_connection_read_request_headers); } /** * evd_http_connection_read_request_headers_finish: * @result: The #GAsyncResult object passed to the callback. * @error: (out) (allow-none): * * Returns: (transfer full): **/ EvdHttpRequest * evd_http_connection_read_request_headers_finish (EvdHttpConnection *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_http_connection_read_request_headers), NULL); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); } else { return NULL; } } /** * evd_http_connection_write_response_headers: * @headers: (type Soup.MessageHeaders) (allow-none): * @error: (out) (allow-none): * **/ gboolean evd_http_connection_write_response_headers (EvdHttpConnection *self, SoupHTTPVersion version, guint status_code, const gchar *reason_phrase, SoupMessageHeaders *headers, GError **error) { GOutputStream *stream; gboolean result = TRUE; gchar *st; GString *buf; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); buf = g_string_new (""); if (reason_phrase == NULL) reason_phrase = soup_status_get_phrase (status_code); /* send status line */ st = g_strdup_printf ("HTTP/1.%d %d %s\r\n", version, status_code, reason_phrase); g_string_append_len (buf, st, strlen (st)); g_free (st); /* send headers, if any */ if (headers != NULL) { SoupMessageHeadersIter iter; const gchar *name; const gchar *value; soup_message_headers_iter_init (&iter, headers); while (soup_message_headers_iter_next (&iter, &name, &value)) { st = g_strdup_printf ("%s: %s\r\n", name, value); g_string_append_len (buf, st, strlen (st)); g_free (st); } self->priv->encoding = soup_message_headers_get_encoding (headers); } else { self->priv->encoding = SOUP_ENCODING_EOF; } g_string_append_len (buf, "\r\n", 2); stream = g_io_stream_get_output_stream (G_IO_STREAM (self)); if (g_output_stream_write (stream, buf->str, buf->len, NULL, error) < 0) result = FALSE; g_string_free (buf, TRUE); return result; } gboolean evd_http_connection_write_content (EvdHttpConnection *self, const gchar *buffer, gsize size, gboolean more, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); if (self->priv->encoding == SOUP_ENCODING_CHUNKED) { if (size == 0 || evd_http_connection_write_chunk (self, buffer, size, error)) { if (! more) return evd_http_connection_write_chunk (self, NULL, 0, error); else return TRUE; } else { return FALSE; } } else { GOutputStream *stream; gssize size_written; stream = g_io_stream_get_output_stream (G_IO_STREAM (self)); size_written = g_output_stream_write (stream, buffer, size, NULL, error); if (size_written < 0) { return FALSE; } else if (size_written < size) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_AGAIN, "Resource temporarily unavailable, output buffer full"); return FALSE; } else { return TRUE; } } } /** * evd_http_connection_read_content: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_http_connection_read_content (EvdHttpConnection *self, gchar *buffer, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_http_connection_read_content); if (! g_io_stream_set_pending (G_IO_STREAM (self), &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } if (self->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && self->priv->content_len == 0) { g_simple_async_result_set_op_res_gssize (res, 0); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; evd_http_connection_read_content_block (self, buffer, size); } /** * evd_http_connection_read_content_finish: * @more: (out) (allow-none): * * Returns: **/ gssize evd_http_connection_read_content_finish (EvdHttpConnection *self, GAsyncResult *result, gboolean *more, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), -1); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_http_connection_read_content), -1); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { struct ContentReadData *data; gssize size = 0; gboolean _more = FALSE; data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); if (data != NULL) { size = data->size; _more = data->more; } if (more != NULL) *more = _more; return size; } else { return -1; } } /** * evd_http_connection_read_all_content: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_http_connection_read_all_content (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_http_connection_read_all_content); if (! g_io_stream_set_pending (G_IO_STREAM (self), &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } if (self->priv->encoding == SOUP_ENCODING_NONE || (self->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH && self->priv->content_len == 0) ) { g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->content_read = 0; g_string_set_size (self->priv->buf, 0); self->priv->async_result = res; evd_http_connection_read_next_content_block (self); } /** * evd_http_connection_read_all_content_finish: * @size: (out) (allow-none): * * Returns: (transfer full): **/ gchar * evd_http_connection_read_all_content_finish (EvdHttpConnection *self, GAsyncResult *result, gssize *size, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_http_connection_read_all_content), NULL); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) { gchar *str = NULL; str = self->priv->buf->str; if (size != NULL) *size = self->priv->content_read; g_string_free (self->priv->buf, FALSE); self->priv->buf = g_string_new (""); return str; } else { return NULL; } } gboolean evd_http_connection_unread_request_headers (EvdHttpConnection *self, EvdHttpRequest *request, GError **error) { GInputStream *stream; gboolean result = TRUE; gchar *buf; gsize size; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); g_return_val_if_fail (EVD_IS_HTTP_REQUEST (request), FALSE); buf = evd_http_request_to_string (request, &size); stream = g_io_stream_get_input_stream (G_IO_STREAM (self)); if (evd_buffered_input_stream_unread (EVD_BUFFERED_INPUT_STREAM (stream), buf, size, NULL, error) < 0) result = FALSE; g_free (buf); return result; } /** * evd_http_connection_respond: * @reason_phrase: (allow-none): * @headers: (allow-none): * @content: (allow-none): * **/ gboolean evd_http_connection_respond (EvdHttpConnection *self, SoupHTTPVersion ver, guint status_code, const gchar *reason_phrase, SoupMessageHeaders *headers, const gchar *content, gsize size, gboolean close_after, GError **error) { SoupMessageHeaders *_headers; gboolean result = FALSE; if (headers == NULL) _headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); else _headers = headers; if (close_after || ! self->priv->keepalive) soup_message_headers_replace (_headers, "Connection", "close"); else soup_message_headers_replace (_headers, "Connection", "keep-alive"); soup_message_headers_set_content_length (_headers, size); if (evd_http_connection_write_response_headers (self, ver, status_code, reason_phrase, _headers, error)) { if (content == NULL || evd_http_connection_write_content (self, content, size, FALSE, error)) { result = TRUE; } } if (headers == NULL) soup_message_headers_free (_headers); return result; } /** * evd_http_connection_respond_simple: * @content: (allow-none): * **/ gboolean evd_http_connection_respond_simple (EvdHttpConnection *self, guint status_code, const gchar *content, gsize size) { return evd_http_connection_respond (self, SOUP_HTTP_1_0, status_code, NULL, NULL, content, size, TRUE, NULL); } /** * evd_http_connection_set_current_request: * @request: (allow-none): * **/ void evd_http_connection_set_current_request (EvdHttpConnection *self, EvdHttpRequest *request) { g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); if (request == self->priv->current_request) return; if (self->priv->current_request != NULL) g_object_unref (self->priv->current_request); self->priv->current_request = request; if (self->priv->current_request != NULL) g_object_ref (self->priv->current_request); } /** * evd_http_connection_get_current_request: * * Returns: (transfer none): **/ EvdHttpRequest * evd_http_connection_get_current_request (EvdHttpConnection *self) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), NULL); return self->priv->current_request; } gboolean evd_http_connection_redirect (EvdHttpConnection *self, const gchar *url, gboolean permanently, GError **error) { SoupMessageHeaders *headers; gboolean result; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); g_return_val_if_fail (url != NULL, FALSE); headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); soup_message_headers_replace (headers, "Location", url); result = evd_http_connection_respond (self, SOUP_HTTP_1_1, permanently ? SOUP_STATUS_MOVED_PERMANENTLY : SOUP_STATUS_MOVED_TEMPORARILY, NULL, headers, NULL, 0, TRUE, error); soup_message_headers_free (headers); return result; } /** * evd_http_connection_set_keepalive: * @self: The #EvdHttpConnection * @keepalive: %TRUE or %FALSE * * Manually sets the keepalive flag, overriding the internal state obtained from * HTTP headers. **/ void evd_http_connection_set_keepalive (EvdHttpConnection *self, gboolean keepalive) { g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); self->priv->keepalive = keepalive; } gboolean evd_http_connection_get_keepalive (EvdHttpConnection *self) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); return self->priv->keepalive; } /** * evd_http_connection_write_request_headers: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_http_connection_write_request_headers (EvdHttpConnection *self, EvdHttpRequest *request, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; GOutputStream *stream; gchar *st; gsize size; GError *error = NULL; g_return_if_fail (EVD_IS_HTTP_CONNECTION (self)); g_return_if_fail (EVD_IS_HTTP_REQUEST (request)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_http_connection_write_request_headers); if (! g_io_stream_set_pending (G_IO_STREAM (self), &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; st = evd_http_request_to_string (request, &size); stream = g_io_stream_get_output_stream (G_IO_STREAM (self)); g_object_ref (self); g_output_stream_write_async (stream, st, size, G_PRIORITY_DEFAULT, cancellable, evd_http_connection_on_write_request_headers, self); g_free (st); } gboolean evd_http_connection_write_request_headers_finish (EvdHttpConnection *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_http_connection_write_request_headers), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } EventDance-0.2.0/evd/evd-http-connection.h000066400000000000000000000244761321356073300203640ustar00rootroot00000000000000/* * evd-http-connection.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_HTTP_CONNECTION_H__ #define __EVD_HTTP_CONNECTION_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-connection.h" #include "evd-http-request.h" G_BEGIN_DECLS typedef struct _EvdHttpConnection EvdHttpConnection; typedef struct _EvdHttpConnectionClass EvdHttpConnectionClass; typedef struct _EvdHttpConnectionPrivate EvdHttpConnectionPrivate; struct _EvdHttpConnection { EvdConnection parent; EvdHttpConnectionPrivate *priv; }; struct _EvdHttpConnectionClass { EvdConnectionClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_HTTP_CONNECTION (evd_http_connection_get_type ()) #define EVD_HTTP_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_HTTP_CONNECTION, EvdHttpConnection)) #define EVD_HTTP_CONNECTION_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_HTTP_CONNECTION, EvdHttpConnectionClass)) #define EVD_IS_HTTP_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_HTTP_CONNECTION)) #define EVD_IS_HTTP_CONNECTION_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_HTTP_CONNECTION)) #define EVD_HTTP_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_HTTP_CONNECTION, EvdHttpConnectionClass)) GType evd_http_connection_get_type (void) G_GNUC_CONST; EvdHttpConnection *evd_http_connection_new (EvdSocket *socket); void evd_http_connection_read_response_headers (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); SoupMessageHeaders *evd_http_connection_read_response_headers_finish (EvdHttpConnection *self, GAsyncResult *result, SoupHTTPVersion *version, guint *status_code, gchar **reason_phrase, GError **error); void evd_http_connection_read_request_headers (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); EvdHttpRequest *evd_http_connection_read_request_headers_finish (EvdHttpConnection *self, GAsyncResult *result, GError **error); gboolean evd_http_connection_write_response_headers (EvdHttpConnection *self, SoupHTTPVersion version, guint status_code, const gchar *reason_phrase, SoupMessageHeaders *headers, GError **error); gboolean evd_http_connection_write_content (EvdHttpConnection *self, const gchar *buffer, gsize size, gboolean more, GError **error); void evd_http_connection_read_content (EvdHttpConnection *self, gchar *buffer, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gssize evd_http_connection_read_content_finish (EvdHttpConnection *self, GAsyncResult *result, gboolean *more, GError **error); void evd_http_connection_read_all_content (EvdHttpConnection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar * evd_http_connection_read_all_content_finish (EvdHttpConnection *self, GAsyncResult *result, gssize *size, GError **error); gboolean evd_http_connection_unread_request_headers (EvdHttpConnection *self, EvdHttpRequest *request, GError **error); gboolean evd_http_connection_respond (EvdHttpConnection *self, SoupHTTPVersion ver, guint status_code, const gchar *reason_phrase, SoupMessageHeaders *headers, const gchar *content, gsize size, gboolean close_after, GError **error); gboolean evd_http_connection_respond_simple (EvdHttpConnection *self, guint status_code, const gchar *content, gsize size); void evd_http_connection_set_current_request (EvdHttpConnection *self, EvdHttpRequest *request); EvdHttpRequest *evd_http_connection_get_current_request (EvdHttpConnection *self); gboolean evd_http_connection_redirect (EvdHttpConnection *self, const gchar *url, gboolean permanently, GError **error); void evd_http_connection_set_keepalive (EvdHttpConnection *self, gboolean keepalive); gboolean evd_http_connection_get_keepalive (EvdHttpConnection *self); void evd_http_connection_write_request_headers (EvdHttpConnection *self, EvdHttpRequest *request, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_http_connection_write_request_headers_finish (EvdHttpConnection *self, GAsyncResult *result, GError **error); G_END_DECLS #endif /* __EVD_HTTP_CONNECTION_H__ */ EventDance-0.2.0/evd/evd-http-message.c000066400000000000000000000162631321356073300176370ustar00rootroot00000000000000/* * evd-http-message.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-http-message.h" #include "evd-http-connection.h" #define EVD_HTTP_MESSAGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_HTTP_MESSAGE, \ EvdHttpMessagePrivate)) G_DEFINE_ABSTRACT_TYPE (EvdHttpMessage, evd_http_message, G_TYPE_OBJECT) /* private data */ struct _EvdHttpMessagePrivate { SoupHTTPVersion version; SoupMessageHeaders *headers; }; /* properties */ enum { PROP_0, PROP_VERSION, PROP_HEADERS }; static void evd_http_message_class_init (EvdHttpMessageClass *class); static void evd_http_message_init (EvdHttpMessage *self); static void evd_http_message_finalize (GObject *obj); static void evd_http_message_dispose (GObject *obj); static void evd_http_message_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_http_message_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_http_message_class_init (EvdHttpMessageClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_http_message_dispose; obj_class->finalize = evd_http_message_finalize; obj_class->get_property = evd_http_message_get_property; obj_class->set_property = evd_http_message_set_property; g_object_class_install_property (obj_class, PROP_VERSION, g_param_spec_enum ("version", "HTTP Version", "The HTTP protocol version to use", SOUP_TYPE_HTTP_VERSION, SOUP_HTTP_1_1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_HEADERS, g_param_spec_boxed ("headers", "HTTP message headers", "The HTTP message headers", SOUP_TYPE_MESSAGE_HEADERS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdHttpMessagePrivate)); } static void evd_http_message_init (EvdHttpMessage *self) { EvdHttpMessagePrivate *priv; priv = EVD_HTTP_MESSAGE_GET_PRIVATE (self); self->priv = priv; } static void evd_http_message_dispose (GObject *obj) { G_OBJECT_CLASS (evd_http_message_parent_class)->dispose (obj); } static void evd_http_message_finalize (GObject *obj) { EvdHttpMessage *self = EVD_HTTP_MESSAGE (obj); if (self->priv->headers != NULL) soup_message_headers_free (self->priv->headers); G_OBJECT_CLASS (evd_http_message_parent_class)->finalize (obj); } static void evd_http_message_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdHttpMessage *self; self = EVD_HTTP_MESSAGE (obj); switch (prop_id) { case PROP_VERSION: self->priv->version = g_value_get_enum (value); break; case PROP_HEADERS: if (self->priv->headers != NULL) soup_message_headers_free (self->priv->headers); self->priv->headers = g_value_get_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_http_message_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdHttpMessage *self; self = EVD_HTTP_MESSAGE (obj); switch (prop_id) { case PROP_VERSION: g_value_set_enum (value, self->priv->version); break; case PROP_HEADERS: g_value_set_boxed (value, evd_http_message_get_headers (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } /* public methods */ EvdHttpMessage * evd_http_message_new (SoupHTTPVersion version, SoupMessageHeaders *headers) { EvdHttpMessage *self; self = g_object_new (EVD_TYPE_HTTP_MESSAGE, "version", version, "headers", headers, NULL); return self; } SoupHTTPVersion evd_http_message_get_version (EvdHttpMessage *self) { g_return_val_if_fail (EVD_IS_HTTP_MESSAGE (self), 0); return self->priv->version; } /** * evd_http_message_get_headers: * * Returns: (transfer none): **/ SoupMessageHeaders * evd_http_message_get_headers (EvdHttpMessage *self) { g_return_val_if_fail (EVD_IS_HTTP_MESSAGE (self), NULL); if (self->priv->headers == NULL) self->priv->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST); return self->priv->headers; } /** * evd_http_message_headers_to_string: * @size: (out) (allow-none): * * Returns: (transfer full): **/ gchar * evd_http_message_headers_to_string (EvdHttpMessage *self, gsize *size) { SoupMessageHeaders *headers; SoupMessageHeadersIter iter; const gchar *name; const gchar *value; gchar *st; GString *buf; gchar *result; g_return_val_if_fail (EVD_IS_HTTP_MESSAGE (self), NULL); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); buf = g_string_new (""); soup_message_headers_iter_init (&iter, headers); while (soup_message_headers_iter_next (&iter, &name, &value)) { st = g_strdup_printf ("%s: %s\r\n", name, value); g_string_append_len (buf, st, strlen (st)); g_free (st); } if (size != NULL) *size = buf->len; result = buf->str; g_string_free (buf, FALSE); return result; } EventDance-0.2.0/evd/evd-http-message.h000066400000000000000000000047021321356073300176370ustar00rootroot00000000000000/* * evd-http-message.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_HTTP_MESSAGE_H__ #define __EVD_HTTP_MESSAGE_H__ #include #include #include G_BEGIN_DECLS typedef struct _EvdHttpMessage EvdHttpMessage; typedef struct _EvdHttpMessageClass EvdHttpMessageClass; typedef struct _EvdHttpMessagePrivate EvdHttpMessagePrivate; struct _EvdHttpMessage { GObject parent; EvdHttpMessagePrivate *priv; }; struct _EvdHttpMessageClass { GObjectClass parent_class; }; #define EVD_TYPE_HTTP_MESSAGE (evd_http_message_get_type ()) #define EVD_HTTP_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_HTTP_MESSAGE, EvdHttpMessage)) #define EVD_HTTP_MESSAGE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_HTTP_MESSAGE, EvdHttpMessageClass)) #define EVD_IS_HTTP_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_HTTP_MESSAGE)) #define EVD_IS_HTTP_MESSAGE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_HTTP_MESSAGE)) #define EVD_HTTP_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_HTTP_MESSAGE, EvdHttpMessageClass)) GType evd_http_message_get_type (void) G_GNUC_CONST; EvdHttpMessage *evd_http_message_new (SoupHTTPVersion version, SoupMessageHeaders *headers); SoupHTTPVersion evd_http_message_get_version (EvdHttpMessage *self); SoupMessageHeaders *evd_http_message_get_headers (EvdHttpMessage *self); gchar *evd_http_message_headers_to_string (EvdHttpMessage *self, gsize *size); G_END_DECLS #endif /* __EVD_HTTP_MESSAGE_H__ */ EventDance-0.2.0/evd/evd-http-request.c000066400000000000000000000320261321356073300176760ustar00rootroot00000000000000/* * evd-http-request.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-http-request.h" #define EVD_HTTP_REQUEST_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_HTTP_REQUEST, \ EvdHttpRequestPrivate)) G_DEFINE_TYPE (EvdHttpRequest, evd_http_request, EVD_TYPE_HTTP_MESSAGE) /* private data */ struct _EvdHttpRequestPrivate { gchar *method; SoupURI *uri; }; /* properties */ enum { PROP_0, PROP_METHOD, PROP_PATH, PROP_URI }; static void evd_http_request_class_init (EvdHttpRequestClass *class); static void evd_http_request_init (EvdHttpRequest *self); static void evd_http_request_finalize (GObject *obj); static void evd_http_request_dispose (GObject *obj); static void evd_http_request_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_http_request_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_http_request_class_init (EvdHttpRequestClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_http_request_dispose; obj_class->finalize = evd_http_request_finalize; obj_class->get_property = evd_http_request_get_property; obj_class->set_property = evd_http_request_set_property; g_object_class_install_property (obj_class, PROP_METHOD, g_param_spec_string ("method", "Method", "The HTTP method of the request (GET, POST, HEAD, etc)", SOUP_METHOD_GET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_PATH, g_param_spec_string ("path", "URI path", "The full path portion of the requested URL", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_URI, g_param_spec_boxed ("uri", "Request URI", "The URI of the requested resource", SOUP_TYPE_URI, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdHttpRequestPrivate)); } static void evd_http_request_init (EvdHttpRequest *self) { EvdHttpRequestPrivate *priv; priv = EVD_HTTP_REQUEST_GET_PRIVATE (self); self->priv = priv; } static void evd_http_request_dispose (GObject *obj) { G_OBJECT_CLASS (evd_http_request_parent_class)->dispose (obj); } static void evd_http_request_finalize (GObject *obj) { EvdHttpRequest *self = EVD_HTTP_REQUEST (obj); g_free (self->priv->method); if (self->priv->uri != NULL) soup_uri_free (self->priv->uri); G_OBJECT_CLASS (evd_http_request_parent_class)->finalize (obj); } static void evd_http_request_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdHttpRequest *self; self = EVD_HTTP_REQUEST (obj); switch (prop_id) { case PROP_METHOD: self->priv->method = g_value_dup_string (value); break; case PROP_URI: self->priv->uri = g_value_dup_boxed (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_http_request_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdHttpRequest *self; self = EVD_HTTP_REQUEST (obj); switch (prop_id) { case PROP_METHOD: g_value_set_string (value, self->priv->method); break; case PROP_PATH: g_value_take_string (value, evd_http_request_get_path (self)); break; case PROP_URI: g_value_set_boxed (value, self->priv->uri); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } /* public methods */ EvdHttpRequest * evd_http_request_new (const gchar *method, const gchar *url) { EvdHttpRequest *self; SoupURI *uri; uri = soup_uri_new (url); self = g_object_new (EVD_TYPE_HTTP_REQUEST, "method", method, "uri", uri, NULL); soup_uri_free (uri); return self; } const gchar * evd_http_request_get_method (EvdHttpRequest *self) { g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); return self->priv->method; } gchar * evd_http_request_get_path (EvdHttpRequest *self) { g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); return soup_uri_to_string (self->priv->uri, TRUE); } /** * evd_http_request_get_uri: * * Returns: (transfer none): **/ SoupURI * evd_http_request_get_uri (EvdHttpRequest *self) { g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); return self->priv->uri; } /** * evd_http_request_to_string: * @size: (out): * * Returns: (transfer full): **/ gchar * evd_http_request_to_string (EvdHttpRequest *self, gsize *size) { SoupHTTPVersion version; gchar *headers_st; gsize headers_size; gchar *st; GString *buf; gchar *result; SoupMessageHeaders *headers; gchar *path; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); version = evd_http_message_get_version (EVD_HTTP_MESSAGE (self)); buf = g_string_new (""); /* send status line */ path = evd_http_request_get_path (self); st = g_strdup_printf ("%s %s HTTP/1.%d\r\n", self->priv->method, path, version); g_free (path); g_string_append_len (buf, st, strlen (st)); g_free (st); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); /* determine 'Host' header */ if (soup_message_headers_get_one (headers, "Host") == NULL) { if (self->priv->uri->port == 80) st = g_strdup_printf ("%s", self->priv->uri->host); else st = g_strdup_printf ("%s:%d", self->priv->uri->host, self->priv->uri->port); soup_message_headers_replace (headers, "Host", st); g_free (st); } /* set 'User-Agent' header if not present. (some server won't answer the request if this is not set */ if (soup_message_headers_get_one (headers, "User-Agent") == NULL) soup_message_headers_replace (headers, "User-Agent", "evd"); headers_st = evd_http_message_headers_to_string (EVD_HTTP_MESSAGE (self), &headers_size); g_string_append_len (buf, headers_st, headers_size); g_free (headers_st); g_string_append_len (buf, "\r\n", 2); if (size != NULL) *size = buf->len; result = buf->str; g_string_free (buf, FALSE); return result; } void evd_http_request_set_basic_auth_credentials (EvdHttpRequest *self, const gchar *user, const gchar *passw) { gchar *st; gchar *b64_st; SoupMessageHeaders *headers; g_return_if_fail (EVD_IS_HTTP_REQUEST (self)); if (user == NULL) user = ""; if (passw == NULL) passw = ""; st = g_strdup_printf ("%s:%s", user, passw); b64_st = g_base64_encode ((guchar *) st, strlen (st)); g_free (st); st = g_strdup_printf ("Basic %s", b64_st); g_free (b64_st); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); soup_message_headers_replace (headers, "Authorization", st); g_free (st); } /** * evd_http_request_get_basic_auth_credentials: * @user: (out) (allow-none): * @password: (out) (allow-none): * * Returns: **/ gboolean evd_http_request_get_basic_auth_credentials (EvdHttpRequest *self, gchar **user, gchar **password) { SoupMessageHeaders *headers; const gchar *auth_st; gchar *st; gsize len; gchar **tokens = NULL; guint tokens_len; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), FALSE); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); auth_st = soup_message_headers_get_one (headers, "Authorization"); if (auth_st == NULL || strlen (auth_st) < 7) return FALSE; st = (gchar *) g_base64_decode (auth_st + 6, &len); tokens = g_strsplit (st, ":", 2); g_free (st); tokens_len = g_strv_length (tokens); if (tokens_len > 0 && user != NULL) *user = g_strdup (tokens[0]); if (tokens_len > 1 && password != NULL) *password = g_strdup (tokens[1]); g_strfreev (tokens); return TRUE; } gchar * evd_http_request_get_cookie_value (EvdHttpRequest *self, const gchar *cookie_name) { gchar *value = NULL; SoupMessageHeaders *headers; const gchar *cookie_str; gchar **cookies; gint i; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); g_return_val_if_fail (cookie_name != NULL, NULL); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); cookie_str = soup_message_headers_get_one (headers, "Cookie"); if (cookie_str == NULL) return NULL; cookies = g_strsplit (cookie_str, ";", -1); i = 0; while (cookies[i] != NULL) { gchar *cookie; cookie = g_strstr_len (cookies[i], strlen (cookie_name) + 1, cookie_name); if (cookie == cookies[i] || (cookie == cookies[i] + 1 && cookies[i][0] == ' ')) { gchar **key_value; key_value = g_strsplit (cookie, "=", 2); value = g_strdup (key_value[1]); g_strfreev (key_value); break; } i++; } g_strfreev (cookies); return value; } const gchar * evd_http_request_get_origin (EvdHttpRequest *self) { SoupMessageHeaders *headers; const gchar *origin; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), NULL); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); origin = soup_message_headers_get_one (headers, "Origin"); if (origin == NULL) origin = soup_message_headers_get_one (headers, "Sec-WebSocket-Origin"); return origin; } gboolean evd_http_request_is_cross_origin (EvdHttpRequest *self) { gchar *host; const gchar *origin; gboolean result = FALSE; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), FALSE); origin = evd_http_request_get_origin (self); if (origin == NULL) return FALSE; host = g_strdup_printf ("%s://%s:%d", self->priv->uri->scheme, self->priv->uri->host, self->priv->uri->port); result = (g_strstr_len (host, -1, origin) != host); g_free (host); return result; } gboolean evd_http_request_is_cors_preflight (EvdHttpRequest *self) { SoupMessageHeaders *headers; g_return_val_if_fail (EVD_IS_HTTP_REQUEST (self), FALSE); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (self)); return g_strcmp0 (self->priv->method, SOUP_METHOD_OPTIONS) == 0 && soup_message_headers_get_one (headers, "Origin") != NULL && (soup_message_headers_get_one (headers, "Access-Control-Request-Headers") != NULL || soup_message_headers_get_one (headers, "Access-Control-Request-Method") != NULL); } EventDance-0.2.0/evd/evd-http-request.h000066400000000000000000000070311321356073300177010ustar00rootroot00000000000000/* * evd-http-request.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_HTTP_REQUEST_H__ #define __EVD_HTTP_REQUEST_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-http-message.h" G_BEGIN_DECLS typedef struct _EvdHttpRequest EvdHttpRequest; typedef struct _EvdHttpRequestClass EvdHttpRequestClass; typedef struct _EvdHttpRequestPrivate EvdHttpRequestPrivate; struct _EvdHttpRequest { EvdHttpMessage parent; EvdHttpRequestPrivate *priv; }; struct _EvdHttpRequestClass { EvdHttpMessageClass parent_class; }; #define EVD_TYPE_HTTP_REQUEST (evd_http_request_get_type ()) #define EVD_HTTP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_HTTP_REQUEST, EvdHttpRequest)) #define EVD_HTTP_REQUEST_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_HTTP_REQUEST, EvdHttpRequestClass)) #define EVD_IS_HTTP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_HTTP_REQUEST)) #define EVD_IS_HTTP_REQUEST_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_HTTP_REQUEST)) #define EVD_HTTP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_HTTP_REQUEST, EvdHttpRequestClass)) GType evd_http_request_get_type (void) G_GNUC_CONST; EvdHttpRequest *evd_http_request_new (const gchar *method, const gchar *url); const gchar *evd_http_request_get_method (EvdHttpRequest *self); gchar *evd_http_request_get_path (EvdHttpRequest *self); SoupURI *evd_http_request_get_uri (EvdHttpRequest *self); gchar *evd_http_request_to_string (EvdHttpRequest *self, gsize *size); void evd_http_request_set_basic_auth_credentials (EvdHttpRequest *self, const gchar *user, const gchar *passw); gboolean evd_http_request_get_basic_auth_credentials (EvdHttpRequest *self, gchar **user, gchar **password); gchar *evd_http_request_get_cookie_value (EvdHttpRequest *self, const gchar *cookie_name); const gchar *evd_http_request_get_origin (EvdHttpRequest *self); gboolean evd_http_request_is_cross_origin (EvdHttpRequest *self); gboolean evd_http_request_is_cors_preflight (EvdHttpRequest *self); G_END_DECLS #endif /* __EVD_HTTP_REQUEST_H__ */ EventDance-0.2.0/evd/evd-io-stream-group.c000066400000000000000000000155441321356073300202710ustar00rootroot00000000000000/* * evd-io-stream-group.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-io-stream-group.h" #include "evd-stream-throttle.h" #include "evd-io-stream.h" G_DEFINE_TYPE (EvdIoStreamGroup, evd_io_stream_group, G_TYPE_OBJECT) #define EVD_IO_STREAM_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_IO_STREAM_GROUP, \ EvdIoStreamGroupPrivate)) /* private data */ struct _EvdIoStreamGroupPrivate { EvdStreamThrottle *input_throttle; EvdStreamThrottle *output_throttle; gboolean recursed; }; /* properties */ enum { PROP_0, PROP_INPUT_THROTTLE, PROP_OUTPUT_THROTTLE }; static void evd_io_stream_group_class_init (EvdIoStreamGroupClass *class); static void evd_io_stream_group_init (EvdIoStreamGroup *self); static void evd_io_stream_group_dispose (GObject *obj); static void evd_io_stream_group_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gboolean evd_io_stream_group_add_internal (EvdIoStreamGroup *self, GIOStream *io_stream); static gboolean evd_io_stream_group_remove_internal (EvdIoStreamGroup *self, GIOStream *io_stream); static void evd_io_stream_group_class_init (EvdIoStreamGroupClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_io_stream_group_dispose; obj_class->get_property = evd_io_stream_group_get_property; class->add = evd_io_stream_group_add_internal; class->remove = evd_io_stream_group_remove_internal; g_object_class_install_property (obj_class, PROP_INPUT_THROTTLE, g_param_spec_object ("input-throttle", "Input throttle object", "The input throttle for all connections within the group", EVD_TYPE_STREAM_THROTTLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_OUTPUT_THROTTLE, g_param_spec_object ("output-throttle", "Output throttle object", "The output throttle for all connections within the group", EVD_TYPE_STREAM_THROTTLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdIoStreamGroupPrivate)); } static void evd_io_stream_group_init (EvdIoStreamGroup *self) { EvdIoStreamGroupPrivate *priv; priv = EVD_IO_STREAM_GROUP_GET_PRIVATE (self); self->priv = priv; priv->input_throttle = evd_stream_throttle_new (); priv->output_throttle = evd_stream_throttle_new (); priv->recursed = FALSE; } static void evd_io_stream_group_dispose (GObject *obj) { EvdIoStreamGroup *self = EVD_IO_STREAM_GROUP (obj); if (self->priv->input_throttle != NULL) { g_object_unref (self->priv->input_throttle); self->priv->input_throttle = NULL; } if (self->priv->output_throttle != NULL) { g_object_unref (self->priv->output_throttle); self->priv->output_throttle = NULL; } G_OBJECT_CLASS (evd_io_stream_group_parent_class)->dispose (obj); } static void evd_io_stream_group_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdIoStreamGroup *self; self = EVD_IO_STREAM_GROUP (obj); switch (prop_id) { case PROP_INPUT_THROTTLE: g_value_set_object (value, self->priv->input_throttle); break; case PROP_OUTPUT_THROTTLE: g_value_set_object (value, self->priv->output_throttle); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gboolean evd_io_stream_group_add_internal (EvdIoStreamGroup *self, GIOStream *io_stream) { return evd_io_stream_set_group (EVD_IO_STREAM (io_stream), self); } static gboolean evd_io_stream_group_remove_internal (EvdIoStreamGroup *self, GIOStream *io_stream) { return evd_io_stream_set_group (EVD_IO_STREAM (io_stream), NULL); } /* public methods */ /** * evd_io_stream_group_new: * * Returns: (transfer full): **/ EvdIoStreamGroup * evd_io_stream_group_new (void) { EvdIoStreamGroup *self; self = g_object_new (EVD_TYPE_IO_STREAM_GROUP, NULL); return self; } gboolean evd_io_stream_group_add (EvdIoStreamGroup *self, GIOStream *io_stream) { EvdIoStreamGroupClass *class; gboolean result = TRUE; g_return_val_if_fail (EVD_IS_IO_STREAM_GROUP (self), FALSE); g_return_val_if_fail (EVD_IS_IO_STREAM (io_stream), FALSE); class = EVD_IO_STREAM_GROUP_GET_CLASS (self); if (class->add == NULL) return FALSE; if (! self->priv->recursed) { self->priv->recursed = TRUE; result = class->add (self, io_stream); self->priv->recursed = FALSE; } return result; } /** * evd_io_stream_group_remove: * **/ gboolean evd_io_stream_group_remove (EvdIoStreamGroup *self, GIOStream *io_stream) { EvdIoStreamGroupClass *class; gboolean result = TRUE; g_return_val_if_fail (EVD_IS_IO_STREAM_GROUP (self), FALSE); g_return_val_if_fail (EVD_IS_IO_STREAM (io_stream), FALSE); class = EVD_IO_STREAM_GROUP_GET_CLASS (self); if (class->remove == NULL) return FALSE; if (! self->priv->recursed) { self->priv->recursed = TRUE; result = class->remove (self, io_stream); self->priv->recursed = FALSE; } return result; } EventDance-0.2.0/evd/evd-io-stream-group.h000066400000000000000000000057541321356073300203000ustar00rootroot00000000000000/* * evd-io-stream-group.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_IO_STREAM_GROUP_H__ #define __EVD_IO_STREAM_GROUP_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _EvdIoStreamGroup EvdIoStreamGroup; typedef struct _EvdIoStreamGroupClass EvdIoStreamGroupClass; typedef struct _EvdIoStreamGroupPrivate EvdIoStreamGroupPrivate; struct _EvdIoStreamGroup { GObject parent; EvdIoStreamGroupPrivate *priv; }; struct _EvdIoStreamGroupClass { GObjectClass parent_class; /* virtual methods */ gboolean (* add) (EvdIoStreamGroup *self, GIOStream *io_stream); gboolean (* remove) (EvdIoStreamGroup *self, GIOStream *io_stream); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_IO_STREAM_GROUP (evd_io_stream_group_get_type ()) #define EVD_IO_STREAM_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_IO_STREAM_GROUP, EvdIoStreamGroup)) #define EVD_IO_STREAM_GROUP_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_IO_STREAM_GROUP, EvdIoStreamGroupClass)) #define EVD_IS_IO_STREAM_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_IO_STREAM_GROUP)) #define EVD_IS_IO_STREAM_GROUP_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_IO_STREAM_GROUP)) #define EVD_IO_STREAM_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_IO_STREAM_GROUP, EvdIoStreamGroupClass)) GType evd_io_stream_group_get_type (void) G_GNUC_CONST; EvdIoStreamGroup *evd_io_stream_group_new (void); gboolean evd_io_stream_group_add (EvdIoStreamGroup *self, GIOStream *io_stream); gboolean evd_io_stream_group_remove (EvdIoStreamGroup *self, GIOStream *io_stream); G_END_DECLS #endif /* __EVD_IO_STREAM_GROUP_H__ */ EventDance-0.2.0/evd/evd-io-stream.c000066400000000000000000000252141321356073300171320ustar00rootroot00000000000000/* * evd-io-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-io-stream.h" #include "evd-marshal.h" G_DEFINE_ABSTRACT_TYPE (EvdIoStream, evd_io_stream, G_TYPE_IO_STREAM) #define EVD_IO_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_IO_STREAM, \ EvdIoStreamPrivate)) /* private data */ struct _EvdIoStreamPrivate { EvdStreamThrottle *input_throttle; EvdStreamThrottle *output_throttle; EvdIoStreamGroup *group; }; /* signals */ enum { SIGNAL_GROUP_CHANGED, SIGNAL_CLOSE, SIGNAL_LAST }; static guint evd_io_stream_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_INPUT_THROTTLE, PROP_OUTPUT_THROTTLE, PROP_GROUP }; static void evd_io_stream_class_init (EvdIoStreamClass *class); static void evd_io_stream_init (EvdIoStream *self); static void evd_io_stream_dispose (GObject *obj); static void evd_io_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_io_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gboolean io_stream_on_close (GIOStream *stream, GCancellable *cancellable, GError **error); static void on_group_destroyed (gpointer data, GObject *where_the_object_was); static void evd_io_stream_class_init (EvdIoStreamClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); GIOStreamClass *io_stream_class = G_IO_STREAM_CLASS (class); obj_class->dispose = evd_io_stream_dispose; obj_class->get_property = evd_io_stream_get_property; obj_class->set_property = evd_io_stream_set_property; io_stream_class->close_fn = io_stream_on_close; /* signals */ evd_io_stream_signals[SIGNAL_GROUP_CHANGED] = g_signal_new ("group-changed", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdIoStreamClass, signal_group_changed), NULL, NULL, evd_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); evd_io_stream_signals[SIGNAL_CLOSE] = g_signal_new ("close", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdIoStreamClass, signal_close), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* properties */ g_object_class_install_property (obj_class, PROP_INPUT_THROTTLE, g_param_spec_object ("input-throttle", "Input throttle object", "The stream's input throttle object", EVD_TYPE_STREAM_THROTTLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_OUTPUT_THROTTLE, g_param_spec_object ("output-throttle", "Output throttle object", "The stream's output throttle object", EVD_TYPE_STREAM_THROTTLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_GROUP, g_param_spec_object ("group", "IO stream group", "The group this stream belongs to", EVD_TYPE_IO_STREAM_GROUP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdIoStreamPrivate)); } static void evd_io_stream_init (EvdIoStream *self) { EvdIoStreamPrivate *priv; priv = EVD_IO_STREAM_GET_PRIVATE (self); self->priv = priv; priv->input_throttle = evd_stream_throttle_new (); priv->output_throttle = evd_stream_throttle_new (); priv->group = NULL; } static void evd_io_stream_dispose (GObject *obj) { EvdIoStream *self = EVD_IO_STREAM (obj); if (self->priv->group != NULL) { EvdIoStreamGroup *group; group = self->priv->group; self->priv->group = NULL; g_object_ref (group); evd_io_stream_group_remove (group, G_IO_STREAM (self)); g_object_weak_unref (G_OBJECT (group), on_group_destroyed, self); g_object_unref (group); } if (self->priv->input_throttle != NULL) { g_object_unref (self->priv->input_throttle); self->priv->input_throttle = NULL; } if (self->priv->output_throttle != NULL) { g_object_unref (self->priv->output_throttle); self->priv->output_throttle = NULL; } G_OBJECT_CLASS (evd_io_stream_parent_class)->dispose (obj); } static void evd_io_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdIoStream *self; self = EVD_IO_STREAM (obj); switch (prop_id) { case PROP_GROUP: evd_io_stream_set_group (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_io_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdIoStream *self; self = EVD_IO_STREAM (obj); switch (prop_id) { case PROP_INPUT_THROTTLE: g_value_set_object (value, self->priv->input_throttle); break; case PROP_OUTPUT_THROTTLE: g_value_set_object (value, self->priv->output_throttle); break; case PROP_GROUP: g_value_set_object (value, self->priv->group); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gboolean io_stream_on_close (GIOStream *stream, GCancellable *cancellable, GError **error) { EvdIoStream *self = EVD_IO_STREAM (stream); g_object_ref (self); g_signal_emit (self, evd_io_stream_signals[SIGNAL_CLOSE], 0, NULL); g_object_unref (self); evd_io_stream_set_group (self, NULL); return TRUE; } static void on_group_destroyed (gpointer data, GObject *where_the_object_was) { EvdIoStream *self = EVD_IO_STREAM (data); EvdIoStreamClass *class; g_return_if_fail (where_the_object_was == G_OBJECT (self->priv->group)); self->priv->group = NULL; class = EVD_IO_STREAM_GET_CLASS (self); if (class->group_changed != NULL) class->group_changed (self, NULL, NULL); g_signal_emit (self, evd_io_stream_signals[SIGNAL_GROUP_CHANGED], 0, NULL, NULL, NULL); } /* public methods */ /** * evd_io_stream_get_input_throttle: * * Returns: (transfer none): **/ EvdStreamThrottle * evd_io_stream_get_input_throttle (EvdIoStream *self) { g_return_val_if_fail (EVD_IS_IO_STREAM (self), NULL); return self->priv->input_throttle; } /** * evd_io_stream_get_output_throttle: * * Returns: (transfer none): **/ EvdStreamThrottle * evd_io_stream_get_output_throttle (EvdIoStream *self) { g_return_val_if_fail (EVD_IS_IO_STREAM (self), NULL); return self->priv->output_throttle; } /** * evd_io_stream_set_group: * @group: (allow-none): * **/ gboolean evd_io_stream_set_group (EvdIoStream *self, EvdIoStreamGroup *group) { EvdIoStreamGroup *old_group = NULL; EvdIoStreamClass *class; g_return_val_if_fail (EVD_IS_IO_STREAM (self), FALSE); g_return_val_if_fail (group == NULL || EVD_IS_IO_STREAM_GROUP (group), FALSE); if (group == self->priv->group) return FALSE; g_object_ref (self); if (self->priv->group != NULL) { old_group = self->priv->group; self->priv->group = NULL; g_object_weak_unref (G_OBJECT (old_group), on_group_destroyed, self); evd_io_stream_group_remove (old_group, G_IO_STREAM (self)); } self->priv->group = group; if (group != NULL) { g_object_weak_ref (G_OBJECT (group), on_group_destroyed, self); evd_io_stream_group_add (group, G_IO_STREAM (self)); } class = EVD_IO_STREAM_GET_CLASS (self); if (class->group_changed != NULL) class->group_changed (self, group, old_group); g_signal_emit (self, evd_io_stream_signals[SIGNAL_GROUP_CHANGED], 0, group, old_group, NULL); g_object_unref (self); return TRUE; } /** * evd_io_stream_get_group: * * Returns: (transfer none): **/ EvdIoStreamGroup * evd_io_stream_get_group (EvdIoStream *self) { g_return_val_if_fail (EVD_IS_IO_STREAM (self), NULL); return self->priv->group; } EventDance-0.2.0/evd/evd-io-stream.h000066400000000000000000000060701321356073300171360ustar00rootroot00000000000000/* * evd-io-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_IO_STREAM_H__ #define __EVD_IO_STREAM_H__ #include #include #include "evd-stream-throttle.h" #include "evd-io-stream-group.h" G_BEGIN_DECLS typedef struct _EvdIoStream EvdIoStream; typedef struct _EvdIoStreamClass EvdIoStreamClass; typedef struct _EvdIoStreamPrivate EvdIoStreamPrivate; struct _EvdIoStream { GIOStream parent; EvdIoStreamPrivate *priv; }; struct _EvdIoStreamClass { GIOStreamClass parent_class; /* virtual methods */ void (* group_changed) (EvdIoStream *self, EvdIoStreamGroup *new_group, EvdIoStreamGroup *old_group); /* signals */ void (* signal_group_changed) (EvdIoStream *self, EvdIoStreamGroup *new_group, EvdIoStreamGroup *old_group, gpointer user_data); void (* signal_close) (EvdIoStream *self, gpointer user_data); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); }; #define EVD_TYPE_IO_STREAM (evd_io_stream_get_type ()) #define EVD_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_IO_STREAM, EvdIoStream)) #define EVD_IO_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_IO_STREAM, EvdIoStreamClass)) #define EVD_IS_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_IO_STREAM)) #define EVD_IS_IO_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_IO_STREAM)) #define EVD_IO_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_IO_STREAM, EvdIoStreamClass)) GType evd_io_stream_get_type (void) G_GNUC_CONST; EvdStreamThrottle * evd_io_stream_get_input_throttle (EvdIoStream *self); EvdStreamThrottle * evd_io_stream_get_output_throttle (EvdIoStream *self); gboolean evd_io_stream_set_group (EvdIoStream *self, EvdIoStreamGroup *group); EvdIoStreamGroup * evd_io_stream_get_group (EvdIoStream *self); G_END_DECLS #endif /* __EVD_IO_STREAM_H__ */ EventDance-0.2.0/evd/evd-ipc-mechanism.c000066400000000000000000000141401321356073300177430ustar00rootroot00000000000000/* * evd-ipc-mechanism.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-ipc-mechanism.h" G_DEFINE_ABSTRACT_TYPE (EvdIpcMechanism, evd_ipc_mechanism, G_TYPE_OBJECT) #define EVD_IPC_MECHANISM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_IPC_MECHANISM, \ EvdIpcMechanismPrivate)) struct _EvdIpcMechanismPrivate { GList *transports; }; static void evd_ipc_mechanism_class_init (EvdIpcMechanismClass *class); static void evd_ipc_mechanism_init (EvdIpcMechanism *self); static void evd_ipc_mechanism_finalize (GObject *obj); static void transport_on_new_peer (EvdTransport *transport, EvdPeer *peer, gpointer user_data); static void transport_on_receive (EvdTransport *transport, EvdPeer *peer, gpointer user_data); static void transport_on_destroyed (gpointer user_data, GObject *where_the_object_was); static void evd_ipc_mechanism_class_init (EvdIpcMechanismClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_ipc_mechanism_finalize; g_type_class_add_private (obj_class, sizeof (EvdIpcMechanismPrivate)); } static void evd_ipc_mechanism_init (EvdIpcMechanism *self) { EvdIpcMechanismPrivate *priv; priv = EVD_IPC_MECHANISM_GET_PRIVATE (self); self->priv = priv; priv->transports = NULL; } static void evd_ipc_mechanism_finalize (GObject *obj) { EvdIpcMechanism *self = EVD_IPC_MECHANISM (obj); GList *node; node = self->priv->transports; while (node != NULL) { if (EVD_IS_TRANSPORT (node->data)) { EvdTransport *transport; transport = EVD_TRANSPORT (node->data); g_signal_handlers_disconnect_by_func (transport, transport_on_new_peer, self); g_signal_handlers_disconnect_by_func (transport, transport_on_receive, self); g_object_weak_unref (G_OBJECT (transport), transport_on_destroyed, self); } node = node->next; } g_list_free (self->priv->transports); G_OBJECT_CLASS (evd_ipc_mechanism_parent_class)->finalize (obj); } static void transport_on_new_peer (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { EvdIpcMechanism *self = EVD_IPC_MECHANISM (user_data); EvdIpcMechanismClass *class; class = EVD_IPC_MECHANISM_GET_CLASS (self); if (class->transport_new_peer != NULL) class->transport_new_peer (self, transport, peer); } static void transport_on_receive (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { EvdIpcMechanism *self = EVD_IPC_MECHANISM (user_data); EvdIpcMechanismClass *class; class = EVD_IPC_MECHANISM_GET_CLASS (self); if (class->transport_receive != NULL) { const gchar *data; gsize size; data = evd_transport_receive (transport, peer, &size); class->transport_receive (self, transport, peer, (const guchar *) data, size); } } static void transport_on_destroyed (gpointer user_data, GObject *where_the_object_was) { EvdIpcMechanism *self = EVD_IPC_MECHANISM (user_data); self->priv->transports = g_list_remove (self->priv->transports, where_the_object_was); } /* public methods */ void evd_ipc_mechanism_use_transport (EvdIpcMechanism *self, EvdTransport *transport) { g_return_if_fail (EVD_IS_IPC_MECHANISM (self)); g_return_if_fail (EVD_IS_TRANSPORT (transport)); if (g_list_find (self->priv->transports, transport) != NULL) return; self->priv->transports = g_list_append (self->priv->transports, transport); g_signal_connect (transport, "new-peer", G_CALLBACK (transport_on_new_peer), self); g_signal_connect (transport, "receive", G_CALLBACK (transport_on_receive), self); g_object_weak_ref (G_OBJECT (transport), transport_on_destroyed, self); } void evd_ipc_mechanism_unuse_transport (EvdIpcMechanism *self, EvdTransport *transport) { g_return_if_fail (EVD_IS_IPC_MECHANISM (self)); g_return_if_fail (EVD_IS_TRANSPORT (transport)); if (g_list_find (self->priv->transports, transport) == NULL) return; g_signal_handlers_disconnect_by_func (transport, transport_on_new_peer, self); g_signal_handlers_disconnect_by_func (transport, transport_on_receive, self); self->priv->transports = g_list_remove (self->priv->transports, transport); g_object_weak_unref (G_OBJECT (transport), transport_on_destroyed, self); } EventDance-0.2.0/evd/evd-ipc-mechanism.h000066400000000000000000000062241321356073300177540ustar00rootroot00000000000000/* * evd-ipc-mechanism.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_IPC_MECHANISM_H__ #define __EVD_IPC_MECHANISM_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-transport.h" G_BEGIN_DECLS typedef struct _EvdIpcMechanism EvdIpcMechanism; typedef struct _EvdIpcMechanismClass EvdIpcMechanismClass; typedef struct _EvdIpcMechanismPrivate EvdIpcMechanismPrivate; struct _EvdIpcMechanism { GObject parent; EvdIpcMechanismPrivate *priv; }; struct _EvdIpcMechanismClass { GObjectClass parent_class; /* virtual/abstract methods */ void (* transport_receive) (EvdIpcMechanism *self, EvdTransport *transport, EvdPeer *peer, const guchar *data, gsize size); void (* transport_new_peer) (EvdIpcMechanism *self, EvdTransport *transport, EvdPeer *peer); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_IPC_MECHANISM (evd_ipc_mechanism_get_type ()) #define EVD_IPC_MECHANISM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_IPC_MECHANISM, EvdIpcMechanism)) #define EVD_IPC_MECHANISM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_IPC_MECHANISM, EvdIpcMechanismClass)) #define EVD_IS_IPC_MECHANISM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_IPC_MECHANISM)) #define EVD_IS_IPC_MECHANISM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_IPC_MECHANISM)) #define EVD_IPC_MECHANISM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_IPC_MECHANISM, EvdIpcMechanismClass)) GType evd_ipc_mechanism_get_type (void) G_GNUC_CONST; void evd_ipc_mechanism_use_transport (EvdIpcMechanism *self, EvdTransport *transport); void evd_ipc_mechanism_unuse_transport (EvdIpcMechanism *self, EvdTransport *transport); G_END_DECLS #endif /* __EVD_IPC_MECHANISM_H__ */ EventDance-0.2.0/evd/evd-json-filter.c000066400000000000000000000435741321356073300174770ustar00rootroot00000000000000/* * evd-json-filter.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-error.h" #include "evd-json-filter.h" #include "evd-marshal.h" G_DEFINE_TYPE (EvdJsonFilter, evd_json_filter, G_TYPE_OBJECT) #define EVD_JSON_FILTER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_JSON_FILTER, \ EvdJsonFilterPrivate)) #define MAX_DEPTH 128 /* * Code pieces taken from http://www.json.org/JSON_checker/. */ #define __ -1 /* the universal error code */ /* Characters are mapped into these 31 character classes. This allows for a significant reduction in the size of the state transition table. */ enum classes { C_SPACE, /* space */ C_WHITE, /* other whitespace */ C_LCURB, /* { */ C_RCURB, /* } */ C_LSQRB, /* [ */ C_RSQRB, /* ] */ C_COLON, /* : */ C_COMMA, /* , */ C_QUOTE, /* " */ C_BACKS, /* \ */ C_SLASH, /* / */ C_PLUS, /* + */ C_MINUS, /* - */ C_POINT, /* . */ C_ZERO , /* 0 */ C_DIGIT, /* 123456789 */ C_LOW_A, /* a */ C_LOW_B, /* b */ C_LOW_C, /* c */ C_LOW_D, /* d */ C_LOW_E, /* e */ C_LOW_F, /* f */ C_LOW_L, /* l */ C_LOW_N, /* n */ C_LOW_R, /* r */ C_LOW_S, /* s */ C_LOW_T, /* t */ C_LOW_U, /* u */ C_ABCDF, /* ABCDF */ C_E, /* E */ C_ETC, /* everything else */ NR_CLASSES }; static gint ascii_class[128] = { /* This array maps the 128 ASCII characters into character classes. The remaining Unicode characters should be mapped to C_ETC. Non-whitespace control characters are errors. */ __, __, __, __, __, __, __, __, __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC }; /* The state codes. */ enum states { GO, /* start */ OK, /* ok */ OB, /* object */ KE, /* key */ CO, /* colon */ VA, /* value */ AR, /* array */ ST, /* string */ ES, /* escape */ U1, /* u1 */ U2, /* u2 */ U3, /* u3 */ U4, /* u4 */ MI, /* minus */ ZE, /* zero */ IN, /* integer */ FR, /* fraction */ E1, /* e */ E2, /* ex */ E3, /* exp */ T1, /* tr */ T2, /* tru */ T3, /* true */ F1, /* fa */ F2, /* fal */ F3, /* fals */ F4, /* false */ N1, /* nu */ N2, /* nul */ N3, /* null */ NR_STATES }; static int state_transition_table[NR_STATES][NR_CLASSES] = { /* The state transition table takes the current state and the current symbol, and returns either a new state or an action. An action is represented as a negative number. A JSON text is accepted if at the end of the text the state is OK and if the mode is MODE_DONE. white 1-9 ABCDF etc space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E |*/ /*start GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*object OB*/ {OB,OB,__,-9,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*key KE*/ {KE,KE,__,__,__,__,__,__,ST,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*value VA*/ {VA,VA,-6,__,-5,__,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, /*array AR*/ {AR,AR,-6,__,-5,-7,__,__,ST,__,__,__,MI,__,ZE,IN,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__}, /*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,ES,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST}, /*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__}, /*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__}, /*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__}, /*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__}, /*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ST,ST,ST,ST,ST,ST,ST,ST,__,__,__,__,__,__,ST,ST,__}, /*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IN,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*int IN*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,FR,IN,IN,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, /*frac FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__}, /*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__}, /*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__}, /*true T3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, /*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__}, /*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__}, /*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__}, /*false F4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__}, /*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__}, /*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__}, /*null N3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__}, }; /* These modes can be pushed on the stack. */ enum modes { MODE_ARRAY, MODE_DONE, MODE_KEY, MODE_OBJECT, }; /* private data */ struct _EvdJsonFilterPrivate { gint state; gint depth; gint top; gint* stack; gint content_start; GString *cache; EvdJsonFilterOnPacketHandler packet_cb; gpointer user_data; GDestroyNotify user_data_free_func; }; static void evd_json_filter_class_init (EvdJsonFilterClass *class); static void evd_json_filter_init (EvdJsonFilter *self); static void evd_json_filter_finalize (GObject *obj); static void evd_json_filter_class_init (EvdJsonFilterClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_json_filter_finalize; /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdJsonFilterPrivate)); } static void evd_json_filter_init (EvdJsonFilter *self) { EvdJsonFilterPrivate *priv; priv = EVD_JSON_FILTER_GET_PRIVATE (self); self->priv = priv; /* initialize private members */ priv->stack = g_new0 (gint, MAX_DEPTH); priv->cache = g_string_new (""); evd_json_filter_reset (self); priv->packet_cb = NULL; priv->user_data = NULL; } static void evd_json_filter_finalize (GObject *obj) { EvdJsonFilter *self = EVD_JSON_FILTER (obj); g_free (self->priv->stack); g_string_free (self->priv->cache, TRUE); if (self->priv->user_data != NULL && self->priv->user_data_free_func != NULL) { self->priv->user_data_free_func (self->priv->user_data); } G_OBJECT_CLASS (evd_json_filter_parent_class)->finalize (obj); } static gboolean evd_json_filter_push (EvdJsonFilter *self, gint mode) { /* Push a mode onto the stack. Return false if there is overflow. */ self->priv->top += 1; if (self->priv->top >= self->priv->depth) return FALSE; self->priv->stack[self->priv->top] = mode; return TRUE; } static gboolean evd_json_filter_pop (EvdJsonFilter *self, gint mode) { /* Pop the stack, assuring that the current mode matches the expectation. Return false if there is underflow or if the modes mismatch. */ if ( (self->priv->top < 0) || (self->priv->stack[self->priv->top] != mode) ) return FALSE; self->priv->top -= 1; return TRUE; } static gboolean evd_json_filter_error (EvdJsonFilter *self) { evd_json_filter_reset (self); return FALSE; } static gboolean evd_json_filter_process (EvdJsonFilter *self, gint next_char, gsize offset) { /* * After calling new_JSON_checker, call this function for each character (or * partial character) in your JSON text. It can accept UTF-8, UTF-16, or * UTF-32. It returns true if things are looking ok so far. If it rejects the * text, it deletes the JSON_checker object and returns false. */ gint next_class, next_state; /* Determine the character's class. */ if (next_char < 0) return evd_json_filter_error (self); if (next_char >= 128) { next_class = C_ETC; } else { next_class = ascii_class[next_char]; if (next_class <= __) return evd_json_filter_error (self); } /* Get the next state from the state transition table. */ next_state = state_transition_table[self->priv->state][next_class]; if (next_state >= 0) { /* Change the state. */ self->priv->state = next_state; } else { if (self->priv->content_start == -1) self->priv->content_start = offset; /* Or perform one of the actions. */ switch (next_state) { /* empty } */ case -9: if (! evd_json_filter_pop (self, MODE_KEY)) return evd_json_filter_error (self); else self->priv->state = OK; break; /* } */ case -8: if (! evd_json_filter_pop (self, MODE_OBJECT)) return evd_json_filter_error (self); else self->priv->state = OK; break; /* ] */ case -7: if (! evd_json_filter_pop (self, MODE_ARRAY)) return evd_json_filter_error (self); else self->priv->state = OK; break; /* { */ case -6: if (! evd_json_filter_push (self, MODE_KEY)) return evd_json_filter_error(self); else self->priv->state = OB; break; /* [ */ case -5: if (! evd_json_filter_push (self, MODE_ARRAY)) return evd_json_filter_error (self); else self->priv->state = AR; break; /* " */ case -4: switch (self->priv->stack[self->priv->top]) { case MODE_KEY: self->priv->state = CO; break; case MODE_ARRAY: case MODE_OBJECT: self->priv->state = OK; break; default: return evd_json_filter_error (self); } break; /* , */ case -3: switch (self->priv->stack[self->priv->top]) { case MODE_OBJECT: /* A comma causes a flip from object mode to key mode. */ if (! evd_json_filter_pop (self, MODE_OBJECT) || ! evd_json_filter_push (self, MODE_KEY)) { return evd_json_filter_error (self); } else { self->priv->state = KE; } break; case MODE_ARRAY: self->priv->state = VA; break; default: return evd_json_filter_error (self); } break; /* : */ case -2: /* A colon causes a flip from key mode to object mode. */ if (! evd_json_filter_pop (self, MODE_KEY) || ! evd_json_filter_push (self, MODE_OBJECT)) { return evd_json_filter_error (self); } else { self->priv->state = VA; } break; /* Bad action. */ default: return evd_json_filter_error (self); } } return TRUE; } static void evd_json_filter_notify_packet (EvdJsonFilter *self, const gchar *buffer, gsize size) { if (self->priv->packet_cb != NULL) self->priv->packet_cb (self, buffer, size, self->priv->user_data); } /* public methods */ EvdJsonFilter * evd_json_filter_new (void) { EvdJsonFilter *self; self = g_object_new (EVD_TYPE_JSON_FILTER, NULL); return self; } void evd_json_filter_reset (EvdJsonFilter *self) { g_return_if_fail (EVD_IS_JSON_FILTER (self)); self->priv->state = GO; self->priv->depth = MAX_DEPTH; self->priv->top = -1; self->priv->content_start = -1; evd_json_filter_push (self, MODE_DONE); } gboolean evd_json_filter_feed_len (EvdJsonFilter *self, const gchar *buffer, gsize size, GError **error) { gint i; g_return_val_if_fail (EVD_IS_JSON_FILTER (self), FALSE); g_return_val_if_fail (buffer != NULL, FALSE); i = 0; while (i < size) { if (! evd_json_filter_process (self, (gint) buffer[i], i)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Malformed JSON sequence at offset %d", i); return FALSE; } else { if ( (self->priv->content_start >= 0) && self->priv->stack[self->priv->top] == MODE_DONE) { if (self->priv->cache->len > 0) { g_string_append_len (self->priv->cache, buffer, i+1); evd_json_filter_notify_packet (self, self->priv->cache->str, self->priv->cache->len); g_string_free (self->priv->cache, TRUE); self->priv->cache = g_string_new (""); } else { evd_json_filter_notify_packet (self, (gchar *) ( (void *) buffer + self->priv->content_start), i - self->priv->content_start + 1); } evd_json_filter_reset (self); } i++; } } if (self->priv->content_start >= 0) { g_string_append_len (self->priv->cache, (gchar *) ( (void *) (buffer) + self->priv->content_start), size - self->priv->content_start); self->priv->content_start = 0; } return TRUE; } gboolean evd_json_filter_feed (EvdJsonFilter *self, const gchar *buffer, GError **error) { return evd_json_filter_feed_len (self, buffer, strlen (buffer), error); } void evd_json_filter_set_packet_handler (EvdJsonFilter *self, EvdJsonFilterOnPacketHandler callback, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_JSON_FILTER (self)); if (self->priv->packet_cb != NULL && self->priv->user_data != NULL && self->priv->user_data_free_func != NULL) { self->priv->user_data_free_func (self->priv->user_data); } self->priv->packet_cb = callback; self->priv->user_data = user_data; self->priv->user_data_free_func = user_data_free_func; } EventDance-0.2.0/evd/evd-json-filter.h000066400000000000000000000064011321356073300174700ustar00rootroot00000000000000/* * evd-json-filter.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_JSON_FILTER_H__ #define __EVD_JSON_FILTER_H__ #include G_BEGIN_DECLS typedef struct _EvdJsonFilter EvdJsonFilter; typedef struct _EvdJsonFilterClass EvdJsonFilterClass; typedef struct _EvdJsonFilterPrivate EvdJsonFilterPrivate; typedef void (* EvdJsonFilterOnPacketHandler) (EvdJsonFilter *self, const gchar *buffer, gsize size, gpointer user_data); struct _EvdJsonFilter { GObject parent; EvdJsonFilterPrivate *priv; }; struct _EvdJsonFilterClass { GObjectClass parent_class; }; #define EVD_TYPE_JSON_FILTER (evd_json_filter_get_type ()) #define EVD_JSON_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_JSON_FILTER, EvdJsonFilter)) #define EVD_JSON_FILTER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_JSON_FILTER, EvdJsonFilterClass)) #define EVD_IS_JSON_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_JSON_FILTER)) #define EVD_IS_JSON_FILTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_JSON_FILTER)) #define EVD_JSON_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_JSON_FILTER, EvdJsonFilterClass)) GType evd_json_filter_get_type (void) G_GNUC_CONST; EvdJsonFilter *evd_json_filter_new (void); void evd_json_filter_reset (EvdJsonFilter *self); gboolean evd_json_filter_feed_len (EvdJsonFilter *self, const gchar *buffer, gsize size, GError **error); gboolean evd_json_filter_feed (EvdJsonFilter *self, const gchar *buffer, GError **error); void evd_json_filter_set_packet_handler (EvdJsonFilter *self, EvdJsonFilterOnPacketHandler handler, gpointer user_data, GDestroyNotify user_data_free_func); G_END_DECLS #endif /* __EVD_JSON_FILTER_H__ */ EventDance-0.2.0/evd/evd-jsonrpc-http-client.c000066400000000000000000000466101321356073300211440ustar00rootroot00000000000000/* * evd-jsonrpc-http-client.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-jsonrpc-http-client.h" #include #include G_DEFINE_TYPE (EvdJsonrpcHttpClient, evd_jsonrpc_http_client, EVD_TYPE_CONNECTION_POOL) #define EVD_JSONRPC_HTTP_CLIENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_JSONRPC_HTTP_CLIENT, \ EvdJsonrpcHttpClientPrivate)) /* private data */ struct _EvdJsonrpcHttpClientPrivate { gchar *url; EvdJsonrpc *rpc; EvdHttpRequest *http_request; }; typedef struct { EvdJsonrpcHttpClient *self; gchar *buf; gpointer context; guint invocation_id; GCancellable *cancellable; JsonNode *json_result; JsonNode *json_error; } CallData; /* properties */ enum { PROP_0, PROP_URL, PROP_HTTP_REQUEST }; static void evd_jsonrpc_http_client_class_init (EvdJsonrpcHttpClientClass *class); static void evd_jsonrpc_http_client_init (EvdJsonrpcHttpClient *self); static void evd_jsonrpc_http_client_constructed (GObject *obj); static void evd_jsonrpc_http_client_finalize (GObject *obj); static void evd_jsonrpc_http_client_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_jsonrpc_http_client_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static GType get_connection_type (EvdConnectionPool *conn_pool); static void jsonrpc_on_send (EvdJsonrpc *rpc, const gchar *buffer, gpointer user_context, guint invocation_id, gpointer user_data); static void evd_jsonrpc_http_client_class_init (EvdJsonrpcHttpClientClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdConnectionPoolClass *conn_pool_class = EVD_CONNECTION_POOL_CLASS (class); obj_class->constructed = evd_jsonrpc_http_client_constructed; obj_class->finalize = evd_jsonrpc_http_client_finalize; obj_class->get_property = evd_jsonrpc_http_client_get_property; obj_class->set_property = evd_jsonrpc_http_client_set_property; conn_pool_class->get_connection_type = get_connection_type; g_object_class_install_property (obj_class, PROP_URL, g_param_spec_string ("url", "URL", "The target server URL", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_HTTP_REQUEST, g_param_spec_object ("http-request", "HTTP request", "The object's internal HTTP request object", EVD_TYPE_HTTP_REQUEST, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdJsonrpcHttpClientPrivate)); } static void evd_jsonrpc_http_client_init (EvdJsonrpcHttpClient *self) { EvdJsonrpcHttpClientPrivate *priv; priv = EVD_JSONRPC_HTTP_CLIENT_GET_PRIVATE (self); self->priv = priv; priv->rpc = evd_jsonrpc_new (); evd_jsonrpc_transport_set_send_callback (priv->rpc, jsonrpc_on_send, self, (GDestroyNotify) g_object_unref); g_object_ref (self); priv->http_request = NULL; } static void evd_jsonrpc_http_client_constructed (GObject *obj) { EvdJsonrpcHttpClient *self = EVD_JSONRPC_HTTP_CLIENT (obj); SoupURI *uri; gchar *sock_addr; self->priv->http_request = evd_http_request_new (SOUP_METHOD_POST, self->priv->url); uri = evd_http_request_get_uri (self->priv->http_request); sock_addr = g_strdup_printf ("%s:%u", uri->host, uri->port); g_object_set (self, "address", sock_addr, NULL); g_free (sock_addr); G_OBJECT_CLASS (evd_jsonrpc_http_client_parent_class)->constructed (obj); } static void evd_jsonrpc_http_client_finalize (GObject *obj) { EvdJsonrpcHttpClient *self = EVD_JSONRPC_HTTP_CLIENT (obj); g_free (self->priv->url); g_object_unref (self->priv->rpc); g_object_unref (self->priv->http_request); G_OBJECT_CLASS (evd_jsonrpc_http_client_parent_class)->finalize (obj); } static void evd_jsonrpc_http_client_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdJsonrpcHttpClient *self; self = EVD_JSONRPC_HTTP_CLIENT (obj); switch (prop_id) { case PROP_URL: { self->priv->url = g_value_dup_string (value); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_jsonrpc_http_client_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdJsonrpcHttpClient *self; self = EVD_JSONRPC_HTTP_CLIENT (obj); switch (prop_id) { case PROP_URL: g_value_set_string (value, self->priv->url); break; case PROP_HTTP_REQUEST: g_value_set_object (value, self->priv->http_request); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static GType get_connection_type (EvdConnectionPool *conn_pool) { return EVD_TYPE_HTTP_CONNECTION; } static void free_call_data (gpointer _data) { CallData *data = _data; g_object_unref (data->self); g_free (data->buf); if (data->json_result != NULL) json_node_free (data->json_result); if (data->json_error != NULL) json_node_free (data->json_error); g_slice_free (CallData, data); } static void on_content_read (GObject *obj, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); GError *error = NULL; gchar *content; gssize size; CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); content = evd_http_connection_read_all_content_finish (conn, result, &size, &error); /* recycle connection if keep-alive */ if (evd_http_connection_get_keepalive (conn)) evd_connection_pool_recycle (EVD_CONNECTION_POOL (data->self), EVD_CONNECTION (conn)); if (content == NULL) { /* notify JSON-RPC of transport error */ evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); } else { if (! evd_jsonrpc_transport_receive (data->self->priv->rpc, content, res, data->invocation_id, NULL)) { /* Server responded with invalid JSON-RPC data. EvdJsonrpc already handles the transport error internally, within transport_receive(). */ } g_free (content); } } static void on_response_headers (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); GError *error = NULL; guint status_code; gchar *reason; SoupMessageHeaders *headers; GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); headers = evd_http_connection_read_response_headers_finish (conn, result, NULL, &status_code, &reason, &error); if (headers == NULL) { /* notify JSON-RPC of transport error */ evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); } else { if (status_code == SOUP_STATUS_OK) { evd_http_connection_read_all_content (conn, NULL, on_content_read, user_data); } else { /* notify JSON-RPC of transport error */ g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "HTTP error from server: %u %s", status_code, reason); evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); } soup_message_headers_free (headers); g_free (reason); } evd_connection_unlock_close (EVD_CONNECTION (conn)); } static void on_request_sent (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); GError *error = NULL; GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); if (! evd_http_connection_write_request_headers_finish (conn, result, &error)) { /* notify JSON-RPC of transport error */ evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); return; } /* write content */ if (! evd_http_connection_write_content (conn, data->buf, strlen (data->buf), FALSE, &error)) { /* notify JSON-RPC of transport error */ evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); } else { evd_http_connection_read_response_headers (conn, data->cancellable, on_response_headers, res); } } static void do_request (EvdHttpConnection *conn, gpointer user_data) { SoupMessageHeaders *headers; GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (data->self->priv->http_request)); soup_message_headers_set_content_length (headers, strlen (data->buf)); evd_connection_lock_close (EVD_CONNECTION (conn)); evd_http_connection_write_request_headers (conn, data->self->priv->http_request, NULL, on_request_sent, res); } static void on_connection (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdHttpConnection *conn; GError *error = NULL; conn = EVD_HTTP_CONNECTION (evd_connection_pool_get_connection_finish (EVD_CONNECTION_POOL (obj), result, &error)); if (conn == NULL) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); /* notify JSON-RPC of transport error */ evd_jsonrpc_transport_error (data->self->priv->rpc, data->invocation_id, error); g_error_free (error); } else { do_request (conn, user_data); g_object_unref (conn); } } static void jsonrpc_on_send (EvdJsonrpc *rpc, const gchar *buffer, gpointer user_context, guint invocation_id, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_context); CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); data->buf = g_strdup (buffer); data->invocation_id = invocation_id; evd_connection_pool_get_connection (EVD_CONNECTION_POOL (data->self), data->cancellable, on_connection, user_context); } static void jsonrpc_on_method_call_result (GObject *obj, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); CallData *data; GError *error = NULL; data = g_simple_async_result_get_op_res_gpointer (res); if (! evd_jsonrpc_call_method_finish (EVD_JSONRPC (obj), result, &data->json_result, &data->json_error, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_simple_async_result_complete (res); g_object_unref (res); } /* public methods */ /** * evd_jsonrpc_http_client_new: * * Returns: (transfer full): **/ EvdJsonrpcHttpClient * evd_jsonrpc_http_client_new (const gchar *url) { EvdJsonrpcHttpClient *self; self = g_object_new (EVD_TYPE_JSONRPC_HTTP_CLIENT, "url", url, NULL); return self; } /** * evd_jsonrpc_http_client_get_http_request: * * Returns: (transfer none): **/ EvdHttpRequest * evd_jsonrpc_http_client_get_http_request (EvdJsonrpcHttpClient *self) { g_return_val_if_fail (EVD_IS_JSONRPC_HTTP_CLIENT (self), NULL); return self->priv->http_request; } /** * evd_jsonrpc_http_client_call_method: * @params: (allow-none): * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_jsonrpc_http_client_call_method (EvdJsonrpcHttpClient *self, const gchar *method, JsonNode *params, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CallData *data; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_JSONRPC_HTTP_CLIENT (self)); g_return_if_fail (method != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_jsonrpc_http_client_call_method); data = g_slice_new0 (CallData); data->cancellable = cancellable; data->self = self; g_object_ref (self); g_simple_async_result_set_op_res_gpointer (res, data, free_call_data); evd_jsonrpc_call_method (self->priv->rpc, method, params, res, cancellable, jsonrpc_on_method_call_result, res); } /** * evd_jsonrpc_http_client_call_method_finish: * @json_result: (out) (allow-none): * @json_error: (out) (allow-none): * @error: (allow-none): **/ gboolean evd_jsonrpc_http_client_call_method_finish (EvdJsonrpcHttpClient *self, GAsyncResult *result, JsonNode **json_result, JsonNode **json_error, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); g_return_val_if_fail (EVD_IS_JSONRPC_HTTP_CLIENT (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_jsonrpc_http_client_call_method), FALSE); if (! g_simple_async_result_propagate_error (res, error)) { CallData *data; data = g_simple_async_result_get_op_res_gpointer (res); if (json_result != NULL) { *json_result = data->json_result; data->json_result = NULL; } if (json_error != NULL) { *json_error = data->json_error; data->json_error = NULL; } return TRUE; } else { return FALSE; } } EventDance-0.2.0/evd/evd-jsonrpc-http-client.h000066400000000000000000000075701321356073300211530ustar00rootroot00000000000000/* * evd-jsonrpc-http-client.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_JSONRPC_HTTP_CLIENT_H__ #define __EVD_JSONRPC_HTTP_CLIENT_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-connection-pool.h" #include "evd-http-request.h" G_BEGIN_DECLS typedef struct _EvdJsonrpcHttpClient EvdJsonrpcHttpClient; typedef struct _EvdJsonrpcHttpClientClass EvdJsonrpcHttpClientClass; typedef struct _EvdJsonrpcHttpClientPrivate EvdJsonrpcHttpClientPrivate; struct _EvdJsonrpcHttpClient { EvdConnectionPool parent; EvdJsonrpcHttpClientPrivate *priv; }; struct _EvdJsonrpcHttpClientClass { EvdConnectionPoolClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_JSONRPC_HTTP_CLIENT (evd_jsonrpc_http_client_get_type ()) #define EVD_JSONRPC_HTTP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_JSONRPC_HTTP_CLIENT, EvdJsonrpcHttpClient)) #define EVD_JSONRPC_HTTP_CLIENT_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_JSONRPC_HTTP_CLIENT, EvdJsonrpcHttpClientClass)) #define EVD_IS_JSONRPC_HTTP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_JSONRPC_HTTP_CLIENT)) #define EVD_IS_JSONRPC_HTTP_CLIENT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_JSONRPC_HTTP_CLIENT)) #define EVD_JSONRPC_HTTP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_JSONRPC_HTTP_CLIENT, EvdJsonrpcHttpClientClass)) GType evd_jsonrpc_http_client_get_type (void) G_GNUC_CONST; EvdJsonrpcHttpClient * evd_jsonrpc_http_client_new (const gchar *url); EvdHttpRequest * evd_jsonrpc_http_client_get_http_request (EvdJsonrpcHttpClient *self); void evd_jsonrpc_http_client_call_method (EvdJsonrpcHttpClient *self, const gchar *method, JsonNode *params, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_jsonrpc_http_client_call_method_finish (EvdJsonrpcHttpClient *self, GAsyncResult *result, JsonNode **json_result, JsonNode **json_error, GError **error); G_END_DECLS #endif /* __EVD_JSONRPC_HTTP_CLIENT_H__ */ EventDance-0.2.0/evd/evd-jsonrpc-http-server.c000066400000000000000000000351501321356073300211710ustar00rootroot00000000000000/* * evd-jsonrpc-http-server.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-jsonrpc-http-server.h" #include #include G_DEFINE_TYPE (EvdJsonrpcHttpServer, evd_jsonrpc_http_server, EVD_TYPE_WEB_SERVICE) #define EVD_JSONRPC_HTTP_SERVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_JSONRPC_HTTP_SERVER, \ EvdJsonrpcHttpServerPrivate)) /* private data */ struct _EvdJsonrpcHttpServerPrivate { EvdJsonrpc *rpc; EvdJsonrpcHttpServerMethodCallCb method_call_cb; gpointer method_call_user_data; GDestroyNotify method_call_user_data_free_func; SoupMessageHeaders *headers; }; typedef struct { EvdJsonrpcHttpServer *self; gchar *buf; gpointer context; guint invocation_id; GCancellable *cancellable; JsonNode *json_result; JsonNode *json_error; } CallData; /* properties */ enum { PROP_0, PROP_RESPONSE_HEADERS }; static void evd_jsonrpc_http_server_class_init (EvdJsonrpcHttpServerClass *class); static void evd_jsonrpc_http_server_init (EvdJsonrpcHttpServer *self); static void evd_jsonrpc_http_server_finalize (GObject *obj); static void evd_jsonrpc_http_server_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_jsonrpc_http_server_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void on_request_headers (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request); static void jsonrpc_on_send (EvdJsonrpc *rpc, const gchar *buffer, gpointer context, guint invocation_id, gpointer user_data); static void jsonrpc_on_method_call (EvdJsonrpc *rpc, const gchar *method_name, JsonNode *params, guint invocation_id, gpointer context, gpointer user_data); static void evd_jsonrpc_http_server_class_init (EvdJsonrpcHttpServerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); obj_class->finalize = evd_jsonrpc_http_server_finalize; obj_class->get_property = evd_jsonrpc_http_server_get_property; obj_class->set_property = evd_jsonrpc_http_server_set_property; web_service_class->request_handler = on_request_headers; g_object_class_install_property (obj_class, PROP_RESPONSE_HEADERS, g_param_spec_boxed ("response-headers", "Response headers", "The object's internal response headers", SOUP_TYPE_MESSAGE_HEADERS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdJsonrpcHttpServerPrivate)); } static void evd_jsonrpc_http_server_init (EvdJsonrpcHttpServer *self) { EvdJsonrpcHttpServerPrivate *priv; priv = EVD_JSONRPC_HTTP_SERVER_GET_PRIVATE (self); self->priv = priv; priv->rpc = evd_jsonrpc_new (); evd_jsonrpc_set_callbacks (priv->rpc, jsonrpc_on_method_call, NULL, self, NULL); evd_jsonrpc_transport_set_send_callback (priv->rpc, jsonrpc_on_send, self, (GDestroyNotify) g_object_unref); priv->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); soup_message_headers_replace (priv->headers, "Content-type", "application/json; charset=utf-8"); soup_message_headers_replace (self->priv->headers, "Pragma", "no-cache"); soup_message_headers_replace (self->priv->headers, "Cache-Control", "no-cache, private, no-store"); } static void evd_jsonrpc_http_server_finalize (GObject *obj) { EvdJsonrpcHttpServer *self = EVD_JSONRPC_HTTP_SERVER (obj); evd_jsonrpc_transport_set_send_callback (self->priv->rpc, NULL, NULL, NULL); g_object_unref (self->priv->rpc); soup_message_headers_free (self->priv->headers); if (self->priv->method_call_user_data != NULL && self->priv->method_call_user_data_free_func) { self->priv->method_call_user_data_free_func (self->priv->method_call_user_data); } G_OBJECT_CLASS (evd_jsonrpc_http_server_parent_class)->finalize (obj); } static void evd_jsonrpc_http_server_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { /* EvdJsonrpcHttpServer *self; self = EVD_JSONRPC_HTTP_SERVER (obj); */ switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_jsonrpc_http_server_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdJsonrpcHttpServer *self; self = EVD_JSONRPC_HTTP_SERVER (obj); switch (prop_id) { case PROP_RESPONSE_HEADERS: g_value_set_boxed (value, self->priv->headers); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void jsonrpc_on_send (EvdJsonrpc *rpc, const gchar *message, gpointer context, guint invocation_id, gpointer user_data) { EvdJsonrpcHttpServer *self = EVD_JSONRPC_HTTP_SERVER (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (context); GError *error = NULL; SoupDate *date; gchar *date_str; /* update 'Expire' header in response headers */ date = soup_date_new_from_now (- 60 * 60 * 24); /* 24h in the past */ date_str = soup_date_to_string (date, SOUP_DATE_HTTP); soup_date_free (date); soup_message_headers_replace (self->priv->headers, "Expires", date_str); g_free (date_str); /* update 'Date' header in response headers */ date = soup_date_new_from_now (0); date_str = soup_date_to_string (date, SOUP_DATE_HTTP); soup_date_free (date); soup_message_headers_replace (self->priv->headers, "Date", date_str); g_free (date_str); if (! evd_web_service_respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_OK, self->priv->headers, message, strlen (message), &error)) { evd_jsonrpc_transport_error (self->priv->rpc, invocation_id, error); g_error_free (error); } g_object_unref (conn); } static void jsonrpc_on_method_call (EvdJsonrpc *rpc, const gchar *method_name, JsonNode *params, guint invocation_id, gpointer context, gpointer user_data) { EvdJsonrpcHttpServer *self = EVD_JSONRPC_HTTP_SERVER (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (context); if (self->priv->method_call_cb != NULL) { EvdHttpRequest *req; req = evd_http_connection_get_current_request (conn); g_object_ref (conn); self->priv->method_call_cb (self, method_name, params, invocation_id, conn, req, self->priv->method_call_user_data); } else { const gchar *err_st = "No handler for method calls"; evd_web_service_respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_INTERNAL_SERVER_ERROR, self->priv->headers, err_st, strlen (err_st), NULL); } } static void on_content_read (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdJsonrpcHttpServer *self = EVD_JSONRPC_HTTP_SERVER (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); GError *error = NULL; gchar *content; content = evd_http_connection_read_all_content_finish (conn, result, NULL, &error); if (content == NULL) { g_print ("Error reading content: %s\n", error->message); g_error_free (error); goto out; } if (! evd_jsonrpc_transport_receive (self->priv->rpc, content, conn, 0, &error)) { evd_web_service_respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_INTERNAL_SERVER_ERROR, self->priv->headers, error->message, strlen (error->message), NULL); g_error_free (error); } out: g_free (content); g_object_unref (self); } static void on_request_headers (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdJsonrpcHttpServer *self = EVD_JSONRPC_HTTP_SERVER (web_service); const gchar *err_st; /* validate request */ /* method must be POST */ if (g_strcmp0 (evd_http_request_get_method (request), SOUP_METHOD_POST) != 0) { err_st = "Method must be POST"; goto err; } /* read request content */ g_object_ref (self); evd_http_connection_read_all_content (conn, NULL, on_content_read, self); return; err: evd_web_service_respond (web_service, conn, SOUP_STATUS_INTERNAL_SERVER_ERROR, self->priv->headers, err_st, strlen (err_st), NULL); } /* public methods */ /** * evd_jsonrpc_http_server_new: * * Returns: (transfer full): **/ EvdJsonrpcHttpServer * evd_jsonrpc_http_server_new (void) { EvdJsonrpcHttpServer *self; self = g_object_new (EVD_TYPE_JSONRPC_HTTP_SERVER, NULL); return self; } /** * evd_jsonrpc_http_server_get_response_headers: * * Returns: (transfer none): **/ SoupMessageHeaders * evd_jsonrpc_http_server_get_response_headers (EvdJsonrpcHttpServer *self) { g_return_val_if_fail (EVD_IS_JSONRPC_HTTP_SERVER (self), NULL); return self->priv->headers; } void evd_jsonrpc_http_server_set_method_call_callback (EvdJsonrpcHttpServer *self, EvdJsonrpcHttpServerMethodCallCb callback, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_JSONRPC_HTTP_SERVER (self)); self->priv->method_call_cb = callback; self->priv->method_call_user_data = user_data; self->priv->method_call_user_data_free_func = user_data_free_func; } gboolean evd_jsonrpc_http_server_respond (EvdJsonrpcHttpServer *self, guint invocation_id, JsonNode *result, GError **error) { g_return_val_if_fail (EVD_IS_JSONRPC_HTTP_SERVER (self), FALSE); return evd_jsonrpc_respond (self->priv->rpc, invocation_id, result, NULL, error); } gboolean evd_jsonrpc_http_server_respond_error (EvdJsonrpcHttpServer *self, guint invocation_id, JsonNode *json_error, GError **error) { g_return_val_if_fail (EVD_IS_JSONRPC_HTTP_SERVER (self), FALSE); return evd_jsonrpc_respond_error (self->priv->rpc, invocation_id, json_error, NULL, error); } EventDance-0.2.0/evd/evd-jsonrpc-http-server.h000066400000000000000000000111051321356073300211700ustar00rootroot00000000000000/* * evd-jsonrpc-http-server.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_JSONRPC_HTTP_SERVER_H__ #define __EVD_JSONRPC_HTTP_SERVER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-web-service.h" G_BEGIN_DECLS typedef struct _EvdJsonrpcHttpServer EvdJsonrpcHttpServer; typedef struct _EvdJsonrpcHttpServerClass EvdJsonrpcHttpServerClass; typedef struct _EvdJsonrpcHttpServerPrivate EvdJsonrpcHttpServerPrivate; typedef void (* EvdJsonrpcHttpServerMethodCallCb) (EvdJsonrpcHttpServer *self, const gchar *method_name, JsonNode *params, guint invocation_id, EvdHttpConnection *connection, EvdHttpRequest *request, gpointer user_data); struct _EvdJsonrpcHttpServer { EvdWebService parent; EvdJsonrpcHttpServerPrivate *priv; }; struct _EvdJsonrpcHttpServerClass { EvdWebServiceClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_JSONRPC_HTTP_SERVER (evd_jsonrpc_http_server_get_type ()) #define EVD_JSONRPC_HTTP_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_JSONRPC_HTTP_SERVER, EvdJsonrpcHttpServer)) #define EVD_JSONRPC_HTTP_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_JSONRPC_HTTP_SERVER, EvdJsonrpcHttpServerClass)) #define EVD_IS_JSONRPC_HTTP_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_JSONRPC_HTTP_SERVER)) #define EVD_IS_JSONRPC_HTTP_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_JSONRPC_HTTP_SERVER)) #define EVD_JSONRPC_HTTP_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_JSONRPC_HTTP_SERVER, EvdJsonrpcHttpServerClass)) GType evd_jsonrpc_http_server_get_type (void) G_GNUC_CONST; EvdJsonrpcHttpServer * evd_jsonrpc_http_server_new (void); SoupMessageHeaders * evd_jsonrpc_http_server_get_response_headers (EvdJsonrpcHttpServer *self); void evd_jsonrpc_http_server_set_method_call_callback (EvdJsonrpcHttpServer *self, EvdJsonrpcHttpServerMethodCallCb callback, gpointer user_data, GDestroyNotify user_data_free_func); gboolean evd_jsonrpc_http_server_respond (EvdJsonrpcHttpServer *self, guint invocation_id, JsonNode *result, GError **error); gboolean evd_jsonrpc_http_server_respond_error (EvdJsonrpcHttpServer *self, guint invocation_id, JsonNode *json_error, GError **error); G_END_DECLS #endif /* __EVD_JSONRPC_HTTP_SERVER_H__ */ EventDance-0.2.0/evd/evd-jsonrpc.c000066400000000000000000000640601321356073300167120ustar00rootroot00000000000000/* * evd-jsonrpc.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-jsonrpc.h" #include "evd-json-filter.h" G_DEFINE_TYPE (EvdJsonrpc, evd_jsonrpc, EVD_TYPE_IPC_MECHANISM) #define EVD_JSONRPC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_JSONRPC, \ EvdJsonrpcPrivate)) #define DEFAULT_TIMEOUT_INTERVAL 15 struct _EvdJsonrpcPrivate { guint invocation_counter; EvdJsonrpcTransportSendCb send_cb; gpointer send_cb_user_data; GDestroyNotify send_cb_user_data_free_func; GHashTable *invocations; EvdJsonFilter *json_filter; gpointer context; EvdJsonrpcMethodCallCb method_call_cb; EvdJsonrpcNotificationCb notification_cb; gpointer cb_user_data; GDestroyNotify cb_user_data_free_func; }; typedef struct { JsonNode *result; JsonNode *error; } MethodResponse; typedef struct { GSimpleAsyncResult *result; JsonNode *remote_id; gpointer context; } InvocationData; static void evd_jsonrpc_class_init (EvdJsonrpcClass *class); static void evd_jsonrpc_init (EvdJsonrpc *self); static void evd_jsonrpc_finalize (GObject *obj); static void evd_jsonrpc_on_json_packet (EvdJsonFilter *self, const gchar *buffer, gsize size, gpointer user_data); static void transport_on_receive (EvdIpcMechanism *ipc_mechanism, EvdTransport *transport, EvdPeer *peer, const guchar *data, gsize size); static void free_invocation_data (InvocationData *data); static void evd_jsonrpc_class_init (EvdJsonrpcClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIpcMechanismClass *ipc_mechanism_class = EVD_IPC_MECHANISM_CLASS (class); obj_class->finalize = evd_jsonrpc_finalize; ipc_mechanism_class->transport_receive = transport_on_receive; g_type_class_add_private (obj_class, sizeof (EvdJsonrpcPrivate)); } static void evd_jsonrpc_init (EvdJsonrpc *self) { EvdJsonrpcPrivate *priv; priv = EVD_JSONRPC_GET_PRIVATE (self); self->priv = priv; priv->invocation_counter = 0; priv->send_cb = NULL; priv->send_cb_user_data = NULL; priv->invocations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) free_invocation_data); priv->json_filter = evd_json_filter_new (); evd_json_filter_set_packet_handler (priv->json_filter, evd_jsonrpc_on_json_packet, self, NULL); priv->context = NULL; priv->method_call_cb = NULL; priv->notification_cb = NULL; priv->cb_user_data = NULL; priv->cb_user_data_free_func = NULL; } static void evd_jsonrpc_finalize (GObject *obj) { EvdJsonrpc *self = EVD_JSONRPC (obj); g_object_unref (self->priv->json_filter); g_hash_table_unref (self->priv->invocations); if (self->priv->send_cb_user_data != NULL && self->priv->send_cb_user_data_free_func != NULL) { self->priv->send_cb_user_data_free_func (self->priv->send_cb_user_data); } G_OBJECT_CLASS (evd_jsonrpc_parent_class)->finalize (obj); } static gchar * evd_jsonrpc_build_message (EvdJsonrpc *self, gboolean request, const gchar *method_name, JsonNode *id, JsonNode *params, JsonNode *error) { JsonNode *root; JsonObject *obj; gchar *msg; JsonGenerator *gen; JsonNode *id_node; JsonNode *error_node = NULL; JsonNode *params_node = NULL; root = json_node_new (JSON_NODE_OBJECT); obj = json_object_new (); json_node_set_object (root, obj); if (id == NULL) id_node = json_node_new (JSON_NODE_NULL); else id_node = json_node_copy (id); json_object_set_member (obj, "id", id_node); if (request) { JsonNode *method_node; if (params == NULL) { params_node = json_node_new (JSON_NODE_ARRAY); json_node_take_array (params_node, json_array_new ()); } else { params_node = json_node_copy (params); } method_node = json_node_new (JSON_NODE_VALUE); json_node_set_string (method_node, method_name); json_object_set_member (obj, "method", method_node); json_object_set_member (obj, "params", params_node); } else { if (params == NULL) params_node = json_node_new (JSON_NODE_NULL); else params_node = json_node_copy (params); if (error == NULL) error_node = json_node_new (JSON_NODE_NULL); else error_node = json_node_copy (error); json_object_set_member (obj, "error", error_node); json_object_set_member (obj, "result", params_node); } gen = json_generator_new (); json_generator_set_root (gen, root); msg = json_generator_to_data (gen, NULL); g_object_unref (gen); json_object_unref (obj); json_node_free (root); return msg; } static gboolean evd_jsonrpc_on_method_called (EvdJsonrpc *self, JsonObject *msg, gpointer context, GError **error) { JsonNode *node; JsonNode *args; const gchar *method_name; InvocationData *inv_data; guint id; gchar *id_st; JsonNode *id_node; node = json_object_get_member (msg, "method"); method_name = json_node_get_string (node); if (! JSON_NODE_HOLDS_VALUE (node) || method_name == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Method name in JSON-RPC must be a valid string"); return FALSE; } args = json_object_get_member (msg, "params"); if (! JSON_NODE_HOLDS_ARRAY (args)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Params in a JSON-RPC request must be an array"); return FALSE; } id_node = json_object_dup_member (msg, "id"); inv_data = g_slice_new0 (InvocationData); inv_data->remote_id = id_node; inv_data->context = context; self->priv->invocation_counter++; id = self->priv->invocation_counter; id_st = g_strdup_printf ("%u", id); g_hash_table_insert (self->priv->invocations, id_st, inv_data); if (self->priv->method_call_cb != NULL) { self->priv->method_call_cb (self, method_name, args, id, context, self->priv->cb_user_data); } return TRUE; } static void free_method_response_data (gpointer _data) { MethodResponse *data = _data; if (data->result != NULL) json_node_free (data->result); if (data->error != NULL) json_node_free (data->error); g_slice_free (MethodResponse, _data); } static void evd_jsonrpc_on_method_result (EvdJsonrpc *self, JsonObject *msg, gpointer context) { const gchar *id; JsonNode *id_node; JsonNode *result_node; JsonNode *error_node; MethodResponse *data; InvocationData *inv_data; GSimpleAsyncResult *res; id_node = json_object_get_member (msg, "id"); id = json_node_get_string (id_node); inv_data = g_hash_table_lookup (self->priv->invocations, id); if (inv_data == NULL) { /* @TODO: do proper logging */ g_print ("Received unexpected JSON-RPC response message with id '%s'\n", id); return; } res = inv_data->result; g_object_ref (res); g_hash_table_remove (self->priv->invocations, id); result_node = json_object_get_member (msg, "result"); error_node = json_object_get_member (msg, "error"); if (! (json_node_is_null (result_node) || json_node_is_null (error_node))) { /* protocol error, one of 'result' or 'error' should be null */ g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Protocol error, invalid JSON-RPC response message: one or 'result' or 'error' must be null"); } else { data = g_slice_new0 (MethodResponse); g_simple_async_result_set_op_res_gpointer (res, data, free_method_response_data); if (! json_node_is_null (result_node)) data->result = json_node_copy (result_node); else data->error = json_node_copy (error_node); } g_simple_async_result_complete (res); g_object_unref (res); } static void evd_jsonrpc_on_notification (EvdJsonrpc *self, JsonObject *msg, gpointer context) { const gchar *method; JsonNode *params; if (self->priv->notification_cb == NULL) return; method = json_object_get_string_member (msg, "method"); params = json_object_get_member (msg, "params"); self->priv->notification_cb (self, method, params, context, self->priv->cb_user_data); } static void evd_jsonrpc_on_json_packet (EvdJsonFilter *filter, const gchar *buffer, gsize size, gpointer user_data) { EvdJsonrpc *self = EVD_JSONRPC (user_data); JsonParser *parser; JsonNode *root; JsonObject *obj; GError *error = NULL; parser = json_parser_new (); json_parser_load_from_data (parser, buffer, size, NULL); root = json_parser_get_root (parser); g_assert (root != NULL); if (! JSON_NODE_HOLDS_OBJECT (root)) { error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "JSON-RPC message must be a JSON object"); goto out; } obj = json_node_get_object (root); if (! json_object_has_member (obj, "id")) { error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "JSON-RPC message must have an 'id' member"); goto out; } if (json_object_has_member (obj, "result") && json_object_has_member (obj, "error")) { /* a method result */ evd_jsonrpc_on_method_result (self, obj, self->priv->context); } else if (json_object_has_member (obj, "method") && json_object_has_member (obj, "params")) { JsonNode *id_node; id_node = json_object_get_member (obj, "id"); if (! json_node_is_null (id_node)) /* a method call */ evd_jsonrpc_on_method_called (self, obj, self->priv->context, &error); else /* a notification */ evd_jsonrpc_on_notification (self, obj, self->priv->context); } else { error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid JSON-RPC message"); } out: if (error != NULL) { /* @TODO: do proper debugging */ g_print ("JSON-RPC ERROR: %s\n", error->message); g_error_free (error); } g_object_unref (parser); } static void evd_jsonrpc_transport_write (EvdJsonrpc *self, const gchar *msg, gpointer user_context, guint invocation_id) { GError *error = NULL; if (user_context != NULL && EVD_IS_PEER (user_context)) { if (! evd_peer_send_text (EVD_PEER (user_context), msg, &error)) { evd_jsonrpc_transport_error (self, invocation_id, error); g_error_free (error); } } else if (self->priv->send_cb != NULL) { self->priv->send_cb (self, msg, user_context, invocation_id, self->priv->send_cb_user_data); } else { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_CLOSED, "No JSON-RPC transport to deliver message"); evd_jsonrpc_transport_error (self, invocation_id, error); g_error_free (error); } } static void transport_on_receive (EvdIpcMechanism *ipc_mechanism, EvdTransport *transport, EvdPeer *peer, const guchar *data, gsize size) { EvdJsonrpc *self = EVD_JSONRPC (ipc_mechanism); evd_jsonrpc_transport_receive (self, (const gchar *) data, peer, 0, NULL); } static gboolean evd_jsonrpc_respond_full (EvdJsonrpc *self, guint invocation_id, JsonNode *result_node, JsonNode *error_node, GError **error) { gchar *id_st; JsonNode *id_node; gchar *msg; gboolean res = TRUE; InvocationData *inv_data; gpointer context; g_return_val_if_fail (EVD_IS_JSONRPC (self), FALSE); g_return_val_if_fail (invocation_id > 0, FALSE); id_st = g_strdup_printf ("%u", invocation_id); inv_data = g_hash_table_lookup (self->priv->invocations, id_st); if (inv_data == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "No method invocation found with such id"); res = FALSE; } else if (inv_data->context == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Failed to respond method, no transport associated"); res = FALSE; } else { id_node = inv_data->remote_id; inv_data->remote_id = NULL; context = inv_data->context; g_hash_table_remove (self->priv->invocations, id_st); msg = evd_jsonrpc_build_message (self, FALSE, NULL, id_node, result_node, error_node); json_node_free (id_node); evd_jsonrpc_transport_write (self, msg, context, invocation_id); g_free (msg); } g_free (id_st); return res; } static void free_invocation_data (InvocationData *data) { if (data->result != NULL) g_object_unref (data->result); if (data->remote_id != NULL) json_node_free (data->remote_id); g_slice_free (InvocationData, data); } /* public methods */ EvdJsonrpc * evd_jsonrpc_new () { return g_object_new (EVD_TYPE_JSONRPC, NULL); } /** * evd_jsonrpc_transport_set_send_callback: * @callback: (scope notified) (allow-none): * @user_data: (allow-none): * @user_data_free_func: (allow-none): * **/ void evd_jsonrpc_transport_set_send_callback (EvdJsonrpc *self, EvdJsonrpcTransportSendCb callback, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_JSONRPC (self)); self->priv->send_cb = callback; self->priv->send_cb_user_data = user_data; self->priv->send_cb_user_data_free_func = user_data_free_func; } void evd_jsonrpc_transport_error (EvdJsonrpc *self, guint invocation_id, GError *error) { InvocationData *inv_data; gchar *id_st; g_return_if_fail (EVD_IS_JSONRPC (self)); g_return_if_fail (error != NULL); id_st = g_strdup_printf ("%u", invocation_id); inv_data = g_hash_table_lookup (self->priv->invocations, id_st); if (inv_data == NULL) { /* @TODO: do proper logging */ g_debug ("Transport error for unknown invocation id"); g_free (id_st); return; } if (inv_data->result != NULL) { GSimpleAsyncResult *res = inv_data->result; g_simple_async_result_set_from_error (res, error); g_simple_async_result_complete_in_idle (res); } else { /* In case of a remote call, there is nothing we can do about a transport error. We can only hope for the remote endpoint to timeout. */ } g_hash_table_remove (self->priv->invocations, id_st); g_free (id_st); } /** * evd_jsonrpc_call_method: * @params: (type Json.Node): **/ void evd_jsonrpc_call_method (EvdJsonrpc *self, const gchar *method_name, JsonNode *params, gpointer context, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; gchar *msg; guint id; gchar *id_st; JsonNode *id_node; InvocationData *inv_data; g_return_if_fail (EVD_IS_JSONRPC (self)); g_return_if_fail (method_name != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_jsonrpc_call_method); if ((context == NULL || ! EVD_IS_PEER (context)) && self->priv->send_cb == NULL) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Failed to call method, no transport associated"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->invocation_counter++; id = self->priv->invocation_counter; id_st = g_strdup_printf ("%u", id); inv_data = g_slice_new0 (InvocationData); inv_data->result = res; inv_data->context = context; g_hash_table_insert (self->priv->invocations, id_st, inv_data); id_node = json_node_new (JSON_NODE_VALUE); json_node_set_string (id_node, id_st); msg = evd_jsonrpc_build_message (self, TRUE, method_name, id_node, params, NULL); json_node_free (id_node); evd_jsonrpc_transport_write (self, msg, context, id); g_free (msg); } /** * evd_jsonrpc_call_method_finish: * @result_json: (out) (allow-none): * @error_json: (out) (allow-none): * @error: (out) (allow-none): * **/ gboolean evd_jsonrpc_call_method_finish (EvdJsonrpc *self, GAsyncResult *result, JsonNode **result_json, JsonNode **error_json, GError **error) { GSimpleAsyncResult *res; g_return_val_if_fail (EVD_IS_JSONRPC (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_jsonrpc_call_method), FALSE); res = G_SIMPLE_ASYNC_RESULT (result); if (! g_simple_async_result_propagate_error (res, error)) { MethodResponse *data; data = g_simple_async_result_get_op_res_gpointer (res); if (result_json != NULL) *result_json = data->result; data->result = NULL; if (error_json != NULL) *error_json = data->error; data->error = NULL; return TRUE; } else return FALSE; } gboolean evd_jsonrpc_transport_receive (EvdJsonrpc *self, const gchar *message, gpointer context, guint invocation_id, GError **error) { gsize size; GError *_error = NULL; gboolean result = TRUE; g_return_val_if_fail (EVD_IS_JSONRPC (self), FALSE); size = strlen (message); if (message == NULL || size == 0) return TRUE; self->priv->context = context; if (! evd_json_filter_feed_len (self->priv->json_filter, message, size, &_error)) { evd_jsonrpc_transport_error (self, invocation_id, _error); g_propagate_error (error, _error); result = FALSE; } self->priv->context = NULL; return result; } /** * evd_jsonrpc_set_callbacks: * @method_call_cb: (scope notified) (allow-none): * @notification_cb: (scope notified) (allow-none): * @user_data: (allow-none): * @user_data_free_func: (allow-none): * **/ void evd_jsonrpc_set_callbacks (EvdJsonrpc *self, EvdJsonrpcMethodCallCb method_call_cb, EvdJsonrpcNotificationCb notification_cb, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_JSONRPC (self)); self->priv->method_call_cb = method_call_cb; self->priv->notification_cb = notification_cb; self->priv->cb_user_data = user_data; self->priv->cb_user_data_free_func = user_data_free_func; } /** * evd_jsonrpc_respond: * @result: (allow-none): * @context: (allow-none) (type GObject): * @error: (allow-none): * **/ gboolean evd_jsonrpc_respond (EvdJsonrpc *self, guint invocation_id, JsonNode *result, gpointer context, GError **error) { return evd_jsonrpc_respond_full (self, invocation_id, result, NULL, error); } gboolean evd_jsonrpc_respond_error (EvdJsonrpc *self, guint invocation_id, JsonNode *json_error, gpointer context, GError **error) { return evd_jsonrpc_respond_full (self, invocation_id, NULL, json_error, error); } gboolean evd_jsonrpc_respond_from_error (EvdJsonrpc *self, guint invocation_id, GError *result_error, gpointer context, GError **error) { gboolean result; JsonNode *result_node; JsonObject *obj; g_return_val_if_fail (EVD_IS_JSONRPC (self), FALSE); g_return_val_if_fail (result_error != NULL, FALSE); result_node = json_node_new (JSON_NODE_OBJECT); obj = json_object_new (); json_node_set_object (result_node, obj); json_object_set_int_member (obj, "code", result_error->code); json_object_set_string_member (obj, "message", result_error->message); result = evd_jsonrpc_respond_error (self, invocation_id, result_node, context, error); json_object_unref (obj); json_node_free (result_node); return result; } /** * evd_jsonrpc_send_notification: * @params: (allow-none): * @context: (allow-none): * @error: (allow-none): * * Returns: **/ gboolean evd_jsonrpc_send_notification (EvdJsonrpc *self, const gchar *notification_name, JsonNode *params, gpointer context, GError **error) { gchar *msg; g_return_val_if_fail (EVD_IS_JSONRPC (self), FALSE); g_return_val_if_fail (notification_name != NULL, FALSE); if ((context == NULL || ! EVD_IS_PEER (context)) && self->priv->send_cb == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Failed to send notificaton, no transport associated"); return FALSE; } msg = evd_jsonrpc_build_message (self, TRUE, notification_name, NULL, params, NULL); evd_jsonrpc_transport_write (self, msg, context, 0); g_free (msg); return TRUE; } EventDance-0.2.0/evd/evd-jsonrpc.h000066400000000000000000000175721321356073300167250ustar00rootroot00000000000000/* * evd-jsonrpc.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_JSONRPC_H__ #define __EVD_JSONRPC_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include "evd-ipc-mechanism.h" G_BEGIN_DECLS typedef struct _EvdJsonrpc EvdJsonrpc; typedef struct _EvdJsonrpcClass EvdJsonrpcClass; typedef struct _EvdJsonrpcPrivate EvdJsonrpcPrivate; typedef gboolean (* EvdJsonrpcTransportWriteCb) (EvdJsonrpc *self, const gchar *buffer, gsize size, gpointer context, gpointer user_data); typedef void (* EvdJsonrpcTransportSendCb) (EvdJsonrpc *self, const gchar *message, gpointer context, guint invocation_id, gpointer user_data); /** * EvdJsonrpcMethodCallCb: * @context: (type GObject): **/ typedef void (* EvdJsonrpcMethodCallCb) (EvdJsonrpc *self, const gchar *method_name, JsonNode *params, guint invocation_id, gpointer context, gpointer user_data); /** * EvdJsonrpcNotificationCb: * @context: (type GObject): **/ typedef void (* EvdJsonrpcNotificationCb) (EvdJsonrpc *self, const gchar *notification_name, JsonNode *params, gpointer context, gpointer user_data); struct _EvdJsonrpc { EvdIpcMechanism parent; EvdJsonrpcPrivate *priv; }; struct _EvdJsonrpcClass { EvdIpcMechanismClass parent_class; }; #define EVD_TYPE_JSONRPC (evd_jsonrpc_get_type ()) #define EVD_JSONRPC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_JSONRPC, EvdJsonrpc)) #define EVD_JSONRPC_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_JSONRPC, EvdJsonrpcClass)) #define EVD_IS_JSONRPC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_JSONRPC)) #define EVD_IS_JSONRPC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_JSONRPC)) #define EVD_JSONRPC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_JSONRPC, EvdJsonrpcClass)) GType evd_jsonrpc_get_type (void) G_GNUC_CONST; EvdJsonrpc * evd_jsonrpc_new (void); void evd_jsonrpc_transport_set_send_callback (EvdJsonrpc *self, EvdJsonrpcTransportSendCb callback, gpointer user_data, GDestroyNotify user_data_free_func); gboolean evd_jsonrpc_transport_receive (EvdJsonrpc *self, const gchar *message, gpointer context, guint invocation_id, GError **error); void evd_jsonrpc_transport_error (EvdJsonrpc *self, guint invocation_id, GError *error); void evd_jsonrpc_call_method (EvdJsonrpc *self, const gchar *method_name, JsonNode *params, gpointer context, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_jsonrpc_call_method_finish (EvdJsonrpc *self, GAsyncResult *result, JsonNode **result_json, JsonNode **error_json, GError **error); void evd_jsonrpc_set_callbacks (EvdJsonrpc *self, EvdJsonrpcMethodCallCb method_call_cb, EvdJsonrpcNotificationCb notification_cb, gpointer user_data, GDestroyNotify user_data_free_func); gboolean evd_jsonrpc_respond (EvdJsonrpc *self, guint invocation_id, JsonNode *result, gpointer context, GError **error); gboolean evd_jsonrpc_respond_error (EvdJsonrpc *self, guint invocation_id, JsonNode *json_error, gpointer context, GError **error); gboolean evd_jsonrpc_respond_from_error (EvdJsonrpc *self, guint invocation_id, GError *result_error, gpointer context, GError **error); gboolean evd_jsonrpc_send_notification (EvdJsonrpc *self, const gchar *notification_name, JsonNode *params, gpointer context, GError **error); G_END_DECLS #endif /* __EVD_JSONRPC_H__ */ EventDance-0.2.0/evd/evd-longpolling-server.c000066400000000000000000000522341321356073300210640ustar00rootroot00000000000000/* * evd-longpolling-server.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009, 2010, 2011, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include "evd-longpolling-server.h" #include "evd-transport.h" #include "evd-error.h" #include "evd-http-connection.h" #include "evd-peer-manager.h" #define EVD_LONGPOLLING_SERVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_LONGPOLLING_SERVER, \ EvdLongpollingServerPrivate)) #define PEER_DATA_KEY "org.eventdance.lib.LongpollingServer.PEER_DATA" #define CONN_PEER_KEY_GET PEER_DATA_KEY ".GET" #define CONN_PEER_KEY_POST PEER_DATA_KEY ".POST" #define ACTION_RECEIVE "receive" #define ACTION_SEND "send" #define ACTION_CLOSE "close" /* private data */ struct _EvdLongpollingServerPrivate { const gchar *current_peer_id; }; typedef struct _EvdLongpollingServerPeerData EvdLongpollingServerPeerData; struct _EvdLongpollingServerPeerData { GQueue *conns; }; static void evd_longpolling_server_class_init (EvdLongpollingServerClass *class); static void evd_longpolling_server_init (EvdLongpollingServer *self); static void evd_longpolling_server_transport_iface_init (EvdTransportInterface *iface); static void evd_longpolling_server_finalize (GObject *obj); static void evd_longpolling_server_dispose (GObject *obj); static void evd_longpolling_server_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request); static gboolean evd_longpolling_server_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream); static gboolean evd_longpolling_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error); static gboolean evd_longpolling_server_actual_send (EvdLongpollingServer *self, EvdPeer *peer, EvdHttpConnection *conn, const gchar *buffer, gsize size, GError **error); static gboolean evd_longpolling_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer); static void evd_longpolling_server_peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully); G_DEFINE_TYPE_WITH_CODE (EvdLongpollingServer, evd_longpolling_server, EVD_TYPE_WEB_SERVICE, G_IMPLEMENT_INTERFACE (EVD_TYPE_TRANSPORT, evd_longpolling_server_transport_iface_init)); static void evd_longpolling_server_class_init (EvdLongpollingServerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIoStreamGroupClass *io_stream_group_class = EVD_IO_STREAM_GROUP_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); obj_class->dispose = evd_longpolling_server_dispose; obj_class->finalize = evd_longpolling_server_finalize; io_stream_group_class->remove = evd_longpolling_server_remove; web_service_class->request_handler = evd_longpolling_server_request_handler; g_type_class_add_private (obj_class, sizeof (EvdLongpollingServerPrivate)); } static void evd_longpolling_server_transport_iface_init (EvdTransportInterface *iface) { iface->send = evd_longpolling_server_send; iface->peer_is_connected = evd_longpolling_server_peer_is_connected; iface->peer_closed = evd_longpolling_server_peer_closed; } static void evd_longpolling_server_init (EvdLongpollingServer *self) { EvdLongpollingServerPrivate *priv; priv = EVD_LONGPOLLING_SERVER_GET_PRIVATE (self); self->priv = priv; priv->current_peer_id = NULL; evd_service_set_io_stream_type (EVD_SERVICE (self), EVD_TYPE_HTTP_CONNECTION); } static void evd_longpolling_server_dispose (GObject *obj) { G_OBJECT_CLASS (evd_longpolling_server_parent_class)->dispose (obj); } static void evd_longpolling_server_finalize (GObject *obj) { G_OBJECT_CLASS (evd_longpolling_server_parent_class)->finalize (obj); } static void evd_longpolling_server_read_msg_header (const gchar *buf, gsize *hdr_len, gsize *msg_len, gboolean *more_fragments) { const gchar MORE_BIT = 0x80; gchar hdr; hdr = buf[0]; if (more_fragments != NULL) *more_fragments = (hdr & MORE_BIT) > 0; hdr &= ~MORE_BIT; if (hdr <= 0x7F - 2) { if (hdr_len != NULL) *hdr_len = 1; if (msg_len != NULL) *msg_len = hdr; } else if (hdr == 0x7F - 1) { if (hdr_len != NULL) *hdr_len = 5; if (msg_len != NULL) sscanf (buf + 1, "%04x", (uint *) msg_len); } else { if (hdr_len != NULL) *hdr_len = 17; if (msg_len != NULL) sscanf (buf + 1, "%16x", (uint *) msg_len); } } static void evd_longpolling_server_conn_on_content_read (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdLongpollingServer *self = EVD_LONGPOLLING_SERVER (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); EvdPeer *peer; gchar *content; gssize size; GError *error = NULL; peer = g_object_get_data (G_OBJECT (conn), CONN_PEER_KEY_POST); if ( (content = evd_http_connection_read_all_content_finish (conn, res, &size, &error)) != NULL) { if (size > 0) { EvdTransportInterface *iface; gint i; gsize hdr_len = 0; gsize msg_len = 0; iface = EVD_TRANSPORT_GET_INTERFACE (self); i = 0; while (i < size) { evd_longpolling_server_read_msg_header (content + i, &hdr_len, &msg_len, NULL); iface->receive (EVD_TRANSPORT (self), peer, content + i + hdr_len, (gsize) msg_len); i += msg_len + hdr_len; } } g_free (content); } else { g_debug ("error reading content: %s", error->message); g_error_free (error); } evd_longpolling_server_actual_send (self, peer, conn, NULL, 0, NULL); g_object_unref (peer); } static gchar * evd_longpolling_server_resolve_action (EvdLongpollingServer *self, EvdHttpRequest *request) { SoupURI *uri; const gchar *path; gchar **tokens; gint i; gchar *action = NULL; uri = evd_http_request_get_uri (request); path = uri->path; tokens = g_strsplit (path, "/", 32); i = 0; while (tokens[i] != NULL) i++; action = g_strdup (tokens[i-1]); g_strfreev (tokens); return action; } static void evd_longpolling_server_free_peer_data (gpointer _data) { EvdLongpollingServerPeerData *data = _data; g_queue_free (data->conns); g_free (data); } static void evd_longpolling_server_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdLongpollingServer *self = EVD_LONGPOLLING_SERVER (web_service); gchar *action; EvdPeer *peer; SoupURI *uri; uri = evd_http_request_get_uri (request); self->priv->current_peer_id = uri->query; if (uri->query == NULL || (peer = evd_transport_lookup_peer (EVD_TRANSPORT (self), uri->query)) == NULL) { EVD_WEB_SERVICE_GET_CLASS (self)->respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_NOT_FOUND, NULL, NULL, 0, NULL); return; } evd_peer_touch (peer); action = evd_longpolling_server_resolve_action (self, request); /* receive? */ if (g_strcmp0 (action, ACTION_RECEIVE) == 0) { EvdLongpollingServerPeerData *data; data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (data == NULL) { data = g_new0 (EvdLongpollingServerPeerData, 1); data->conns = g_queue_new (); g_object_set_data_full (G_OBJECT (peer), PEER_DATA_KEY, data, evd_longpolling_server_free_peer_data); } /* send Peer's backlogged frames */ if (evd_peer_backlog_get_length (peer) > 0) { evd_longpolling_server_actual_send (self, peer, conn, NULL, 0, NULL); } else { g_object_ref (conn); g_object_ref (peer); g_object_set_data (G_OBJECT (conn), CONN_PEER_KEY_GET, peer); g_queue_push_tail (data->conns, conn); } } /* send? */ else if (g_strcmp0 (action, ACTION_SEND) == 0) { g_object_ref (peer); g_object_set_data (G_OBJECT (conn), CONN_PEER_KEY_POST, peer); evd_http_connection_read_all_content (conn, NULL, evd_longpolling_server_conn_on_content_read, self); } /* close? */ else if (g_strcmp0 (action, ACTION_CLOSE) == 0) { EVD_WEB_SERVICE_GET_CLASS (self)->respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_OK, NULL, NULL, 0, NULL); evd_transport_close_peer (EVD_TRANSPORT (self), peer, TRUE, NULL); } self->priv->current_peer_id = NULL; g_free (action); } static gboolean evd_longpolling_server_write_frame_delivery (EvdLongpollingServer *self, EvdHttpConnection *conn, const gchar *buf, gsize size, GError **error) { guint8 hdr[17]; gsize hdr_len = 1; gchar *len_st; if (size <= 0x7F - 2) { hdr_len = 1; hdr[0] = (guint8) size; } else if (size <= 0xFFFF) { hdr[0] = 0x7F - 1; hdr_len = 5; len_st = g_strdup_printf ("%04x", (uint) size); g_memmove (hdr + 1, len_st, 4); g_free (len_st); } else { hdr[0] = 0x7F; hdr_len = 17; len_st = g_strdup_printf ("%16x", (uint) size); g_memmove (hdr + 1, len_st, 16); g_free (len_st); } evd_http_connection_write_content (conn, (gchar *) hdr, hdr_len, TRUE, NULL); return evd_http_connection_write_content (conn, buf, size, TRUE, error); } static gboolean evd_longpolling_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer) { EvdLongpollingServer *self = EVD_LONGPOLLING_SERVER (transport); EvdLongpollingServerPeerData *data; data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (g_strcmp0 (self->priv->current_peer_id, evd_peer_get_id (peer)) != 0 && (data == NULL || g_queue_get_length (data->conns) == 0)) return FALSE; else return TRUE; } static gboolean evd_longpolling_server_actual_send (EvdLongpollingServer *self, EvdPeer *peer, EvdHttpConnection *conn, const gchar *buffer, gsize size, GError **error) { SoupMessageHeaders *headers; gboolean result = TRUE; EvdHttpRequest *request; /* build and send HTTP headers */ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); soup_message_headers_replace (headers, "Content-type", "text/plain; charset=utf-8"); soup_message_headers_replace (headers, "Transfer-Encoding", "chunked"); if (evd_http_connection_get_keepalive (conn)) soup_message_headers_replace (headers, "Connection", "keep-alive"); else soup_message_headers_replace (headers, "Connection", "close"); request = evd_http_connection_get_current_request (conn); if (request != NULL) { const gchar *origin; origin = evd_http_request_get_origin (request); if (origin != NULL && evd_web_service_origin_allowed (EVD_WEB_SERVICE (self), origin)) { soup_message_headers_replace (headers, "Access-Control-Allow-Origin", origin); } } if (evd_http_connection_write_response_headers (conn, SOUP_HTTP_1_1, SOUP_STATUS_OK, NULL, headers, error)) { gchar *frame; gsize frame_size; EvdMessageType frame_type; /* send frames in peer's backlog first */ while ( result && (frame = evd_peer_pop_message (peer, &frame_size, &frame_type)) != NULL) { if (! evd_longpolling_server_write_frame_delivery (self, conn, frame, frame_size, NULL)) { evd_peer_unshift_message (peer, frame, frame_size, frame_type, NULL); result = FALSE; } g_free (frame); } /* then send the requested frame */ if (result && buffer != NULL && ! evd_longpolling_server_write_frame_delivery (self, conn, buffer, size, NULL)) { result = FALSE; } /* notify end of content */ evd_http_connection_write_content (conn, NULL, 0, FALSE, NULL); /* flush connection's buffer, and shutdown connection after */ EVD_WEB_SERVICE_GET_CLASS (self)-> flush_and_return_connection (EVD_WEB_SERVICE (self), conn); } soup_message_headers_free (headers); return result; } static gboolean evd_longpolling_server_select_conn_and_send (EvdLongpollingServer *self, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { EvdLongpollingServerPeerData *data; EvdHttpConnection *conn; data = (EvdLongpollingServerPeerData *) g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unable to associate peer with long-polling transport"); return FALSE; } if (g_queue_get_length (data->conns) == 0) return FALSE; evd_peer_touch (peer); conn = EVD_HTTP_CONNECTION (g_queue_pop_head (data->conns)); if (evd_longpolling_server_actual_send (self, peer, conn, buffer, size, error)) return TRUE; else return FALSE; } static gboolean evd_longpolling_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { EvdLongpollingServer *self = EVD_LONGPOLLING_SERVER (transport); return evd_longpolling_server_select_conn_and_send (self, peer, buffer, size, type, error); } static gboolean evd_longpolling_server_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream) { EvdConnection *conn = EVD_CONNECTION (io_stream); EvdPeer *peer; if (! EVD_IO_STREAM_GROUP_CLASS (evd_longpolling_server_parent_class)-> remove (io_stream_group, io_stream)) { return FALSE; } /* remove conn from Peer's list of conns */ peer = g_object_get_data (G_OBJECT (conn), CONN_PEER_KEY_GET); if (peer != NULL) { EvdLongpollingServerPeerData *data; evd_peer_touch (peer); g_object_set_data (G_OBJECT (conn), CONN_PEER_KEY_GET, NULL); data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (data != NULL) g_queue_remove (data->conns, conn); g_object_unref (peer); g_object_unref (conn); } return TRUE; } static void evd_longpolling_server_peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully) { EvdLongpollingServerPeerData *data; data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (data == NULL) return; while (g_queue_get_length (data->conns) > 0) { EvdHttpConnection *conn; conn = EVD_HTTP_CONNECTION (g_queue_pop_head (data->conns)); g_object_set_data (G_OBJECT (conn), CONN_PEER_KEY_GET, NULL); EVD_WEB_SERVICE_GET_CLASS (transport)-> flush_and_return_connection (EVD_WEB_SERVICE (transport), conn); g_object_unref (conn); } g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); } /* public methods */ EvdLongpollingServer * evd_longpolling_server_new (void) { EvdLongpollingServer *self; self = g_object_new (EVD_TYPE_LONGPOLLING_SERVER, NULL); return self; } EventDance-0.2.0/evd/evd-longpolling-server.h000066400000000000000000000050131321356073300210620ustar00rootroot00000000000000/* * evd-longpolling-server.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_LONGPOLLING_SERVER_H__ #define __EVD_LONGPOLLING_SERVER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-service.h" G_BEGIN_DECLS typedef struct _EvdLongpollingServer EvdLongpollingServer; typedef struct _EvdLongpollingServerClass EvdLongpollingServerClass; typedef struct _EvdLongpollingServerPrivate EvdLongpollingServerPrivate; struct _EvdLongpollingServer { EvdWebService parent; EvdLongpollingServerPrivate *priv; }; struct _EvdLongpollingServerClass { EvdWebServiceClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_LONGPOLLING_SERVER (evd_longpolling_server_get_type ()) #define EVD_LONGPOLLING_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_LONGPOLLING_SERVER, EvdLongpollingServer)) #define EVD_LONGPOLLING_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_LONGPOLLING_SERVER, EvdLongpollingServerClass)) #define EVD_IS_LONGPOLLING_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_LONGPOLLING_SERVER)) #define EVD_IS_LONGPOLLING_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_LONGPOLLING_SERVER)) #define EVD_LONGPOLLING_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_LONGPOLLING_SERVER, EvdLongpollingServerClass)) GType evd_longpolling_server_get_type (void) G_GNUC_CONST; EvdLongpollingServer * evd_longpolling_server_new (void); G_END_DECLS #endif /* __EVD_LONGPOLLING_SERVER_H__ */ EventDance-0.2.0/evd/evd-marshal.list000066400000000000000000000001511321356073300174030ustar00rootroot00000000000000VOID:UINT,INT,STRING VOID:STRING,ULONG VOID:UINT,UINT UINT:OBJECT VOID:OBJECT,OBJECT VOID:OBJECT,BOOLEAN EventDance-0.2.0/evd/evd-peer-manager.c000066400000000000000000000225061321356073300175760ustar00rootroot00000000000000/* * evd-peer-manager.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-peer-manager.h" #include "evd-marshal.h" #include "evd-utils.h" G_DEFINE_TYPE (EvdPeerManager, evd_peer_manager, G_TYPE_OBJECT) #define EVD_PEER_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_PEER_MANAGER, \ EvdPeerManagerPrivate)) #define DEFAULT_PEER_CLEANUP_INTERVAL 5 /* seconds */ #define PEER_DATA_KEY "org.eventdance.lib.PeerManager.PEER_DATA" /* private data */ struct _EvdPeerManagerPrivate { GHashTable *peers; GTimer *peer_cleanup_timer; guint peer_cleanup_interval; guint peer_cleanup_src_id; GQueue *removal_list; }; /* signals */ enum { SIGNAL_NEW_PEER, SIGNAL_PEER_CLOSED, SIGNAL_LAST }; static guint evd_peer_manager_signals[SIGNAL_LAST] = { 0 }; static EvdPeerManager *evd_peer_manager_default = NULL; static void evd_peer_manager_class_init (EvdPeerManagerClass *class); static void evd_peer_manager_init (EvdPeerManager *self); static void evd_peer_manager_finalize (GObject *obj); static void evd_peer_manager_dispose (GObject *obj); static void evd_peer_manager_close_peer_internal (EvdPeerManager *self, EvdPeer *peer, gboolean gracefully); static void evd_peer_manager_class_init (EvdPeerManagerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_peer_manager_dispose; obj_class->finalize = evd_peer_manager_finalize; evd_peer_manager_signals[SIGNAL_NEW_PEER] = g_signal_new ("new-peer", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdPeerManagerClass, new_peer), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, EVD_TYPE_PEER); evd_peer_manager_signals[SIGNAL_PEER_CLOSED] = g_signal_new ("peer-closed", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdPeerManagerClass, peer_closed), NULL, NULL, evd_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, EVD_TYPE_PEER, G_TYPE_BOOLEAN); g_type_class_add_private (obj_class, sizeof (EvdPeerManagerPrivate)); } static void evd_peer_manager_init (EvdPeerManager *self) { EvdPeerManagerPrivate *priv; priv = EVD_PEER_MANAGER_GET_PRIVATE (self); self->priv = priv; priv->peers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); priv->peer_cleanup_timer = g_timer_new (); priv->peer_cleanup_interval = DEFAULT_PEER_CLEANUP_INTERVAL; priv->removal_list = g_queue_new (); } static void evd_peer_manager_dispose (GObject *obj) { EvdPeerManager *self = EVD_PEER_MANAGER (obj); if (self->priv->peers != NULL) { while (g_queue_get_length (self->priv->removal_list) > 0) { EvdPeer *peer; peer = EVD_PEER (g_queue_pop_head (self->priv->removal_list)); evd_peer_manager_close_peer_internal (self, peer, FALSE); g_object_unref (peer); } g_queue_free (self->priv->removal_list); g_hash_table_unref (self->priv->peers); self->priv->peers = NULL; } G_OBJECT_CLASS (evd_peer_manager_parent_class)->dispose (obj); } static void evd_peer_manager_finalize (GObject *obj) { EvdPeerManager *self = EVD_PEER_MANAGER (obj); g_timer_destroy (self->priv->peer_cleanup_timer); if (self->priv->peer_cleanup_src_id != 0) g_source_remove (self->priv->peer_cleanup_src_id); G_OBJECT_CLASS (evd_peer_manager_parent_class)->finalize (obj); if (self == evd_peer_manager_default) evd_peer_manager_default = NULL; } static void evd_peer_manager_close_peer_internal (EvdPeerManager *self, EvdPeer *peer, gboolean gracefully) { evd_peer_close (peer, gracefully); g_signal_emit (self, evd_peer_manager_signals[SIGNAL_PEER_CLOSED], 0, peer, gracefully, NULL); } static gboolean evd_peer_manager_check_peer (gpointer key, gpointer value, gpointer user_data) { EvdPeerManager *self = EVD_PEER_MANAGER (user_data); EvdPeer *peer = EVD_PEER (value); if (evd_peer_is_alive (peer)) { return FALSE; } else { g_queue_push_tail (self->priv->removal_list, g_object_ref (peer)); return TRUE; } } static void evd_peer_manager_cleanup_peers (EvdPeerManager *self) { if (g_timer_elapsed (self->priv->peer_cleanup_timer, NULL) <= self->priv->peer_cleanup_interval) return; g_timer_start (self->priv->peer_cleanup_timer); g_hash_table_foreach_remove (self->priv->peers, evd_peer_manager_check_peer, self); while (g_queue_get_length (self->priv->removal_list) > 0) { EvdPeer *peer; peer = EVD_PEER (g_queue_pop_head (self->priv->removal_list)); evd_peer_manager_close_peer_internal (self, peer, FALSE); g_object_unref (peer); } } static gboolean evd_peer_manager_notify_new_peer (gpointer user_data) { EvdPeerManager *self; EvdPeer *peer; peer = EVD_PEER (user_data); self = EVD_PEER_MANAGER (g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY)); g_signal_emit (self, evd_peer_manager_signals[SIGNAL_NEW_PEER], 0, peer, NULL); g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); g_object_unref (self); return FALSE; } static gboolean evd_peer_manager_cleanup_peers_cb (gpointer user_data) { EvdPeerManager *self = EVD_PEER_MANAGER (user_data); self->priv->peer_cleanup_src_id = 0; evd_peer_manager_cleanup_peers (self); return FALSE; } /* public methods */ /** * evd_peer_manager_get_default: * * Returns: (transfer full): **/ EvdPeerManager * evd_peer_manager_get_default (void) { if (evd_peer_manager_default == NULL) evd_peer_manager_default = evd_peer_manager_new (); else g_object_ref (evd_peer_manager_default); return evd_peer_manager_default; } EvdPeerManager * evd_peer_manager_new (void) { EvdPeerManager *self; self = g_object_new (EVD_TYPE_PEER_MANAGER, NULL); return self; } void evd_peer_manager_add_peer (EvdPeerManager *self, EvdPeer *peer) { g_return_if_fail (EVD_IS_PEER_MANAGER (self)); g_return_if_fail (EVD_IS_PEER (peer)); g_object_ref (peer); g_hash_table_insert (self->priv->peers, g_strdup (evd_peer_get_id (peer)), peer); g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, self); g_object_ref (self); evd_timeout_add (g_main_context_get_thread_default (), 0, G_PRIORITY_DEFAULT, evd_peer_manager_notify_new_peer, peer); evd_peer_manager_cleanup_peers (self); } /** * evd_peer_manager_lookup_peer: * * Returns: (transfer none): The #EvdPeer, or NULL if not found. **/ EvdPeer * evd_peer_manager_lookup_peer (EvdPeerManager *self, const gchar *id) { EvdPeer *peer; g_return_val_if_fail (EVD_IS_PEER_MANAGER (self), NULL); if (id == NULL) return NULL; peer = EVD_PEER (g_hash_table_lookup (self->priv->peers, (gconstpointer) id)); /* trigger a peer cleanup in idle */ self->priv->peer_cleanup_src_id = evd_timeout_add (NULL, 0, G_PRIORITY_DEFAULT, evd_peer_manager_cleanup_peers_cb, self); return peer; } /** * evd_peer_manager_get_all_peers: * * Returns: (transfer container) (element-type Evd.Peer): **/ GList * evd_peer_manager_get_all_peers (EvdPeerManager *self) { g_return_val_if_fail (EVD_IS_PEER_MANAGER (self), NULL); evd_peer_manager_cleanup_peers (self); return g_hash_table_get_values (self->priv->peers); } void evd_peer_manager_close_peer (EvdPeerManager *self, EvdPeer *peer, gboolean gracefully) { g_return_if_fail (EVD_IS_PEER_MANAGER (self)); g_return_if_fail (EVD_IS_PEER (peer)); if (g_hash_table_remove (self->priv->peers, evd_peer_get_id (peer))) evd_peer_manager_close_peer_internal (self, peer, gracefully); } EventDance-0.2.0/evd/evd-peer-manager.h000066400000000000000000000062621321356073300176040ustar00rootroot00000000000000/* * evd-peer-manager.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PEER_MANAGER_H__ #define __EVD_PEER_MANAGER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-peer.h" G_BEGIN_DECLS typedef struct _EvdPeerManager EvdPeerManager; typedef struct _EvdPeerManagerClass EvdPeerManagerClass; typedef struct _EvdPeerManagerPrivate EvdPeerManagerPrivate; struct _EvdPeerManager { GObject parent; EvdPeerManagerPrivate *priv; }; struct _EvdPeerManagerClass { GObjectClass parent_class; /* signal prototypes */ void (* new_peer) (EvdPeerManager *self, EvdPeer *peer, gpointer user_data); void (* peer_closed) (EvdPeerManager *self, EvdPeer *peer, gboolean gracefully, gpointer user_data); }; #define EVD_TYPE_PEER_MANAGER (evd_peer_manager_get_type ()) #define EVD_PEER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_PEER_MANAGER, EvdPeerManager)) #define EVD_PEER_MANAGER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_PEER_MANAGER, EvdPeerManagerClass)) #define EVD_IS_PEER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_PEER_MANAGER)) #define EVD_IS_PEER_MANAGER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_PEER_MANAGER)) #define EVD_PEER_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_PEER_MANAGER, EvdPeerManagerClass)) GType evd_peer_manager_get_type (void) G_GNUC_CONST; EvdPeerManager *evd_peer_manager_get_default (void); EvdPeerManager *evd_peer_manager_new (void); void evd_peer_manager_add_peer (EvdPeerManager *self, EvdPeer *peer); EvdPeer *evd_peer_manager_lookup_peer (EvdPeerManager *self, const gchar *id); GList *evd_peer_manager_get_all_peers (EvdPeerManager *self); void evd_peer_manager_close_peer (EvdPeerManager *self, EvdPeer *peer, gboolean gracefully); G_END_DECLS #endif /* __EVD_PEER_MANAGER_H__ */ EventDance-0.2.0/evd/evd-peer.c000066400000000000000000000263411321356073300161670ustar00rootroot00000000000000/* * evd-peer.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010/2011/2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-peer.h" #include "evd-transport.h" #include "evd-utils.h" G_DEFINE_TYPE (EvdPeer, evd_peer, G_TYPE_OBJECT) #define EVD_PEER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_PEER, \ EvdPeerPrivate)) #define DEFAULT_TIMEOUT_INTERVAL 15 /* private data */ struct _EvdPeerPrivate { gchar *id; gboolean closed; GQueue *backlog; GTimer *idle_timer; guint timeout_interval; EvdTransport *transport; }; typedef struct { EvdMessageType type; gsize len; gchar *buf; } BacklogFrame; /* properties */ enum { PROP_0, PROP_ID, PROP_TRANSPORT }; static void evd_peer_class_init (EvdPeerClass *class); static void evd_peer_init (EvdPeer *self); static void evd_peer_finalize (GObject *obj); static void evd_peer_dispose (GObject *obj); static void evd_peer_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_peer_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void free_backlog_frame (gpointer data, gpointer user_data); static void evd_peer_class_init (EvdPeerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_peer_dispose; obj_class->finalize = evd_peer_finalize; obj_class->get_property = evd_peer_get_property; obj_class->set_property = evd_peer_set_property; g_object_class_install_property (obj_class, PROP_ID, g_param_spec_string ("id", "Peer's UUID", "A string representing the UUID of the peer", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TRANSPORT, g_param_spec_object ("transport", "Peer's transport", "Transport object which this peer uses for sending and receiving data", G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdPeerPrivate)); } static void evd_peer_init (EvdPeer *self) { EvdPeerPrivate *priv; priv = EVD_PEER_GET_PRIVATE (self); self->priv = priv; self->priv->closed = FALSE; priv->backlog = g_queue_new (); priv->idle_timer = g_timer_new (); priv->timeout_interval = DEFAULT_TIMEOUT_INTERVAL; self->priv->id = evd_uuid_new (); } static void evd_peer_dispose (GObject *obj) { EvdPeer *self = EVD_PEER (obj); if (self->priv->transport != NULL) { g_object_unref (self->priv->transport); self->priv->transport = NULL; } G_OBJECT_CLASS (evd_peer_parent_class)->dispose (obj); } static void evd_peer_finalize (GObject *obj) { EvdPeer *self = EVD_PEER (obj); g_timer_destroy (self->priv->idle_timer); g_queue_foreach (self->priv->backlog, free_backlog_frame, NULL); g_queue_free (self->priv->backlog); g_free (self->priv->id); G_OBJECT_CLASS (evd_peer_parent_class)->finalize (obj); } static void evd_peer_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdPeer *self; self = EVD_PEER (obj); switch (prop_id) { case PROP_TRANSPORT: self->priv->transport = EVD_TRANSPORT (g_value_dup_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_peer_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdPeer *self; self = EVD_PEER (obj); switch (prop_id) { case PROP_ID: g_value_set_string (value, evd_peer_get_id (self)); break; case PROP_TRANSPORT: g_value_set_object (value, G_OBJECT (self->priv->transport)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void free_backlog_frame (gpointer data, gpointer user_data) { BacklogFrame *frame = data; if (frame->buf != NULL) g_free (frame->buf); g_slice_free (BacklogFrame, frame); } static BacklogFrame * create_new_backlog_frame (const gchar *message, gsize size, EvdMessageType type) { BacklogFrame *frame; frame = g_slice_new0 (BacklogFrame); frame->type = type; frame->len = size; frame->buf = g_new (gchar, size + 1); memcpy (frame->buf, message, size); frame->buf[size] = '\0'; return frame; } /* public methods */ const gchar * evd_peer_get_id (EvdPeer *self) { g_return_val_if_fail (EVD_IS_PEER (self), NULL); return self->priv->id; } /** * evd_peer_get_transport: * * Returns: (transfer none): **/ EvdTransport * evd_peer_get_transport (EvdPeer *self) { g_return_val_if_fail (EVD_IS_PEER (self), NULL); return self->priv->transport; } gboolean evd_peer_backlog_push_frame (EvdPeer *self, const gchar *frame, gsize size, GError **error) { return evd_peer_push_message (self, frame, size, EVD_MESSAGE_TYPE_TEXT, error); } gboolean evd_peer_backlog_unshift_frame (EvdPeer *self, const gchar *frame, gsize size, GError **error) { return evd_peer_unshift_message (self, frame, size, EVD_MESSAGE_TYPE_TEXT, error); } gchar * evd_peer_backlog_pop_frame (EvdPeer *self, gsize *size) { return evd_peer_pop_message (self, size, NULL); } guint evd_peer_backlog_get_length (EvdPeer *self) { g_return_val_if_fail (EVD_IS_PEER (self), 0); return g_queue_get_length (self->priv->backlog); } void evd_peer_touch (EvdPeer *self) { g_return_if_fail (EVD_IS_PEER (self)); g_timer_start (self->priv->idle_timer); } gboolean evd_peer_is_alive (EvdPeer *self) { g_return_val_if_fail (EVD_IS_PEER (self), FALSE); return ! self->priv->closed && (g_timer_elapsed (self->priv->idle_timer, NULL) <= self->priv->timeout_interval || evd_transport_peer_is_connected (self->priv->transport, self)); } gboolean evd_peer_is_closed (EvdPeer *self) { g_return_val_if_fail (EVD_IS_PEER (self), FALSE); return self->priv->closed; } gboolean evd_peer_send (EvdPeer *self, const gchar *buffer, gsize size, GError **error) { g_return_val_if_fail (EVD_IS_PEER (self), FALSE); return evd_transport_send (self->priv->transport, self, buffer, size, error); } gboolean evd_peer_send_text (EvdPeer *self, const gchar *buffer, GError **error) { g_return_val_if_fail (EVD_IS_PEER (self), FALSE); return evd_transport_send_text (self->priv->transport, self, buffer, error); } void evd_peer_close (EvdPeer *self, gboolean gracefully) { g_return_if_fail (EVD_IS_PEER (self)); if (! self->priv->closed) { self->priv->closed = TRUE; evd_transport_close_peer (self->priv->transport, self, gracefully, NULL); } } /** * evd_peer_push_message: * * Returns: * * Since: 0.1.20 **/ gboolean evd_peer_push_message (EvdPeer *self, const gchar *message, gsize size, EvdMessageType type, GError **error) { BacklogFrame *frame; g_return_val_if_fail (EVD_IS_PEER (self), FALSE); g_return_val_if_fail (message != NULL, FALSE); /* TODO: check backlog limits here */ frame = create_new_backlog_frame (message, size, type); g_queue_push_tail (self->priv->backlog, frame); return TRUE; } /** * evd_peer_unshift_message: * * Returns: * * Since: 0.1.20 **/ gboolean evd_peer_unshift_message (EvdPeer *self, const gchar *message, gsize size, EvdMessageType type, GError **error) { BacklogFrame *frame; g_return_val_if_fail (EVD_IS_PEER (self), FALSE); g_return_val_if_fail (message != NULL, FALSE); if (size == 0) return TRUE; /* TODO: check backlog limits here */ frame = create_new_backlog_frame (message, size, type); g_queue_push_head (self->priv->backlog, frame); return TRUE; } /** * evd_peer_pop_message: * @size: (allow-none): * @type: (allow-none): * * Returns: (transfer full): * * Since: 0.1.20 **/ gchar * evd_peer_pop_message (EvdPeer *self, gsize *size, EvdMessageType *type) { BacklogFrame *frame; g_return_val_if_fail (EVD_IS_PEER (self), NULL); frame = g_queue_pop_head (self->priv->backlog); if (frame != NULL) { gchar *str; str = frame->buf; if (size != NULL) *size = frame->len; if (type != NULL) *type = frame->type; g_slice_free (BacklogFrame, frame); return str; } else { return NULL; } } EventDance-0.2.0/evd/evd-peer.h000066400000000000000000000116461321356073300161760ustar00rootroot00000000000000/* * evd-peer.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PEER_H__ #define __EVD_PEER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef enum { EVD_MESSAGE_TYPE_BINARY = 0, EVD_MESSAGE_TYPE_TEXT = 1 } EvdMessageType; typedef struct _EvdPeer EvdPeer; typedef struct _EvdPeerClass EvdPeerClass; typedef struct _EvdPeerPrivate EvdPeerPrivate; struct _EvdPeer { GObject parent; EvdPeerPrivate *priv; }; struct _EvdPeerClass { GObjectClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_PEER (evd_peer_get_type ()) #define EVD_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_PEER, EvdPeer)) #define EVD_PEER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_PEER, EvdPeerClass)) #define EVD_IS_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_PEER)) #define EVD_IS_PEER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_PEER)) #define EVD_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_PEER, EvdPeerClass)) GType evd_peer_get_type (void) G_GNUC_CONST; const gchar * evd_peer_get_id (EvdPeer *self); gboolean evd_peer_backlog_push_frame (EvdPeer *self, const gchar *frame, gsize size, GError **error) G_GNUC_DEPRECATED_FOR('evd_peer_push_message'); gboolean evd_peer_backlog_unshift_frame (EvdPeer *self, const gchar *frame, gsize size, GError **error) G_GNUC_DEPRECATED_FOR('evd_peer_unshift_message'); gchar * evd_peer_backlog_pop_frame (EvdPeer *self, gsize *size) G_GNUC_DEPRECATED_FOR('evd_peer_pop_message'); guint evd_peer_backlog_get_length (EvdPeer *self); void evd_peer_touch (EvdPeer *self); gboolean evd_peer_is_alive (EvdPeer *self); gboolean evd_peer_is_closed (EvdPeer *self); gboolean evd_peer_send (EvdPeer *self, const gchar *buffer, gsize size, GError **error); gboolean evd_peer_send_text (EvdPeer *self, const gchar *buffer, GError **error); void evd_peer_close (EvdPeer *self, gboolean gracefully); gboolean evd_peer_push_message (EvdPeer *self, const gchar *message, gsize size, EvdMessageType type, GError **error); gchar * evd_peer_pop_message (EvdPeer *self, gsize *size, EvdMessageType *type); gboolean evd_peer_unshift_message (EvdPeer *self, const gchar *message, gsize size, EvdMessageType type, GError **error); G_END_DECLS #endif /* __EVD_PEER_H__ */ EventDance-0.2.0/evd/evd-pki-common.h000066400000000000000000000022151321356073300173040ustar00rootroot00000000000000/* * evd-pki-common.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PKI_COMMON_H__ #define __EVD_PKI_COMMON_H__ #include G_BEGIN_DECLS /* PKI key algorithms */ typedef enum { EVD_PKI_KEY_TYPE_UNKNOWN = GNUTLS_PK_UNKNOWN, EVD_PKI_KEY_TYPE_RSA = GNUTLS_PK_RSA, EVD_PKI_KEY_TYPE_DSA = GNUTLS_PK_DSA, EVD_PKI_KEY_TYPE_DH = GNUTLS_PK_DH, EVD_PKI_KEY_TYPE_EC = GNUTLS_PK_EC } EvdPkiKeyType; G_END_DECLS #endif /* __EVD_PKI_COMMON_H__ */ EventDance-0.2.0/evd/evd-pki-privkey.c000066400000000000000000000366671321356073300175220ustar00rootroot00000000000000/* * evd-pki-privkey.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-pki-privkey.h" #include "evd-error.h" G_DEFINE_TYPE (EvdPkiPrivkey, evd_pki_privkey, G_TYPE_OBJECT) #define EVD_PKI_PRIVKEY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_PKI_PRIVKEY, \ EvdPkiPrivkeyPrivate)) /* private data */ struct _EvdPkiPrivkeyPrivate { gnutls_privkey_t key; EvdPkiKeyType type; }; typedef struct { EvdPkiKeyType key_type; guint bits; } GenKeyData; /* properties */ enum { PROP_0, PROP_TYPE }; static void evd_pki_privkey_class_init (EvdPkiPrivkeyClass *class); static void evd_pki_privkey_init (EvdPkiPrivkey *self); static void evd_pki_privkey_finalize (GObject *obj); static void evd_pki_privkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_pki_privkey_class_init (EvdPkiPrivkeyClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_pki_privkey_finalize; obj_class->get_property = evd_pki_privkey_get_property; /* install properties */ g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_uint ("type", "Key type", "The type of private key (RSA, DSA, etc)", EVD_PKI_KEY_TYPE_UNKNOWN, EVD_PKI_KEY_TYPE_DSA, EVD_PKI_KEY_TYPE_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdPkiPrivkeyPrivate)); } static void evd_pki_privkey_init (EvdPkiPrivkey *self) { EvdPkiPrivkeyPrivate *priv; priv = EVD_PKI_PRIVKEY_GET_PRIVATE (self); self->priv = priv; priv->key = NULL; self->priv->type = EVD_PKI_KEY_TYPE_UNKNOWN; } static void evd_pki_privkey_finalize (GObject *obj) { EvdPkiPrivkey *self = EVD_PKI_PRIVKEY (obj); if (self->priv->key != NULL) gnutls_privkey_deinit (self->priv->key); G_OBJECT_CLASS (evd_pki_privkey_parent_class)->finalize (obj); } static void evd_pki_privkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdPkiPrivkey *self; self = EVD_PKI_PRIVKEY (obj); switch (prop_id) { case PROP_TYPE: g_value_set_uint (value, self->priv->type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void decrypt_in_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdPkiPrivkey *self = EVD_PKI_PRIVKEY (object); gint err_code; GError *error = NULL; gnutls_datum_t *data; gnutls_datum_t *msg; data = g_simple_async_result_get_op_res_gpointer (res); msg = g_new (gnutls_datum_t, 1); err_code = gnutls_privkey_decrypt_data (self->priv->key, 0, data, msg); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_take_error (res, error); g_free (msg); } else { g_simple_async_result_set_op_res_gpointer (res, msg, g_free); } g_object_unref (res); } static void sign_in_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdPkiPrivkey *self = EVD_PKI_PRIVKEY (object); gint err_code; GError *error = NULL; gnutls_datum_t *data; gnutls_datum_t *signed_data; data = g_simple_async_result_get_op_res_gpointer (res); signed_data = g_new (gnutls_datum_t, 1); err_code = gnutls_privkey_sign_data (self->priv->key, GNUTLS_DIG_SHA256, 0, data, signed_data); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_take_error (res, error); g_free (signed_data); } else { g_simple_async_result_set_op_res_gpointer (res, signed_data, g_free); } g_object_unref (res); } static void generate_in_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdPkiPrivkey *self = EVD_PKI_PRIVKEY (object); GenKeyData *data; gnutls_x509_privkey_t x509_privkey; gnutls_privkey_t privkey; gint err_code; GError *error = NULL; data = g_simple_async_result_get_op_res_gpointer (res); /* generate X.509 private key */ gnutls_x509_privkey_init (&x509_privkey); err_code = gnutls_x509_privkey_generate (x509_privkey, data->key_type, data->bits, 0); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_take_error (res, error); goto out; } /* import to abstract private key struct */ gnutls_privkey_init (&privkey); err_code = gnutls_privkey_import_x509 (privkey, x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_take_error (res, error); goto out; } /* set the abstract key as the new internal key */ if (self->priv->key != NULL) gnutls_privkey_deinit (self->priv->key); self->priv->key = privkey; out: gnutls_x509_privkey_deinit (x509_privkey); g_object_unref (res); } /* public methods */ EvdPkiPrivkey * evd_pki_privkey_new (void) { return g_object_new (EVD_TYPE_PKI_PRIVKEY, NULL); } EvdPkiKeyType evd_pki_privkey_get_key_type (EvdPkiPrivkey *self) { g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), -1); return self->priv->type; } /** * evd_pki_privkey_import_native: * @privkey: (type guintptr): * **/ gboolean evd_pki_privkey_import_native (EvdPkiPrivkey *self, gnutls_privkey_t privkey, GError **error) { gint err_code; guint bits; EvdPkiKeyType type; g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), FALSE); g_return_val_if_fail (privkey != NULL, FALSE); /* @TODO: check if there are operations pending and return error if so */ type = gnutls_privkey_get_pk_algorithm (privkey, &bits); if (type < 0 && evd_error_propagate_gnutls (err_code, error)) return FALSE; if (self->priv->key != NULL) gnutls_privkey_deinit (self->priv->key); self->priv->key = privkey; self->priv->type = type; return TRUE; } void evd_pki_privkey_decrypt (EvdPkiPrivkey *self, const gchar *data, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; gnutls_datum_t *dec_data; g_return_if_fail (EVD_IS_PKI_PRIVKEY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_pki_privkey_decrypt); if (self->priv->key == NULL) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Private key not initialized"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } dec_data = g_new (gnutls_datum_t, 1); dec_data->data = (guchar *) data; dec_data->size = size; g_simple_async_result_set_op_res_gpointer (res, dec_data, g_free); /* @TODO: use a thread pool to avoid overhead */ g_simple_async_result_run_in_thread (res, decrypt_in_thread, G_PRIORITY_DEFAULT, cancellable); } gchar * evd_pki_privkey_decrypt_finish (EvdPkiPrivkey *self, GAsyncResult *result, gsize *size, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_pki_privkey_decrypt), NULL); if (! g_simple_async_result_propagate_error (res, error)) { gnutls_datum_t *msg; msg = g_simple_async_result_get_op_res_gpointer (res); if (size != NULL) *size = msg->size; return (gchar *) msg->data; } else return NULL; } /** * evd_pki_privkey_sign_data: * * Since: 0.2.0 **/ void evd_pki_privkey_sign_data (EvdPkiPrivkey *self, const gchar *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; gnutls_datum_t *sign_data; g_return_if_fail (EVD_IS_PKI_PRIVKEY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_pki_privkey_sign_data); if (self->priv->key == NULL) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Private key not initialized"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } sign_data = g_new (gnutls_datum_t, 1); sign_data->data = (guchar *) data; sign_data->size = data_size; g_simple_async_result_set_op_res_gpointer (res, sign_data, g_free); /* @TODO: use a thread pool to avoid overhead */ g_simple_async_result_run_in_thread (res, sign_in_thread, G_PRIORITY_DEFAULT, cancellable); } /** * evd_pki_privkey_sign_data_finish: * * Since: 0.2.0 **/ gchar * evd_pki_privkey_sign_data_finish (EvdPkiPrivkey *self, GAsyncResult *result, gsize *size, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_pki_privkey_sign_data), NULL); if (! g_simple_async_result_propagate_error (res, error)) { gnutls_datum_t *data; data = g_simple_async_result_get_op_res_gpointer (res); if (size != NULL) *size = data->size; return (gchar *) data->data; } else return NULL; } /** * evd_pki_privkey_generate: * * Since: 0.2.0 **/ void evd_pki_privkey_generate (EvdPkiPrivkey *self, EvdPkiKeyType key_type, guint bits, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; GenKeyData *data; g_return_if_fail (EVD_IS_PKI_PRIVKEY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_pki_privkey_generate); data = g_new (GenKeyData, 1); data->key_type = key_type; data->bits = bits; g_simple_async_result_set_op_res_gpointer (res, data, g_free); g_simple_async_result_run_in_thread (res, generate_in_thread, G_PRIORITY_DEFAULT, cancellable); } /** * evd_pki_privkey_generate_finish: * * Since: 0.2.0 **/ gboolean evd_pki_privkey_generate_finish (EvdPkiPrivkey *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_pki_privkey_generate), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } /** * evd_pki_privkey_get_public_key: * * Returns: (transfer full): * * Since: 0.2.0 **/ EvdPkiPubkey * evd_pki_privkey_get_public_key (EvdPkiPrivkey *self, GError **error) { gnutls_pubkey_t pubkey; gint err_code; EvdPkiPubkey *result = NULL; g_return_val_if_fail (EVD_IS_PKI_PRIVKEY (self), NULL); if (self->priv->key == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Private key not initialized"); return NULL; } gnutls_pubkey_init (&pubkey); err_code = gnutls_pubkey_import_privkey (pubkey, self->priv->key, GNUTLS_KEY_ENCIPHER_ONLY, 0); if (evd_error_propagate_gnutls (err_code, error)) { gnutls_pubkey_deinit (pubkey); return NULL; } result = evd_pki_pubkey_new (); if (! evd_pki_pubkey_import_native (result, pubkey, error)) { gnutls_pubkey_deinit (pubkey); g_object_unref (result); return NULL; } return result; } EventDance-0.2.0/evd/evd-pki-privkey.h000066400000000000000000000121571321356073300175130ustar00rootroot00000000000000/* * evd-pki-privkey.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PKI_PRIVKEY_H__ #define __EVD_PKI_PRIVKEY_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include "evd-pki-common.h" #include "evd-pki-pubkey.h" G_BEGIN_DECLS typedef struct _EvdPkiPrivkey EvdPkiPrivkey; typedef struct _EvdPkiPrivkeyClass EvdPkiPrivkeyClass; typedef struct _EvdPkiPrivkeyPrivate EvdPkiPrivkeyPrivate; struct _EvdPkiPrivkey { GObject parent; EvdPkiPrivkeyPrivate *priv; }; struct _EvdPkiPrivkeyClass { GObjectClass parent_class; }; #define EVD_TYPE_PKI_PRIVKEY (evd_pki_privkey_get_type ()) #define EVD_PKI_PRIVKEY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_PKI_PRIVKEY, EvdPkiPrivkey)) #define EVD_PKI_PRIVKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_PKI_PRIVKEY, EvdPkiPrivkeyClass)) #define EVD_IS_PKI_PRIVKEY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_PKI_PRIVKEY)) #define EVD_IS_PKI_PRIVKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_PKI_PRIVKEY)) #define EVD_PKI_PRIVKEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_PKI_PRIVKEY, EvdPkiPrivkeyClass)) GType evd_pki_privkey_get_type (void) G_GNUC_CONST; EvdPkiPrivkey * evd_pki_privkey_new (void); EvdPkiKeyType evd_pki_privkey_get_key_type (EvdPkiPrivkey *self); gboolean evd_pki_privkey_import_native (EvdPkiPrivkey *self, gnutls_privkey_t privkey, GError **error); void evd_pki_privkey_decrypt (EvdPkiPrivkey *self, const gchar *data, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar * evd_pki_privkey_decrypt_finish (EvdPkiPrivkey *self, GAsyncResult *result, gsize *size, GError **error); void evd_pki_privkey_sign_data (EvdPkiPrivkey *self, const gchar *data, gsize data_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar * evd_pki_privkey_sign_data_finish (EvdPkiPrivkey *self, GAsyncResult *result, gsize *size, GError **error); void evd_pki_privkey_generate (EvdPkiPrivkey *self, EvdPkiKeyType key_type, guint bits, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_pki_privkey_generate_finish (EvdPkiPrivkey *self, GAsyncResult *result, GError **error); EvdPkiPubkey * evd_pki_privkey_get_public_key (EvdPkiPrivkey *self, GError **error); G_END_DECLS #endif /* __EVD_PKI_PRIVKEY_H__ */ EventDance-0.2.0/evd/evd-pki-pubkey.c000066400000000000000000000265231321356073300173160ustar00rootroot00000000000000/* * evd-pki-pubkey.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-pki-pubkey.h" #include "evd-error.h" G_DEFINE_TYPE (EvdPkiPubkey, evd_pki_pubkey, G_TYPE_OBJECT) #define EVD_PKI_PUBKEY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_PKI_PUBKEY, \ EvdPkiPubkeyPrivate)) /* private data */ struct _EvdPkiPubkeyPrivate { gnutls_pubkey_t key; EvdPkiKeyType type; }; typedef struct { gnutls_datum_t data; gnutls_datum_t signature; } VerifyData; /* properties */ enum { PROP_0, PROP_TYPE }; static void evd_pki_pubkey_class_init (EvdPkiPubkeyClass *class); static void evd_pki_pubkey_init (EvdPkiPubkey *self); static void evd_pki_pubkey_finalize (GObject *obj); static void evd_pki_pubkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_pki_pubkey_class_init (EvdPkiPubkeyClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_pki_pubkey_finalize; obj_class->get_property = evd_pki_pubkey_get_property; /* install properties */ g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_uint ("type", "Key type", "The type of private key (RSA, DSA, etc)", EVD_PKI_KEY_TYPE_UNKNOWN, EVD_PKI_KEY_TYPE_DSA, EVD_PKI_KEY_TYPE_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdPkiPubkeyPrivate)); } static void evd_pki_pubkey_init (EvdPkiPubkey *self) { EvdPkiPubkeyPrivate *priv; priv = EVD_PKI_PUBKEY_GET_PRIVATE (self); self->priv = priv; priv->key = NULL; self->priv->type = EVD_PKI_KEY_TYPE_UNKNOWN; } static void evd_pki_pubkey_finalize (GObject *obj) { EvdPkiPubkey *self = EVD_PKI_PUBKEY (obj); if (self->priv->key != NULL) gnutls_pubkey_deinit (self->priv->key); G_OBJECT_CLASS (evd_pki_pubkey_parent_class)->finalize (obj); } static void evd_pki_pubkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdPkiPubkey *self; self = EVD_PKI_PUBKEY (obj); switch (prop_id) { case PROP_TYPE: g_value_set_uint (value, self->priv->type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void encrypt_in_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdPkiPubkey *self = EVD_PKI_PUBKEY (object);; gnutls_datum_t *clear_data; gnutls_datum_t *enc_data; gint err_code; GError *error = NULL; clear_data = g_simple_async_result_get_op_res_gpointer (res); enc_data = g_new (gnutls_datum_t, 1); /* encrypt */ err_code = gnutls_pubkey_encrypt_data (self->priv->key, 0, clear_data, enc_data); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_take_error (res, error); g_free (enc_data); } else { g_simple_async_result_set_op_res_gpointer (res, enc_data, g_free); } g_object_unref (res); } static void verify_in_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdPkiPubkey *self = EVD_PKI_PUBKEY (object);; VerifyData *verify_data; gint err_code; GError *error = NULL; gnutls_sign_algorithm_t sign_algo; verify_data = g_simple_async_result_get_op_res_gpointer (res); /* verify */ switch (self->priv->type) { case GNUTLS_PK_RSA: sign_algo = GNUTLS_SIGN_RSA_SHA256; break; case GNUTLS_PK_DSA: sign_algo = GNUTLS_SIGN_DSA_SHA256; break; case GNUTLS_PK_EC: sign_algo = GNUTLS_SIGN_ECDSA_SHA256; break; default: sign_algo = GNUTLS_SIGN_UNKNOWN; } err_code = gnutls_pubkey_verify_data2 (self->priv->key, sign_algo, 0, &verify_data->data, &verify_data->signature); if (err_code < 0 && evd_error_propagate_gnutls (err_code, &error)) g_simple_async_result_take_error (res, error); g_object_unref (res); } /* public methods */ EvdPkiPubkey * evd_pki_pubkey_new (void) { return g_object_new (EVD_TYPE_PKI_PUBKEY, NULL); } EvdPkiKeyType evd_pki_pubkey_get_key_type (EvdPkiPubkey *self) { g_return_val_if_fail (EVD_IS_PKI_PUBKEY (self), -1); return self->priv->type; } /** * evd_pki_pubkey_import_native: * @pubkey: (type guintptr): * **/ gboolean evd_pki_pubkey_import_native (EvdPkiPubkey *self, gnutls_pubkey_t pubkey, GError **error) { EvdPkiKeyType type; gint err_code; guint bits; g_return_val_if_fail (EVD_IS_PKI_PUBKEY (self), FALSE); g_return_val_if_fail (pubkey != NULL, FALSE); /* @TODO: check if there are operations pending and return error if so */ type = gnutls_pubkey_get_pk_algorithm (pubkey, &bits); if (type < 0 && evd_error_propagate_gnutls (err_code, error)) return FALSE; if (self->priv->key != NULL) gnutls_pubkey_deinit (self->priv->key); self->priv->key = pubkey; self->priv->type = type; return TRUE; } void evd_pki_pubkey_encrypt (EvdPkiPubkey *self, const gchar *data, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; gnutls_datum_t *enc_data; g_return_if_fail (EVD_IS_PKI_PUBKEY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_pki_pubkey_encrypt); if (self->priv->key == NULL) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Public key not initialized"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } enc_data = g_new (gnutls_datum_t, 1); enc_data->data = (guchar *) data; enc_data->size = size; g_simple_async_result_set_op_res_gpointer (res, enc_data, g_free); /* @TODO: use a thread pool to avoid overhead */ g_simple_async_result_run_in_thread (res, encrypt_in_thread, G_PRIORITY_DEFAULT, cancellable); } gchar * evd_pki_pubkey_encrypt_finish (EvdPkiPubkey *self, GAsyncResult *result, gsize *size, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); g_return_val_if_fail (EVD_IS_PKI_PUBKEY (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_pki_pubkey_encrypt), NULL); if (! g_simple_async_result_propagate_error (res, error)) { gnutls_datum_t *data; data = g_simple_async_result_get_op_res_gpointer (res); if (size != NULL) *size = data->size; return (gchar *) data->data; } else return NULL; } /** * evd_pki_pubkey_verify_data: * * Since: 0.2.0 **/ void evd_pki_pubkey_verify_data (EvdPkiPubkey *self, const gchar *data, gsize data_size, const gchar *signature, gsize signature_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; VerifyData *verify_data; g_return_if_fail (EVD_IS_PKI_PUBKEY (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_pki_pubkey_verify_data); if (self->priv->key == NULL) { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Public key not initialized"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } verify_data = g_new (VerifyData, 1); verify_data->data.data = (guchar *) data; verify_data->data.size = data_size; verify_data->signature.data = (guchar *) signature; verify_data->signature.size = signature_size; g_simple_async_result_set_op_res_gpointer (res, verify_data, g_free); /* @TODO: use a thread pool to avoid overhead */ g_simple_async_result_run_in_thread (res, verify_in_thread, G_PRIORITY_DEFAULT, cancellable); } /** * evd_pki_pubkey_verify_data_finish: * * Since: 0.2.0 **/ gboolean evd_pki_pubkey_verify_data_finish (EvdPkiPubkey *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result); g_return_val_if_fail (EVD_IS_PKI_PUBKEY (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_pki_pubkey_verify_data), FALSE); return ! g_simple_async_result_propagate_error (res, error); } EventDance-0.2.0/evd/evd-pki-pubkey.h000066400000000000000000000102671321356073300173210ustar00rootroot00000000000000/* * evd-pki-pubkey.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PKI_PUBKEY_H__ #define __EVD_PKI_PUBKEY_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include "evd-pki-common.h" G_BEGIN_DECLS typedef struct _EvdPkiPubkey EvdPkiPubkey; typedef struct _EvdPkiPubkeyClass EvdPkiPubkeyClass; typedef struct _EvdPkiPubkeyPrivate EvdPkiPubkeyPrivate; struct _EvdPkiPubkey { GObject parent; EvdPkiPubkeyPrivate *priv; }; struct _EvdPkiPubkeyClass { GObjectClass parent_class; }; #define EVD_TYPE_PKI_PUBKEY (evd_pki_pubkey_get_type ()) #define EVD_PKI_PUBKEY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_PKI_PUBKEY, EvdPkiPubkey)) #define EVD_PKI_PUBKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_PKI_PUBKEY, EvdPkiPubkeyClass)) #define EVD_IS_PKI_PUBKEY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_PKI_PUBKEY)) #define EVD_IS_PKI_PUBKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_PKI_PUBKEY)) #define EVD_PKI_PUBKEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_PKI_PUBKEY, EvdPkiPubkeyClass)) GType evd_pki_pubkey_get_type (void) G_GNUC_CONST; EvdPkiPubkey * evd_pki_pubkey_new (void); EvdPkiKeyType evd_pki_pubkey_get_key_type (EvdPkiPubkey *self); gboolean evd_pki_pubkey_import_native (EvdPkiPubkey *self, gnutls_pubkey_t pubkey, GError **error); void evd_pki_pubkey_encrypt (EvdPkiPubkey *self, const gchar *data, gsize size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gchar * evd_pki_pubkey_encrypt_finish (EvdPkiPubkey *self, GAsyncResult *result, gsize *size, GError **error); void evd_pki_pubkey_verify_data (EvdPkiPubkey *self, const gchar *data, gsize data_size, const gchar *signature, gsize signature_size, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_pki_pubkey_verify_data_finish (EvdPkiPubkey *self, GAsyncResult *result, GError **error); G_END_DECLS #endif /* __EVD_PKI_PUBKEY_H__ */ EventDance-0.2.0/evd/evd-poll.c000066400000000000000000000352721321356073300162050ustar00rootroot00000000000000/* * evd-poll.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include #include "evd-poll.h" #include "evd-error.h" #include "evd-utils.h" #define DEFAULT_MAX_FDS 1000 /* maximum number of file descriptors to poll */ G_DEFINE_TYPE (EvdPoll, evd_poll, G_TYPE_OBJECT) #define EVD_POLL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_POLL, \ EvdPollPrivate)) /* private data */ struct _EvdPollPrivate { gint epoll_fd; GThread *thread; gboolean started; guint max_fds; GMainLoop *main_loop; struct epoll_event events[DEFAULT_MAX_FDS]; gint nr_events; gint interrupt_fds[2]; }; struct _EvdPollSession { gint ref_count; EvdPoll *self; gint fd; GIOCondition cond_in; GIOCondition cond_out; GMainContext *main_context; guint priority; EvdPollCallback callback; gpointer user_data; GDestroyNotify user_data_free_func; gint src_id; }; G_LOCK_DEFINE_STATIC (epoll_mutex); G_LOCK_DEFINE_STATIC (interrupt_mutex); static EvdPoll *evd_poll_default = NULL; static void evd_poll_class_init (EvdPollClass *class); static void evd_poll_init (EvdPoll *self); static void evd_poll_finalize (GObject *obj); static void evd_poll_stop (EvdPoll *self); static gboolean evd_poll_epoll_ctl (EvdPoll *self, gint fd, gint op, GIOCondition cond, gpointer data); static void evd_poll_class_init (EvdPollClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_poll_finalize; g_type_class_add_private (obj_class, sizeof (EvdPollPrivate)); } static void evd_poll_init (EvdPoll *self) { EvdPollPrivate *priv; priv = EVD_POLL_GET_PRIVATE (self); self->priv = priv; priv->started = FALSE; priv->thread = NULL; priv->max_fds = DEFAULT_MAX_FDS; priv->main_loop = NULL; } static void evd_poll_finalize (GObject *obj) { EvdPoll *self = EVD_POLL (obj); evd_poll_stop (self); if (self->priv->main_loop != NULL) g_main_loop_unref (self->priv->main_loop); G_OBJECT_CLASS (evd_poll_parent_class)->finalize (obj); G_LOCK (epoll_mutex); if (self == evd_poll_default) evd_poll_default = NULL; G_UNLOCK (epoll_mutex); } static void evd_poll_session_ref (EvdPollSession *session) { #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_atomic_int_exchange_and_add (&session->ref_count, 1); #else g_atomic_int_add (&session->ref_count, 1); #endif } static gboolean evd_poll_session_unref (EvdPollSession *session) { gint old_ref; old_ref = g_atomic_int_get (&session->ref_count); if (old_ref > 1) { g_atomic_int_compare_and_exchange (&session->ref_count, old_ref, old_ref - 1); return TRUE; } else { if (session->src_id != 0) { g_source_remove (session->src_id); session->src_id = 0; } g_main_context_unref (session->main_context); if (session->user_data != NULL && session->user_data_free_func != NULL) session->user_data_free_func (session->user_data); g_slice_free (EvdPollSession, session); return FALSE; } } static gboolean evd_poll_callback_wrapper (gpointer user_data) { EvdPollSession *session; GIOCondition cond_out = 0; EvdPollCallback callback = NULL; G_LOCK (epoll_mutex); session = (EvdPollSession *) user_data; session->src_id = 0; if (evd_poll_session_unref (session)) { callback = session->callback; cond_out = session->cond_out; session->cond_out = 0; } G_UNLOCK (epoll_mutex); if (callback != NULL) callback (session->self, cond_out, session->user_data); return FALSE; } static gboolean evd_poll_check_and_consume_interrupt_event (EvdPoll *self, struct epoll_event event) { if (* (gint *) event.data.ptr == self->priv->interrupt_fds[0]) { static gchar buf[1024]; /* self-pipe trick, here we just read long enough to guarantee that pipe is empty, and next call to write will edge-trigger a read event again */ return read (self->priv->interrupt_fds[0], buf, 1024) > 0; } else { return FALSE; } } static gboolean evd_poll_dispatch (gpointer user_data) { EvdPoll *self = EVD_POLL (user_data); gint i; gint nfds; gboolean started; struct epoll_event *events; G_LOCK (interrupt_mutex); self->priv->nr_events = epoll_wait (self->priv->epoll_fd, self->priv->events, self->priv->max_fds, -1); G_UNLOCK (interrupt_mutex); G_LOCK (epoll_mutex); events = self->priv->events; nfds = self->priv->nr_events; started = self->priv->started; if (started && nfds > 0) for (i=0; i < nfds; i++) { EvdPollSession *session; GIOCondition cond = 0; session = (EvdPollSession *) events[i].data.ptr; if (session == NULL || evd_poll_check_and_consume_interrupt_event (self, events[i]) || ! evd_poll_session_unref (session)) { continue; } else { evd_poll_session_ref (session); } if ( (events[i].events & EPOLLIN) > 0 || (events[i].events & EPOLLPRI) > 0) cond |= G_IO_IN; if (events[i].events & EPOLLOUT) cond |= G_IO_OUT; if ( (events[i].events & EPOLLHUP) > 0 || (events[i].events & EPOLLRDHUP) > 0) cond |= G_IO_HUP; if (events[i].events & EPOLLERR) cond |= G_IO_ERR; session->cond_out |= cond; if (session->src_id == 0) { evd_poll_session_ref (session); session->src_id = evd_timeout_add (session->main_context, 0, session->priority, evd_poll_callback_wrapper, session); } } self->priv->nr_events = 0; G_UNLOCK (epoll_mutex); return started; } static gpointer evd_poll_thread_loop (gpointer data) { EvdPoll *self = data; GMainContext *main_context; main_context = g_main_context_new (); g_main_context_push_thread_default (main_context); self->priv->main_loop = g_main_loop_new (main_context, FALSE); g_main_context_unref (main_context); evd_timeout_add (main_context, 0, G_PRIORITY_HIGH, evd_poll_dispatch, self); g_main_loop_run (self->priv->main_loop); g_main_context_pop_thread_default (main_context); g_main_loop_unref (self->priv->main_loop); self->priv->main_loop = NULL; return NULL; } static gboolean evd_poll_start (EvdPoll *self, GError **error) { self->priv->started = TRUE; if ( (self->priv->epoll_fd = epoll_create (DEFAULT_MAX_FDS)) == -1) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create epoll set"); return FALSE; } errno = 0; if (pipe (self->priv->interrupt_fds) != 0 || ! evd_poll_epoll_ctl (self, self->priv->interrupt_fds[0], EPOLL_CTL_ADD, G_IO_IN | G_IO_OUT, &self->priv->interrupt_fds[0])) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to setup epoll's interrupt pipe"); return FALSE; } #if (! GLIB_CHECK_VERSION(2, 31, 0)) if (! g_thread_get_initialized ()) g_thread_init (NULL); self->priv->thread = g_thread_create (evd_poll_thread_loop, (gpointer) self, TRUE, error); #else self->priv->thread = g_thread_new ("EvdPollThread", evd_poll_thread_loop, self); #endif return self->priv->thread != NULL; } static gboolean evd_poll_epoll_ctl (EvdPoll *self, gint fd, gint op, GIOCondition cond, gpointer data) { gboolean result; if (op == EPOLL_CTL_DEL) { result = epoll_ctl (self->priv->epoll_fd, EPOLL_CTL_DEL, fd, NULL) != -1; } else { struct epoll_event ev = { 0 }; ev.events = EPOLLET | EPOLLRDHUP; if (cond & G_IO_IN) ev.events |= EPOLLIN | EPOLLPRI; if (cond & G_IO_OUT) ev.events |= EPOLLOUT; ev.data.fd = fd; ev.data.ptr = (void *) data; result = (epoll_ctl (self->priv->epoll_fd, op, fd, &ev) == 0); } return result; } static gboolean evd_poll_interrupt_epoll_wait (EvdPoll *self) { const gchar *buf = " "; /* self-pipe trick, here we write to pipe to wake up epoll_wait */ return write (self->priv->interrupt_fds[1], buf, 1) > 0; } static void evd_poll_stop (EvdPoll *self) { G_LOCK (epoll_mutex); self->priv->started = FALSE; evd_poll_interrupt_epoll_wait (self); if (self->priv->main_loop != NULL) g_main_loop_quit (self->priv->main_loop); G_UNLOCK (epoll_mutex); if (self->priv->thread != NULL) { g_thread_join (self->priv->thread); self->priv->thread = NULL; } close (self->priv->epoll_fd); self->priv->epoll_fd = 0; close (self->priv->interrupt_fds[0]); close (self->priv->interrupt_fds[1]); } /* public methods */ EvdPoll * evd_poll_new (void) { EvdPoll *self; self = g_object_new (EVD_TYPE_POLL, NULL); return self; } /** * evd_poll_get_default: * * Returns: (transfer full): **/ EvdPoll * evd_poll_get_default (void) { G_LOCK (epoll_mutex); if (evd_poll_default == NULL) evd_poll_default = evd_poll_new (); else g_object_ref (evd_poll_default); G_UNLOCK (epoll_mutex); return evd_poll_default; } /** * evd_poll_add: * * Returns: (type any) (transfer none): **/ EvdPollSession * evd_poll_add (EvdPoll *self, gint fd, GIOCondition condition, guint priority, EvdPollCallback callback, gpointer user_data, GDestroyNotify user_data_free_func, GError **error) { EvdPollSession *session; g_return_val_if_fail (EVD_IS_POLL (self), NULL); g_return_val_if_fail (fd > 0, NULL); g_return_val_if_fail (callback != NULL, NULL); G_LOCK (epoll_mutex); if (! self->priv->started) if (! evd_poll_start (self, error)) return NULL; session = g_slice_new0 (EvdPollSession); session->ref_count = 1; session->self = self; session->fd = fd; session->cond_in = condition; session->cond_out = 0; session->main_context = g_main_context_get_thread_default (); if (session->main_context == NULL) session->main_context = g_main_context_default (); g_main_context_ref (session->main_context); session->priority = priority; session->callback = callback; session->user_data = user_data; session->user_data_free_func = user_data_free_func; session->src_id = 0; if (! evd_poll_epoll_ctl (self, fd, EPOLL_CTL_ADD, condition, session)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to add file descriptor to epoll set"); evd_poll_session_unref (session); session = NULL; } else { evd_poll_session_ref (session); } G_UNLOCK (epoll_mutex); return session; } gboolean evd_poll_mod (EvdPoll *self, EvdPollSession *session, GIOCondition condition, guint priority, GError **error) { gboolean result = TRUE; g_return_val_if_fail (EVD_IS_POLL (self), FALSE); g_return_val_if_fail (session != NULL, FALSE); G_LOCK (epoll_mutex); session->priority = priority; if (session->cond_in != condition) { session->cond_in = condition; if (! evd_poll_epoll_ctl (self, session->fd, EPOLL_CTL_MOD, condition, session)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to modify watched conditions in epoll set"); result = FALSE; } } G_UNLOCK (epoll_mutex); return result; } gboolean evd_poll_del (EvdPoll *self, EvdPollSession *session, GError **error) { gboolean result; gint i; g_return_val_if_fail (EVD_IS_POLL (self), FALSE); g_return_val_if_fail (session != NULL, FALSE); G_LOCK (epoll_mutex); if (! G_TRYLOCK (interrupt_mutex)) { evd_poll_interrupt_epoll_wait (self); G_LOCK (interrupt_mutex); } for (i = 0; i < self->priv->nr_events; i++) if (self->priv->events[i].data.ptr == session) self->priv->events[i].data.ptr = NULL; if (session->src_id != 0) { g_source_remove (session->src_id); session->src_id = 0; evd_poll_session_unref (session); } session->callback = NULL; if (evd_poll_epoll_ctl (self, session->fd, EPOLL_CTL_DEL, 0, NULL)) { evd_poll_session_unref (session); result = TRUE; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to delete file descriptor from epoll set"); result = FALSE; } evd_poll_session_unref (session); G_UNLOCK (interrupt_mutex); G_UNLOCK (epoll_mutex); return result; } EventDance-0.2.0/evd/evd-poll.h000066400000000000000000000061071321356073300162050ustar00rootroot00000000000000/* * evd-poll.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_POLL_H__ #define __EVD_POLL_H__ #include G_BEGIN_DECLS typedef struct _EvdPoll EvdPoll; typedef struct _EvdPollClass EvdPollClass; typedef struct _EvdPollPrivate EvdPollPrivate; typedef struct _EvdPollSession EvdPollSession; typedef GIOCondition (* EvdPollCallback) (EvdPoll *self, GIOCondition condition, gpointer user_data); struct _EvdPoll { GObject parent; EvdPollPrivate *priv; }; struct _EvdPollClass { GObjectClass parent_class; }; #define EVD_TYPE_POLL (evd_poll_get_type ()) #define EVD_POLL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_POLL, EvdPoll)) #define EVD_POLL_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_POLL, EvdPollClass)) #define EVD_IS_POLL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_POLL)) #define EVD_IS_POLL_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_POLL)) #define EVD_POLL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_POLL, EvdPollClass)) GType evd_poll_get_type (void) G_GNUC_CONST; EvdPoll *evd_poll_new (void); EvdPoll *evd_poll_get_default (void); EvdPollSession *evd_poll_add (EvdPoll *self, gint fd, GIOCondition condition, guint priority, EvdPollCallback callback, gpointer user_data, GDestroyNotify user_data_free_func, GError **error); gboolean evd_poll_mod (EvdPoll *self, EvdPollSession *session, GIOCondition condition, guint priority, GError **error); gboolean evd_poll_del (EvdPoll *self, EvdPollSession *session, GError **error); G_END_DECLS #endif /* __EVD_POLL_H__ */ EventDance-0.2.0/evd/evd-promise.c000066400000000000000000000565431321356073300167210ustar00rootroot00000000000000/* * evd-promise.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ /** * SECTION:evd-promise * @short_description: Promises asynchronous pattern * @stability: Unstable * * The deferred/promise asynchronous pattern is similar to, and compatible with, * GIO's #GAsyncResult model. However, it allows for new functionality that is * currently not easy to implement with GIO's model. * * Specifically, promises allow for several callbacks to observe the completion * of an asynchronous operation. It also simplifies the code for cases when * an application action depends on the completion of several asynchronous * operations. * * It works as follows: * * First, the object that performs the asynchronous operation creates a deferred * object with evd_deferred_new(). Every deferred object has an #EvdPromise * object associated that can be retrieved with evd_deferred_get_promise(). * * An #EvdPromise object represents the future completion of the deferred * operation, and is immediately returned to the application. The promise * cannot resolve the operation by itself, only the deferred object can. * The application can then register one or more callbacks (or none) using * evd_promise_then(), to get notified when the operation completes. Callbacks * can be attached even after the operation completed, in which case they are * called immediately on the next even loop cycle. The result value held by a * resolved promise remains immutable until the object is destroyed. * * The #EvdDeferred object is kept private during the implementation of the * asynchronous operation (e.g, as with #GSimpleAsyncResult). * * When the operation completes, a set of convenient methods are provided * by #EvdDeferred to set the result of the operation: * evd_deferred_set_result_pointer(), evd_deferred_set_result_size(), * evd_deferred_set_result_boolean() and evd_deferred_take_result_error(). These * methods work similarly to the g_simple_async_result_set_op_res_*() and * g_simple_async_result_*_error() family. * * After the result has been set, evd_deferred_complete() or * evd_deferred_complete_in_idle() must be called to notify the application * that the promise has been resolved and the operation completed. * * If callbacks were attached to the promise, these will be called in order upon * completion of the operation. To retrieve the result, a set of methods are * provided by #EvdPromise: evd_promise_get_result_pointer(), * evd_promise_get_result_size(), evd_promise_get_result_boolean() and * evd_promise_propagate_error(). Calling these methods before the operation * completes returns an undefined value. * * If a cancellable object was provided by the application when launching * the asynchronous operation, then it can be cancelled using * evd_promise_cancel(). Alternatively, it can be retrieved from the promise * with evd_promise_get_cancellable() (e.g, to give it to another asynchronous * operation). **/ /** * EvdPromiseClass: * @parent_class: The parent class * * The class for #EvdPromise objects. **/ /** * EvdDeferred: * * An opaque structure that represents a deferred object. **/ #include "evd-promise.h" #define WARN_IF_NOT_COMPLETED(promise) if (!promise->priv->completed) \ g_warning ("Getting the result from an unresolved promise") #define EVD_PROMISE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_PROMISE, \ EvdPromisePrivate)) typedef void (* ResolvePointer) (EvdPromise *self, gpointer data, GDestroyNotify data_free_func); typedef void (* ResolveSize) (EvdPromise *self, gssize size); typedef void (* ResolveBoolean) (EvdPromise *self, gboolean bool); typedef void (* Reject) (EvdPromise *self, GError *error); typedef struct { ResolvePointer resolve_pointer; ResolveSize resolve_size; ResolveBoolean resolve_boolean; Reject reject; } ResolveFuncs; typedef struct { EvdPromise *promise; GAsyncReadyCallback callback; gpointer user_data; } PromiseClosure; struct _EvdPromisePrivate { gboolean completed; GObject *src_obj; gpointer tag; GCancellable *cancellable; gpointer user_data; gpointer res_pointer; GDestroyNotify res_pointer_free_func; gssize res_size; gboolean res_boolean; GError *res_error; ResolveFuncs *resolve_funcs; GList *listeners; }; struct _EvdDeferred { gint ref_count; gboolean completed; EvdPromise *promise; ResolveFuncs *resolve_funcs; }; static void evd_promise_class_init (EvdPromiseClass *class); static void evd_promise_init (EvdPromise *self); static void evd_promise_finalize (GObject *obj); static void evd_promise_dispose (GObject *obj); static void async_result_iface_init (GAsyncResultIface *iface); static gpointer async_result_get_user_data (GAsyncResult *res); static GObject * async_result_get_source_object (GAsyncResult *res); static gboolean async_result_is_tagged (GAsyncResult *res, gpointer source_tag); static void resolve_pointer_real (EvdPromise *self, gpointer data, GDestroyNotify data_free_func); static void resolve_size_real (EvdPromise *self, gssize size); static void resolve_boolean_real (EvdPromise *self, gboolean bool); static void reject_real (EvdPromise *self, GError *error); static void free_promise_closure (PromiseClosure *closure); G_DEFINE_TYPE_WITH_CODE (EvdPromise, evd_promise, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, async_result_iface_init)); G_DEFINE_BOXED_TYPE (EvdDeferred, evd_deferred, evd_deferred_ref, evd_deferred_unref) static void evd_promise_class_init (EvdPromiseClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_promise_dispose; obj_class->finalize = evd_promise_finalize; g_type_class_add_private (obj_class, sizeof (EvdPromisePrivate)); } static void async_result_iface_init (GAsyncResultIface *iface) { iface->get_user_data = async_result_get_user_data; iface->get_source_object = async_result_get_source_object; iface->is_tagged = async_result_is_tagged; } static void evd_promise_init (EvdPromise *self) { EvdPromisePrivate *priv; priv = EVD_PROMISE_GET_PRIVATE (self); self->priv = priv; priv->completed = FALSE; priv->src_obj = NULL; priv->tag = NULL; priv->cancellable = NULL; priv->res_pointer = NULL; priv->res_pointer_free_func = NULL; priv->res_size = 0; priv->res_boolean = FALSE; priv->res_error = NULL; priv->resolve_funcs = g_new0 (ResolveFuncs, 1); priv->resolve_funcs->resolve_pointer = resolve_pointer_real; priv->resolve_funcs->resolve_size = resolve_size_real; priv->resolve_funcs->resolve_boolean = resolve_boolean_real; priv->resolve_funcs->reject = reject_real; priv->listeners = NULL; } static void evd_promise_dispose (GObject *obj) { EvdPromise *self = EVD_PROMISE (obj); if (self->priv->res_pointer != NULL && self->priv->res_pointer_free_func != NULL) { self->priv->res_pointer_free_func (self->priv->res_pointer); self->priv->res_pointer = NULL; } if (self->priv->listeners != NULL) { g_list_free_full (self->priv->listeners, (GDestroyNotify) free_promise_closure); self->priv->listeners = NULL; } if (self->priv->src_obj != NULL) { g_object_unref (self->priv->src_obj); self->priv->src_obj = NULL; } G_OBJECT_CLASS (evd_promise_parent_class)->dispose (obj); } static void evd_promise_finalize (GObject *obj) { EvdPromise *self = EVD_PROMISE (obj); g_free (self->priv->resolve_funcs); if (self->priv->res_error != NULL) g_error_free (self->priv->res_error); G_OBJECT_CLASS (evd_promise_parent_class)->finalize (obj); } static gpointer async_result_get_user_data (GAsyncResult *res) { EvdPromise *self = EVD_PROMISE (res); return self->priv->user_data; } static GObject * async_result_get_source_object (GAsyncResult *res) { EvdPromise *self = EVD_PROMISE (res); if (self->priv->src_obj != NULL) return g_object_ref (self->priv->src_obj); else return NULL; } static gboolean async_result_is_tagged (GAsyncResult *res, gpointer source_tag) { EvdPromise *self = EVD_PROMISE (res); return self->priv->tag == source_tag; } static ResolveFuncs * steal_resolve_funcs (EvdPromise *self) { ResolveFuncs *result; result = self->priv->resolve_funcs; self->priv->resolve_funcs = NULL; return result; } static void free_promise_closure (PromiseClosure *closure) { g_object_unref (closure->promise); g_free (closure); } static void evd_promise_notify_completion (EvdPromise *self) { GList *node; self->priv->completed = TRUE; node = self->priv->listeners; while (node != NULL) { PromiseClosure *closure; closure = node->data; /* this is to make g_async_result_get_user_data() work */ self->priv->user_data = closure->user_data; closure->callback (closure->promise->priv->src_obj, G_ASYNC_RESULT (self), closure->user_data); free_promise_closure (closure); node = g_list_next (node); } g_list_free (self->priv->listeners); self->priv->listeners = NULL; } static void resolve_pointer_real (EvdPromise *self, gpointer data, GDestroyNotify data_free_func) { g_return_if_fail (! self->priv->completed); self->priv->res_pointer = data; self->priv->res_pointer_free_func = data_free_func; } static void resolve_size_real (EvdPromise *self, gssize size) { g_return_if_fail (! self->priv->completed); self->priv->res_size = size; } static void resolve_boolean_real (EvdPromise *self, gboolean bool) { g_return_if_fail (! self->priv->completed); self->priv->res_boolean = bool; } static void reject_real (EvdPromise *self, GError *error) { g_return_if_fail (! self->priv->completed); self->priv->res_error = error; } static EvdPromise * evd_promise_new (GObject *source_object, GCancellable *cancellable, gpointer tag) { EvdPromise *self; self = g_object_new (EVD_TYPE_PROMISE, NULL); if (source_object != NULL) self->priv->src_obj = g_object_ref (source_object); if (cancellable != NULL) self->priv->cancellable = cancellable; self->priv->tag = tag; return self; } static gboolean call_listener_in_idle (gpointer user_data) { PromiseClosure *closure = user_data; closure->callback (closure->promise->priv->src_obj, G_ASYNC_RESULT (closure->promise), closure->user_data); free_promise_closure (closure); return FALSE; } static gboolean deferred_complete_in_idle_cb (gpointer user_data) { EvdDeferred *self = user_data; evd_promise_notify_completion (self->promise); return FALSE; } static void deferred_free (EvdDeferred *self) { g_object_unref (self->promise); g_free (self); } /* public methods */ /** * evd_promise_then: * @self: An #EvdPromise object * @callback: (scope async): Function to call when the promise is resolved * @user_data: (allow-none): Application data to pass in @callback * * Adds a new listener function to the asynchronous operation represented by the * promise. If the operation has not yet completed, @callback will be called * together with all the other listeners as soon as it completes, in the * same order as the listeners were added. If the operation already completed, * @callback will be called immediately on the next turn of the event loop. **/ void evd_promise_then (EvdPromise *self, GAsyncReadyCallback callback, gpointer user_data) { PromiseClosure *closure; g_return_if_fail (EVD_IS_PROMISE (self)); g_return_if_fail (callback != NULL); closure = g_new0 (PromiseClosure, 1); closure->promise = g_object_ref (self); closure->callback = callback; closure->user_data = user_data; /* this is to make g_async_result_get_user_data() work */ self->priv->user_data = user_data; if (self->priv->completed) g_idle_add (call_listener_in_idle, closure); else self->priv->listeners = g_list_append (self->priv->listeners, closure); } /** * evd_promise_get_result_pointer: * @self: An #EvdPromise object * * Retrieves the result of the asynchronous operation represented by the * promise if it is held as a gpointer, otherwise returns %NULL. It is an * error to call this method before the promise has been resolved. * * Returns: (transfer none): The result of the operation as a gpointer, or %NULL **/ gpointer evd_promise_get_result_pointer (EvdPromise *self) { g_return_val_if_fail (EVD_IS_PROMISE (self), NULL); WARN_IF_NOT_COMPLETED(self); return self->priv->res_pointer; } /** * evd_promise_get_result_size: * @self: An #EvdPromise object * * Retrieves the result of the asynchronous operation represented by the * promise if it is held as gssize, otherwise returns zero. It is an * error to call this method before the promise has been resolved. * * Returns: (transfer none): The result of the operation as a gssize, or zero **/ gssize evd_promise_get_result_size (EvdPromise *self) { g_return_val_if_fail (EVD_IS_PROMISE (self), NULL); WARN_IF_NOT_COMPLETED(self); return self->priv->res_size; } /** * evd_promise_get_result_boolean: * @self: An #EvdPromise object * * Retrieves the result of the asynchronous operation represented by the * promise if it is held as gboolean, otherwise returns %FALSE. It is an * error to call this method before the promise has been resolved. * * Returns: (transfer none): The result of the operation as a gboolean, * or %FALSE **/ gboolean evd_promise_get_result_boolean (EvdPromise *self) { g_return_val_if_fail (EVD_IS_PROMISE (self), NULL); WARN_IF_NOT_COMPLETED(self); return self->priv->res_boolean; } /** * evd_promise_propagate_error: * @self: An #EvdPromise object * @error: (allow-none): A pointer to a #GError to retrieve the error, or %NULL * * Tells whether an asynchronous operation failed, in which case %TRUE is * returned and the resulting error copied into @error. Otherwise returns %FALSE. * * It is an error to call this method before the promise has been resolved. * * Returns: %TRUE if an error was propagated, %FALSE otherwise **/ gboolean evd_promise_propagate_error (EvdPromise *self, GError **error) { g_return_val_if_fail (EVD_IS_PROMISE (self), NULL); WARN_IF_NOT_COMPLETED(self); if (self->priv->res_error == NULL) return FALSE; g_propagate_error (error, g_error_copy (self->priv->res_error)); return TRUE; } /** * evd_promise_cancel: * @self: An #EvdPromise object * * Cancels the asynchronous operation represented by the promise by calling * g_cancellable_cancel() on the #GCancellable associated with the promise, * if it is not %NULL. **/ void evd_promise_cancel (EvdPromise *self) { g_return_if_fail (EVD_IS_PROMISE (self)); if (self->priv->cancellable == NULL) return; g_cancellable_cancel (self->priv->cancellable); } /** * evd_promise_get_cancellable: * @self: An #EvdPromise object * * Obtains the #GCancellable object associated with the promise, which can * be %NULL. Normally, the cancellable is passed to the function that triggered * the asynchronous operation represented by the promise. * * Returns: (transfer none): The #GCancellable, or %NULL **/ GCancellable * evd_promise_get_cancellable (EvdPromise *self) { g_return_val_if_fail (EVD_IS_PROMISE (self), NULL); return self->priv->cancellable; } /** * evd_deferred_new: * @source_object: (allow-none): The #GObject performing the async operation, * or %NULL * @cancellable: (allow-none): A #GCancellable object, or %NULL * @tag: (allow-none): An arbitrary pointer identifying the async operation, * or %NULL * * Creates a new deferred object to track the execution of an * asynchronous operation. It works like #GSimpleAsyncResult, but with some * important differences. * * #EvdDeferred does not represent itself the result of the asynchronous * operation. Instead, it delegates on #EvdPromise, which is a * #GAsyncResult, all the functionality except the ability to set the result * and complete the operation. This way, the #EvdPromise can be made public * to the application, while only the #EvdDeferred object is kept private in * the implementation as with #GSimpleAsyncResult. * * An #EvdDeferred and its associated #EvdPromise are bound together so that * only a deferred object can resolve or reject its associated * promise. The promise of a deferred object can be obtained with * evd_deferred_get_promise(). * * Returns: (transfer full): A new #EvdDeferred, to be freed with * evd_deferred_unref() **/ EvdDeferred * evd_deferred_new (GObject *source_object, GCancellable *cancellable, gpointer tag) { EvdDeferred *self; g_return_val_if_fail (G_IS_OBJECT (source_object) || source_object == NULL, NULL); g_return_val_if_fail (G_IS_CANCELLABLE (cancellable) || cancellable == NULL, NULL); self = g_new0 (EvdDeferred, 1); self->ref_count = 1; self->completed = FALSE; self->promise = evd_promise_new (source_object, cancellable, tag); self->resolve_funcs = steal_resolve_funcs (self->promise); return self; } /** * evd_deferred_ref: * @self: An #EvdDeferred object * * Increases the reference count of the deferred object. * * Returns: (transfer full): The same #EvdDeferred object **/ EvdDeferred * evd_deferred_ref (EvdDeferred *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (self->ref_count > 0, NULL); g_atomic_int_add (&self->ref_count, 1); return self; } /** * evd_deferred_unref: * @self: An #EvdDeferred object * * Decreases the reference count of the deferred. If it reaches * zero, the object is destroyed and all its memory released. **/ void evd_deferred_unref (EvdDeferred *self) { gint old_ref; g_return_if_fail (self != NULL); g_return_if_fail (self->ref_count > 0); old_ref = g_atomic_int_get (&self->ref_count); if (old_ref > 1) g_atomic_int_compare_and_exchange (&self->ref_count, old_ref, old_ref - 1); else deferred_free (self); } /** * evd_deferred_get_promise: * @self: An #EvdDeferred object * * Retrieves the promise object associated with the deferred. * * Returns: (transfer none): The #EvdPromise, owned by the deferred object **/ EvdPromise * evd_deferred_get_promise (EvdDeferred *self) { g_return_val_if_fail (self != NULL, NULL); return self->promise; } /** * evd_deferred_set_result_pointer: * @self: An #EvdDeferred object * @data: (allow-none): The result of the async operation as a gpointer * @data_free_func: (allow-none): #GDestroyNotify callback to free @data, * or %NULL * * Sets the result of the asynchronous operation as an arbitrary pointer of * data. @data_free_func, if provided, will be called when @data is no longer * used. * * This method does not completes the operation. evd_deferred_complete() or * evd_deferred_complete_in_idle() should be called after for that purpose. **/ void evd_deferred_set_result_pointer (EvdDeferred *self, gpointer data, GDestroyNotify data_free_func) { g_return_if_fail (self != NULL); self->resolve_funcs->resolve_pointer (self->promise, data, data_free_func); } /** * evd_deferred_set_result_size: * @self: An #EvdDeferred object * @size: The result of the async operation as a gssize * * Sets the result of the asynchronous operation as a long signed integer, * useful for size results. * * This method does not completes the operation. evd_deferred_complete() or * evd_deferred_complete_in_idle() should be called after for that purpose. **/ void evd_deferred_set_result_size (EvdDeferred *self, gssize size) { g_return_if_fail (self != NULL); self->resolve_funcs->resolve_size (self->promise, size); } /** * evd_deferred_set_result_boolean: * @self: An #EvdDeferred object * @bool: (allow-none): The result of the async operation as a gboolean * * Sets the result of the asynchronous operation as a boolean value. * * This method does not completes the operation. evd_deferred_complete() or * evd_deferred_complete_in_idle() should be called after for that purpose. **/ void evd_deferred_set_result_boolean (EvdDeferred *self, gboolean bool) { g_return_if_fail (self != NULL); self->resolve_funcs->resolve_boolean (self->promise, bool); } /** * evd_deferred_take_result_error: * @self: An #EvdDeferred object * @error: The result of the async operation as a #GError * * Sets the result of the asynchronous operation as an error, indicating that * the operation failed. The deferred object takes ownership of @error. * * This method does not completes the operation. evd_deferred_complete() or * evd_deferred_complete_in_idle() should be called after for that purpose. **/ void evd_deferred_take_result_error (EvdDeferred *self, GError *error) { g_return_if_fail (self != NULL); self->resolve_funcs->reject (self->promise, error); } /** * evd_deferred_complete: * @self: An #EvdDeferred object * * Completes the asynchronous operation represented by the deferred object, * immediately calling all the listener callbacks added to the associated * #EvdPromise object. * * This method must not be used if the operation is completed on the same * event loop cycle. For those cases, evd_deferred_complete_in_idle() * is provided. **/ void evd_deferred_complete (EvdDeferred *self) { g_return_if_fail (self != NULL); if (self->completed) return; self->completed = TRUE; evd_promise_notify_completion (self->promise); } /** * evd_deferred_complete_in_idle: * @self: An #EvdDeferred object * * Works as evd_deferred_complete(), but defers the actual completion * to the next event loop cycle. **/ void evd_deferred_complete_in_idle (EvdDeferred *self) { g_return_if_fail (self != NULL); if (self->completed) return; self->completed = TRUE; g_idle_add (deferred_complete_in_idle_cb, self); } EventDance-0.2.0/evd/evd-promise.h000066400000000000000000000076021321356073300167160ustar00rootroot00000000000000/* * evd-promise.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_PROMISE_H__ #define __EVD_PROMISE_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef struct _EvdPromise EvdPromise; typedef struct _EvdPromiseClass EvdPromiseClass; typedef struct _EvdPromisePrivate EvdPromisePrivate; typedef struct _EvdDeferred EvdDeferred; struct _EvdPromise { GObject parent; EvdPromisePrivate *priv; }; struct _EvdPromiseClass { GObjectClass parent_class; }; #define EVD_TYPE_PROMISE (evd_promise_get_type ()) #define EVD_PROMISE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_PROMISE, EvdPromise)) #define EVD_PROMISE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_PROMISE, EvdPromiseClass)) #define EVD_IS_PROMISE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_PROMISE)) #define EVD_IS_PROMISE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_PROMISE)) #define EVD_PROMISE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_PROMISE, EvdPromiseClass)) GType evd_promise_get_type (void) G_GNUC_CONST; void evd_promise_then (EvdPromise *self, GAsyncReadyCallback callback, gpointer user_data); gpointer evd_promise_get_result_pointer (EvdPromise *self); gssize evd_promise_get_result_size (EvdPromise *self); gboolean evd_promise_get_result_boolean (EvdPromise *self); gboolean evd_promise_propagate_error (EvdPromise *self, GError **error); GCancellable * evd_promise_get_cancellable (EvdPromise *self); void evd_promise_cancel (EvdPromise *self); #define EVD_TYPE_DEFERRED (evd_deferred_get_type ()) GType evd_deferred_get_type (void); EvdDeferred * evd_deferred_new (GObject *source_object, GCancellable *cancellable, gpointer tag); EvdDeferred * evd_deferred_ref (EvdDeferred *self); void evd_deferred_unref (EvdDeferred *self); EvdPromise * evd_deferred_get_promise (EvdDeferred *self); void evd_deferred_set_result_pointer (EvdDeferred *self, gpointer data, GDestroyNotify data_free_func); void evd_deferred_set_result_size (EvdDeferred *self, gssize size); void evd_deferred_set_result_boolean (EvdDeferred *self, gboolean bool); void evd_deferred_take_result_error (EvdDeferred *self, GError *error); void evd_deferred_complete (EvdDeferred *self); void evd_deferred_complete_in_idle (EvdDeferred *self); G_END_DECLS #endif /* __EVD_PROMISE_H__ */ EventDance-0.2.0/evd/evd-reproxy.c000066400000000000000000000312351321356073300167420ustar00rootroot00000000000000/* * evd-reproxy.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-reproxy.h" #include "evd-utils.h" #include "evd-buffered-input-stream.h" #include "evd-connection.h" G_DEFINE_TYPE (EvdReproxy, evd_reproxy, EVD_TYPE_SERVICE) #define EVD_REPROXY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_REPROXY, \ EvdReproxyPrivate)) #define DEFAULT_BACKEND_MIN_CONNS 1 #define DEFAULT_BACKEND_MAX_CONNS 2 #define BRIDGE_BLOCK_SIZE 8193 #define BRIDGE_DATA_KEY "org.eventdance.lib.reproxy.bridge" typedef struct _EvdReproxySocketData EvdReproxySocketData; /* private data */ struct _EvdReproxyPrivate { GList *backends; GList *next_backend_node; guint backend_max_conns; guint backend_min_conns; GQueue *conns; }; typedef struct { EvdConnection *conn; gchar *buf; gsize size; } EvdReproxyBridge; static void evd_reproxy_class_init (EvdReproxyClass *class); static void evd_reproxy_init (EvdReproxy *self); static void evd_reproxy_finalize (GObject *obj); static void evd_reproxy_dispose (GObject *obj); static void evd_reproxy_connection_accepted (EvdService *service, EvdConnection *conn); static gboolean evd_reproxy_bridge_read (gpointer user_data); static void evd_reproxy_class_init (EvdReproxyClass *class) { GObjectClass *obj_class; EvdServiceClass *service_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_reproxy_dispose; obj_class->finalize = evd_reproxy_finalize; service_class = EVD_SERVICE_CLASS (class); service_class->connection_accepted = evd_reproxy_connection_accepted; /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdReproxyPrivate)); } static void evd_reproxy_init (EvdReproxy *self) { EvdReproxyPrivate *priv; priv = EVD_REPROXY_GET_PRIVATE (self); self->priv = priv; /* initialize private members */ priv->backends = NULL; priv->backend_min_conns = DEFAULT_BACKEND_MIN_CONNS; priv->backend_max_conns = DEFAULT_BACKEND_MAX_CONNS; priv->next_backend_node = NULL; priv->conns = g_queue_new (); } static void evd_reproxy_free_backend (gpointer data, gpointer user_data) { EvdConnectionPool *backend = EVD_CONNECTION_POOL (data); g_object_unref (backend); } static void evd_reproxy_free_connection (gpointer data, gpointer user_data) { EvdConnection *conn = EVD_CONNECTION (data); g_object_unref (conn); } static void evd_reproxy_dispose (GObject *obj) { EvdReproxy *self = EVD_REPROXY (obj); g_list_foreach (self->priv->backends, evd_reproxy_free_backend, NULL); g_list_free (self->priv->backends); self->priv->backends = NULL; G_OBJECT_CLASS (evd_reproxy_parent_class)->dispose (obj); } static void evd_reproxy_finalize (GObject *obj) { EvdReproxy *self = EVD_REPROXY (obj); g_queue_foreach (self->priv->conns, evd_reproxy_free_connection, NULL); g_queue_free (self->priv->conns); G_OBJECT_CLASS (evd_reproxy_parent_class)->finalize (obj); } static EvdConnectionPool * evd_reproxy_get_backend_from_node (GList *backend_node) { return (backend_node != NULL) ? EVD_CONNECTION_POOL (backend_node->data) : NULL; } static GList * evd_reproxy_get_next_backend_node (EvdReproxy *self, GList *backend_node) { if (backend_node != NULL) { if (backend_node->next != NULL) return backend_node->next; else return self->priv->backends; } else { return NULL; } } static void evd_reproxy_hop_backend (EvdReproxy *self) { self->priv->next_backend_node = evd_reproxy_get_next_backend_node (self, self->priv->next_backend_node); } static void evd_reproxy_enqueue_connection (EvdReproxy *self, EvdConnection *conn) { g_object_ref (conn); g_queue_push_tail (self->priv->conns, (gpointer) conn); } static EvdConnectionPool * evd_reproxy_get_backend_with_free_connections (EvdReproxy *self) { EvdConnectionPool *backend; GList *orig_node; if (self->priv->next_backend_node == NULL) return NULL; orig_node = self->priv->next_backend_node; do { backend = evd_reproxy_get_backend_from_node (self->priv->next_backend_node); if (evd_connection_pool_has_free_connections (backend)) return backend; else evd_reproxy_hop_backend (self); } while (self->priv->next_backend_node != orig_node); return NULL; } static gboolean evd_reproxy_bridge_write (gpointer user_data) { EvdConnection *conn0 = EVD_CONNECTION (user_data); EvdReproxyBridge *bridge; GOutputStream *stream; gssize out_size; GError *error = NULL; bridge = g_object_get_data (G_OBJECT (conn0), BRIDGE_DATA_KEY); stream = g_io_stream_get_output_stream (G_IO_STREAM (bridge->conn)); if ( (out_size = g_output_stream_write (stream, bridge->buf, bridge->size, NULL, &error)) >= 0) { if (out_size < bridge->size) { GInputStream *input_stream; input_stream = g_io_stream_get_input_stream (G_IO_STREAM (conn0)); evd_buffered_input_stream_unread (EVD_BUFFERED_INPUT_STREAM (input_stream), bridge->buf + out_size, bridge->size - out_size, NULL, &error); } else { evd_reproxy_bridge_read (conn0); } } if (error != NULL) { g_debug ("error: %s", error->message); g_error_free (error); } return FALSE; } static void evd_reproxy_bridge_on_read (GObject *obj, GAsyncResult *res, gpointer user_data) { GError *error = NULL; EvdConnection *conn0 = EVD_CONNECTION (user_data); EvdReproxyBridge *bridge; gssize size; bridge = g_object_get_data (G_OBJECT (conn0), BRIDGE_DATA_KEY); if ( (size = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error)) > 0) { bridge->size = (gsize) size; evd_reproxy_bridge_write (conn0); } else if (size < 0) { g_debug ("reproxy read error: %s", error->message); g_error_free (error); } } static gboolean evd_reproxy_bridge_read (gpointer user_data) { EvdConnection *conn0 = EVD_CONNECTION (user_data); EvdReproxyBridge *bridge; bridge = g_object_get_data (G_OBJECT (conn0), BRIDGE_DATA_KEY); if (evd_connection_get_max_writable (bridge->conn) > 0) { GInputStream *stream; stream = g_io_stream_get_input_stream (G_IO_STREAM (conn0)); if (! g_input_stream_has_pending (stream)) g_input_stream_read_async (stream, bridge->buf, BRIDGE_BLOCK_SIZE, evd_connection_get_priority (conn0), NULL, evd_reproxy_bridge_on_read, conn0); } else { evd_connection_lock_close (EVD_CONNECTION (conn0)); } return FALSE; } static void evd_reproxy_bridge_on_write (EvdConnection *bridge, gpointer user_data) { evd_connection_unlock_close (EVD_CONNECTION (user_data)); evd_reproxy_bridge_read (EVD_CONNECTION (user_data)); } static void evd_reproxy_connection_setup_bridge (EvdReproxy *self, EvdConnection *conn0, EvdConnection *conn1) { EvdReproxyBridge *bridge; bridge = g_new0 (EvdReproxyBridge, 1); bridge->conn = conn1; g_object_ref (conn1); bridge->buf = g_new (gchar, BRIDGE_BLOCK_SIZE); g_object_set_data (G_OBJECT (conn0), BRIDGE_DATA_KEY, bridge); g_signal_connect (conn1, "write", G_CALLBACK (evd_reproxy_bridge_on_write), conn0); evd_reproxy_bridge_read (conn0); } static void evd_reproxy_connection_on_flush (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdReproxyBridge *bridge = (EvdReproxyBridge *) user_data; EvdConnection *conn; conn = bridge->conn; g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), res, NULL); if (! g_io_stream_is_closed (G_IO_STREAM (conn))) g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); g_free (bridge->buf); g_free (bridge); g_object_unref (conn); } static void evd_reproxy_connection_on_close (EvdConnection *conn, gpointer user_data) { EvdReproxyBridge *bridge; GOutputStream *stream; bridge = g_object_get_data (G_OBJECT (conn), BRIDGE_DATA_KEY); stream = g_io_stream_get_output_stream (G_IO_STREAM (bridge->conn)); g_output_stream_flush_async (stream, evd_connection_get_priority (bridge->conn), NULL, evd_reproxy_connection_on_flush, bridge); } static void evd_reproxy_backend_on_connection (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdReproxy *self = EVD_REPROXY (user_data); EvdConnection *conn1; GError *error = NULL; if ( (conn1 = evd_connection_pool_get_connection_finish (EVD_CONNECTION_POOL (obj), res, &error)) != NULL) { EvdConnection *conn0; g_signal_connect (conn1, "close", G_CALLBACK (evd_reproxy_connection_on_close), self); conn0 = g_queue_pop_head (self->priv->conns); g_signal_connect (conn0, "close", G_CALLBACK (evd_reproxy_connection_on_close), self); evd_reproxy_connection_setup_bridge (self, conn0, conn1); evd_reproxy_connection_setup_bridge (self, conn1, conn0); g_object_unref (conn0); } else { g_debug ("reproxy new conn error: %s", error->message); g_error_free (error); /* @TODO: retry, but wisely */ } } static void evd_reproxy_connection_accepted (EvdService *service, EvdConnection *conn) { EvdReproxy *self = EVD_REPROXY (service); EvdConnectionPool *backend; backend = evd_reproxy_get_backend_with_free_connections (self); if (backend == NULL) { backend = evd_reproxy_get_backend_from_node (self->priv->next_backend_node); evd_reproxy_hop_backend (self); } evd_connection_pool_get_connection (backend, NULL, evd_reproxy_backend_on_connection, self); evd_reproxy_enqueue_connection (self, conn); } /* public methods */ EvdReproxy * evd_reproxy_new (void) { EvdReproxy *self; self = g_object_new (EVD_TYPE_REPROXY, NULL); return self; } /** * evd_reproxy_add_backend: * * Returns: (transfer none): An #EvdConnectionPool object representing the new backend. **/ EvdConnectionPool * evd_reproxy_add_backend (EvdReproxy *self, const gchar *address) { EvdConnectionPool *backend; g_return_val_if_fail (EVD_IS_REPROXY (self), NULL); g_return_val_if_fail (address != NULL, NULL); backend = evd_connection_pool_new (address, EVD_TYPE_CONNECTION); self->priv->backends = g_list_append (self->priv->backends, backend); if (self->priv->next_backend_node == NULL) self->priv->next_backend_node = self->priv->backends; return backend; } void evd_reproxy_remove_backend (EvdReproxy *self, EvdConnectionPool *backend) { g_return_if_fail (EVD_IS_REPROXY (self)); g_return_if_fail (EVD_IS_CONNECTION_POOL (self)); self->priv->backends = g_list_remove_all (self->priv->backends, backend); } EventDance-0.2.0/evd/evd-reproxy.h000066400000000000000000000050061321356073300167440ustar00rootroot00000000000000/* * evd-reproxy.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_REPROXY_H__ #define __EVD_REPROXY_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-service.h" #include "evd-connection-pool.h" G_BEGIN_DECLS typedef struct _EvdReproxy EvdReproxy; typedef struct _EvdReproxyClass EvdReproxyClass; typedef struct _EvdReproxyPrivate EvdReproxyPrivate; struct _EvdReproxy { EvdService parent; EvdReproxyPrivate *priv; }; struct _EvdReproxyClass { EvdServiceClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_REPROXY (evd_reproxy_get_type ()) #define EVD_REPROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_REPROXY, EvdReproxy)) #define EVD_REPROXY_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_REPROXY, EvdReproxyClass)) #define EVD_IS_REPROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_REPROXY)) #define EVD_IS_REPROXY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_REPROXY)) #define EVD_REPROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_REPROXY, EvdReproxyClass)) GType evd_reproxy_get_type (void) G_GNUC_CONST; EvdReproxy *evd_reproxy_new (void); EvdConnectionPool *evd_reproxy_add_backend (EvdReproxy *self, const gchar *address); void evd_reproxy_remove_backend (EvdReproxy *self, EvdConnectionPool *backend); G_END_DECLS #endif /* __EVD_REPROXY_H__ */ EventDance-0.2.0/evd/evd-resolver.c000066400000000000000000000172331321356073300170750ustar00rootroot00000000000000/* * evd-resolver.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifdef HAVE_GIO_UNIX #include #endif #include "evd-resolver.h" G_DEFINE_TYPE (EvdResolver, evd_resolver, G_TYPE_OBJECT) typedef struct { guint16 port; GList *addresses; EvdResolver *resolver; } EvdResolverData; static void evd_resolver_class_init (EvdResolverClass *class); static void evd_resolver_init (EvdResolver *self); static void evd_resolver_finalize (GObject *obj); static EvdResolver *evd_resolver_default = NULL; /** * evd_resolver_get_default: * * Returns: (transfer full): **/ EvdResolver * evd_resolver_get_default (void) { if (evd_resolver_default == NULL) evd_resolver_default = evd_resolver_new (); else g_object_ref (evd_resolver_default); return evd_resolver_default; } static void evd_resolver_class_init (EvdResolverClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_resolver_finalize; } static void evd_resolver_init (EvdResolver *self) { } static void evd_resolver_finalize (GObject *obj) { G_OBJECT_CLASS (evd_resolver_parent_class)->finalize (obj); if (obj == G_OBJECT (evd_resolver_default)) evd_resolver_default = NULL; } static void evd_resolver_free_data (gpointer _data) { EvdResolverData *data = (EvdResolverData *) _data; g_object_unref (data->resolver); if (data->addresses != NULL) { g_list_foreach (data->addresses, (GFunc) g_object_unref, NULL); g_list_free (data->addresses); } g_slice_free (EvdResolverData, data); } static void evd_resolver_on_resolver_result (GResolver *resolver, GAsyncResult *async_result, gpointer user_data) { GList *result = NULL; GError *error = NULL; GSimpleAsyncResult *res; EvdResolverData *data; res = G_SIMPLE_ASYNC_RESULT (user_data); data = (EvdResolverData *) g_simple_async_result_get_op_res_gpointer (res); if ((result = g_resolver_lookup_by_name_finish (resolver, async_result, &error)) != NULL) { GList *node = result; GInetAddress *inet_addr; GSocketAddress *addr; while (node != NULL) { inet_addr = G_INET_ADDRESS (node->data); addr = g_inet_socket_address_new (inet_addr, data->port); data->addresses = g_list_append (data->addresses, addr); node = node->next; } g_resolver_free_addresses (result); } else { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_simple_async_result_complete (res); g_object_unref (res); } /* public methods */ EvdResolver * evd_resolver_new (void) { EvdResolver *self; self = g_object_new (EVD_TYPE_RESOLVER, NULL); return self; } void evd_resolver_resolve (EvdResolver *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; EvdResolverData *data; res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_resolver_resolve); data = g_slice_new0 (EvdResolverData); data->resolver = self; g_object_ref (self); g_simple_async_result_set_op_res_gpointer (res, data, evd_resolver_free_data); if (address[0] == '/') { #ifdef HAVE_GIO_UNIX GSocketAddress *addr; /* assume unix address */ /* TODO: improve this detection, seems very naive */ addr = (GSocketAddress *) g_unix_socket_address_new (address); data->addresses = g_list_append (data->addresses, addr); #else g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Unix socket addresses are not supported"); #endif } else { GNetworkAddress *net_addr; GSocketConnectable *connectable; gchar *domain; guint16 port; GError *error = NULL; if ( (connectable = g_network_address_parse (address, 0, &error)) == NULL) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } else { GInetAddress *inet_addr; net_addr = G_NETWORK_ADDRESS (connectable); domain = g_strdup (g_network_address_get_hostname (net_addr)); port = g_network_address_get_port (net_addr); g_object_unref (net_addr); /* at this point we have a valid port, and a host to validate, so let's build the request */ data->port = port; inet_addr = g_inet_address_new_from_string (domain); if (inet_addr != NULL) { GSocketAddress *addr; addr = g_inet_socket_address_new (inet_addr, port); g_object_unref (inet_addr); data->addresses = g_list_append (data->addresses, addr); g_free (domain); } else { g_resolver_lookup_by_name_async (g_resolver_get_default (), domain, cancellable, (GAsyncReadyCallback) evd_resolver_on_resolver_result, (gpointer) res); g_free (domain); return; } } } g_simple_async_result_complete_in_idle (res); g_object_unref (res); } /** * evd_resolver_resolve_finish: * * Returns: (element-type GSocketAddress) (transfer full): **/ GList * evd_resolver_resolve_finish (EvdResolver *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *res; g_return_val_if_fail (EVD_IS_RESOLVER (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_resolver_resolve), NULL); res = G_SIMPLE_ASYNC_RESULT (result); if (! g_simple_async_result_propagate_error (res, error)) { EvdResolverData *data; GList *addresses; data = g_simple_async_result_get_op_res_gpointer (res); addresses = data->addresses; data->addresses = NULL; return addresses; } else { return NULL; } } /** * evd_resolver_free_addresses: * @addresses: (element-type GSocketAddress): **/ void evd_resolver_free_addresses (GList *addresses) { GList *node; node = addresses; while (node != NULL) { GSocketAddress *addr; addr = G_SOCKET_ADDRESS (node->data); g_object_unref (addr); node = node->next; } g_list_free (addresses); } EventDance-0.2.0/evd/evd-resolver.h000066400000000000000000000050541321356073300171000ustar00rootroot00000000000000/* * evd-resolver.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_RESOLVER_H__ #define __EVD_RESOLVER_H__ #include #include G_BEGIN_DECLS typedef struct _EvdResolver EvdResolver; typedef struct _EvdResolverClass EvdResolverClass; struct _EvdResolver { GObject parent; }; struct _EvdResolverClass { GObjectClass parent_class; }; #define EVD_TYPE_RESOLVER (evd_resolver_get_type ()) #define EVD_RESOLVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_RESOLVER, EvdResolver)) #define EVD_RESOLVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_RESOLVER, EvdResolverClass)) #define EVD_IS_RESOLVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_RESOLVER)) #define EVD_IS_RESOLVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_RESOLVER)) #define EVD_RESOLVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_RESOLVER, EvdResolverClass)) GType evd_resolver_get_type (void) G_GNUC_CONST; EvdResolver *evd_resolver_get_default (void); EvdResolver *evd_resolver_new (void); void evd_resolver_resolve (EvdResolver *resolver, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GList *evd_resolver_resolve_finish (EvdResolver *self, GAsyncResult *result, GError **error); void evd_resolver_free_addresses (GList *addresses); G_END_DECLS #endif /* __EVD_RESOLVER_H__ */ EventDance-0.2.0/evd/evd-service.c000066400000000000000000000513001321356073300166650ustar00rootroot00000000000000/* * evd-service.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-service.h" #include "evd-error.h" #include "evd-utils.h" #include "evd-marshal.h" #include "evd-socket.h" #include "evd-tls-session.h" G_DEFINE_TYPE (EvdService, evd_service, EVD_TYPE_IO_STREAM_GROUP) #define EVD_SERVICE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_SERVICE, \ EvdServicePrivate)) #define VALIDATION_HINT_KEY "org.eventdance.lib.Service.VALIDATION_HINT" /* private data */ struct _EvdServicePrivate { GHashTable *listeners; GType io_stream_type; gboolean tls_autostart; EvdTlsCredentials *tls_cred; }; /* signals */ enum { SIGNAL_VALIDATE_CONNECTION, SIGNAL_VALIDATE_TLS_CONNECTION, SIGNAL_LAST }; /* properties */ enum { PROP_0, PROP_TLS_AUTOSTART, PROP_TLS_CREDENTIALS }; static guint evd_service_signals[SIGNAL_LAST] = { 0 }; static void evd_service_class_init (EvdServiceClass *class); static void evd_service_init (EvdService *self); static void evd_service_finalize (GObject *obj); static void evd_service_dispose (GObject *obj); static void evd_service_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_service_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_service_listener_destroy (gpointer listener); static void evd_service_listener_on_new_connection (EvdSocket *listener, EvdConnection *conn, gpointer user_data); static void evd_service_listener_on_close (EvdSocket *listener, gpointer user_data); static void connection_accepted (EvdService *self, EvdConnection *conn); static void connection_rejected (EvdService *self, EvdConnection *conn); static void evd_service_connection_starttls (EvdService *self, EvdConnection *conn); static gboolean evd_service_validate_conn_signal_acc (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data); static gboolean evd_service_add (EvdIoStreamGroup *self, GIOStream *io_stream); static void evd_service_class_init (EvdServiceClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIoStreamGroupClass *conn_group_class = EVD_IO_STREAM_GROUP_CLASS (class); class->connection_accepted = connection_accepted; class->connection_rejected = connection_rejected; obj_class->dispose = evd_service_dispose; obj_class->finalize = evd_service_finalize; obj_class->get_property = evd_service_get_property; obj_class->set_property = evd_service_set_property; conn_group_class->add = evd_service_add; evd_service_signals[SIGNAL_VALIDATE_CONNECTION] = g_signal_new ("validate-connection", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdServiceClass, signal_validate_connection), evd_service_validate_conn_signal_acc, NULL, evd_marshal_UINT__OBJECT, G_TYPE_UINT, 1, EVD_TYPE_CONNECTION); evd_service_signals[SIGNAL_VALIDATE_TLS_CONNECTION] = g_signal_new ("validate-tls-connection", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdServiceClass, signal_validate_tls_connection), evd_service_validate_conn_signal_acc, NULL, evd_marshal_UINT__OBJECT, G_TYPE_UINT, 1, EVD_TYPE_CONNECTION); g_object_class_install_property (obj_class, PROP_TLS_AUTOSTART, g_param_spec_boolean ("tls-autostart", "Autostart TLS in connections", "Returns TRUE if TLS upgrade should be performed automatically upon incoming connections", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TLS_CREDENTIALS, g_param_spec_object ("tls-credentials", "The TLS credentials", "The TLS credentials that will be passed to the connections of the service", EVD_TYPE_TLS_CREDENTIALS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdServicePrivate)); } static void evd_service_init (EvdService *self) { EvdServicePrivate *priv; priv = EVD_SERVICE_GET_PRIVATE (self); self->priv = priv; priv->listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, evd_service_listener_destroy); priv->io_stream_type = EVD_TYPE_CONNECTION; priv->tls_autostart = FALSE; priv->tls_cred = NULL; } static void evd_service_dispose (GObject *obj) { EvdService *self = EVD_SERVICE (obj); if (self->priv->listeners != NULL) { g_hash_table_destroy (self->priv->listeners); self->priv->listeners = NULL; } G_OBJECT_CLASS (evd_service_parent_class)->dispose (obj); } static void evd_service_finalize (GObject *obj) { EvdService *self = EVD_SERVICE (obj); if (self->priv->tls_cred != NULL) g_object_unref (self->priv->tls_cred); G_OBJECT_CLASS (evd_service_parent_class)->finalize (obj); } static void evd_service_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdService *self; self = EVD_SERVICE (obj); switch (prop_id) { case PROP_TLS_AUTOSTART: evd_service_set_tls_autostart (self, g_value_get_boolean (value)); break; case PROP_TLS_CREDENTIALS: evd_service_set_tls_credentials (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_service_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdService *self; self = EVD_SERVICE (obj); switch (prop_id) { case PROP_TLS_AUTOSTART: g_value_set_boolean (value, evd_service_get_tls_autostart (self)); break; case PROP_TLS_CREDENTIALS: g_value_set_object (value, evd_service_get_tls_credentials (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_service_listener_destroy (gpointer listener) { EvdSocket *socket = EVD_SOCKET (listener); EvdService *self; self = g_object_get_data (G_OBJECT (socket), "evd-service"); g_signal_handlers_disconnect_by_func (socket, evd_service_listener_on_new_connection, self); g_signal_handlers_disconnect_by_func (socket, evd_service_listener_on_close, self); g_object_unref (socket); } static void evd_service_listener_on_new_connection (EvdSocket *listener, EvdConnection *conn, gpointer user_data) { EvdService *self = EVD_SERVICE (user_data); evd_io_stream_group_add (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); } static void evd_service_listener_on_close (EvdSocket *listener, gpointer user_data) { EvdService *self = EVD_SERVICE (user_data); evd_service_remove_listener (self, listener); } void connection_accepted (EvdService *self, EvdConnection *conn) { /* nothing to do here other than logging */ } static void connection_rejected (EvdService *self, EvdConnection *conn) { /* refuse connection */ g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); } void accept_connection_priv (EvdService *self, EvdConnection *conn) { if (self->priv->tls_autostart && ! evd_connection_get_tls_active (conn)) { evd_service_connection_starttls (self, conn); } else { EvdServiceClass *class; class = EVD_SERVICE_GET_CLASS (self); if (class->connection_accepted != NULL) class->connection_accepted (self, conn); } } void reject_connection_priv (EvdService *self, EvdConnection *conn) { EvdServiceClass *class; class = EVD_SERVICE_GET_CLASS (self); if (class->connection_rejected != NULL) class->connection_rejected (self, conn); } static gboolean evd_service_validate_conn_signal_acc (GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data) { guint ret; guint acc_ret; ret = g_value_get_uint (handler_return); acc_ret = g_value_get_uint (return_accu); if (ret > acc_ret) g_value_set_uint (return_accu, ret); return ret != EVD_VALIDATE_REJECT; } static void evd_service_validate_tls_connection (EvdService *self, EvdConnection *conn) { EvdValidateEnum ret = EVD_VALIDATE_ACCEPT; g_signal_emit (self, evd_service_signals[SIGNAL_VALIDATE_TLS_CONNECTION], 0, conn, &ret); if (ret == EVD_VALIDATE_ACCEPT) { accept_connection_priv (self, conn); } else if (ret == EVD_VALIDATE_REJECT) { reject_connection_priv (self, conn); } else { gboolean *hint; /* validation is pending, add validation hint to connection */ hint = g_new (gboolean, 1); *hint = TRUE; g_object_set_data_full (G_OBJECT (conn), VALIDATION_HINT_KEY, hint, g_free); g_object_ref (conn); } } static void evd_service_validate_connection (EvdService *self, EvdConnection *conn) { EvdValidateEnum ret = EVD_VALIDATE_ACCEPT; g_signal_emit (self, evd_service_signals[SIGNAL_VALIDATE_CONNECTION], 0, conn, &ret); if (ret == EVD_VALIDATE_ACCEPT) { accept_connection_priv (self, conn); } else if (ret == EVD_VALIDATE_REJECT) { reject_connection_priv (self, conn); } else { gboolean *hint; /* validation is pending, add validation hint to connection */ hint = g_new (gboolean, 1); *hint = TRUE; g_object_set_data_full (G_OBJECT (conn), VALIDATION_HINT_KEY, hint, g_free); g_object_ref (conn); } } static void evd_service_connection_on_tls_started (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdService *self = EVD_SERVICE (user_data); EvdConnection *conn = EVD_CONNECTION (obj); GError *error = NULL; if (evd_connection_starttls_finish (conn, res, &error)) { evd_service_validate_tls_connection (self, conn); } else { g_debug ("TLS upgrade error in EvdService: %s", error->message); g_error_free (error); g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); } } static void evd_service_connection_starttls (EvdService *self, EvdConnection *conn) { EvdTlsSession *tls_session; EvdTlsCredentials *tls_cred; tls_session = evd_connection_get_tls_session (conn); tls_cred = evd_service_get_tls_credentials (self); evd_tls_session_set_credentials (tls_session, tls_cred); evd_connection_starttls (conn, EVD_TLS_MODE_SERVER, NULL, evd_service_connection_on_tls_started, self); } static gboolean evd_service_add (EvdIoStreamGroup *group, GIOStream *io_stream) { EvdService *self = EVD_SERVICE (group); EvdConnection *conn; /* @TODO: implement connection limit */ /* @TODO: check if connection type matches service's io_stream_type */ if (! evd_connection_is_connected (EVD_CONNECTION (io_stream)) || ! EVD_IO_STREAM_GROUP_CLASS (evd_service_parent_class)->add (group, io_stream)) { return FALSE; } conn = EVD_CONNECTION (io_stream); evd_service_validate_connection (self, conn); return TRUE; } static void evd_service_socket_on_listen (GObject *obj, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); EvdService *self; GError *error = NULL; self = EVD_SERVICE (g_async_result_get_source_object (G_ASYNC_RESULT (res))); if (! evd_socket_listen_finish (EVD_SOCKET (obj), result, &error)) { g_simple_async_result_take_error (res, error); } else { evd_service_add_listener (self, EVD_SOCKET (obj)); g_object_unref (obj); } g_simple_async_result_complete (res); g_object_unref (res); /* this is because g_async_result_get_source_object() increases reference count */ g_object_unref (self); } /* public methods */ EvdService * evd_service_new (void) { EvdService *self; self = g_object_new (EVD_TYPE_SERVICE, NULL); return self; } void evd_service_set_tls_autostart (EvdService *self, gboolean autostart) { g_return_if_fail (EVD_IS_SERVICE (self)); self->priv->tls_autostart = autostart; } gboolean evd_service_get_tls_autostart (EvdService *self) { g_return_val_if_fail (EVD_IS_SERVICE (self), FALSE); return self->priv->tls_autostart; } void evd_service_set_tls_credentials (EvdService *self, EvdTlsCredentials *credentials) { g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (EVD_IS_TLS_CREDENTIALS (credentials)); if (self->priv->tls_cred != NULL) g_object_unref (self->priv->tls_cred); self->priv->tls_cred = credentials; g_object_ref (self->priv->tls_cred); } /** * evd_service_get_tls_credentials: * * Returns: (transfer none): The #EvdTlsCredentials object of this session **/ EvdTlsCredentials * evd_service_get_tls_credentials (EvdService *self) { g_return_val_if_fail (EVD_IS_SERVICE (self), NULL); if (self->priv->tls_cred == NULL) self->priv->tls_cred = evd_tls_credentials_new (); return self->priv->tls_cred; } void evd_service_set_io_stream_type (EvdService *self, GType io_stream_type) { g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (g_type_is_a (io_stream_type, EVD_TYPE_CONNECTION)); self->priv->io_stream_type = io_stream_type; } /** * evd_service_get_io_stream_type: * * Returns: (type guint): **/ GType evd_service_get_io_stream_type (EvdService *self) { g_return_val_if_fail (EVD_IS_SERVICE (self), 0); return self->priv->io_stream_type; } void evd_service_add_listener (EvdService *self, EvdSocket *socket) { g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (EVD_IS_SOCKET (socket)); g_object_ref (socket); g_object_set (socket, "io-stream-type", self->priv->io_stream_type, NULL); g_hash_table_insert (self->priv->listeners, (gpointer) socket, (gpointer) socket); g_signal_connect (socket, "new-connection", G_CALLBACK (evd_service_listener_on_new_connection), self); g_signal_connect (socket, "close", G_CALLBACK (evd_service_listener_on_close), self); g_object_set_data (G_OBJECT (socket), "evd-service", self); } gboolean evd_service_remove_listener (EvdService *self, EvdSocket *socket) { g_return_val_if_fail (EVD_IS_SERVICE (self), FALSE); g_return_val_if_fail (EVD_IS_SOCKET (socket), FALSE); return g_hash_table_remove (self->priv->listeners, (gconstpointer) socket); } /** * evd_service_listen: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): **/ void evd_service_listen (EvdService *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { EvdSocket *socket; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (address != NULL); socket = evd_socket_new (); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_service_listen); evd_socket_listen (socket, address, cancellable, evd_service_socket_on_listen, res); } gboolean evd_service_listen_finish (EvdService *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_SERVICE (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_service_listen), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } void evd_service_accept_connection (EvdService *self, EvdConnection *conn) { gboolean *hint; g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (EVD_IS_CONNECTION (self)); /* check that connection is being validated */ hint = g_object_get_data (G_OBJECT (conn), VALIDATION_HINT_KEY); if (hint == NULL || ! *hint) return; /* remove validation hint */ g_object_set_data (G_OBJECT (conn), VALIDATION_HINT_KEY, NULL); accept_connection_priv (self, conn); g_object_unref (conn); } void evd_service_reject_connection (EvdService *self, EvdConnection *conn) { gboolean *hint; g_return_if_fail (EVD_IS_SERVICE (self)); g_return_if_fail (EVD_IS_CONNECTION (self)); /* check that connection is being validated */ hint = g_object_get_data (G_OBJECT (conn), VALIDATION_HINT_KEY); if (hint == NULL || ! *hint) return; /* remove validation hint */ g_object_set_data (G_OBJECT (conn), VALIDATION_HINT_KEY, NULL); reject_connection_priv (self, conn); g_object_unref (conn); } EventDance-0.2.0/evd/evd-service.h000066400000000000000000000115161321356073300166770ustar00rootroot00000000000000/* * evd-service.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_SERVICE_H__ #define __EVD_SERVICE_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-io-stream-group.h" #include "evd-connection.h" #include "evd-tls-credentials.h" G_BEGIN_DECLS typedef struct _EvdService EvdService; typedef struct _EvdServiceClass EvdServiceClass; typedef struct _EvdServicePrivate EvdServicePrivate; struct _EvdService { EvdIoStreamGroup parent; EvdServicePrivate *priv; }; struct _EvdServiceClass { EvdIoStreamGroupClass parent_class; /* virtual methods */ void (* connection_accepted) (EvdService *self, EvdConnection *conn); void (* connection_rejected) (EvdService *self, EvdConnection *conn); /* signal prototypes */ guint (* signal_validate_connection) (EvdService *self, EvdConnection *socket, gpointer user_data); guint (* signal_validate_tls_connection) (EvdService *self, EvdConnection *socket, gpointer user_data); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_SERVICE (evd_service_get_type ()) #define EVD_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_SERVICE, EvdService)) #define EVD_SERVICE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_SERVICE, EvdServiceClass)) #define EVD_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_SERVICE)) #define EVD_IS_SERVICE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_SERVICE)) #define EVD_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_SERVICE, EvdServiceClass)) GType evd_service_get_type (void) G_GNUC_CONST; EvdService *evd_service_new (void); void evd_service_set_tls_autostart (EvdService *self, gboolean autostart); gboolean evd_service_get_tls_autostart (EvdService *self); void evd_service_set_tls_credentials (EvdService *self, EvdTlsCredentials *credentials); EvdTlsCredentials *evd_service_get_tls_credentials (EvdService *self); void evd_service_set_io_stream_type (EvdService *self, GType io_stream_type); GType evd_service_get_io_stream_type (EvdService *self); void evd_service_add_listener (EvdService *self, EvdSocket *socket); gboolean evd_service_remove_listener (EvdService *self, EvdSocket *socket); void evd_service_listen (EvdService *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_service_listen_finish (EvdService *self, GAsyncResult *result, GError **error); void evd_service_accept_connection (EvdService *self, EvdConnection *conn); void evd_service_reject_connection (EvdService *self, EvdConnection *conn); G_END_DECLS #endif /* __EVD_SERVICE_H__ */ EventDance-0.2.0/evd/evd-socket-input-stream.c000066400000000000000000000205651321356073300211540ustar00rootroot00000000000000/* * evd-socket-input-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-error.h" #include "evd-socket-input-stream.h" G_DEFINE_TYPE (EvdSocketInputStream, evd_socket_input_stream, G_TYPE_INPUT_STREAM) #define EVD_SOCKET_INPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_SOCKET_INPUT_STREAM, \ EvdSocketInputStreamPrivate)) /* private data */ struct _EvdSocketInputStreamPrivate { EvdSocket *socket; gchar bag; gboolean has_bag; }; /* signals */ enum { SIGNAL_DRAINED, SIGNAL_LAST }; static guint evd_socket_input_stream_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_SOCKET }; static void evd_socket_input_stream_class_init (EvdSocketInputStreamClass *class); static void evd_socket_input_stream_init (EvdSocketInputStream *self); static void evd_socket_input_stream_finalize (GObject *obj); static void evd_socket_input_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_socket_input_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gssize evd_socket_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_socket_input_stream_class_init (EvdSocketInputStreamClass *class) { GObjectClass *obj_class; GInputStreamClass *input_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_socket_input_stream_finalize; obj_class->get_property = evd_socket_input_stream_get_property; obj_class->set_property = evd_socket_input_stream_set_property; input_stream_class = G_INPUT_STREAM_CLASS (class); input_stream_class->read_fn = evd_socket_input_stream_read; evd_socket_input_stream_signals[SIGNAL_DRAINED] = g_signal_new ("drained", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketInputStreamClass, drained), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_object_class_install_property (obj_class, PROP_SOCKET, g_param_spec_object ("socket", "The socket", "The socket object wrapped by this stream", EVD_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdSocketInputStreamPrivate)); } static void evd_socket_input_stream_init (EvdSocketInputStream *self) { EvdSocketInputStreamPrivate *priv; priv = EVD_SOCKET_INPUT_STREAM_GET_PRIVATE (self); self->priv = priv; priv->bag = 0; priv->has_bag = FALSE; } static void evd_socket_input_stream_finalize (GObject *obj) { EvdSocketInputStream *self = EVD_SOCKET_INPUT_STREAM (obj); g_object_unref (self->priv->socket); G_OBJECT_CLASS (evd_socket_input_stream_parent_class)->finalize (obj); } static void evd_socket_input_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdSocketInputStream *self; self = EVD_SOCKET_INPUT_STREAM (obj); switch (prop_id) { case PROP_SOCKET: evd_socket_input_stream_set_socket (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_socket_input_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdSocketInputStream *self; self = EVD_SOCKET_INPUT_STREAM (obj); switch (prop_id) { case PROP_SOCKET: g_value_set_object (value, evd_socket_input_stream_get_socket (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gssize evd_socket_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdSocketInputStream *self = EVD_SOCKET_INPUT_STREAM (stream); GError *_error = NULL; GSocket *socket; gssize actual_size = 0; gchar *buf; gssize bag_size = 0; gboolean drained = FALSE; buf = (gchar *) buffer; socket = evd_socket_get_socket (self->priv->socket); if (socket == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Input stream socket not initialized"); return -1; } if (self->priv->has_bag) { buf[0] = self->priv->bag; buf = buf + 1; bag_size = 1; } else { size++; } actual_size = g_socket_receive (socket, buf, size, cancellable, &_error); if (actual_size < 0) { if (g_error_matches (_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) drained = TRUE; if (bag_size > 0) { actual_size = 0; self->priv->has_bag = FALSE; g_clear_error (&_error); } else { g_propagate_error (error, _error); } } else if (actual_size < size) { self->priv->has_bag = FALSE; drained = TRUE; } else { self->priv->bag = buf[actual_size-1]; buf[actual_size-1] = '\0'; actual_size--; self->priv->has_bag = TRUE; } if (drained) { g_object_ref (self); g_signal_emit (self, evd_socket_input_stream_signals[SIGNAL_DRAINED], 0, NULL); g_object_unref (self); } return actual_size + bag_size; } /* public methods */ EvdSocketInputStream * evd_socket_input_stream_new (EvdSocket *socket) { EvdSocketInputStream *self; g_return_val_if_fail (EVD_IS_SOCKET (socket), NULL); self = g_object_new (EVD_TYPE_SOCKET_INPUT_STREAM, "socket", socket, NULL); return self; } void evd_socket_input_stream_set_socket (EvdSocketInputStream *self, EvdSocket *socket) { g_return_if_fail (EVD_IS_SOCKET_INPUT_STREAM (self)); g_return_if_fail (EVD_IS_SOCKET (socket)); if (self->priv->socket != NULL) g_object_unref (self->priv->socket); self->priv->socket = socket; g_object_ref (self->priv->socket); } /** * evd_socket_input_stream_get_socket: * * Returns: (transfer none): the #EvdSocket **/ EvdSocket * evd_socket_input_stream_get_socket (EvdSocketInputStream *self) { g_return_val_if_fail (EVD_IS_SOCKET_INPUT_STREAM (self), NULL); return self->priv->socket; } EventDance-0.2.0/evd/evd-socket-input-stream.h000066400000000000000000000051331321356073300211530ustar00rootroot00000000000000/* * evd-socket-input-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_SOCKET_INPUT_STREAM_H__ #define __EVD_SOCKET_INPUT_STREAM_H__ #include #include #include "evd-socket.h" G_BEGIN_DECLS typedef struct _EvdSocketInputStream EvdSocketInputStream; typedef struct _EvdSocketInputStreamClass EvdSocketInputStreamClass; typedef struct _EvdSocketInputStreamPrivate EvdSocketInputStreamPrivate; struct _EvdSocketInputStream { GInputStream parent; EvdSocketInputStreamPrivate *priv; }; struct _EvdSocketInputStreamClass { GInputStreamClass parent_class; /* signal prototypes */ void (* drained) (EvdSocketInputStream *self); }; #define EVD_TYPE_SOCKET_INPUT_STREAM (evd_socket_input_stream_get_type ()) #define EVD_SOCKET_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_SOCKET_INPUT_STREAM, EvdSocketInputStream)) #define EVD_SOCKET_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_SOCKET_INPUT_STREAM, EvdSocketInputStreamClass)) #define EVD_IS_SOCKET_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_SOCKET_INPUT_STREAM)) #define EVD_IS_SOCKET_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_SOCKET_INPUT_STREAM)) #define EVD_SOCKET_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_SOCKET_INPUT_STREAM, EvdSocketInputStreamClass)) GType evd_socket_input_stream_get_type (void) G_GNUC_CONST; EvdSocketInputStream *evd_socket_input_stream_new (EvdSocket *socket); EvdSocket *evd_socket_input_stream_get_socket (EvdSocketInputStream *self); void evd_socket_input_stream_set_socket (EvdSocketInputStream *self, EvdSocket *socket); G_END_DECLS #endif /* __EVD_SOCKET_INPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-socket-output-stream.c000066400000000000000000000174131321356073300213530ustar00rootroot00000000000000/* * evd-socket-output-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-error.h" #include "evd-socket-output-stream.h" G_DEFINE_TYPE (EvdSocketOutputStream, evd_socket_output_stream, G_TYPE_OUTPUT_STREAM) #define EVD_SOCKET_OUTPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_SOCKET_OUTPUT_STREAM, \ EvdSocketOutputStreamPrivate)) /* private data */ struct _EvdSocketOutputStreamPrivate { EvdSocket *socket; }; /* signals */ enum { SIGNAL_FILLED, SIGNAL_LAST }; static guint evd_socket_output_stream_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_SOCKET }; static void evd_socket_output_stream_class_init (EvdSocketOutputStreamClass *class); static void evd_socket_output_stream_init (EvdSocketOutputStream *self); static void evd_socket_output_stream_finalize (GObject *obj); static void evd_socket_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_socket_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gssize evd_socket_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_socket_output_stream_class_init (EvdSocketOutputStreamClass *class) { GObjectClass *obj_class; GOutputStreamClass *output_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_socket_output_stream_finalize; obj_class->get_property = evd_socket_output_stream_get_property; obj_class->set_property = evd_socket_output_stream_set_property; output_stream_class = G_OUTPUT_STREAM_CLASS (class); output_stream_class->write_fn = evd_socket_output_stream_write; evd_socket_output_stream_signals[SIGNAL_FILLED] = g_signal_new ("filled", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketOutputStreamClass, filled), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_object_class_install_property (obj_class, PROP_SOCKET, g_param_spec_object ("socket", "socket", "The socket that this stream wraps", EVD_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdSocketOutputStreamPrivate)); } static void evd_socket_output_stream_init (EvdSocketOutputStream *self) { EvdSocketOutputStreamPrivate *priv; priv = EVD_SOCKET_OUTPUT_STREAM_GET_PRIVATE (self); self->priv = priv; } static void evd_socket_output_stream_finalize (GObject *obj) { EvdSocketOutputStream *self = EVD_SOCKET_OUTPUT_STREAM (obj); g_object_unref (self->priv->socket); G_OBJECT_CLASS (evd_socket_output_stream_parent_class)->finalize (obj); } static void evd_socket_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdSocketOutputStream *self; self = EVD_SOCKET_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_SOCKET: evd_socket_output_stream_set_socket (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_socket_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdSocketOutputStream *self; self = EVD_SOCKET_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_SOCKET: g_value_set_object (value, evd_socket_output_stream_get_socket (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gssize evd_socket_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdSocketOutputStream *self = EVD_SOCKET_OUTPUT_STREAM (stream); GSocket *socket; gssize actual_size = 0; GError *_error = NULL; gboolean filled = FALSE; socket = evd_socket_get_socket (self->priv->socket); if (socket == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Output stream socket not initialized"); return -1; } actual_size = g_socket_send (socket, buffer, size, cancellable, &_error); if (actual_size < 0) { if (g_error_matches (_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) filled = TRUE; g_propagate_error (error, _error); } else if (actual_size < size) { filled = TRUE; } if (filled) { g_object_ref (self); g_signal_emit (self, evd_socket_output_stream_signals[SIGNAL_FILLED], 0, NULL); g_object_unref (self); } return actual_size; } /* public methods */ EvdSocketOutputStream * evd_socket_output_stream_new (EvdSocket *socket) { EvdSocketOutputStream *self; g_return_val_if_fail (EVD_IS_SOCKET (socket), NULL); self = g_object_new (EVD_TYPE_SOCKET_OUTPUT_STREAM, "socket", socket, NULL); return self; } void evd_socket_output_stream_set_socket (EvdSocketOutputStream *self, EvdSocket *socket) { g_return_if_fail (EVD_IS_SOCKET_OUTPUT_STREAM (self)); g_return_if_fail (EVD_IS_SOCKET (socket)); if (self->priv->socket != NULL) g_object_unref (self->priv->socket); self->priv->socket = socket; g_object_ref (self->priv->socket); } /** * evd_socket_output_stream_get_socket: * * Returns: (transfer none): **/ EvdSocket * evd_socket_output_stream_get_socket (EvdSocketOutputStream *self) { g_return_val_if_fail (EVD_IS_SOCKET_OUTPUT_STREAM (self), NULL); return self->priv->socket; } EventDance-0.2.0/evd/evd-socket-output-stream.h000066400000000000000000000052011321356073300213500ustar00rootroot00000000000000/* * evd-socket-output-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_SOCKET_OUTPUT_STREAM_H__ #define __EVD_SOCKET_OUTPUT_STREAM_H__ #include #include #include "evd-socket.h" G_BEGIN_DECLS typedef struct _EvdSocketOutputStream EvdSocketOutputStream; typedef struct _EvdSocketOutputStreamClass EvdSocketOutputStreamClass; typedef struct _EvdSocketOutputStreamPrivate EvdSocketOutputStreamPrivate; struct _EvdSocketOutputStream { GOutputStream parent; EvdSocketOutputStreamPrivate *priv; }; struct _EvdSocketOutputStreamClass { GOutputStreamClass parent_class; /* signal prototypes */ void (* filled) (EvdSocketOutputStream *self); }; #define EVD_TYPE_SOCKET_OUTPUT_STREAM (evd_socket_output_stream_get_type ()) #define EVD_SOCKET_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_SOCKET_OUTPUT_STREAM, EvdSocketOutputStream)) #define EVD_SOCKET_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_SOCKET_OUTPUT_STREAM, EvdSocketOutputStreamClass)) #define EVD_IS_SOCKET_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_SOCKET_OUTPUT_STREAM)) #define EVD_IS_SOCKET_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_SOCKET_OUTPUT_STREAM)) #define EVD_SOCKET_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_SOCKET_OUTPUT_STREAM, EvdSocketOutputStreamClass)) GType evd_socket_output_stream_get_type (void) G_GNUC_CONST; EvdSocketOutputStream *evd_socket_output_stream_new (EvdSocket *socket); void evd_socket_output_stream_set_socket (EvdSocketOutputStream *self, EvdSocket *socket); EvdSocket *evd_socket_output_stream_get_socket (EvdSocketOutputStream *self); G_END_DECLS #endif /* __EVD_SOCKET_OUTPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-socket.c000066400000000000000000001355611321356073300165310ustar00rootroot00000000000000/* * evd-socket.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ /** * SECTION:evd-socket * @short_description: EventDance's base socket class. * * #EvdSocket sockets are Berkeley-style sockets optmized for performance and scalability * under high-concurrency scenarios. * **/ #include "evd-socket.h" #ifdef HAVE_GIO_UNIX #include #endif #include "evd-utils.h" #include "evd-marshal.h" #include "evd-error.h" #include "evd-poll.h" #include "evd-resolver.h" #include "evd-connection.h" #include G_DEFINE_TYPE (EvdSocket, evd_socket, G_TYPE_OBJECT) #define EVD_SOCKET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_SOCKET, \ EvdSocketPrivate)) #define SOCKET_ACTIVE(socket) (socket->priv->status == EVD_SOCKET_STATE_CONNECTED || \ (socket->priv->status == EVD_SOCKET_STATE_BOUND && \ socket->priv->protocol == G_SOCKET_PROTOCOL_UDP)) /* private data */ struct _EvdSocketPrivate { GSocket *socket; GSocketFamily family; GSocketType type; GSocketProtocol protocol; EvdSocketState status; EvdSocketState sub_status; GIOCondition cond; gint actual_priority; gint priority; gboolean bind_allow_reuse; EvdSocketNotifyConditionCallback notify_cond_cb; gpointer notify_cond_user_data; GType io_stream_type; GIOStream *io_stream; gboolean has_pending; GSimpleAsyncResult *async_result; EvdPoll *poll; EvdPollSession *poll_session; }; /* signals */ enum { SIGNAL_ERROR, SIGNAL_STATE_CHANGED, SIGNAL_CLOSE, SIGNAL_NEW_CONNECTION, SIGNAL_LAST }; static guint evd_socket_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_SOCKET, PROP_FAMILY, PROP_TYPE, PROP_PROTOCOL, PROP_PRIORITY, PROP_STATUS, PROP_IO_STREAM_TYPE }; static void evd_socket_class_init (EvdSocketClass *class); static void evd_socket_init (EvdSocket *self); static void evd_socket_finalize (GObject *obj); static void evd_socket_dispose (GObject *obj); static void evd_socket_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_socket_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gboolean evd_socket_cleanup (EvdSocket *self, GError **error); static gboolean evd_socket_cleanup_internal (EvdSocket *self, GError **error); static void evd_socket_set_status (EvdSocket *self, EvdSocketState status); static void evd_socket_copy_properties (EvdSocket *self, EvdSocket *target); static void evd_socket_deliver_async_result_error (EvdSocket *self, GSimpleAsyncResult *res, GError *error, GAsyncReadyCallback callback, gpointer user_data, gboolean in_idle); static gboolean evd_socket_check_availability (EvdSocket *self, GError **error); static void evd_socket_throw_error (EvdSocket *self, GError *error); static void evd_socket_handle_condition (EvdSocket *self, GIOCondition condition); static EvdSocket *evd_socket_accept (EvdSocket *self, GError **error); static void evd_socket_class_init (EvdSocketClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_socket_dispose; obj_class->finalize = evd_socket_finalize; obj_class->get_property = evd_socket_get_property; obj_class->set_property = evd_socket_set_property; class->handle_condition = NULL; class->cleanup = evd_socket_cleanup_internal; /* install signals */ evd_socket_signals[SIGNAL_ERROR] = g_signal_new ("error", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketClass, error), NULL, NULL, evd_marshal_VOID__UINT_INT_STRING, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_INT, G_TYPE_STRING); evd_socket_signals[SIGNAL_STATE_CHANGED] = g_signal_new ("state-changed", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketClass, state_changed), NULL, NULL, evd_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); evd_socket_signals[SIGNAL_CLOSE] = g_signal_new ("close", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketClass, close), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); evd_socket_signals[SIGNAL_NEW_CONNECTION] = g_signal_new ("new-connection", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdSocketClass, new_connection), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_IO_STREAM); /* install properties */ g_object_class_install_property (obj_class, PROP_SOCKET, g_param_spec_object ("socket", "The actual GSocket", "The underlaying socket", G_TYPE_SOCKET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_FAMILY, g_param_spec_enum ("family", "Socket family", "The sockets address family", G_TYPE_SOCKET_FAMILY, G_SOCKET_FAMILY_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_enum ("type", "Socket type", "The sockets type", G_TYPE_SOCKET_TYPE, G_SOCKET_TYPE_INVALID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_PROTOCOL, g_param_spec_enum ("protocol", "Socket protocol", "The id of the protocol to use, or -1 for unknown", G_TYPE_SOCKET_PROTOCOL, G_SOCKET_PROTOCOL_UNKNOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_PRIORITY, g_param_spec_int ("priority", "The priority of socket's events", "The priority of the socket when dispatching its events in the loop", G_PRIORITY_HIGH, G_PRIORITY_LOW, G_PRIORITY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_STATUS, g_param_spec_uint ("status", "Socket status", "The current status of the socket (closed, connected, listening, etc)", 0, EVD_SOCKET_STATE_LISTENING, EVD_SOCKET_STATE_CLOSED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_IO_STREAM_TYPE, g_param_spec_gtype ("io-stream-type", "The IO stream GType", "The GType of the IO stream returned by socket when connected", EVD_TYPE_CONNECTION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdSocketPrivate)); } static void evd_socket_init (EvdSocket *self) { EvdSocketPrivate *priv; priv = EVD_SOCKET_GET_PRIVATE (self); self->priv = priv; priv->socket = NULL; priv->family = G_SOCKET_FAMILY_INVALID; priv->type = G_SOCKET_TYPE_INVALID; priv->protocol = G_SOCKET_PROTOCOL_UNKNOWN; priv->status = EVD_SOCKET_STATE_CLOSED; priv->sub_status = EVD_SOCKET_STATE_CLOSED; priv->cond = 0; priv->priority = G_PRIORITY_DEFAULT; priv->actual_priority = G_PRIORITY_DEFAULT; priv->notify_cond_cb = NULL; priv->notify_cond_user_data = NULL; priv->io_stream_type = EVD_TYPE_CONNECTION; priv->has_pending = FALSE; priv->async_result = NULL; priv->poll = evd_poll_get_default (); priv->poll_session = NULL; } static void evd_socket_dispose (GObject *obj) { EvdSocket *self = EVD_SOCKET (obj); self->priv->status = EVD_SOCKET_STATE_CLOSED; evd_socket_cleanup (self, NULL); G_OBJECT_CLASS (evd_socket_parent_class)->dispose (obj); } static void evd_socket_finalize (GObject *obj) { EvdSocket *self = EVD_SOCKET (obj); g_object_unref (self->priv->poll); G_OBJECT_CLASS (evd_socket_parent_class)->finalize (obj); } static void evd_socket_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdSocket *self; self = EVD_SOCKET (obj); switch (prop_id) { case PROP_FAMILY: self->priv->family = g_value_get_enum (value); break; case PROP_TYPE: self->priv->type = g_value_get_enum (value); break; case PROP_PROTOCOL: self->priv->protocol = g_value_get_enum (value); break; case PROP_PRIORITY: evd_socket_set_priority (self, g_value_get_uint (value)); break; case PROP_IO_STREAM_TYPE: self->priv->io_stream_type = g_value_get_gtype (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_socket_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdSocket *self; self = EVD_SOCKET (obj); switch (prop_id) { case PROP_SOCKET: g_value_set_object (value, self->priv->socket); break; case PROP_FAMILY: if (self->priv->socket != NULL) g_value_set_enum (value, g_socket_get_family (self->priv->socket)); else g_value_set_enum (value, self->priv->family); break; case PROP_TYPE: if (self->priv->socket != NULL) { GSocketType type; g_object_get (self->priv->socket, "type", &type, NULL); g_value_set_enum (value, type); } else g_value_set_enum (value, self->priv->type); break; case PROP_PROTOCOL: if (self->priv->socket != NULL) g_value_set_enum (value, g_socket_get_protocol (self->priv->socket)); else g_value_set_enum (value, self->priv->protocol); break; case PROP_PRIORITY: g_value_set_int (value, self->priv->priority); break; case PROP_STATUS: g_value_set_uint (value, self->priv->status); break; case PROP_IO_STREAM_TYPE: g_value_set_gtype (value, self->priv->io_stream_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_socket_set_socket (EvdSocket *self, GSocket *socket) { self->priv->socket = socket; g_object_set (socket, "blocking", FALSE, "keepalive", TRUE, NULL); } static gboolean evd_socket_setup (EvdSocket *self, GError **error) { if (self->priv->socket == NULL) { GSocket *socket; if ( (socket = g_socket_new (self->priv->family, self->priv->type, self->priv->protocol, error)) == NULL) { return FALSE; } else { evd_socket_set_socket (self, socket); } } return TRUE; } static GIOCondition evd_socket_on_condition (EvdPoll *poll, GIOCondition cond, gpointer user_data) { EvdSocket *self = EVD_SOCKET (user_data); evd_socket_handle_condition (self, cond); return cond; } gboolean static evd_socket_watch (EvdSocket *self, GIOCondition cond, GError **error) { self->priv->cond = 0; if (self->priv->poll_session == NULL) { self->priv->poll_session = evd_poll_add (self->priv->poll, g_socket_get_fd (self->priv->socket), cond, self->priv->actual_priority, evd_socket_on_condition, self, NULL, error); return (self->priv->poll_session != NULL); } else { return evd_poll_mod (self->priv->poll, self->priv->poll_session, cond, self->priv->actual_priority, error); } } static gboolean evd_socket_unwatch (EvdSocket *self, GError **error) { if (self->priv->poll_session == NULL || evd_poll_del (self->priv->poll, self->priv->poll_session, error)) { return TRUE; } else { return FALSE; } } static gboolean evd_socket_check_address (EvdSocket *self, GSocketAddress *address, GError **error) { if (self->priv->family == G_SOCKET_FAMILY_INVALID) self->priv->family = g_socket_address_get_family (address); else if (self->priv->family != g_socket_address_get_family (address)) { if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Socket family and address family mismatch"); return FALSE; } if (self->priv->type == G_SOCKET_TYPE_INVALID) { if (self->priv->protocol == G_SOCKET_PROTOCOL_UDP) self->priv->type = G_SOCKET_TYPE_DATAGRAM; else self->priv->type = G_SOCKET_TYPE_STREAM; } if (self->priv->protocol == G_SOCKET_PROTOCOL_UNKNOWN) self->priv->protocol = G_SOCKET_PROTOCOL_DEFAULT; return TRUE; } static gboolean evd_socket_is_connected (EvdSocket *self, GError **error) { if (self->priv->socket == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Socket is not connected"); return FALSE; } return TRUE; } static gboolean evd_socket_cleanup (EvdSocket *self, GError **error) { EvdSocketClass *class = EVD_SOCKET_GET_CLASS (self); if (class->cleanup != NULL) return class->cleanup (self, error); else return evd_socket_cleanup (self, error); } static void evd_socket_deliver_async_result_error (EvdSocket *self, GSimpleAsyncResult *res, GError *error, GAsyncReadyCallback callback, gpointer user_data, gboolean in_idle) { if (res == NULL) { res = g_simple_async_result_new_from_error (G_OBJECT (self), callback, user_data, error); } else { g_simple_async_result_set_from_error (res, error); } if (in_idle) g_simple_async_result_complete_in_idle (res); else g_simple_async_result_complete (res); g_object_unref (res); } static gboolean evd_socket_bind_addr_internal (EvdSocket *self, GSocketAddress *address, gboolean allow_reuse, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), FALSE); if (! evd_socket_check_address (self, address, error)) return FALSE; if (! evd_socket_setup (self, error)) return FALSE; if (! g_socket_bind (self->priv->socket, address, allow_reuse, error)) { evd_socket_cleanup (self, NULL); return FALSE; } else { evd_socket_set_status (self, EVD_SOCKET_STATE_BOUND); } return TRUE; } static gboolean evd_socket_listen_addr_internal (EvdSocket *self, GSocketAddress *address, GError **error) { if (address != NULL && ! evd_socket_bind_addr_internal (self, address, TRUE, error)) return FALSE; if (self->priv->status != EVD_SOCKET_STATE_BOUND) { /* this is to consider that socket could have been closed during 'state-changed' signal handler after call to bind */ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Socket has been closed"); return FALSE; } g_socket_set_listen_backlog (self->priv->socket, 10000); /* TODO: change by a max-conn prop */ if (g_socket_listen (self->priv->socket, error)) { if (evd_socket_watch (self, G_IO_IN, error)) { self->priv->cond = 0; self->priv->actual_priority = G_PRIORITY_HIGH + 1; evd_socket_set_status (self, EVD_SOCKET_STATE_LISTENING); } else { evd_socket_cleanup (self, NULL); return FALSE; } } return TRUE; } static gboolean evd_socket_connect_addr_internal (EvdSocket *self, GSocketAddress *address, GError **error) { GError *_error = NULL; g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), FALSE); if (! evd_socket_check_address (self, address, error)) return FALSE; if (! evd_socket_setup (self, error)) return FALSE; if (! g_socket_connect (self->priv->socket, address, NULL, &_error)) { /* an error ocurred, but error-pending is normal as on async ops */ if ((_error)->code != G_IO_ERROR_PENDING) { if (error != NULL) *error = _error; return FALSE; } else { g_error_free (_error); _error = NULL; } } /* g_socket_connect returns TRUE on a non-blocking socket, however fills error with "connection in progress" hint */ if (_error != NULL) { g_error_free (_error); _error = NULL; } if (! evd_socket_watch (self, G_IO_IN | G_IO_OUT, error)) { return FALSE; } else { self->priv->actual_priority = G_PRIORITY_HIGH + 2; evd_socket_set_status (self, EVD_SOCKET_STATE_CONNECTING); return TRUE; } } static void evd_socket_on_address_resolved (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdSocket *self = EVD_SOCKET (user_data); EvdResolver *resolver = EVD_RESOLVER (obj); GList *addresses; GError *error = NULL; if (self->priv->status != EVD_SOCKET_STATE_RESOLVING) goto free_mem; if ( (addresses = evd_resolver_resolve_finish (EVD_RESOLVER (obj), res, &error)) != NULL) { GSocketAddress *socket_address; GList *node = addresses; gboolean match = FALSE; while (node != NULL) { socket_address = G_SOCKET_ADDRESS (node->data); if (evd_socket_check_address (self, socket_address, NULL)) { match = TRUE; break; } else node = node->next; } if (match) { gint sub_status; sub_status = self->priv->sub_status; self->priv->sub_status = EVD_SOCKET_STATE_CLOSED; switch (sub_status) { case EVD_SOCKET_STATE_LISTENING: { if (evd_socket_listen_addr_internal (self, socket_address, &error)) { if (self->priv->async_result != NULL) { self->priv->has_pending = FALSE; g_simple_async_result_complete_in_idle (self->priv->async_result); g_object_unref (self->priv->async_result); self->priv->async_result = NULL; } } break; } case EVD_SOCKET_STATE_BOUND: { if (evd_socket_bind_addr_internal (self, socket_address, self->priv->bind_allow_reuse, &error)) { if (self->priv->async_result != NULL) { self->priv->has_pending = FALSE; g_simple_async_result_complete_in_idle (self->priv->async_result); g_object_unref (self->priv->async_result); self->priv->async_result = NULL; } } break; } case EVD_SOCKET_STATE_CONNECTING: { evd_socket_connect_addr_internal (self, socket_address, &error); break; } default: { } } } else { error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "None of the resolved addresses match socket family"); } evd_resolver_free_addresses (addresses); } if (error != NULL) { if (self->priv->async_result != NULL) { evd_socket_deliver_async_result_error (self, self->priv->async_result, error, NULL, NULL, TRUE); self->priv->async_result = NULL; } evd_socket_throw_error (self, error); evd_socket_close (self, NULL); } free_mem: g_object_unref (self); g_object_unref (resolver); } static void evd_socket_resolve_address (EvdSocket *self, const gchar *address, EvdSocketState action, GCancellable *cancellable) { EvdResolver *resolver; self->priv->sub_status = action; resolver = evd_resolver_get_default (); g_object_ref (self); evd_resolver_resolve (resolver, address, cancellable, evd_socket_on_address_resolved, self); } static void evd_socket_copy_properties (EvdSocket *self, EvdSocket *target) { evd_socket_set_priority (target, self->priv->priority); target->priv->io_stream_type = self->priv->io_stream_type; } static gboolean evd_socket_check_availability (EvdSocket *self, GError **error) { if (self->priv->has_pending) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PENDING, "Socket has outstanding operation"); return FALSE; } if (SOCKET_ACTIVE (self)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_BUSY, "Socket is currently active, should be closed first before requesting new operation"); return FALSE; } return TRUE; } static void evd_socket_set_status (EvdSocket *self, EvdSocketState status) { EvdSocketState old_status; if (status == self->priv->status) return; old_status = self->priv->status; self->priv->status = status; g_signal_emit (self, evd_socket_signals[SIGNAL_STATE_CHANGED], 0, status, old_status, NULL); } static void evd_socket_throw_error (EvdSocket *self, GError *error) { g_object_ref (self); g_signal_emit (self, evd_socket_signals[SIGNAL_ERROR], 0, error->domain, error->code, error->message, NULL); g_object_unref (self); g_error_free (error); } static void evd_socket_handle_condition (EvdSocket *self, GIOCondition condition) { GError *error = NULL; if (condition == self->priv->cond) return; self->priv->cond = condition; g_object_ref (self); if (self->priv->status == EVD_SOCKET_STATE_LISTENING) { EvdSocket *client; GIOStream *conn; self->priv->cond &= ~G_IO_IN; while ( (self->priv->status == EVD_SOCKET_STATE_LISTENING) && ((client = evd_socket_accept (self, &error)) != NULL) ) { evd_socket_copy_properties (self, client); conn = g_object_new (self->priv->io_stream_type, "socket", client, NULL); /* fire 'new-connection' signal */ g_signal_emit (self, evd_socket_signals[SIGNAL_NEW_CONNECTION], 0, G_IO_STREAM (conn), NULL); g_object_unref (conn); g_object_unref (client); } if (error != NULL) { if (error->code != G_IO_ERROR_WOULD_BLOCK) { evd_socket_throw_error (self, error); /* @TODO: even on error, we should continue accepting new connection until EAGAIN */ } else { g_error_free (error); } } } else { if (condition & G_IO_ERR) { if (self->priv->status == EVD_SOCKET_STATE_CONNECTING) { /* assume connection was refused */ error = g_error_new (G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, "Connection refused"); evd_socket_deliver_async_result_error (self, self->priv->async_result, error, NULL, NULL, TRUE); self->priv->async_result = NULL; } else { error = g_error_new (G_IO_ERROR, G_IO_ERROR_UNKNOWN, "Unknown socket error"); } evd_socket_throw_error (self, error); evd_socket_close (self, NULL); } else if (self->priv->status != EVD_SOCKET_STATE_CLOSED) { /* write condition */ if (condition & G_IO_OUT) { if (self->priv->status == EVD_SOCKET_STATE_CONNECTING) { /* socket has just connected! */ self->priv->has_pending = FALSE; /* restore priority */ self->priv->actual_priority = self->priv->priority; evd_socket_set_status (self, EVD_SOCKET_STATE_CONNECTED); if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_complete_in_idle (res); g_object_unref (res); } } } } } if (SOCKET_ACTIVE (self) && self->priv->notify_cond_cb != NULL) self->priv->notify_cond_cb (self, condition, self->priv->notify_cond_user_data); g_object_unref (self); } static gboolean evd_socket_cleanup_internal (EvdSocket *self, GError **error) { gboolean result = TRUE; self->priv->family = G_SOCKET_FAMILY_INVALID; self->priv->cond = 0; if (self->priv->socket != NULL) { if (! evd_socket_unwatch (self, error) || (! g_socket_is_closed (self->priv->socket) && ! g_socket_close (self->priv->socket, error != NULL && *error == NULL ? error : NULL))) { result = FALSE; } g_object_unref (self->priv->socket); self->priv->socket = NULL; } if (self->priv->poll_session != NULL) self->priv->poll_session = NULL; self->priv->has_pending = FALSE; return result; } static EvdSocket * evd_socket_accept (EvdSocket *self, GError **error) { EvdSocket *client = NULL; GSocket *client_socket; g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); if ( (client_socket = g_socket_accept (self->priv->socket, NULL, error)) != NULL) { client = EVD_SOCKET (g_object_new (G_OBJECT_TYPE (self), NULL, NULL)); evd_socket_set_socket (client, client_socket); if (evd_socket_watch (client, G_IO_IN | G_IO_OUT, error)) { evd_socket_set_status (client, EVD_SOCKET_STATE_CONNECTED); return client; } } return NULL; } /* public methods */ EvdSocket * evd_socket_new (void) { EvdSocket *self; self = g_object_new (EVD_TYPE_SOCKET, NULL); return self; } /** * evd_socket_get_socket: * * Returns: (transfer none): **/ GSocket * evd_socket_get_socket (EvdSocket *self) { g_return_val_if_fail (EVD_IS_SOCKET (self), NULL); return self->priv->socket; } GSocketFamily evd_socket_get_family (EvdSocket *self) { g_return_val_if_fail (EVD_IS_SOCKET (self), 0); return self->priv->family; } EvdSocketState evd_socket_get_status (EvdSocket *self) { g_return_val_if_fail (EVD_IS_SOCKET (self), 0); return self->priv->status; } gint evd_socket_get_priority (EvdSocket *self) { g_return_val_if_fail (EVD_IS_SOCKET (self), G_PRIORITY_DEFAULT); return self->priv->priority; } void evd_socket_set_priority (EvdSocket *self, gint priority) { g_return_if_fail (EVD_IS_SOCKET (self)); g_return_if_fail ( (priority <= G_PRIORITY_LOW) && (priority >= G_PRIORITY_HIGH)); if (self->priv->actual_priority == self->priv->priority) self->priv->actual_priority = priority; self->priv->priority = priority; } gboolean evd_socket_close (EvdSocket *self, GError **error) { gboolean result = TRUE; g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); if (self->priv->async_result != NULL) { GSimpleAsyncResult *res; res = self->priv->async_result; self->priv->async_result = NULL; g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_CLOSED, "Socket has been closed"); g_simple_async_result_complete (res); g_object_unref (res); } if (self->priv->status != EVD_SOCKET_STATE_CLOSED && self->priv->status != EVD_SOCKET_STATE_CLOSING) { if (! evd_socket_cleanup (self, error)) result = FALSE; g_object_ref (self); evd_socket_set_status (self, EVD_SOCKET_STATE_CLOSED); g_signal_emit (self, evd_socket_signals[SIGNAL_CLOSE], 0, NULL); g_object_unref (self); } return result; } /** * evd_socket_get_remote_address: * * Returns: (transfer full): **/ GSocketAddress * evd_socket_get_remote_address (EvdSocket *self, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), NULL); if (self->priv->socket == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Failed to get remote address, socket not initializaed"); return NULL; } else return g_socket_get_remote_address (self->priv->socket, error); } /** * evd_socket_get_local_address: * * Returns: (transfer full): **/ GSocketAddress * evd_socket_get_local_address (EvdSocket *self, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), NULL); if (self->priv->socket == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Failed to get local address, socket not initializaed"); return NULL; } else return g_socket_get_local_address (self->priv->socket, error); } gboolean evd_socket_shutdown (EvdSocket *self, gboolean shutdown_read, gboolean shutdown_write, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); if (! evd_socket_is_connected (self, error)) return FALSE; return g_socket_shutdown (self->priv->socket, shutdown_read, shutdown_write, error); } gboolean evd_socket_watch_condition (EvdSocket *self, GIOCondition cond, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); if (SOCKET_ACTIVE (self)) { self->priv->cond = ~cond; return TRUE; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Socket is not active"); return FALSE; } } GIOCondition evd_socket_get_condition (EvdSocket *self) { g_return_val_if_fail (EVD_IS_SOCKET (self), 0); return self->priv->cond; } /** * evd_socket_set_notify_condition_callback: * @callback: (scope notified) (allow-none): * @user_data: (allow-none): * **/ void evd_socket_set_notify_condition_callback (EvdSocket *self, EvdSocketNotifyConditionCallback callback, gpointer user_data) { g_return_if_fail (EVD_IS_SOCKET (self)); self->priv->notify_cond_cb = callback; self->priv->notify_cond_user_data = user_data; } /** * evd_socket_connect_to: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_socket_connect_to (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_SOCKET (self)); g_return_if_fail (address != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_socket_connect_addr); if (! evd_socket_check_availability (self, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; self->priv->has_pending = TRUE; evd_socket_set_status (self, EVD_SOCKET_STATE_RESOLVING); evd_socket_resolve_address (self, address, EVD_SOCKET_STATE_CONNECTING, cancellable); } /** * evd_socket_connect_addr: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_socket_connect_addr (EvdSocket *self, GSocketAddress *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; g_return_if_fail (EVD_IS_SOCKET (self)); if (! evd_socket_check_availability (self, &error) || ! evd_socket_connect_addr_internal (self, address, &error)) { evd_socket_deliver_async_result_error (self, NULL, error, callback, user_data, TRUE); return; } else { self->priv->has_pending = TRUE; self->priv->async_result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_socket_connect_addr); } return; } /** * evd_socket_connect_finish: * @result: (type Gio.AsyncResult): * * Returns: (transfer full) (type Gio.IOStream): **/ GIOStream * evd_socket_connect_finish (EvdSocket *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), NULL); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_socket_connect_addr), NULL); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return g_object_new (self->priv->io_stream_type, "socket", self, NULL); else return NULL; } gboolean evd_socket_listen_addr (EvdSocket *self, GSocketAddress *address, GError **error) { gboolean result; g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); if (! evd_socket_check_availability (self, error)) return FALSE; else self->priv->has_pending = TRUE; result = evd_socket_listen_addr_internal (self, address, error); self->priv->has_pending = FALSE; return result; } /** * evd_socket_listen: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_socket_listen (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_SOCKET (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_socket_listen); if (! evd_socket_check_availability (self, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; self->priv->has_pending = TRUE; evd_socket_set_status (self, EVD_SOCKET_STATE_RESOLVING); evd_socket_resolve_address (self, address, EVD_SOCKET_STATE_LISTENING, cancellable); } gboolean evd_socket_listen_finish (EvdSocket *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_socket_listen), FALSE); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return TRUE; else return FALSE; } gboolean evd_socket_bind_addr (EvdSocket *self, GSocketAddress *address, gboolean allow_reuse, GError **error) { gboolean result; g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), FALSE); if (! evd_socket_check_availability (self, error)) return FALSE; else self->priv->has_pending = TRUE; result = evd_socket_bind_addr_internal (self, address, allow_reuse, error); self->priv->has_pending = FALSE; return result; } /** * evd_socket_bind: * @cancellable: (allow-none): * @callback: (allow-none): * @user_data: (allow-none): * **/ void evd_socket_bind (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_SOCKET (self)); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_socket_bind); if (! evd_socket_check_availability (self, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); g_simple_async_result_complete_in_idle (res); g_object_unref (res); return; } self->priv->async_result = res; self->priv->has_pending = TRUE; evd_socket_set_status (self, EVD_SOCKET_STATE_RESOLVING); evd_socket_resolve_address (self, address, EVD_SOCKET_STATE_BOUND, cancellable); } gboolean evd_socket_bind_finish (EvdSocket *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_SOCKET (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_socket_bind), FALSE); if (! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) return TRUE; else return FALSE; } gchar * evd_socket_get_remote_address_str (EvdSocket *self, gsize *len, GError **error) { gchar *result = NULL; GSocketAddress *sock_addr; GInetAddress *inet_addr; g_return_val_if_fail (EVD_IS_SOCKET (self), NULL); sock_addr = evd_socket_get_remote_address (self, error); if (sock_addr == NULL) return NULL; #ifdef HAVE_GIO_UNIX if (self->priv->family == G_SOCKET_FAMILY_UNIX) { result = g_strdup (g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (sock_addr))); if (len != NULL) *len = g_unix_socket_address_get_path_len (G_UNIX_SOCKET_ADDRESS (sock_addr)); return result; } #endif inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (sock_addr)); result = g_inet_address_to_string (inet_addr); if (len != NULL) *len = strlen (result); g_object_unref (sock_addr); return result; } EventDance-0.2.0/evd/evd-socket.h000066400000000000000000000211441321356073300165250ustar00rootroot00000000000000/* * evd-socket.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_SOCKET_H__ #define __EVD_SOCKET_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS typedef struct _EvdSocket EvdSocket; typedef struct _EvdSocketClass EvdSocketClass; typedef struct _EvdSocketPrivate EvdSocketPrivate; typedef struct _EvdSocketEvent EvdSocketEvent; typedef void (* EvdSocketNotifyConditionCallback) (EvdSocket *self, GIOCondition condition, gpointer user_data); /* socket states */ typedef enum { EVD_SOCKET_STATE_CLOSED, EVD_SOCKET_STATE_CONNECTING, EVD_SOCKET_STATE_CONNECTED, EVD_SOCKET_STATE_RESOLVING, EVD_SOCKET_STATE_BOUND, EVD_SOCKET_STATE_LISTENING, EVD_SOCKET_STATE_TLS_HANDSHAKING, EVD_SOCKET_STATE_CLOSING } EvdSocketState; struct _EvdSocket { GObject parent; /* private structure */ EvdSocketPrivate *priv; }; struct _EvdSocketClass { GObjectClass parent_class; /* virtual methods */ gboolean (* handle_condition) (EvdSocket *self, GIOCondition condition); gboolean (* cleanup) (EvdSocket *self, GError **error); /* signal prototypes */ void (* error) (EvdSocket *self, guint32 error_domain, gint error_code, gchar *error_message, gpointer user_data); void (* state_changed) (EvdSocket *self, EvdSocketState new_state, EvdSocketState old_state); void (* close) (EvdSocket *self); void (* new_connection) (EvdSocket *self, GIOStream *socket, gpointer user_data); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_SOCKET (evd_socket_get_type ()) #define EVD_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_SOCKET, EvdSocket)) #define EVD_SOCKET_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_SOCKET, EvdSocketClass)) #define EVD_IS_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_SOCKET)) #define EVD_IS_SOCKET_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_SOCKET)) #define EVD_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_SOCKET, EvdSocketClass)) GType evd_socket_get_type (void) G_GNUC_CONST; EvdSocket *evd_socket_new (void); GSocket *evd_socket_get_socket (EvdSocket *self); GSocketFamily evd_socket_get_family (EvdSocket *self); EvdSocketState evd_socket_get_status (EvdSocket *self); gint evd_socket_get_priority (EvdSocket *self); void evd_socket_set_priority (EvdSocket *self, gint priority); gboolean evd_socket_close (EvdSocket *self, GError **error); GSocketAddress *evd_socket_get_remote_address (EvdSocket *self, GError **error); gchar *evd_socket_get_remote_address_str (EvdSocket *self, gsize *len, GError **error); GSocketAddress *evd_socket_get_local_address (EvdSocket *self, GError **error); gboolean evd_socket_shutdown (EvdSocket *self, gboolean shutdown_read, gboolean shutdown_write, GError **error); gboolean evd_socket_watch_condition (EvdSocket *self, GIOCondition cond, GError **error); GIOCondition evd_socket_get_condition (EvdSocket *self); void evd_socket_set_notify_condition_callback (EvdSocket *self, EvdSocketNotifyConditionCallback callback, gpointer user_data); gboolean evd_socket_bind_addr (EvdSocket *self, GSocketAddress *address, gboolean allow_reuse, GError **error); void evd_socket_bind (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_socket_bind_finish (EvdSocket *self, GAsyncResult *result, GError **error); gboolean evd_socket_listen_addr (EvdSocket *self, GSocketAddress *address, GError **error); void evd_socket_listen (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_socket_listen_finish (EvdSocket *self, GAsyncResult *result, GError **error); void evd_socket_connect_addr (EvdSocket *self, GSocketAddress *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); void evd_socket_connect_to (EvdSocket *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GIOStream *evd_socket_connect_finish (EvdSocket *self, GAsyncResult *result, GError **error); G_END_DECLS #endif /* __EVD_SOCKET_H__ */ EventDance-0.2.0/evd/evd-stream-throttle.c000066400000000000000000000236621321356073300203750ustar00rootroot00000000000000/* * evd-stream-throttle.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-stream-throttle.h" G_DEFINE_TYPE (EvdStreamThrottle, evd_stream_throttle, G_TYPE_OBJECT) #define EVD_STREAM_THROTTLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_STREAM_THROTTLE, \ EvdStreamThrottlePrivate)) /* private data */ struct _EvdStreamThrottlePrivate { gsize bandwidth; gulong latency; GTimeVal current_time; gsize bytes; gsize actual_bandwidth; guint64 total; GTimeVal last; }; /* properties */ enum { PROP_0, PROP_BANDWIDTH, PROP_LATENCY, PROP_TOTAL }; G_LOCK_DEFINE_STATIC (counters); static void evd_stream_throttle_class_init (EvdStreamThrottleClass *class); static void evd_stream_throttle_init (EvdStreamThrottle *self); static void evd_stream_throttle_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_stream_throttle_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_stream_throttle_class_init (EvdStreamThrottleClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->get_property = evd_stream_throttle_get_property; obj_class->set_property = evd_stream_throttle_set_property; g_object_class_install_property (obj_class, PROP_BANDWIDTH, g_param_spec_float ("bandwidth", "Bandwidth limit", "The maximum bandwidth in kilobytes", 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_LATENCY, g_param_spec_float ("latency", "Minimum latency", "The minimum time between two transfers, in miliseconds", 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_TOTAL, g_param_spec_uint64 ("total", "Total transferred", "Total sum of bytes ever reported to this stream-throttle", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdStreamThrottlePrivate)); } static void evd_stream_throttle_init (EvdStreamThrottle *self) { EvdStreamThrottlePrivate *priv; priv = EVD_STREAM_THROTTLE_GET_PRIVATE (self); self->priv = priv; priv->bandwidth = 0; priv->latency = 0; priv->current_time.tv_sec = 0; priv->current_time.tv_usec = 0; priv->bytes = 0; priv->total = 0; priv->actual_bandwidth = 0; } static void evd_stream_throttle_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdStreamThrottle *self; self = EVD_STREAM_THROTTLE (obj); switch (prop_id) { case PROP_BANDWIDTH: self->priv->bandwidth = (gsize) (g_value_get_float (value) * 1024.0); break; /* Latency properties are in miliseconds, but we store the value internally in microseconds, to allow up to 1/1000 fraction of a milisecond */ case PROP_LATENCY: self->priv->latency = (gulong) (g_value_get_float (value) * 1000.0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_stream_throttle_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdStreamThrottle *self; self = EVD_STREAM_THROTTLE (obj); switch (prop_id) { case PROP_BANDWIDTH: g_value_set_float (value, self->priv->bandwidth / 1024.0); break; /* Latency values are stored in microseconds internally */ case PROP_LATENCY: g_value_set_float (value, self->priv->latency / 1000.0); break; case PROP_TOTAL: g_value_set_uint64 (value, evd_stream_throttle_get_total (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_stream_throttle_update_current_time (EvdStreamThrottle *self) { GTimeVal time_val; g_get_current_time (&time_val); if (time_val.tv_sec != self->priv->current_time.tv_sec) { G_LOCK (counters); self->priv->actual_bandwidth = self->priv->bytes; self->priv->bytes = 0; G_UNLOCK (counters); } g_memmove (&self->priv->current_time, &time_val, sizeof (GTimeVal)); } static gulong g_timeval_get_diff_micro (GTimeVal *time1, GTimeVal *time2) { gulong result; result = ABS (time2->tv_sec - time1->tv_sec) * G_USEC_PER_SEC; result += ABS (time2->tv_usec - time1->tv_usec); return result; } static gsize evd_stream_throttle_request_internal (EvdStreamThrottle *self, gsize bandwidth, gulong latency, gsize bytes, GTimeVal *last, gsize size, guint *wait) { gsize actual_size = size; if ( (wait != NULL) && (*wait < 0) ) *wait = 0; G_LOCK (counters); /* latency check */ if (latency > 0) { gulong elapsed; elapsed = g_timeval_get_diff_micro (&self->priv->current_time, last); if (elapsed < latency) { actual_size = 0; if (wait != NULL) *wait = MAX ((gulong) (latency - elapsed) / 1000 + 2, *wait); } } /* bandwidth check */ if ( (bandwidth > 0) && (actual_size > 0) ) { actual_size = MAX ((gssize) (bandwidth - bytes), 0); actual_size = MIN (actual_size, size); if (wait != NULL) if (actual_size < size) *wait = MAX ((guint) (((1000001 - self->priv->current_time.tv_usec) / 1000)) + 1, *wait); } G_UNLOCK (counters); return actual_size; } /* public methods */ EvdStreamThrottle * evd_stream_throttle_new (void) { EvdStreamThrottle *self; self = g_object_new (EVD_TYPE_STREAM_THROTTLE, NULL); return self; } gsize evd_stream_throttle_request (EvdStreamThrottle *self, gsize size, guint *wait) { g_return_val_if_fail (EVD_IS_STREAM_THROTTLE (self), -1); evd_stream_throttle_update_current_time (self); return evd_stream_throttle_request_internal (self, self->priv->bandwidth, self->priv->latency, self->priv->bytes, &self->priv->last, size, wait); } void evd_stream_throttle_report (EvdStreamThrottle *self, gsize size) { g_return_if_fail (EVD_IS_STREAM_THROTTLE (self)); evd_stream_throttle_update_current_time (self); G_LOCK (counters); self->priv->bytes += size; self->priv->total += size; g_memmove (&self->priv->last, &self->priv->current_time, sizeof (GTimeVal)); G_UNLOCK (counters); } gfloat evd_stream_throttle_get_actual_bandwidth (EvdStreamThrottle *self) { g_return_val_if_fail (EVD_IS_STREAM_THROTTLE (self), -1.0); return self->priv->actual_bandwidth / 1024.0; } guint64 evd_stream_throttle_get_total (EvdStreamThrottle *self) { g_return_val_if_fail (EVD_IS_STREAM_THROTTLE (self), 0); return self->priv->total; } EventDance-0.2.0/evd/evd-stream-throttle.h000066400000000000000000000053421321356073300203750ustar00rootroot00000000000000/* * evd-stream-throttle.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_STREAM_THROTTLE_H__ #define __EVD_STREAM_THROTTLE_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef struct _EvdStreamThrottle EvdStreamThrottle; typedef struct _EvdStreamThrottleClass EvdStreamThrottleClass; typedef struct _EvdStreamThrottlePrivate EvdStreamThrottlePrivate; struct _EvdStreamThrottle { GObject parent; EvdStreamThrottlePrivate *priv; }; struct _EvdStreamThrottleClass { GObjectClass parent_class; }; #define EVD_TYPE_STREAM_THROTTLE (evd_stream_throttle_get_type ()) #define EVD_STREAM_THROTTLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_STREAM_THROTTLE, EvdStreamThrottle)) #define EVD_STREAM_THROTTLE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_STREAM_THROTTLE, EvdStreamThrottleClass)) #define EVD_IS_STREAM_THROTTLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_STREAM_THROTTLE)) #define EVD_IS_STREAM_THROTTLE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_STREAM_THROTTLE)) #define EVD_STREAM_THROTTLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_STREAM_THROTTLE, EvdStreamThrottleClass)) GType evd_stream_throttle_get_type (void) G_GNUC_CONST; EvdStreamThrottle *evd_stream_throttle_new (void); gsize evd_stream_throttle_request (EvdStreamThrottle *self, gsize size, guint *wait); void evd_stream_throttle_report (EvdStreamThrottle *self, gsize size); gfloat evd_stream_throttle_get_actual_bandwidth (EvdStreamThrottle *self); guint64 evd_stream_throttle_get_total (EvdStreamThrottle *self); G_END_DECLS #endif /* __EVD_STREAM_THROTTLE_H__ */ EventDance-0.2.0/evd/evd-throttled-input-stream.c000066400000000000000000000202401321356073300216630ustar00rootroot00000000000000/* * evd-throttled-input-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-throttled-input-stream.h" G_DEFINE_TYPE (EvdThrottledInputStream, evd_throttled_input_stream, G_TYPE_FILTER_INPUT_STREAM) #define EVD_THROTTLED_INPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_THROTTLED_INPUT_STREAM, \ EvdThrottledInputStreamPrivate)) /* private data */ struct _EvdThrottledInputStreamPrivate { GList *stream_throttles; }; /* signals */ enum { SIGNAL_DELAY_READ, SIGNAL_LAST }; static guint evd_throttled_input_stream_signals[SIGNAL_LAST] = { 0 }; static void evd_throttled_input_stream_class_init (EvdThrottledInputStreamClass *class); static void evd_throttled_input_stream_init (EvdThrottledInputStream *self); static void evd_throttled_input_stream_dispose (GObject *obj); static gssize evd_throttled_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_throttled_input_stream_class_init (EvdThrottledInputStreamClass *class) { GObjectClass *obj_class; GInputStreamClass *input_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_throttled_input_stream_dispose; input_stream_class = G_INPUT_STREAM_CLASS (class); input_stream_class->read_fn = evd_throttled_input_stream_read; evd_throttled_input_stream_signals[SIGNAL_DELAY_READ] = g_signal_new ("delay-read", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdThrottledInputStreamClass, delay_read), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_type_class_add_private (obj_class, sizeof (EvdThrottledInputStreamPrivate)); } static void evd_throttled_input_stream_init (EvdThrottledInputStream *self) { EvdThrottledInputStreamPrivate *priv; priv = EVD_THROTTLED_INPUT_STREAM_GET_PRIVATE (self); self->priv = priv; priv->stream_throttles = NULL; } static void evd_throttled_input_stream_dispose (GObject *obj) { EvdThrottledInputStream *self = EVD_THROTTLED_INPUT_STREAM (obj); GList *node; node = self->priv->stream_throttles; while (node != NULL) { g_object_unref (G_OBJECT (node->data)); node = node->next; } g_list_free (self->priv->stream_throttles); G_OBJECT_CLASS (evd_throttled_input_stream_parent_class)->dispose (obj); } static void evd_throttled_input_stream_report_size (EvdStreamThrottle *throttle, gsize *size) { evd_stream_throttle_report (throttle, *size); } static gsize evd_throttled_input_stream_get_max_readable_priv (EvdThrottledInputStream *self, gsize size, guint *retry_wait) { GList *node; node = self->priv->stream_throttles; while (node != NULL) { EvdStreamThrottle *throttle; throttle = EVD_STREAM_THROTTLE (node->data); size = MIN (size, evd_stream_throttle_request (throttle, size, retry_wait)); node = node->next; } return size; } static gssize evd_throttled_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdThrottledInputStream *self = EVD_THROTTLED_INPUT_STREAM (stream); gssize actual_size = 0; gsize limited_size; guint wait = 0; if (size == 0) return 0; limited_size = MIN (size, evd_throttled_input_stream_get_max_readable_priv ( EVD_THROTTLED_INPUT_STREAM (stream), size, &wait)); if (limited_size > 0) { GInputStream *base_stream; base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream)); actual_size = g_input_stream_read (base_stream, buffer, limited_size, cancellable, error); if (actual_size > 0) { g_list_foreach (self->priv->stream_throttles, (GFunc) evd_throttled_input_stream_report_size, &actual_size); } } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Resource temporarily unavailable"); actual_size = -1; } if (wait > 0) { g_signal_emit (self, evd_throttled_input_stream_signals[SIGNAL_DELAY_READ], 0, wait, NULL); } return actual_size; } /* public methods */ EvdThrottledInputStream * evd_throttled_input_stream_new (GInputStream *base_stream) { EvdThrottledInputStream *self; g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_THROTTLED_INPUT_STREAM, "base-stream", base_stream, NULL); return self; } /** * evd_throttled_input_stream_get_max_readable: * @retry_wait: (out): * **/ gsize evd_throttled_input_stream_get_max_readable (EvdThrottledInputStream *self, guint *retry_wait) { g_return_val_if_fail (EVD_IS_THROTTLED_INPUT_STREAM (self), 0); return evd_throttled_input_stream_get_max_readable_priv (self, G_MAXSSIZE, retry_wait); } void evd_throttled_input_stream_add_throttle (EvdThrottledInputStream *self, EvdStreamThrottle *throttle) { g_return_if_fail (EVD_IS_THROTTLED_INPUT_STREAM (self)); g_return_if_fail (EVD_IS_STREAM_THROTTLE (throttle)); if (g_list_find (self->priv->stream_throttles, throttle) == NULL) { g_object_ref (throttle); self->priv->stream_throttles = g_list_prepend (self->priv->stream_throttles, (gpointer) throttle); } } void evd_throttled_input_stream_remove_throttle (EvdThrottledInputStream *self, EvdStreamThrottle *throttle) { g_return_if_fail (EVD_IS_THROTTLED_INPUT_STREAM (self)); g_return_if_fail (EVD_IS_STREAM_THROTTLE (throttle)); if (g_list_find (self->priv->stream_throttles, throttle) != NULL) { self->priv->stream_throttles = g_list_remove (self->priv->stream_throttles, (gpointer) throttle); g_object_unref (throttle); } } EventDance-0.2.0/evd/evd-throttled-input-stream.h000066400000000000000000000063411321356073300216760ustar00rootroot00000000000000/* * evd-throttled-input-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_THROTTLED_INPUT_STREAM_H__ #define __EVD_THROTTLED_INPUT_STREAM_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-stream-throttle.h" G_BEGIN_DECLS typedef struct _EvdThrottledInputStream EvdThrottledInputStream; typedef struct _EvdThrottledInputStreamClass EvdThrottledInputStreamClass; typedef struct _EvdThrottledInputStreamPrivate EvdThrottledInputStreamPrivate; struct _EvdThrottledInputStream { GFilterInputStream parent; EvdThrottledInputStreamPrivate *priv; }; struct _EvdThrottledInputStreamClass { GFilterInputStreamClass parent_class; /* signal prototypes */ void (* delay_read) (EvdThrottledInputStream *self, guint wait, gpointer user_data); }; #define EVD_TYPE_THROTTLED_INPUT_STREAM (evd_throttled_input_stream_get_type ()) #define EVD_THROTTLED_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_THROTTLED_INPUT_STREAM, EvdThrottledInputStream)) #define EVD_THROTTLED_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_THROTTLED_INPUT_STREAM, EvdThrottledInputStreamClass)) #define EVD_IS_THROTTLED_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_THROTTLED_INPUT_STREAM)) #define EVD_IS_THROTTLED_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_THROTTLED_INPUT_STREAM)) #define EVD_THROTTLED_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_THROTTLED_INPUT_STREAM, EvdThrottledInputStreamClass)) GType evd_throttled_input_stream_get_type (void) G_GNUC_CONST; EvdThrottledInputStream *evd_throttled_input_stream_new (GInputStream *base_stream); gsize evd_throttled_input_stream_get_max_readable (EvdThrottledInputStream *self, guint *retry_wait); void evd_throttled_input_stream_add_throttle (EvdThrottledInputStream *self, EvdStreamThrottle *throttle); void evd_throttled_input_stream_remove_throttle (EvdThrottledInputStream *self, EvdStreamThrottle *throttle); G_END_DECLS #endif /* __EVD_THROTTLED_INPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-throttled-output-stream.c000066400000000000000000000261741321356073300221000ustar00rootroot00000000000000/* * evd-throttled-output-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-throttled-output-stream.h" G_DEFINE_TYPE (EvdThrottledOutputStream, evd_throttled_output_stream, G_TYPE_FILTER_OUTPUT_STREAM) #define EVD_THROTTLED_OUTPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_THROTTLED_OUTPUT_STREAM, \ EvdThrottledOutputStreamPrivate)) /* private data */ struct _EvdThrottledOutputStreamPrivate { GList *stream_throttles; }; /* signals */ enum { SIGNAL_DELAY_WRITE, SIGNAL_LAST }; static guint evd_throttled_output_stream_signals[SIGNAL_LAST] = { 0 }; static void evd_throttled_output_stream_class_init (EvdThrottledOutputStreamClass *class); static void evd_throttled_output_stream_init (EvdThrottledOutputStream *self); static void evd_throttled_output_stream_dispose (GObject *obj); static gssize evd_throttled_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error); static void flush_async (GOutputStream *stream, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static gboolean flush_finish (GOutputStream *stream, GAsyncResult *res, GError **error); static void evd_throttled_output_stream_class_init (EvdThrottledOutputStreamClass *class) { GObjectClass *obj_class; GOutputStreamClass *output_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_throttled_output_stream_dispose; output_stream_class = G_OUTPUT_STREAM_CLASS (class); output_stream_class->write_fn = evd_throttled_output_stream_write; output_stream_class->flush_async = flush_async; output_stream_class->flush_finish = flush_finish; evd_throttled_output_stream_signals[SIGNAL_DELAY_WRITE] = g_signal_new ("delay-write", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdThrottledOutputStreamClass, delay_write), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_type_class_add_private (obj_class, sizeof (EvdThrottledOutputStreamPrivate)); } static void evd_throttled_output_stream_init (EvdThrottledOutputStream *self) { EvdThrottledOutputStreamPrivate *priv; priv = EVD_THROTTLED_OUTPUT_STREAM_GET_PRIVATE (self); self->priv = priv; priv->stream_throttles = NULL; } static void evd_throttled_output_stream_dispose (GObject *obj) { EvdThrottledOutputStream *self = EVD_THROTTLED_OUTPUT_STREAM (obj); g_list_free_full (self->priv->stream_throttles, g_object_unref); self->priv->stream_throttles = NULL; G_OBJECT_CLASS (evd_throttled_output_stream_parent_class)->dispose (obj); } static void evd_throttled_output_stream_report_size (EvdStreamThrottle *throttle, gsize *size) { evd_stream_throttle_report (throttle, *size); } static gsize evd_throttled_output_stream_get_max_writable_priv (EvdThrottledOutputStream *self, gsize size, guint *retry_wait) { GList *node; guint _retry_wait = 0; node = self->priv->stream_throttles; while (node != NULL) { EvdStreamThrottle *throttle; throttle = EVD_STREAM_THROTTLE (node->data); size = MIN (size, evd_stream_throttle_request (throttle, size, &_retry_wait)); node = node->next; } if (_retry_wait > 0) { g_signal_emit (self, evd_throttled_output_stream_signals[SIGNAL_DELAY_WRITE], 0, _retry_wait, NULL); } if (retry_wait != NULL) *retry_wait = _retry_wait; return size; } static gssize evd_throttled_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdThrottledOutputStream *self = EVD_THROTTLED_OUTPUT_STREAM (stream); gssize actual_size = 0; gsize limited_size; guint wait = 0; if (size == 0) return 0; limited_size = MIN (size, evd_throttled_output_stream_get_max_writable_priv ( EVD_THROTTLED_OUTPUT_STREAM (stream), size, &wait)); if (limited_size > 0) { GOutputStream *base_stream; base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream)); actual_size = g_output_stream_write (base_stream, buffer, limited_size, cancellable, error); if (actual_size > 0) { g_list_foreach (self->priv->stream_throttles, (GFunc) evd_throttled_output_stream_report_size, &actual_size); } } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Resource temporarily unavailable"); actual_size = -1; } return actual_size; } static void base_stream_on_flush (GObject *obj, GAsyncResult *result, gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); GError *error = NULL; GOutputStream *stream; stream = G_OUTPUT_STREAM (g_async_result_get_source_object (G_ASYNC_RESULT (res))); g_output_stream_clear_pending (stream); if (! g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), result, &error)) { g_simple_async_result_take_error (res, error); } g_simple_async_result_complete (res); g_object_unref (res); /* this is because g_async_result_get_source_object() increases reference count */ g_object_unref (stream); } static void flush_async (GOutputStream *stream, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; GOutputStream *base_stream; res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, flush_async); base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream)); g_output_stream_flush_async (base_stream, io_priority, cancellable, base_stream_on_flush, res); } static gboolean flush_finish (GOutputStream *stream, GAsyncResult *res, GError **error) { g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (stream), flush_async), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } /* public methods */ EvdThrottledOutputStream * evd_throttled_output_stream_new (GOutputStream *base_stream) { EvdThrottledOutputStream *self; g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_THROTTLED_OUTPUT_STREAM, "base-stream", base_stream, NULL); return self; } /** * evd_throttled_output_stream_get_max_readable: * @retry_wait: (out): * **/ gsize evd_throttled_output_stream_get_max_writable (EvdThrottledOutputStream *self, guint *retry_wait) { g_return_val_if_fail (EVD_IS_THROTTLED_OUTPUT_STREAM (self), 0); return evd_throttled_output_stream_get_max_writable_priv (self, G_MAXSSIZE, retry_wait); } void evd_throttled_output_stream_add_throttle (EvdThrottledOutputStream *self, EvdStreamThrottle *throttle) { g_return_if_fail (EVD_IS_THROTTLED_OUTPUT_STREAM (self)); g_return_if_fail (EVD_IS_STREAM_THROTTLE (throttle)); if (g_list_find (self->priv->stream_throttles, throttle) == NULL) { g_object_ref (throttle); self->priv->stream_throttles = g_list_prepend (self->priv->stream_throttles, (gpointer) throttle); } } void evd_throttled_output_stream_remove_throttle (EvdThrottledOutputStream *self, EvdStreamThrottle *throttle) { g_return_if_fail (EVD_IS_THROTTLED_OUTPUT_STREAM (self)); g_return_if_fail (EVD_IS_STREAM_THROTTLE (throttle)); if (g_list_find (self->priv->stream_throttles, throttle) != NULL) { self->priv->stream_throttles = g_list_remove (self->priv->stream_throttles, (gpointer) throttle); g_object_unref (throttle); } } EventDance-0.2.0/evd/evd-throttled-output-stream.h000066400000000000000000000064211321356073300220760ustar00rootroot00000000000000/* * evd-throttled-output-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_THROTTLED_OUTPUT_STREAM_H__ #define __EVD_THROTTLED_OUTPUT_STREAM_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-stream-throttle.h" G_BEGIN_DECLS typedef struct _EvdThrottledOutputStream EvdThrottledOutputStream; typedef struct _EvdThrottledOutputStreamClass EvdThrottledOutputStreamClass; typedef struct _EvdThrottledOutputStreamPrivate EvdThrottledOutputStreamPrivate; struct _EvdThrottledOutputStream { GFilterOutputStream parent; EvdThrottledOutputStreamPrivate *priv; }; struct _EvdThrottledOutputStreamClass { GFilterOutputStreamClass parent_class; /* signal prototypes */ void (* delay_write) (EvdThrottledOutputStream *self, guint wait, gpointer user_data); }; #define EVD_TYPE_THROTTLED_OUTPUT_STREAM (evd_throttled_output_stream_get_type ()) #define EVD_THROTTLED_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_THROTTLED_OUTPUT_STREAM, EvdThrottledOutputStream)) #define EVD_THROTTLED_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_THROTTLED_OUTPUT_STREAM, EvdThrottledOutputStreamClass)) #define EVD_IS_THROTTLED_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_THROTTLED_OUTPUT_STREAM)) #define EVD_IS_THROTTLED_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_THROTTLED_OUTPUT_STREAM)) #define EVD_THROTTLED_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_THROTTLED_OUTPUT_STREAM, EvdThrottledOutputStreamClass)) GType evd_throttled_output_stream_get_type (void) G_GNUC_CONST; EvdThrottledOutputStream *evd_throttled_output_stream_new (GOutputStream *base_stream); gsize evd_throttled_output_stream_get_max_writable (EvdThrottledOutputStream *self, guint *retry_wait); void evd_throttled_output_stream_add_throttle (EvdThrottledOutputStream *self, EvdStreamThrottle *throttle); void evd_throttled_output_stream_remove_throttle (EvdThrottledOutputStream *self, EvdStreamThrottle *throttle); G_END_DECLS #endif /* __EVD_THROTTLED_OUTPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-tls-certificate.c000066400000000000000000000436021321356073300203150ustar00rootroot00000000000000/* * evd-tls-certificate.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include "evd-error.h" #include "evd-tls-common.h" #include "evd-tls-certificate.h" G_DEFINE_TYPE (EvdTlsCertificate, evd_tls_certificate, G_TYPE_OBJECT) #define EVD_TLS_CERTIFICATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_CERTIFICATE, \ EvdTlsCertificatePrivate)) /* private data */ struct _EvdTlsCertificatePrivate { gnutls_x509_crt_t x509_cert; gnutls_openpgp_crt_t openpgp_cert; EvdTlsCertificateType type; gboolean native_stolen; }; /* properties */ enum { PROP_0, PROP_TYPE }; static void evd_tls_certificate_class_init (EvdTlsCertificateClass *class); static void evd_tls_certificate_init (EvdTlsCertificate *self); static void evd_tls_certificate_finalize (GObject *obj); static void evd_tls_certificate_dispose (GObject *obj); static void evd_tls_certificate_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_tls_certificate_cleanup (EvdTlsCertificate *self); static void evd_tls_certificate_class_init (EvdTlsCertificateClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_tls_certificate_dispose; obj_class->finalize = evd_tls_certificate_finalize; obj_class->get_property = evd_tls_certificate_get_property; /* install properties */ g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_uint ("type", "Certificate type", "The type of certificate (X.509 or OPENPGP)", EVD_TLS_CERTIFICATE_TYPE_UNKNOWN, EVD_TLS_CERTIFICATE_TYPE_OPENPGP, EVD_TLS_CERTIFICATE_TYPE_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdTlsCertificatePrivate)); } static void evd_tls_certificate_init (EvdTlsCertificate *self) { EvdTlsCertificatePrivate *priv; priv = EVD_TLS_CERTIFICATE_GET_PRIVATE (self); self->priv = priv; priv->x509_cert = NULL; priv->openpgp_cert = NULL; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; priv->native_stolen = FALSE; } static void evd_tls_certificate_dispose (GObject *obj) { G_OBJECT_CLASS (evd_tls_certificate_parent_class)->dispose (obj); } static void evd_tls_certificate_finalize (GObject *obj) { EvdTlsCertificate *self = EVD_TLS_CERTIFICATE (obj); evd_tls_certificate_cleanup (self); G_OBJECT_CLASS (evd_tls_certificate_parent_class)->finalize (obj); } static void evd_tls_certificate_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsCertificate *self; self = EVD_TLS_CERTIFICATE (obj); switch (prop_id) { case PROP_TYPE: g_value_set_uint (value, self->priv->type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_certificate_cleanup (EvdTlsCertificate *self) { if (self->priv->x509_cert != NULL) { if (! self->priv->native_stolen) gnutls_x509_crt_deinit (self->priv->x509_cert); self->priv->x509_cert = NULL; } if (self->priv->openpgp_cert != NULL) { if (! self->priv->native_stolen) gnutls_openpgp_crt_deinit (self->priv->openpgp_cert); self->priv->openpgp_cert = NULL; } self->priv->native_stolen = FALSE; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; } static EvdTlsCertificateType evd_tls_certificate_detect_type (const gchar *raw_data) { if (g_strstr_len (raw_data, 22, "BEGIN CERTIFICATE") != NULL) return EVD_TLS_CERTIFICATE_TYPE_X509; else if (g_strstr_len (raw_data, 22, "BEGIN PGP ") != NULL) return EVD_TLS_CERTIFICATE_TYPE_OPENPGP; else return EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; } static gboolean evd_tls_certificate_import_x509 (EvdTlsCertificate *self, const gchar *raw_data, gsize size, gnutls_x509_crt_fmt_t format, GError **error) { gint err_code; gnutls_x509_crt_t cert; err_code = gnutls_x509_crt_init (&cert); if (err_code == GNUTLS_E_SUCCESS) { gnutls_datum_t datum = { NULL, 0 }; datum.data = (void *) raw_data; datum.size = size; err_code = gnutls_x509_crt_import (cert, &datum, format); } if (! evd_error_propagate_gnutls (err_code, error)) { evd_tls_certificate_cleanup (self); self->priv->x509_cert = cert; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_X509; return TRUE; } return FALSE; } static void evd_tls_certificate_import_from_file_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdTlsCertificate *self = EVD_TLS_CERTIFICATE (object); gchar *filename; gchar *content; gsize size; GError *error = NULL; filename = g_simple_async_result_get_op_res_gpointer (res); if (! g_file_get_contents (filename, &content, &size, &error) || ! evd_tls_certificate_import (self, content, size, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_free (content); g_free (filename); g_object_unref (res); } /* public methods */ EvdTlsCertificate * evd_tls_certificate_new (void) { EvdTlsCertificate *self; self = g_object_new (EVD_TYPE_TLS_CERTIFICATE, NULL); return self; } gboolean evd_tls_certificate_import (EvdTlsCertificate *self, const gchar *raw_data, gsize size, GError **error) { EvdTlsCertificateType type; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), FALSE); g_return_val_if_fail (raw_data != NULL, FALSE); type = evd_tls_certificate_detect_type (raw_data); switch (type) { case EVD_TLS_CERTIFICATE_TYPE_X509: { if (evd_tls_certificate_import_x509 (self, raw_data, size, GNUTLS_X509_FMT_PEM, error)) { return TRUE; } break; } case EVD_TLS_CERTIFICATE_TYPE_OPENPGP: { gint err_code; gnutls_openpgp_crt_t cert; err_code = gnutls_openpgp_crt_init (&cert); if (err_code == GNUTLS_E_SUCCESS) { gnutls_datum_t datum = { NULL, 0 }; datum.data = (void *) raw_data; datum.size = size; err_code = gnutls_openpgp_crt_import (cert, &datum, GNUTLS_OPENPGP_FMT_BASE64); } if (! evd_error_propagate_gnutls (err_code, error)) { evd_tls_certificate_cleanup (self); self->priv->openpgp_cert = cert; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_OPENPGP; return TRUE; } break; } default: { /* probe DER format */ if (evd_tls_certificate_import_x509 (self, raw_data, size, GNUTLS_X509_FMT_DER, NULL)) { return TRUE; } else { if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unable to detect certificate type when trying to import"); } break; } }; return FALSE; } void evd_tls_certificate_import_from_file (EvdTlsCertificate *self, const gchar *filename, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_TLS_CERTIFICATE (self)); g_return_if_fail (filename != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_tls_certificate_import_from_file); g_simple_async_result_set_op_res_gpointer (res, g_strdup (filename), NULL); g_simple_async_result_run_in_thread (res, evd_tls_certificate_import_from_file_thread, G_PRIORITY_DEFAULT, cancellable); } gboolean evd_tls_certificate_import_from_file_finish (EvdTlsCertificate *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_tls_certificate_import_from_file), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } /** * evd_tls_certificate_get_native: * * Returns: (transfer none): **/ gpointer evd_tls_certificate_get_native (EvdTlsCertificate *self) { g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), NULL); if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_X509) return self->priv->x509_cert; else if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_OPENPGP) return self->priv->openpgp_cert; else return NULL; } /** * evd_tls_certificate_steal_native: * * Returns: (transfer full): **/ gpointer evd_tls_certificate_steal_native (EvdTlsCertificate *self) { gpointer native; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), NULL); native = evd_tls_certificate_get_native (self); if (native != NULL) self->priv->native_stolen = TRUE; return native; } gchar * evd_tls_certificate_get_dn (EvdTlsCertificate *self, GError **error) { gchar *dn = NULL; gint ret; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), NULL); switch (self->priv->type) { case EVD_TLS_CERTIFICATE_TYPE_X509: { gchar *buf = NULL; gsize size = 0; ret = gnutls_x509_crt_get_dn (self->priv->x509_cert, NULL, &size); if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { buf = g_new (gchar, size); ret = gnutls_x509_crt_get_dn (self->priv->x509_cert, buf, &size); if (ret == GNUTLS_E_SUCCESS) dn = buf; } else { evd_error_propagate_gnutls (ret, error); } break; } case EVD_TLS_CERTIFICATE_TYPE_OPENPGP: { gchar *buf = NULL; gsize size = 1; buf = g_new (gchar, 1); ret = gnutls_openpgp_crt_get_name (self->priv->openpgp_cert, 0, buf, &size); if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { g_free (buf); buf = g_new (gchar, size); ret = gnutls_openpgp_crt_get_name (self->priv->openpgp_cert, 0, buf, &size); if (ret == GNUTLS_E_SUCCESS) dn = buf; } else { evd_error_propagate_gnutls (ret, error); } break; } default: if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Certificate not initialized when requesting 'dn'"); break; }; return dn; } time_t evd_tls_certificate_get_expiration_time (EvdTlsCertificate *self, GError **error) { time_t time = -1; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), -1); switch (self->priv->type) { case EVD_TLS_CERTIFICATE_TYPE_X509: { time = gnutls_x509_crt_get_expiration_time (self->priv->x509_cert); if (time == -1 && error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to obtain expiration time from X.509 certificate"); break; } case EVD_TLS_CERTIFICATE_TYPE_OPENPGP: { time = gnutls_openpgp_crt_get_expiration_time (self->priv->openpgp_cert); if (time == -1 && error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to obtain expiration time from OpenPGP certificate"); break; } default: if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Certificate not initialized when requesting expiration time"); break; }; return time; } time_t evd_tls_certificate_get_activation_time (EvdTlsCertificate *self, GError **error) { time_t time = -1; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), -1); switch (self->priv->type) { case EVD_TLS_CERTIFICATE_TYPE_X509: { time = gnutls_x509_crt_get_activation_time (self->priv->x509_cert); if (time == -1 && error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to obtain activation time from X.509 certificate"); break; } case EVD_TLS_CERTIFICATE_TYPE_OPENPGP: { time = gnutls_openpgp_crt_get_creation_time (self->priv->openpgp_cert); if (time == -1 && error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to obtain activation time from OpenPGP certificate"); break; } default: if (error != NULL) *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Certificate not initialized when requesting activation time"); break; }; return time; } gint evd_tls_certificate_verify_validity (EvdTlsCertificate *self, GError **error) { gint result = EVD_TLS_VERIFY_STATE_OK; time_t time_from; time_t time_to; time_t now; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), -1); if ( (time_from = evd_tls_certificate_get_expiration_time (self, error)) == -1 || (time_to = evd_tls_certificate_get_activation_time (self, error)) == -1 ) return -1; now = time (NULL); if (time_from < now) result |= EVD_TLS_VERIFY_STATE_EXPIRED; if (time_to > now) result |= EVD_TLS_VERIFY_STATE_NOT_ACTIVE; return result; } /** * evd_tls_certificate_get_pki_key: * * Returns: (transfer full): **/ EvdPkiPubkey * evd_tls_certificate_get_pki_key (EvdTlsCertificate *self, GError **error) { EvdPkiPubkey *key = NULL; gnutls_pubkey_t pubkey = NULL; gint err_code; g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (self), NULL); if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_UNKNOWN) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to get key from not initialized private key"); return NULL; } err_code = gnutls_pubkey_init (&pubkey); if (evd_error_propagate_gnutls (err_code, error)) return NULL; if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_X509) err_code = gnutls_pubkey_import_x509 (pubkey, self->priv->x509_cert, 0); else if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_OPENPGP) err_code = gnutls_pubkey_import_openpgp (pubkey, self->priv->openpgp_cert, 0); if (evd_error_propagate_gnutls (err_code, error)) { gnutls_pubkey_deinit (pubkey); return NULL; } else { key = evd_pki_pubkey_new (); if (! evd_pki_pubkey_import_native (key, pubkey, error)) { gnutls_pubkey_deinit (pubkey); g_object_unref (key); key = NULL; } } return key; } EventDance-0.2.0/evd/evd-tls-certificate.h000066400000000000000000000105111321356073300203130ustar00rootroot00000000000000/* * evd-tls-certificate.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_CERTIFICATE_H__ #define __EVD_TLS_CERTIFICATE_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-pki-pubkey.h" G_BEGIN_DECLS typedef struct _EvdTlsCertificate EvdTlsCertificate; typedef struct _EvdTlsCertificateClass EvdTlsCertificateClass; typedef struct _EvdTlsCertificatePrivate EvdTlsCertificatePrivate; struct _EvdTlsCertificate { GObject parent; EvdTlsCertificatePrivate *priv; }; struct _EvdTlsCertificateClass { GObjectClass parent_class; }; #define EVD_TYPE_TLS_CERTIFICATE (evd_tls_certificate_get_type ()) #define EVD_TLS_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_CERTIFICATE, EvdTlsCertificate)) #define EVD_TLS_CERTIFICATE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_CERTIFICATE, EvdTlsCertificateClass)) #define EVD_IS_TLS_CERTIFICATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_CERTIFICATE)) #define EVD_IS_TLS_CERTIFICATE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_CERTIFICATE)) #define EVD_TLS_CERTIFICATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_CERTIFICATE, EvdTlsCertificateClass)) GType evd_tls_certificate_get_type (void) G_GNUC_CONST; EvdTlsCertificate *evd_tls_certificate_new (void); gboolean evd_tls_certificate_import (EvdTlsCertificate *self, const gchar *raw_data, gsize len, GError **error); gpointer evd_tls_certificate_get_native (EvdTlsCertificate *self); gpointer evd_tls_certificate_steal_native (EvdTlsCertificate *self); gchar *evd_tls_certificate_get_dn (EvdTlsCertificate *self, GError **error); time_t evd_tls_certificate_get_expiration_time (EvdTlsCertificate *self, GError **error); time_t evd_tls_certificate_get_activation_time (EvdTlsCertificate *self, GError **error); gint evd_tls_certificate_verify_validity (EvdTlsCertificate *self, GError **error); void evd_tls_certificate_import_from_file (EvdTlsCertificate *self, const gchar *filename, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_tls_certificate_import_from_file_finish (EvdTlsCertificate *self, GAsyncResult *result, GError **error); EvdPkiPubkey * evd_tls_certificate_get_pki_key (EvdTlsCertificate *self, GError **error); G_END_DECLS #endif /* __EVD_TLS_CERTIFICATE_H__ */ EventDance-0.2.0/evd/evd-tls-common.c000066400000000000000000000064371321356073300173300ustar00rootroot00000000000000/* * evd-tls-common.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include #include #include "evd-error.h" #include "evd-tls-common.h" #include "evd-tls-dh-generator.h" G_LOCK_DEFINE_STATIC (evd_tls_init); static gboolean evd_tls_initialized = FALSE; static EvdTlsDhGenerator *evd_tls_dh_gen; gboolean evd_tls_init (GError **error) { gboolean result = FALSE; G_LOCK (evd_tls_init); if (! evd_tls_initialized) { gint err_code; #ifndef GLIB_VERSION_2_36 g_type_init (); #endif #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_thread_init (NULL); #endif err_code = gnutls_global_init (); if (! evd_error_propagate_gnutls (err_code, error)) { evd_tls_dh_gen = evd_tls_dh_generator_new (); result = TRUE; } else { result = FALSE; } evd_tls_initialized = TRUE; } G_UNLOCK (evd_tls_init); return result; } void evd_tls_deinit (void) { G_LOCK (evd_tls_init); if (evd_tls_initialized) { g_object_unref (evd_tls_dh_gen); /* check why after calling 'gnutls_global_deinit', calling again 'gnutls_global_init' throws a segfault */ /* gnutls_global_deinit (); */ evd_tls_initialized = FALSE; } G_UNLOCK (evd_tls_init); } /** * evd_tls_free_certificates: * @certificates: (element-type Evd.TlsCertificate): * **/ void evd_tls_free_certificates (GList *certificates) { GList *node; node = certificates; while (node != NULL) { GObject *cert; cert = G_OBJECT (node->data); g_object_unref (cert); node = node->next; } g_list_free (certificates); } void evd_tls_generate_dh_params (guint bit_length, gboolean regenerate, GAsyncReadyCallback callback, GCancellable *cancellable, gpointer user_data) { evd_tls_dh_generator_generate (evd_tls_dh_gen, bit_length, regenerate, callback, cancellable, user_data); } /** * evd_tls_generate_dh_params_finish: * * Returns: (transfer none): **/ gpointer evd_tls_generate_dh_params_finish (GAsyncResult *result, GError **error) { return evd_tls_dh_generator_generate_finish (evd_tls_dh_gen, result, error); } EventDance-0.2.0/evd/evd-tls-common.h000066400000000000000000000050151321356073300173240ustar00rootroot00000000000000/* * evd-tls-common.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_COMMON_H__ #define __EVD_TLS_COMMON_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS /* TLS mode (client vs. server) */ typedef enum { EVD_TLS_MODE_CLIENT = GNUTLS_CLIENT, EVD_TLS_MODE_SERVER = GNUTLS_SERVER } EvdTlsMode; typedef enum { EVD_TLS_CERTIFICATE_TYPE_UNKNOWN = GNUTLS_CRT_UNKNOWN, EVD_TLS_CERTIFICATE_TYPE_X509 = GNUTLS_CRT_X509, EVD_TLS_CERTIFICATE_TYPE_OPENPGP = GNUTLS_CRT_OPENPGP } EvdTlsCertificateType; typedef enum { EVD_TLS_VERIFY_STATE_OK = 0, EVD_TLS_VERIFY_STATE_NO_CERT = 1 << 0, EVD_TLS_VERIFY_STATE_INVALID = 1 << 1, EVD_TLS_VERIFY_STATE_REVOKED = 1 << 2, EVD_TLS_VERIFY_STATE_SIGNER_NOT_FOUND = 1 << 3, EVD_TLS_VERIFY_STATE_SIGNER_NOT_CA = 1 << 4, EVD_TLS_VERIFY_STATE_INSECURE_ALG = 1 << 5, EVD_TLS_VERIFY_STATE_EXPIRED = 1 << 6, EVD_TLS_VERIFY_STATE_NOT_ACTIVE = 1 << 7 } EvdTlsVerifyState; gboolean evd_tls_init (GError **error); void evd_tls_deinit (void); void evd_tls_free_certificates (GList *certificates); void evd_tls_generate_dh_params (guint bit_length, gboolean regenerate, GAsyncReadyCallback callback, GCancellable *cancellable, gpointer user_data); gpointer evd_tls_generate_dh_params_finish (GAsyncResult *result, GError **error); G_END_DECLS #endif /* __EVD_TLS_COMMON_H__ */ EventDance-0.2.0/evd/evd-tls-credentials.c000066400000000000000000000503331321356073300203270ustar00rootroot00000000000000/* * evd-tls-credentials.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-tls-credentials.h" #include "evd-error.h" G_DEFINE_TYPE (EvdTlsCredentials, evd_tls_credentials, G_TYPE_OBJECT) #define EVD_TLS_CREDENTIALS_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_CREDENTIALS, \ EvdTlsCredentialsPrivate)) #define MAX_DYNAMIC_CERTS 8 /* private data */ struct _EvdTlsCredentialsPrivate { gnutls_certificate_credentials_t cred; guint dh_bits; gnutls_dh_params_t dh_params; gboolean ready; gboolean preparing; EvdTlsCredentialsCertCb cert_cb; gpointer cert_cb_user_data; GDestroyNotify cert_cb_user_data_free_func; gpointer cert_cb_certs[MAX_DYNAMIC_CERTS]; gnutls_retr2_st *cert_cb_ret_st; gboolean inside_cert_cb; gint cert_cb_result; guint async_ops_count; GList *x509_privkeys; GList *openpgp_privkeys; }; struct CertData { gchar *cert_file; gchar *key_file; }; /* signals */ enum { SIGNAL_READY, SIGNAL_LAST }; static guint evd_tls_credentials_signals[SIGNAL_LAST] = { 0 }; /* properties */ enum { PROP_0, PROP_DH_BITS }; static void evd_tls_credentials_class_init (EvdTlsCredentialsClass *class); static void evd_tls_credentials_init (EvdTlsCredentials *self); static void evd_tls_credentials_finalize (GObject *obj); static void evd_tls_credentials_dispose (GObject *obj); static void evd_tls_credentials_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_tls_credentials_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_tls_credentials_free_x509_key (gpointer data); static void evd_tls_credentials_free_openpgp_key (gpointer data); static void evd_tls_credentials_class_init (EvdTlsCredentialsClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_tls_credentials_dispose; obj_class->finalize = evd_tls_credentials_finalize; obj_class->get_property = evd_tls_credentials_get_property; obj_class->set_property = evd_tls_credentials_set_property; evd_tls_credentials_signals[SIGNAL_READY] = g_signal_new ("ready", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdTlsCredentialsClass, ready), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /* install properties */ g_object_class_install_property (obj_class, PROP_DH_BITS, g_param_spec_uint ("dh-bits", "DH parameters's depth", "Bit depth of the Diffie-Hellman key exchange parameters to use during handshake", 0, 4096, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdTlsCredentialsPrivate)); } static void evd_tls_credentials_init (EvdTlsCredentials *self) { EvdTlsCredentialsPrivate *priv; priv = EVD_TLS_CREDENTIALS_GET_PRIVATE (self); self->priv = priv; priv->cred = NULL; priv->dh_params = NULL; priv->ready = FALSE; priv->preparing = FALSE; priv->cert_cb = NULL; priv->cert_cb_user_data = NULL; priv->cert_cb_user_data_free_func = NULL; priv->cert_cb_result = 0; priv->inside_cert_cb = FALSE; priv->async_ops_count = 0; priv->x509_privkeys = NULL; priv->openpgp_privkeys = NULL; } static void evd_tls_credentials_dispose (GObject *obj) { EvdTlsCredentials *self = EVD_TLS_CREDENTIALS (obj); if (self->priv->cert_cb_user_data != NULL && self->priv->cert_cb_user_data_free_func != NULL) { self->priv->cert_cb_user_data_free_func (self->priv->cert_cb_user_data); self->priv->cert_cb_user_data = NULL; } G_OBJECT_CLASS (evd_tls_credentials_parent_class)->dispose (obj); } static void evd_tls_credentials_finalize (GObject *obj) { EvdTlsCredentials *self = EVD_TLS_CREDENTIALS (obj); if (self->priv->cred != NULL) gnutls_certificate_free_credentials (self->priv->cred); if (self->priv->dh_params != NULL) gnutls_dh_params_deinit (self->priv->dh_params); g_list_free_full (self->priv->x509_privkeys, evd_tls_credentials_free_x509_key); g_list_free_full (self->priv->openpgp_privkeys, evd_tls_credentials_free_openpgp_key); G_OBJECT_CLASS (evd_tls_credentials_parent_class)->finalize (obj); } static void evd_tls_credentials_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdTlsCredentials *self; self = EVD_TLS_CREDENTIALS (obj); switch (prop_id) { case PROP_DH_BITS: if (g_value_get_uint (value) != self->priv->dh_bits) { self->priv->dh_bits = g_value_get_uint (value); if (self->priv->dh_params != NULL) { gnutls_dh_params_deinit (self->priv->dh_params); self->priv->dh_params = NULL; } self->priv->ready = FALSE; } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_credentials_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsCredentials *self; self = EVD_TLS_CREDENTIALS (obj); switch (prop_id) { case PROP_DH_BITS: g_value_set_uint (value, self->priv->dh_bits); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_credentials_free_x509_key (gpointer data) { gnutls_x509_privkey_deinit (data); } static void evd_tls_credentials_free_openpgp_key (gpointer data) { gnutls_openpgp_privkey_deinit (data); } static gint evd_tls_credentials_server_cert_cb (gnutls_session_t session, const gnutls_datum_t *req_ca_dn, gint nreqs, const gnutls_pk_algorithm_t *pk_algos, gint pk_algos_length, gnutls_retr2_st *st) { EvdTlsCredentials *self; EvdTlsSession *tls_session; tls_session = gnutls_transport_get_ptr (session); g_assert (EVD_IS_TLS_SESSION (tls_session)); self = evd_tls_session_get_credentials (tls_session); g_assert (self->priv->cert_cb != NULL); self->priv->cert_cb_ret_st = st; self->priv->cert_cb_ret_st->ncerts = 0; self->priv->cert_cb_ret_st->deinit_all = 0; self->priv->cert_cb_result = 0; self->priv->inside_cert_cb = TRUE; if (! self->priv->cert_cb (self, tls_session, NULL, NULL, self->priv->cert_cb_user_data)) { self->priv->cert_cb_result = -1; } self->priv->inside_cert_cb = FALSE; return self->priv->cert_cb_result; } /* @TODO static gint evd_tls_credentials_client_cert_cb (gnutls_session_t session, const gnutls_datum_t *req_ca_rdn, gint nreqs, const gnutls_pk_algorithm_t *sign_algos, int sign_algos_length, gnutls_retr_st *st) { } */ static gboolean evd_tls_credentials_prepare_finish (EvdTlsCredentials *self, GError **error) { if (self->priv->preparing) return TRUE; if (self->priv->cred == NULL) gnutls_certificate_allocate_credentials (&self->priv->cred); if (self->priv->cert_cb != NULL) { gnutls_certificate_set_retrieve_function (self->priv->cred, evd_tls_credentials_server_cert_cb); /* @TODO: client side cert retrieval disabled by now */ /* gnutls_certificate_client_set_retrieve_function (self->priv->cred, evd_tls_credentials_client_cert_cb); */ } if (self->priv->dh_bits != 0) gnutls_certificate_set_dh_params (self->priv->cred, self->priv->dh_params); if (self->priv->async_ops_count == 0) { self->priv->ready = TRUE; self->priv->preparing = FALSE; g_signal_emit (self, evd_tls_credentials_signals[SIGNAL_READY], 0, NULL); } return TRUE; } static void evd_tls_credentials_dh_params_ready (GObject *source_obj, GAsyncResult *res, gpointer user_data) { EvdTlsCredentials *self = EVD_TLS_CREDENTIALS (user_data); GError *error = NULL; if ( (self->priv->dh_params = evd_tls_generate_dh_params_finish (res, &error)) != NULL) { evd_tls_credentials_prepare_finish (self, &error); } else { /* @TODO: handle async error */ g_debug ("Error generating DH params: %s", error->message); } } static void add_certificate_from_file_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdTlsCredentials *self = EVD_TLS_CREDENTIALS (object); GError *error = NULL; struct CertData *data; gint err_code; data = g_simple_async_result_get_op_res_gpointer (res); if (self->priv->cred == NULL) gnutls_certificate_allocate_credentials (&self->priv->cred); /* @TODO: by now only X.509 certificates supported */ err_code = gnutls_certificate_set_x509_key_file (self->priv->cred, data->cert_file, data->key_file, GNUTLS_X509_FMT_PEM); if (evd_error_propagate_gnutls (err_code, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_free (data->cert_file); g_free (data->key_file); g_slice_free (struct CertData, data); g_object_unref (res); } /* public methods */ EvdTlsCredentials * evd_tls_credentials_new (void) { EvdTlsCredentials *self; self = g_object_new (EVD_TYPE_TLS_CREDENTIALS, NULL); return self; } gboolean evd_tls_credentials_ready (EvdTlsCredentials *self) { g_return_val_if_fail (EVD_IS_TLS_CREDENTIALS (self), FALSE); return self->priv->ready; } gboolean evd_tls_credentials_prepare (EvdTlsCredentials *self, GError **error) { g_return_val_if_fail (EVD_IS_TLS_CREDENTIALS (self), FALSE); if (self->priv->preparing) return TRUE; if (self->priv->dh_bits != 0 && self->priv->dh_params == NULL) { evd_tls_generate_dh_params (self->priv->dh_bits, 0, evd_tls_credentials_dh_params_ready, NULL, /* @TODO: functions ignores cancellable by now */ (gpointer) self); return TRUE; } else { return evd_tls_credentials_prepare_finish (self, error); } } /** * evd_tls_credentials_get_credentials: * * Returns: (transfer none): **/ gpointer evd_tls_credentials_get_credentials (EvdTlsCredentials *self) { g_return_val_if_fail (EVD_IS_TLS_CREDENTIALS (self), NULL); return self->priv->cred; } /** * evd_tls_credentials_set_cert_callback: * @self: * @callback: (allow-none): * @user_data: (allow-none): **/ void evd_tls_credentials_set_cert_callback (EvdTlsCredentials *self, EvdTlsCredentialsCertCb callback, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_TLS_CREDENTIALS (self)); if (self->priv->cert_cb_user_data != NULL && self->priv->cert_cb_user_data_free_func != NULL) { self->priv->cert_cb_user_data_free_func (self->priv->cert_cb_user_data); } self->priv->cert_cb = callback; self->priv->cert_cb_user_data = user_data; self->priv->cert_cb_user_data_free_func = user_data_free_func; if (self->priv->cred == NULL) gnutls_certificate_allocate_credentials (&self->priv->cred); gnutls_certificate_set_retrieve_function (self->priv->cred, evd_tls_credentials_server_cert_cb); /* @TODO: client cert retrieval disabled by now */ /* gnutls_certificate_client_set_retrieve_function (self->priv->cred, evd_tls_credentials_client_cert_cb); */ } gboolean evd_tls_credentials_add_certificate (EvdTlsCredentials *self, EvdTlsCertificate *cert, EvdTlsPrivkey *privkey, GError **error) { gpointer _cert; gpointer _privkey; EvdTlsCertificateType cert_type; EvdTlsCertificateType key_type; g_return_val_if_fail (EVD_IS_TLS_CREDENTIALS (self), FALSE); g_return_val_if_fail (EVD_IS_TLS_CERTIFICATE (cert), FALSE); g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (privkey), FALSE); g_object_get (cert, "type", &cert_type, NULL); g_object_get (privkey, "type", &key_type, NULL); if (cert_type == EVD_TLS_CERTIFICATE_TYPE_UNKNOWN || key_type == EVD_TLS_CERTIFICATE_TYPE_UNKNOWN) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Invalid certificate or key type"); return FALSE; } if (cert_type != key_type) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Certificate and private key do not match type"); return FALSE; } _cert = evd_tls_certificate_get_native (cert); _privkey = evd_tls_privkey_steal_native (privkey); if (cert_type == EVD_TLS_CERTIFICATE_TYPE_X509) self->priv->x509_privkeys = g_list_append (self->priv->x509_privkeys, _privkey); else self->priv->openpgp_privkeys = g_list_append (self->priv->openpgp_privkeys, _privkey); if (_cert == NULL || _privkey == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Certificate or private key not initialized"); return FALSE; } if (self->priv->inside_cert_cb) { gnutls_retr2_st *ret_st; ret_st = self->priv->cert_cb_ret_st; if (ret_st->ncerts >= MAX_DYNAMIC_CERTS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS, "Too many certificates for credentials"); return FALSE; } else { self->priv->cert_cb_certs[ret_st->ncerts] = _cert; ret_st->ncerts++; if (cert_type == EVD_TLS_CERTIFICATE_TYPE_X509) { ret_st->cert_type = GNUTLS_CRT_X509; ret_st->cert.x509 = (gnutls_x509_crt_t *) self->priv->cert_cb_certs; ret_st->key.x509 = (gnutls_x509_privkey_t) _privkey; } else { ret_st->cert_type = GNUTLS_CRT_OPENPGP; ret_st->cert.pgp = (gnutls_openpgp_crt_t) _cert; ret_st->key.pgp = (gnutls_openpgp_privkey_t) _privkey; } } } else { gint err_code; if (self->priv->cred == NULL) gnutls_certificate_allocate_credentials (&self->priv->cred); if (cert_type == EVD_TLS_CERTIFICATE_TYPE_X509) { err_code = gnutls_certificate_set_x509_key (self->priv->cred, (gnutls_x509_crt_t *) &_cert, 1, (gnutls_x509_privkey_t) _privkey); } else { err_code = gnutls_certificate_set_openpgp_key (self->priv->cred, (gnutls_openpgp_crt_t) _cert, (gnutls_openpgp_privkey_t) _privkey); } if (evd_error_propagate_gnutls (err_code, error)) return FALSE; } return TRUE; } /** * evd_tls_credentials_add_certificate_from_file: * @cancellable: (allow-none): * @callback: (scope async) (allow-none): * @user_data: (allow-none): * **/ void evd_tls_credentials_add_certificate_from_file (EvdTlsCredentials *self, const gchar *cert_file, const gchar *key_file, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; struct CertData *data; g_return_if_fail (EVD_IS_TLS_CREDENTIALS (self)); g_return_if_fail (cert_file != NULL); g_return_if_fail (key_file != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_tls_credentials_add_certificate_from_file); data = g_slice_new0 (struct CertData); g_simple_async_result_set_op_res_gpointer (res, data, NULL); data->cert_file = g_strdup (cert_file); data->key_file = g_strdup (key_file); g_simple_async_result_run_in_thread (res, add_certificate_from_file_thread, G_PRIORITY_DEFAULT, cancellable); } gboolean evd_tls_credentials_add_certificate_from_file_finish (EvdTlsCredentials *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_TLS_CREDENTIALS (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_tls_credentials_add_certificate_from_file), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } EventDance-0.2.0/evd/evd-tls-credentials.h000066400000000000000000000126241321356073300203350ustar00rootroot00000000000000/* * evd-tls-credentials.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_CREDENTIALS_H__ #define __EVD_TLS_CREDENTIALS_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-tls-common.h" #include "evd-tls-session.h" #include "evd-tls-certificate.h" #include "evd-tls-privkey.h" G_BEGIN_DECLS typedef struct _EvdTlsCredentials EvdTlsCredentials; typedef struct _EvdTlsCredentialsClass EvdTlsCredentialsClass; typedef struct _EvdTlsCredentialsPrivate EvdTlsCredentialsPrivate; typedef gboolean (* EvdTlsCredentialsCertCb) (EvdTlsCredentials *self, EvdTlsSession *session, GList *ca_rdns, GList *algorithms, gpointer user_data); struct _EvdTlsCredentials { GObject parent; EvdTlsCredentialsPrivate *priv; }; struct _EvdTlsCredentialsClass { GObjectClass parent_class; /* signal prototypes */ void (* ready) (EvdTlsCredentials *self); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_TLS_CREDENTIALS (evd_tls_credentials_get_type ()) #define EVD_TLS_CREDENTIALS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_CREDENTIALS, EvdTlsCredentials)) #define EVD_TLS_CREDENTIALS_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_CREDENTIALS, EvdTlsCredentialsClass)) #define EVD_IS_TLS_CREDENTIALS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_CREDENTIALS)) #define EVD_IS_TLS_CREDENTIALS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_CREDENTIALS)) #define EVD_TLS_CREDENTIALS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_CREDENTIALS, EvdTlsCredentialsClass)) GType evd_tls_credentials_get_type (void) G_GNUC_CONST; EvdTlsCredentials *evd_tls_credentials_new (void); gboolean evd_tls_credentials_ready (EvdTlsCredentials *self); gboolean evd_tls_credentials_prepare (EvdTlsCredentials *self, GError **error); gpointer evd_tls_credentials_get_credentials (EvdTlsCredentials *self); void evd_tls_credentials_set_cert_callback (EvdTlsCredentials *self, EvdTlsCredentialsCertCb callback, gpointer user_data, GDestroyNotify user_data_free_func); gboolean evd_tls_credentials_add_certificate (EvdTlsCredentials *self, EvdTlsCertificate *cert, EvdTlsPrivkey *privkey, GError **error); void evd_tls_credentials_add_certificate_from_file (EvdTlsCredentials *self, const gchar *cert_file, const gchar *key_file, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_tls_credentials_add_certificate_from_file_finish (EvdTlsCredentials *self, GAsyncResult *result, GError **error); void evd_tls_session_set_credentials (EvdTlsSession *self, EvdTlsCredentials *credentials); EvdTlsCredentials *evd_tls_session_get_credentials (EvdTlsSession *self); G_END_DECLS #endif /* __EVD_TLS_CREDENTIALS_H__ */ EventDance-0.2.0/evd/evd-tls-dh-generator.c000066400000000000000000000257551321356073300204230ustar00rootroot00000000000000/* * evd-tls-dh-generator.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-error.h" #include "evd-tls-dh-generator.h" #include "evd-tls-common.h" G_DEFINE_TYPE (EvdTlsDhGenerator, evd_tls_dh_generator, G_TYPE_OBJECT) #define EVD_TLS_DH_GENERATOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_DH_GENERATOR, \ EvdTlsDhGeneratorPrivate)) struct _EvdTlsDhGeneratorPrivate { GHashTable *cache; #if (! GLIB_CHECK_VERSION(2, 31, 0)) GMutex *cache_mutex; #else GMutex cache_mutex; #endif }; typedef struct _EvdTlsDhParamsSource EvdTlsDhParamsSource; struct _EvdTlsDhParamsSource { guint dh_bits; gnutls_dh_params_t dh_params; GQueue *queue; #if (! GLIB_CHECK_VERSION(2, 31, 0)) GMutex *mutex; #else GMutex mutex; #endif EvdTlsDhGenerator *parent; }; static void evd_tls_dh_generator_class_init (EvdTlsDhGeneratorClass *class); static void evd_tls_dh_generator_init (EvdTlsDhGenerator *self); static void evd_tls_dh_generator_finalize (GObject *obj); static void evd_tls_dh_generator_free_source (EvdTlsDhParamsSource *source); static void evd_tls_dh_generator_class_init (EvdTlsDhGeneratorClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_tls_dh_generator_finalize; g_type_class_add_private (obj_class, sizeof (EvdTlsDhGeneratorPrivate)); } static void evd_tls_dh_generator_init (EvdTlsDhGenerator *self) { EvdTlsDhGeneratorPrivate *priv; priv = EVD_TLS_DH_GENERATOR_GET_PRIVATE (self); self->priv = priv; priv->cache = g_hash_table_new (g_int_hash, g_int_equal); #if (! GLIB_CHECK_VERSION(2, 31, 0)) priv->cache_mutex = g_mutex_new (); #else g_mutex_init (&priv->cache_mutex); #endif } static gboolean evd_tls_dh_generator_free_cache_item (gpointer key, gpointer value, gpointer user_data) { evd_tls_dh_generator_free_source ((EvdTlsDhParamsSource *) value); return TRUE; } static void evd_tls_dh_generator_finalize (GObject *obj) { EvdTlsDhGenerator *self = EVD_TLS_DH_GENERATOR (obj); g_hash_table_foreach_remove (self->priv->cache, evd_tls_dh_generator_free_cache_item, self); g_hash_table_destroy (self->priv->cache); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_free (self->priv->cache_mutex); #else g_mutex_clear (&self->priv->cache_mutex); #endif G_OBJECT_CLASS (evd_tls_dh_generator_parent_class)->finalize (obj); } static void evd_tls_dh_generator_free_source (EvdTlsDhParamsSource *source) { GSimpleAsyncResult *item; #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (source->mutex); #else g_mutex_lock (&source->mutex); #endif if (source->queue != NULL) { while ( (item = G_SIMPLE_ASYNC_RESULT (g_queue_pop_head (source->queue))) != NULL) { g_object_unref (item); } g_queue_free (source->queue); } if (source->dh_params != NULL) gnutls_dh_params_deinit (source->dh_params); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (source->mutex); g_mutex_free (source->mutex); #else g_mutex_unlock (&source->mutex); g_mutex_clear (&source->mutex); #endif g_slice_free (EvdTlsDhParamsSource, source); } static void evd_tls_dh_generator_generate_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdTlsDhParamsSource *source; gnutls_dh_params_t dh_params; gint err_code; GSimpleAsyncResult *item; GError *error = NULL; source = (EvdTlsDhParamsSource *) g_simple_async_result_get_source_tag (res); /* @TODO: handle cancellation */ err_code = gnutls_dh_params_init (&dh_params); if (err_code == GNUTLS_E_SUCCESS) err_code = gnutls_dh_params_generate2 (dh_params, source->dh_bits); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (source->mutex); #else g_mutex_lock (&source->mutex); #endif if (! evd_error_propagate_gnutls (err_code, &error)) source->dh_params = dh_params; while ( (item = G_SIMPLE_ASYNC_RESULT (g_queue_pop_head (source->queue))) != NULL) { if (error != NULL) { g_simple_async_result_set_from_error (item, error); } else { g_simple_async_result_set_op_res_gpointer (item, (gpointer) source->dh_params, NULL); } if (item != res) g_simple_async_result_complete_in_idle (item); g_object_unref (item); } g_queue_free (source->queue); source->queue = NULL; if (error != NULL) { g_error_free (error); g_hash_table_remove (source->parent->priv->cache, &source->dh_bits); evd_tls_dh_generator_free_source (source); } #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (source->mutex); #else g_mutex_unlock (&source->mutex); #endif } /* public methods */ EvdTlsDhGenerator * evd_tls_dh_generator_new (void) { EvdTlsDhGenerator *self; self = g_object_new (EVD_TYPE_TLS_DH_GENERATOR, NULL); return self; } void evd_tls_dh_generator_generate (EvdTlsDhGenerator *self, guint bit_length, gboolean regenerate, GAsyncReadyCallback callback, GCancellable *cancellable, gpointer user_data) { EvdTlsDhParamsSource *source; GSimpleAsyncResult *result; g_return_if_fail (bit_length > 0); g_return_if_fail (callback != NULL); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (self->priv->cache_mutex); #else g_mutex_lock (&self->priv->cache_mutex); #endif source = g_hash_table_lookup (self->priv->cache, &bit_length); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (self->priv->cache_mutex); #else g_mutex_unlock (&self->priv->cache_mutex); #endif if (source != NULL) { gboolean params_ready; gboolean done = FALSE; #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (source->mutex); #else g_mutex_lock (&source->mutex); #endif params_ready = (source->dh_params != NULL); result = g_simple_async_result_new (NULL, callback, user_data, NULL); if (params_ready) { if (! regenerate) { g_simple_async_result_set_op_res_gpointer (result, (gpointer) source->dh_params, NULL); g_simple_async_result_complete_in_idle (result); g_object_unref (result); done = TRUE; } else { g_object_unref (result); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (self->priv->cache_mutex); #else g_mutex_lock (&self->priv->cache_mutex); #endif g_hash_table_remove (self->priv->cache, &source->dh_bits); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (self->priv->cache_mutex); #else g_mutex_unlock (&self->priv->cache_mutex); #endif #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (source->mutex); #else g_mutex_unlock (&source->mutex); #endif evd_tls_dh_generator_free_source (source); source = NULL; } } else { g_queue_push_tail (source->queue, (gpointer) result); done = TRUE; } if (source != NULL) { #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (source->mutex); #else g_mutex_unlock (&source->mutex); #endif } if (done) return; } source = g_slice_new (EvdTlsDhParamsSource); source->dh_bits = bit_length; source->dh_params = NULL; source->queue = g_queue_new (); #if (! GLIB_CHECK_VERSION(2, 31, 0)) source->mutex = g_mutex_new (); #else g_mutex_init (&source->mutex); #endif source->parent = self; #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (self->priv->cache_mutex); #else g_mutex_lock (&self->priv->cache_mutex); #endif g_hash_table_insert (self->priv->cache, &source->dh_bits, source); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (self->priv->cache_mutex); #else g_mutex_unlock (&self->priv->cache_mutex); #endif result = g_simple_async_result_new (NULL, callback, user_data, (gpointer) source); /* append the result to the source queue, only to allow destroying it in case of premature freeing of the generator */ #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_lock (source->mutex); #else g_mutex_lock (&source->mutex); #endif g_queue_push_tail (source->queue, result); #if (! GLIB_CHECK_VERSION(2, 31, 0)) g_mutex_unlock (source->mutex); #else g_mutex_unlock (&source->mutex); #endif g_simple_async_result_run_in_thread (result, evd_tls_dh_generator_generate_func, G_PRIORITY_DEFAULT, cancellable); } /** * evd_tls_dh_generator_generate_finish: * * Returns: (transfer none): **/ gpointer evd_tls_dh_generator_generate_finish (EvdTlsDhGenerator *self, GAsyncResult *result, GError **error) { GSimpleAsyncResult *res; g_return_val_if_fail (EVD_IS_TLS_DH_GENERATOR (self), NULL); g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); res = G_SIMPLE_ASYNC_RESULT (result); if (g_simple_async_result_get_op_res_gpointer (res) == NULL) { g_simple_async_result_propagate_error (res, error); return NULL; } else { return g_simple_async_result_get_op_res_gpointer (res); } } EventDance-0.2.0/evd/evd-tls-dh-generator.h000066400000000000000000000055271321356073300204230ustar00rootroot00000000000000/* * evd-tls-dh-generator.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_DH_GENERATOR_H__ #define __EVD_TLS_DH_GENERATOR_H__ #include #include #include G_BEGIN_DECLS typedef struct _EvdTlsDhGenerator EvdTlsDhGenerator; typedef struct _EvdTlsDhGeneratorClass EvdTlsDhGeneratorClass; typedef struct _EvdTlsDhGeneratorPrivate EvdTlsDhGeneratorPrivate; struct _EvdTlsDhGenerator { GObject parent; EvdTlsDhGeneratorPrivate *priv; }; struct _EvdTlsDhGeneratorClass { GObjectClass parent_class; }; #define EVD_TYPE_TLS_DH_GENERATOR (evd_tls_dh_generator_get_type ()) #define EVD_TLS_DH_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_DH_GENERATOR, EvdTlsDhGenerator)) #define EVD_TLS_DH_GENERATOR_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_DH_GENERATOR, EvdTlsDhGeneratorClass)) #define EVD_IS_TLS_DH_GENERATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_DH_GENERATOR)) #define EVD_IS_TLS_DH_GENERATOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_DH_GENERATOR)) #define EVD_TLS_DH_GENERATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_DH_GENERATOR, EvdTlsDhGeneratorClass)) GType evd_tls_dh_generator_get_type (void) G_GNUC_CONST; EvdTlsDhGenerator *evd_tls_dh_generator_new (void); void evd_tls_dh_generator_generate (EvdTlsDhGenerator *self, guint bit_length, gboolean regenerate, GAsyncReadyCallback callback, GCancellable *cancellable, gpointer user_data); gpointer evd_tls_dh_generator_generate_finish (EvdTlsDhGenerator *self, GAsyncResult *result, GError **error); G_END_DECLS #endif /* __EVD_TLS_DH_GENERATOR_H__ */ EventDance-0.2.0/evd/evd-tls-input-stream.c000066400000000000000000000167671321356073300204770ustar00rootroot00000000000000/* * evd-tls-input-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-tls-input-stream.h" #include "evd-error.h" #include "evd-tls-session.h" G_DEFINE_TYPE (EvdTlsInputStream, evd_tls_input_stream, G_TYPE_FILTER_INPUT_STREAM) #define EVD_TLS_INPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_INPUT_STREAM, \ EvdTlsInputStreamPrivate)) /* private data */ struct _EvdTlsInputStreamPrivate { EvdTlsSession *session; }; /* properties */ enum { PROP_0, PROP_SESSION }; static void evd_tls_input_stream_class_init (EvdTlsInputStreamClass *class); static void evd_tls_input_stream_init (EvdTlsInputStream *self); static void evd_tls_input_stream_finalize (GObject *obj); static void evd_tls_input_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_tls_input_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gssize evd_tls_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_tls_input_stream_class_init (EvdTlsInputStreamClass *class) { GObjectClass *obj_class; GInputStreamClass *input_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_tls_input_stream_finalize; obj_class->get_property = evd_tls_input_stream_get_property; obj_class->set_property = evd_tls_input_stream_set_property; input_stream_class = G_INPUT_STREAM_CLASS (class); input_stream_class->read_fn = evd_tls_input_stream_read; g_object_class_install_property (obj_class, PROP_SESSION, g_param_spec_object ("session", "The TLS session", "The TLS session associated with this stream", EVD_TYPE_TLS_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdTlsInputStreamPrivate)); } static void evd_tls_input_stream_init (EvdTlsInputStream *self) { EvdTlsInputStreamPrivate *priv; priv = EVD_TLS_INPUT_STREAM_GET_PRIVATE (self); self->priv = priv; } static void evd_tls_input_stream_finalize (GObject *obj) { EvdTlsInputStream *self = EVD_TLS_INPUT_STREAM (obj); g_object_unref (self->priv->session); G_OBJECT_CLASS (evd_tls_input_stream_parent_class)->finalize (obj); } static void evd_tls_input_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdTlsInputStream *self; self = EVD_TLS_INPUT_STREAM (obj); switch (prop_id) { case PROP_SESSION: self->priv->session = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_input_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsInputStream *self; self = EVD_TLS_INPUT_STREAM (obj); switch (prop_id) { case PROP_SESSION: g_value_set_object (value, self->priv->session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gssize evd_tls_input_stream_pull (EvdTlsSession *session, gchar *buffer, gsize size, gpointer user_data, GError **error) { EvdTlsInputStream *self = EVD_TLS_INPUT_STREAM (user_data); gssize result; GInputStream *base_stream; base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (self)); result = g_input_stream_read (base_stream, buffer, size, NULL, error); if (result >= 0) { if (result < size) { if (! g_input_stream_has_pending (G_INPUT_STREAM (self)) && ! g_input_stream_set_pending (G_INPUT_STREAM (self), error)) { result = -1; } } else { g_input_stream_clear_pending (G_INPUT_STREAM (self)); } } return result; } static gssize evd_tls_input_stream_read (GInputStream *stream, void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdTlsInputStream *self = EVD_TLS_INPUT_STREAM (stream); GError *_error = NULL; gssize actual_size; actual_size = evd_tls_session_read (self->priv->session, buffer, size, &_error); /* hack to gracefully recover from peer abruptly closing TLS connection */ if (actual_size < 0) { if (g_error_matches (_error, EVD_GNUTLS_ERROR, GNUTLS_E_UNEXPECTED_PACKET_LENGTH)) { g_clear_error (&_error); actual_size = 0; g_input_stream_clear_pending (stream); g_input_stream_close (stream, NULL, error); } else { g_propagate_error (error, _error); } } return actual_size; } /* public methods */ EvdTlsInputStream * evd_tls_input_stream_new (EvdTlsSession *session, GInputStream *base_stream) { EvdTlsInputStream *self; g_return_val_if_fail (EVD_IS_TLS_SESSION (session), NULL); g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_TLS_INPUT_STREAM, "session", session, "base-stream", base_stream, NULL); evd_tls_session_set_transport_pull_func (session, evd_tls_input_stream_pull, self, NULL); return self; } EventDance-0.2.0/evd/evd-tls-input-stream.h000066400000000000000000000043521321356073300204670ustar00rootroot00000000000000/* * evd-tls-input-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_INPUT_STREAM_H__ #define __EVD_TLS_INPUT_STREAM_H__ #include #include #include "evd-tls-session.h" G_BEGIN_DECLS typedef struct _EvdTlsInputStream EvdTlsInputStream; typedef struct _EvdTlsInputStreamClass EvdTlsInputStreamClass; typedef struct _EvdTlsInputStreamPrivate EvdTlsInputStreamPrivate; struct _EvdTlsInputStream { GFilterInputStream parent; EvdTlsInputStreamPrivate *priv; }; struct _EvdTlsInputStreamClass { GFilterInputStreamClass parent_class; }; #define EVD_TYPE_TLS_INPUT_STREAM (evd_tls_input_stream_get_type ()) #define EVD_TLS_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_INPUT_STREAM, EvdTlsInputStream)) #define EVD_TLS_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_INPUT_STREAM, EvdTlsInputStreamClass)) #define EVD_IS_TLS_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_INPUT_STREAM)) #define EVD_IS_TLS_INPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_INPUT_STREAM)) #define EVD_TLS_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_INPUT_STREAM, EvdTlsInputStreamClass)) GType evd_tls_input_stream_get_type (void) G_GNUC_CONST; EvdTlsInputStream *evd_tls_input_stream_new (EvdTlsSession *session, GInputStream *base_stream); G_END_DECLS #endif /* __EVD_TLS_INPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-tls-output-stream.c000066400000000000000000000155341321356073300206670ustar00rootroot00000000000000/* * evd-tls-output-stream.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-tls-output-stream.h" G_DEFINE_TYPE (EvdTlsOutputStream, evd_tls_output_stream, EVD_TYPE_BUFFERED_OUTPUT_STREAM) #define EVD_TLS_OUTPUT_STREAM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_OUTPUT_STREAM, \ EvdTlsOutputStreamPrivate)) /* private data */ struct _EvdTlsOutputStreamPrivate { EvdTlsSession *session; }; /* properties */ enum { PROP_0, PROP_SESSION }; static void evd_tls_output_stream_class_init (EvdTlsOutputStreamClass *class); static void evd_tls_output_stream_init (EvdTlsOutputStream *self); static void evd_tls_output_stream_finalize (GObject *obj); static void evd_tls_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_tls_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static gssize evd_tls_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error); static void evd_tls_output_stream_class_init (EvdTlsOutputStreamClass *class) { GObjectClass *obj_class; GOutputStreamClass *output_stream_class; obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_tls_output_stream_finalize; obj_class->get_property = evd_tls_output_stream_get_property; obj_class->set_property = evd_tls_output_stream_set_property; output_stream_class = G_OUTPUT_STREAM_CLASS (class); output_stream_class->write_fn = evd_tls_output_stream_write; g_object_class_install_property (obj_class, PROP_SESSION, g_param_spec_object ("session", "The TLS session", "The TLS session associated with this stream", EVD_TYPE_TLS_SESSION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdTlsOutputStreamPrivate)); } static void evd_tls_output_stream_init (EvdTlsOutputStream *self) { EvdTlsOutputStreamPrivate *priv; priv = EVD_TLS_OUTPUT_STREAM_GET_PRIVATE (self); self->priv = priv; } static void evd_tls_output_stream_finalize (GObject *obj) { EvdTlsOutputStream *self = EVD_TLS_OUTPUT_STREAM (obj); g_object_unref (self->priv->session); G_OBJECT_CLASS (evd_tls_output_stream_parent_class)->finalize (obj); } static void evd_tls_output_stream_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdTlsOutputStream *self; self = EVD_TLS_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_SESSION: self->priv->session = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_output_stream_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsOutputStream *self; self = EVD_TLS_OUTPUT_STREAM (obj); switch (prop_id) { case PROP_SESSION: g_value_set_object (value, self->priv->session); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gssize evd_tls_output_stream_push (EvdTlsSession *session, const gchar *buffer, gsize size, gpointer user_data, GError **error) { EvdTlsOutputStream *self = EVD_TLS_OUTPUT_STREAM (user_data); gssize result; if (g_output_stream_is_closed (G_OUTPUT_STREAM (self))) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "TLS output stream is already closed"); return -1; } result = G_OUTPUT_STREAM_CLASS (evd_tls_output_stream_parent_class)-> write_fn (G_OUTPUT_STREAM (self), buffer, size, NULL, error); return result; } static gssize evd_tls_output_stream_write (GOutputStream *stream, const void *buffer, gsize size, GCancellable *cancellable, GError **error) { EvdTlsOutputStream *self = EVD_TLS_OUTPUT_STREAM (stream); return evd_tls_session_write (self->priv->session, buffer, size, error); } /* public methods */ EvdTlsOutputStream * evd_tls_output_stream_new (EvdTlsSession *session, GOutputStream *base_stream) { EvdTlsOutputStream *self; g_return_val_if_fail (EVD_IS_TLS_SESSION (session), NULL); g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL); self = g_object_new (EVD_TYPE_TLS_OUTPUT_STREAM, "session", session, "base-stream", base_stream, NULL); evd_tls_session_set_transport_push_func (session, evd_tls_output_stream_push, self, NULL); return self; } EventDance-0.2.0/evd/evd-tls-output-stream.h000066400000000000000000000044731321356073300206740ustar00rootroot00000000000000/* * evd-tls-output-stream.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_OUTPUT_STREAM_H__ #define __EVD_TLS_OUTPUT_STREAM_H__ #include #include #include "evd-buffered-output-stream.h" #include "evd-tls-session.h" G_BEGIN_DECLS typedef struct _EvdTlsOutputStream EvdTlsOutputStream; typedef struct _EvdTlsOutputStreamClass EvdTlsOutputStreamClass; typedef struct _EvdTlsOutputStreamPrivate EvdTlsOutputStreamPrivate; struct _EvdTlsOutputStream { EvdBufferedOutputStream parent; EvdTlsOutputStreamPrivate *priv; }; struct _EvdTlsOutputStreamClass { EvdBufferedOutputStreamClass parent_class; }; #define EVD_TYPE_TLS_OUTPUT_STREAM (evd_tls_output_stream_get_type ()) #define EVD_TLS_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_OUTPUT_STREAM, EvdTlsOutputStream)) #define EVD_TLS_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_OUTPUT_STREAM, EvdTlsOutputStreamClass)) #define EVD_IS_TLS_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_OUTPUT_STREAM)) #define EVD_IS_TLS_OUTPUT_STREAM_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_OUTPUT_STREAM)) #define EVD_TLS_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_OUTPUT_STREAM, EvdTlsOutputStreamClass)) GType evd_tls_output_stream_get_type (void) G_GNUC_CONST; EvdTlsOutputStream *evd_tls_output_stream_new (EvdTlsSession *session, GOutputStream *base_stream); G_END_DECLS #endif /* __EVD_TLS_OUTPUT_STREAM_H__ */ EventDance-0.2.0/evd/evd-tls-privkey.c000066400000000000000000000321731321356073300175250ustar00rootroot00000000000000/* * evd-tls-privkey.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-tls-privkey.h" #include "evd-error.h" #include "evd-tls-common.h" G_DEFINE_TYPE (EvdTlsPrivkey, evd_tls_privkey, G_TYPE_OBJECT) #define EVD_TLS_PRIVKEY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_PRIVKEY, \ EvdTlsPrivkeyPrivate)) /* private data */ struct _EvdTlsPrivkeyPrivate { gnutls_x509_privkey_t x509_privkey; gnutls_openpgp_privkey_t openpgp_privkey; EvdTlsCertificateType type; gboolean native_stolen; }; /* properties */ enum { PROP_0, PROP_TYPE }; static void evd_tls_privkey_class_init (EvdTlsPrivkeyClass *class); static void evd_tls_privkey_init (EvdTlsPrivkey *self); static void evd_tls_privkey_finalize (GObject *obj); static void evd_tls_privkey_dispose (GObject *obj); static void evd_tls_privkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_tls_privkey_cleanup (EvdTlsPrivkey *self); static void evd_tls_privkey_class_init (EvdTlsPrivkeyClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_tls_privkey_dispose; obj_class->finalize = evd_tls_privkey_finalize; obj_class->get_property = evd_tls_privkey_get_property; /* install properties */ g_object_class_install_property (obj_class, PROP_TYPE, g_param_spec_uint ("type", "Privkey type", "The type of privkey (X.509 or OPENPGP)", EVD_TLS_CERTIFICATE_TYPE_UNKNOWN, EVD_TLS_CERTIFICATE_TYPE_OPENPGP, EVD_TLS_CERTIFICATE_TYPE_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdTlsPrivkeyPrivate)); } static void evd_tls_privkey_init (EvdTlsPrivkey *self) { EvdTlsPrivkeyPrivate *priv; priv = EVD_TLS_PRIVKEY_GET_PRIVATE (self); self->priv = priv; priv->x509_privkey = NULL; priv->openpgp_privkey = NULL; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; priv->native_stolen = FALSE; } static void evd_tls_privkey_dispose (GObject *obj) { G_OBJECT_CLASS (evd_tls_privkey_parent_class)->dispose (obj); } static void evd_tls_privkey_finalize (GObject *obj) { EvdTlsPrivkey *self = EVD_TLS_PRIVKEY (obj); evd_tls_privkey_cleanup (self); G_OBJECT_CLASS (evd_tls_privkey_parent_class)->finalize (obj); } static void evd_tls_privkey_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsPrivkey *self; self = EVD_TLS_PRIVKEY (obj); switch (prop_id) { case PROP_TYPE: g_value_set_uint (value, self->priv->type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_privkey_cleanup (EvdTlsPrivkey *self) { if (self->priv->x509_privkey != NULL) { if (! self->priv->native_stolen) gnutls_x509_privkey_deinit (self->priv->x509_privkey); self->priv->x509_privkey = NULL; } if (self->priv->openpgp_privkey != NULL) { if (! self->priv->native_stolen) gnutls_openpgp_privkey_deinit (self->priv->openpgp_privkey); self->priv->openpgp_privkey = NULL; } self->priv->native_stolen = FALSE; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; } static EvdTlsCertificateType evd_tls_privkey_detect_type (const gchar *raw_data) { if (g_strstr_len (raw_data, 26, "BEGIN RSA PRIVATE KEY") != NULL) return EVD_TLS_CERTIFICATE_TYPE_X509; else if (g_strstr_len (raw_data, 32, "BEGIN PGP PRIVATE KEY BLOCK") != NULL) return EVD_TLS_CERTIFICATE_TYPE_OPENPGP; else return EVD_TLS_CERTIFICATE_TYPE_UNKNOWN; } static gboolean evd_tls_privkey_import_x509 (EvdTlsPrivkey *self, const gchar *raw_data, gsize size, gnutls_x509_crt_fmt_t format, GError **error) { gint err_code; gnutls_x509_privkey_t privkey; err_code = gnutls_x509_privkey_init (&privkey); if (err_code == GNUTLS_E_SUCCESS) { gnutls_datum_t datum = { NULL, 0 }; datum.data = (void *) raw_data; datum.size = size; err_code = gnutls_x509_privkey_import (privkey, &datum, format); } if (! evd_error_propagate_gnutls (err_code, error)) { evd_tls_privkey_cleanup (self); self->priv->x509_privkey = privkey; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_X509; return TRUE; } return FALSE; } static void evd_tls_privkey_import_from_file_thread (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable) { EvdTlsPrivkey *self = EVD_TLS_PRIVKEY (object); gchar *filename; gchar *content; gsize size; GError *error = NULL; filename = g_simple_async_result_get_op_res_gpointer (res); if (! g_file_get_contents (filename, &content, &size, &error) || ! evd_tls_privkey_import (self, content, size, &error)) { g_simple_async_result_set_from_error (res, error); g_error_free (error); } g_free (content); g_free (filename); g_object_unref (res); } /* public methods */ EvdTlsPrivkey * evd_tls_privkey_new (void) { EvdTlsPrivkey *self; self = g_object_new (EVD_TYPE_TLS_PRIVKEY, NULL); return self; } gboolean evd_tls_privkey_import (EvdTlsPrivkey *self, const gchar *raw_data, gsize size, GError **error) { EvdTlsCertificateType type; g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (self), FALSE); g_return_val_if_fail (raw_data != NULL, FALSE); type = evd_tls_privkey_detect_type (raw_data); switch (type) { case EVD_TLS_CERTIFICATE_TYPE_X509: { if (evd_tls_privkey_import_x509 (self, raw_data, size, GNUTLS_X509_FMT_PEM, error)) { return TRUE; } break; } case EVD_TLS_CERTIFICATE_TYPE_OPENPGP: { gint err_code; gnutls_openpgp_privkey_t privkey; err_code = gnutls_openpgp_privkey_init (&privkey); if (err_code == GNUTLS_E_SUCCESS) { gnutls_datum_t datum = { NULL, 0 }; datum.data = (void *) raw_data; datum.size = size; err_code = gnutls_openpgp_privkey_import (privkey, &datum, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0); } if (! evd_error_propagate_gnutls (err_code, error)) { evd_tls_privkey_cleanup (self); self->priv->openpgp_privkey = privkey; self->priv->type = EVD_TLS_CERTIFICATE_TYPE_OPENPGP; return TRUE; } break; } default: { /* probe DER format */ if (evd_tls_privkey_import_x509 (self, raw_data, size, GNUTLS_X509_FMT_DER, NULL)) { return TRUE; } else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unable to detect privkey type when trying to import"); } break; } }; return FALSE; } void evd_tls_privkey_import_from_file (EvdTlsPrivkey *self, const gchar *filename, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_TLS_PRIVKEY (self)); g_return_if_fail (filename != NULL); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_tls_privkey_import_from_file); g_simple_async_result_set_op_res_gpointer (res, g_strdup (filename), NULL); g_simple_async_result_run_in_thread (res, evd_tls_privkey_import_from_file_thread, G_PRIORITY_DEFAULT, cancellable); } gboolean evd_tls_privkey_import_from_file_finish (EvdTlsPrivkey *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_tls_privkey_import_from_file), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } /** * evd_tls_privkey_get_native: * * Returns: (transfer none): **/ gpointer evd_tls_privkey_get_native (EvdTlsPrivkey *self) { g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (self), NULL); if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_X509) return self->priv->x509_privkey; else if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_OPENPGP) return self->priv->openpgp_privkey; else return NULL; } /** * evd_tls_privkey_steal_native: * * Returns: (transfer full): **/ gpointer evd_tls_privkey_steal_native (EvdTlsPrivkey *self) { gpointer native; g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (self), NULL); native = evd_tls_privkey_get_native (self); if (native != NULL) self->priv->native_stolen = TRUE; return native; } /** * evd_tls_privkey_get_pki_key: * * Returns: (transfer full): **/ EvdPkiPrivkey * evd_tls_privkey_get_pki_key (EvdTlsPrivkey *self, GError **error) { EvdPkiPrivkey *key = NULL; gnutls_privkey_t privkey = NULL; gint err_code; g_return_val_if_fail (EVD_IS_TLS_PRIVKEY (self), NULL); if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_UNKNOWN) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Failed to get key from not initialized private key"); return NULL; } err_code = gnutls_privkey_init (&privkey); if (evd_error_propagate_gnutls (err_code, error)) return NULL; if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_X509) err_code = gnutls_privkey_import_x509 (privkey, self->priv->x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY); else if (self->priv->type == EVD_TLS_CERTIFICATE_TYPE_OPENPGP) err_code = gnutls_privkey_import_openpgp (privkey, self->priv->openpgp_privkey, GNUTLS_PRIVKEY_IMPORT_COPY); if (evd_error_propagate_gnutls (err_code, error)) { gnutls_privkey_deinit (privkey); return NULL; } else { key = evd_pki_privkey_new (); if (! evd_pki_privkey_import_native (key, privkey, error)) { gnutls_privkey_deinit (privkey); g_object_unref (key); key = NULL; } } return key; } EventDance-0.2.0/evd/evd-tls-privkey.h000066400000000000000000000066261321356073300175360ustar00rootroot00000000000000/* * evd-tls-privkey.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_PRIVKEY_H__ #define __EVD_TLS_PRIVKEY_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include "evd-pki-privkey.h" G_BEGIN_DECLS typedef struct _EvdTlsPrivkey EvdTlsPrivkey; typedef struct _EvdTlsPrivkeyClass EvdTlsPrivkeyClass; typedef struct _EvdTlsPrivkeyPrivate EvdTlsPrivkeyPrivate; struct _EvdTlsPrivkey { GObject parent; EvdTlsPrivkeyPrivate *priv; }; struct _EvdTlsPrivkeyClass { GObjectClass parent_class; }; #define EVD_TYPE_TLS_PRIVKEY (evd_tls_privkey_get_type ()) #define EVD_TLS_PRIVKEY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_PRIVKEY, EvdTlsPrivkey)) #define EVD_TLS_PRIVKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_PRIVKEY, EvdTlsPrivkeyClass)) #define EVD_IS_TLS_PRIVKEY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_PRIVKEY)) #define EVD_IS_TLS_PRIVKEY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_PRIVKEY)) #define EVD_TLS_PRIVKEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_PRIVKEY, EvdTlsPrivkeyClass)) GType evd_tls_privkey_get_type (void) G_GNUC_CONST; EvdTlsPrivkey * evd_tls_privkey_new (void); gboolean evd_tls_privkey_import (EvdTlsPrivkey *self, const gchar *raw_data, gsize len, GError **error); void evd_tls_privkey_import_from_file (EvdTlsPrivkey *self, const gchar *filename, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_tls_privkey_import_from_file_finish (EvdTlsPrivkey *self, GAsyncResult *result, GError **error); gpointer evd_tls_privkey_get_native (EvdTlsPrivkey *self); gpointer evd_tls_privkey_steal_native (EvdTlsPrivkey *self); EvdPkiPrivkey * evd_tls_privkey_get_pki_key (EvdTlsPrivkey *self, GError **error); G_END_DECLS #endif /* __EVD_TLS_PRIVKEY_H__ */ EventDance-0.2.0/evd/evd-tls-session.c000066400000000000000000000667331321356073300175300ustar00rootroot00000000000000/* * evd-tls-session.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-tls-session.h" #include "evd-error.h" #include "evd-tls-common.h" #include "evd-tls-credentials.h" #include "evd-tls-certificate.h" #define EVD_TLS_SESSION_DEFAULT_PRIORITY "NORMAL" G_DEFINE_TYPE (EvdTlsSession, evd_tls_session, G_TYPE_OBJECT) #define EVD_TLS_SESSION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_TLS_SESSION, \ EvdTlsSessionPrivate)) /* private data */ struct _EvdTlsSessionPrivate { gnutls_session_t session; EvdTlsCredentials *cred; EvdTlsMode mode; EvdTlsSessionPullFunc pull_func; EvdTlsSessionPushFunc push_func; gpointer pull_user_data; gpointer push_user_data; GDestroyNotify pull_user_data_free_func; GDestroyNotify push_user_data_free_func; gchar *priority; gulong cred_ready_sig_id; gboolean cred_bound; gboolean require_peer_cert; gboolean write_shutdown; gchar *server_name; }; /* properties */ enum { PROP_0, PROP_CREDENTIALS, PROP_MODE, PROP_PRIORITY, PROP_REQUIRE_PEER_CERT }; static void evd_tls_session_class_init (EvdTlsSessionClass *class); static void evd_tls_session_init (EvdTlsSession *self); static void evd_tls_session_finalize (GObject *obj); static void evd_tls_session_dispose (GObject *obj); static void evd_tls_session_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_tls_session_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_tls_session_class_init (EvdTlsSessionClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS (class); obj_class->dispose = evd_tls_session_dispose; obj_class->finalize = evd_tls_session_finalize; obj_class->get_property = evd_tls_session_get_property; obj_class->set_property = evd_tls_session_set_property; g_object_class_install_property (obj_class, PROP_CREDENTIALS, g_param_spec_object ("credentials", "The SSL/TLS session's credentials", "The certificate credentials object to use by this SSL/TLS session", EVD_TYPE_TLS_CREDENTIALS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_MODE, g_param_spec_uint ("mode", "SSL/TLS session mode", "The SSL/TLS session's mode of operation: client or server", EVD_TLS_MODE_SERVER, EVD_TLS_MODE_CLIENT, EVD_TLS_MODE_SERVER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_PRIORITY, g_param_spec_string ("priority", "Priority string to use in this session", "Gets/sets the priorities to use on the ciphers, key exchange methods, macs and compression methods", EVD_TLS_SESSION_DEFAULT_PRIORITY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_REQUIRE_PEER_CERT, g_param_spec_boolean ("require-peer-cert", "Require peer certificate", "Controls whether a peer certificate will be requested during handshake", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdTlsSessionPrivate)); } static void evd_tls_session_init (EvdTlsSession *self) { EvdTlsSessionPrivate *priv; priv = EVD_TLS_SESSION_GET_PRIVATE (self); self->priv = priv; priv->session = NULL; priv->cred = NULL; priv->mode = EVD_TLS_MODE_SERVER; priv->pull_func = NULL; priv->push_func = NULL; priv->pull_user_data = NULL; priv->push_user_data = NULL; priv->pull_user_data_free_func = NULL; priv->push_user_data_free_func = NULL; priv->priority = g_strdup (EVD_TLS_SESSION_DEFAULT_PRIORITY); priv->cred_ready_sig_id = 0; priv->cred_bound = FALSE; priv->require_peer_cert = FALSE; priv->write_shutdown = FALSE; priv->server_name = NULL; } static void evd_tls_session_dispose (GObject *obj) { EvdTlsSession *self = EVD_TLS_SESSION (obj); if (self->priv->pull_user_data != NULL && self->priv->pull_user_data_free_func != NULL) { self->priv->pull_user_data_free_func (self->priv->pull_user_data); self->priv->pull_user_data = NULL; } if (self->priv->push_user_data != NULL && self->priv->push_user_data_free_func != NULL) { self->priv->push_user_data_free_func (self->priv->push_user_data); self->priv->push_user_data = NULL; } if (self->priv->cred != NULL) { if (self->priv->cred_ready_sig_id != 0) g_signal_handler_disconnect (self->priv->cred, self->priv->cred_ready_sig_id); g_object_unref (self->priv->cred); self->priv->cred = NULL; } G_OBJECT_CLASS (evd_tls_session_parent_class)->dispose (obj); } static void evd_tls_session_finalize (GObject *obj) { EvdTlsSession *self = EVD_TLS_SESSION (obj); if (self->priv->session != NULL) gnutls_deinit (self->priv->session); if (self->priv->priority != NULL) g_free (self->priv->priority); if (self->priv->server_name != NULL) g_free (self->priv->server_name); G_OBJECT_CLASS (evd_tls_session_parent_class)->finalize (obj); } static void evd_tls_session_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdTlsSession *self; self = EVD_TLS_SESSION (obj); switch (prop_id) { case PROP_CREDENTIALS: evd_tls_session_set_credentials (self, g_value_get_object (value)); break; case PROP_MODE: self->priv->mode = g_value_get_uint (value); break; case PROP_PRIORITY: if (self->priv->priority != NULL) g_free (self->priv->priority); self->priv->priority = g_strdup (g_value_get_string (value)); break; case PROP_REQUIRE_PEER_CERT: self->priv->require_peer_cert = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_tls_session_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdTlsSession *self; self = EVD_TLS_SESSION (obj); switch (prop_id) { case PROP_CREDENTIALS: g_value_set_object (value, evd_tls_session_get_credentials (self)); break; case PROP_MODE: g_value_set_uint (value, self->priv->mode); break; case PROP_PRIORITY: g_value_set_string (value, self->priv->priority); break; case PROP_REQUIRE_PEER_CERT: g_value_set_boolean (value, self->priv->require_peer_cert); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gssize evd_tls_session_push (gnutls_transport_ptr_t ptr, const void *buf, gsize size) { EvdTlsSession *self = EVD_TLS_SESSION (ptr); gssize res; GError *error = NULL; res = self->priv->push_func (self, buf, size, self->priv->push_user_data, &error); if (res < 0) { if (! self->priv->write_shutdown || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { /* @TODO: Handle transport error */ g_debug ("TLS session transport error during push: %s", error->message); } g_error_free (error); } return res; } static gssize evd_tls_session_pull (gnutls_transport_ptr_t ptr, void *buf, gsize size) { EvdTlsSession *self = EVD_TLS_SESSION (ptr); gssize res; GError *error = NULL; res = self->priv->pull_func (self, buf, size, self->priv->pull_user_data, &error); if (res < 0) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { gnutls_transport_set_errno (self->priv->session, EAGAIN); res = -1; } else { /* @TODO: handle transport error */ g_debug ("TLS transport error during pull: %s", error->message); } g_error_free (error); } else if (res == 0) { /* @TODO: handle end of stream */ } return res; } static gint evd_tls_session_handshake_internal (EvdTlsSession *self, GError **error) { gint err_code; err_code = gnutls_handshake (self->priv->session); if (err_code == GNUTLS_E_SUCCESS) { return 1; } else if (gnutls_error_is_fatal (err_code) == 1) { evd_error_propagate_gnutls (err_code, error); return -1; } else { return 0; } } static gboolean evd_tls_session_bind_credentials (EvdTlsSession *self, EvdTlsCredentials *cred, GError **error) { gpointer cred_internal; gint err_code; /* set credentials */ cred_internal = evd_tls_credentials_get_credentials (cred); if (cred_internal == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "TLS credentials not initialized"); return FALSE; } else { err_code = gnutls_credentials_set (self->priv->session, GNUTLS_CRD_CERTIFICATE, (gnutls_certificate_credentials_t) cred_internal); } if (evd_error_propagate_gnutls (err_code, error)) { return FALSE; } else { self->priv->cred_bound = TRUE; return TRUE; } } static void evd_tls_session_on_credentials_ready (EvdTlsCredentials *cred, gpointer user_data) { EvdTlsSession *self = EVD_TLS_SESSION (user_data); GError *error = NULL; if (! evd_tls_session_bind_credentials (self, cred, &error)) { /* TODO: handle error */ g_debug ("error binding credentials: %s", error->message); g_error_free (error); } else if (evd_tls_session_handshake_internal (self, &error) < 0) { /* TODO: raise error asynchronously, by firing 'error' signal */ g_debug ("handshake error!: %s", error->message); g_error_free (error); } } static gboolean evd_tls_session_shutdown (EvdTlsSession *self, gnutls_close_request_t how, GError **error) { g_return_val_if_fail (EVD_IS_TLS_SESSION (self), FALSE); if (self->priv->session != NULL) { gint err_code; err_code = gnutls_bye (self->priv->session, how); if (err_code < 0 && gnutls_error_is_fatal (err_code) != 0) { evd_error_propagate_gnutls (err_code, error); return FALSE; } self->priv->write_shutdown = TRUE; } return TRUE; } static gboolean evd_tls_session_check_initialized (EvdTlsSession *self, GError **error) { if (self->priv->session == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "SSL/TLS session not yet initialized"); return FALSE; } else { return TRUE; } } static gboolean evd_tls_session_set_server_name_internal (EvdTlsSession *self, GError **error) { if (self->priv->session != NULL && self->priv->server_name != NULL && self->priv->mode == EVD_TLS_MODE_CLIENT) { gint err_code; err_code = gnutls_server_name_set (self->priv->session, GNUTLS_NAME_DNS, self->priv->server_name, strlen (self->priv->server_name)); if (evd_error_propagate_gnutls (err_code, error)) return FALSE; } return TRUE; } /* public methods */ EvdTlsSession * evd_tls_session_new (void) { EvdTlsSession *self; self = g_object_new (EVD_TYPE_TLS_SESSION, NULL); return self; } void evd_tls_session_set_credentials (EvdTlsSession *self, EvdTlsCredentials *credentials) { g_return_if_fail (EVD_IS_TLS_SESSION (self)); g_return_if_fail (EVD_IS_TLS_CREDENTIALS (credentials)); if (self->priv->cred != NULL) { if (self->priv->cred_ready_sig_id != 0) { g_signal_handler_disconnect (self->priv->cred, self->priv->cred_ready_sig_id); self->priv->cred_ready_sig_id = 0; } g_object_unref (self->priv->cred); } self->priv->cred = credentials; g_object_ref (self->priv->cred); } /** * evd_tls_session_get_credentials: * * Returns: (transfer none): The #EvdTlsCredentials object of this session **/ EvdTlsCredentials * evd_tls_session_get_credentials (EvdTlsSession *self) { g_return_val_if_fail (EVD_IS_TLS_SESSION (self), NULL); if (self->priv->cred == NULL) { self->priv->cred = evd_tls_credentials_new (); g_object_ref_sink (self->priv->cred); } return self->priv->cred; } void evd_tls_session_set_transport_pull_func (EvdTlsSession *self, EvdTlsSessionPullFunc func, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_TLS_SESSION (self)); g_return_if_fail (func != NULL); if (self->priv->pull_user_data != NULL && self->priv->pull_user_data_free_func != NULL) { self->priv->pull_user_data_free_func (self->priv->pull_user_data); } self->priv->pull_func = func; self->priv->pull_user_data = user_data; self->priv->pull_user_data_free_func = user_data_free_func; } void evd_tls_session_set_transport_push_func (EvdTlsSession *self, EvdTlsSessionPushFunc func, gpointer user_data, GDestroyNotify user_data_free_func) { g_return_if_fail (EVD_IS_TLS_SESSION (self)); g_return_if_fail (func != NULL); if (self->priv->push_user_data != NULL && self->priv->push_user_data_free_func != NULL) { self->priv->push_user_data_free_func (self->priv->push_user_data); } self->priv->push_func = func; self->priv->push_user_data = user_data; self->priv->push_user_data_free_func = user_data_free_func; } gint evd_tls_session_handshake (EvdTlsSession *self, GError **error) { EvdTlsCredentials *cred; gint err_code; g_return_val_if_fail (EVD_IS_TLS_SESSION (self), FALSE); if (self->priv->session == NULL) { err_code = gnutls_init (&self->priv->session, self->priv->mode); if (evd_error_propagate_gnutls (err_code, error)) { return -1; } else { if (! evd_tls_session_set_server_name_internal (self, error)) return -1; err_code = gnutls_priority_set_direct (self->priv->session, self->priv->priority, NULL); if (evd_error_propagate_gnutls (err_code, error)) return -1; if (self->priv->require_peer_cert && self->priv->mode == EVD_TLS_MODE_SERVER) { gnutls_certificate_server_set_request (self->priv->session, GNUTLS_CERT_REQUEST); } gnutls_transport_set_ptr (self->priv->session, self); gnutls_transport_set_ptr2 (self->priv->session, self, self); gnutls_transport_set_push_function (self->priv->session, evd_tls_session_push); gnutls_transport_set_pull_function (self->priv->session, evd_tls_session_pull); cred = evd_tls_session_get_credentials (self); if (! evd_tls_credentials_ready (cred)) { if (self->priv->cred_ready_sig_id == 0) self->priv->cred_ready_sig_id = g_signal_connect (cred, "ready", G_CALLBACK (evd_tls_session_on_credentials_ready), self); evd_tls_credentials_prepare (cred, error); return 0; } else { if (! evd_tls_session_bind_credentials (self, cred, error)) return -1; } } } if (self->priv->cred_bound) return evd_tls_session_handshake_internal (self, error); else return 0; } gssize evd_tls_session_read (EvdTlsSession *self, gchar *buffer, gsize size, GError **error) { gssize result; g_return_val_if_fail (EVD_IS_TLS_SESSION (self), -1); g_return_val_if_fail (size > 0, -1); g_return_val_if_fail (buffer != NULL, -1); result = gnutls_record_recv (self->priv->session, buffer, size); if (result == 0) { /* @TODO: EOF condition, emit 'close' signal */ } else if (result < 0) { if (gnutls_error_is_fatal (result) != 0) { evd_error_propagate_gnutls (result, error); result = -1; } else { result = 0; } } return result; } gssize evd_tls_session_write (EvdTlsSession *self, const gchar *buffer, gsize size, GError **error) { gssize result; g_return_val_if_fail (EVD_IS_TLS_SESSION (self), -1); g_return_val_if_fail (size > 0, -1); g_return_val_if_fail (buffer != NULL, -1); result = gnutls_record_send (self->priv->session, buffer, size); if (result < 0) { if (gnutls_error_is_fatal (result) != 0) { evd_error_propagate_gnutls (result, error); result = -1; } else { result = 0; } } return result; } GIOCondition evd_tls_session_get_direction (EvdTlsSession *self) { g_return_val_if_fail (EVD_IS_TLS_SESSION (self), 0); if (self->priv->session == NULL) return 0; else if (gnutls_record_get_direction (self->priv->session) == 0) return G_IO_IN; else return G_IO_OUT; } gboolean evd_tls_session_close (EvdTlsSession *self, GError **error) { return evd_tls_session_shutdown (self, GNUTLS_SHUT_RDWR, error); } gboolean evd_tls_session_shutdown_write (EvdTlsSession *self, GError **error) { return evd_tls_session_shutdown (self, GNUTLS_SHUT_WR, error); } void evd_tls_session_copy_properties (EvdTlsSession *self, EvdTlsSession *target) { g_return_if_fail (EVD_IS_TLS_SESSION (self)); g_return_if_fail (self != target); g_object_set (target, "credentials", evd_tls_session_get_credentials (self), "priority", self->priv->priority, "require-peer-cert", self->priv->require_peer_cert, NULL); } /** * evd_tls_session_get_peer_certificates: * @self: * @error: * * Returns: (transfer full) (element-type Evd.TlsCertificate): The list of certificates * as sent by the peer. **/ GList * evd_tls_session_get_peer_certificates (EvdTlsSession *self, GError **error) { GList *list = NULL; const gnutls_datum_t *raw_certs_list; guint raw_certs_len; g_return_val_if_fail (EVD_IS_TLS_SESSION (self), NULL); if (! evd_tls_session_check_initialized (self, error)) return NULL; raw_certs_list = gnutls_certificate_get_peers (self->priv->session, &raw_certs_len); if (raw_certs_list != NULL) { guint i; EvdTlsCertificate *cert; for (i=0; ipriv->session, &status); if (err_code != GNUTLS_E_SUCCESS) { if (err_code != GNUTLS_E_NO_CERTIFICATE_FOUND) { evd_error_propagate_gnutls (err_code, error); return -1; } else { result |= EVD_TLS_VERIFY_STATE_NO_CERT; } } else { if (status & GNUTLS_CERT_INVALID) result |= EVD_TLS_VERIFY_STATE_INVALID; if (status & GNUTLS_CERT_REVOKED) result |= EVD_TLS_VERIFY_STATE_REVOKED; if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) result |= EVD_TLS_VERIFY_STATE_SIGNER_NOT_FOUND; if (status & GNUTLS_CERT_SIGNER_NOT_CA) result |= EVD_TLS_VERIFY_STATE_SIGNER_NOT_CA; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) result |= EVD_TLS_VERIFY_STATE_INSECURE_ALG; } /* check each peer certificate individually */ peer_certs = evd_tls_session_get_peer_certificates (self, error); if (peer_certs != NULL) { GList *node; EvdTlsCertificate *cert; gint cert_result; node = peer_certs; do { cert = EVD_TLS_CERTIFICATE (node->data); cert_result = evd_tls_certificate_verify_validity (cert, error); if (cert_result < 0) break; else result |= cert_result; node = node->next; } while (node != NULL); evd_tls_free_certificates (peer_certs); } return result; } void evd_tls_session_reset (EvdTlsSession *self) { g_return_if_fail (EVD_IS_TLS_SESSION (self)); if (self->priv->session != NULL) { if (self->priv->cred_ready_sig_id != 0) { g_signal_handler_disconnect (self->priv->cred, self->priv->cred_ready_sig_id); self->priv->cred_ready_sig_id = 0; } self->priv->cred_bound = FALSE; gnutls_deinit (self->priv->session); self->priv->session = NULL; } if (self->priv->server_name != NULL) { g_free (self->priv->server_name); self->priv->server_name = NULL; } } gboolean evd_tls_session_set_server_name (EvdTlsSession *self, const gchar *server_name, GError **error) { g_return_val_if_fail (EVD_IS_TLS_SESSION (self), FALSE); if (self->priv->server_name != NULL) { g_free (self->priv->server_name); self->priv->server_name = NULL; } if (server_name != NULL) self->priv->server_name = g_strdup (server_name); return evd_tls_session_set_server_name_internal (self, error); } const gchar * evd_tls_session_get_server_name (EvdTlsSession *self) { g_return_val_if_fail (EVD_IS_TLS_SESSION (self), NULL); if (self->priv->mode == EVD_TLS_MODE_SERVER && self->priv->server_name == NULL && self->priv->session != NULL) { gint err; gsize len = 16; gchar buf[16] = {0, }; guint type; guint index = 0; do { err = gnutls_server_name_get (self->priv->session, buf, &len, &type, index); if (err == GNUTLS_E_SUCCESS && type == GNUTLS_NAME_DNS) { self->priv->server_name = g_new0 (gchar, len + 1); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { err = gnutls_server_name_get (self->priv->session, self->priv->server_name, &len, &type, index); } else { memmove (self->priv->server_name, buf, len); } } else { index++; } } while (err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && self->priv->server_name == NULL); } return self->priv->server_name; } EventDance-0.2.0/evd/evd-tls-session.h000066400000000000000000000137121321356073300175220ustar00rootroot00000000000000/* * evd-tls-session.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TLS_SESSION_H__ #define __EVD_TLS_SESSION_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include "evd-tls-common.h" G_BEGIN_DECLS typedef struct _EvdTlsSession EvdTlsSession; typedef struct _EvdTlsSessionClass EvdTlsSessionClass; typedef struct _EvdTlsSessionPrivate EvdTlsSessionPrivate; typedef gssize (* EvdTlsSessionPullFunc) (EvdTlsSession *self, gchar *buf, gsize size, gpointer user_data, GError **error); typedef gssize (* EvdTlsSessionPushFunc) (EvdTlsSession *self, const gchar *buf, gsize size, gpointer user_data, GError **error); struct _EvdTlsSession { GObject parent; EvdTlsSessionPrivate *priv; }; struct _EvdTlsSessionClass { GObjectClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_TLS_SESSION (evd_tls_session_get_type ()) #define EVD_TLS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TLS_SESSION, EvdTlsSession)) #define EVD_TLS_SESSION_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_TLS_SESSION, EvdTlsSessionClass)) #define EVD_IS_TLS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TLS_SESSION)) #define EVD_IS_TLS_SESSION_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_TLS_SESSION)) #define EVD_TLS_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_TLS_SESSION, EvdTlsSessionClass)) GType evd_tls_session_get_type (void) G_GNUC_CONST; EvdTlsSession *evd_tls_session_new (void); void evd_tls_session_set_transport_pull_func (EvdTlsSession *self, EvdTlsSessionPullFunc func, gpointer user_data, GDestroyNotify user_data_free_func); void evd_tls_session_set_transport_push_func (EvdTlsSession *self, EvdTlsSessionPushFunc func, gpointer user_data, GDestroyNotify user_data_free_func); gint evd_tls_session_handshake (EvdTlsSession *self, GError **error); gssize evd_tls_session_read (EvdTlsSession *self, gchar *buffer, gsize size, GError **error); gssize evd_tls_session_write (EvdTlsSession *self, const gchar *buffer, gsize size, GError **error); GIOCondition evd_tls_session_get_direction (EvdTlsSession *self); gboolean evd_tls_session_close (EvdTlsSession *self, GError **error); gboolean evd_tls_session_shutdown_write (EvdTlsSession *self, GError **error); void evd_tls_session_copy_properties (EvdTlsSession *self, EvdTlsSession *target); GList *evd_tls_session_get_peer_certificates (EvdTlsSession *self, GError **error); gint evd_tls_session_verify_peer (EvdTlsSession *self, guint flags, GError **error); void evd_tls_session_reset (EvdTlsSession *self); gboolean evd_tls_session_set_server_name (EvdTlsSession *self, const gchar *server_name, GError **error); const gchar *evd_tls_session_get_server_name (EvdTlsSession *self); G_END_DECLS #endif /* __EVD_TLS_SESSION_H__ */ EventDance-0.2.0/evd/evd-transport.c000066400000000000000000000437771321356073300173040ustar00rootroot00000000000000/* * evd-transport.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-transport.h" #include "evd-utils.h" #include "evd-marshal.h" #include "evd-peer-manager.h" #define PEER_MSG_KEY "org.eventdance.lib.transport.PEER_MESSAGE" #define PEER_CLOSING_KEY "org.eventdance.lib.Transport.PEER_CLOSING" /* signals */ enum { SIGNAL_RECEIVE, SIGNAL_NEW_PEER, SIGNAL_PEER_CLOSED, SIGNAL_VALIDATE_PEER, SIGNAL_LAST }; typedef struct { const gchar *buffer; gchar *text_buffer; gsize size; } EvdTransportPeerMessage; static guint evd_transport_signals[SIGNAL_LAST] = { 0 }; static void evd_transport_receive_internal (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size); static void evd_transport_notify_receive (EvdTransport *self, EvdPeer *peer); static void evd_transport_notify_new_peer (EvdTransport *self, EvdPeer *peer); static EvdPeer *evd_transport_create_new_peer_internal (EvdTransport *self); static void evd_transport_notify_peer_closed (EvdTransport *self, EvdPeer *peer, gboolean gracefully); static guint evd_transport_notify_validate_peer (EvdTransport *self, EvdPeer *peer); static gboolean evd_transport_validate_peer_signal_acc (GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data); static void evd_transport_base_init (gpointer g_class) { static gboolean is_initialized = FALSE; EvdTransportInterface *iface = (EvdTransportInterface *) g_class; iface->receive = evd_transport_receive_internal; iface->notify_receive = evd_transport_notify_receive; iface->create_new_peer = evd_transport_create_new_peer_internal; iface->notify_new_peer = evd_transport_notify_new_peer; iface->notify_peer_closed = evd_transport_notify_peer_closed; iface->notify_validate_peer = evd_transport_notify_validate_peer; iface->open = NULL; if (! is_initialized) { evd_transport_signals[SIGNAL_RECEIVE] = g_signal_new ("receive", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdTransportInterface, signal_receive), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, EVD_TYPE_PEER); evd_transport_signals[SIGNAL_NEW_PEER] = g_signal_new ("new-peer", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdTransportInterface, signal_new_peer), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, EVD_TYPE_PEER); evd_transport_signals[SIGNAL_PEER_CLOSED] = g_signal_new ("peer-closed", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdTransportInterface, signal_peer_closed), NULL, NULL, evd_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2, EVD_TYPE_PEER, G_TYPE_BOOLEAN); evd_transport_signals[SIGNAL_VALIDATE_PEER] = g_signal_new ("validate-peer", G_TYPE_FROM_CLASS (g_class), G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdTransportInterface, signal_validate_peer), evd_transport_validate_peer_signal_acc, NULL, evd_marshal_UINT__OBJECT, G_TYPE_UINT, 1, EVD_TYPE_PEER); is_initialized = TRUE; } iface->peer_manager = evd_peer_manager_get_default (); } static void evd_transport_base_finalize (gpointer g_class) { EvdTransportInterface *iface = (EvdTransportInterface *) g_class; g_object_unref (iface->peer_manager); } static gboolean evd_transport_validate_peer_signal_acc (GSignalInvocationHint *hint, GValue *return_accu, const GValue *handler_return, gpointer data) { guint signal_result; guint global_result; global_result = g_value_get_uint (return_accu); signal_result = g_value_get_uint (handler_return); if (signal_result == EVD_VALIDATE_REJECT) global_result = EVD_VALIDATE_REJECT; else if (signal_result == EVD_VALIDATE_PENDING) global_result = EVD_VALIDATE_PENDING; else if (global_result != EVD_VALIDATE_PENDING) global_result = EVD_VALIDATE_ACCEPT; g_value_set_uint (return_accu, global_result); return (global_result != EVD_VALIDATE_REJECT); } GType evd_transport_get_type (void) { static GType iface_type = 0; if (iface_type == 0) { static const GTypeInfo info = { sizeof (EvdTransportInterface), evd_transport_base_init, evd_transport_base_finalize, NULL, }; iface_type = g_type_register_static (G_TYPE_INTERFACE, "EvdTransport", &info, 0); } return iface_type; } static void evd_transport_notify_receive (EvdTransport *self, EvdPeer *peer) { g_signal_emit (evd_peer_get_transport (peer), evd_transport_signals[SIGNAL_RECEIVE], 0, peer, NULL); } static void evd_transport_receive_internal (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size) { EvdTransportPeerMessage *msg; g_object_ref (peer); msg = g_object_get_data (G_OBJECT (peer), PEER_MSG_KEY); if (msg == NULL) { msg = g_new (EvdTransportPeerMessage, 1); g_object_set_data_full (G_OBJECT (peer), PEER_MSG_KEY, msg, g_free); } msg->buffer = buffer; msg->size = size; msg->text_buffer = NULL; evd_transport_notify_receive (self, peer); if (msg->text_buffer != NULL) { g_slice_free1 (msg->size + 1, msg->text_buffer); msg->text_buffer = NULL; } msg->buffer = NULL; msg->size = 0; g_object_unref (peer); } static void evd_transport_notify_new_peer (EvdTransport *self, EvdPeer *peer) { g_signal_emit (evd_peer_get_transport (peer), evd_transport_signals[SIGNAL_NEW_PEER], 0, peer, NULL); } static EvdPeer * evd_transport_create_new_peer_internal (EvdTransport *self) { EvdPeer *peer; guint validate_result; peer = g_object_new (EVD_TYPE_PEER, "transport", self, NULL); validate_result = evd_transport_notify_validate_peer (self, peer); if (validate_result == EVD_VALIDATE_REJECT) { g_object_unref (peer); return NULL; } else if (validate_result == EVD_VALIDATE_ACCEPT) { evd_transport_accept_peer (self, peer); } return peer; } static void evd_transport_notify_peer_closed (EvdTransport *self, EvdPeer *peer, gboolean gracefully) { g_signal_emit (evd_peer_get_transport (peer), evd_transport_signals[SIGNAL_PEER_CLOSED], 0, peer, gracefully, NULL); } static guint evd_transport_notify_validate_peer (EvdTransport *self, EvdPeer *peer) { guint result; g_signal_emit (self, evd_transport_signals[SIGNAL_VALIDATE_PEER], 0, peer, &result); return result; } static gboolean evd_transport_accept_peer_internal (EvdTransport *self, EvdPeer *peer) { EvdPeerManager *peer_manager; peer_manager = EVD_TRANSPORT_GET_INTERFACE (self)->peer_manager; if (evd_peer_manager_lookup_peer (peer_manager, evd_peer_get_id (peer)) == NULL) { evd_peer_manager_add_peer (peer_manager, peer); /* By design, only EvdPeerManager should hold a reference to a peer, so after adding a peer we should drop our reference */ g_object_unref (peer); evd_transport_notify_new_peer (self, peer); } return TRUE; } static gboolean send_frame (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { g_return_val_if_fail (EVD_IS_TRANSPORT (self), FALSE); g_return_val_if_fail (EVD_IS_PEER (peer), FALSE); if (! EVD_TRANSPORT_GET_INTERFACE (self)->send (self, peer, buffer, size, type, NULL) && ! evd_peer_push_message (peer, buffer, size, type, error)) { return FALSE; } else { return TRUE; } } /* public methods */ gboolean evd_transport_send (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size, GError **error) { return send_frame (self, peer, buffer, size, EVD_MESSAGE_TYPE_BINARY, error); } gboolean evd_transport_send_text (EvdTransport *self, EvdPeer *peer, const gchar *text, GError **error) { gsize size; size = strlen (text); return send_frame (self, peer, text, size, EVD_MESSAGE_TYPE_TEXT, error); } /** * evd_transport_receive: * @size: (out): * * Returns: (transfer none): **/ const gchar * evd_transport_receive (EvdTransport *self, EvdPeer *peer, gsize *size) { EvdTransportPeerMessage *msg; g_return_val_if_fail (EVD_IS_TRANSPORT (self), NULL); msg = g_object_get_data (G_OBJECT (peer), PEER_MSG_KEY); if (msg == NULL) return NULL; if (size != NULL) *size = msg->size; return msg->buffer; } /** * evd_transport_receive_text: * * Returns: (transfer none): **/ const gchar * evd_transport_receive_text (EvdTransport *self, EvdPeer *peer) { EvdTransportPeerMessage *msg; g_return_val_if_fail (EVD_IS_TRANSPORT (self), NULL); msg = g_object_get_data (G_OBJECT (peer), PEER_MSG_KEY); if (msg == NULL) return NULL; if (msg->text_buffer == NULL) { msg->text_buffer = g_slice_alloc (msg->size + 1); msg->text_buffer[msg->size] = '\0'; memcpy (msg->text_buffer, msg->buffer, msg->size); } return msg->text_buffer; } gboolean evd_transport_peer_is_connected (EvdTransport *self, EvdPeer *peer) { g_return_val_if_fail (EVD_IS_TRANSPORT (self), FALSE); g_return_val_if_fail (EVD_IS_PEER (peer), FALSE); if (EVD_TRANSPORT_GET_INTERFACE (self)->peer_is_connected (self, peer)) { evd_peer_touch (peer); return TRUE; } else { return FALSE; } } void evd_transport_close_peer (EvdTransport *self, EvdPeer *peer, gboolean gracefully, GError **error) { EvdTransportInterface *iface; EvdPeerManager *peer_manager; gpointer bag; g_return_if_fail (EVD_IS_TRANSPORT (self)); g_return_if_fail (EVD_IS_PEER (peer)); bag = g_object_get_data (G_OBJECT (peer), PEER_CLOSING_KEY); if (bag != NULL) return; g_object_ref (self); g_object_set_data (G_OBJECT (peer), PEER_CLOSING_KEY, self); g_object_ref (peer); peer_manager = EVD_TRANSPORT_GET_INTERFACE (self)->peer_manager; evd_peer_manager_close_peer (peer_manager, peer, gracefully); evd_peer_close (peer, gracefully); iface = EVD_TRANSPORT_GET_INTERFACE (self); if (iface->peer_closed != NULL) iface->peer_closed (self, peer, gracefully); evd_transport_notify_peer_closed (self, peer, gracefully); g_object_set_data (G_OBJECT (peer), PEER_CLOSING_KEY, NULL); g_object_unref (self); g_object_unref (peer); } /** * evd_transport_create_new_peer: * * Returns: (transfer full): **/ EvdPeer * evd_transport_create_new_peer (EvdTransport *self) { EvdTransportInterface *iface; g_return_val_if_fail (EVD_IS_TRANSPORT (self), NULL); iface = EVD_TRANSPORT_GET_INTERFACE (self); g_assert (iface->create_new_peer != NULL); return iface->create_new_peer (self); } /** * evd_transport_lookup_peer: * * Returns: (transfer none): **/ EvdPeer * evd_transport_lookup_peer (EvdTransport *self, const gchar *peer_id) { EvdPeerManager *peer_manager; g_return_val_if_fail (EVD_IS_TRANSPORT (self), NULL); if (peer_id == NULL) return NULL; peer_manager = EVD_TRANSPORT_GET_INTERFACE (self)->peer_manager; return evd_peer_manager_lookup_peer (peer_manager, peer_id); } gboolean evd_transport_accept_peer (EvdTransport *self, EvdPeer *peer) { EvdTransportInterface *iface; g_return_val_if_fail (EVD_IS_TRANSPORT (self), FALSE); g_return_val_if_fail (EVD_IS_PEER (peer), FALSE); iface = EVD_TRANSPORT_GET_INTERFACE (self); if (iface->accept_peer != NULL) return iface->accept_peer (self, peer); else return evd_transport_accept_peer_internal (self, peer); } gboolean evd_transport_reject_peer (EvdTransport *self, EvdPeer *peer) { EvdTransportInterface *iface; g_return_val_if_fail (EVD_IS_TRANSPORT (self), FALSE); g_return_val_if_fail (EVD_IS_PEER (peer), FALSE); iface = EVD_TRANSPORT_GET_INTERFACE (self); if (iface->reject_peer != NULL) { return iface->reject_peer (self, peer); } else { g_object_unref (peer); return TRUE; } } /** * evd_transport_get_peer_manager: * * Returns: (transfer none): **/ EvdPeerManager * evd_transport_get_peer_manager (EvdTransport *self) { g_return_val_if_fail (EVD_IS_TRANSPORT (self), NULL); return EVD_TRANSPORT_GET_INTERFACE (self)->peer_manager; } /** * evd_transport_set_peer_manager: * **/ void evd_transport_set_peer_manager (EvdTransport *self, EvdPeerManager *peer_manager) { EvdTransportInterface *iface; g_return_if_fail (EVD_IS_TRANSPORT (self)); g_return_if_fail (EVD_IS_PEER_MANAGER (peer_manager)); iface = EVD_TRANSPORT_GET_INTERFACE (self); if (iface->peer_manager != peer_manager) { g_object_unref (iface->peer_manager); iface->peer_manager = peer_manager; g_object_ref (iface->peer_manager); } } /** * evd_transport_open: * @cancellable: (allow-none): * @callback: (scope async) (allow-none): * @user_data: (allow-none): * **/ void evd_transport_open (EvdTransport *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *res; g_return_if_fail (EVD_IS_TRANSPORT (self)); g_return_if_fail (address != NULL && address[0] != '\0'); res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, evd_transport_open); if (EVD_TRANSPORT_GET_INTERFACE (self)->open != NULL) { EVD_TRANSPORT_GET_INTERFACE (self)->open (self, address, res, cancellable); } else { g_simple_async_result_set_error (res, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Method open() not implemented in transport"); g_simple_async_result_complete_in_idle (res); g_object_unref (res); } } gboolean evd_transport_open_finish (EvdTransport *self, GAsyncResult *result, GError **error) { g_return_val_if_fail (EVD_IS_TRANSPORT (self), FALSE); g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), evd_transport_open), FALSE); return ! g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); } EventDance-0.2.0/evd/evd-transport.h000066400000000000000000000175361321356073300173030ustar00rootroot00000000000000/* * evd-transport.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_TRANSPORT_H__ #define __EVD_TRANSPORT_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include #include #include "evd-utils.h" #include "evd-peer-manager.h" #include "evd-peer.h" G_BEGIN_DECLS typedef struct _EvdTransport EvdTransport; typedef struct _EvdTransportInterface EvdTransportInterface; struct _EvdTransportInterface { GTypeInterface parent_iface; /* virtual methods */ gboolean (* send) (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error); void (* notify_receive) (EvdTransport *self, EvdPeer *peer); void (* receive) (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size); void (* notify_new_peer) (EvdTransport *self, EvdPeer *peer); EvdPeer * (* create_new_peer) (EvdTransport *self); void (* notify_peer_closed) (EvdTransport *self, EvdPeer *peer, gboolean gracefully); void (* peer_closed) (EvdTransport *self, EvdPeer *peer, gboolean gracefully); guint (* notify_validate_peer) (EvdTransport *self, EvdPeer *peer); gboolean (* peer_is_connected) (EvdTransport *self, EvdPeer *peer); gboolean (* accept_peer) (EvdTransport *self, EvdPeer *peer); gboolean (* reject_peer) (EvdTransport *self, EvdPeer *peer); void (* open) (EvdTransport *self, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable); /* signals */ void (* signal_receive) (EvdTransport *self, EvdPeer *peer, gpointer user_data); void (* signal_new_peer) (EvdTransport *self, EvdPeer *peer, gpointer user_data); void (* signal_peer_closed) (EvdTransport *self, EvdPeer *peer, gboolean gracefully, gpointer user_data); guint (* signal_validate_peer) (EvdTransport *self, EvdPeer *peer, gpointer user_data); /* members */ EvdPeerManager *peer_manager; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_TRANSPORT (evd_transport_get_type ()) #define EVD_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_TRANSPORT, EvdTransport)) #define EVD_IS_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_TRANSPORT)) #define EVD_TRANSPORT_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EVD_TYPE_TRANSPORT, EvdTransportInterface)) GType evd_transport_get_type (void); gboolean evd_transport_send (EvdTransport *self, EvdPeer *peer, const gchar *buffer, gsize size, GError **error); gboolean evd_transport_send_text (EvdTransport *self, EvdPeer *peer, const gchar *text, GError **error); const gchar *evd_transport_receive (EvdTransport *self, EvdPeer *peer, gsize *size); const gchar *evd_transport_receive_text (EvdTransport *self, EvdPeer *peer); gboolean evd_transport_peer_is_connected (EvdTransport *self, EvdPeer *peer); void evd_transport_close_peer (EvdTransport *self, EvdPeer *peer, gboolean gracefully, GError **error); EvdPeer *evd_transport_create_new_peer (EvdTransport *self); EvdPeer *evd_transport_lookup_peer (EvdTransport *self, const gchar *peer_id); gboolean evd_transport_accept_peer (EvdTransport *self, EvdPeer *peer); gboolean evd_transport_reject_peer (EvdTransport *self, EvdPeer *peer); EvdPeerManager *evd_transport_get_peer_manager (EvdTransport *self); void evd_transport_set_peer_manager (EvdTransport *self, EvdPeerManager *peer_manager); void evd_transport_open (EvdTransport *self, const gchar *address, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean evd_transport_open_finish (EvdTransport *self, GAsyncResult *result, GError **error); /* defines here get_transport() method of EvdPeer to avoid cyclic dependency */ EvdTransport * evd_peer_get_transport (EvdPeer *self); G_END_DECLS #endif /* __EVD_TRANSPORT_H__ */ EventDance-0.2.0/evd/evd-utils.c000066400000000000000000000036171321356073300163750ustar00rootroot00000000000000/* * evd-utils.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-utils.h" /** * evd_timeout_add: * @context: (allow-none): * @callback: (scope notified): * @user_data: (allow-none): * **/ guint evd_timeout_add (GMainContext *context, guint timeout, gint priority, GSourceFunc callback, gpointer user_data) { guint src_id; GSource *src; if (context == NULL) context = g_main_context_get_thread_default (); if (timeout == 0) src = g_idle_source_new (); else src = g_timeout_source_new (timeout); g_source_set_priority (src, priority); g_source_set_callback (src, callback, user_data, NULL); src_id = g_source_attach (src, context); g_source_unref (src); return src_id; } void evd_nanosleep (gulong nanoseconds) { struct timespec delay; delay.tv_sec = 0; delay.tv_nsec = nanoseconds; nanosleep (&delay, NULL); } gchar * evd_uuid_new (void) { uuid_t uuid; gchar *uuid_st = NULL; uuid_st = g_new (gchar, 37); uuid_generate (uuid); uuid_unparse (uuid, uuid_st); return uuid_st; } EventDance-0.2.0/evd/evd-utils.h000066400000000000000000000026731321356073300164030ustar00rootroot00000000000000/* * evd-utils.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_UTILS_H__ #define __EVD_UTILS_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include typedef enum { EVD_VALIDATE_ACCEPT = 0, EVD_VALIDATE_REJECT = 1, EVD_VALIDATE_PENDING = 2 } EvdValidateEnum; typedef enum { EVD_POLICY_DENY = 0, EVD_POLICY_ALLOW = 1 } EvdPolicy; guint evd_timeout_add (GMainContext *context, guint timeout, gint priority, GSourceFunc callback, gpointer user_data); void evd_nanosleep (gulong nanoseconds); gchar *evd_uuid_new (void); #endif /* __EVD_UTILS_H__ */ EventDance-0.2.0/evd/evd-web-dir.c000066400000000000000000000563701321356073300165720ustar00rootroot00000000000000/* * evd-web-dir.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-web-dir.h" #define EVD_WEB_DIR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEB_DIR, \ EvdWebDirPrivate)) G_DEFINE_TYPE (EvdWebDir, evd_web_dir, EVD_TYPE_WEB_SERVICE) #define DEFAULT_ROOT_PATH "." #define DEFAULT_ALLOW_PUT FALSE #define BLOCK_SIZE 0x0FFF #define DEFAULT_DIRECTORY_INDEX "index.html" /* private data */ struct _EvdWebDirPrivate { gchar *root; gchar *alias; gboolean allow_put; gchar *dir_index; }; typedef struct { EvdWebDir *web_dir; GFile *file; GFileInputStream *file_input_stream; EvdHttpConnection *conn; EvdHttpRequest *request; void *buffer; gsize size; gchar *filename; gsize response_content_size; guint response_status_code; SoupMessageHeaders *response_headers; gboolean response_headers_sent; } EvdWebDirBinding; /* properties */ enum { PROP_0, PROP_ROOT, PROP_ALIAS, PROP_ALLOW_PUT }; static void evd_web_dir_class_init (EvdWebDirClass *class); static void evd_web_dir_init (EvdWebDir *self); static void evd_web_dir_finalize (GObject *obj); static void evd_web_dir_dispose (GObject *obj); static void evd_web_dir_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_web_dir_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_web_dir_request_handler (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request); static void evd_web_dir_file_read_block (EvdWebDirBinding *binding); static void evd_web_dir_conn_on_write (EvdConnection *conn, gpointer user_data); static void evd_web_dir_request_file (EvdWebDir *self, const gchar *filename, EvdWebDirBinding *binding); static void evd_web_dir_class_init (EvdWebDirClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); obj_class->dispose = evd_web_dir_dispose; obj_class->finalize = evd_web_dir_finalize; obj_class->get_property = evd_web_dir_get_property; obj_class->set_property = evd_web_dir_set_property; web_service_class->request_handler = evd_web_dir_request_handler; g_object_class_install_property (obj_class, PROP_ROOT, g_param_spec_string ("root", "Document root", "The root path to serve files from", DEFAULT_ROOT_PATH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_ALIAS, g_param_spec_string ("alias", "Document alias", "The alias path to serve files from", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_ALLOW_PUT, g_param_spec_boolean ("allow-put", "Allow PUT method", "Sets/gets whether to allow HTTP PUT method", DEFAULT_ALLOW_PUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdWebDirPrivate)); } static void evd_web_dir_init (EvdWebDir *self) { EvdWebDirPrivate *priv; priv = EVD_WEB_DIR_GET_PRIVATE (self); self->priv = priv; priv->allow_put = DEFAULT_ALLOW_PUT; priv->dir_index = g_strdup (DEFAULT_DIRECTORY_INDEX); evd_service_set_io_stream_type (EVD_SERVICE (self), EVD_TYPE_HTTP_CONNECTION); } static void evd_web_dir_dispose (GObject *obj) { G_OBJECT_CLASS (evd_web_dir_parent_class)->dispose (obj); } static void evd_web_dir_finalize (GObject *obj) { EvdWebDir *self = EVD_WEB_DIR (obj); g_free (self->priv->root); g_free (self->priv->alias); g_free (self->priv->dir_index); G_OBJECT_CLASS (evd_web_dir_parent_class)->finalize (obj); } static void evd_web_dir_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdWebDir *self; self = EVD_WEB_DIR (obj); switch (prop_id) { case PROP_ROOT: self->priv->root = g_value_dup_string (value); break; case PROP_ALIAS: evd_web_dir_set_alias (self, g_value_dup_string (value)); break; case PROP_ALLOW_PUT: self->priv->allow_put = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_web_dir_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdWebDir *self; self = EVD_WEB_DIR (obj); switch (prop_id) { case PROP_ROOT: g_value_set_string (value, self->priv->root); break; case PROP_ALIAS: g_value_set_string (value, evd_web_dir_get_alias (self)); break; case PROP_ALLOW_PUT: g_value_set_boolean (value, self->priv->allow_put); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_web_dir_finish_request (EvdWebDirBinding *binding) { EvdWebDir *self; EvdHttpConnection *conn; /* @TODO: consider caching the GFile for future requests */ EVD_WEB_SERVICE_LOG (EVD_WEB_SERVICE (binding->web_dir), binding->conn, binding->request, binding->response_status_code, binding->response_content_size, NULL); self = binding->web_dir; conn = binding->conn; g_signal_handlers_disconnect_by_func (conn, evd_web_dir_conn_on_write, binding); g_object_unref (binding->request); g_object_unref (binding->file); if (binding->file_input_stream != NULL) g_object_unref (binding->file_input_stream); if (binding->buffer != NULL) g_slice_free1 (BLOCK_SIZE, binding->buffer); if (binding->response_headers != NULL) soup_message_headers_free (binding->response_headers); g_free (binding->filename); g_slice_free (EvdWebDirBinding, binding); EVD_WEB_SERVICE_CLASS (evd_web_dir_parent_class)-> flush_and_return_connection (EVD_WEB_SERVICE (self), conn); g_object_unref (conn); } static void evd_web_dir_handle_content_error (EvdWebDirBinding *binding, GError *error) { switch (error->code) { case G_IO_ERROR_NOT_FOUND: binding->response_status_code = SOUP_STATUS_NOT_FOUND; break; case G_IO_ERROR_PERMISSION_DENIED: binding->response_status_code = SOUP_STATUS_FORBIDDEN; break; default: binding->response_status_code = SOUP_STATUS_IO_ERROR; break; } if (! binding->response_headers_sent) EVD_WEB_SERVICE_CLASS (evd_web_dir_parent_class)-> respond (EVD_WEB_SERVICE (binding->web_dir), binding->conn, binding->response_status_code, NULL, NULL, 0, NULL); else g_io_stream_close (G_IO_STREAM (binding->conn), NULL, NULL); evd_web_dir_finish_request (binding); } static void evd_web_dir_file_on_block_read (GObject *object, GAsyncResult *res, gpointer user_data) { EvdWebDirBinding *binding = (EvdWebDirBinding *) user_data; gssize size; GError *error = NULL; if ( (size = g_input_stream_read_finish (G_INPUT_STREAM (object), res, &error)) > 0) { if (! evd_http_connection_write_content (binding->conn, binding->buffer, size, TRUE, &error)) { evd_web_dir_handle_content_error (binding, error); g_error_free (error); } else { binding->response_content_size += size; evd_web_dir_file_read_block (binding); } } else if (size == 0) /* EOF */ { evd_web_dir_finish_request (binding); } else { /* @TODO: do proper logging */ g_debug ("Error reading file block: %s", error->message); evd_web_dir_handle_content_error (binding, error); g_error_free (error); } } static void evd_web_dir_file_read_block (EvdWebDirBinding *binding) { GInputStream *stream; stream = G_INPUT_STREAM (binding->file_input_stream); if (! g_input_stream_has_pending (stream) && evd_connection_get_max_writable (EVD_CONNECTION (binding->conn)) > 0) { g_input_stream_read_async (stream, binding->buffer, BLOCK_SIZE, evd_connection_get_priority (EVD_CONNECTION (binding->conn)), NULL, evd_web_dir_file_on_block_read, binding); } } static void evd_web_dir_file_on_open (GObject *object, GAsyncResult *res, gpointer user_data) { EvdWebDirBinding *binding = (EvdWebDirBinding *) user_data; GFile *file = G_FILE (object); GError *error = NULL; SoupHTTPVersion ver; if ( (binding->file_input_stream = g_file_read_finish (file, res, &error)) == NULL) { /* @TODO: do proper logging */ g_print ("Error opening file: %s\n", error->message); evd_web_dir_handle_content_error (binding, error); g_error_free (error); return; } /* file opened successfully */ /* now it is ok to send response headers */ ver = evd_http_message_get_version (EVD_HTTP_MESSAGE (binding->request)); if (! evd_http_connection_write_response_headers (binding->conn, ver, SOUP_STATUS_OK, NULL, binding->response_headers, &error)) { g_print ("Error sending response headers: %s\n", error->message); evd_web_dir_handle_content_error (binding, error); g_error_free (error); return; } /* headers successfully sent */ binding->response_headers_sent = TRUE; binding->response_status_code = SOUP_STATUS_OK; /* start reading */ binding->buffer = g_slice_alloc (BLOCK_SIZE); evd_web_dir_file_read_block (binding); } static gboolean evd_web_dir_check_not_modified (EvdWebDir *self, EvdHttpConnection *conn, EvdHttpRequest *request, SoupMessageHeaders *response_headers, SoupHTTPVersion http_version, guint64 file_last_modified_time) { gboolean result = FALSE; SoupMessageHeaders *req_headers; const gchar *modified_date_st; SoupDate *modified_date; req_headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); modified_date_st = soup_message_headers_get_one (req_headers, "If-Modified-Since"); if (modified_date_st == NULL) return FALSE; modified_date = soup_date_new_from_string (modified_date_st); if (modified_date != NULL) { guint64 modified_date_int; modified_date_int = soup_date_to_time_t (modified_date); if (modified_date_int >= file_last_modified_time) { GError *error = NULL; if (! evd_web_service_respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_NOT_MODIFIED, response_headers, NULL, 0, &error)) { g_debug ("Error sending NOT-MODIFIED response headers: %s", error->message); g_error_free (error); } result = TRUE; } } soup_date_free (modified_date); return result; } static void evd_web_dir_file_on_info (GObject *object, GAsyncResult *res, gpointer user_data) { EvdWebDirBinding *binding = user_data; EvdWebDir *self = binding->web_dir; GFile *file = G_FILE (object); EvdHttpConnection *conn = binding->conn; GError *error = NULL; GFileInfo *info; SoupHTTPVersion ver; EvdHttpRequest *request; GFileType file_type; SoupMessageHeaders *headers = NULL; guint64 file_modified_date_int; SoupDate *sdate; gchar *date; request = binding->request; ver = evd_http_message_get_version (EVD_HTTP_MESSAGE (request)); info = g_file_query_info_finish (G_FILE (object), res, &error); if (info == NULL) { evd_web_dir_handle_content_error (binding, error); g_error_free (error); return; } file_type = g_file_info_get_file_type (info); /* file is a directory */ if (file_type == G_FILE_TYPE_DIRECTORY) { if (self->priv->dir_index != NULL) { gchar *new_filename; new_filename = g_strdup_printf ("%s/%s", binding->filename, self->priv->dir_index); evd_web_dir_request_file (self, new_filename, binding); g_free (new_filename); } else { /* @TODO: respond with 404 Not Found */ } goto out; } /* file is a symbolic link */ else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK) { /* @TODO: check if we allow following symlinks */ goto out; } /* file is not a regular file */ else if (file_type != G_FILE_TYPE_REGULAR) { /* @TODO: respond with 404 Not Found */ goto out; } /* file is a regular file */ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); if (evd_http_connection_get_keepalive (conn)) soup_message_headers_replace (headers, "Connection", "keep-alive"); else soup_message_headers_replace (headers, "Connection", "close"); /* obtain last-modified value from file info */ file_modified_date_int = g_file_info_get_attribute_uint64 (info, "time::modified"); /* check last-modified time */ if (evd_web_dir_check_not_modified (self, conn, request, headers, ver, file_modified_date_int)) { evd_web_dir_finish_request (binding); goto out; } /* set 'last-modified' header in response */ sdate = soup_date_new_from_time_t (file_modified_date_int); date = soup_date_to_string (sdate, SOUP_DATE_HTTP); soup_message_headers_replace (headers, "Last-Modified", date); g_free (date); soup_date_free (sdate); /* check cross origin */ if (evd_http_request_is_cross_origin (request)) { const gchar *origin; origin = evd_http_request_get_origin (request); /* check if this origin is allowed */ if (evd_web_service_origin_allowed (EVD_WEB_SERVICE (self), origin)) { soup_message_headers_replace (headers, "Access-Control-Allow-Origin", origin); } } soup_message_headers_set_content_type (headers, g_file_info_get_content_type (info), NULL); soup_message_headers_set_content_length (headers, g_file_info_get_size (info)); /* now open file */ g_file_read_async (file, evd_connection_get_priority (EVD_CONNECTION (conn)), NULL, evd_web_dir_file_on_open, binding); binding->response_headers = headers; headers = NULL; out: if (headers != NULL) soup_message_headers_free (headers); g_object_unref (info); } static void evd_web_dir_conn_on_write (EvdConnection *conn, gpointer user_data) { EvdWebDirBinding *binding = (EvdWebDirBinding *) user_data; if (binding->file_input_stream != NULL) evd_web_dir_file_read_block (binding); } static gboolean evd_web_dir_method_allowed (EvdWebDir *self, const gchar *method) { return g_strcmp0 (method, "GET") == 0 || (g_strcmp0 (method, "PUT") == 0 && self->priv->allow_put); } static void evd_web_dir_request_file (EvdWebDir *self, const gchar *filename, EvdWebDirBinding *binding) { GFile *file; const gchar *FILE_ATTRS = "standard::content-type,standard::size,standard::type,time::modified"; g_free (binding->filename); binding->filename = g_strdup (filename); file = g_file_new_for_path (filename); if (binding->file != NULL) g_object_unref (binding->file); binding->file = file; g_file_query_info_async (file, FILE_ATTRS, G_FILE_QUERY_INFO_NONE, evd_connection_get_priority (EVD_CONNECTION (binding->conn)), NULL, evd_web_dir_file_on_info, binding); } static void evd_web_dir_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebDir *self = EVD_WEB_DIR (web_service); gchar *filename = NULL; EvdWebDirBinding *binding; SoupURI *uri; const gchar *path_without_alias = ""; if (! evd_web_dir_method_allowed (self, evd_http_request_get_method (request))) { EVD_WEB_SERVICE_CLASS (evd_web_dir_parent_class)-> respond (web_service, conn, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL, NULL, 0, NULL); EVD_WEB_SERVICE_LOG (web_service, conn, request, SOUP_STATUS_METHOD_NOT_ALLOWED, 0, NULL); return; } uri = evd_http_request_get_uri (request); if (self->priv->alias != NULL) { if (g_strstr_len (uri->path, -1, self->priv->alias) == uri->path) { path_without_alias = uri->path + strlen (self->priv->alias); } else { EVD_WEB_SERVICE_CLASS (evd_web_dir_parent_class)-> respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_NOT_FOUND, NULL, NULL, 0, NULL); EVD_WEB_SERVICE_LOG (web_service, conn, request, SOUP_STATUS_NOT_FOUND, 0, NULL); return; } } else { path_without_alias = uri->path; } filename = g_strconcat (self->priv->root, "/", path_without_alias, NULL); binding = g_slice_new0 (EvdWebDirBinding); binding->web_dir = self; g_object_ref (conn); binding->conn = conn; g_signal_connect (conn, "write", G_CALLBACK (evd_web_dir_conn_on_write), binding); g_object_ref (request); binding->request = request; evd_web_dir_request_file (self, filename, binding); g_free (filename); } /* public methods */ EvdWebDir * evd_web_dir_new () { EvdWebDir *self; self = g_object_new (EVD_TYPE_WEB_DIR, NULL); return self; } void evd_web_dir_set_root (EvdWebDir *self, const gchar *root) { gchar *_root; g_return_if_fail (EVD_IS_WEB_DIR (self)); g_return_if_fail (root != NULL); if (self->priv->root != NULL) g_free (self->priv->root); if (! g_path_is_absolute (root)) { gchar *current_dir; current_dir = g_get_current_dir (); _root = g_strdup_printf ("%s/%s", current_dir, root); g_free (current_dir); } else { _root = g_strdup (root); } self->priv->root = _root; } const gchar * evd_web_dir_get_root (EvdWebDir *self) { g_return_val_if_fail (EVD_IS_WEB_DIR (self), NULL); return self->priv->root; } void evd_web_dir_set_alias (EvdWebDir *self, const gchar *alias) { g_return_if_fail (EVD_IS_WEB_DIR (self)); if (self->priv->alias != NULL) g_free (self->priv->alias); self->priv->alias = g_strdup (alias); } const gchar * evd_web_dir_get_alias (EvdWebDir *self) { g_return_val_if_fail (EVD_IS_WEB_DIR (self), NULL); return self->priv->alias; } EventDance-0.2.0/evd/evd-web-dir.h000066400000000000000000000045041321356073300165670ustar00rootroot00000000000000/* * evd-web-dir.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEB_DIR_H__ #define __EVD_WEB_DIR_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-service.h" G_BEGIN_DECLS typedef struct _EvdWebDir EvdWebDir; typedef struct _EvdWebDirClass EvdWebDirClass; typedef struct _EvdWebDirPrivate EvdWebDirPrivate; struct _EvdWebDir { EvdWebService parent; EvdWebDirPrivate *priv; }; struct _EvdWebDirClass { EvdWebServiceClass parent_class; }; #define EVD_TYPE_WEB_DIR (evd_web_dir_get_type ()) #define EVD_WEB_DIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEB_DIR, EvdWebDir)) #define EVD_WEB_DIR_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEB_DIR, EvdWebDirClass)) #define EVD_IS_WEB_DIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEB_DIR)) #define EVD_IS_WEB_DIR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEB_DIR)) #define EVD_WEB_DIR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEB_DIR, EvdWebDirClass)) GType evd_web_dir_get_type (void) G_GNUC_CONST; EvdWebDir *evd_web_dir_new (void); void evd_web_dir_set_root (EvdWebDir *self, const gchar *root); const gchar *evd_web_dir_get_root (EvdWebDir *self); void evd_web_dir_set_alias (EvdWebDir *self, const gchar *alias); const gchar *evd_web_dir_get_alias (EvdWebDir *self); G_END_DECLS #endif /* __EVD_WEB_DIR_H__ */ EventDance-0.2.0/evd/evd-web-selector.c000066400000000000000000000223651321356073300176310ustar00rootroot00000000000000/* * evd-web-selector.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-web-selector.h" G_DEFINE_TYPE (EvdWebSelector, evd_web_selector, EVD_TYPE_WEB_SERVICE) #define EVD_WEB_SELECTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEB_SELECTOR, \ EvdWebSelectorPrivate)) /* private data */ struct _EvdWebSelectorPrivate { GList *candidates; EvdService *default_service; }; typedef struct { gchar *domain_pattern; gchar *path_pattern; GRegex *domain_regex; GRegex *path_regex; EvdService *service; } EvdWebSelectorCandidate; static void evd_web_selector_class_init (EvdWebSelectorClass *class); static void evd_web_selector_init (EvdWebSelector *self); static void evd_web_selector_dispose (GObject *obj); static void evd_web_selector_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request); static void evd_web_selector_free_candidate (gpointer user_data); static void evd_web_selector_class_init (EvdWebSelectorClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); obj_class->dispose = evd_web_selector_dispose; web_service_class->request_handler = evd_web_selector_request_handler; /* add private structure */ g_type_class_add_private (obj_class, sizeof (EvdWebSelectorPrivate)); } static void evd_web_selector_init (EvdWebSelector *self) { EvdWebSelectorPrivate *priv; priv = EVD_WEB_SELECTOR_GET_PRIVATE (self); self->priv = priv; priv->candidates = NULL; priv->default_service = NULL; } static void evd_web_selector_dispose (GObject *obj) { EvdWebSelector *self = EVD_WEB_SELECTOR (obj); if (self->priv->default_service != NULL) { g_object_unref (self->priv->default_service); self->priv->default_service = NULL; } g_list_free_full (self->priv->candidates, evd_web_selector_free_candidate); self->priv->candidates = NULL; G_OBJECT_CLASS (evd_web_selector_parent_class)->dispose (obj); } static void evd_web_selector_free_candidate (gpointer user_data) { EvdWebSelectorCandidate *candidate = user_data; if (candidate->domain_pattern != NULL) { g_free (candidate->domain_pattern); g_regex_unref (candidate->domain_regex); } if (candidate->path_pattern != NULL) { g_free (candidate->path_pattern); g_regex_unref (candidate->path_regex); } g_object_unref (candidate->service); g_free (candidate); } static EvdService * evd_web_selector_find_match (EvdWebSelector *self, const gchar *domain, const gchar *path) { GList *node; node = self->priv->candidates; while (node != NULL) { EvdWebSelectorCandidate *candidate; candidate = (EvdWebSelectorCandidate *) node->data; if ( (candidate->domain_pattern == NULL || g_regex_match (candidate->domain_regex, domain, 0, NULL)) && (candidate->path_pattern == NULL || g_regex_match (candidate->path_regex, path, 0, NULL)) ) { return candidate->service; } node = node->next; } return NULL; } static void evd_web_selector_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebSelector *self = EVD_WEB_SELECTOR (web_service); EvdService *service; SoupURI *uri; SoupMessageHeaders *headers; const gchar *domain; GError *error = NULL; uri = evd_http_request_get_uri (request); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); domain = soup_message_headers_get_one (headers, "host"); if ( (service = evd_web_selector_find_match (self, domain, uri->path)) == NULL) service = self->priv->default_service; if (service != NULL) { if (EVD_IS_WEB_SERVICE (service)) { evd_web_service_add_connection_with_request (EVD_WEB_SERVICE (service), conn, request, EVD_SERVICE (self)); } else { if (evd_http_connection_unread_request_headers (conn, request, &error)) { evd_io_stream_group_add (EVD_IO_STREAM_GROUP (service), G_IO_STREAM (conn)); } } } else { /* no service found, respond with a 403 Forbidden message and close the connection */ EVD_WEB_SERVICE_GET_CLASS (self)->respond (EVD_WEB_SERVICE (self), conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); } } /* public methods */ EvdWebSelector * evd_web_selector_new (void) { EvdWebSelector *self; self = g_object_new (EVD_TYPE_WEB_SELECTOR, NULL); return self; } /** * evd_web_selector_add_service: * @domain_pattern: (allow-none): * @path_pattern: (allow-none): * **/ gboolean evd_web_selector_add_service (EvdWebSelector *self, const gchar *domain_pattern, const gchar *path_pattern, EvdService *service, GError **error) { EvdWebSelectorCandidate *candidate; GRegex *domain_regex = NULL; GRegex *path_regex = NULL; g_return_val_if_fail (EVD_IS_WEB_SELECTOR (self), FALSE); g_return_val_if_fail (EVD_IS_SERVICE (service), FALSE); if (domain_pattern != NULL && (domain_regex = g_regex_new (domain_pattern, G_REGEX_CASELESS, 0, error)) == NULL) return FALSE; if (path_pattern != NULL && (path_regex = g_regex_new (path_pattern, G_REGEX_CASELESS, 0, error)) == NULL) { if (domain_regex != NULL) g_regex_unref (domain_regex); return FALSE; } candidate = g_new0 (EvdWebSelectorCandidate, 1); g_object_ref (service); candidate->service = service; candidate->domain_pattern = g_strdup (domain_pattern); candidate->path_pattern = g_strdup (path_pattern); candidate->domain_regex = domain_regex; candidate->path_regex = path_regex; self->priv->candidates = g_list_append (self->priv->candidates, candidate); return TRUE; } void evd_web_selector_remove_service (EvdWebSelector *self, const gchar *domain_pattern, const gchar *path_pattern, EvdService *service) { GList *node; g_return_if_fail (EVD_IS_WEB_SELECTOR (self)); g_return_if_fail (EVD_IS_SERVICE (service)); node = self->priv->candidates; while (node != NULL) { EvdWebSelectorCandidate *candidate; candidate = (EvdWebSelectorCandidate *) node->data; if (g_strcmp0 (candidate->domain_pattern, domain_pattern) == 0 && g_strcmp0 (candidate->path_pattern, path_pattern) == 0 && candidate->service == service) { node = node->next; self->priv->candidates = g_list_delete_link (self->priv->candidates, node); evd_web_selector_free_candidate (candidate); } else { node = node->next; } } } /** * evd_web_selector_set_default_service: * @service: (allow-none): * **/ void evd_web_selector_set_default_service (EvdWebSelector *self, EvdService *service) { g_return_if_fail (EVD_IS_WEB_SELECTOR (self)); g_return_if_fail (service == NULL || EVD_IS_SERVICE (self)); if (self->priv->default_service != NULL) { g_object_unref (self->priv->default_service); self->priv->default_service = NULL; } self->priv->default_service = service; if (self->priv->default_service != NULL) g_object_ref (self->priv->default_service); } EventDance-0.2.0/evd/evd-web-selector.h000066400000000000000000000064011321356073300176270ustar00rootroot00000000000000/* * evd-web-selector.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEB_SELECTOR_H__ #define __EVD_WEB_SELECTOR_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-service.h" G_BEGIN_DECLS typedef struct _EvdWebSelector EvdWebSelector; typedef struct _EvdWebSelectorClass EvdWebSelectorClass; typedef struct _EvdWebSelectorPrivate EvdWebSelectorPrivate; struct _EvdWebSelector { EvdWebService parent; EvdWebSelectorPrivate *priv; }; struct _EvdWebSelectorClass { EvdWebServiceClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_WEB_SELECTOR (evd_web_selector_get_type ()) #define EVD_WEB_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEB_SELECTOR, EvdWebSelector)) #define EVD_WEB_SELECTOR_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEB_SELECTOR, EvdWebSelectorClass)) #define EVD_IS_WEB_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEB_SELECTOR)) #define EVD_IS_WEB_SELECTOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEB_SELECTOR)) #define EVD_WEB_SELECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEB_SELECTOR, EvdWebSelectorClass)) GType evd_web_selector_get_type (void) G_GNUC_CONST; EvdWebSelector *evd_web_selector_new (void); gboolean evd_web_selector_add_service (EvdWebSelector *self, const gchar *domain_pattern, const gchar *path_pattern, EvdService *service, GError **error); void evd_web_selector_remove_service (EvdWebSelector *self, const gchar *domain_pattern, const gchar *path_pattern, EvdService *service); void evd_web_selector_set_default_service (EvdWebSelector *self, EvdService *service); G_END_DECLS #endif /* __EVD_WEB_SELECTOR_H__ */ EventDance-0.2.0/evd/evd-web-service.c000066400000000000000000000622271321356073300174520ustar00rootroot00000000000000/* * evd-web-service.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009/2010/2011, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include "evd-web-service.h" #include "evd-error.h" #include "evd-marshal.h" G_DEFINE_TYPE (EvdWebService, evd_web_service, EVD_TYPE_SERVICE) #define EVD_WEB_SERVICE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEB_SERVICE, \ EvdWebServicePrivate)) #define RETURN_DATA_KEY "org.eventdance.lib.WebService.RETURN_TO" #define DEFAULT_ORIGIN_POLICY EVD_POLICY_DENY #define DEFAULT_CORS_PREFLIGHT_MAX_AGE "600" /* in seconds */ typedef struct _EvdWebServicePrivate EvdWebServicePrivate; struct _EvdWebServicePrivate { GHashTable *origins; EvdPolicy origin_policy; }; /* signals */ enum { SIGNAL_REQUEST_HEADERS, SIGNAL_LOG_ENTRY, SIGNAL_LAST }; static guint evd_web_service_signals[SIGNAL_LAST] = { 0 }; static void evd_web_service_class_init (EvdWebServiceClass *class); static void evd_web_service_init (EvdWebService *self); static void evd_web_service_finalize (GObject *obj); static void evd_web_service_connection_accepted (EvdService *service, EvdConnection *conn); static void evd_web_service_return_connection (EvdWebService *self, EvdHttpConnection *conn); static gboolean evd_web_service_respond_internal (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, const gchar *content, gsize size, GError **error); static void evd_web_service_flush_and_return_connection (EvdWebService *self, EvdHttpConnection *conn); static gboolean evd_web_service_log (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request, guint status_code, gsize content_size, GError **error); static void evd_web_service_class_init (EvdWebServiceClass *class) { EvdServiceClass *service_class = EVD_SERVICE_CLASS (class); GObjectClass *obj_class = G_OBJECT_CLASS (class); obj_class->finalize = evd_web_service_finalize; class->return_connection = evd_web_service_return_connection; class->respond = evd_web_service_respond_internal; class->flush_and_return_connection = evd_web_service_flush_and_return_connection; class->log = evd_web_service_log; service_class->connection_accepted = evd_web_service_connection_accepted; evd_web_service_signals[SIGNAL_REQUEST_HEADERS] = g_signal_new ("request-headers", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdWebServiceClass, signal_request_headers), NULL, NULL, evd_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, EVD_TYPE_HTTP_CONNECTION, EVD_TYPE_HTTP_REQUEST); evd_web_service_signals[SIGNAL_LOG_ENTRY] = g_signal_new ("log-entry", G_TYPE_FROM_CLASS (obj_class), G_SIGNAL_ACTION, G_STRUCT_OFFSET (EvdWebServiceClass, signal_log_entry), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_type_class_add_private (obj_class, sizeof (EvdWebServicePrivate)); } static void evd_web_service_init (EvdWebService *self) { EvdWebServicePrivate *priv = EVD_WEB_SERVICE_GET_PRIVATE (self); evd_service_set_io_stream_type (EVD_SERVICE (self), EVD_TYPE_HTTP_CONNECTION); priv->origin_policy = DEFAULT_ORIGIN_POLICY; priv->origins = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void evd_web_service_finalize (GObject *obj) { EvdWebService *self = EVD_WEB_SERVICE (obj); EvdWebServicePrivate *priv = EVD_WEB_SERVICE_GET_PRIVATE (self); g_hash_table_unref (priv->origins); G_OBJECT_CLASS (evd_web_service_parent_class)->finalize (obj); } static void evd_web_service_invoke_request_handler (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebServiceClass *class; class = EVD_WEB_SERVICE_GET_CLASS (self); if (class->request_handler != NULL) { (* class->request_handler) (self, conn, request); } g_signal_emit (self, evd_web_service_signals[SIGNAL_REQUEST_HEADERS], 0, conn, request, NULL); } static void evd_web_service_respond_cors_preflight (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebServiceClass *class; SoupMessageHeaders *req_headers; SoupMessageHeaders *res_headers; const gchar *request_headers; const gchar *request_method; req_headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); res_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); /* @TODO: check that the actual method and headers are allowed. By now just allow all */ request_headers = soup_message_headers_get_one (req_headers, "Access-Control-Request-Headers"); if (request_headers != NULL) soup_message_headers_replace (res_headers, "Access-Control-Allow-Headers", request_headers); request_method = soup_message_headers_get_one (req_headers, "Access-Control-Request-Method"); if (request_method != NULL) soup_message_headers_replace (res_headers, "Access-Control-Allow-Methods", request_method); soup_message_headers_replace (res_headers, "Access-Control-Max-Age", DEFAULT_CORS_PREFLIGHT_MAX_AGE); class = EVD_WEB_SERVICE_GET_CLASS (self); class->respond (self, conn, SOUP_STATUS_OK, res_headers, NULL, 0, NULL); soup_message_headers_free (res_headers); } static gboolean evd_web_service_validate_request (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebServiceClass *class; gboolean result; /* check for cross-origin */ if (evd_http_request_is_cross_origin (request)) { const gchar *origin; origin = evd_http_request_get_origin (request); if (! evd_web_service_origin_allowed (self, origin)) { result = FALSE; goto reject; } else if (evd_http_request_is_cors_preflight (request)) { evd_web_service_respond_cors_preflight (self, conn, request); result = FALSE; goto out; } } /* @TODO: continue validation */ result = TRUE; goto out; reject: class = EVD_WEB_SERVICE_GET_CLASS (self); class->respond (self, conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); goto out; out: return result; } static void evd_web_service_conn_on_headers_read (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdWebService *self = EVD_WEB_SERVICE (user_data); EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); EvdHttpRequest *request; GError *error = NULL; if ( (request = evd_http_connection_read_request_headers_finish (conn, res, &error)) != NULL) { if (evd_web_service_validate_request (self, conn, request)) evd_web_service_invoke_request_handler (self, conn, request); } else { if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { g_print ("error reading request headers: %s\n", error->message); g_io_stream_close (G_IO_STREAM (conn), NULL, NULL); } g_error_free (error); } g_object_unref (self); } static void evd_web_service_connection_accepted (EvdService *service, EvdConnection *conn) { EvdWebService *self = EVD_WEB_SERVICE (service); EvdHttpRequest *request; request = evd_http_connection_get_current_request (EVD_HTTP_CONNECTION (conn)); if (request != NULL) { if (evd_web_service_validate_request (self, EVD_HTTP_CONNECTION (conn), request)) { evd_web_service_invoke_request_handler (self, EVD_HTTP_CONNECTION (conn), request); } } else { g_object_ref (self); evd_http_connection_read_request_headers (EVD_HTTP_CONNECTION (conn), NULL, evd_web_service_conn_on_headers_read, self); } } static void evd_web_service_on_service_destroyed (gpointer data, GObject *where_the_object_was) { EvdHttpConnection *conn = EVD_HTTP_CONNECTION (data); g_object_set_data (G_OBJECT (conn), RETURN_DATA_KEY, NULL); } static void evd_web_service_on_conn_destroyed (gpointer data, GObject *where_the_object_was) { EvdHttpConnection *conn = EVD_HTTP_CONNECTION (where_the_object_was); EvdService *service; service = EVD_SERVICE (g_object_get_data (G_OBJECT (conn), RETURN_DATA_KEY)); if (service != NULL) g_object_weak_unref (G_OBJECT (service), evd_web_service_on_service_destroyed, conn); } static void evd_web_service_return_connection (EvdWebService *self, EvdHttpConnection *conn) { if (g_io_stream_is_closed (G_IO_STREAM (conn))) return; evd_http_connection_set_current_request (conn, NULL); if (evd_http_connection_get_keepalive (conn)) { EvdService *return_to; return_to = EVD_SERVICE (g_object_get_data (G_OBJECT (conn), RETURN_DATA_KEY)); if (return_to != NULL) { g_object_set_data (G_OBJECT (conn), RETURN_DATA_KEY, NULL); g_object_weak_unref (G_OBJECT (conn), evd_web_service_on_conn_destroyed, NULL); g_object_weak_unref (G_OBJECT (return_to), evd_web_service_on_service_destroyed, conn); evd_io_stream_group_add (EVD_IO_STREAM_GROUP (return_to), G_IO_STREAM (conn)); } else { EVD_SERVICE_GET_CLASS (self)-> connection_accepted (EVD_SERVICE (self), EVD_CONNECTION (conn)); } } else { evd_socket_shutdown (evd_connection_get_socket (EVD_CONNECTION (conn)), TRUE, TRUE, NULL); } } static void evd_web_service_connection_on_flush (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdHttpConnection *conn = EVD_HTTP_CONNECTION (user_data); EvdIoStreamGroup *group; g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), res, NULL); group = evd_io_stream_get_group (EVD_IO_STREAM (conn)); if (group != NULL && EVD_IS_WEB_SERVICE (group)) { EvdWebService *self; self = EVD_WEB_SERVICE (group); EVD_WEB_SERVICE_GET_CLASS (group)->return_connection (self, conn); } g_object_unref (conn); } static void evd_web_service_flush_and_return_connection (EvdWebService *self, EvdHttpConnection *conn) { GOutputStream *stream; stream = g_io_stream_get_output_stream (G_IO_STREAM (conn)); g_object_ref (conn); g_output_stream_flush_async (stream, evd_connection_get_priority (EVD_CONNECTION (conn)), NULL, evd_web_service_connection_on_flush, conn); } static SoupMessageHeaders * prepare_response_headers (EvdWebService *self, EvdHttpConnection *conn, SoupMessageHeaders *_headers, SoupHTTPVersion *version) { SoupMessageHeaders *headers; EvdHttpRequest *request; request = evd_http_connection_get_current_request (conn); if (version != NULL) { if (request == NULL) *version = SOUP_HTTP_1_1; else *version = evd_http_message_get_version (EVD_HTTP_MESSAGE (request)); } if (_headers == NULL) headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); else headers = _headers; /* check cross origin */ if (request != NULL && evd_http_request_is_cross_origin (request)) { const gchar *origin; origin = evd_http_request_get_origin (request); /* check if this origin is allowed */ if (evd_web_service_origin_allowed (self, origin)) { soup_message_headers_replace (headers, "Access-Control-Allow-Origin", origin); } } return headers; } static gboolean evd_web_service_respond_internal (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, const gchar *content, gsize size, GError **error) { SoupMessageHeaders *_headers; gboolean result; SoupHTTPVersion ver; _headers = prepare_response_headers (self, conn, headers, &ver); if (evd_http_connection_respond (conn, ver, status_code, NULL, _headers, content, size, FALSE, error)) { EVD_WEB_SERVICE_GET_CLASS (self)->flush_and_return_connection (self, conn); result = TRUE; } else { evd_connection_flush_and_shutdown (EVD_CONNECTION (conn), NULL); result = FALSE; } if (headers == NULL) soup_message_headers_free (_headers); return result; } static gchar * evd_web_service_build_log_entry (EvdWebService *self, const gchar *format, EvdHttpConnection *conn, EvdHttpRequest *request, guint status_code, gsize content_size, GError **error) { gchar *entry; SoupMessageHeaders *headers; const gchar *user_agent; const gchar *referer; gchar *remote_addr; GDateTime *date; gchar *date_str; gchar *user; gchar *path; /* @TODO: format is currently ignored, and a standard Apache log entry format is used */ g_return_val_if_fail (EVD_IS_WEB_SERVICE (self), NULL); if (! EVD_IS_HTTP_CONNECTION (conn)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Cannot build log entry, invalid HTTP connection"); return NULL; } if (! EVD_IS_HTTP_REQUEST (request)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Cannot build log entry, invalid HTTP request"); return NULL; } remote_addr = evd_connection_get_remote_address_as_string (EVD_CONNECTION (conn), NULL); if (remote_addr == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Cannot build log entry, unable to determine remote address"); return NULL; } headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); user_agent = soup_message_headers_get_one (headers, "user-agent"); if (user_agent == NULL) user_agent = "-"; referer = soup_message_headers_get_one (headers, "referer"); if (referer == NULL) referer = "-"; date = g_date_time_new_now_local (); date_str = g_date_time_format (date, "%d/%b/%Y:%H:%M:%S %z"); g_date_time_unref (date); if (! evd_http_request_get_basic_auth_credentials (request, &user, NULL)) user = g_strdup ("-"); path = evd_http_request_get_path (request); entry = g_strdup_printf ("%s - %s [%s] \"%s %s HTTP/1.%d\" %u %" G_GSIZE_FORMAT " \"%s\" \"%s\"", remote_addr, user, date_str, evd_http_request_get_method (request), path, evd_http_message_get_version (EVD_HTTP_MESSAGE (request)), status_code, content_size, referer, user_agent); g_free (remote_addr); g_free (date_str); g_free (user); g_free (path); return entry; } static gboolean evd_web_service_log (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request, guint status_code, gsize content_size, GError **error) { gchar *log_entry; /* @TODO: provide a 'log-format' property to EvdWebService to use here */ log_entry = evd_web_service_build_log_entry (self, NULL, conn, request, status_code, content_size, error); if (log_entry == NULL) return FALSE; g_signal_emit (self, evd_web_service_signals[SIGNAL_LOG_ENTRY], 0, log_entry, NULL); g_free (log_entry); return TRUE; } /* public methods */ EvdWebService * evd_web_service_new (void) { EvdWebService *self; self = g_object_new (EVD_TYPE_WEB_SERVICE, NULL); return self; } gboolean evd_web_service_add_connection_with_request (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request, EvdService *return_to) { EvdService *old_service; g_return_val_if_fail (EVD_IS_WEB_SERVICE (self), FALSE); g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); g_return_val_if_fail (EVD_IS_HTTP_REQUEST (request), FALSE); evd_http_connection_set_current_request (conn, request); old_service = EVD_SERVICE (g_object_get_data (G_OBJECT (conn), RETURN_DATA_KEY)); if (old_service == NULL && return_to != NULL) { g_object_set_data (G_OBJECT (conn), RETURN_DATA_KEY, return_to); g_object_weak_ref (G_OBJECT (conn), evd_web_service_on_conn_destroyed, NULL); g_object_weak_ref (G_OBJECT (return_to), evd_web_service_on_service_destroyed, conn); } return evd_io_stream_group_add (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); } /** * evd_web_service_respond: * @headers: (allow-none): * **/ gboolean evd_web_service_respond (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, const gchar *content, gsize size, GError **error) { EvdWebServiceClass *class; g_return_val_if_fail (EVD_IS_WEB_SERVICE (self), FALSE); g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); class = EVD_WEB_SERVICE_GET_CLASS (self); g_assert (class->respond != NULL); return class->respond (self, conn, status_code, headers, content, size, error); } void evd_web_service_set_origin_policy (EvdWebService *self, EvdPolicy policy) { EvdWebServicePrivate *priv; g_return_if_fail (EVD_IS_WEB_SERVICE (self)); priv = EVD_WEB_SERVICE_GET_PRIVATE (self); priv->origin_policy = policy; } EvdPolicy evd_web_service_get_origin_policy (EvdWebService *self) { EvdWebServicePrivate *priv; g_return_val_if_fail (EVD_IS_WEB_SERVICE (self), 0); priv = EVD_WEB_SERVICE_GET_PRIVATE (self); return priv->origin_policy; } void evd_web_service_allow_origin (EvdWebService *self, const gchar *origin) { EvdWebServicePrivate *priv; gboolean *allowed; g_return_if_fail (EVD_IS_WEB_SERVICE (self)); g_return_if_fail (origin != NULL); priv = EVD_WEB_SERVICE_GET_PRIVATE (self); allowed = g_new (gboolean, 1); *allowed = TRUE; g_hash_table_insert (priv->origins, g_strdup (origin), allowed); } void evd_web_service_deny_origin (EvdWebService *self, const gchar *origin) { EvdWebServicePrivate *priv; gboolean *allowed; g_return_if_fail (EVD_IS_WEB_SERVICE (self)); g_return_if_fail (origin != NULL); priv = EVD_WEB_SERVICE_GET_PRIVATE (self); allowed = g_new (gboolean, 1); *allowed = FALSE; g_hash_table_insert (priv->origins, g_strdup (origin), allowed); } gboolean evd_web_service_origin_allowed (EvdWebService *self, const gchar *origin) { EvdWebServicePrivate *priv; gboolean *allowed; g_return_val_if_fail (EVD_IS_WEB_SERVICE (self), FALSE); g_return_val_if_fail (origin != NULL, FALSE); priv = EVD_WEB_SERVICE_GET_PRIVATE (self); allowed = g_hash_table_lookup (priv->origins, origin); if (allowed == NULL) return priv->origin_policy == EVD_POLICY_ALLOW; else return (gboolean) (*allowed); } gboolean evd_web_service_respond_headers (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, GError **error) { SoupMessageHeaders *_headers; SoupHTTPVersion ver; gboolean result; _headers = prepare_response_headers (self, conn, headers, &ver); result = evd_http_connection_write_response_headers (conn, ver, status_code, NULL, _headers, error); if (headers == NULL) soup_message_headers_free (headers); return result; } EventDance-0.2.0/evd/evd-web-service.h000066400000000000000000000150361321356073300174530ustar00rootroot00000000000000/* * evd-web-service.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEB_SERVICE_H__ #define __EVD_WEB_SERVICE_H__ #include #include "evd-service.h" #include "evd-http-connection.h" #include "evd-http-request.h" #include "evd-utils.h" G_BEGIN_DECLS typedef struct _EvdWebService EvdWebService; typedef struct _EvdWebServiceClass EvdWebServiceClass; struct _EvdWebService { EvdService parent; /* padding for future private struct */ gpointer _padding_; }; struct _EvdWebServiceClass { EvdServiceClass parent_class; /* virtual methods */ void (* request_handler) (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request); void (* return_connection) (EvdWebService *self, EvdHttpConnection *conn); void (* flush_and_return_connection) (EvdWebService *self, EvdHttpConnection *conn); gboolean (* respond) (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, const gchar *content, gsize size, GError **error); gboolean (* log) (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request, guint status_code, gsize content_size, GError **error); /* signals */ void (* signal_request_headers) (EvdWebService *self, EvdHttpConnection *connection, EvdHttpRequest *request, gpointer user_data); void (* signal_log_entry) (EvdWebService *self, const gchar *entry, gpointer user_data); /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_WEB_SERVICE (evd_web_service_get_type ()) #define EVD_WEB_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEB_SERVICE, EvdWebService)) #define EVD_WEB_SERVICE_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEB_SERVICE, EvdWebServiceClass)) #define EVD_IS_WEB_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEB_SERVICE)) #define EVD_IS_WEB_SERVICE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEB_SERVICE)) #define EVD_WEB_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEB_SERVICE, EvdWebServiceClass)) GType evd_web_service_get_type (void) G_GNUC_CONST; EvdWebService * evd_web_service_new (void); gboolean evd_web_service_add_connection_with_request (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request, EvdService *return_to); gboolean evd_web_service_respond (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, const gchar *content, gsize size, GError **error); void evd_web_service_set_origin_policy (EvdWebService *self, EvdPolicy policy); EvdPolicy evd_web_service_get_origin_policy (EvdWebService *self); void evd_web_service_allow_origin (EvdWebService *self, const gchar *origin); void evd_web_service_deny_origin (EvdWebService *self, const gchar *origin); gboolean evd_web_service_origin_allowed (EvdWebService *self, const gchar *origin); gboolean evd_web_service_respond_headers (EvdWebService *self, EvdHttpConnection *conn, guint status_code, SoupMessageHeaders *headers, GError **error); #define EVD_WEB_SERVICE_LOG(web_service, conn, request, status_code, content_size, error) \ (EVD_WEB_SERVICE_GET_CLASS (web_service)->log (web_service, conn, request, status_code, content_size, error)) G_END_DECLS #endif /* __EVD_WEB_SERVICE_H__ */ EventDance-0.2.0/evd/evd-web-transport-server.c000066400000000000000000001007701321356073300213460ustar00rootroot00000000000000/* * evd-web-transport-server.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-web-transport-server.h" #include "evd-transport.h" #include "evd-utils.h" #include "evd-error.h" #include "evd-http-connection.h" #include "evd-peer-manager.h" #include "evd-web-dir.h" #include "evd-longpolling-server.h" #include "evd-websocket-server.h" #define EVD_WEB_TRANSPORT_SERVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEB_TRANSPORT_SERVER, \ EvdWebTransportServerPrivate)) #define DEFAULT_BASE_PATH "/transport" #define MECHANISM_HEADER_NAME "X-Org-EventDance-WebTransport-Mechanism" #define PEER_ID_HEADER_NAME "X-Org-EventDance-WebTransport-Peer-Id" #define URL_HEADER_NAME "X-Org-EventDance-WebTransport-Url" #define HANDSHAKE_TOKEN_NAME "handshake" #define LONG_POLLING_TOKEN_NAME "lp" #define WEB_SOCKET_TOKEN_NAME "ws" #define LONG_POLLING_MECHANISM_NAME "long-polling" #define WEB_SOCKET_MECHANISM_NAME "websocket" #define HANDSHAKE_DATA_KEY "org.eventdance.lib.WebTransport.HANDSHAKE_DATA" #define PEER_DATA_KEY "org.eventdance.lib.WebTransportServer.PEER_DATA" /* handshake data */ typedef struct { EvdWebTransportServer *self; EvdHttpConnection *conn; EvdHttpRequest *request; JsonNode *request_data; JsonNode *response_data; } HandshakeData; /* private data */ struct _EvdWebTransportServerPrivate { gchar *base_path; gchar *hs_base_path; EvdLongpollingServer *lp; gchar *lp_base_path; EvdWebsocketServer *ws; gchar *ws_base_path; gboolean enable_ws; HandshakeData *current_handshake_data; gchar *external_url; }; /* properties */ enum { PROP_0, PROP_BASE_PATH, PROP_LP_SERVICE, PROP_WEBSOCKET_SERVICE }; static void evd_web_transport_server_class_init (EvdWebTransportServerClass *class); static void evd_web_transport_server_init (EvdWebTransportServer *self); static void evd_web_transport_server_transport_iface_init (EvdTransportInterface *iface); static void evd_web_transport_server_finalize (GObject *obj); static void evd_web_transport_server_dispose (GObject *obj); static void evd_web_transport_server_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec); static void evd_web_transport_server_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec); static void evd_web_transport_server_open (EvdTransport *transport, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable); static gboolean evd_web_transport_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error); static gboolean evd_web_transport_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer); static void evd_web_transport_server_on_request (EvdWebService *self, EvdHttpConnection *conn, EvdHttpRequest *request); static void evd_web_transport_server_set_base_path (EvdWebTransportServer *self, const gchar *base_path); static gboolean evd_web_transport_server_accept_peer (EvdTransport *transport, EvdPeer *peer); static gboolean evd_web_transport_server_reject_peer (EvdTransport *transport, EvdPeer *peer); G_DEFINE_TYPE_WITH_CODE (EvdWebTransportServer, evd_web_transport_server, EVD_TYPE_WEB_DIR, G_IMPLEMENT_INTERFACE (EVD_TYPE_TRANSPORT, evd_web_transport_server_transport_iface_init)); static void evd_web_transport_server_class_init (EvdWebTransportServerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); obj_class->dispose = evd_web_transport_server_dispose; obj_class->finalize = evd_web_transport_server_finalize; obj_class->get_property = evd_web_transport_server_get_property; obj_class->set_property = evd_web_transport_server_set_property; web_service_class->request_handler = evd_web_transport_server_on_request; g_object_class_install_property (obj_class, PROP_BASE_PATH, g_param_spec_string ("base-path", "Base path", "URL base path the transport handles", DEFAULT_BASE_PATH, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_LP_SERVICE, g_param_spec_object ("lp-service", "Long-Polling service", "Internal Long-Polling service used by the transport", EVD_TYPE_LONGPOLLING_SERVER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (obj_class, PROP_WEBSOCKET_SERVICE, g_param_spec_object ("websocket-service", "Websocket service", "Internal Websocket service used by the transport", EVD_TYPE_WEBSOCKET_SERVER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_type_class_add_private (obj_class, sizeof (EvdWebTransportServerPrivate)); } static void evd_web_transport_server_transport_iface_init (EvdTransportInterface *iface) { iface->send = evd_web_transport_server_send; iface->peer_is_connected = evd_web_transport_server_peer_is_connected; iface->accept_peer = evd_web_transport_server_accept_peer; iface->reject_peer = evd_web_transport_server_reject_peer; iface->open = evd_web_transport_server_open; } static void evd_web_transport_server_init (EvdWebTransportServer *self) { EvdWebTransportServerPrivate *priv; const gchar *js_path; priv = EVD_WEB_TRANSPORT_SERVER_GET_PRIVATE (self); self->priv = priv; priv->lp = evd_longpolling_server_new (); priv->ws = evd_websocket_server_new (); js_path = g_getenv ("JSLIBDIR"); if (js_path == NULL) js_path = JSLIBDIR; evd_web_dir_set_root (EVD_WEB_DIR (self), js_path); priv->enable_ws = TRUE; priv->current_handshake_data = NULL; priv->external_url = NULL; } static void evd_web_transport_server_dispose (GObject *obj) { G_OBJECT_CLASS (evd_web_transport_server_parent_class)->dispose (obj); } static void evd_web_transport_server_finalize (GObject *obj) { EvdWebTransportServer *self = EVD_WEB_TRANSPORT_SERVER (obj); g_free (self->priv->lp_base_path); g_object_unref (self->priv->lp); g_free (self->priv->ws_base_path); g_object_unref (self->priv->ws); g_free (self->priv->hs_base_path); g_free (self->priv->base_path); g_free (self->priv->external_url); G_OBJECT_CLASS (evd_web_transport_server_parent_class)->finalize (obj); } static void evd_web_transport_server_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { EvdWebTransportServer *self; self = EVD_WEB_TRANSPORT_SERVER (obj); switch (prop_id) { case PROP_BASE_PATH: evd_web_transport_server_set_base_path (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void evd_web_transport_server_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { EvdWebTransportServer *self; self = EVD_WEB_TRANSPORT_SERVER (obj); switch (prop_id) { case PROP_BASE_PATH: g_value_set_string (value, evd_web_transport_server_get_base_path (self)); break; case PROP_LP_SERVICE: g_value_set_object (value, self->priv->lp); break; case PROP_WEBSOCKET_SERVICE: g_value_set_object (value, self->priv->ws); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static gboolean evd_web_transport_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { EvdTransport *_transport; _transport = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (_transport == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Failed to send data, peer is not associated with a " "sub-transport"); return FALSE; } else { return EVD_TRANSPORT_GET_INTERFACE (_transport)->send (_transport, peer, buffer, size, type, error); } } static gboolean evd_web_transport_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer) { EvdTransport *_transport; _transport = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); return _transport != NULL && evd_transport_peer_is_connected (_transport, peer); } static void add_mechanism_to_response_list (JsonArray *mech_list, const gchar *mechanism_name, const gchar *mechanism_url) { JsonObject *obj; obj = json_object_new (); json_object_set_string_member (obj, "name", mechanism_name); json_object_set_string_member (obj, "url", mechanism_url); json_array_add_object_element (mech_list, obj); } static gboolean has_mechanism (JsonArray *mech_list, const gchar *mech_name) { gint i; JsonNode *element; for (i=0; iself); g_object_unref (data->conn); g_object_unref (data->request); if (data->request_data != NULL) json_node_free (data->request_data); if (data->response_data != NULL) json_node_free (data->response_data); g_slice_free (HandshakeData, data); } static void evd_web_transport_server_respond_handshake (HandshakeData *data, EvdPeer *peer) { EvdWebTransportServer *self; gchar *mechanism_url; GError *error = NULL; SoupURI *uri = NULL; JsonObject *request_obj; JsonObject *response_obj; JsonArray *request_mechs; JsonArray *response_mechs; JsonGenerator *generator; gchar *content; gsize content_size; SoupMessageHeaders *headers; self = data->self; /* setup the repsponse data JSON object */ data->response_data = json_node_new (JSON_NODE_OBJECT); response_obj = json_object_new (); json_node_set_object (data->response_data, response_obj); /* set peer id in response data*/ json_object_set_string_member (response_obj, "peer-id", evd_peer_get_id (peer)); /* set the list of mechanisms in response data */ response_mechs = json_array_new (); json_object_set_array_member (response_obj, "mechanisms", response_mechs); request_obj = json_node_get_object (data->request_data); request_mechs = json_object_get_array_member (request_obj, "mechanisms"); /* resolve the transport url from peer's perspective */ if (json_object_has_member (request_obj, "url")) { const gchar *uri_str; uri_str = json_object_get_string_member (request_obj, "url"); uri = soup_uri_new (uri_str); /* @TODO: validate that uri is not null and fail the handshake if so */ } if (uri == NULL) uri = soup_uri_copy (evd_http_request_get_uri (data->request)); /* websocket? */ if (self->priv->enable_ws && has_mechanism (request_mechs, WEB_SOCKET_MECHANISM_NAME)) { SoupURI *ws_uri; gboolean tls = FALSE; guint32 port = 0; if (self->priv->external_url != NULL) { ws_uri = soup_uri_new (self->priv->external_url); if (g_strcmp0 (ws_uri->scheme, "https") == 0) tls = TRUE; } else { ws_uri = soup_uri_copy (uri); if (evd_connection_get_tls_active (EVD_CONNECTION (data->conn))) tls = TRUE; } port = ws_uri->port; if (tls) soup_uri_set_scheme (ws_uri, "wss"); else soup_uri_set_scheme (ws_uri, "ws"); ws_uri->port = port; soup_uri_set_path (ws_uri, self->priv->ws_base_path); mechanism_url = soup_uri_to_string (ws_uri, FALSE); soup_uri_free (ws_uri); add_mechanism_to_response_list (response_mechs, WEB_SOCKET_MECHANISM_NAME, mechanism_url); g_free (mechanism_url); } /* long-polling? */ if (has_mechanism (request_mechs, LONG_POLLING_MECHANISM_NAME)) { SoupURI *lp_uri; if (self->priv->external_url != NULL) lp_uri = soup_uri_new (self->priv->external_url); else lp_uri = soup_uri_copy (uri); soup_uri_set_path (lp_uri, self->priv->lp_base_path); soup_uri_set_query (lp_uri, NULL); mechanism_url = soup_uri_to_string (lp_uri, FALSE); soup_uri_free (lp_uri); add_mechanism_to_response_list (response_mechs, LONG_POLLING_MECHANISM_NAME, mechanism_url); g_free (mechanism_url); } /* generate JSON data for the response */ generator = json_generator_new (); json_generator_set_root (generator, data->response_data); content = json_generator_to_data (generator, &content_size); json_object_unref (response_obj); g_object_unref (generator); headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); /* prevent HTTP caching */ soup_message_headers_replace (headers, "Cache-Control", "Cache-Control: no-cache, must-revalidate"); soup_message_headers_replace (headers, "Expires", "Sat, 01 Jan 2000 00:00:00 GMT"); if (! EVD_WEB_SERVICE_GET_CLASS (self)->respond (EVD_WEB_SERVICE (self), data->conn, SOUP_STATUS_OK, headers, content, content_size, &error)) { /* @TODO: do proper logging */ g_debug ("Error responding handshake: %s", error->message); g_error_free (error); } soup_message_headers_free (headers); soup_uri_free (uri); g_free (content); } static void evd_web_transport_server_conn_on_close (EvdConnection *conn, gpointer user_data) { EvdPeer *peer = user_data; g_object_set_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY, NULL); g_signal_handlers_disconnect_by_func (conn, evd_web_transport_server_conn_on_close, user_data); g_object_unref (peer); } static void evd_web_transport_handshake (HandshakeData *data) { EvdWebTransportServer *self; EvdPeer *peer; JsonObject *request_obj; JsonArray *request_mechs; EvdTransportInterface *iface; guint validate_result; self = data->self; /* check if at least one mechanism can be negotiated */ request_obj = json_node_get_object (data->request_data); request_mechs = json_object_get_array_member (request_obj, "mechanisms"); if (request_mechs == NULL || (! has_mechanism (request_mechs, WEB_SOCKET_MECHANISM_NAME) && ! has_mechanism (request_mechs, LONG_POLLING_MECHANISM_NAME))) { /* return 503 Service Unavailable, no mechanism can be negotiated */ EVD_WEB_SERVICE_GET_CLASS (self)-> respond (EVD_WEB_SERVICE (self), data->conn, SOUP_STATUS_SERVICE_UNAVAILABLE, NULL, NULL, 0, NULL); return; } /* create peer */ peer = g_object_new (EVD_TYPE_PEER, "transport", self, NULL); /* setup peer arguments for validation */ self->priv->current_handshake_data = data; /* validate peer */ iface = EVD_TRANSPORT_GET_INTERFACE (self); validate_result = iface->notify_validate_peer (EVD_TRANSPORT (self), peer); /* teardown peer arguments */ self->priv->current_handshake_data = NULL; /* check result of peer validation */ if (validate_result == EVD_VALIDATE_ACCEPT) { /* accept peer */ evd_web_transport_server_accept_peer (EVD_TRANSPORT (self), peer); evd_web_transport_server_respond_handshake (data, peer); free_handshake_data (data); } else if (validate_result == EVD_VALIDATE_REJECT) { /* reject peer */ EVD_WEB_SERVICE_GET_CLASS (self)-> respond (EVD_WEB_SERVICE (self), data->conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); free_handshake_data (data); } else { /* peer validation pending */ g_object_set_data_full (G_OBJECT (peer), HANDSHAKE_DATA_KEY, data, (GDestroyNotify) free_handshake_data); g_object_ref (peer); g_signal_connect (data->conn, "close", G_CALLBACK (evd_web_transport_server_conn_on_close), peer); } g_object_unref (peer); } static void evd_web_transport_on_handshake_data (GObject *obj, GAsyncResult *res, gpointer user_data) { HandshakeData *data = user_data; gchar *content; gssize size; GError *error = NULL; g_assert (obj == G_OBJECT (data->conn)); content = evd_http_connection_read_all_content_finish (data->conn, res, &size, &error); if (content != NULL) { JsonParser *parser; parser = json_parser_new (); if (json_parser_load_from_data (parser, content, size, &error)) { JsonNode *root; root = json_parser_get_root (parser); if (root == NULL) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "No handshake data sent"); } else if (! JSON_NODE_HOLDS_OBJECT (root)) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Handshake data must be a JSON object"); } else { data->request_data = json_node_copy (json_parser_get_root (parser)); } } g_object_unref (parser); } if (error == NULL) { evd_web_transport_handshake (data); } else { /* @TODO: do proper logging */ g_debug ("Web transport handshake failed: %s", error->message); g_error_free (error); EVD_WEB_SERVICE_GET_CLASS (data->self)-> respond (EVD_WEB_SERVICE (data->self), data->conn, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL, NULL, 0, NULL); free_handshake_data (data); } g_free (content); } static void evd_web_transport_server_read_handshake_data (EvdWebTransportServer *self, EvdHttpConnection *conn, EvdHttpRequest *request) { HandshakeData *data; data = g_slice_new0 (HandshakeData); data->self = self; g_object_ref (self); data->conn = conn; g_object_ref (conn); data->request = request; g_object_ref (request); evd_http_connection_read_all_content (conn, NULL, evd_web_transport_on_handshake_data, data); } static EvdWebService * get_actual_transport_from_path (EvdWebTransportServer *self, const gchar *path) { if (g_strstr_len (path, -1, self->priv->lp_base_path) == path) return EVD_WEB_SERVICE (self->priv->lp); else if (self->priv->enable_ws && g_strstr_len (path, -1, self->priv->ws_base_path) == path) return EVD_WEB_SERVICE (self->priv->ws); else return NULL; } static void evd_web_transport_server_on_request (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebTransportServer *self = EVD_WEB_TRANSPORT_SERVER (web_service); SoupURI *uri; EvdWebService *actual_service; uri = evd_http_request_get_uri (request); /* handshake? */ if (g_strcmp0 (uri->path, self->priv->hs_base_path) == 0) { evd_web_transport_server_read_handshake_data (self, conn, request); } /* longpolling or websocket? */ else if ((actual_service = get_actual_transport_from_path (self, uri->path)) != NULL) { EvdPeer *peer; EvdTransport *current_transport; if (uri->query != NULL && (peer = evd_transport_lookup_peer (EVD_TRANSPORT (self), uri->query)) != NULL) { evd_peer_touch (peer); current_transport = EVD_TRANSPORT (g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY)); if (current_transport != EVD_TRANSPORT (actual_service)) { g_object_ref (actual_service); g_object_set_data_full (G_OBJECT (peer), PEER_DATA_KEY, actual_service, g_object_unref); } } evd_web_service_add_connection_with_request (EVD_WEB_SERVICE (actual_service), conn, request, NULL); } else { EVD_WEB_SERVICE_CLASS (evd_web_transport_server_parent_class)-> request_handler (web_service, conn, request); } } static void evd_web_transport_server_set_base_path (EvdWebTransportServer *self, const gchar *base_path) { g_return_if_fail (base_path != NULL); if (base_path[strlen (base_path) - 1] == '/') self->priv->base_path = g_strdup (base_path); else self->priv->base_path = g_strdup_printf ("%s/", base_path); self->priv->hs_base_path = g_strdup_printf ("%s%s", self->priv->base_path, HANDSHAKE_TOKEN_NAME); self->priv->lp_base_path = g_strdup_printf ("%s%s", self->priv->base_path, LONG_POLLING_TOKEN_NAME); self->priv->ws_base_path = g_strdup_printf ("%s%s", self->priv->base_path, WEB_SOCKET_TOKEN_NAME); evd_web_dir_set_alias (EVD_WEB_DIR (self), base_path); } static gboolean evd_web_transport_server_accept_peer (EvdTransport *transport, EvdPeer *peer) { EvdPeerManager *peer_manager; HandshakeData *data; peer_manager = evd_transport_get_peer_manager (transport); if (evd_peer_manager_lookup_peer (peer_manager, evd_peer_get_id (peer)) == NULL) { evd_peer_manager_add_peer (peer_manager, peer); EVD_TRANSPORT_GET_INTERFACE (transport)-> notify_new_peer (transport, peer); } data = g_object_get_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY); if (data == NULL) return TRUE; g_signal_handlers_disconnect_by_func (data->conn, evd_web_transport_server_conn_on_close, peer); evd_web_transport_server_respond_handshake (data, peer); g_object_set_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY, NULL); g_object_unref (peer); return TRUE; } static gboolean evd_web_transport_server_reject_peer (EvdTransport *transport, EvdPeer *peer) { HandshakeData *data; data = g_object_get_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY); if (data == NULL) return TRUE; g_signal_handlers_disconnect_by_func (data->conn, evd_web_transport_server_conn_on_close, peer); EVD_WEB_SERVICE_GET_CLASS (transport)->respond (EVD_WEB_SERVICE (transport), data->conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); g_object_set_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY, NULL); g_object_unref (peer); return TRUE; } static void evd_web_transport_server_on_open (GObject *obj, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *orig_res = G_SIMPLE_ASYNC_RESULT (user_data); if (! evd_service_listen_finish (EVD_SERVICE (obj), res, &error)) { g_simple_async_result_set_from_error (orig_res, error); g_error_free (error); } g_simple_async_result_complete (orig_res); g_object_unref (orig_res); } static void evd_web_transport_server_open (EvdTransport *transport, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable) { evd_service_listen (EVD_SERVICE (transport), address, cancellable, evd_web_transport_server_on_open, async_result); } /* public methods */ EvdWebTransportServer * evd_web_transport_server_new (const gchar *base_path) { if (base_path == NULL) base_path = DEFAULT_BASE_PATH; return g_object_new (EVD_TYPE_WEB_TRANSPORT_SERVER, "base-path", base_path, NULL); } void evd_web_transport_server_use_selector (EvdWebTransportServer *self, EvdWebSelector *selector) { g_return_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self)); g_return_if_fail (EVD_IS_WEB_SELECTOR (selector)); evd_web_selector_add_service (selector, NULL, self->priv->base_path, EVD_SERVICE (self), NULL); } void evd_web_transport_server_unuse_selector (EvdWebTransportServer *self, EvdWebSelector *selector) { g_return_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self)); g_return_if_fail (EVD_IS_WEB_SELECTOR (selector)); evd_web_selector_remove_service (selector, NULL, self->priv->base_path, EVD_SERVICE (self)); } const gchar * evd_web_transport_server_get_base_path (EvdWebTransportServer *self) { g_return_val_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self), NULL); return self->priv->base_path; } void evd_web_transport_server_set_enable_websocket (EvdWebTransportServer *self, gboolean enabled) { g_return_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self)); self->priv->enable_ws = enabled; } /** * evd_web_transport_server_get_validate_peer_arguments: * @conn: (out) (allow-none) (transfer none): * @request: (out) (allow-none) (transfer none): * **/ void evd_web_transport_server_get_validate_peer_arguments (EvdWebTransportServer *self, EvdPeer *peer, EvdHttpConnection **conn, EvdHttpRequest **request) { g_return_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self)); if (self->priv->current_handshake_data == NULL) return; if (conn != NULL) *conn = self->priv->current_handshake_data->conn; if (request != NULL) *request = self->priv->current_handshake_data->request; } /** * evd_web_transport_server_set_external_base_url: * @base_url: (allow-none): **/ void evd_web_transport_server_set_external_base_url (EvdWebTransportServer *self, const gchar *base_url) { g_return_if_fail (EVD_IS_WEB_TRANSPORT_SERVER (self)); g_free (self->priv->external_url); self->priv->external_url = NULL; if (base_url != NULL) self->priv->external_url = g_strdup (base_url); } EventDance-0.2.0/evd/evd-web-transport-server.h000066400000000000000000000100321321356073300213420ustar00rootroot00000000000000/* * evd-web-transport-server.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEB_TRANSPORT_SERVER_H__ #define __EVD_WEB_TRANSPORT_SERVER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-dir.h" #include "evd-web-selector.h" #include "evd-peer.h" G_BEGIN_DECLS typedef struct _EvdWebTransportServer EvdWebTransportServer; typedef struct _EvdWebTransportServerClass EvdWebTransportServerClass; typedef struct _EvdWebTransportServerPrivate EvdWebTransportServerPrivate; struct _EvdWebTransportServer { EvdWebDir parent; EvdWebTransportServerPrivate *priv; }; struct _EvdWebTransportServerClass { EvdWebDirClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_WEB_TRANSPORT_SERVER (evd_web_transport_server_get_type ()) #define EVD_WEB_TRANSPORT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEB_TRANSPORT_SERVER, EvdWebTransportServer)) #define EVD_WEB_TRANSPORT_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEB_TRANSPORT_SERVER, EvdWebTransportServerClass)) #define EVD_IS_WEB_TRANSPORT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEB_TRANSPORT_SERVER)) #define EVD_IS_WEB_TRANSPORT_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEB_TRANSPORT_SERVER)) #define EVD_WEB_TRANSPORT_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEB_TRANSPORT_SERVER, EvdWebTransportServerClass)) GType evd_web_transport_server_get_type (void) G_GNUC_CONST; EvdWebTransportServer * evd_web_transport_server_new (const gchar *base_path); void evd_web_transport_server_use_selector (EvdWebTransportServer *self, EvdWebSelector *selector); void evd_web_transport_server_unuse_selector (EvdWebTransportServer *self, EvdWebSelector *selector); const gchar * evd_web_transport_server_get_base_path (EvdWebTransportServer *self); void evd_web_transport_server_set_enable_websocket (EvdWebTransportServer *self, gboolean enabled); void evd_web_transport_server_get_validate_peer_arguments (EvdWebTransportServer *self, EvdPeer *peer, EvdHttpConnection **conn, EvdHttpRequest **request); void evd_web_transport_server_set_external_base_url (EvdWebTransportServer *self, const gchar *base_url); G_END_DECLS #endif /* __EVD_WEB_TRANSPORT_SERVER_H__ */ EventDance-0.2.0/evd/evd-websocket-client.c000066400000000000000000000567651321356073300205130ustar00rootroot00000000000000/* * evd-websocket-client.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2014 Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-websocket-client.h" #include "evd-transport.h" #include "evd-connection-pool.h" #include "evd-websocket-protocol.h" #define EVD_WEBSOCKET_CLIENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEBSOCKET_CLIENT, \ EvdWebsocketClientPrivate)) #define PEER_DATA_KEY "org.eventdance.lib.WebsocketClient.PEER_DATA" #define CONN_DATA_KEY "org.eventdance.lib.WebsocketClient.CONN_DATA" #define DEFAULT_STANDALONE TRUE struct _EvdWebsocketClientPrivate { gboolean standalone; EvdHttpConnection *peer_arg_conn; SoupMessageHeaders *peer_arg_headers; }; typedef struct { EvdWebsocketClient *self; gchar *address; EvdConnectionPool *pool; EvdPeer *peer; GSimpleAsyncResult *async_result; GCancellable *cancellable; gchar *handshake_key; SoupMessageHeaders *res_headers; gboolean validating_peer; } ConnectionData; static void evd_websocket_client_class_init (EvdWebsocketClientClass *class); static void evd_websocket_client_init (EvdWebsocketClient *self); static void evd_websocket_client_transport_iface_init (EvdTransportInterface *iface); static gboolean io_stream_group_add (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream); static gboolean io_stream_group_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream); static void start_opening_handshake (EvdWebsocketClient *self, EvdHttpConnection *conn); static void resolve_peer_and_validate (EvdWebsocketClient *self, EvdHttpConnection *conn); static void on_connection_closed (EvdConnection *conn, gpointer user_data); static gboolean evd_websocket_client_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error); static gboolean peer_is_connected (EvdTransport *transport, EvdPeer *peer); static void peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully); static gboolean transport_accept_peer (EvdTransport *transport, EvdPeer *peer); static gboolean transport_reject_peer (EvdTransport *transport, EvdPeer *peer); static void transport_open (EvdTransport *self, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable); static void retry_connection (ConnectionData *data); G_DEFINE_TYPE_WITH_CODE (EvdWebsocketClient, evd_websocket_client, EVD_TYPE_IO_STREAM_GROUP, G_IMPLEMENT_INTERFACE (EVD_TYPE_TRANSPORT, evd_websocket_client_transport_iface_init)); static void evd_websocket_client_class_init (EvdWebsocketClientClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIoStreamGroupClass *io_stream_group_class = EVD_IO_STREAM_GROUP_CLASS (class); io_stream_group_class->add = io_stream_group_add; io_stream_group_class->remove = io_stream_group_remove; g_type_class_add_private (obj_class, sizeof (EvdWebsocketClientPrivate)); } static void evd_websocket_client_transport_iface_init (EvdTransportInterface *iface) { iface->send = evd_websocket_client_send; iface->peer_is_connected = peer_is_connected; iface->peer_closed = peer_closed; iface->accept_peer = transport_accept_peer; iface->reject_peer = transport_reject_peer; iface->open = transport_open; } static void evd_websocket_client_init (EvdWebsocketClient *self) { EvdWebsocketClientPrivate *priv; priv = EVD_WEBSOCKET_CLIENT_GET_PRIVATE (self); self->priv = priv; priv->standalone = DEFAULT_STANDALONE; } static gboolean io_stream_group_add (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream) { EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (io_stream_group); EvdHttpConnection *conn; EvdWebsocketState state; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (io_stream), FALSE); if (! EVD_IO_STREAM_GROUP_CLASS (evd_websocket_client_parent_class)->add (io_stream_group, io_stream)) { return FALSE; } g_signal_connect (io_stream, "close", G_CALLBACK (on_connection_closed), io_stream_group); conn = EVD_HTTP_CONNECTION (io_stream); /* check state of the connection and act accordingly */ state = evd_websocket_protocol_get_state (conn); if (state == EVD_WEBSOCKET_STATE_NONE) start_opening_handshake (self, conn); else resolve_peer_and_validate (self, conn); return TRUE; } static gboolean io_stream_group_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream) { ConnectionData *conn_data; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (io_stream), FALSE); if (! EVD_IO_STREAM_GROUP_CLASS (evd_websocket_client_parent_class)->remove (io_stream_group, io_stream)) { return FALSE; } evd_websocket_protocol_unbind (EVD_HTTP_CONNECTION (io_stream)); g_signal_handlers_disconnect_by_func (io_stream, on_connection_closed, io_stream_group); /* unlink peer and connection */ conn_data = g_object_get_data (G_OBJECT (io_stream), CONN_DATA_KEY); if (conn_data != NULL) { EvdPeer *peer; peer = conn_data->peer; if (peer != NULL) { g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); conn_data->peer = NULL; if (evd_peer_get_transport (peer) == EVD_TRANSPORT (io_stream_group)) { evd_transport_close_peer (EVD_TRANSPORT (io_stream_group), peer, FALSE, NULL); } } } return TRUE; } static void on_frame_received (EvdHttpConnection *conn, const gchar *frame, gsize frame_len, gboolean is_binary, gpointer user_data) { EvdTransportInterface *iface; EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (user_data); EvdTransport *transport = EVD_TRANSPORT (self); ConnectionData *conn_data; conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); g_assert (conn_data->peer != NULL); evd_peer_touch (conn_data->peer); iface = EVD_TRANSPORT_GET_INTERFACE (transport); iface->receive (transport, conn_data->peer, frame, frame_len); } static void on_connection_closed (EvdConnection *conn, gpointer user_data) { EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (user_data); evd_io_stream_group_remove (EVD_IO_STREAM_GROUP (self), G_IO_STREAM (conn)); } static void on_close_requested (EvdHttpConnection *conn, gboolean gracefully, gpointer user_data) { EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (user_data); ConnectionData *conn_data; conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (on_connection_closed), self); if (conn_data->peer != NULL) { if (gracefully) { evd_transport_close_peer (EVD_TRANSPORT (self), conn_data->peer, TRUE, NULL); } else { /* retry */ retry_connection (conn_data); } } } static gboolean peer_is_connected (EvdTransport *transport, EvdPeer *peer) { gpointer peer_data; EvdHttpConnection *conn; EvdWebsocketState state; peer_data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (peer_data == NULL || ! EVD_IS_HTTP_CONNECTION (peer_data)) return FALSE; conn = EVD_HTTP_CONNECTION (peer_data); state = evd_websocket_protocol_get_state (conn); return state == EVD_WEBSOCKET_STATE_OPENING || state == EVD_WEBSOCKET_STATE_OPENED || state == EVD_WEBSOCKET_STATE_CLOSING; } static gboolean evd_websocket_client_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { gpointer peer_data; peer_data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (peer_data == NULL || ! EVD_IS_HTTP_CONNECTION (peer_data)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Peer has no WebSocket connection associated"); return FALSE; } return evd_websocket_protocol_send (EVD_HTTP_CONNECTION (peer_data), buffer, size, type, error); } static void peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully) { gpointer peer_data; EvdHttpConnection *conn; ConnectionData *conn_data; peer_data = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (peer_data == NULL) return; conn = EVD_HTTP_CONNECTION (peer_data); if (! g_io_stream_is_closed (G_IO_STREAM (conn))) { GError *error = NULL; guint16 code; /* @TODO: choose a proper closing code */ code = gracefully ? EVD_WEBSOCKET_CLOSE_NORMAL : EVD_WEBSOCKET_CLOSE_ABNORMAL; if (! evd_websocket_protocol_close (conn, code, NULL, &error)) { /* @TODO: do proper error logging */ g_print ("Error closing websocket connection: %s\n", error->message); g_error_free (error); } } conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); conn_data->peer = NULL; g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); } static void on_websocket_connection_ready (EvdWebsocketClient *self, EvdHttpConnection *conn, ConnectionData *conn_data) { EvdTransportInterface *iface; g_assert (conn_data->peer != NULL); /* notify new peer */ iface = EVD_TRANSPORT_GET_INTERFACE (self); evd_peer_manager_add_peer (iface->peer_manager, conn_data->peer); g_object_unref (conn_data->peer); iface->notify_new_peer (EVD_TRANSPORT (self), conn_data->peer); g_object_ref (self); evd_websocket_protocol_bind (conn, on_frame_received, on_close_requested, self, g_object_unref); } static gboolean transport_accept_peer (EvdTransport *transport, EvdPeer *peer) { EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (transport); ConnectionData *conn_data; EvdHttpConnection *conn; conn = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (conn == NULL) return FALSE; conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); if (conn_data == NULL) return FALSE; if (! conn_data->validating_peer) return FALSE; conn_data->validating_peer = FALSE; on_websocket_connection_ready (self, conn, conn_data); return TRUE; } static gboolean transport_reject_peer (EvdTransport *transport, EvdPeer *peer) { ConnectionData *conn_data; EvdHttpConnection *conn; conn = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (conn == NULL) return FALSE; conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); if (conn_data == NULL) return FALSE; if (! conn_data->validating_peer) return FALSE; conn_data->validating_peer = FALSE; g_object_unref (conn_data->peer); conn_data->peer = NULL; evd_websocket_protocol_close (conn, EVD_WEBSOCKET_CLOSE_POLICY_VIOLATION, "Peer rejected", NULL); return TRUE; } static void free_connection_data (ConnectionData *data) { g_object_unref (data->self); if (data->cancellable != NULL) g_object_unref (data->cancellable); g_object_unref (data->pool); g_free (data->address); g_free (data->handshake_key); if (data->res_headers != NULL) soup_message_headers_free (data->res_headers); g_slice_free (ConnectionData, data); } static void resolve_peer_and_validate (EvdWebsocketClient *self, EvdHttpConnection *conn) { ConnectionData *conn_data; EvdTransportInterface *iface; guint validate_result; conn_data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); g_assert (conn_data != NULL); if (conn_data->peer == NULL) conn_data->peer = g_object_new (EVD_TYPE_PEER, "transport", self, NULL); else g_object_ref (conn_data->peer); g_object_set_data_full (G_OBJECT (conn_data->peer), PEER_DATA_KEY, conn, g_object_unref); /* validate peer */ iface = EVD_TRANSPORT_GET_INTERFACE (self); self->priv->peer_arg_conn = conn; self->priv->peer_arg_headers = conn_data->res_headers; validate_result = iface->notify_validate_peer (EVD_TRANSPORT (self), conn_data->peer); self->priv->peer_arg_conn = NULL; self->priv->peer_arg_headers = NULL; if (validate_result == EVD_VALIDATE_ACCEPT) { /* peer accepted */ on_websocket_connection_ready (self, conn, conn_data); } else if (validate_result == EVD_VALIDATE_REJECT) { /* peer rejected */ g_object_unref (conn_data->peer); conn_data->peer = NULL; evd_websocket_protocol_close (conn, EVD_WEBSOCKET_CLOSE_POLICY_VIOLATION, "Peer rejected", NULL); } else { conn_data->validating_peer = TRUE; } } static void on_handshake_response (GObject *obj, GAsyncResult *res, gpointer user_data) { ConnectionData *conn_data = user_data; GError *error = NULL; EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); SoupMessageHeaders *res_headers = NULL; SoupHTTPVersion http_version; guint status_code; res_headers = evd_http_connection_read_response_headers_finish (conn, res, &http_version, &status_code, NULL, &error); if (res_headers == NULL) { goto out; } /* validate handshake response */ if (! evd_websocket_protocol_handle_handshake_response (conn, http_version, status_code, res_headers, conn_data->handshake_key, &error)) { goto out; } /* handshake succeeded */ conn_data->res_headers = res_headers; resolve_peer_and_validate (conn_data->self, conn); out: if (conn_data->async_result != NULL) { if (error != NULL) g_simple_async_result_take_error (conn_data->async_result, error); g_simple_async_result_complete (conn_data->async_result); g_object_unref (conn_data->async_result); conn_data->async_result = NULL; } g_free (conn_data->handshake_key); conn_data->handshake_key = NULL; } static void retry_connection (ConnectionData *data) { /* @TODO: retry websocket connection */ g_print ("Retry websocket connection\n"); } static void on_handshake_sent (GObject *obj, GAsyncResult *res, gpointer user_data) { ConnectionData *data = user_data; GError *error = NULL; EvdHttpConnection *conn = EVD_HTTP_CONNECTION (obj); if (! evd_http_connection_write_request_headers_finish (conn, res, &error)) { /* @TODO: do proper debugging */ g_print ("WebSocket connection error: %s\n", error->message); g_error_free (error); retry_connection (data); return; } /* read response headers */ evd_http_connection_read_response_headers (conn, data->cancellable, on_handshake_response, data); } static void start_opening_handshake (EvdWebsocketClient *self, EvdHttpConnection *conn) { EvdHttpRequest *request; ConnectionData *data; data = g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY); g_assert (data != NULL); request = evd_websocket_protocol_create_handshake_request (data->address, NULL, NULL, &data->handshake_key); evd_http_connection_write_request_headers (conn, request, data->cancellable, on_handshake_sent, data); g_object_unref (request); } static void on_connection (GObject *obj, GAsyncResult *res, gpointer user_data) { ConnectionData *data = user_data; GError *error = NULL; EvdHttpConnection *conn; conn = EVD_HTTP_CONNECTION (evd_connection_pool_get_connection_finish (EVD_CONNECTION_POOL (obj), res, &error)); if (conn == NULL) { g_print ("Websocket connection failed: %s\n", error->message); g_error_free (error); retry_connection (data); return; } /* got a connection */ g_object_set_data_full (G_OBJECT (conn), CONN_DATA_KEY, data, (GDestroyNotify) free_connection_data); /* add it to my group */ evd_io_stream_group_add (EVD_IO_STREAM_GROUP (data->self), G_IO_STREAM (conn)); } static void get_connection (EvdConnectionPool *pool, GCancellable *cancellable, gpointer user_data) { evd_connection_pool_get_connection (pool, cancellable, on_connection, user_data); } static void transport_open (EvdTransport *transport, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable) { SoupURI *uri; EvdWebsocketClient *self = EVD_WEBSOCKET_CLIENT (transport); gchar *addr; ConnectionData *data; uri = soup_uri_new (address); if (uri == NULL) { g_simple_async_result_set_error (async_result, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Websocket URI is invalid"); g_simple_async_result_complete_in_idle (async_result); g_object_unref (async_result); return; } /* validate URI scheme */ if (g_strcmp0 (uri->scheme, "ws") != 0 && g_strcmp0 (uri->scheme, "wss") != 0) { g_simple_async_result_set_error (async_result, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Websocket URI scheme is invalid"); g_simple_async_result_complete_in_idle (async_result); g_object_unref (async_result); goto out; } data = g_slice_new0 (ConnectionData); data->self = g_object_ref (self); data->address = g_strdup (address); data->async_result = async_result; if (data->cancellable) data->cancellable = g_object_ref (cancellable); /* connection pool */ addr = g_strdup_printf ("%s:%d", soup_uri_get_host (uri), soup_uri_get_port (uri)); data->pool = evd_connection_pool_new (addr, EVD_TYPE_HTTP_CONNECTION); /* if scheme is WSS (secure WebSocket), set connection pool to auto start TLS */ if (g_strcmp0 (uri->scheme, "wss") == 0) evd_connection_pool_set_tls_autostart (data->pool, TRUE); g_free (addr); /* get a connection */ get_connection (data->pool, cancellable, data); out: soup_uri_free (uri); } /* public methods */ EvdWebsocketClient * evd_websocket_client_new (void) { return g_object_new (EVD_TYPE_WEBSOCKET_CLIENT, NULL); } /** * evd_websocket_client_get_validate_peer_arguments: * @conn: (out) (allow-none) (transfer none): * @response_headers: (out) (allow-none) (transfer none): * **/ void evd_websocket_client_get_validate_peer_arguments (EvdWebsocketClient *self, EvdPeer *peer, EvdHttpConnection **conn, SoupMessageHeaders **response_headers) { g_return_if_fail (EVD_IS_WEBSOCKET_CLIENT (self)); if (conn != NULL) *conn = self->priv->peer_arg_conn; if (response_headers != NULL) *response_headers = self->priv->peer_arg_headers; } EventDance-0.2.0/evd/evd-websocket-client.h000066400000000000000000000064271321356073300205060ustar00rootroot00000000000000/* * evd-websocket-client.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEBSOCKET_CLIENT_H__ #define __EVD_WEBSOCKET_CLIENT_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-service.h" #include "evd-http-connection.h" #include "evd-http-request.h" #include "evd-peer.h" G_BEGIN_DECLS typedef struct _EvdWebsocketClient EvdWebsocketClient; typedef struct _EvdWebsocketClientClass EvdWebsocketClientClass; typedef struct _EvdWebsocketClientPrivate EvdWebsocketClientPrivate; struct _EvdWebsocketClient { EvdIoStreamGroup parent; EvdWebsocketClientPrivate *priv; }; struct _EvdWebsocketClientClass { EvdIoStreamGroupClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_WEBSOCKET_CLIENT (evd_websocket_client_get_type ()) #define EVD_WEBSOCKET_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEBSOCKET_CLIENT, EvdWebsocketClient)) #define EVD_WEBSOCKET_CLIENT_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEBSOCKET_CLIENT, EvdWebsocketClientClass)) #define EVD_IS_WEBSOCKET_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEBSOCKET_CLIENT)) #define EVD_IS_WEBSOCKET_CLIENT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEBSOCKET_CLIENT)) #define EVD_WEBSOCKET_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEBSOCKET_CLIENT, EvdWebsocketClientClass)) GType evd_websocket_client_get_type (void) G_GNUC_CONST; EvdWebsocketClient * evd_websocket_client_new (void); void evd_websocket_client_set_standalone (EvdWebsocketClient *self, gboolean standalone); gboolean evd_websocket_client_get_standalone (EvdWebsocketClient *self); void evd_websocket_client_get_validate_peer_arguments (EvdWebsocketClient *self, EvdPeer *peer, EvdHttpConnection **conn, SoupMessageHeaders **response_headers); G_END_DECLS #endif /* __EVD_WEBSOCKET_CLIENT_H__ */ EventDance-0.2.0/evd/evd-websocket-protocol.c000066400000000000000000000726251321356073300210670ustar00rootroot00000000000000/* * evd-websocket-protocol.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include #include "evd-websocket-protocol.h" #include "evd-utils.h" #define EVD_WEBSOCKET_MAGIC_UUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define EVD_WEBSOCKET_DATA_KEY "org.eventdance.lib.Websocket.CONN_DATA" #define BLOCK_SIZE 0x00000FFF #define MAX_FRAGMENT_SIZE 0x10000000 #define MAX_PAYLOAD_SIZE 0x40000000 /* websocket reading states */ typedef enum { EVD_WEBSOCKET_READING_STATE_IDLE, EVD_WEBSOCKET_READING_STATE_PAYLOAD_LEN, EVD_WEBSOCKET_READING_STATE_HEADER, EVD_WEBSOCKET_READING_STATE_MASKING_KEY, EVD_WEBSOCKET_READING_STATE_PAYLOAD, /* padding for future expansion */ EVD_WEBSOCKET_READING_STATE_PADDING0, EVD_WEBSOCKET_READING_STATE_PADDING1, EVD_WEBSOCKET_READING_STATE_PADDING2, EVD_WEBSOCKET_READING_STATE_PADDING3, } EvdWebsocketReadingStates; static const guint16 HEADER_MASK_FIN = (1 << 15); static const guint16 HEADER_MASK_OPCODE = ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 11)); static const guint16 HEADER_MASK_MASKED = (1 << 7); static const guint16 HEADER_MASK_PAYLOAD_LEN = (0x00FF & ~(1 << 7)); typedef enum { OPCODE_CONTINUATION = 0x00, OPCODE_TEXT_FRAME = 0x01, OPCODE_BINARY_FRAME = 0x02, OPCODE_NON_CONTROL_RSV0 = 0x03, OPCODE_NON_CONTROL_RSV1 = 0x04, OPCODE_NON_CONTROL_RSV2 = 0x05, OPCODE_NON_CONTROL_RSV3 = 0x06, OPCODE_NON_CONTROL_RSV4 = 0x07, OPCODE_CLOSE = 0x08, OPCODE_PING = 0x09, OPCODE_PONG = 0x0A, OPCODE_CONTROL_RSV0 = 0x0B, OPCODE_CONTROL_RSV1 = 0x0C, OPCODE_CONTROL_RSV2 = 0x0D, OPCODE_CONTROL_RSV3 = 0x0E, OPCODE_CONTROL_RSV4 = 0x0F } EvdWebsocketOpcodes; typedef struct { gboolean server; EvdHttpConnection *conn; EvdWebsocketFrameCb frame_cb; EvdWebsocketCloseCb close_cb; gpointer user_data; GDestroyNotify user_data_destroy_notify; EvdWebsocketState state; EvdWebsocketReadingStates reading_state; GString *buf; gsize buf_len; gsize offset; guint8 opcode; gsize payload_len; gchar *frame_data; gsize frame_len; gboolean close_frame_sent; gboolean close_frame_received; guint16 close_code; gchar *close_reason; gboolean fin; gboolean masked; guint8 masking_key[4]; gchar *extensions_data; gsize extension_len; guint close_timeout_src_id; } EvdWebsocketData; static void read_from_connection (EvdWebsocketData *data); static void on_close_frame_received (EvdWebsocketData *data, guint16 code, const gchar *reason); static void apply_masking (gchar *frame, gsize frame_len, guint8 masking_key[4]) { gsize i; guint8 j; for (i=0; ilen + 2); frame->str[0] = (gchar) ((header & 0xFF00) >> 8); frame->str[1] = (gchar) (header & 0x00FF); if (payload_len_len > 0) g_string_append_len (frame, (const gchar *) &payload_len_hbo, payload_len_len); if (masked) { masking_key = g_random_int (); g_string_append_len (frame, (const gchar *) &masking_key, 4); } g_string_append_len (frame, payload, payload_len); if (masked) { apply_masking (frame->str + (frame->len - payload_len), payload_len, (guint8 *) &masking_key); } } static gboolean send_close_frame (EvdWebsocketData *data, guint16 code, const gchar *reason, GError **error) { gboolean result = TRUE; GString *frame; GOutputStream *stream; /* @TODO: send the code and reason. By now send no payload */ data->frame_data = NULL; data->frame_len = 0; frame = g_string_new (""); build_frame (frame, TRUE, OPCODE_CLOSE, (! data->server), data->frame_data, data->frame_len); stream = g_io_stream_get_output_stream (G_IO_STREAM (data->conn)); if (g_output_stream_write (stream, frame->str, frame->len, NULL, error) < 0) { result = FALSE; } g_string_free (frame, TRUE); return result; } static gboolean send_data_frame (EvdWebsocketData *data, const gchar *frame, gsize frame_len, EvdMessageType frame_type, GError **error) { gsize bytes_sent; gsize bytes_left; GString *frag; GOutputStream *stream; gboolean result = TRUE; frag = g_string_new (""); stream = g_io_stream_get_output_stream (G_IO_STREAM (data->conn)); bytes_sent = 0; bytes_left = frame_len; while (bytes_left > 0) { gsize frag_len; gboolean fin; guint8 opcode; gboolean masked; frag_len = MIN (MAX_FRAGMENT_SIZE, bytes_left); fin = frag_len >= bytes_left; opcode = bytes_sent == 0 ? (frame_type == EVD_MESSAGE_TYPE_TEXT ? OPCODE_TEXT_FRAME : OPCODE_BINARY_FRAME) : OPCODE_CONTINUATION; masked = ! data->server; build_frame (frag, fin, opcode, masked, frame + bytes_sent, frag_len); if (! g_output_stream_write (stream, frag->str, frag->len, NULL, error) < 0) { result = FALSE; break; } bytes_sent += frag_len; bytes_left -= frag_len; g_string_set_size (frag, 0); } g_string_free (frag, TRUE); return result; } static gboolean handle_control_frame (EvdWebsocketData *data) { switch (data->opcode) { case OPCODE_CLOSE: /* @TODO: load code and reason from payload */ on_close_frame_received (data, EVD_WEBSOCKET_CLOSE_NO_STATUS, NULL); break; default: /* @TODO: handle 'ping' and 'pong' control frames */ data->state = EVD_WEBSOCKET_STATE_CLOSED; g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL); g_warning ("Error, handling 'ping' and/or 'pong' control frames is not" " yet implemented in websocket version 08"); break; } return TRUE; } static gboolean read_payload (EvdWebsocketData *data) { if (data->buf_len - data->offset < data->payload_len) return FALSE; data->extensions_data = data->buf->str + data->offset; data->frame_len = data->payload_len - data->extension_len; data->frame_data = data->buf->str + data->offset + data->extension_len; if (data->masked) apply_masking (data->frame_data, data->frame_len, data->masking_key); if (data->opcode >= OPCODE_CLOSE) { /* control frame */ handle_control_frame (data); } else { /* data frame */ if (data->fin) { data->frame_cb (EVD_HTTP_CONNECTION (data->conn), data->frame_data, data->frame_len, data->opcode == OPCODE_BINARY_FRAME, data->user_data); } else { /* @TODO: handle fragmented frames */ g_warning ("Error, receiving fragmented frames is not yet implemented" " in websocket version 08"); data->state = EVD_WEBSOCKET_STATE_CLOSED; g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL); } } /* reset state */ data->offset += data->payload_len; data->reading_state = EVD_WEBSOCKET_READING_STATE_IDLE; g_string_erase (data->buf, 0, data->offset); data->buf_len -= data->offset; data->offset = 0; return TRUE; } static gboolean read_masking_key (EvdWebsocketData *data) { if (data->buf_len - data->offset < 4) return FALSE; memcpy (&data->masking_key, data->buf->str + data->offset, 4); data->offset += 4; data->reading_state = EVD_WEBSOCKET_READING_STATE_PAYLOAD; if (data->payload_len == 0) return read_payload (data); return TRUE; } static gboolean read_payload_len (EvdWebsocketData *data) { if (data->payload_len == 126) { guint16 len; if (data->buf_len - data->offset < 2) return FALSE; memcpy (&len, data->buf->str + data->offset, 2); data->offset += 2; data->payload_len = (gsize) GUINT16_FROM_BE(len); } else { guint64 len; if (data->buf_len - data->offset < 8) return FALSE; memcpy (&len, data->buf->str + data->offset, 8); data->offset += 8; data->payload_len = (gsize) GUINT64_FROM_BE(len); } /* @TODO: validated payload length against MAX_PAYLOAD_SIZE */ if (data->masked) data->reading_state = EVD_WEBSOCKET_READING_STATE_MASKING_KEY; else data->reading_state = EVD_WEBSOCKET_READING_STATE_PAYLOAD; return TRUE; } static gboolean read_header (EvdWebsocketData *data) { guint16 header = 0; if (data->buf_len - data->offset < 2) return FALSE; header = ( (guint8) data->buf->str[data->offset] << 8) + (guint8) data->buf->str[data->offset + 1]; data->offset += 2; /* fin flag */ data->fin = (guint16) (header & HEADER_MASK_FIN); /* opcode */ data->opcode = (guint8) ((header & HEADER_MASK_OPCODE) >> 8); /* masking */ data->masked = (guint16) (header & HEADER_MASK_MASKED); /* payload len */ data->payload_len = header & HEADER_MASK_PAYLOAD_LEN; /* @TODO: validate header values */ if (data->payload_len > 125) data->reading_state = EVD_WEBSOCKET_READING_STATE_PAYLOAD_LEN; else if (data->masked) data->reading_state = EVD_WEBSOCKET_READING_STATE_MASKING_KEY; else data->reading_state = EVD_WEBSOCKET_READING_STATE_PAYLOAD; return TRUE; } static gboolean process_data (EvdWebsocketData *data) { while (data->offset < data->buf_len && data->state != EVD_WEBSOCKET_STATE_CLOSED) { if (data->reading_state == EVD_WEBSOCKET_READING_STATE_IDLE) if (! read_header (data)) return TRUE; switch (data->reading_state) { case EVD_WEBSOCKET_READING_STATE_PAYLOAD_LEN: if (! read_payload_len (data)) return TRUE; break; case EVD_WEBSOCKET_READING_STATE_MASKING_KEY: if (! read_masking_key (data)) return TRUE; break; case EVD_WEBSOCKET_READING_STATE_PAYLOAD: if (! read_payload (data)) return TRUE; break; default: { /* @TODO */ } } } return data->state != EVD_WEBSOCKET_STATE_CLOSED; } static void on_connection_read (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdWebsocketData *data = user_data; EvdHttpConnection *conn; GError *error = NULL; gssize size; conn = data->conn; size = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error); if (size < 0) { if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { /* @TODO: log error properly */ g_print ("Error reading from WebSocket: %s", error->message); g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL); } g_error_free (error); } else if (size > 0) { data->buf_len += size; if (data->state != EVD_WEBSOCKET_STATE_CLOSED && process_data (data)) read_from_connection (data); } g_object_unref (conn); } static void read_from_connection (EvdWebsocketData *data) { GInputStream *stream; g_return_if_fail (data != NULL); if (data->buf_len + BLOCK_SIZE >= data->buf->len) g_string_set_size (data->buf, data->buf_len + BLOCK_SIZE); stream = g_io_stream_get_input_stream (G_IO_STREAM (data->conn)); g_object_ref (data->conn); g_input_stream_read_async (stream, data->buf->str + data->buf_len, BLOCK_SIZE, G_PRIORITY_DEFAULT, NULL, on_connection_read, data); } static gboolean close_timeout (gpointer user_data) { EvdWebsocketData *data = user_data; data->close_timeout_src_id = 0; data->state = EVD_WEBSOCKET_STATE_CLOSED; data->close_cb (EVD_HTTP_CONNECTION (data->conn), FALSE, data->user_data); g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL); g_object_unref (data->conn); return FALSE; } static guint8 get_version_from_request (EvdHttpRequest *request) { SoupMessageHeaders *headers; const gchar *version_str; guint8 version; headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); version_str = soup_message_headers_get_one (headers, "Sec-Websocket-Version"); if (version_str == NULL) return 0; version = g_ascii_strtoull (version_str, NULL, 10); return version; } static gchar * get_accept_key (const gchar *key) { gchar *concat; GChecksum *checksum; guint8 *digest; gsize digest_len; gchar *accept; concat = g_strconcat (key, EVD_WEBSOCKET_MAGIC_UUID, NULL); checksum = g_checksum_new (G_CHECKSUM_SHA1); g_checksum_update (checksum, (guchar *) concat, -1); digest_len = g_checksum_type_get_length (G_CHECKSUM_SHA1); digest = g_slice_alloc (digest_len); g_checksum_get_digest (checksum, digest, &digest_len); accept = g_base64_encode (digest, digest_len); g_slice_free1 (digest_len, digest); g_checksum_free (checksum); g_free (concat); return accept; } static void free_websocket_connection_data (EvdWebsocketData *data) { g_return_if_fail (data != NULL); if (data->user_data != NULL && data->user_data_destroy_notify != NULL) data->user_data_destroy_notify (data->user_data); if (data->buf != NULL) g_string_free (data->buf, TRUE); if (data->close_timeout_src_id != 0) { g_source_remove (data->close_timeout_src_id); data->close_timeout_src_id = 0; g_object_unref (data->conn); } g_free (data->close_reason); g_slice_free (EvdWebsocketData, data); } static void setup_connection (EvdHttpConnection *conn, gboolean is_server, EvdWebsocketState state) { EvdWebsocketData *data; data = g_slice_new0 (EvdWebsocketData); data->conn = conn; data->server = is_server; data->state = state; data->reading_state = EVD_WEBSOCKET_READING_STATE_IDLE; g_object_set_data_full (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY, data, (GDestroyNotify) free_websocket_connection_data); } static void finish_close_handshake (EvdWebsocketData *data, guint16 code, const gchar *reason) { data->state = EVD_WEBSOCKET_STATE_CLOSED; if (data->close_cb != NULL) { gboolean gracefully; gracefully = code == EVD_WEBSOCKET_CLOSE_NORMAL || code == EVD_WEBSOCKET_CLOSE_NO_STATUS; data->close_cb (EVD_HTTP_CONNECTION (data->conn), gracefully, data->user_data); } g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL); if (data->close_timeout_src_id != 0) { g_source_remove (data->close_timeout_src_id); data->close_timeout_src_id = 0; g_object_unref (data->conn); } } static void on_close_frame_received (EvdWebsocketData *data, guint16 code, const gchar *reason) { data->close_frame_received = TRUE; if (data->state == EVD_WEBSOCKET_STATE_CLOSING) { finish_close_handshake (data, code, reason); } else { evd_websocket_protocol_close (data->conn, code, NULL, NULL); } } static void on_connection_flushed (GObject *obj, GAsyncResult *res, gpointer user_data) { GError *error = NULL; EvdWebsocketData *data = user_data; if (! g_output_stream_flush_finish (G_OUTPUT_STREAM (obj), res, &error)) { /* @TODO: do proper logging */ g_print ("Error flushing WebSocket connection: %s\n", error->message); g_error_free (error); } finish_close_handshake (user_data, data->close_code, data->close_reason); g_object_unref (data->conn); g_object_unref (obj); } /* public methods */ gboolean evd_websocket_protocol_handle_handshake_request (EvdHttpConnection *conn, EvdHttpRequest *request, GError **error) { guint8 version; gboolean result = FALSE; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); g_return_val_if_fail (EVD_IS_HTTP_REQUEST (request), FALSE); version = get_version_from_request (request); if (version != 13) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "The WebSocket protocol version MUST be 13 [RFC 6455, 4.1.9]"); return FALSE; } SoupMessageHeaders *req_headers; SoupMessageHeaders *res_headers = NULL; const gchar *key; gchar *accept_key; const gchar *conn_header; req_headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); conn_header = soup_message_headers_get_one (req_headers, "Connection"); if (g_strcmp0 (soup_message_headers_get_one (req_headers, "Upgrade"), "websocket") != 0 || conn_header == NULL || g_strstr_len (conn_header, -1, "Upgrade") == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid Websocket handshake request"); goto finish; } key = soup_message_headers_get_one (req_headers, "Sec-WebSocket-Key"); if (key == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid Websocket handshake request, missing 'Sec-Websocket-Key' header"); goto finish; } accept_key = get_accept_key (key); /* build HTTP response */ res_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); soup_message_headers_replace (res_headers, "Connection", "Upgrade"); soup_message_headers_replace (res_headers, "Upgrade", "websocket"); soup_message_headers_replace (res_headers, "Sec-WebSocket-Accept", accept_key); g_free (accept_key); /* send handshake response headers */ if (! evd_http_connection_write_response_headers (conn, SOUP_HTTP_1_1, SOUP_STATUS_SWITCHING_PROTOCOLS, NULL, res_headers, error)) { goto finish; } /* success, setup the WebSocket connection */ setup_connection (conn, TRUE, EVD_WEBSOCKET_STATE_OPENED); result = TRUE; finish: if (res_headers != NULL) soup_message_headers_free (res_headers); return result; } /** * evd_websocket_protocol_create_handshake_request: * * Returns: (transfer full): **/ EvdHttpRequest * evd_websocket_protocol_create_handshake_request (const gchar *url, const gchar *sub_protocol, const gchar *origin, gchar **key_base64) { EvdHttpRequest *request; SoupMessageHeaders *headers; guchar key[16]; gchar *key_b64; gint i; request = evd_http_request_new (SOUP_METHOD_GET, url); headers = evd_http_message_get_headers (EVD_HTTP_MESSAGE (request)); soup_message_headers_replace (headers, "Upgrade", "websocket"); soup_message_headers_replace (headers, "Connection", "Upgrade"); soup_message_headers_replace (headers, "Sec-WebSocket-Version", "13"); if (sub_protocol != NULL) soup_message_headers_replace (headers, "Sec-WebSocket-Protocol", sub_protocol); if (origin != NULL) soup_message_headers_replace (headers, "Sec-WebSocket-Origin", origin); for (i=0; i<4; i++) { guint32 rnd; rnd = g_random_int (); key[i*4 + 0] = rnd >> 24 & 0xFF; key[i*4 + 1] = rnd >> 16 & 0xFF; key[i*4 + 2] = rnd >> 8 & 0xFF; key[i*4 + 3] = rnd & 0xFF; } key_b64 = g_base64_encode (key, 16); soup_message_headers_replace (headers, "Sec-WebSocket-Key", key_b64); if (key_base64 != NULL) *key_base64 = key_b64; else g_free (key_b64); return request; } gboolean evd_websocket_protocol_handle_handshake_response (EvdHttpConnection *conn, SoupHTTPVersion http_version, guint status_code, SoupMessageHeaders *headers, const gchar *handshake_key, GError **error) { const gchar *accept_key; gchar *expected_accept_key; gboolean result = TRUE; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); if (http_version != SOUP_HTTP_1_1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid HTTP version received, expected 1.1"); return FALSE; } if (status_code != SOUP_STATUS_SWITCHING_PROTOCOLS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid status code received: %u", status_code); return FALSE; } accept_key = soup_message_headers_get_one (headers, "Sec-WebSocket-Accept"); if (accept_key == NULL || strlen (accept_key) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Received invalid 'Sec-WebSocket-Accept' header"); return FALSE; } expected_accept_key = get_accept_key (handshake_key); if (g_strcmp0 (accept_key, expected_accept_key) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Received invalid accept key"); result = FALSE; } else { /* setup websocket data on connection */ setup_connection (conn, FALSE, EVD_WEBSOCKET_STATE_OPENED); } g_free (expected_accept_key); return result; } /** * evd_websocket_protocol_bind: * @frame_cb: (scope notified): * @close_cb: (scope notified): * **/ void evd_websocket_protocol_bind (EvdHttpConnection *conn, EvdWebsocketFrameCb frame_cb, EvdWebsocketCloseCb close_cb, gpointer user_data, GDestroyNotify user_data_destroy_notify) { EvdWebsocketData *data; g_return_if_fail (EVD_IS_HTTP_CONNECTION (conn)); g_return_if_fail (frame_cb != NULL); g_return_if_fail (close_cb != NULL); data = g_object_get_data (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY); g_return_if_fail (data != NULL); g_return_if_fail (data->conn == conn); data->frame_cb = frame_cb; data->close_cb = close_cb; data->user_data = user_data; data->user_data_destroy_notify = user_data_destroy_notify; data->buf = g_string_new_len ("", BLOCK_SIZE); /* start reading from websocket endpoint */ read_from_connection (data); } void evd_websocket_protocol_unbind (EvdHttpConnection *conn) { EvdWebsocketData *data; g_return_if_fail (EVD_IS_HTTP_CONNECTION (conn)); data = g_object_get_data (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY); if (data == NULL) return; data->frame_cb = NULL; data->close_cb = NULL; if (data->user_data != NULL && data->user_data_destroy_notify != NULL) data->user_data_destroy_notify (data->user_data); data->user_data = NULL; data->user_data_destroy_notify = NULL; /* stop reading from websocket endpoint */ /* @TODO: we need to cancel any ongoing read operation */ } gboolean evd_websocket_protocol_close (EvdHttpConnection *conn, guint16 code, const gchar *reason, GError **error) { EvdWebsocketData *data; gboolean result = TRUE; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); data = g_object_get_data (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY); if (data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Not a WebSocket connection"); return FALSE; } if (data->state == EVD_WEBSOCKET_STATE_CLOSING || data->state == EVD_WEBSOCKET_STATE_CLOSED) { return TRUE; } /* start the WebSocket Closing Handshake */ data->state = EVD_WEBSOCKET_STATE_CLOSING; g_assert (! data->close_frame_sent); result = send_close_frame (data, code, reason, error); data->close_frame_sent = TRUE; if (data->close_frame_received) { GOutputStream *stream; data->close_code = code; data->close_reason = g_strdup (reason); stream = g_io_stream_get_output_stream (G_IO_STREAM (data->conn)); g_object_ref (data->conn); g_object_ref (stream); g_output_stream_flush_async (stream, evd_connection_get_priority (EVD_CONNECTION (data->conn)), NULL, on_connection_flushed, data); } else { /* force closing the WebSocket Connection after a grace period */ g_object_ref (data->conn); data->close_timeout_src_id = evd_timeout_add (NULL, 3000, G_PRIORITY_DEFAULT, close_timeout, data); } return result; } gboolean evd_websocket_protocol_send (EvdHttpConnection *conn, const gchar *frame, gsize frame_len, EvdMessageType frame_type, GError **error) { EvdWebsocketData *data; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); g_return_val_if_fail (frame != NULL, FALSE); data = g_object_get_data (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY); if (data == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, "Given HTTP connection doesn't appear to be initialized for Websocket"); return FALSE; } if (data->state == EVD_WEBSOCKET_STATE_CLOSING || data->state == EVD_WEBSOCKET_STATE_CLOSED) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Websocket connection is closed"); return FALSE; } return send_data_frame (data, frame, frame_len, frame_type, error); } EvdWebsocketState evd_websocket_protocol_get_state (EvdHttpConnection *conn) { EvdWebsocketData *data; g_return_val_if_fail (EVD_IS_HTTP_CONNECTION (conn), FALSE); data = g_object_get_data (G_OBJECT (conn), EVD_WEBSOCKET_DATA_KEY); if (data == NULL) return EVD_WEBSOCKET_STATE_NONE; else return data->state; } EventDance-0.2.0/evd/evd-websocket-protocol.h000066400000000000000000000122601321356073300210610ustar00rootroot00000000000000/* * evd-websocket-protocol.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEBSOCKET_PROTOCOL_H__ #define __EVD_WEBSOCKET_PROTOCOL_H__ #include "evd-web-service.h" #include "evd-http-connection.h" #include "evd-http-request.h" #include "evd-peer.h" G_BEGIN_DECLS /* websocket connection states */ typedef enum { EVD_WEBSOCKET_STATE_NONE, EVD_WEBSOCKET_STATE_OPENING, EVD_WEBSOCKET_STATE_OPENED, EVD_WEBSOCKET_STATE_CLOSING, EVD_WEBSOCKET_STATE_CLOSED } EvdWebsocketState; typedef enum { EVD_WEBSOCKET_CLOSE_NORMAL = 1000, EVD_WEBSOCKET_CLOSE_GOING_AWAY = 1001, EVD_WEBSOCKET_CLOSE_PROTOCOL_ERROR = 1002, EVD_WEBSOCKET_CLOSE_UNSUPPORTED_DATA = 1003, EVD_WEBSOCKET_CLOSE_RESERVED = 1004, EVD_WEBSOCKET_CLOSE_NO_STATUS = 1005, EVD_WEBSOCKET_CLOSE_ABNORMAL = 1006, EVD_WEBSOCKET_CLOSE_INVALID_DATA = 1007, EVD_WEBSOCKET_CLOSE_POLICY_VIOLATION = 1008, EVD_WEBSOCKET_CLOSE_MESSAGE_TOO_BIG = 1009, EVD_WEBSOCKET_CLOSE_MANDATORY_EXT = 1010, EVD_WEBSOCKET_CLOSE_INTERNAL_ERROR = 1011, EVD_WEBSOCKET_CLOSE_TLS_HANDSHAKE = 1015 } EvdWebsocketClose; typedef void (* EvdWebsocketFrameCb) (EvdHttpConnection *conn, const gchar *frame, gsize frame_length, gboolean is_binary, gpointer user_data); typedef void (* EvdWebsocketCloseCb) (EvdHttpConnection *conn, gboolean gracefully, gpointer user_data); gboolean evd_websocket_protocol_handle_handshake_request (EvdHttpConnection *conn, EvdHttpRequest *request, GError **error); EvdHttpRequest * evd_websocket_protocol_create_handshake_request (const gchar *url, const gchar *sub_protocol, const gchar *origin, gchar **key_base64); gboolean evd_websocket_protocol_handle_handshake_response (EvdHttpConnection *conn, SoupHTTPVersion http_version, guint status_code, SoupMessageHeaders *headers, const gchar *handshake_key, GError **error); void evd_websocket_protocol_bind (EvdHttpConnection *conn, EvdWebsocketFrameCb frame_cb, EvdWebsocketCloseCb close_cb, gpointer user_data, GDestroyNotify user_data_destroy_notify); void evd_websocket_protocol_unbind (EvdHttpConnection *conn); gboolean evd_websocket_protocol_close (EvdHttpConnection *conn, guint16 code, const gchar *reason, GError **error); gboolean evd_websocket_protocol_send (EvdHttpConnection *conn, const gchar *frame, gsize frame_len, EvdMessageType frame_type, GError **error); EvdWebsocketState evd_websocket_protocol_get_state (EvdHttpConnection *conn); G_END_DECLS #endif /* __EVD_WEBSOCKET_PROTOCOL_H__ */ EventDance-0.2.0/evd/evd-websocket-server.c000066400000000000000000000426221321356073300205260ustar00rootroot00000000000000/* * evd-websocket-server.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #include #include "evd-websocket-server.h" #include "evd-transport.h" #include "evd-websocket-protocol.h" #define EVD_WEBSOCKET_SERVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ EVD_TYPE_WEBSOCKET_SERVER, \ EvdWebsocketServerPrivate)) #define CONN_DATA_KEY "org.eventdance.lib.WebsocketServer.CONN_DATA" #define PEER_DATA_KEY "org.eventdance.lib.WebsocketServer.PEER_DATA" #define HANDSHAKE_DATA_KEY "org.eventdance.lib.WebsocketServer.HANDSHAKE_DATA" #define DEFAULT_STANDALONE TRUE struct _EvdWebsocketServerPrivate { gboolean standalone; EvdHttpConnection *peer_arg_conn; EvdHttpRequest *peer_arg_request; }; typedef struct { EvdHttpConnection *conn; EvdHttpRequest *request; gboolean is_new_peer; } HandshakeData; static void evd_websocket_server_class_init (EvdWebsocketServerClass *class); static void evd_websocket_server_init (EvdWebsocketServer *self); static void evd_websocket_server_transport_iface_init (EvdTransportInterface *iface); static void evd_websocket_server_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request); static gboolean evd_websocket_server_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream); static gboolean evd_websocket_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error); static gboolean evd_websocket_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer); static void evd_websocket_server_peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully); static gboolean accept_peer (EvdTransport *transport, EvdPeer *peer); static gboolean reject_peer (EvdTransport *transport, EvdPeer *peer); static void transport_open (EvdTransport *transport, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable); G_DEFINE_TYPE_WITH_CODE (EvdWebsocketServer, evd_websocket_server, EVD_TYPE_WEB_SERVICE, G_IMPLEMENT_INTERFACE (EVD_TYPE_TRANSPORT, evd_websocket_server_transport_iface_init)); static void evd_websocket_server_class_init (EvdWebsocketServerClass *class) { GObjectClass *obj_class = G_OBJECT_CLASS (class); EvdIoStreamGroupClass *io_stream_group_class = EVD_IO_STREAM_GROUP_CLASS (class); EvdWebServiceClass *web_service_class = EVD_WEB_SERVICE_CLASS (class); io_stream_group_class->remove = evd_websocket_server_remove; web_service_class->request_handler = evd_websocket_server_request_handler; g_type_class_add_private (obj_class, sizeof (EvdWebsocketServerPrivate)); } static void evd_websocket_server_transport_iface_init (EvdTransportInterface *iface) { iface->send = evd_websocket_server_send; iface->peer_is_connected = evd_websocket_server_peer_is_connected; iface->peer_closed = evd_websocket_server_peer_closed; iface->accept_peer = accept_peer; iface->reject_peer = reject_peer; iface->open = transport_open; } static void evd_websocket_server_init (EvdWebsocketServer *self) { EvdWebsocketServerPrivate *priv; priv = EVD_WEBSOCKET_SERVER_GET_PRIVATE (self); self->priv = priv; priv->standalone = DEFAULT_STANDALONE; evd_service_set_io_stream_type (EVD_SERVICE (self), EVD_TYPE_HTTP_CONNECTION); } static void on_frame_received (EvdHttpConnection *conn, const gchar *frame, gsize frame_len, gboolean is_binary, gpointer user_data) { EvdTransportInterface *iface; EvdPeer *peer; EvdTransport *transport = EVD_TRANSPORT (user_data); iface = EVD_TRANSPORT_GET_INTERFACE (transport); peer = EVD_PEER (g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY)); if (peer == NULL || evd_peer_is_closed (peer)) return; iface->receive (transport, peer, frame, frame_len); } static void on_close_requested (EvdHttpConnection *conn, gboolean gracefully, gpointer user_data) { EvdPeer *peer; EvdWebsocketServer *self = EVD_WEBSOCKET_SERVER (user_data); peer = EVD_PEER (g_object_get_data (G_OBJECT (conn), CONN_DATA_KEY)); if (peer == NULL) return; if (gracefully) { evd_transport_close_peer (EVD_TRANSPORT (self), peer, TRUE, NULL); } else { g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); } } static void on_websocket_connection_ready (EvdWebsocketServer *self, EvdPeer *peer, EvdHttpConnection *conn, gboolean is_new_peer) { if (is_new_peer) { EvdTransportInterface *iface; EvdPeerManager *peer_manager; iface = EVD_TRANSPORT_GET_INTERFACE (self); peer_manager = iface->peer_manager; if (evd_peer_manager_lookup_peer (peer_manager, evd_peer_get_id (peer)) == NULL) { evd_peer_manager_add_peer (peer_manager, peer); /* notify new peer */ iface->notify_new_peer (EVD_TRANSPORT (self), peer); } } g_object_set_data (G_OBJECT (conn), CONN_DATA_KEY, peer); g_object_ref (conn); g_object_set_data_full (G_OBJECT (peer), PEER_DATA_KEY, conn, g_object_unref); g_object_ref (self); evd_websocket_protocol_bind (conn, on_frame_received, on_close_requested, self, g_object_unref); /* send frames from Peer's backlog */ while (evd_peer_backlog_get_length (peer) > 0) { gsize size; gchar *frame; EvdMessageType type; GError *error = NULL; frame = evd_peer_pop_message (peer, &size, &type); if (! evd_websocket_server_send (EVD_TRANSPORT (self), peer, frame, size, type, &error)) { g_print ("Error, failed to send frame from peer's backlog: %s\n", error->message); g_error_free (error); evd_peer_unshift_message (peer, frame, size, type, NULL); break; } g_free (frame); } } static gboolean accept_peer (EvdTransport *transport, EvdPeer *peer) { EvdWebsocketServer *self = EVD_WEBSOCKET_SERVER (transport); HandshakeData *data; data = g_object_get_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY); if (data == NULL) return FALSE; on_websocket_connection_ready (self, peer, data->conn, data->is_new_peer); g_object_set_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY, NULL); g_object_unref (peer); return FALSE; } static gboolean reject_peer (EvdTransport *transport, EvdPeer *peer) { EvdWebsocketServer *self = EVD_WEBSOCKET_SERVER (transport); HandshakeData *data; data = g_object_get_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY); if (data == NULL) return FALSE; evd_web_service_respond (EVD_WEB_SERVICE (self), data->conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); g_object_set_data (G_OBJECT (peer), HANDSHAKE_DATA_KEY, NULL); g_object_unref (peer); return FALSE; } static void free_handshake_data (gpointer _data) { HandshakeData *data = _data; g_object_unref (data->conn); g_object_unref (data->request); g_slice_free (HandshakeData, data); } static void evd_websocket_server_request_handler (EvdWebService *web_service, EvdHttpConnection *conn, EvdHttpRequest *request) { EvdWebsocketServer *self = EVD_WEBSOCKET_SERVER (web_service); EvdPeer *peer = NULL; SoupURI *uri; guint validate_result; EvdTransportInterface *iface; GError *error = NULL; gboolean is_new_peer = FALSE; uri = evd_http_request_get_uri (request); /* resolve peer */ peer = evd_transport_lookup_peer (EVD_TRANSPORT (self), uri->query); if (peer == NULL) { if (! self->priv->standalone) { evd_web_service_respond (web_service, conn, SOUP_STATUS_NOT_FOUND, NULL, NULL, 0, NULL); return; } else { peer = g_object_new (EVD_TYPE_PEER, "transport", self, NULL); is_new_peer = TRUE; } } else { evd_peer_touch (peer); g_object_ref (peer); } /* let WebSocket protocol handle request */ if (! evd_websocket_protocol_handle_handshake_request (conn, request, &error)) { g_print ("%s\n", error->message); g_error_free (error); evd_web_service_respond (web_service, conn, SOUP_STATUS_BAD_REQUEST, NULL, NULL, 0, NULL); goto out; } /* validate peer */ iface = EVD_TRANSPORT_GET_INTERFACE (self); self->priv->peer_arg_conn = conn; self->priv->peer_arg_request = request; validate_result = iface->notify_validate_peer (EVD_TRANSPORT (self), peer); self->priv->peer_arg_conn = NULL; self->priv->peer_arg_request = NULL; if (validate_result == EVD_VALIDATE_ACCEPT) { /* peer accepted */ if (! g_io_stream_is_closed (G_IO_STREAM (conn))) { on_websocket_connection_ready (self, peer, conn, is_new_peer); } } else if (validate_result == EVD_VALIDATE_REJECT) { /* peer rejected */ evd_web_service_respond (web_service, conn, SOUP_STATUS_FORBIDDEN, NULL, NULL, 0, NULL); } else { /* validation pending */ HandshakeData *data; data = g_slice_new (HandshakeData); data->is_new_peer = is_new_peer; data->conn = g_object_ref (conn); data->request = g_object_ref (request); g_object_ref (peer); g_object_set_data_full (G_OBJECT (peer), HANDSHAKE_DATA_KEY, data, free_handshake_data); } out: g_object_unref (peer); } static gboolean evd_websocket_server_peer_is_connected (EvdTransport *transport, EvdPeer *peer) { EvdConnection *conn; conn = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); return (conn != NULL && ! g_io_stream_is_closed (G_IO_STREAM (conn))); } static gboolean evd_websocket_server_send (EvdTransport *transport, EvdPeer *peer, const gchar *buffer, gsize size, EvdMessageType type, GError **error) { EvdHttpConnection *conn; conn = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (conn == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Send failed. Peer is not associated with WebSocket server transport, or is corrupted"); return FALSE; } else { return evd_websocket_protocol_send (conn, buffer, size, type, error); } } static gboolean evd_websocket_server_remove (EvdIoStreamGroup *io_stream_group, GIOStream *io_stream) { EvdPeer *peer; if (! EVD_IO_STREAM_GROUP_CLASS (evd_websocket_server_parent_class)-> remove (io_stream_group, io_stream)) { return FALSE; } evd_websocket_protocol_unbind (EVD_HTTP_CONNECTION (io_stream)); peer = g_object_get_data (G_OBJECT (io_stream), CONN_DATA_KEY); if (peer != NULL) g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); return TRUE; } static void evd_websocket_server_peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully) { EvdHttpConnection *conn; conn = g_object_get_data (G_OBJECT (peer), PEER_DATA_KEY); if (conn == NULL) return; if (! g_io_stream_is_closed (G_IO_STREAM (conn))) { GError *error = NULL; guint16 code; /* @TODO: Choose a proper closing code */ code = gracefully ? EVD_WEBSOCKET_CLOSE_NORMAL : EVD_WEBSOCKET_CLOSE_ABNORMAL; if (! evd_websocket_protocol_close (conn, code, NULL, &error)) { /* @TODO: do proper error logging */ g_print ("Error closing websocket connection: %s\n", error->message); g_error_free (error); } } g_object_set_data (G_OBJECT (conn), CONN_DATA_KEY, NULL); g_object_set_data (G_OBJECT (peer), PEER_DATA_KEY, NULL); } static void transport_on_open (GObject *obj, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GSimpleAsyncResult *orig_res = G_SIMPLE_ASYNC_RESULT (user_data); if (! evd_service_listen_finish (EVD_SERVICE (obj), res, &error)) { g_simple_async_result_set_from_error (orig_res, error); g_error_free (error); } g_simple_async_result_complete (orig_res); g_object_unref (orig_res); } static void transport_open (EvdTransport *transport, const gchar *address, GSimpleAsyncResult *async_result, GCancellable *cancellable) { evd_service_listen (EVD_SERVICE (transport), address, cancellable, transport_on_open, async_result); } /* public methods */ EvdWebsocketServer * evd_websocket_server_new (void) { return g_object_new (EVD_TYPE_WEBSOCKET_SERVER, NULL); } void evd_websocket_server_set_standalone (EvdWebsocketServer *self, gboolean standalone) { g_return_if_fail (EVD_IS_WEBSOCKET_SERVER (self)); self->priv->standalone = standalone; } gboolean evd_websocket_server_get_standalone (EvdWebsocketServer *self) { g_return_val_if_fail (EVD_IS_WEBSOCKET_SERVER (self), FALSE); return self->priv->standalone; } /** * evd_websocket_server_get_validate_peer_arguments: * @conn: (out) (allow-none) (transfer none): * @request: (out) (allow-none) (transfer none): * **/ void evd_websocket_server_get_validate_peer_arguments (EvdWebsocketServer *self, EvdPeer *peer, EvdHttpConnection **conn, EvdHttpRequest **request) { g_return_if_fail (EVD_IS_WEBSOCKET_SERVER (self)); if (conn != NULL) *conn = self->priv->peer_arg_conn; if (request != NULL) *request = self->priv->peer_arg_request; } EventDance-0.2.0/evd/evd-websocket-server.h000066400000000000000000000064101321356073300205260ustar00rootroot00000000000000/* * evd-websocket-server.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_WEBSOCKET_SERVER_H__ #define __EVD_WEBSOCKET_SERVER_H__ #if !defined (__EVD_H_INSIDE__) && !defined (EVD_COMPILATION) #error "Only can be included directly." #endif #include "evd-web-service.h" #include "evd-http-connection.h" #include "evd-http-request.h" #include "evd-peer.h" G_BEGIN_DECLS typedef struct _EvdWebsocketServer EvdWebsocketServer; typedef struct _EvdWebsocketServerClass EvdWebsocketServerClass; typedef struct _EvdWebsocketServerPrivate EvdWebsocketServerPrivate; struct _EvdWebsocketServer { EvdWebService parent; EvdWebsocketServerPrivate *priv; }; struct _EvdWebsocketServerClass { EvdWebServiceClass parent_class; /* padding for future expansion */ void (* _padding_0_) (void); void (* _padding_1_) (void); void (* _padding_2_) (void); void (* _padding_3_) (void); void (* _padding_4_) (void); void (* _padding_5_) (void); void (* _padding_6_) (void); void (* _padding_7_) (void); }; #define EVD_TYPE_WEBSOCKET_SERVER (evd_websocket_server_get_type ()) #define EVD_WEBSOCKET_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EVD_TYPE_WEBSOCKET_SERVER, EvdWebsocketServer)) #define EVD_WEBSOCKET_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), EVD_TYPE_WEBSOCKET_SERVER, EvdWebsocketServerClass)) #define EVD_IS_WEBSOCKET_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EVD_TYPE_WEBSOCKET_SERVER)) #define EVD_IS_WEBSOCKET_SERVER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), EVD_TYPE_WEBSOCKET_SERVER)) #define EVD_WEBSOCKET_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EVD_TYPE_WEBSOCKET_SERVER, EvdWebsocketServerClass)) GType evd_websocket_server_get_type (void) G_GNUC_CONST; EvdWebsocketServer * evd_websocket_server_new (void); void evd_websocket_server_set_standalone (EvdWebsocketServer *self, gboolean standalone); gboolean evd_websocket_server_get_standalone (EvdWebsocketServer *self); void evd_websocket_server_get_validate_peer_arguments (EvdWebsocketServer *self, EvdPeer *peer, EvdHttpConnection **conn, EvdHttpRequest **request); G_END_DECLS #endif /* __EVD_WEBSOCKET_SERVER_H__ */ EventDance-0.2.0/evd/evd.h000066400000000000000000000036401321356073300152400ustar00rootroot00000000000000/* * evd.h * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2015, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3, or (at your option) any later version as published by * the Free Software Foundation. * * 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 at http://www.gnu.org/licenses/lgpl-3.0.txt * for more details. */ #ifndef __EVD_H__ #define __EVD_H__ #define __EVD_H_INSIDE__ #include "evd-utils.h" #include "evd-socket.h" #include "evd-stream-throttle.h" #include "evd-buffered-input-stream.h" #include "evd-buffered-output-stream.h" #include "evd-throttled-input-stream.h" #include "evd-throttled-output-stream.h" #include "evd-service.h" #include "evd-tls-session.h" #include "evd-tls-credentials.h" #include "evd-tls-certificate.h" #include "evd-tls-privkey.h" #include "evd-connection.h" #include "evd-io-stream-group.h" #include "evd-http-connection.h" #include "evd-peer.h" #include "evd-peer-manager.h" #include "evd-http-request.h" #include "evd-longpolling-server.h" #include "evd-websocket-server.h" #include "evd-websocket-client.h" #include "evd-connection-pool.h" #include "evd-reproxy.h" #include "evd-web-selector.h" #include "evd-web-transport-server.h" #include "evd-web-dir.h" #include "evd-ipc-mechanism.h" #include "evd-dbus-bridge.h" #include "evd-dbus-daemon.h" #include "evd-jsonrpc.h" #include "evd-pki-privkey.h" #include "evd-pki-pubkey.h" #include "evd-daemon.h" #include "evd-jsonrpc-http-client.h" #include "evd-jsonrpc-http-server.h" #include "evd-promise.h" #undef __EVD_H_INSIDE__ #endif /* __EVD_H__ */ EventDance-0.2.0/evd/js/000077500000000000000000000000001321356073300147225ustar00rootroot00000000000000EventDance-0.2.0/evd/js/evdDBusBridge.js000066400000000000000000000460031321356073300177340ustar00rootroot00000000000000Evd.DBus = { NAMESPACE: "org.eventdance.lib.dbus", Commands: { NONE: 0, ERROR: 1, REPLY: 2, NEW_CONNECTION: 3, CLOSE_CONNECTION: 4, OWN_NAME: 5, UNOWN_NAME: 6, NAME_ACQUIRED: 7, NAME_LOST: 8, REGISTER_OBJECT: 9, UNREGISTER_OBJECT: 10, NEW_PROXY: 11, CLOSE_PROXY: 12, CALL_METHOD: 13, CALL_METHOD_RETURN: 14, EMIT_SIGNAL: 15 }, ProxyFlags: { NONE: 0, DO_NOT_LOAD_PROPERTIES: 1 << 0, DO_NOT_CONNECT_SIGNALS: 1 << 1, DO_NOT_AUTO_START: 1 << 2 }, MethodCallFlags: { NONE: 0, NO_AUTO_START: 1 << 0 }, OwnNameFlags: { NONE: 0, ALLOW_REPLACEMENT: 1 << 0, REPLACE: 1 << 1 } }; // Evd.DBus.Connection Evd.DBus.Connection = new Evd.Constructor (); Evd.DBus.Connection.prototype = new Evd.Object (Evd.DBus.Connection); Evd.DBus.Connection._serial = 0; Evd.Object.extend (Evd.DBus.Connection.prototype, { _init: function (args) { // @TODO: validate arguments */ var self = this; this._id = 0; this._expected = {}; this._proxies = {}; this._nameOwners = {}; this._regObjs = {}; var reuse = args.reuse; if (reuse === undefined) reuse = true; this._callback = args.callback; this._peer = args.peer; this._peerOnReceive = function (peer) { self._processMsg (peer.receiveText ()); }; this._peer.transport.addEventListener ("receive", this._peerOnReceive); this._peerOnClose = function (peer) { if (peer == self._peer) self.close (); }; this._peer.transport.addEventListener ("peer-closed", this._peerOnClose); var addr = args.address; this.sendMessage (Evd.DBus.Commands.NEW_CONNECTION, 0, [addr,reuse], this._onNewConnection, this); }, _processMsg: function (msgStr) { try { var msg = JSON.parse (msgStr); if (typeof (msg) != "object" || msg.constructor != Array) { throw ("Message must be a JSON array"); } } catch (e) { throw ("Message parsing error: " + e); return; }; var cmd = msg[0]; var serial = msg[1]; var connId = msg[2]; var subject = msg[3]; var args; if (connId != this._id) return; if (cmd == Evd.DBus.Commands.CALL_METHOD) { args = JSON.parse (msg[4]); this._onMethodCalled (serial, subject, args); } else if (this._expected[serial]) { var closure = this._expected[serial]; delete (this._expected[serial]); if (closure.cb) { args = JSON.parse (msg[4]); closure.cb.apply (closure.scope, [cmd, subject, args]); } } else { switch (cmd) { case Evd.DBus.Commands.EMIT_SIGNAL: args = JSON.parse (msg[4]); this._signalEmitted (subject, args); break; case Evd.DBus.Commands.NAME_ACQUIRED: case Evd.DBus.Commands.NAME_LOST: args = JSON.parse (msg[4]); var owningId = subject; var ownerData = this._nameOwners[owningId]; if (! ownerData) { throw ("Error: No owner for name id '"+owningId+"'"); } if (cmd == Evd.DBus.Commands.NAME_ACQUIRED && ownerData.nameAcquiredCb) ownerData.nameAcquiredCb (this, ownerData.name, owningId); else if (cmd == Evd.DBus.Commands.NAME_LOST && ownerData.nameLostCb) ownerData.nameLostCb (this, ownerData.name, owningId); break; default: throw ("Error: unexpected message: " + msgStr); break; } } }, _sendMessage: function (cmd, serial, subject, args) { var argsStr = JSON.stringify (args); var msg = JSON.stringify ([cmd, serial, this._id, subject, argsStr]); this._peer.sendText (msg); }, sendMessage: function (cmd, subject, args, callback, scope) { // @TODO: validate message arguments */ var serial = Evd.DBus.Connection._serial++; var closure = { cmd: cmd, cb: callback, scope: scope }; this._expected[serial] = closure; this._sendMessage (cmd, serial, subject, args); return closure; }, _buildErrorFromArgs: function (prefix, args) { return new Error (prefix + " (" + args[0] + "): " + args[1]); }, _onNewConnection: function (cmd, subject, args) { if (! this._callback) return; if (cmd == Evd.DBus.Commands.REPLY) { this._id = args[0]; this._callback (this, null); } else if (cmd == Evd.DBus.Commands.ERROR) { this._callback (null, this._buildErrorFromArgs ("Connection failed", args)); } else { throw ("Unexpected reply for NEW_CONNECTION command"); } delete (this._callback); }, newProxy: function (name, objectPath, interfaceName, flags, obj, vtable) { var args = [name, objectPath, interfaceName, flags]; this.sendMessage (Evd.DBus.Commands.NEW_PROXY, this._id, args, function (cmd, subject, argsStr) { this._onNewProxy (cmd, subject, argsStr, obj, vtable); }, this); }, _onNewProxy: function (cmd, subject, args, proxyObj, vtable) { if (cmd == Evd.DBus.Commands.REPLY) { var proxyId = args[0]; this._proxies[proxyId] = { proxy: proxyObj, vtable: vtable }; vtable.onNewProxy.apply (proxyObj, [proxyId, null]); } else if (cmd == Evd.DBus.Commands.ERROR) { callback (null, this._buildErrorFromArgs ("Proxy failed", args)); } else { throw ("Unexpected reply for NEW_PROXY command"); } }, _signalEmitted: function (subject, args) { var proxyData = this._proxies[subject]; if (! proxyData) throw ("Signal emitted for unknown proxy"); var signalName = args[0]; var signalArgs = JSON.parse (args[1]); if (proxyData.vtable.onSignalEmitted) proxyData.vtable.onSignalEmitted.apply (proxyData.proxy, [signalName, signalArgs]); }, _onMethodCalled: function (serial, subject, args) { var self = this; var regObjId = subject; var obj = this._regObjs[regObjId]; if (! obj) throw ("Error: Method called on unknown registered object '"+owningId+"'"); var methodName = args[0]; if (! obj[methodName]) { throw ("Method '"+methodName+"' not implemented in registered object"); } var methodArgs = JSON.parse (args[1]); var returnArgs; var invObj = { _regObjId: regObjId, _serial: serial, methodName: methodName, returnValue: function (outArgs) { self._methodCalledReturn (this, outArgs, null); }, returnError: function (err) { self._methodCalledReturn (this, null, err); } }; var result; try { result = obj[methodName].apply (obj, [methodArgs, invObj]); } catch (e) { throw ("Method call error: " + e); return; } }, _methodCalledReturn: function (invObj, outArgs, err) { if (! err) { var returnArgs = JSON.stringify (outArgs); var msgArgs = [returnArgs]; this._sendMessage (Evd.DBus.Commands.CALL_METHOD_RETURN, invObj._serial, invObj._regObjId, msgArgs); } else { if (! err.code) err.code = 0; var msgArgs = [err.code, err.toString ()]; this._sendMessage (Evd.DBus.Commands.ERROR, invObj._serial, invObj._regObjId, msgArgs); } }, callProxyMethod: function (proxyId, methodName, args, signature, callback, flags, timeout) { var msgArgs = [methodName, JSON.stringify (args), signature, flags, timeout]; this.sendMessage (Evd.DBus.Commands.CALL_METHOD, proxyId, msgArgs, function (cmd, subject, msgArgs) { this._onProxyMethodCall (cmd, subject, msgArgs, callback); }, this); }, _onProxyMethodCall: function (cmd, subject, msgArgs, callback) { var proxyData = this._proxies[subject]; if (! proxyData) throw ("Method call reponse for unknown proxy"); if (! callback) return; var proxyObj = proxyData.proxy; if (cmd == Evd.DBus.Commands.CALL_METHOD_RETURN) { var args = JSON.parse (msgArgs[0]); callback.apply (proxyObj, [args, null]); } else if (cmd == Evd.DBus.Commands.ERROR) { callback.apply (proxyObj, [null, this._buildErrorFromArgs ("Method call failed", msgArgs)]); } else { throw ("Unexpected reply for NEW_PROXY command"); } }, ownName: function (name, flags, callback, nameAcquiredCb, nameLostCb) { var args = [name, flags]; var ownerData = { name: name, nameAcquiredCb: nameAcquiredCb, nameLostCb: nameLostCb }; this.sendMessage (Evd.DBus.Commands.OWN_NAME, this._id, args, function (cmd, subject, msgArgs) { this._onOwnNameResponse (cmd, subject, msgArgs, callback, ownerData); }, this); }, _onOwnNameResponse: function (cmd, subject, msgArgs, callback, ownerData) { if (cmd == Evd.DBus.Commands.REPLY) { var owningId = msgArgs[0]; this._nameOwners[owningId] = ownerData; if (callback) callback (owningId, null); } else if (cmd == Evd.DBus.Commands.ERROR) { if (callback) callback (0, this._buildErrorFromArgs ("Own-name failed", msgArgs)); } else { throw ("Unexpected reply for OWN_NAME command"); } }, unownName: function (owningId, callback) { var args = []; this.sendMessage (Evd.DBus.Commands.UNOWN_NAME, owningId, args, function (cmd, subject, msgArgs) { this._onUnownNameResponse (cmd, subject, msgArgs, owningId, callback); }, this); }, _onUnownNameResponse: function (cmd, subject, msgArgs, owningId, callback) { if (cmd == Evd.DBus.Commands.REPLY) { delete (this._nameOwners[owningId]); if (callback) callback (true, null); } else if (cmd == Evd.DBus.Commands.ERROR) { if (callback) callback (false, this._buildErrorFromArgs ("Unown-name failed", msgArgs)); } else { throw ("Unexpected reply for UNOWN_NAME command"); } }, registerObject: function (object, path, iface, callback) { var args = [path, iface]; this.sendMessage (Evd.DBus.Commands.REGISTER_OBJECT, this._id, args, function (cmd, subject, msgArgs) { this._onRegisterObject (cmd, subject, msgArgs, object, callback); }, this); }, _onRegisterObject: function (cmd, subject, msgArgs, obj, callback) { if (cmd == Evd.DBus.Commands.REPLY) { var regObjId = msgArgs[0]; this._regObjs[regObjId] = obj; // inject prototype into object's prototype chain var regObj = new Evd.DBus.RegisteredObject ({ id: regObjId, connection: this }); var tmpProto = obj.__proto__; obj.__proto__ = regObj.__proto__; obj.__proto__.__proto__ = tmpProto; if (callback) callback (regObjId, null); } else if (cmd == Evd.DBus.Commands.ERROR) { if (callback) callback (0, this._buildErrorFromArgs ("Object registering failed", msgArgs)); } else { throw ("Unexpected reply for REGISTER_OBJECT command"); } }, unregisterObject: function (object, callback) { var regObjId = object._regObjId; var args = [regObjId]; this.sendMessage (Evd.DBus.Commands.UNREGISTER_OBJECT, regObjId, args, function (cmd, subject, msgArgs) { this._onUnregisterObject (cmd, subject, msgArgs, object, callback); }, this); }, _onUnregisterObject: function (cmd, subject, msgArgs, obj, callback) { if (cmd == Evd.DBus.Commands.REPLY) { var regObjId = obj._regObjId; delete (this._regObjs[regObjId]); // remove the previously injected prototype obj.__proto__ = obj.__proto__.__proto__; if (callback) callback (true, null); } else if (cmd == Evd.DBus.Commands.ERROR) { if (callback) callback (0, this._buildErrorFromArgs ("Object unregistering failed", msgArgs)); } else { throw ("Unexpected reply for UNREGISTER_OBJECT command"); } }, emitSignal: function (object, signalName, args, signature) { var argsStr = JSON.stringify (args); var subject = object._regObjId; var msgArgs = [signalName, argsStr, signature]; this._sendMessage (Evd.DBus.Commands.EMIT_SIGNAL, 0, subject, msgArgs); }, close: function () { if (! this._peer) return; this._peer.transport.removeEventListener ("close", this._peerOnClose); this._peer.transport.removeEventListener ("receive", this._peerOnReceive); this._peer = null; this._fireEvent ("close", []); } }); // Evd.DBus.RegisteredObject Evd.DBus.RegisteredObject = new Evd.Constructor (); Evd.Object.extend (Evd.DBus.RegisteredObject.prototype, { _init: function (args) { this.__proto__._regObjId = args.id; this.__proto__._dbusConn = args.connection; }, emitDBusSignal: function (signalName, args, signature) { var conn = this._dbusConn; conn.emitSignal (this, signalName, args, signature); }, unregister: function (callback) { var conn = this._dbusConn; conn.unregisterObject (this, callback); } }); // Evd.DBus.Proxy Evd.DBus.Proxy = new Evd.Constructor (); Evd.DBus.Proxy.prototype = new Evd.Object (Evd.DBus.Proxy); Evd.Object.extend (Evd.DBus.Proxy.prototype, { _init: function (args) { // @TODO: validate arguments */ this.name = args.name; this.objectPath = args.objectPath; this.interfaceName = args.interfaceName; this.flags = args.flags; this._id = 0; this.connection = args.connection; this._callback = args.callback; this._defaultTimeout = 60; this._vtable = { onNewProxy: this._onNewProxy, onSignalEmitted: this._onSignalEmitted }; this.connection.newProxy (this.name, this.objectPath, this.interfaceName, this.flags, this, this._vtable); }, _onNewProxy: function (proxyId, err) { if (! this._callback) return; if (err == null) { this._id = proxyId; this._callback (this, null); } else { this._callback (null, err); } delete (this._callback); }, _onSignalEmitted: function (name, args) { this._fireEvent ("signal-emitted", [name, args]); this._fireEvent (name, [args]); }, call: function (methodName, args, signature, callback, flags, timeout) { if (flags == undefined) flags = Evd.DBus.CallMethodFlags.NONE; if (timeout == undefined) timeout = this._defaultTimeout; this.connection.callProxyMethod (this._id, methodName, args, signature, callback, flags, timeout); } }); EventDance-0.2.0/evd/js/evdJsonrpc.js000066400000000000000000000142601321356073300174000ustar00rootroot00000000000000(function () { function defineJsonRpc (Evd) { // Evd.Jsonrpc var Jsonrpc = new Evd.Constructor (); Jsonrpc.prototype = new Evd.Object (); Evd.Object.extend (Jsonrpc.prototype, { _init: function (args) { this._invocationCounter = 0; this._transportWriteCb = args.transportWriteCb; this._methodCallCb = args.methodCallCb; this._invocationsIn = {}; this._invocationsOut = {}; this._registeredMethods = {}; this._transports = []; var self = this; this._transportOnReceive = function (peer) { var data = peer.receiveText (); self.transportRead (data, peer); }; }, transportRead: function (data, context) { try { var msg = JSON.parse (data); } catch (e) { throw ("Malformed JSON-RPC msg"); } if (typeof (msg) != "object" || msg.constructor != Object || msg["id"] === undefined) { throw ("Received invalid JSON-RPC msg"); } if (msg["result"] !== undefined && msg["error"] !== undefined) { /* a JSON-RPC response */ if (this._invocationsOut[msg.id] === undefined) { /* unexpected response, discard silently? */ return; } else { var invObj = this._invocationsOut[msg.id]; delete (this._invocationsOut[msg.id]); if (invObj.callback) invObj.callback (msg.result, msg.error); } } else if (msg["method"] !== undefined && msg["params"] !== undefined) { /* a JSON-RPC request */ if (msg.id === null) { /* a JSON-RPC notification */ this._fireEvent (msg.method, [msg.params, context]); } else { /* a JSON-RPC method call */ var self = this; var invObj = this._newInvocationObj (msg.id, msg.method, msg.params, null); var key = invObj.id.toString (); this._invocationsIn[key] = invObj; if (this._registeredMethods[invObj.method]) { this._registeredMethods[invObj.method] (this, invObj.params, key, context); } else if (this._methodCallCb) this._methodCallCb (invObj.method, invObj.params, key, context); else { // method not handled, respond call with error this.respondError (key, "Method '"+invObj.method+"' not handled", context); } } } else { throw ("Invalid JSON-RPC message"); } }, _newInvocationObj: function (id, methodName, params, callback) { var invObj = { id: id, method: methodName, params: params, callback: callback }; return invObj; }, _transportWrite: function (msg, context) { /* @TODO: when using require.js AMD, context.prototype is not referentially equal to Evd.Peer. A bug in require.js? */ if (context && typeof (context) == "object"/* && context.prototype == Evd.Peer.prototype*/) { context.sendText (msg); } else if (this._transportWriteCb) { this._transportWriteCb (this, msg); } else { throw ("No transport to send message over"); } }, callMethod: function (methodName, params, callback, context) { // @TODO: validate params to be an array var msg = { method: methodName, params: params }; this._invocationCounter++; msg.id = this._invocationCounter + ""; var invObj = this._newInvocationObj (msg.id, methodName, params, callback); this._invocationsOut[msg.id] = invObj; var msgSt = JSON.stringify (msg); this._transportWrite (msgSt, context); return invObj; }, _respond: function (invocationId, result, error, context) { if (this._invocationsIn[invocationId] === undefined) throw ("No active method invocation with such id"); var invObj = this._invocationsIn[invocationId]; var msg = { id: invObj.id, result: result, error: null }; var msgSt = JSON.stringify (msg); this._transportWrite (msgSt, context); }, respond: function (invocationId, result, context) { return this._respond (invocationId, result, null, context); }, respondError: function (invocationId, error, context) { return this._respond (invocationId, null, error, context); }, registerMethod: function (methodName, callback) { this._registeredMethods[methodName] = callback; }, unregisterMethod: function (methodName) { delete (this._registeredMethods[methodName]); }, useTransport: function (transport) { transport.addEventListener ("receive", this._transportOnReceive); }, unuseTransport: function (transport) { transport.removeEventListener ("receive", this._transportOnReceive); }, sendNotification: function (notificationName, params, context) { // @TODO: validate params to be an array var msg = { id: null, method: methodName, params: params }; var msgSt = JSON.stringify (msg); this._transportWrite (msgSt, context); } }); return Object.freeze (Jsonrpc); } // defineJsonrpc() if (window["define"] && define.constructor == Function && define.amd) define (["./evdWebTransport.js"], defineJsonRpc); else if (! window["Evd"]) throw ("Evd namespace not found, you need evdWebTransport.js"); else window.Evd.Jsonrpc = defineJsonrpc (Evd); }) (); EventDance-0.2.0/evd/js/evdWebTransport.js000066400000000000000000000510611321356073300204140ustar00rootroot00000000000000(function () { function defineEvd () { var Evd = {}; if (! Evd["Object"] || typeof (Evd["Object"]) != "object") { // Evd.Constructor Evd.Constructor = function (args) { return function (args) { if (! args || typeof (args) != "object" || args.constructor != Object) args = {}; this._init (args); }; }; // Evd.Object Evd.Object = function (constructor) { var eventListeners = {}; this.constructor = constructor; this.addEventListener = function (eventName, handler) { if (! eventListeners[eventName]) eventListeners[eventName] = []; eventListeners[eventName].push (handler); }; this.removeEventListener = function (eventName, handler) { if (! eventListeners[eventName]) return; for (var i=0; i= 0) self._activeXhrs.splice (self._activeXhrs.indexOf (this)); self._recycleXhr (this); if (this.status != 200) { var error = new Error ("Long polling error " + this.status); error.code = this.status; if (this._sender) self._fireEvent ("send", [false, error]); else self._fireEvent ("receive", [null, error]); } else { var data = xhr.responseText.toString (); if (this._sender) self._fireEvent ("send", [true, null]); else setTimeout (function () { self._connect (); }, 1); if (data) setTimeout (function () { self._xhrOnLoad (data); }, 1); } }; return xhr; }, _recycleXhr: function (xhr) { if (! xhr._sender) this._receivers.push (xhr); else this._senders.push (xhr); }, _connectXhr: function (xhr) { xhr.open ("GET", this._addr + "/receive?" + this._peerId, true); this._activeXhrs.push (xhr); xhr.send (); }, _connect: function () { for (var i=0; i 0; }, open: function (address, callback) { this._addr = address; this._opened = true; this._connect (); }, close: function (gracefully) { this._opened = false; this._connected = false; var xhr; // cancel all active XHRs for (xhr in this._activeXhrs) xhr.abort (); this._activeXhrs = []; if (gracefully) { // send a 'close' command xhr = new XMLHttpRequest (); xhr.open ("POST", this._addr + "/close?" + this._peerId, false); xhr.send (); } this._peerId = null; }, reconnect: function () { this._connect (); } }); // Evd.WebSocket Evd.WebSocket = new Evd.Constructor (); Evd.WebSocket.prototype = new Evd.Object (Evd.WebSocket); Evd.Object.extend (Evd.WebSocket.prototype, { _init: function (args) { this._peerId = args.peerId; }, open: function (address, callback) { this._addr = address; this._opened = true; this._error = false; this._connect (); }, _connect: function () { var self = this; if (this._ws != null) { this._ws.onopen = null; this._ws.onmessage = null; this._ws.onerror = null; this._onclose = null; } this._ws = new WebSocket (this._addr + "?" + this._peerId); this._ws.onopen = function () { self._connected = true; self._fireEvent ("connect", [true, null]); self._fireEvent ("send", [true, null]); }; this._ws.onmessage = function (e) { if (typeof (e.data) == "object") { var reader = new FileReader (); reader.readAsArrayBuffer (e.data); reader.onload = function () { self._fireEvent ("receive", [[this.result], null]); }; } else { self._fireEvent ("receive", [[e.data.toString ()], null]); } }; this._ws.onerror = function (e) { self._error = true; }; this._ws.onclose = function (e) { if (! self._opened) return; self._ws = null; self._connected = false; self._fireEvent ("disconnect", [false]); }; }, canSend: function () { return this._opened && this._ws != null && this._ws.readyState == 1; }, send: function (msgs) { for (var i in msgs) this._ws.send (msgs[i]); if (! this._error) this._fireEvent ("send", [true, null]); }, reconnect: function () { this._connect (); }, close: function (gracefully) { this._opened = false; this._connected = false; this._peerId = null; if (this._ws) this._ws.close (); } }); // Evd.WebTransport Evd.WebTransport = new Evd.Constructor (); Evd.WebTransport.prototype = new Evd.Object (Evd.WebTransport); Evd.Object.extend (Evd.WebTransport, { DEFAULT_ADDR: "/transport/", PEER_DATA_KEY: "org.eventdance.lib.WebTransport.data" }); Evd.Object.extend (Evd.WebTransport.prototype, { _init: function (args) { if (args.address) { this._addr = args.address.toString (); if (this._addr.charAt (this._addr.length - 1) != "/") this._addr += "/"; } else this._addr = Evd.WebTransport.DEFAULT_ADDR; this._opened = false; this._outBuf = []; this._flushBuf = []; this._retryInterval = 500; this._retryCount = 0; this._dispatching = false; this._availableMechs = ["long-polling"]; if (window["WebSocket"]) this._availableMechs.unshift ("websocket"); this._negotiatedMechs = null; this._currentMechIndex = 0; }, _onDisconnect: function (fatal) { if (this._retryCount >= 3) { if (this._currentMechIndex == this._negotiatedMechs.length) { // @TODO: no more transports to try, give up? } else { this._retryCount = 0; this._currentMechIndex++; this._setupMechanism (this._peer.id, this._currentMechIndex); return; } } this._retry (fatal); }, _setupMechanism: function (peerId, mechIndex) { if (this._transport != null) { this._transport.removeAllEventListeners (); this._transport = null; } this._connected = false; var mechName = this._negotiatedMechs[mechIndex].name; var mechUrl = this._negotiatedMechs[mechIndex].url; var transportProto = null; if (mechName == "long-polling") transportProto = Evd.LongPolling; else if (mechName == "websocket") transportProto = Evd.WebSocket; else { // @TODO: raise error, failed to negotiate mechanism throw ("No mechanism can be negotiated"); return; } var self = this; this._transport = new transportProto ({ peerId: peerId }); this._transport.addEventListener ("connect", function (result, error) { self._onConnect (result, error); }); this._transport.addEventListener ("receive", function (msg, error) { self._onReceive (msg, error); }); this._transport.addEventListener ("send", function (result, error) { self._onFlush (result, error); }); this._transport.addEventListener ("disconnect", function (fatal) { self._onDisconnect (fatal); }); this._transport.open (mechUrl); }, _handshake: function () { if (this._handshaking == true) return; var self = this; this._currentMechIndex = 0; var xhr = new XMLHttpRequest (); xhr.onreadystatechange = function () { if (this.readyState != 4) return; self._handshaking = false; if (this.status == 200) { self._handshakeData = JSON.parse (this.responseText); if (! self._handshakeData["peer-id"]) throw ("Invalid handshake response, no 'peer-id' specified"); if (! self._handshakeData["mechanisms"]) throw ("Invalid handshake response, no 'mechanisms' specified"); if (self._handshakeData["mechanisms"].length == 0) throw ("No mechanism could be negotiated"); var peerId = self._handshakeData["peer-id"]; self._negotiatedMechs = self._handshakeData["mechanisms"]; // create new peer var peer = new Evd.Peer ({ id: peerId, transport: self }); peer[Evd.WebTransport.PEER_DATA_KEY] = {}; self._peer = peer; self._setupMechanism (peerId, self._currentMechIndex); } else { // @TODO: Check why handshake failed and decide if retry } }; var hsData = { mechanisms: this._availableMechs, url: this._addr }; this._handshaking = true; xhr.open ("POST", this._addr + "handshake", true); xhr.send (JSON.stringify (hsData)); }, _connect: function (peer) { }, _onConnect: function (result, error) { this._retryCount = 0; this._connected = true; this._fireEvent ("new-peer", [this._peer]); }, open: function (address) { if (this._opened) throw ("Transport already opened, try closing first"); this._opened = true; if (address) this._addr = address.toString (); this._handshake (); }, _flushing: function () { return this._flushBuf.length > 0; }, _flush: function () { if (this._transport && this._transport.canSend ()) { this._flushBuf = this._outBuf; this._outBuf = []; this._transport.send (this._flushBuf); } }, send: function (peer, data, size) { throw ("Sending raw data is not implemented. Use 'sendText()' instead"); }, sendText: function (peer, data) { if (peer != this._peer) throw ("Send failed, invalid peer"); if (! data) return; this._outBuf.push (data); if (! this._dispatching && ! this._flushing ()) this._flush (); }, _onReceive: function (msgs, error) { if (! error) { if (! this._peer || this._peer.isClosed ()) return; this._dispatching = true; var msg; for (var i in msgs) { msg = msgs[i]; this._peer[Evd.WebTransport.PEER_DATA_KEY].msg = msg; this._fireEvent ("receive", [this._peer]); this._peer[Evd.WebTransport.PEER_DATA_KEY].msg = null; } this._dispatching = false; if (! this._flushing ()) { this._retryCount = 0; this._flush (); } } else { this._retry (error.code == 404); } }, receiveText: function (peer) { return peer[Evd.WebTransport.PEER_DATA_KEY].msg; }, _onFlush: function (result, error) { if (! error) { this._flushBuf = []; if (this._outBuf.length > 0) { var self = this; setTimeout (function () { self._flush (); }, 1); } this._retryCount = 0; } else { for (var i in this._flushBuf) { this._outBuf.push (this._flushBuf[i]); } this._flushBuf = []; this._retry (error.code == 404); } }, _retry: function (rehandshake) { if (! this._opened) return; // @TODO: implement a retry count and abort after a maximum. // Having fibonacci-based retry intervals would be nice. this._retryCount++; var self = this; if (rehandshake) { if (this._peer) this._closePeer (this._peer, false); this._handshake (); } else if (! this._connected) { // try reconnect setTimeout (function () { if (self._transport) self._transport.reconnect (); }, 1); } else { // try reconnect setTimeout (function () { if (self._transport) self._transport.reconnect (); }, self._retryInterval); // retry send if (this._outBuf.length > 0) { setTimeout (function () { if (self._transport) self._flush (); }, self._retryInterval); } } }, _closePeer: function (peer, gracefully) { this._peer = null; peer.close (gracefully); this._fireEvent ("peer-closed", [peer, gracefully]); }, closePeer: function (peer, gracefully) { if (peer != this._peer) return; if (gracefully == undefined) gracefully = true; if (this._transport) { this._transport.removeAllEventListeners (); this._transport.close (gracefully); this._transport = null; } this._closePeer (peer, gracefully); if (this._opened) { var self = this; setTimeout (function () { self._handshake (); }, 100); } }, close: function (gracefully) { if (! this._opened) return; if (gracefully == undefined) gracefully = true; this._opened = false; this.closePeer (this._peer, gracefully); this._fireEvent ("close", [gracefully]); } }); return Evd; } // defineEvd() if (window["define"] && define.constructor == Function && define.amd) define ([], defineEvd); else window["Evd"] = defineEvd (); }) (); EventDance-0.2.0/examples/000077500000000000000000000000001321356073300153465ustar00rootroot00000000000000EventDance-0.2.0/examples/.gitignore000066400000000000000000000000301321356073300173270ustar00rootroot00000000000000ping-server dbus-bridge EventDance-0.2.0/examples/Makefile.am000066400000000000000000000022441321356073300174040ustar00rootroot00000000000000MAINTAINERCLEANFILES = \ Makefile.in examples_common_dir = `pwd`/common AM_CFLAGS = \ $(GLIB_CFLAGS) \ $(TLS_CFLAGS) \ $(UUID_CFLAGS) \ $(JSON_CFLAGS) \ $(SOUP_CFLAGS) \ -DEXAMPLES_COMMON_DIR="\"$(examples_common_dir)\"" \ -I$(top_srcdir)/evd if ENABLE_DEBUG AM_CFLAGS += -Werror -g3 -O0 -ggdb else AM_CFLAGS += -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS endif AM_LIBS = \ $(GLIB_LIBS) \ $(TLS_LIBS) \ $(UUID_LIBS) \ $(JSON_LIBS) \ $(SOUP_LIBS) \ $(top_builddir)/evd/libevd-@EVD_API_VERSION@.la if HAVE_GIO_UNIX AM_LIBS += \ $(GIO_UNIX_LIBS) AM_CFLAGS += \ $(GIO_UNIX_CFLAGS) \ -DHAVE_GIO_UNIX endif noinst_PROGRAMS = \ ping-server \ dbus-bridge # ping-server ping_server_CFLAGS = $(AM_CFLAGS) -DHAVE_JS ping_server_LDADD = $(AM_LIBS) ping_server_SOURCES = ping-server.c # dbus-bridge dbus_bridge_CFLAGS = $(AM_CFLAGS) -DHAVE_JS dbus_bridge_LDADD = $(AM_LIBS) dbus_bridge_SOURCES = dbus-bridge.c EXTRA_DIST = \ common/dbus-bridge-own-name.html \ common/dbus-bridge-proxy.html \ common/ping.html \ common/shared_image.html \ common/sharedImage.js \ js/dbusBridge.js \ js/pingServer.js \ js/sharedImage.js \ js/sharedImageServer.js \ python/dbus-bridge.py EventDance-0.2.0/examples/common/000077500000000000000000000000001321356073300166365ustar00rootroot00000000000000EventDance-0.2.0/examples/common/dbus-bridge-own-name.html000066400000000000000000000121351321356073300234340ustar00rootroot00000000000000 Owning names - DBus bridge - EventDance examples

Owning/unowning a name

Replace
Allow replacement
EventDance-0.2.0/examples/common/dbus-bridge-proxy.html000066400000000000000000000072231321356073300230760ustar00rootroot00000000000000 Proxying objects - DBus bridge - EventDance examples

Example proxy for /org/freedesktop/Notifications

Notify

Title
Message
Timeout

EventDance-0.2.0/examples/common/igalia.png000066400000000000000000000604601321356073300206000ustar00rootroot00000000000000‰PNG  IHDR@@Í¥ªsRGB®ÎébKGDÿÿÿ ½§“ pHYsLåLåuÎð•tIMEÚ .6ÅÇu IDATxÚìwœ]Uµø¿kŸÛ§ff2)3é )BR Ä.Š)R>x>AAEDl?û³,Ø…ç¡—H !žI¦×{ï^¿?ι3H™É´[öÊg%“™¹÷ž³ÏÞß½ÖÚk¯ Nœ8qR "® œJ>~ëÚIO¯Ù8¥½µmF2•žbÓvJ*•ž’J¦jÒVk»:“¤­EÄd:• F@ãÿ@¤GEðº{¡¿ " F1xÁ{TÁÿ÷Avÿßx‘i…½mób(l6EÂf}<Y[Q^´åô×Ïßúºéeîi:qtr@ùàŸ¬yñ¹-5Édjž¢Ë;Ú:ON¦R“÷µ`‹£FDDPŒ1=A"‚d>K @Ó*b#0ª¼ˆHØûW<yЪÞWœˆ¾ü–×ÌÝòæcF·¸§ï褀äìo<\¾ñÅ­¥Å‰ø{Z›[ÏhoëX¾gÇ^ÄDDÅ2`4 1A§ÉnØÝŸ‹ €*ê_!^FW–’H„¿‹Dÿ¨ªÏsÔ¸†ÿ|ÝÌv×Käœóý'å¥^Ž$Šb'tv&/Ù»»þ¬ºû|  ã[s Hn’L0¸(ƒ Æú÷ €ŠŠ ‘ÇèÊâcªJ¿—L¦¾Y”ˆ´\ÿþ%Iדä¼ãË«ïÞ¾÷³»·ï=­i_“!`Y7³Ä`TÀ8f~jPÿÚ D]Uœ?vÔÏ?cñ¦™´ë]€N²Lþ¬úƵ¿nÇæç7Ô5V«Qk-ˆ¨T# -Àà>Œ`ÄhæC yˆ4Œ©,Y?kêØ®xËÜ?¹žçèd„äÜŸ=?í©ÕOqÏöºÒéô„dg2Œ ¢\ŠƒÁâx¤ôU}£0ø¡ï±hÄF"æÙšêòßÜxî²\tt2ÄòÎï­™ÿüÃÏÿWK}ó›:ZÛÇuu%1&‡d@#€Ã@¢ª¨Å#„=y¼º¢ô7â×EÄ-¤8: yÓ×™¾qͺsÛ[:>ܼ¯¹4JûÐË@*Àa`¦ýDc@-Ä¢aŠâ‘U¥%Ño}鼓þ(")׋ôC.úÛîÄý·þóM]]_Ø·½î(›öóÚöƒ”â˜eUADA¢±¥ñ_D#æ«_:wÅ׳Bf^të1ò¹Ý›vœ×ÕÖñ<Ћú£Ð0'hÄõ_ETF•ĵ¼4ñîʲ¢|ì-ó›\owtÝÝ^Û¾ÆE ÛëîiÚ¹7Š1š˜`.PPD4óY*"2®ªô_eEï¾æm öºàX°2íÒ?~u×s/·©´`1øÃGÀ¼ AÓ ž§‰h¸}æÄѯÿÈ›æÝïFƒ`AÈT½ËÎúÅ][w.·i6‚ "Ò€y @Á ¨çù‰×"xhóQ«¿põ or#Ä0/eÞ·žœ¾ùoý¢³±u‰M¦0F4À €@ÿyøw( ‡šKÑ|õ¼•u#Æ0/dòuwÏÛóð³ßJ6·¨é$i™AçXè bDÕø{’‹‹Â?úÊÙ'_äF`Nʘýeaˆ­_ê¬o:EÕ~Kw£;:€~ý.@<%j<Š‘¯~é¬ ‚$N³[Ê.úÝ´®»¿Öµ·ñtTUD2¤Áа¯4!0UcŒ…?|ÚüßãœÚ7ʳ|—ÿoudž—¯KÖ5^еJ7öÀÐðH(&¸9c …<Šb‘wßtæŠ?8‹Ð0+$òîŸz^È\ÑþâË7‹!³G´{@9: =ë·¥°1ÉÉã+}ôµÇÿÛ@À‘ƒß™?=5õò®j*…âÕ” ‡€‚‡BÈ#â™ã+ÊÇ_ù†Î<1® ŽLоþd•¬¸yWrã¶ÚdRÝ„âd¬M¥ìØ-{ÒWÿö¾ß»qžŽwê×ÿÔú«;öh{g5 ‚8ð9ú†¶´u&ßyù¯în¼öO÷ŸíšÅphä-ß;‹%7µhSËXUÇ='YeBIS[ç-ùÍ=ÿ¿»ŸœäÚÄpp䲿”°âËw±£î—$“ º#eNœd°ªÇ½´§aóÕ¸ï:×$€““¿r>ÓDKû©AßÏIöƒP„d:ý™ýîžûý=s\“8ö×Ý­`ÙMÑÐòsÒ¶g)Ó‰“Ü¡ @µÂ³WþyÕÇ\‹8öM^÷Í·²}Ï^::8ì9Éuÿ~éª?¯j¾áwMâx`9톥7­e÷¾¿`µwçqâ$÷ B¤¨±½}ÛÕ·­ºÂ5‡àþòúoÏ¡®¡öŽ™¸-FNòÕô‹r|õÚ¿®~Ñ5‡ /‹nø6»ö> DÜ ¯“Bp‰Uuú§îXÝ~ÿ{£`¡Ê™?I0ÿsÏÒ•¼uàsRHÆ ÑÖήÿ»þÿ̰ା/¼–µ/íÃêl—Ðì¤@MAM¦Óç~æ?ý“'žåX²ðóWÓ•ü;hĹ»NAUÞ´¯±îëñ…}ûöåÎÌíxßtuýÄï%E0{Ä"¾Û]¼Tzõ>z˜1=º-+ðèeZ¿³ñ­Aº(@(SÍ<°èŒ*Ú•¢yënš¶ïA­bŒ8 °€-@?ìb©,-bÂØQ”Gýþâù÷åÏÇ‚ÛÓ7Ðý˜‰ÒtǶL5€¥éþÁý÷zæÁs¨kieK}âù^‹1t.žP3öµ“'48æšsÝ7ý/¬*3' ÕUõ¸¿†^aÈØý:À `d“)l{'{6n£µ®/d‚’ú€ù @P«ÄãQ&)§²ñª>ä˜3ðûô_Q}GMVÆV !/è ½z…Ñ‘`pb’ ÞÏ3©4]-m4îi î¥]þ÷zʼns€V•ʲ"ÆW—QV# êÿL21@éé£ÃÀŒªˆQCG:ÅÖ†mL%%êyüØâ…ÿã˜í2ûS3vÓ&AØÛoVÝ/Ø’E4Áåxtl…ÖºFê÷4а£.p€¹@ ¤ÊŠ¢ÔŽ)eô¨b¢‘PX°¿uŸÌ\'МJ²©¡ž1ßþð‚ÿ嘭²àú:Ž9ª’Ò?%XQË×bDðT‘À¬Û¶›};ëikhE¼€Y À´Ub‘Õ•ÅLWA"Fmºûzºc€9@ÿZÏ[›š©oë¼ýŠEǽխg“Lÿxc*w²àh›öóøò€ïäqUÕŒfô„jRI¶¾¸•æ}ÍØTÚ­"gÓú­BQ"ÆÔÚJª+ŠI[‹Š`­ÍkC° µ¥%TƧ{Í¿ï¹tÞ±§ŠHN§ÉäúD%S'ìaj­’N“¯[ÛlÚŠ„™rôBa]›w²ã¥¤“©nËÏÉðOPž'Œ./æèéc}K[•TÚ–[~=b!™U•+üÌ3ÛÿöòKã_?aRÎB0÷Xúe,:¶ŽÒ¢ý“Ÿò\Ò©4Õ5UTOM²³‹ukÖ“ìLöòã µ#Ìš:†1ÅdœX)ˆö÷ï)£FÙÝÖºïÖW¾cÚô´à°Ëå Þ¶¤žâ" sÔ Š‰F˜»dªÊÆg7ÑX×ìÇàœ ‰ y,˜3‘D<‚1‚RˆÛý-ÄÕEEe»ÛZë®W­ütºÃ9»X•hcë×[MÉBÞÛ:á¨c§qüŠc?©šT*ílÁAAž¢ÖR’ˆ±dÞ–/˜F"q!TtÆÓW—êFÙ–“V|.^tJ‰«²¯$î¼é*„gÝ(õ ˆ*xž¡vê82Ÿ SÇ‘N[Ôi8²PCÚR]QÊÒã¦qüÜID#a×–™iA<Êý¨,Øó'¨’±ö:Ùè8ä’±4€$P‘ª"açMŸÀȳÁ^2'[ž=Ï0nb5KN=ŽIÓÇ“N¥)lk¹ïbӖꪖ.˜ÊÑ3Æ:ðí¿£žü$ vߊ)72‘)zk‡ ~)B M"éýýÑŰ÷æ« wÌÁÁœäqªYöšã[Så*vr‚MS^š`ñ¼)Ì™1žx,ìÚkö¡&JÅS×qÜöŸ€xA¥´Agz3íeüÓp°ÝÞ41 õöZš¾öQbáMÎÈ9yòŒZ–r•£ËKÑ5 ÖZŠ–.œÆqGO ¨(â×xtQÔý-?/BåS×±àåï„èÙ9à'h‹˜ãªçr¿à`º$Ê3F¤(Óa_©ª6BÛ×ÿ›¢è?ußÉ«»pP0sÆÑ“8aÅ\ŠŠã=íXí¡D"‹æMæ„ù“‰GÃX¾ƒL¤!*Ÿ¼†Û¾ &´ûöã`T`®·\Ï倃 ]üeªUíµ¹çÀšNCËW/#Ùä:ìa¬æ-<Šã—Í! µµ@n_1"Ìž>ŽeÇO§$ÃZpçà ~¨Ç?‚? ,¿ÃH…AËx¿=ŸK¿$?@8Í#~ÚÁ¡UDH[¡áË"zç‘Hˆã—Îaþ£ "µCU™TSÅÊ%3;ºÌåŽn¦aÔ#—±°î÷¨ñößÔ}08&¬ßÓ XîxÒ‘äÓª\ä§whmß71ì¾é£xòŒƒà¡§ö`‚ŠŠã,9i.S¦×¶6ï˜`­RQVÌÊ%3™:©ª'ÙÁïø*¹ŒE ÿ”ö:¼ÒmŒ„DtœQ,÷ëyLs쇴wòvµ|V5ÝU$é—… ;¾ð ÿvì£W,"Œ«­bÙŠ¹T.#Îý#!TýÝ‹çOaÞœ #®;ô¥ÝÄPùÄG8¾ùNTäˆæ ™òK×x¬×0ΰ’ì’iÀïUQ#Avïʨ„°ã†kñä)Á¾Ï3Ì:f2Ç-<Šh,’“© ¬îΞ1ž'EqQÌõ€¾ZË¡êß×°¨á/(½R]ŽDG{5zÊþ q<ŒtZ}Ê‚¨ b+rÀÔ—¾ê¨„°ûÆOñžs ì‡kl­RRš`Ñ’ÙL>>X#ÉL«R]UÂÊ¥³[]FÚºßW?@M”êg®eQý¯Á¿6à@tv1ˆ*åáöû!­lª=ûùe€ P54|é*‘]ŠL¿-)ËøÚѬ|Í|FU”eܳvø„8þØÉ3«¶çœ '}„_„êg®eaÝÏð‘[}½Uâ×Àµœ`/áG€–v¾–¶L¶Á™ä‚ÁZ45x4Ý|9%±õ¸ÈwÿG:m9æØ)Ì;nžçeUuP Úñ,_<ƒ²’„Kòîg "!ªŸ¾š…{ÀOO§EºWÛ%Éö29Ûpø¢p¹€ö¬¦KŸVÜû£©´GÃMÿMqt³l˜PZ–`ùʹŒWÑë»#i¡*±h˜•Ëf1cê¬U7½õ· ÅPµæ 5ü†!©’WéùLTPéÔ[ôbJ¦&ZånÕÌñCTÏNRÖcï/'~<¢V$J3cf-K—ÏÑÜAUåèYµ,Y8Ýõ£Ÿ¸”ZnG 1èV‡X2¯{Ç\0mŽx ­¬ µ{Í™ÌI>1²m3ü"H—ý“Õ7™ák•a”ÝÍÂŽ`‡ ¨ˆ‹…ƒq˜T„ŽT˜=Ÿû‰°‹ VÀÃZ˜3g" 35õ † µµUœ¸t6^&-ÃÉ@ÍqŽyâ f³–áõy qïÐךVåŠ;ïÎKŠòUÊüƒV9p«^a¤T€´ ³ý3Ÿ â¹™Áa¢(ÊI'Mù¨âýHÿ@¢Å‹¦3mʘÀês2p·æ>ù6fšÍ¾a1‚¶DŒÿïA)B—]©Ÿ“WÜÕÈkù²úñ69h.ÈÔ‘e „<—?ýYH=ì 8È2oîæÌž@2•FU)/+âäÇÏ®jI9Ì>Åb8æ©3™Ú fäèõ¡Â ¨6§oà}Úï6Û0S5<{õ ãÁA´eôè2]¾t3¦ß~ܼ)Ú“Ôìd ’£Ÿ9Yf#ªÆ¯ç§Y y€‡ôÀDHkÔ~Ä{"/¸»‘sŽêKQY##ìÿ@'Wë®»M½ä8¸ÚžX,2ëŸ}Sª~¯—׿d–_£×^ÌyÖk´žß`* EÞaO Ðe³Ÿ/Ú©xÈáçE­Mwô5¤2†h$”uýJêZ»˜~ÃO02ÎWð¬ÿ3#àn†Xú„4˜ôDðz…:<üì{ÿëàû ™_#‚§ Æ`‚ÏWUŒˆÿû¼Gf÷Œ & r)ñ“¬AƒŸK&¤|^`˜gf¨ÌëMÐgÅ·Ùüê<™Y,óÞ½<š^ÞMwÙy“¹–î}ñ¼ýo¿õÁ¥gönãËðÐY ¿üÆ0jü€_ܘîL0F»ïÉTÐèù,COAÉÞmÓ3ó j,FLÏýöjŒø+u&h{ÉX ½Ú5¨V”©ó”¹>ÕàkÑà í~MæC Šv?[ ~Çt_+€ñü•BÉÜœñŸcæõâe2fý«7¢¤ÄcîºK˜«Odg>«<Ü]¶o8‰HmÜ“µ ÉÙ CnZ›þi¯Î؃¤Òj‡¤Å@ ª(ÂKŸúao;.-ã>¡ÿ½¯„À×/^ò+ÑPt³kªþ7­üµðá— ŒïõëºDÒªÚœünNZ€;åMëCýú…¢l„+4uvqÔ ?Fï,À¾Y€ *"Ò*BÕ·ÿsiÇášùŠ>ô+ƒyŸÿ\FgÂTÿÙÍ[{6³½ Ù½“)$p÷nõÝöRUdllº\Û¾!§,@›Ö_ª¢¬îöE»+*½r\¶(JI4ÌÚ«/ Þî >M*"<òK—÷~_»hÉYŠž ˆæÒa$#Ÿ™ÿì;™ÚÌŒd¯ff˜~¼F<û·œrwÔ˧Õ2MõÕ{}×`[šd­¢BY<ÂsW_D{rneä ‹¾i¤òþï\ºì„þ¾þ«p¯*AÁUs8ðš žy;³¢;ü¾I–«Í€°ŸÚÜ5M?;+g˜¶zµô•¥®«½W²YF‡yöšÿ¤¥ÓAð ñ¾.`Âwÿké/ôM¾ráâú/_°H€ßöz_'€UaþÚ÷23¶+8Ó9.:ydeÒTDµ%õËœà¶}üˆ÷ ¢šcóüø2áùO\F«ƒà+ã6«E¤ì{—-Û:ï÷• Ÿ‰r®ßM ÝöS]æ¯;ŸÙ‘L’³d¿Ñ â¯þŠÉU¡+~&úµA ~ÇŸ*/×mLñêò}–’x$§<ØÝlYpÓ÷ˆ†jñ¤àAnüþ/ÿÄP´õÇúØ|U]m ñî5ˆ[Iã±`ã%ÌõžÍ½Òmõð|ý[:äÝSËdÉ‹MYinÝ»qj·—D1 Í/¸wŠLu‰á™O\ˆak†«4õ{ÃPÁà¦ó®Q5UªúLÁYÜŠ¿Ídnø¹ì_ð8v¥ýúH_ŸVø¿-¿ÍJxg½7ÙZŽÍli;E‚š€’mûA¯e‰(O^}!aïå+ªª€ìÂ?üðò¿ õ§Ý|Áñí7¿øXUùU!Å-†ã7^ØËòËA͸ÀGøzA[S¯·PuìJÛ»3 Ѯ¹§‰h”‡¯¼ÙRÃRƒ½kÏÿðÃË«ðß'¦†óÿxÞ¢³ÎÎ{“[ýq±pÃ9^GÖ&9N О¸©ªòXÝ#YÀ—öÈÜTZ§ô'çï`šL¥{ÜáS‘«¯¼Õ—óÞìùá?|✑ºˆÏ]ô+ HåsC/^ÿŽŽžE®¹F5Z’ƒP®N„æ®IöoSÆf Uõ7ÁÙ5†H*¥9 ¿ÞZqÿ•—БܒCRñ”ßö£/¿x¤¯æ†sîQˆmªù´Bì¨ÅÞÃìÄž ž_«?8Ö¤ª"«¶=“ܲG–¦-sdVã¬jN?k€Q‰0÷_õ_´vnÉŸ0•v×§Ÿú“ËO¼-[.ë†s¦¾ðþ…E"rW~´µbUX¼ñýÌJìCu(OÉ&­ï´<7AÐŽt¥þiâ‚`Jõ›*~-ÓÁP ˜ªJ„ÕW}ˆöü€ ëPJzÅI›²ñ?ÿþã_|Fs¼™Ó½t!³‹vúåsrmµ÷@ÚÜä| Ni-Aáß»þ90P¶î ¯hïJÞ;ØQèD4L4ÊÉ\`_kš×|å;D½‰=…r'PÅ/fpûÏ?rÒ[s¡É¯½åÑ·cnU ör%0îä„íbnlS~­ïÂ@òå\ÀçÞ¿èUuH2wLo%•êâ´`nü¥ü‚Ÿèüu*Aê;îxoslRRu±d°Üߌv¦mwñ¸|Ñ¢xŒ»>z1/Wfö Þ'rá/>zÒ•¹6æ®?{áúp˜8°±Wü2kƒ 6äõ;Ïart/=æhž¨©!q­uwë$ýþèc†€õm¿FQ θTRi ª ÉûÆ"qî¸âbB&Û!¤ø©Î¼å£'þ8W O½ûxû™÷?Õ'ý%ºl´«iÞ´óL&ÆÉKÙÕš9ïbð[Ï3КüʰPu±±i]J?Ë]õ§,V2•öcWyô”h(Æÿ^~1ØÍÙ뜉X”â[®\±.ÆßgÞwüUýCP3*»Z]Ó¼uÇ;ï,&“º»5œÝÕò:½mÊQÃÀM»ÿ‹Õ¡Í›ìJYüðpž©ñhœÛ/ÿ]é¶,ªÀÖ_}ì$ïWW®hÏ'#äº÷ÿ.àªl2½Õ¦8}û[©Ž'Ñ|-r#øùCÚk}©ñša`W:}ªJ·±6$š‰æÇJÈþªÅq¸ýòsèèêÌ*È_~}åŠ ä©|ú}Çß ¼¶ç~GÒðKrúîwR·=%­òQÒ i;¤!"°·í|«Ÿ 97ï_¡–ꕃ®®t°½„ÈKKg•þaêû†Ì|q‡÷`g*½d¸†¨*”Å{^ÞHº’¼ÿÛ¿&™®j PŠÃ)¿ýøŠû(pùÂïž|LDŽJ ÐÚ$om¸€ ±æÂiØM°eßðMêªÊ¨ÄcreÓâAà–ºXISkGÓ°®¡)DÃ%‰Xat…¦övÎùöo›Šn¨ *ýòöeü¯Y¹ 'Üðû5 ºDD#2xÕNÞ^ãâ…Õ¨lî.œ6lC(Â|º½OŸØ/ت¾Û2´+¿X' 3•.œ"ó%ñ8ß<ÿ,’©¡JŠ•vU­rðÛ_>ñ®ùKùS7åKl’wí{/ãâ]yº˜wMÙž2ï:|*)ìÆÿlÐ-Àç¶™T2m‡ÝU…QE1"!¯€†£²µÎrÉ~Kq4á—¸¨#ÿãÕ+;ò,_üÚOc䳃aZMóž†w3:á÷ã™Èv4ÂËûF`ä¨ööšOwv1¤Ïàú]áY]);2hé袰¦O¨­2üÏœMKGû€“dƒWo 9øZ®~çüëQ>: #P•tº‹w5¾·ðà—ép[öŽ.DHk•ýçÜÙƒf®Û¾±½+õqFð9Ž)/*¸aE”­u!.ùÁ/(Ša‚’UýµAž¾í'ëðÖw¹ékÞbŒÜÞ ¬íä­R“è O¯£# O¿HUJãQâÑpÁ Dƒ²uŸá¿~ø â‘ÒþPí_oûä)oqHë¿Üü矎èmjº³@…dªw´_ÊÄDKa6š÷@Ý®v+JȤtÖø˜yÏ; xýÎèiÉ´ÙÍc"4wtùÕa  Tj*,ß¾èL¬íwLå6¿#—+Ϙw;07˜bôp“t2ÕÎ;:.cbQká6ZZa×WµkÃE8Ø•J¿çÀ£ªÍñÃ’Ž\…q£Â|å¼3=}tŸåK·båÛÆÁ·ÍÕ¹þÖ¹ƒa”tº“wuügå—Oõüú£õ-à|ÄSUaÛÞ[ì?õ’ÑTZ³â†¢aQÅñ‹«t·°}_WßòŒTuǦös}øÝð×OüI‡¯Á“/ÿå©cDôi@1ÒÛVÛÅ™06ž,ìF2k6CWv´ƒŠÀ 3ÊÌëŸi:" pÓîÄ[“)›7#"t&ÓþÎÍã-D‡Pa|E7s6ÉTýf0¸ÎÁoðåco;ö©Æß¥Ù#¶‹³:Îal"Ÿµüúªhî€ÎdÖ\“(*ûZ± ÜÜÑõÞlÚ7«@{W2§Ï‡p‚ŒÀ¸Š(_ùÀ9´w6÷¶†äšÿ»ö”뮆F>rú±{Tµ2HKRµœÕñ~F'LþÖóë³õìh´£/ËnÒ¶®ëŽ€6­o•," ÐØÒ‰Z-\[dÆ–þßçÐÚÞªÁÉnWßyí)_t˜jKp~PeSmò¾Îóµ*áùëÀ"®q꛳ €"H]ãIúÄIåýŽnÝ[ºp{}Ó£Y—©Jei¢Àv†,ˆ«º}_X>ú³»ÏXõùãþâFàðÉæ?¿nô¤èŽÝªªR`yÎŽ—킽Md[S¨Z˜T½Ðœ¿õñ~ð™-‘[[;»Þ“3›aLy…ºÒ+à'gÕV¶üÚ!iøÅÞyl¥@]pvháJȃGÖ’;TQjGÿœ¿õ¼~ðñf}*m§eãä¦(£K‹ Þ Tå’‰UMßw(AÞ1¿tŸ…Ùر¶×e[ü¯çË‹1—í‘~ð‘EÓ6{Û=2T—a ×üܤÑMŸvÊ›@d/8wXž|qÈÏý˜¡ È’9 å´'ösƒºòâŽÄ%i«YoÙ™JÓ•ò׆ 0ßô3~Y’yãSm"2ADD -.Sß ét–_¤*/íZÙg pÍæðúöÎäÔl_ÚŠ…=*KÔãT®™4ºÉ­öfãÓ¹sÞ,5æyö2 #ÇÖ¬ëU%4kÍT´ªü1sÑŽýrz„\GWª Év²´u¦([<#yÝÛÔ/H"Š|truÓWj²t˜½áßkíßÌTx!=ù Á¦6ªHÖߪ´w-T½Ë9-}H PuJèá7's"¯S•H8DUiž¯û·öçIÕMoÇIö?®¿/8Õåý>ùB`ýåçÕÂÂ9o“×=qÛ!-ÀwîþHº›êÙ>å íÉ)k»ÌÓ!õÇ)ÕMïthÉKðuOÜcÿ¾àdDîÉ[°±ÕŸ™MŽÜ¡ÝY÷ €$FKg×9ÕÙ€½ÍmùYË/ó¸ƒ_î‰yÝ÷ï ÊùhÞ•(Ú°%0ürdû”hë8é•ìØO6î*3Ûê›ÚæVøB©U‚HÞYM­n\êp’»bïZx)ðí¼Zil† /熗؛‰8æ²:9¨¸»¡Å „Å bÉÅÂîÆ¶¼)˜ÜÇK~y` žöØwùQEóÂ7Ù´-çà íèƒ+—€¥‰“ŒïIvVDH¦,Öïa¹­þ Ù;¥ºq²ÃGžÄO{ôƒ ¿Í‹Š uõ`mn^{: ÛwyP¶u¦.µä&D¬(;ê[ñþ&'gV¿¬‹¨Ât‡|³=SDîÊé\càåí¹û{EP÷6tç¾j¸³+õvÒ¹«„4–®dÚß#œ{=MƒÃ5ÓÆ668dä¡%øšG^«wŸð0;'o`ÛÎàÔ­Ü5d%Ù5á€à¦=£ŠÛüRÖ’k.poWxG}3͵kWDѱSÇ6ìp¨Ècžúð -ˆÖäÐô ì®ËiøRs@îkn/ñŸPnß mÉ^O-¢~ ªó§mÜåQ#~–“æLò/C*·Ów´³ }ä5+^å‹È ›ÛwDØÓÜF< Î*È fŸ3clý¿ Å |¨Eÿµ´V`kN\p2 Í-~ 0×Û^ݾë à¾ý¨–·H:?êz+ÐÐÒ០§Ù| pÓŒq ·8,Oyp›Þ½ä8y2«sE`ý¦>¢›x––3€ìÀ®TúLÍ›};BCk%‰(ždqÐVùÓŒq W;¬%¸Fÿµì}¿VE%kz´´BWg>ÄþzˆÞ܇W0½+ž˜Oç‰ö4µemê•¢OÏ×ð‡B·ø ÈWɾ˜ 6ù¨ÈÕ•ÑW¯”B*]Bo“{ÃŽQÕw×ïÊ·¢ReLY1‰H(Û.­iÆø†27ütOˆÿZv»ó–¬;wÂÎ]ydýâyÈeõÒM…º–Ž›—ÕË„ºæ6&T•gK(0s~ŽKtvòJKðt½÷ÄÇP=>¼DR©~w¥æÒiì§,í`W*UùY½1•¶ìmj¥¢$1¢î°ªª_2]§Í_¿Ç y'¯âÎÊûÚ{O¬ÊFôlc`Ó¦Ü)uÕo+Ø·ïÝ yÞ<‘d~v*š;º¨,-É&WÐ¥3Ç×otCÝÉ!úëlUÙ1¢ÖHK+´·çŸëÛ3¡¥eE7“ÉôB›Îï¢ò/ïi¤¦²t„ì\àâY5õ¹!îä=eŪzߊE¨>:2lXߊ|lcPM&wÐ* òûô!™¶t$SÄ"ááŸm௳jê膷“¾Að¾ÇtÕÊ÷)üzØswìèqƒó--=Û$RÖÎ Êêå±*Û÷5wïŠFãïÙY5õoqÃÚI¿ºÍI÷þøþ°Â¯« víÈ_×·wûªõ‚Ú›³Šïxrm³Haœç\–ˆRQ2,ñ@m™5~_©ÎNޏÝòÓÀ1Ã0QËkýØ_!´+’<¶aÛ˜a·ŠFPöµtˆF‰„†27P3§Jㆰ“qéÄ{æêª“ëò!…ßž:hmË•ýóƒá{!€Öή¥ ¬Þ྅íõML­®ÀªzjŒªN ¯Ÿ]³o‹ÂNîß3JWŸÚ M[vnÏN›Tƒ1Fj»=¶ka_K»_,aïÛßÏ)WήÙûw7t ¢¼QUït;EÖ=_8.`/ $ÓZ«H°A¡pð¿·¥D,J8ä nh~}tMÝ—Ýxu2¨]vùÝÿ´«Oý"ÂÕƒ:롳½ ¬¿ÀU ˜L—a)$üu?üûš™\= k>ûù{ܤ å7\ …˜åw_£œ6K•3¹É$×¼¼¹ ÛR»]`‘ñˆ Ø]©4»›Z©,I t6QDD¡æè uÖ U'C(ïDxYaÜ€¶Ë °am\pö µå;bE¨oi§,Ã3rD "Ý{|Uç3ao«ŸN†´Ë.»Ëꃯêîô{š¡½Bôý2#7¸ÂUiþõ’—ë™2fzd`Qå†c&ìu%í —þc>øÚyXý*wý ˜BÚÂæuÁnÂü~9¬TZÃÞH¦,{Û¨êgÁ?ÛOï=fBÝ'ݰt2Ì|Ä>øº¯‚|DéG5i1ðòz‚,¸‚2frÁw&ñKè§ûyâ½ »™Pw²ŽNFd/ýûGÝB*×ï…–&×x&mZó©þ‘«²aç>¬ö«*ùLלŒ¬%ø÷I"ÒŠˆ’9ÿæ`j­oý™Ài.p ˆ•¨º~´ŠR×ÐÊèòâC†F‚ÓÜÞq줺×nNFZ&‰jÝ¡Í6?çÇýÔx¨0É5Gë[;(IĈE<[ö3^¾6wâž?¹ör’®Ü wîµ¼áê ïz:Û /áù0&¿È8þ a¦«ð¿’WN¶l™7qÏd×JN²MìÃo¼X!½ó¹T!‚ž,Ì|¿CMÝCÚé~ª Ûö5cd¿#õüÍ*ǹ®ã$;-Á;N‘Fzp<6<íàwØÉ¤¹½‹}-í”Å{%;³hþäÝõ®uœd¯û"+Tõ)ñ¿†-ëÀ¦ 7ßùptrÙT„] -”Ä¢xFDU3òžÇ\Ë8Éjþ-úëÓöÑ7_®"_—¶hmÌ÷òöGÞV·>àb€‡’°g˜:¶bÛüI»k]k8ÉÑ‡Þøqþ”Ý÷»Vq’õýö±Ó¯U‘™ql|Ê5È¡˜vípJâQJ1±~•„{çO8Énø=þÖå׉Ay4ìÁ­‚¼Z ¸1Ó'ÔT–eJý‰ˆ˜'7qg|8Éfø…€*hwÚËøé9Ì ìWo…ó«!»ÙaãO™\ÝS«W™¬Ú'6U߸`Êîk\+9É>‘"@PáXÀ*L=^|Ìå¼È{É´+bÜ ?UÆ” °J˜ \ýĦ1÷.˜²ëN×ZN²¦ß>qÆÍ*²ðÀ¾žêI°û¥Â9ö²/ÓÀo kWÊE3ñ ÓÇUq¸)AT«ŽŸºk¯k1'#îú>qÆ"à‘Cúqƃ—ž¶&çñõ¶QÒ¢.¸Txaʘ>¬ˆÜÌvÝÈɈÂïÉ·GDd=Ç´Ë2q¬{Ðm‹ËÌ ¸A-ºbaBU9F ¨î÷E­Îzlã˜ß¸nädÄ&í5ï0 ;€hŸÍºšYA‡wÒ½X |BPUbáE±(Ö¯sß—‚g>¶qìÝ §îü¾ëNN†¿ßòC Õ¾ž "P\%£¡ÙEoüà‘UÉÔ‰…Þ3k«Ä3P@Teü¢i;v¸!édø¬¿wžŽÈm‡w}Ðe­Âº‡ {pæ`t ±w˨*“ÇT_÷›€~itpÃÒɰôÙ§Þ=ÕÛz2ýê²F`Ò\Øú …º ¢Ä=cš ~%ñ(‰hd A@â¬û‚šN†Zì¿ßQÕgt@‰À‰R(*÷ço)@%Tvõ=î•_âÄѣ'?Tä¨GÖýüâé;?冩“¡s7H<Àß@¦¨9^|¨ &H€žgvâ^k• Õå}Iyé{ùä#ÆÝ·xÚŽ¿»êdÐûìSïù®ˆ,<µ³aÛsgi71…¸*^–ˆQ‹Ò}&è õ(þöðúqÓO˜¾cƒ²N ~OŸù.ú'Ê!^ íMÁ`$¥lõº(œ›·Ö2¾²œ424¦¯êz\º½“Áƒ_øÝjÒ|xñþs…Å_IÄÂ[3æK!(ªL[‰12tŸ<ôâ¸'ÝÐu2H²·Çqµ)¨ž$HÊég¨˜>®²N èÄ·¢h„âXdhçD˜ÿЋãoqc×É€¬¿gÞ·KĉüÄ!ÒÒ±5´Ÿ‘M*ÆÑŠÈ-÷>ÔÙ™Ìÿ‚Š2gbµÜå0y¨"\µdÆö›ÝPvrðû£ˆ¼}xG` n|ˆ9G¤«›·®NhK{gžÓOWYFEI|x?UAN[rÔ¶º!í¤ÏçÙ³>|‘~ïôØlMýVØ»9ÏDÄëè>ĈwÖ)ª|1è¦2¬`U [ASùŒˆ—vtoŒFBkòºC LWÙ3Ë §I†"RÿÐúw•“ÃX~gÏP5w ÆÓ» ŒŸÄÌM~*^ô_¡3Ež°i=;?í^¥8Ã3;rùŽA»ê. Ò s'c^V·÷@+…Dt4æe‹oÞkzLB}\[%ßÔaÚ¸ŠÌÊìˆ*0jõ ã] A'žªŸ?gC0[ް!¢0vVÞæªZ1yu· ‡vçcÈSU7ª›%[]D”3X7þ‘eGmÿªòNzÁoŠLÍšqè…¡zÔmÌ?ÇP<ÌŒ<Ö Àµc¶m©k$eó‡øD#a*ˋɪÛòûÒWV¯­Ù¶|Ö¶[ÝÐw¢kÏý¥*ó² 3 ”Œ‡½›ò±É›xeŒáÖÕÅZßÜž7w˜VˬÚj¢á’}»Ò4¸¨eËgn}Ð! pÅ®=ïÃÀ×e¤ã~“dl{"¿Ý ï×®¿_9ØgîUñòA+JÄ¢aÉÆëõ#,«V­­©v(Xø­ø:Ù ?€H„ó§Ö¯$*·ì—’QýkWCËJÉ?m-ã*ÊüRWÙ[“@#"/­ÚPStÒ´mB‚ß çÏDäÞ #d÷ ?¶<”±@QE£%ÿ|Û;ºnEùL®Ÿ¯(µ•儌ñÏ>Èî &]4E ¿q"¬UÍËEUÐZ—û5ŽDÚão;÷¿•<5vÌ [wïÌuÊ«*ÇNO®Y²[Ošõò‡‡ü}ñ¨ª¶xäN6Ý—ûV`(Šœú´@p&HFÆV”6$¢Ÿ ¹K?Žª;È9p×Ü·vÂß"ò^Ö‰ˆ—‰©ýR`ÔD?‹8— ÁˆnÌ<ˆý8·v]gQ<º#sdHî©"Æ‹„±hî]¿ˆ(úºûž¯ýœcDžº¾ë.xTU'¢*h° –KZ1-°s¶ž)ÞÚm ¾òU•ݺ{_óå¹hä*pÔÄÑ~H%g—E?uïsµ;WÎÙúm‡Œ<‚ß‹>‚ÈÂÜv -T{×å¦+¬V4Qy×-@€d*}ƒˆÉ½´   …üÉ*‡Ów—ã[÷>7áCy÷[Ñí ‹òb?Eñx0áœ4U<3ÿ½¢šûËkÆ…×mÛÓ¦°³=öwÌ䱄¼¼)ä˜É {ÛÊÙ[nsÉa›iýÅ7×H6çúõW:`ÏÓ¹7¨"Å)³ü±ðA-ÀŠ’Dª4M£ª¹sƱ ‡ /ŸJ÷ Š¢üåÞç&œâ0’«–ßÅŸ¸†|‚@¬Ää^€I¼Uûœ ~dô=›wí[™+>¾ª2gâ"á¼,µ§¨Z™uòœ-ëRrèÁmøw!ò»¼½ÁŽFØóT.ÅÿÐ’Ú›Íñw]uP `tyñ9“C§Jq,ì÷ÍËSìDüÓp^ü׳Ü–¹\q{7\òμ†@´” ày®ØP:áöÃZ€ßø¿H*emÖÔÔ*³'Tû{~óÚœð](òSŽÞÒˆ“ì}T?¸PáÑ‚8º³v¯É ï>ZŠ,yh¿ =¨Ï ‡v·ttŽËvK°$#÷Ç9ûÛ¥T¡áîg&Ž:õ˜-îl‘l´ü6~p!"’o1¿ƒB¥ ¼Ppžp–OL¡Ø«%;h³²8ñHg¯¦S–ñ¥¨U?ï/ÿUÄ¢"RÿÏg&º²úÙ6À6_ºHøI!ÀÀ'©Ê…³Ô E~ýÊïÔœ0¦âó/×5¾9Åþ}Y"N"ŪÒ8üM#Ï.&˜=ð+RÿÙHáܸB¬Â/–ŧȩµPuìýð÷W ¦ƒÊww$S±¬t5‚=¿Åñha8U@¶¾æ˜—\ñ„‘~›.­RØ)ÔFhÝ ë³Öë×XeÚ,¼7Ôg   ?ÕÚ‘\”3Z8¢$^pÖß+kí]OOÜzÚÜ-µC#eù]6‘mP(>ïA¤d4oMg§™-½ù@?9d&ã˜òÒßk>WUeò˜QXr»,Å hÍ]ÏLªkპÃÑðÊÆ?\4 #ÛüM9XØ`P5 ‰Ñdk™à׋'4oˆ~ûŽ{::ºRYÀã¦×RH¡–ƒMlj ý'aÜ_&´+R>ïÑûºš†^¾½ø’IéTzóÛ®C'—ÂXò=\´°ý¾¬Û!¢&‚Ì»<.ѳ;úeŠÜÙ…ÖXU¡5+T™P= DÈžk™v°Ò¿jþ<…¸¨¶­Y¼2æð4Äð;áâñÀf ‰þåsF¶=ÓÞ£TÐ"Ä«‹¬©~%ñÑ¿?ü @€²Dü¯j³hrSUœ TU#Ø[ 5·O@E{?Ï–5‹O‰8L |ç„ÿ˜«°-xbŒð§ë ÛŸmGDÐ|ÝÔ'ŠjAlÖDˆT€PÑõ{ž‡àôšªŸ{a/;ÒàT)+N`ŒÔ (LÅ3¤¿/Ôþ­»áC<°ûÖœà,ÁÁ‡ßÅK@ŸzeUUá×¶<ÙŽ˜\ªð<¯€piÖT°/åGm8b.œòüºò¢øæ¬˜_D˜4¦"éúÇìw æÞqèÁŸ^BUšŸX|²Ë,ø-ù¥"ò`P,]^.‚?^'¬  ¼Žª…’ Yb” OxDÆ}¡íˆ6ÞÍ6­¨eDUŒH.´©_0õ;Jí걇ÞâÃÍ/9uŒÃ×Àä»K.^f”4H¾pÎä±·…C#›e¡ªL¬®(ȵ6Uõ™ö#¥öþñ`úÐ þÞá°é‰%¯™ç0vdò½¥_¬ô°i*xáoÖü¹¡Ü.M~¤*!(ž jFò: 'I¶ì8ô0é£üäîQÏíÚ×4{¤jŠÇϘ€µ6/gîýPò+eâãI›žÇ¦Æø¿ƒÅ·­1€øÁ_ÿÿ**bÑy ¾û)‡´¾Ëÿ,½èŠüXD´©¢ ‹E0dº¤Zº'Ùi9é¼N8·R¸<œjƒºGFl¬ª*OºÓÌüÓl„á«R éÒX4LZ+R u‚tܪLºcpÞäšUÑPØŸâ†Y5 µ•£Ðøì‘S]æ—0åöqXÀ Ji=ºô5+Þ-ß_zÑ×ü2ö³^LȰêçÅ<öÛ]AŠ … ,=j„>Å„¶YÚ9hœ;aMcíèò¿(:ì©N‘°GI"V0ýFQðóeòcHË ˜ß,÷>ºôµÿé0w`ùÁ²‹î.4CÄÀ}?)á±?ìDŒPP†`¼:˜w‡Ùý‘¢‰ßíÓ$ÕŸ7¶j?Ä0¯B¨*%‰XA=O0ßI3ù®jTìô¾óÈÒ×^çp·¿ühùE@O‘Aå”?\îýq1ÿzö ''ÐDÀ„†ýs%GfÜú…AàY'®ØZVGuøöaXUF—Î>J¿fòªÑþBÆ Ó 6*}æ¡e¯ÿ…Ã^¿e¾„êÔLÆé´;«~\Ä?Ý S8,á·¦‘áQA %îís˜¢_ÏP~¯5Uå*"õç5 Qšˆå}¢Ÿªsñç £,éç“é÷XÌDßÿÐò×?PÈàûñ²‹Š´ü¢:…‰C»b)îûIŒû¿¿ "^aÌè±±~`Ø>7BùÜõÏ>ï§|þw‘.µ64äåXT©Uäÿ屨où%>§Ô®­ÀúQ F±ƒjwš ô9 Á_9×îÇåÿܪAü÷ß°tõÓ ~Ë/˜…˜çÔjwg«™Gâ/DÙàгþ¥Áø¿hmðlgÁ¢$;•åçt²â?ÇAW:ÿzÏýÃR'Pˆ§<® IDAT%T´Á̹{F__sDéÖT•?°e÷¾•Cÿƒ²â6¯áçW\LÜh©][‘ á7€§=¸ì ­*2nÙê;š ~?]~Á …{í°7¸ŽÀê_ij““. ɵ¢Ñêoôç5Gäh½}Ù±¯ yQ†T¥¼8ž¿µ32ð»Ù2ééQÁÉ—#±ê@B”ÆÕ'¾!ï-ÁŸžxÁوܫ#ur›@(bXýó(«¼B‚WŒl$´hRZÕHfÚo¾9ä,ÞÕEÖ©ÚýÁ•ÅE õgŒœðû¢eò¿G¼•Ûyqõ‰oZ‘Çð»¸%¸çÝRä…áþŸÅxàç;ò»ž ‰€ }Ý¿PÙõý¾´#½§Ù“Ç^¢"C¶•Ï¢Œ*-"mÉÃZ×Ñz“ áÒ¯‘w>}ï²—VœˆÕ…4º¬˜Øu7¤ *®L1~cq.Tõ ®PÜwÒé›sqÌݲâËOÕªÓŽƒÿhœgàr㋵¥½s¨¡,™3¥{[Rκ½F}®“©Ï–’ÎDÔ2+WF‚íh™­jø[«‚‰d·Âe..˜s¤ûµ~röþ?÷×¥ä_мæÔUΊ§ô›•çVY5;ý:ìˆb2%±ÁV6?½Jº×-ê·qf«ñ.pÛÔk ªŠˆbƒ _ƒ¿¬õSµW\ÐÅŠ‹kƒßËe Ø÷w¿dþ ¸¾˜¢-2õÎI¼¢Á‘ò¢øÛkÑbtyIf“Dîîð U×v2íÙbooI0Oѽw­xGtäáwÞ ”=€ÉqB¼Ê>ñë F¹ï‡[ý<Á\Ž!`âƒYB`Ê“Ƀ#|ÝY·Å¢ƒÒPe‰xnwÛˆ0æcL~¡([A%ªmw­|û쑺Œ[Wž{5°.“b™ Ê=?Šò¯ïlCrº”þBÈÀßG$Tö°ÈÀ+EþŸ-KÄ[«XË€´8E•¿Ïð«¢!ûßLØ”¹½½Ã96ý¿Ÿ»kå;®îÿíÊsïR¸Ñ¯%–ç -Ê}? ó¯l…¨—«®„о“Ê„ÐÚž2HNùàÉkŽŸýx"mV° cº-æœÊs Æ}¸ƒš-±ü‡ß+M¸ö+Þ¾z8>ðw'¿Ì­+Îݼ¦·OžïÍlô!ð … <ÛCR©ÊP©JM©Ô<ÏwžÎÙ«ÿ8çÞª@Bj®{ëî/9¿$•[uïY{Ÿo¯µöÚßÑœ3xœšÏ™s1,_ c}wŽ«8šUHvžûãßn¹©ý·[n._¬7z¶ê“£—k‘u h:aÏÓ/?<âKgšL¾'2ÎMîž )Ö¿¸m¡,ºàËȧ¶ô´ççxÚç•dÔ‚îÖù­ùJÚ½²°ìô€ØÈÀð«—ݼuÁÉoË1ó‹4dÝ3efÝöþo /?<xugG9S.‘3zfw1¬åu1&í‚cÇÓ)%Ïá=—ž·1Cæ·èn|(†ÊFÃ) š*c!„€¥JPVBLÒSœŸÁoú»ÕÞ’èÁîzö¡ùZùWUw ÞE<±Œ$YVâ–¤>§SÞ’ùe0©aŸ_™¼E†´Í—†p×OÏ[R'È À¿k.¥0ÌdúÅúÅ ùq-‘P˜ëy`š­—›ôÓCˉòÏúz •GìõûNåÂ;w¾zÙ-m¯l¹eÎ]²ŸÛrÇÙ`ŽƒñAeØ·æÛ÷çã©»›2¨ï°˜k’ˆÌëÚŠ‹F€;nößdêzhV䘑—ãI­‚i{1Àá;bXÛ¤+ò{»ø‚°@蕪mëfM~U·o'¢c© …“¦^;k‹ðÔ§›@¥ÿæ¡ðºGÙfk¹»éŒ_Ï€Ò¢ü˜A³i&@N¬ŸÎ—A8÷ó¬iÕùÍ4ÍÂÜûrÕ¶ûfúM/TÝñ*ÏdsRu6æm¯-ÄOïjyõ ðgé2"oûbXoQ ðó׎ü«×40ƒg$ÏSd:^ öο7„²¡žÍY†ÅÄüƒ_Wm?øöÄw{þ [npÕ´:C…ÓÒ ££¦OÞѤ1 ¦þ:s±SÖóþœÖþb ãÖ¯)¹Q$sB3Šçç´*âdá‚Û|(í걜{ÄöÞ_oÙîûõ–íï{ó¿XuÛˆ9 DknOsÛ¾\<¹­(4Òy1œÅK5 èÿºˆ&[\|ö#½=y^óë3*‹™^TœNY?·Ôå¼Û}(ó© È 0Ñ—ªn}0E~[n{A‚ˆÕâ2Z޽áÁO?Ñäà"wb›õ•ÌçÎìõ’õÒwе¿âÅœŽK‚¯>‰Ä-ÏÛ¿'£0/çoZ—V½2Ø’8÷³>TúM0¹åÉ’!Üò—i_Ëö2÷ó9e(•;±T…AL òý5¹9¹ïB+`ÉSœú~žöwU3ÕANJ÷$áܶ l¾4†Ï>{8d§‘¥_è4*,ÌbÍK×,²Ó¼4(È÷¾çte1`»#œ.¥.œxçŸN¢Ry~ ¸¢8C<0Ò‡†æÃ—ÔÖí+ †üDÓŸl…y¹5Bc¯öâÇ76€Lw‘N›3£§+x Òƒ‹M~KJ€sãxkq~î“oW"„\MÁå/uqDÎýìÖO*ò[ÈÖ6𣻷š®C¦c èéïRv^àT[Gm.~´­$%29mБ~°®]¢´éÒ¡0?ç^YœÚî=Iº Ž"ôrú±F¸àîqœá3Ô“´€ô P[·‘høÄç€Ã}¨©Û‹D"1ÝST˜ç‚Óu ?ºµaªNpYáÆõoëÒ ¢âÕ]+Ž?÷±AëâwnXCÓ·|OF‚ryÝ?ö\xûÖNª"ç‹z™Ñtì(šZëqªZì†Àª18ÒŸ"/K„®ùøá¶§Np9óJH8íïNþÿ ÒB´úå—Ê.K®©sÛ‡ŽMäz=ß³á$Œß|1ÈÉãÒ2\dŽÀEŸÅjŸªó[(ÄcQ<²Á ïô%D èìmÇáÆÉ]…ù.Aè¬Íïkrå‹9ìÐÎI@vvé®\J«,‹¨ØÃw¾¨kbÐ=4‚œÝINöXÂØ$\|Ó0JBQß¼=>@:ºŽ¡¾é $ËYíD¢±öúF'F@Šçd´×xñÃk“$¸ 9qªXü=•ÿçÞO€pþ†µçˆYžxJ’à FÒÙ[’‹!xï-Ã( h©Ü‰ÂÜóN‘H‡ëk1612÷6 î·µuµà豺TY‹ÂÜG†ãxµé’ ‰%-$p'Ýõµˆ²—¾¹ÔY6¼ûòŽÉ¼ÏŸ1ó[æôÈdpI]g¼çÎ!TøE6)9/Š-…ÐÐÓ׉£-‡‘°â ’Ç#„¨>¼£ã#$”­çA‚B0ŽWøÁÖ: W[º¾Ã¤ð¿ù"˜«/[k,ëLúæþŸ˜†öË7Ÿ÷‡––üîB¥O Ì×ë GB8T·C#ÐÄBO-§%dkg3޶ÕòÊès_Q оßÀ÷?Z24·0{‘/lŸèù±QÞõTøäpÖ üÓÝ‘í OEÀpŠÙc ’U×¶\x×Ö+ò›Ï³0c`°Í-G ¥½¨»·$|ªìÄoÜi§rƒs»ÎZ/¿æÓr3µ”-R´=âzÓ„±“ŠñârÙ -b ¯i\ìn‚31 ˆŒ8áñb\Ží/¹sëýjÃcŽB4ÁÁº}ê ±do.„@SG#·„”nvP äœ<÷®ƒ¹ø—k/n €Ç¦Î;× ÿêšå¼ÿ´ Ào*ØAD–Ì$™YèÜ3¬ ëù1¤)ðÁ[ú°6 rIóñÄŽ·7£©¹´L99"B8Fuý„*œ;ºå⟯9 òjX”h˜4A§›‹@ FE/œ±Ü÷6 ðØg¢{LC”3ߘ„K$Ög¼r]ÊP¡Ó}†‰‰1>¼~ÿ$Òe_öxï1Ô4TòP{Åssè;äà±+ƒLsáC`»Ï}3EáUDd+œ†G>~@Á ƒ'áiÃ3I+©.»¶«‚*Zš $K´4AW×1·}iú$³ˆ€¸Çþ£Õ8ÖÝâl¨n–fdtÖ˜xìŠC€éYÀ°{,€&2¿)òŸ~=î9íbÀ/nÛº‘@Ì î胤óºX¤M¸ò†^”•Ìðl]A„ÞÞÔ××  §ï5÷s ãº==å±;…S‚`tTkøÞå¹@á/"ÖÀÐŒ—©à—Ÿ6÷›n°¹ä™°ì 0d²xd"§bÝÛbŒlïA~@y³&üþ =zccàéºeiíÉ8Š3Í8ÜrÑx4)¨0ƒ‡Ð€ŽýVÕ†1ÿ¾Ãöa0ôÎýÅÇÓŠðÓq~x_bØ4ƒˆ:GYÓÄœ6<˜eTÝÙƒ"¿žù#@ˆ'âh;ÖˆŽŽcHXVF ¡!ò£¦±nJaF3@ºj5|÷²é »ÎÉd€BLB“¬—_,ˆ¤"Àà{÷„^Ñ„øj8£á‰ À rwHfv1g|äŽn”øÕÒ?“P°-10Ѓ¦ÆC…KXÚ²xî úFz±»nÆ|cj¬gáIw4ñÝ?©uêyö›l…ÓÙHnž'&Òn‘LçxüÞè·ˆÄí}ÃäS›Ñ/WLáªí](Vaï Ö{‡}¾qm8€‘áyÜLš:boÃDãQªœÑÊØsȃïVÕ̲NŽ3a ùG"÷ÙÞ´ŒÒÝüOüyìþ„äß´õ§?²ÃÒØzCŠ‚RÍß ‹¢áH zºŽcÅ7!'‚-%jšöã@K-2$­¹¼Ë#݇¼ø§ËêÜ/>öe"†¬%€o¥œgëÓ6M’ Cðƒ{#WŒNažÖ×礋Cš„ë¯iC~PªÕý4Û²hn:‚c-õN¨›EL,¢þÑÝhêj„PÙÁÓ¢ë°cÓ~@#çÈÖÛ¶ÿ"ýûäý¿¿Hç{ʘ8Ç’|ÑþÆŽñ¤|ÕI%­ᆫ[‘RžßÛŸm[èhkBkóØV"{] 7õâõ»ÑÖ׿°*œÒ^/¾²i?HÓOÞc# ÈÞWÈ|ú/Óý–2†òq+·Ö4uà-13`×ouÈO9~oIã Ùq­§« -uˆ„CÓºädûƒí´àìëÇîú? k¨šÐ”]Nn*LöøÊ¦j‡ßTòÂD7 œ¿þ|”ä• [r„DD5‰ÚŽÚœÉ÷±"’CN²e “izWì¤ñXCý½ˆÇb`çzêØšòû–:å™#]G 4%y«p^å¹+:Oåjã^ºA¿áúëPFßËŠÉŽRäDãñ>Òõu¤­Œˆ˜H’1><ˆ@À‡„eM5¯Jã)M äá°C¾!äyò±¾t=Ö­ª„Å–Ûn5³IÑ€Žë¬ŸÕáè]uÖÑŒº·=è*#–õ;¶íÿ¡™fFÊÀ˜þÉ1“…ÂÂÍë%ÕYï¥1Âñ0ûšÐØßŒò‚2¬+®Di^)XÚ5vìôe¶hˆ5"Ìá¢õ¡•2\+²>"¸<ÄüÓD,öiÒ4¦I$4È4^ƒ‰¶e! "àŸD(L ˆ¤7«$Þ3.eÁFü£ô C*ŠÖ¢¼ E9%ËÖLj¦$’atƺxØ&Æ§Ž¢õ?Vҭر<àžÐ)mû‰H„5M'áñ€t-=(rOZ‰b±(¾ D"€40‘³“KÊÕ[!Tá–ÌôûÐ;Ñ  ¢`5*Š*‘kæÂ£› 4iq,@ðË:c¶F C'¿ûêVÚȬè Ù<àáQ ˜_¶m›ázN„®O…“‹>ý“ÁCÚñx A¿¡`Ð!;á´ÿ$‘&䬰¸#Ä#Á Fa³„&4œY²åùå0„]Kº«Oâ2ñÄ$š"-°YB ‘æ“,×Ô£%¶ÇbÅ(`ÞÔ –•èNòP<!éjkétoNŠ “aË”—6ËYDS¦˜Ò²ÇÀîéIN)‹­Ø.ËCrÅg%:ƺÐ1Öéìî(Ï+ǺÂu(0 œp™ÉÍÏ}NæÄ“âÁ6[èŽô 3Òí–R9aAÀuÜ´n%›?+ÎHå[‰ž ‚‡ ÌÒtÚ(: V4ŠxÔ!&ÀDÐ<^]é:4çD/Žv•4YJX ‰D +x,†„epÄ@›Ùùû”XšòóNIˆÎ/ÆHp#ÁQgž¥æ @±·…f!н%0… ¯æu" wŽ%kB“ë7àOøL„áû1G\ÆRb"SoÔ³bò ïëÕòЖ•nî¬9$ZˆÒ3Nô,˜o~³prH)!#agr ·½)¹¤æzpî*Í $»;KrInj"8ç)ÌÖc£©îÖÎü´1Ãhx Œçëœ<·Lî¬t_ˆtw9ù;E´I§Uè03òµü¿Úm×<žéˆl›V«˜?ÁD7áT”äÖ”d¥& ŸðMäîcœÐƒS»UX|bÖ0”„xÀI(oP!k¸:éöjOÙUû䑲ÕJ)@…´íâ)fnsˆ”ˆžÂŠå>d óè»îD³ÛBÍ뤜¨d^KD¯€™Ô±3…É} ZeÿÝ»îeE€oÁRne¢Mb'Ô((d8õÂ:¿øÜ²±7þAYDà)q¦”º¦ç𼲆BF;[ë¼k¿veEUÁSÏ)«LAåO[ Àͺv©„½oEº)dûå9v‘Yrþs×ZŸýNÙDy€³ÃF+^_ºÆ#Hü³Ê *dŠ×'H <§üËÿ?vÀx.ðZ«²Š"À9cõPü,+ñÝëý0ˆú]"Tl¨V €%3òüÚKË/ÝðRh×w•U.6…Ã{α•š®?Rú¤ iåõÁzpcÁ™÷þ&ºÿý <Ù­¬¢pQðŽxìÁs¿ø×†Ú.e …åf>Ay/üW¼®ðÿL¾úceE€‹n|çÙç&âU¹EÅçy$9•e–Ìågæ <õÑŸ˜ÏúÿëFeE€K4ÿ‘m­ÎÉË¿ìêMªØXañ˜àÕ½‰K×¾ÿ];Ã5ë6½ô„²‹"Àå ‹}ÿ¯°¤ÔãÍÍÿrÚ4wPXaN³G3q^Ù9Û.ÛðaïwºŸjRV™g$§L°ðh,.)dÉF#á/H€%IwñfrV­6GŸ„£-q¢èj²)’í®SÌ ™”ìg@ Jý§ãÝÔk“â¬6äþ¿¤©a—`H€ûÞΤ”,`!Rb¯ÉïK‰¿&ÿdù¦NÓï#ùZéªKˆiڊο“2íÓ_ïü9õÙyÚ}&¿oúk§Û ÌàÔkáÚ&)d;µ69úyâÄÏÎS¶À´×ì”®-, !SŸR÷9õÃR¶”I%q¤>?³#ƒïØÃ‘2µ¥p¶r“ŸE:Ò}’™=º—Ö¬½÷ìUïxzGócõ”)L{´¯ß¸:è÷íˆF#Ÿ·¥d€H "À™ -ÍÄêü5ßúä…·}ãŠ×§J`ÆaøÂ‹‹zŽ·ý£ý™ÍL¤Pà) Ж…ÞBäy~îC÷?øþ—n¨§H`æçp~ò3ýÀŸûD"q‹e[:H°ÍNÃ/E€ÙM€`fK‚ ½¡Uy«ÿ·Á;Ô£pÅâÐꊅí Û.™ˆf!2H ÇðŸ]~Î÷¿Ýþó‡ÕÓ¡0kаaóí“c#?°,«ØéL,ÉQdU¸2 ÐU¢'A%¹«.>óâ?ý«Cþ§zf-Ž¿û¢²ÑÁ¾7‚ÀY v[q*\1È IMh¼¹ló¾³WŸ}Íý5øÔìW¨à¢¦â æñÜ?96öHŠ;IqE€N€ÌLE¨,9ã[ʧ¿ÙúsU¼¬PáTàÏ|NÛ÷ëçÏñ÷|>ßViÛ`€¥¤0ý ™a³DAN!6”žùK[òŽoíWt© yE€ ³A_Õ5íÍ U–mí…‚´¥‘P˜nÈ ‹% ¼(/({Í4¼}蜿~Ý+_Re,Š—]]Ùu¼åæX4úÉX"vqÂJ8Ä@¤pLl³¤ÂÜBä{ öäçþüªw_ñÌåÏÿŸš­ŠÁÛ>ST»ç÷÷…áY¶ý¡x,ê‘"ÀÅ"@fØÌ¤!?'9FÎï+K+w~ùúÓ¿uQLÍJE€ Ë„ý\r×Èðàݱxô\KÊÕ¶m;> R8Gdç™ÓðÂБ²ÂòÆMk6}ÿÞ½O<«f"@…4„ïÎûKV¿¾Ã3‰Jæ<‹eêÁf‚"À“ ƒÝ¢=Mè‰`YqÙĦ5g=så{¯øÚ9?º5ªf—"@… CË•7ô÷÷ü­Ï7~Ÿß7™Ç€nƒÄ er’Ù‘]¤®{ìµ¥ë&×”T<^š_úØ­¿{Dm`(TXIØwéåz_w»XsƦŒŒ þO˶îñMŽO;‚G+—™Á –`2 *J+Q”Wôsé}f28ù›ÊòõÖ§v=a«Y¢P!‹Ðô±;rš›ëK²LÓ=7†ÃÁª„´«|~ˆÈ%,ç˜^º » g»óÛ4½(+*ƒÐôÚÂüâçcñØk^ON÷ÆŠMë^þNH¾"@…“¢ù¦{ ››JCáàªH4R¢›Þ‹,+q‘-å%RòÆP$ 4/‘@´°È '\Ȇ“§còróa&„0z Ãh0tÏg׌IDATf®×4£{UqéÐ…ç¼·÷}ÿþ¥°ME€ Ž—.¹ª4œ1á+†‚9^onE ôè†Y°klɺžkÛve*Ìv=5]7‹ ÃX ¡hô8RÓ ˜† H2&5Ýf0 ¡'lšfŸ×›3u³çœMï æ¾ïé¿Sgjfÿ›Hµ ÑU'IEND®B`‚EventDance-0.2.0/examples/common/ping.html000066400000000000000000000101411321356073300204560ustar00rootroot00000000000000 Ping - EventDance examples

Packets

Sent:
Received:
Lost:
Per second:

Round-trip time (miliseconds)

Last:
Min:
Max:
Mean:
EventDance-0.2.0/examples/common/sharedImage.js000066400000000000000000000065731321356073300214200ustar00rootroot00000000000000/* * sharedImage.js * * This file is part of the 'sharedImage.js' example. * * Authors: * Eduardo Lima Mitev */ function SharedImage (args) { this._init (args); } SharedImage.prototype = { _init: function (args) { this._cnt = args.container; this._peer = args.peer; this._createElements (); this._drawn = false; }, _createElements: function () { var self = this; this._canvas = document.createElement ("canvas"); this._canvas.className = "canvas"; this._ctx = this._canvas.getContext ("2d"); this._wrapper = document.createElement ("div"); this._wrapper.className = "wrapper"; this._wrapper.onmousedown = function (e) { this.grabbed = true; this.grab_x = e.x; this.grab_y = e.y; var cmd = ["grab", {x: e.clientX, y: e.clientY}]; var msg = JSON.stringify (cmd); self._peer.sendText (msg); }; this._wrapper.onmousemove = function (e) { if (this.grabbed) { var cmd = ["move", {x: e.clientX, y: e.clientY}]; var msg = JSON.stringify (cmd); self._peer.sendText (msg); } }; function ungrab () { if (this.grabbed) { var cmd = ["ungrab"]; var msg = JSON.stringify (cmd); self._peer.sendText (msg); this.grabbed = false; } }; this._wrapper.onmouseup = ungrab; this._wrapper.onmouseout = ungrab; this._viewport = document.createElement ("div"); this._viewport.className = "viewport"; this._indexBox = document.createElement ("div"); this._indexBox.className = "index_box"; this._wrapper.appendChild (this._canvas); this._viewport.appendChild (this._indexBox); this._viewport.appendChild (this._wrapper); this._cnt.appendChild (this._viewport); this._img = new Image (); this._img.onload = function () { self._aspectRatio = this.width / this.height; self._requestUpdate (); }; this._img.src = "igalia.png"; }, setPosition: function (x, y) { this._wrapper.style.left = x + "px"; this._wrapper.style.top = y + "px"; }, setRotation: function (angle) { var st = "rotate(" + angle + "deg)"; this._canvas.style.transform = st; this._canvas.style.webkitTransform = st; this._canvas.style.OTransform = st; this._canvas.style.MozTransform = st; }, setSize: function (w, h) { this._canvas.setAttribute ("width", w); this._canvas.setAttribute ("height", h); this._ctx.drawImage (this._img, 0, 0, w, h); this._drawn = true; }, update: function (args) { if (args.w) this.setSize (args.w, args.w / this._aspectRatio); else if (! this._drawn) this.setSize (320, 320 / this._aspectRatio); if (args.r) this.setRotation (args.r); if (args.x && args.y) { this.setPosition (args.x, args.y); } }, _requestUpdate: function () { var msg = '["req-update"]'; this._peer.sendText (msg); }, setViewportIndex: function (index) { this._indexBox.innerHTML = index/1; } }; EventDance-0.2.0/examples/common/shared_image.html000066400000000000000000000035651321356073300221450ustar00rootroot00000000000000 Shared image - EventDance examples EventDance-0.2.0/examples/dbus-bridge.c000066400000000000000000000057111321356073300177050ustar00rootroot00000000000000/* * dbus-bridge.c * * EventDance examples * * Copyright (C) 2010-2013 Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #define LISTEN_PORT 8080 #define DBUS_ADDR "alias:abstract=/org/eventdance/lib/examples/dbus-bridge" static GMainLoop *main_loop; static void on_listen (GObject *service, GAsyncResult *result, gpointer user_data) { GError *error = NULL; if (evd_service_listen_finish (EVD_SERVICE (service), result, &error)) { g_debug ("Listening on port %d, now point your browser to any of the DBus example web pages", LISTEN_PORT); } else { g_debug ("Error: %s", error->message); g_error_free (error); g_main_loop_quit (main_loop); } } static void transport_on_new_peer (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { /* This is to send a virtual DBus address to the peer instead of the real DBus daemon address, for consistency and security reasons. */ evd_dbus_agent_create_address_alias (G_OBJECT (peer), (gchar *) user_data, DBUS_ADDR); } gint main (gint argc, gchar *argv[]) { EvdDBusBridge *dbus_bridge; const gchar *session_bus_addr; EvdWebTransportServer *transport; EvdWebDir *web_dir; EvdWebSelector *selector; gchar *addr; #ifndef GLIB_VERSION_2_36 g_type_init (); #endif /* Session bus address */ session_bus_addr = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL); /* web transport */ transport = evd_web_transport_server_new (NULL); g_signal_connect (transport, "new-peer", G_CALLBACK (transport_on_new_peer), (gpointer) session_bus_addr); /* DBus bridge */ dbus_bridge = evd_dbus_bridge_new (); evd_ipc_mechanism_use_transport (EVD_IPC_MECHANISM (dbus_bridge), EVD_TRANSPORT (transport)); /* web dir */ web_dir = evd_web_dir_new (); evd_web_dir_set_root (web_dir, EXAMPLES_COMMON_DIR); /* web selector */ selector = evd_web_selector_new (); evd_web_selector_set_default_service (selector, EVD_SERVICE (web_dir)); evd_web_transport_server_use_selector (transport, selector); /* start listening */ addr = g_strdup_printf ("0.0.0.0:%d", LISTEN_PORT); evd_service_listen (EVD_SERVICE (selector), addr, NULL, on_listen, NULL); g_free (addr); /* start the show */ main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); /* free stuff */ g_main_loop_unref (main_loop); g_object_unref (selector); g_object_unref (web_dir); g_object_unref (dbus_bridge); g_object_unref (transport); return 0; } EventDance-0.2.0/examples/js/000077500000000000000000000000001321356073300157625ustar00rootroot00000000000000EventDance-0.2.0/examples/js/dbusBridge.js000066400000000000000000000032531321356073300203750ustar00rootroot00000000000000/* * dbusBridge.js * * EventDance examples * * Copyright (C) 2011-2012, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ imports.searchPath.unshift ("../common"); const Gio = imports.gi.Gio; const Evd = imports.gi.Evd; const LISTEN_PORT = 8080; const DBUS_ADDR = "alias:abstract=/org/eventdance/lib/examples/dbus-bridge"; // Evd daemon var daemon = new Evd.Daemon.get_default (null, null); // Session bus addr var sessionBusAddr = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION, null); // Web transport var transport = new Evd.WebTransportServer (); function onNewPeer (transport, peer) { // This is to send a virtual DBus address to the peer instead of the real DBus daemon // address, for consistency and security reasons. Evd.dbus_agent_create_address_alias (peer, sessionBusAddr, DBUS_ADDR); } if (transport["signal"]) transport.signal.new_peer.connect (onNewPeer); else transport.connect ("new-peer", onNewPeer); // DBus bridge var dbusBridge = new Evd.DBusBridge (); dbusBridge.add_transport (transport); // Web dir var webDir = new Evd.WebDir ({ root: "../common" }); // Web selector var selector = new Evd.WebSelector (); selector.set_default_service (webDir); transport.set_selector (selector); // start listening selector.listen ("0.0.0.0:" + LISTEN_PORT, null, function (service, result) { try { service.listen_finish (result); print ("Listening on port " + LISTEN_PORT + ", now point your browser to any of the DBus example web pages"); } catch (e) { print ("Error: " + e); daemon.quit (-1); } }, null); // start the show daemon.run (); EventDance-0.2.0/examples/js/echoServerTls.js000066400000000000000000000120121321356073300211040ustar00rootroot00000000000000/* * echoServerTls.js * * EventDance project - An event distribution framework * * Authors: * Eduardo Lima Mitev * */ // A simple echo server over TLS. // Use any TLS echo client (e.g 'gnutls-cli') to test this. const MainLoop = imports.mainloop; const Lang = imports.lang; const Evd = imports.gi.Evd; const LISTEN_PORT = 5556; const BLOCK_SIZE = 8192; // initialize TLS Evd.tls_init (); // create socket let socket = new Evd.Socket (); // setup TLS credentials let cred = new Evd.TlsCredentials (); cred.dh_bits = 1024; cred.cert_file = "../../tests/certs/x509-server.pem"; cred.key_file = "../../tests/certs/x509-server-key.pem"; cred.trust_file = "../../tests/certs/x509-ca.pem"; // hook-up new-connection event socket.connect ("new-connection", function (listener, conn) { log ("Client connected"); conn.socket.connect ("close", function (socket) { log ("Client closed connection"); }); conn.socket.connect ("error", function (socket, code, msg) { log ("Socket error: " + msg); }); conn.tls.credentials = cred; conn.tls.require_peer_cert = true; conn.starttls_async (Evd.TlsMode.SERVER, 0, null, function (conn, res, userData) { try { conn.starttls_finish (res); let certificates = conn.tls.get_peer_certificates (); log ("Peer sent " + certificates.length + " certificate(s):"); for (let i=0; i */ imports.searchPath.unshift ("../common"); const Evd = imports.gi.Evd; const LISTEN_PORT = 8080; Evd.tls_init (); /* daemon */ var daemon = new Evd.Daemon.get_default (null, null); daemon.set_pid_file ("/tmp/ping.pid"); /* web transport */ var transport = new Evd.WebTransportServer (); function peerOnReceive (t, peer) { var data = peer.transport.receive_text (peer); transport.send_text (peer, data); } if (transport["signal"]) transport.signal.receive.connect (peerOnReceive); else transport.connect ("receive", peerOnReceive); /* web dir */ var webDir = new Evd.WebDir ({ root: "../common" }); /* web selector */ var selector = new Evd.WebSelector (); selector.set_default_service (webDir); transport.selector = selector; //selector.tls_autostart = true; selector.tls_credentials.add_certificate_from_file ("../../tests/certs/x509-server.pem", "../../tests/certs/x509-server-key.pem", null, null, null); /* start listening */ selector.listen ("0.0.0.0:" + LISTEN_PORT, null, function (service, result) { try { service.listen_finish (result); print ("Listening, now point your browser to http://localhost:" + LISTEN_PORT + "/ping.html"); } catch (e) { if (e["message"]) print (e.message); else print (e); daemon.quit (-1); } }, null); /* start the show */ daemon.run (); Evd.tls_deinit (); EventDance-0.2.0/examples/js/sharedImage.js000066400000000000000000000044531321356073300205370ustar00rootroot00000000000000/* * sharedImage.js * * EventDance examples * * Authors: * Eduardo Lima Mitev */ imports.searchPath.unshift ("."); imports.searchPath.unshift ("../common"); const Evd = imports.gi.Evd; const SharedImageServer = imports.sharedImageServer.SharedImageServer; const PORT = 8080; // Evd daemon */ var daemon = new Evd.Daemon.get_default (null, null); /* shared image server */ var sis = new SharedImageServer ({rotate: true}); sis.setPeerOnUpdate ( function (peer, updateObj) { var cmd = ["update", updateObj]; var msg = JSON.stringify (cmd); peer.transport.send_text (peer, msg); }, null); /* web transport */ var transport = new Evd.WebTransportServer (); function peerOnReceive (t, peer) { var data = peer.transport.receive_text (peer); var cmd = JSON.parse (data, true); if (cmd === null) return; switch (cmd[0]) { case "req-update": sis.updatePeer (peer, sis.UPDATE_ALL); break; case "grab": sis.grabImage (peer, cmd[1]); break; case "ungrab": sis.ungrabImage (peer); break; case "move": sis.moveImage (peer, cmd[1]); break; } } function peerOnOpen (transport, peer) { var index = sis.acquireViewport (peer); print ("New peer " + peer.id + " acquired viewport " + index); } function peerOnClose (transport, peer) { var index = sis.releaseViewport (peer); print ("Release viewport " + index + " by peer " + peer.id); } if (transport["signal"]) { transport.signal.receive.connect (peerOnReceive); transport.signal.new_peer.connect (peerOnOpen); transport.signal.peer_closed.connect (peerOnClose); } else { transport.connect ("receive", peerOnReceive); transport.connect ("new-peer", peerOnOpen); transport.connect ("peer-closed", peerOnClose); } /* web dir */ var webDir = new Evd.WebDir ({ root: "../common" }); /* web selector */ var selector = new Evd.WebSelector (); selector.set_default_service (webDir); transport.selector = selector; selector.listen ("0.0.0.0:" + PORT, null, function (service, result) { if (service.listen_finish (result)) print ("Listening, now point your browser to http://localhost:" + PORT + "/shared_image.html"); }, null); /* start the show */ daemon.run (); EventDance-0.2.0/examples/js/sharedImageServer.js000066400000000000000000000216021321356073300217210ustar00rootroot00000000000000/* * sharedImageServer.js * * This file is part of the 'sharedImage.js' example. * * Authors: * Eduardo Lima Mitev */ const Lang = imports.lang; const Evd = imports.gi.Evd; function SharedImageServer (args) { this._init (args); } SharedImageServer.prototype = { DEFAULT_VP_WIDTH: 640, DEFAULT_VP_HEIGHT: 400, IMAGE_WIDTH: 320, IMAGE_HEIGHT: 320, UPDATE_SIZE: 1, UPDATE_ROTATION: 2, UPDATE_POSITION: 4, UPDATE_ALL: 7, _init: function (args) { this.image = { w: this.IMAGE_WIDTH, h: this.IMAGE_HEIGHT, r: 0, url: "img.png", grabs: {} }; this.image.x = -this.image.w / 2; this.image.y = -this.image.h / 2; this.image.s = Math.floor (Math.sqrt (Math.abs (this.image.w * this.image.w) + Math.abs (this.image.h * this.image.h))); this._viewports = []; this._freeViewports = []; this._mapping = {}; if (args["rotate"]) this._rotateSrcId = Evd.timeout_add (null, 35, 0, Lang.bind (this, this._rotateImage)); this._updateFlags = 0; }, _rotateImage: function () { this.image.r = (this.image.r + 1) % 360; this._updateFlags |= this.UPDATE_ROTATION; this.updateAllPeers (); return true; }, _calculateViewportCoordsFromIndex: function (index) { var jumps = [{dx:-1, dy: 0}, {dx: 0, dy:-1}, {dx: 1, dy: 0}, {dx: 0, dy: 1}]; var i = 0; var c = 0; var times = 1; var ct = 0; var x = 0, y = 0; var j = 0; for (i=0; i 0) { obj.x = x; obj.y = y; } if ( (flags & this.UPDATE_ROTATION) > 0) obj.r = this.image.r; if ( (flags & this.UPDATE_SIZE) > 0) { obj.w = this.image.w; } if (this._peerOnUpdateFunc) this._peerOnUpdateFunc (peer, obj); return true; }, _viewportNeedsUpdate: function (vp) { var needsUpdate = vp.needsUpdate; vp.needsUpdate = ( ( (this.image.x >= vp.x && this.image.x <= vp.x + vp.w) || (this.image.x+this.image.w >= vp.x && this.image.x+this.image.w <= vp.x + vp.w) ) && ( (this.image.y >= vp.y && this.image.y <= vp.y + vp.h) || (this.image.y+this.image.h >= vp.y && this.image.y+this.image.h <= vp.y + vp.h) ) ) || ( ( (vp.x >= this.image.x && vp.x <= this.image.x + this.image.w) || (vp.x + vp.w >= this.image.x && vp.x + vp.w <= this.image.x + this.image.w) ) && ( (vp.y >= this.image.y && vp.y <= this.image.y + this.image.h) || (vp.y + vp.h >= this.image.y && vp.y + vp.h <= this.image.y + this.image.h) ) ); return needsUpdate; }, updateAllPeers: function (force) { var vp, peer; for (var i in this._mapping) { var index = this._mapping[i]; vp = this._viewports[index]; if (force || this._viewportNeedsUpdate (vp)) { peer = vp.owner; this.updatePeer (peer, this._updateFlags); peer = null; } vp = null; } this._updateFlags = 0; }, grabImage: function (peer, args) { var vpIndex = this._mapping[peer.id]; if (vpIndex == undefined) return false; var vp = this._viewports[vpIndex]; if (args.x == undefined || args.y == undefined) return false; var coord = this._translateFromViewportCoords (vp, args.x, args.y); var x = coord[0]; var y = coord[1]; this.image.grabs[peer.id] = {x: x, y: y}; return true; }, ungrabImage: function (peer) { var vpIndex = this._mapping[peer.id]; if (vpIndex == undefined) return false; var vp = this._viewports[vpIndex]; delete (this.image.grabs[peer.id]); return true; }, _calculateS: function (x1, y1, x2, y2) { var dx = Math.abs (x2 - x1); var dy = Math.abs (y2 - y1); return Math.floor (Math.sqrt (dx * dx + dy * dy)); }, moveImage: function (peer, args) { var force = false; var vpIndex = this._mapping[peer.id]; if (vpIndex == undefined) return false; var vp = this._viewports[vpIndex]; var grab = this.image.grabs[peer.id]; if (grab == undefined) return false; var coord = this._translateFromViewportCoords (vp, args.x, args.y); var x = coord[0]; var y = coord[1]; var dx = x - grab.x; var dy = y - grab.y; var grabCount = 0; for (var i in this.image.grabs) grabCount++; if (grabCount == 1) { this.image.x += dx; this.image.y += dy; } else if (grabCount == 2) { var otherGrab; for (var id in this.image.grabs) if (id != peer.id) { otherGrab = this.image.grabs[id]; break; } var ds1 = this._calculateS (otherGrab.x, otherGrab.y, grab.x, grab.y); var ds2 = this._calculateS (otherGrab.x, otherGrab.y, x, y); var per = (ds2 / ds1) * 100; var newW = Math.abs ( (this.image.w * per) / 100); var newH = Math.abs ( (this.image.h * per) / 100); this.image.x += (this.image.w - newW) / 2; this.image.y += (this.image.h - newH) / 2; this.image.w = newW; this.image.h = newH; force = true; this._updateFlags |= this.UPDATE_SIZE; } grab.x = x; grab.y = y; this._updateFlags |= this.UPDATE_POSITION; this.updateAllPeers (force); return true; }, setPeerOnUpdate: function (func) { this._peerOnUpdateFunc = func; } }; EventDance-0.2.0/examples/js/simpleHttpGet.js000066400000000000000000000113401321356073300211100ustar00rootroot00000000000000/* * simpleHttpGet.js * * EventDance project - An event distribution framework * * Authors: * Eduardo Lima Mitev * */ // A simple http GET client, supporting TLS upgrade // Use it passing any http URL as first argument // (e.g 'gjs-console simpleHttpGet.js http://foo.bar'). const MainLoop = imports.mainloop; const Evd = imports.gi.Evd; const Lang = imports.lang; const BLOCK_SIZE = 2048; let uri = ARGV[0]; let schema, resource, domain, port; function performRequest (req) { // initialize TLS Evd.tls_init (); // create socket let socket = new Evd.Socket (); socket.connect ("error", function (socket, domain, code, msg) { log ("socket error: " + msg); }); function receiveResponse (inputStream, result) { try { let [data, len] = inputStream.read_str_finish (result); if (data) print (data); } catch (e) { log (e); return; } if (this.is_connected ()) { inputStream.read_str_async (BLOCK_SIZE, 0, null, Lang.bind (this, receiveResponse), 0); } } // connect to server socket.connect_async (domain + ":" + port, null, function (socket, result, userData) { let conn; try { socket.connect_finish (result); conn = new Evd.HttpConnection ({socket: socket}); } catch (e) { log (e); MainLoop.quit ("main"); return; } log ("socket connected"); conn.input_throttle.bandwidth = 0.0; conn.input_throttle.latency = 0.0; conn.output_throttle.bandwidth = 0.0; conn.output_throttle.latency = 0.0; conn.connect ("close", function (socket) { log ("connection closed"); MainLoop.quit ("main"); }); if (schema == "https") { conn.tls.credentials.cert_file = "../../tests/certs/x509-server.pem"; conn.tls.credentials.key_file = "../../tests/certs/x509-server-key.pem"; conn.starttls_async (Evd.TlsMode.CLIENT, null, function (conn, result, userData) { try { conn.starttls_finish (result); log ("TLS started"); } catch (e) { log (e); } }, null, null); } conn.output_stream.write_str_async (req, 0, null, null, null); conn.read_response_headers_async (null, function (conn, result) { try { let [headers, ver, code, reason] = conn.read_response_headers_finish (result); log ("response: " + code + " " + reason); if (code == 200) conn.input_stream.read_str_async (BLOCK_SIZE, 0, null, Lang.bind (conn, receiveResponse), null); else conn.close (null); } catch (e) { log (e); } }); }, null, null); // start the main event loop MainLoop.run ("main"); // deinitialize TLS Evd.tls_deinit (); } if (! uri) { log ("Usage: simpleHttpGet.js "); } else { let uriTokens = uri.match ("(?:([^:/?#]+):)?(?://([^/?#:]*))(:([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?"); try { schema = uriTokens[1]; domain = uriTokens[2]; port = uriTokens[4]; resource = uriTokens[5]; if (uriTokens[6]) resource += "?" + uriTokens[6]; if (! resource) resource = "/"; if (! schema) schema = "http"; if (! port) if (schema == "https") port = 443; else port = 80; let req = "GET "+resource+" HTTP/1.1\r\n"+ "Host: "+domain+"\r\n"+ "Connection: close\r\n\r\n"; performRequest (req); } catch (e) { log ("error parsing url: " + e); } } EventDance-0.2.0/examples/ping-server.c000066400000000000000000000053761321356073300177660ustar00rootroot00000000000000/* * ping-server.c * * EventDance examples * * Copyright (C) 2010, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #define LISTEN_PORT 8080 static EvdDaemon *evd_daemon; static void on_listen (GObject *service, GAsyncResult *result, gpointer user_data) { GError *error = NULL; if (evd_service_listen_finish (EVD_SERVICE (service), result, &error)) { gchar *url; url = g_strdup_printf ("http://localhost:%d/ping.html", LISTEN_PORT); g_debug ("Listening, now point your browser to %s", url); g_free (url); } else { g_debug ("%s", error->message); g_error_free (error); evd_daemon_quit (evd_daemon, -1); } } static void transport_on_receive (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { const gchar *buf; buf = evd_transport_receive_text (transport, peer); evd_transport_send_text (transport, peer, buf, NULL); } gint main (gint argc, gchar *argv[]) { EvdWebTransportServer *transport; EvdWebDir *web_dir; EvdWebSelector *selector; EvdTlsCredentials *cred; gchar *addr; evd_tls_init (NULL); /* daemon */ evd_daemon = evd_daemon_get_default (&argc, &argv); /* web transport */ transport = evd_web_transport_server_new (NULL); g_signal_connect (transport, "receive", G_CALLBACK (transport_on_receive), NULL); /* web dir */ web_dir = evd_web_dir_new (); evd_web_dir_set_root (web_dir, EXAMPLES_COMMON_DIR); /* web selector */ selector = evd_web_selector_new (); evd_web_selector_set_default_service (selector, EVD_SERVICE (web_dir)); evd_web_transport_server_use_selector (transport, selector); /* evd_service_set_tls_autostart (EVD_SERVICE (selector), TRUE); */ cred = evd_service_get_tls_credentials (EVD_SERVICE (selector)); evd_tls_credentials_add_certificate_from_file (cred, "../tests/certs/x509-server.pem", "../tests/certs/x509-server-key.pem", NULL, NULL, NULL); /* start listening */ addr = g_strdup_printf ("0.0.0.0:%d", LISTEN_PORT); evd_service_listen (EVD_SERVICE (selector), addr, NULL, on_listen, NULL); g_free (addr); /* start the show */ evd_daemon_run (evd_daemon, NULL); /* free stuff */ g_object_unref (transport); g_object_unref (selector); g_object_unref (web_dir); g_object_unref (evd_daemon); evd_tls_deinit (); return 0; } EventDance-0.2.0/examples/python/000077500000000000000000000000001321356073300166675ustar00rootroot00000000000000EventDance-0.2.0/examples/python/dbus-bridge.py000066400000000000000000000027541321356073300214400ustar00rootroot00000000000000# # dbusBridge.js # # EventDance examples # # Copyright (C) 2011, Igalia S.L. # # Authors: # Eduardo Lima Mitev # from gi.repository import GObject from gi.repository import Gio from gi.repository import Evd LISTEN_PORT = '8080' DBUS_ADDR = 'alias:abstract=/org/eventdance/lib/examples/dbus-bridge' # Session bus address session_bus_addr = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION, None) # Web transport transport = Evd.WebTransportServer() def on_new_peer(transport, peer, main_loop): # This is to send a virtual DBus address to the peer instead of the real DBus daemon # address, for consistency and security reasons. Evd.dbus_agent_create_address_alias (peer, session_bus_addr, DBUS_ADDR); transport.connect('new-peer', on_new_peer, None) # DBus bridge dbus_bridge = Evd.DBusBridge() dbus_bridge.add_transport(transport) # Web dir web_dir = Evd.WebDir() web_dir.set_root ('../common') # Main loop main_loop = GObject.MainLoop(); # Web selector selector = Evd.WebSelector() selector.set_default_service(web_dir) transport.set_selector(selector); def on_listen(self, result, main_loop): try: self.listen_finish(result) print ('Listening on port ' + LISTEN_PORT + ', now point your browser to any of the DBus example web pages') except Exception as e: print('Error: ' + str(e)) main_loop.quit() # start listening selector.listen('0.0.0.0:' + LISTEN_PORT, None, on_listen, main_loop) # start the show main_loop.run(); EventDance-0.2.0/m4/000077500000000000000000000000001321356073300140505ustar00rootroot00000000000000EventDance-0.2.0/m4/.gitignore000066400000000000000000000000411321356073300160330ustar00rootroot00000000000000!javascript.m4 !introspection.m4 EventDance-0.2.0/m4/introspection.m4000066400000000000000000000066141321356073300172210ustar00rootroot00000000000000dnl -*- mode: autoconf -*- dnl Copyright 2009 Johan Dahlin dnl dnl This file is free software; the author(s) gives unlimited dnl permission to copy and/or distribute it, with or without dnl modifications, as long as this notice is preserved. dnl # serial 1 m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL], [ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([LT_INIT],[$0])dnl setup libtool first dnl enable/disable introspection m4_if([$2], [require], [dnl enable_introspection=yes ],[dnl AC_ARG_ENABLE(introspection, AS_HELP_STRING([--enable-introspection[=@<:@no/auto/yes@:>@]], [Enable introspection for this build]),, [enable_introspection=auto]) ])dnl AC_MSG_CHECKING([for gobject-introspection]) dnl presence/version checking AS_CASE([$enable_introspection], [no], [dnl found_introspection="no (disabled, use --enable-introspection to enable)" ],dnl [yes],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0],, AC_MSG_ERROR([gobject-introspection-1.0 is not installed])) PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, AC_MSG_ERROR([You need to have gobject-introspection >= $1 installed to build AC_PACKAGE_NAME])) ],dnl [auto],[dnl PKG_CHECK_EXISTS([gobject-introspection-1.0 >= $1], found_introspection=yes, found_introspection=no) ],dnl [dnl AC_MSG_ERROR([invalid argument passed to --enable-introspection, should be one of @<:@no/auto/yes@:>@]) ])dnl AC_MSG_RESULT([$found_introspection]) INTROSPECTION_SCANNER= INTROSPECTION_COMPILER= INTROSPECTION_GENERATE= INTROSPECTION_GIRDIR= INTROSPECTION_TYPELIBDIR= if test "x$found_introspection" = "xyes"; then INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0` INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0` INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0` INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)" INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0` INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0` INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection fi AC_SUBST(INTROSPECTION_SCANNER) AC_SUBST(INTROSPECTION_COMPILER) AC_SUBST(INTROSPECTION_GENERATE) AC_SUBST(INTROSPECTION_GIRDIR) AC_SUBST(INTROSPECTION_TYPELIBDIR) AC_SUBST(INTROSPECTION_CFLAGS) AC_SUBST(INTROSPECTION_LIBS) AC_SUBST(INTROSPECTION_MAKEFILE) AM_CONDITIONAL(HAVE_INTROSPECTION, test "x$found_introspection" = "xyes") ]) dnl Usage: dnl GOBJECT_INTROSPECTION_CHECK([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_CHECK], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1]) ]) dnl Usage: dnl GOBJECT_INTROSPECTION_REQUIRE([minimum-g-i-version]) AC_DEFUN([GOBJECT_INTROSPECTION_REQUIRE], [ _GOBJECT_INTROSPECTION_CHECK_INTERNAL([$1], [require]) ]) EventDance-0.2.0/m4/javascript.m4000066400000000000000000000046231321356073300164650ustar00rootroot00000000000000dnl -*- mode: autoconf -*- dnl Copyright 2010 Eduardo Lima Mitev dnl dnl This file is free software; the author(s) gives unlimited dnl permission to copy and/or distribute it, with or without dnl modifications, as long as this notice is preserved. dnl m4_define([_JS_CHECK_INTERNAL], [ if test x"$found_introspection" = x"yes"; then AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first AC_BEFORE([LT_INIT],[$0])dnl setup libtool first dnl enable/disable js m4_if([$2], [require], [dnl enable_js=yes ],[dnl AC_ARG_ENABLE(js, AS_HELP_STRING([--enable-js[=@<:@no/auto/yes@:>@]], [Enable server-side Javascript tests and examples using GJS engine and GObject-Introspection [default=no]]),, [enable_js=no]) ])dnl AC_MSG_CHECKING([for Javascript]) dnl presence/version checking AS_CASE([$enable_js], [no], [dnl found_js="no (disabled, use --enable-js to enable)" ],dnl [yes],[dnl PKG_CHECK_EXISTS([gjs-1.0],, AC_MSG_ERROR([gjs-1.0 is not installed])) PKG_CHECK_EXISTS([gjs-1.0 >= $1], found_js=yes, AC_MSG_ERROR([You need to have gjs >= $1 installed to build AC_PACKAGE_NAME])) PKG_CHECK_EXISTS([gjs-gi-1.0],, AC_MSG_ERROR([gjs-gi-1.0 is not installed])) PKG_CHECK_EXISTS([gjs-gi-1.0 >= $1], found_js=yes, AC_MSG_ERROR([You need to have gjs-gi >= $1 installed to build AC_PACKAGE_NAME])) ],dnl [auto],[dnl PKG_CHECK_EXISTS([gjs-1.0 >= $1], found_js=yes, found_js=no) ],dnl [dnl AC_MSG_ERROR([invalid argument passed to --enable-js, should be one of @<:@no/auto/yes@:>@]) ])dnl AC_MSG_RESULT([$found_js]) else enable_js="no (requires introspection to be enabled)" found_js=no fi if test "x$found_js" = "xyes"; then PKG_CHECK_MODULES([GJS], gjs-1.0 >= 0.3) fi AM_CONDITIONAL(HAVE_JS, test "x$found_js" = "xyes") ]) dnl Usage: dnl JS_CHECK([minimum-gjs-version]) AC_DEFUN([JS_CHECK], [ _JS_CHECK_INTERNAL([$1]) ]) dnl Usage: dnl JS_REQUIRE([minimum-gjs-version]) AC_DEFUN([JS_REQUIRE], [ _JS_CHECK_INTERNAL([$1], [require]) ]) EventDance-0.2.0/tests/000077500000000000000000000000001321356073300146725ustar00rootroot00000000000000EventDance-0.2.0/tests/.gitignore000066400000000000000000000002441321356073300166620ustar00rootroot00000000000000*.log *.trs test-all test-json-filter test-resolver test-dbus-bridge test-tls-cipher test-pki test-io-stream-group test-websocket-transport test-suite test-promise EventDance-0.2.0/tests/Makefile.am000066400000000000000000000051601321356073300167300ustar00rootroot00000000000000MAINTAINERCLEANFILES = \ Makefile.in tests_dir = `cd "$(top_srcdir)/tests" && pwd`/ AM_CFLAGS = \ $(GLIB_CFLAGS) \ $(TLS_CFLAGS) \ $(UUID_CFLAGS) \ $(JSON_CFLAGS) \ $(SOUP_CFLAGS) \ -DTESTS_DIR="\"$(tests_dir)\"" \ -I$(top_srcdir)/evd if ENABLE_TESTS AM_CFLAGS += -DENABLE_TESTS endif if ENABLE_DEBUG AM_CFLAGS += -g3 -O0 -ggdb -Wall -Werror endif AM_LIBS = \ $(GLIB_LIBS) \ $(TLS_LIBS) \ $(UUID_LIBS) \ $(JSON_LIBS) \ $(SOUP_LIBS) \ $(top_builddir)/evd/libevd-@EVD_API_VERSION@.la if HAVE_GIO_UNIX AM_LIBS += \ $(GIO_UNIX_LIBS) AM_CFLAGS += \ $(GIO_UNIX_CFLAGS) \ -DHAVE_GIO_UNIX endif if ENABLE_TESTS noinst_PROGRAMS = \ test-all \ test-json-filter \ test-resolver \ test-dbus-bridge \ test-pki \ test-websocket-transport \ test-io-stream-group \ test-promise TESTS = \ test-json-filter \ test-resolver \ test-dbus-bridge \ test-pki \ test-websocket-transport \ test-io-stream-group \ test-promise # test-all test_all_CFLAGS = $(AM_CFLAGS) -DHAVE_JS test_all_LDADD = $(AM_LIBS) test_all_SOURCES = test-all.c # test-resolver test_resolver_CFLAGS = $(AM_CFLAGS) test_resolver_LDADD = $(AM_LIBS) test_resolver_SOURCES = test-resolver.c # test-json-filter test_json_filter_CFLAGS = $(AM_CFLAGS) test_json_filter_LDADD = $(AM_LIBS) test_json_filter_SOURCES = test-json-filter.c # test-json-filter test_dbus_bridge_CFLAGS = $(AM_CFLAGS) test_dbus_bridge_LDADD = $(AM_LIBS) test_dbus_bridge_SOURCES = test-dbus-bridge.c # test-pki test_pki_CFLAGS = $(AM_CFLAGS) test_pki_LDADD = $(AM_LIBS) test_pki_SOURCES = test-pki.c # test-websocket-transport test_websocket_transport_CFLAGS = $(AM_CFLAGS) test_websocket_transport_LDADD = $(AM_LIBS) test_websocket_transport_SOURCES = test-websocket-transport.c # test-io-stream-group test_io_stream_group_CFLAGS = $(AM_CFLAGS) test_io_stream_group_LDADD = $(AM_LIBS) test_io_stream_group_SOURCES = test-io-stream-group.c # test-promise test_promise_CFLAGS = $(AM_CFLAGS) test_promise_LDADD = $(AM_LIBS) test_promise_SOURCES = test-promise.c if HAVE_JS noinst_PROGRAMS += test-all-js # test-all-js test_all_js_CFLAGS = $(AM_CFLAGS) $(GJS_CFLAGS) test_all_js_LDADD = $(AM_LIBS) $(GJS_LIBS) test_all_js_SOURCES = test-all-js.c endif endif # ENABLE_TESTS EXTRA_DIST = \ certs/openpgp-server.asc \ certs/openpgp-server-key.asc \ certs/x509-ca-key.pem \ certs/x509-ca.pem \ certs/x509-jane-key.pem \ certs/x509-jane.p12 \ certs/x509-jane.pem \ certs/x509-mary-key.pem \ certs/x509-mary.p12 \ certs/x509-mary.pem \ certs/x509-server-key.pem \ certs/x509-server.pem \ js/common/assert.js \ js/common/test.js \ js/testResolver.js \ js/testTlsCertificate.js \ dbus-daemon.conf EventDance-0.2.0/tests/certs/000077500000000000000000000000001321356073300160125ustar00rootroot00000000000000EventDance-0.2.0/tests/certs/openpgp-server-key.asc000066400000000000000000000070321321356073300222460ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) lQO9BEvsCPcBCADNgYvZWxrTI6BPWU8yhTNTUocIz59mZGGNIcCuLA957ug8aoC7 i0srCNH4AGeKqNm/I8oJ9ss9z81vK4m8LxPiey14omrg7Fjr7P7Ef10hOFYJr4xq PMNXbvbil2Vc+KWeHIs1q9xbLBQsJuPS7T9381iJYO/JVqEyaKyvNUPTVefhobDP rgUikzX2QHQ9VCpQfW3wJEGv6+/cCxkgRHbf72FlDsYUlUWTKEIpwhcVnBTfDVhg G5PiXXvV8eVI07pby0VYzRVcG36rDzvv7gaRzgJ8tlbqgCsrDZZum+1AnCz33hEY wsTH9+FYmNg4550cGTBsjqy5uEwjx5VmMnTfABEBAAH+AwMCsct7qL2AVKlgl3PC uE6x1ag/i4v9UGgusikxUSAdhTD2p1N70kmzPzDrCzyTM5Cga+vpGmoZtff9I/wz EFR/6pXeYvsErg2xnskkISJ/HBGPQfSKONQqKZE6MqX4P7l9af3ABKgiXWPvwZ04 xlcF3uCXNNns5ZwLuhtQ9ORpyKHSsJLNXPEsTsoX/E+SsL9TBm8NHvm7bBR5yHUy 2PYvcz/eR1ZsjRXcga3XeY0E5S+FcrUruAiLsmAgeIIVXRG4ga6v94XhijxkSjEM fWyRIrkt0So8n3Vz84DcU7e9ikkazC6GRAz6dMVFsR5wLDK7ybG2a35OUBZ3aTow PggOj7JQEAuki/10kDSpHzsZ5K8NxZ/MiRM7MbLZcSWRTbfAYti9jh2kObXV9LfN q2GmYAcaaFuQXfjgsB6d8DAs5TozMIMUdL8m+/kGXqB8fokyY/tlwexX9DUUAxnG 4A05JowyHMwJZTmfyDfrPnvpgBN7dB+jSlvYsJUqOWJ9xYlfAgBqrIZBoHAy+APY Ghrt870gvr4ZO/D+BJLe8L8COKMEHpKPIDB7rVZB6oHWGtfnCyosDrpxYV7e5z4h lbJOAx7qlyU1kRbIKY189Vtwd3L5BX3eEC4G2zQxOd98QjnX/2kq0i1Xfjo6UtDo jFvQXsqi9Y7bLhFbLmMLUFe5hpu6b0Lcds974MjQSLuPqB5Vt3Pv4de4GiGZyfqO DKIO3ynBtKdtVvyGNbWDRaQkzz1pPlfyoe7aC41wZBbB5Wn2e1/kVxridTXUF48v bKYb8hcGE9r6Ga4sp/3Lqa2Rn2fybueXl9SkZw7rG4QJg0CqH4hg/+x44RyOzBu/ 9RyIopdg7WUn967m2FCKwOKjNk9il4SXT8AnInogwsNlTz+ZoS63d+tcvEaYGEse tCZFdmVudERhbmNlIChFdmQpIDx0ZXN0QGV2ZW50ZGFuY2Uub3JnPokBPgQTAQIA KAUCS+wI9wIbAwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQIVAW 9EKtUmLeqAgAtyjD13ssTvLsONxE+4f7A3AQNAvzq1w1si1tvldkkKvNNFwnoQEs gIR8QleYpkjdG0CjQihOxyKqQ2sH/U+gXJIJY+MCj9bVU1ZRsUJEyFHARx6AzLG4 +UEIg6PS07sDDXSKfYLMXwhmqQRR5c6nQWnFA/dn8+RjzzY/+/Sb1zsK6WXXuKTq 4L+158R8huiPhJz4svoXZaZCTCMzJq2yFJvW4/CaWQokvnsmOEHdCwtmREGHQt51 IqPt94oKMiuvQrkZMHsdL63TPOxf/pQti/s+7reSpK4PTJ7SEyaKrqF2vNg0oN4c bwMXAj5/Nvss813K/NgA5nsCtm6235V+ZJ0DvgRL7Aj3AQgA4lZq1je65+3Lbd8z qZV9wztinKBLOMR+e1sE8H6SObueKcEp7sfaWCC0kELDOU8I3hX/+TG3PVcfpF2e gRvsVDttX9Bxic41quDdiVYMB1oEXTuSRWKMJFiY5GjcpI10w51HUbhF1RYcKMR7 60sBO4chBIIP0JQzVKXB6VUetDIphBIE/lrlJl5xXUTw+D4Hu97f/ZNbYxR1NckH J6Jsa/8BxJFiMHNnT87MCiyHN8Ds2qOrTbA0mMNMKkVpYgk4+piqY9Y4BJCuy4FG k03ltCBwl5OW0qBX6B4MxPdfTpNitTbsrlVEm/j6U1ZouP32pOBjeaCRivToRM+7 6IcHGQARAQAB/gMDArHLe6i9gFSpYAHH2Xhs1/RUumPEMwsiOfyAGqtCgTKPtGkF UKVFN0nN/5N3CBQQeAImrBK535aKQ00nyQFYP9HC+4Y3IbLxARSDTBUVXtJoMZNP XCte6SsoOaGD61MTxnokw/PmkW8gpF92mcNA/d0D3eJ7JiASMuTywhISkfdEvC8R mMzSJ4WD7N9UDfISUt7u3ugBHKBiFiq2pxxK2y+oMkyxrbQTsOYylwUjhptXkqUR 8QwCJTmfG57665IJOn6Uf3ekGFZeYO5Ng8eGgprLkoe6kVaRCEVPt9MTh25aXHc4 Sw3M12Ra4fYkv4DsxoI7z6VAznyILQnBMnz3YsOFJiydQyTaFrxnfcQPuVIziywF MAzRV00KLwpIAyeBULwAC//hNH9QDczrwpe6BnUeaqEhVh5G08BOZubCqs4un7Gw rfgYd/obCdpkOR6lEYfgfFtL2/zEN9zfyJrrS7MlP8goXErkQtLNBjIav0tju2Ay Q5ywbYdvgDhoJjikKb/4uTMPpfbfdgtIrTdFq5FdeLPqvdYqnje5vJtYgNrQM/M5 316x+iXh4mqW5xjd2bJkFuOkzFS9LDvetrDwKiJ4Mu+ktvE+7g8gKJxNJTfBKqFG 1RJF6AF2qn4WevihJnttW62rPRTobiP8goedVQcmAXFCprD3+cik5NCvBr+dQSqd pxjSkCGGDryrRqt5nb31q5gO/inFlLI5X2ou20M6uKQWWAsNlyB+RUFe1hGAfY1T vOBsu+M8KEDfUgzbq8Nn1u1RVLEy1R4iX3aPlK3usm9jOg0jt+H50FmDmGdDth9F QjepsF/TOHFa/I4FaK50+FCE7hAtVQnwxHConNgtBPCDP2gN+5teTux5HVUFcOqY RPSuNasDXHSMc1rjN2hJC+R5YG93wzBPvtOJASUEGAECAA8FAkvsCPcCGwwFCQlm AYAACgkQIVAW9EKtUmJVfAgAtzCtb/QwOKt42u3/KvF7gLRXei4D3QEZdjFXfi1U aXNmS7wiUS3p5YVZsnGzj4Ho99bhikfSHutIPZsp5F7tUovhrtuhBA532OChPksK uqQKjc36vUwaJzwwKRBsBQPtN5qYl7e12xHvJkE6A5l7fZYfgFbDZq/QGf/uNdbR 4H189v+h9+rSY1+cPbBIJtM5PaHjNzTC42rxBNJ4HgTlNy5ZpU05nXFI9TOCC3nw Ol7TJPYlBpTfRcZrd+EyTrwwXM/Cq5JCD9FYK3/7v0YxTPni3JNZrTsaZoSgllqz r/vTBzrbKLwk4vva6+wO/kK1NHI7fiSWXHpIeCeRLZCrsw== =Qfhp -----END PGP PRIVATE KEY BLOCK----- EventDance-0.2.0/tests/certs/openpgp-server.asc000066400000000000000000000033201321356073300214540ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) mQENBEvsCPcBCADNgYvZWxrTI6BPWU8yhTNTUocIz59mZGGNIcCuLA957ug8aoC7 i0srCNH4AGeKqNm/I8oJ9ss9z81vK4m8LxPiey14omrg7Fjr7P7Ef10hOFYJr4xq PMNXbvbil2Vc+KWeHIs1q9xbLBQsJuPS7T9381iJYO/JVqEyaKyvNUPTVefhobDP rgUikzX2QHQ9VCpQfW3wJEGv6+/cCxkgRHbf72FlDsYUlUWTKEIpwhcVnBTfDVhg G5PiXXvV8eVI07pby0VYzRVcG36rDzvv7gaRzgJ8tlbqgCsrDZZum+1AnCz33hEY wsTH9+FYmNg4550cGTBsjqy5uEwjx5VmMnTfABEBAAG0JkV2ZW50RGFuY2UgKEV2 ZCkgPHRlc3RAZXZlbnRkYW5jZS5vcmc+iQE+BBMBAgAoBQJL7Aj3AhsDBQkJZgGA BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAhUBb0Qq1SYt6oCAC3KMPXeyxO 8uw43ET7h/sDcBA0C/OrXDWyLW2+V2SQq800XCehASyAhHxCV5imSN0bQKNCKE7H IqpDawf9T6Bckglj4wKP1tVTVlGxQkTIUcBHHoDMsbj5QQiDo9LTuwMNdIp9gsxf CGapBFHlzqdBacUD92fz5GPPNj/79JvXOwrpZde4pOrgv7XnxHyG6I+EnPiy+hdl pkJMIzMmrbIUm9bj8JpZCiS+eyY4Qd0LC2ZEQYdC3nUio+33igoyK69CuRkwex0v rdM87F/+lC2L+z7ut5Kkrg9MntITJoquoXa82DSg3hxvAxcCPn82+yzzXcr82ADm ewK2brbflX5kuQENBEvsCPcBCADiVmrWN7rn7ctt3zOplX3DO2KcoEs4xH57WwTw fpI5u54pwSnux9pYILSQQsM5TwjeFf/5Mbc9Vx+kXZ6BG+xUO21f0HGJzjWq4N2J VgwHWgRdO5JFYowkWJjkaNykjXTDnUdRuEXVFhwoxHvrSwE7hyEEgg/QlDNUpcHp VR60MimEEgT+WuUmXnFdRPD4Pge73t/9k1tjFHU1yQcnomxr/wHEkWIwc2dPzswK LIc3wOzao6tNsDSYw0wqRWliCTj6mKpj1jgEkK7LgUaTTeW0IHCXk5bSoFfoHgzE 919Ok2K1NuyuVUSb+PpTVmi4/fak4GN5oJGK9OhEz7vohwcZABEBAAGJASUEGAEC AA8FAkvsCPcCGwwFCQlmAYAACgkQIVAW9EKtUmJVfAgAtzCtb/QwOKt42u3/KvF7 gLRXei4D3QEZdjFXfi1UaXNmS7wiUS3p5YVZsnGzj4Ho99bhikfSHutIPZsp5F7t UovhrtuhBA532OChPksKuqQKjc36vUwaJzwwKRBsBQPtN5qYl7e12xHvJkE6A5l7 fZYfgFbDZq/QGf/uNdbR4H189v+h9+rSY1+cPbBIJtM5PaHjNzTC42rxBNJ4HgTl Ny5ZpU05nXFI9TOCC3nwOl7TJPYlBpTfRcZrd+EyTrwwXM/Cq5JCD9FYK3/7v0Yx TPni3JNZrTsaZoSgllqzr/vTBzrbKLwk4vva6+wO/kK1NHI7fiSWXHpIeCeRLZCr sw== =/enc -----END PGP PUBLIC KEY BLOCK----- EventDance-0.2.0/tests/certs/x509-ca-key.pem000066400000000000000000000032131321356073300203700ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEArQfRjV6V8YZjINvD4GlO8zJaVZhh09Z054rUFc2muyrB8QYt bYWb6vuDEifNo71BeGiGpW2nzpdfUbpKoKqb/UcLjauU/1muxYl5cVCzufJco/05 uY2xrJB8dPkLmjf1hQsBHuII/E6miO0C3n2IbqXXZd6RuBC8GYhwFkCHFRUEMik8 /tMEHZm3mtdXpbjCqMjg+vJiCxhRdScgaZMimjXscn9lYhgeY6HypiYZe/SoUeYs 4o1AiKu5HjNyVZkWDjh3CDupl552owAXuqIe6A32N+Vg5nULQN0GW58LZCQqbh9E rDUQNZsiBKLq2zfa3wjau17sfaoJN4uKJCYWkwIDAQABAoIBABItDb81ngymsEe7 A4LRrSBw5qUH19GX5+SMDyanLrTfPfY3+ZJVWavqdbn018OJKup0wx13kmFqQfIP iVgCnquRc04+JZhZRGRYbPBmqNUx92H772dlfLItj1VTn/ti63XpUlnQfAnSUpN0 SjvdoJ+hk38498JdHP/q9tNE6Ku3GCbvw+6G92vdgz0hnf2Li6+w2o4B/BN6Ts8R i2SeMUCG6ih1PIik4u828Juv5onG5RQ8P0MseoZdYAjS23rpQtzc9DyvveYie9Na qlU/EhviWHCDDrg6QLgugQjNzSuLwtxiW7ky5LaRM5EB2dykvUL4M8QwshSJ+Vqb qls6HmECgYEAxbSDxKvPZrYm3J0oit54f8/9eD2uIz/kQfNef1qR3F3UAhMYPYih vXHmzdaF6I37PU+0u0Ta2oNJ3eIEnzOa3PFviPz739zi6YwX7lPizWwY1vSTNRDI kSNJ91NdYBHamJZQzRyx/svDpXfAU8xyhZxeVEWXE0b5S56astvuaNECgYEA4AzH I/nPGRDYXnf66jYLhd8uz21VhW4Df1913Gnj6iyPVMjoz4zB0dlnW7LpZtlFO6Qs 059RYR5JweqgOvoNektvaEZgIV5l82GoM4TqNIfNi1fEYp+n9Gda9kR0RI0xP1Wi qHbPXUSVrMj5cICZaFwoi60iHQUCDscAP+cCIiMCgYAc7VWeUBkviRVUkDYfY3+0 C8WlczjodW+7apV4AtN2r8/WYd7ZGgW4R1Mi6PF83lFVXjUudVukPzNuoKRWeuF8 1GKiY4mi7pnQ3CugNmvn7JoR0YU8bXE7MzOeWXc/GD2ot82oxTsR8dR1gHkGidJz 1DZFFE2Ph4HwW4t9Agr3EQKBgF8QLOKv2NZa1mNm5vi/S65Wnwb17gZ6QlOmDl0i NMkkdQBVaqq9n3NJmlhHFk9EOuLuavKzs5TQhB0aTzgW5ucP4MpOqfUel4Vn1zVc 3P7C7EyGRdZOxbWmsOqy6t43RkEINcbi+mMpeZRhhazdc7na+H0jTckgWdopR4rV mosfAoGAME5bDScpmkSFUsD0a6v/boxSp18ciBqJHDIJj0cl+uvpu10Gcv6Vd3Cs WprQk0Pj5XYVoMqQVeKJ9RUHiRTGW+1qsAkg+Gg/hzvbxv6/2GNAvcZJ3AElP+JG hLEokktcW2vFmY2DL3g9DHB+YcwwmpI96sfu5RsTV+u5dSJRlNc= -----END RSA PRIVATE KEY----- EventDance-0.2.0/tests/certs/x509-ca.pem000066400000000000000000000021061321356073300176020ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC/DCCAeagAwIBAgIES6jTBjALBgkqhkiG9w0BAQUwITEfMB0GA1UEAxMWRXZl bnREYW5jZSBUTFMgdGVzdCBDQTAeFw0xMDAzMjMxNDQxMTBaFw0xMTAzMjMxNDQx MTBaMCExHzAdBgNVBAMTFkV2ZW50RGFuY2UgVExTIHRlc3QgQ0EwggEfMAsGCSqG SIb3DQEBAQOCAQ4AMIIBCQKCAQCtB9GNXpXxhmMg28PgaU7zMlpVmGHT1nTnitQV zaa7KsHxBi1thZvq+4MSJ82jvUF4aIalbafOl19Rukqgqpv9RwuNq5T/Wa7FiXlx ULO58lyj/Tm5jbGskHx0+QuaN/WFCwEe4gj8TqaI7QLefYhupddl3pG4ELwZiHAW QIcVFQQyKTz+0wQdmbea11eluMKoyOD68mILGFF1JyBpkyKaNexyf2ViGB5jofKm Jhl79KhR5izijUCIq7keM3JVmRYOOHcIO6mXnnajABe6oh7oDfY35WDmdQtA3QZb nwtkJCpuH0SsNRA1myIEourbN9rfCNq7Xux9qgk3i4okJhaTAgMBAAGjQzBBMA8G A1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4EFgQUULQ4Y+C1 EYvM+yg+S2BFzkWwP5UwCwYJKoZIhvcNAQEFA4IBAQBmvfbi7mP5dleTJ7/i9WqQ bO6/0PrhOHHTKo0ypWwxD5v8wqwDvy7C57JGcvbYp7j3NfE4IFnB0FZwFVs/awL6 F7ZDQjR3dL0AiMbSR1fbH9Wet84JAHr0NlaHOr0ZtE0JRR/+Ey9aF1GVjRgr4PJW Q4bGiHD0J49m3c8DNH70N3niMyDB8yVHQy0Fi2vS4EQYlM45pB3M36rJLxSJ3nCm KygzInrJ6KAdIEhYlfOXomSmK3N+BsZDBCzFBroNaW0V9uR8SAfp/Ri9dAbnP7S9 0iFx5Q3eZO6aR/fbWTpiArqkTekXOS+6LiU6IUV3dF56xsFsj/I/eJrU8npS/UVN -----END CERTIFICATE----- EventDance-0.2.0/tests/certs/x509-jane-key.pem000066400000000000000000000032131321356073300207220ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAx1+T+gtiqkGGdvT+pYvt6KeT5GE4A8Te1eV0AT3hDQ/cTqSS PvPoyHO3fJ5TIx+eQ5BO5iaKQbWTRmhY6+tAcvg4WGRXAF7vQfONPUsp/1BDK6Vk mnLglZu1rpdPn0wNBgrHtTFOkta7F/vrAbc7qWi13Hgdx4y8I5W8VGKpjosJ/9cd igtKEiJjNhgn+LycFO+iaY7nyv7umNcszgiEgv0a2GC5NMVxP4HdGTW5EFsxkcm5 kobZIrI8joPvqUW5LjOz8Axazyz7668Dhk0Jn42njELOzG5F4I5b7wGk9lvO7tdw Q8CPhyYrMozVikfzEJiMT5Zsf9a+KVjNVPTd/wIDAQABAoIBADGnajFCGKaSfyyt Q6ZzPSNOaRTd6Zd2tpDK/qM4iA46vwGPFyU6leUmfLI6tDXxL/dfv2aufoxy7mUA o/TNuFVp4I6THt/mxOvF/o0fZSxDDxeVr4KPzcM07oC3Cot/41Z35NkJmKoVdK86 fIMG4YMFoxVvjOsf79Nk/Wx+zf/DzG+tbule1HcT3+I7xNdb4tU17PuInNleL11F 0yW6UZkuOgDKO+vf+btPZ6ilsxnNZ3yVnw+WMAKHN70/nuWeuyWGKDsjFRZSgEs8 vzIbLW7wPQ5y8JgQpOsorlh+WrOg9AGb7PUP5HMvRZGAHaVnauQXmO5E6bH4Kkbe 5ve2D0ECgYEA2Otb3GjCsYKWBTT1VsY4UPrbwiTw7oNg1JKOwKIvKBkyvOrzLbAX nH/XqpK211Y4s3Vgp9kmSFA10jw01fU1sM4E+ovbPnYusyLpiFRnhhcAUzs17g6W /HHab3uF6JNTSyIeYZL8uP8jF9HWp38yf3FpjTKJ2yg2Xk6OP1SCy68CgYEA60r4 kCqisH8bFGlpORqqRyHYXdhuygnuNLJKTE7FU7NaVKhhtTSgsZh3rbWfGfKXKfr0 OfOD5KjXKSlR8+J2eIvTykdf8NE0Yrrz7nYmI/uGBUG2CHBHMeiztjpBfy/QRsdc Imfp3VJII0UEnXDr2iR2J/UmiqWcexkKJD+LFrECgYBMb1qWJt/71qzwNsIAiqZL eqm3FndDRE35eY8Nt9hwA7kKMqvvvG4FelLPSxVceYhPTBv76RuBPapJY2emwNnj bCT6+A8QSmqYOsQZgsxbxn0Z1vJg6Qkw+RlkUR1VX5xpDCOydC3TLiv6d7lwmfR8 TcVIuLc0302RG5MqI6hXBwKBgBJwfapOrRFH5Mdku2pOPKn2bQ5tt8D9pj1a+5ef VLeKhrm7uGyLA0zntflXEnDmIhKYKCuieeOhl7sSEeobBU3NJ3vivBITUxeprxQx Diwjug0PaUSgB/RRWEQfrKJbDOd/GClaG2nM2PpqnkH4Z4Ng+CGJhMu4ztqWy8Sr disBAoGAQmTOPvc67prILAzY/GhtiqMMNoBxMnJpfqXSk4/fvJZ6jdeTAKNLFd1g iGkitLj3NHO1NM1rsOqXx9WVIOeCYANHYt5rO724HJb+VEifE1JqHLSI4ccuXn4h TqEXaiCc+6ZVIqahL4BNfnkiYMg/eo7IZf9IFNcdQ0y9KSPribE= -----END RSA PRIVATE KEY----- EventDance-0.2.0/tests/certs/x509-jane.p12000066400000000000000000000046741321356073300177710ustar00rootroot000000000000000‚ ¸0‚ „ *†H†÷  ‚ u‚ q0‚ m0‚÷ *†H†÷  ‚è0‚ä0‚Ý *†H†÷ 0 *†H†÷  0%QÇzz£Û¤€‚°Äd;‰kÃÛ‹¶ß¹Œb*Ž•›áßA¡*9ù7‡X:Ÿ6†tBŠVBçÓæþø‚ô9L·PŸ=”4D½ˆ Ö’ÆB û‰†aGÁ<²Ñâ0uÆ¥@½Ü"]㉓‹i‡`›Ì• ¼¢iœÜÎàaGk§D=þHÔbweÜY‚òŒD>ê?‰ë¨æaå,¢SÎJ)’ÝS%~±Ï¾šÔš+tG9­$fã·ÐH|ˆEÓJÂ9‘<½(Õ·EX¼[qk6ða¹¬ÓÞÖDÅ7{lÕÌUá^’N3ä#uËbÑßn;ˆ1­ÿÜ8éᦄ–®cõÖ¤Åô‰øš×°80òšq­)aCÔ†D¦¿vÂ,Û"þŽh¶ã % ‡–Pòƒ™ß’wñ™õQå’ýRç»a>{–G€z†cb*×Ú?^Ÿÿ–V¨mŸÌüº±¥¾“±DÍÄ{éȰŠ»Éá&»FùÈÙE0áäP ¸|ÅQxÑá³æ}ñ4Á ¿È¥ƒÈÔ([\šÜ:p]‘Ä:}o‰ÁZí/)‘pŒ¶Ýý°±š“L$/rÕº²Mœ@ 2=g‡gé=£‚?ytdü@Ï/±änVñ†ã‰ušÆ†îâ8ŒâEyò>’³ ¬ß+)n’ï~Á¨/$ST#£b¾!'OßýÄCé™í=”Ù4dóyÒ%y²w “‡^üÕR¦ ɾ¶1y‚Sºaš¥H×BÃİæ»)ÃJÜ`Í•ûh¼Æm øé5r¥¢vVñÑ*ÕšÿÇMép¦ÙMûf¢è$ºú3ЕJ_òpvh% í§È;™k°´zK·@´Ã ‡´‚Õ@q£S“,5ÔÚõ~ÉÝw_5ààõ^;M_Ný[,ƒ×Ò¢ú:"KÕxý~‰ŠNòú¨¢}˜ô†Î‹¥ù>ôNí›æ7IõÒŠ7‘jcR R/F”ž…Ñ<ÇßJ´â—–—Dïðô‡âÜRÛé¦f†'·x?©×_¢R„瑉ú.öÁä ×…y”qõ÷åötÖÅj=ýµKV:¥–ñ¡½ɾ¿u`Q…Õ|H+¶6 „>5óNš¿`®Œ 3¸G[E¼}nÛ@Œ+(uŠ-eÈ"(Q•k²PWÆuc®š^&w!–ÝŸ¡g&~Än(^žŠGŒü›9òìJ×ð„\2ŽcfŠ—Ä&âdì¢WÀM$Ò_½Ò GnÉJ‹ä6ýƒ‚¥ÚÝk¾à6ìÕĤeÓT]Ú %­²#w™r(ÈÓóÁû» G&€mZ&TGévc=H\6ËÍ·°84Þ{š^²†Of¯‰¼mî ÚË/O쇊¸¢*Ÿ8ÇmÁsž™³ÏíÜYÈÌ[ÈFúhd…AK·ÿ©NÜðºs†ý5ÊÓÈQDÎ9Æ·+eÂP]ª½Nû šKjСÅ3ÂÀÌZ®ÁlSÅ Ýú^YG÷ÁìøÜ,çØ­lL;%ì†pœ`/ZÙÝ[ØîãR~–ÓbÆ21]»·m²ÇŽXýºß8f(ZÓÐPž¹Þ)Tâxõ¡ðÜ'÷W:šÔl`~SH•áTSéÑð'¸Ú!º1Æ“•(f,9..kÌí­äQ£™™{İ“¦%;.w•4ôœèÝâ¼gUëcÈÌ”€3FvàLͯròÕNµ}&|FÊ}¶Ëì6R }Ò‡Hæk9z:Œòø_hém*u/SôwÃÀÍ/“/ê&ãíl¨'Ï)ß²ö"Ésb¿^,öîgêã!pô†/¦C¹¸2”‡CqÕ–Í mu›ã8¼fòö%Ï5÷nïzÓp4?l¤t‰´,íàg{NwÙgÉ@°T0Þ6ÁC¯0e¿š B¶¸Þ<† ×°”ZIB“™Øü6 @±QÑü;ÏÂøb—w‡›Š5XфÃ÷…÷n€©³˜‚“ëÔ õ¡®S;,í+N,¿lÏröçËak£”yV‡LFÏèõÐÇ:ãŽ_>9«ú{hö«"ÿm¨G 1ôä¢âæ3Ø9gZZÇá²ìê>m%h&8€¶²õFFí…/ƞûJ­˜ê&ä®]ÊÕÓÒÏlK¬]ÑÓÈÆþ’ån¨øPró²x1ùžÄ–*Wý•ÿm ëÊ”®v:kµÆ4mUGyA\¹Wë9¿<9Wvàtª l«äÍÈ(!Âß_¢º6«5ÚƒÑÃ|‚°¥91ZD„…ˆ×ê€Ë_ +#Ô• #Sv1Z0# *†H†÷  1ÈÔÓǦÞßÝw>Fï TCÝ Ðö&03 *†H†÷  1&$Jane's certificate0+00+<vz±Y„À†\Ì¢ Xú®û¥7ÔÀ©bíÓEventDance-0.2.0/tests/certs/x509-jane.pem000066400000000000000000000021631321356073300201370ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDHTCCAgegAwIBAgIES6jUuTALBgkqhkiG9w0BAQUwITEfMB0GA1UEAxMWRXZl bnREYW5jZSBUTFMgdGVzdCBDQTAeFw0xMDAzMjMxNDQ4MjVaFw0xMTAzMjMxNDQ4 MjVaMA8xDTALBgNVBAMTBEphbmUwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC AQDHX5P6C2KqQYZ29P6li+3op5PkYTgDxN7V5XQBPeEND9xOpJI+8+jIc7d8nlMj H55DkE7mJopBtZNGaFjr60By+DhYZFcAXu9B8409Syn/UEMrpWSacuCVm7Wul0+f TA0GCse1MU6S1rsX++sBtzupaLXceB3HjLwjlbxUYqmOiwn/1x2KC0oSImM2GCf4 vJwU76JpjufK/u6Y1yzOCISC/RrYYLk0xXE/gd0ZNbkQWzGRybmShtkisjyOg++p RbkuM7PwDFrPLPvrrwOGTQmfjaeMQs7MbkXgjlvvAaT2W87u13BDwI+HJisyjNWK R/MQmIxPlmx/1r4pWM1U9N3/AgMBAAGjdjB0MAwGA1UdEwEB/wQCMAAwEwYDVR0l BAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4EFgQUyNTTx6be 3913PkbvC1RD3QvQ9iYwHwYDVR0jBBgwFoAUULQ4Y+C1EYvM+yg+S2BFzkWwP5Uw CwYJKoZIhvcNAQEFA4IBAQBIm7b+8v1l66cDjPVQOSmcBCMeNhRUxEaLektbeMS/ nXr//oxL5h7uQrIATnAspzwyOBMQmjuq74R7KJyJvVIcfFt239yqY+a6OkBcREr+ inmxu3FATzksuv69s2mlY+/t6D0rRb7yT+qKttG3GqRlE2POrp5sq76eu8a2iy+f YsBnJzrs3DK/fV02sPSEhWryaEKoOI2+Bd15afrkIwRqQbGYdnmzmHc6jOiLwVHC yqhtGf3eVyZ105D4dOFfjcVWwckN2V+zTR0Z0MWqpR+ExuJCz3uJzdPneRTR18ln v9xTF16DRZL1tm8Q1Uye1Hb0st37Y6JhPAJf36Mq3rnF -----END CERTIFICATE----- EventDance-0.2.0/tests/certs/x509-mary-key.pem000066400000000000000000000032171321356073300207610ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAwrDOJie8Y0Z966Nh7ITyf++Tmpw9OxW5lZ+hhw8vYb+M7fCJ eXG7xk7ABsXZjja3wOmTx5TpsLf6RlA9gmRo8ABp6Re2AsgyUrnJD5ijz/wFQoJ7 jXtczGpaWpF9A6B9TGx82cc+TX3+z9eKJC/NPU7SR6F8ANfWHy1Z9rwteHk9/pUG i7B4Wthl4IXB9zBA/WIwa6LlFjhhh+OYkV2jrOyZGbP+bu3hZi/cd15s1oz+WsUB BJqxxrEJStOyRnw4IsWgW/x9RbEGm2IatNwbsQ8xyNaHDAxAiW/tIYeiIAQPNitv mWfpM3+8bAhEeF0YMFJbTJYFXVVol8dlsuCt5wIDAQABAoIBABoYulP2+OjoZrHn 6+pY78B3tqCqLLL3eIbhJ4pZCLrg2DJ/ZDi5Jrha5KEucrj+BfzVJ7qVmrWAvmAf xVRDF/suXa6hlQN5HksPHYT6VYroId8Gan/5IePspZaE8JhEh5xvi6oNa6vWJbUV E9lB5xA+uS+3f6MFS22ma0Fde/gtMadGYinQsyS2CB96nMCTC2P93fcDOSHAeS6D 8ejI7SjtQkytd9LaRdz+CvSJopwDEoKS4rtTg3P4BjCrlnJX0XOVQkWyFGGwvreV fucvE8Q77EPGZ/RlyunsxyX8XyhgiXwpjJ/YdSOJ7Nw/cjkqiDWYKB5M6YqVWYCm cDO/i9ECgYEA00/RpgFoqn3O9lUxFzEbT/x45oWLVjETQyIAElD5qVaqsaUCa0OQ YIz04KsnyGLmYbWI69NFrD3VrdR7dISYjeqHKq+xYwPbLpUJMBZ5qFjYZzfvFZ1A +lg3SrBRRx/+s5iEQdTW6j8yyXkHTKNJWAvUcXxEtLynqq/uc8pfd5MCgYEA690i 4Y3oYsF5MuykfThIxpnyOgodiaj4Hb9cGa/rqcYwK29+aCqV16xf1vIZkH2CjuGn P05UpjK1uJQxN5TUG/eqK+qvZ++pa4fJ2v+VFy4GonnS4H9al/jZy69CXgw/+bzX 5nJV/AVTwazhRIPxsUHWKajZZE2jRIpN6g3QPN0CgYBfPjPNcgceCVSWXPGidWTN GYvYw0p5T+dRs+p6Vvsgo0qOhURg2nQhwIbDGQH2ZPVYBv2u5Wwa5UgBcKswam9k T9gamlQGv/8zsMMSZJ7zDRCE/YYKpPVboRVS2cPYnggsJMs/7NHthQy8GIv8tABj 6fkYKZ3CJOIhoeiFUirTdwKBgQC3joqF1MWEjaM/4XCFzxa8ELS1PIgOSQSHShRo HUOfBZEZTJSohnPEu+5+Pj1s+HBxt+VL3C7+hLptok7HPbjw5wZ1VRgbICXOfIe4 Om2R84zQR1S4yAP9/3moDYXt4USsm62+Nz75ej/bmypZAwJGe8Eq/K/qYo1sWuIW A5gKtQKBgQDR4xL6JM00WCndcgu4zM9/J/ACTkIr7gxPR1wnfYvLcGhKqHHAAJR/ ZK5+ZEle/UAwPGwZrt9qIdToWGiJCsbnZNA/1a/NK0qciGrVXLHdAfoKyqav5bCV LxWaYnY9mvngb8fyu5xgUCr0wd84EYCuB4p/etV290ABRavIpMcfTA== -----END RSA PRIVATE KEY----- EventDance-0.2.0/tests/certs/x509-mary.p12000066400000000000000000000047041321356073300200160ustar00rootroot000000000000000‚ À0‚ Œ *†H†÷  ‚ }‚ y0‚ u0‚÷ *†H†÷  ‚è0‚ä0‚Ý *†H†÷ 0 *†H†÷  0Ø8΋¶ „€‚°{ì¦ Ñ‹½].•ôå ?¾4Áª±¾WéŸüš©)]TU÷·l«!ºè”^<à´š~ï½Õèð28Þ5“V3@ÎeÄ&N¦ë°Æ6èÆ 0MÂþW{ßå¡pMÕm;([ºè!€9±ë‚¡ïýk×Ñh»:n³óq=ç~,’q¯ÅDÊñ"•b–®ILj¯Ì„6¼äl½²=Ķ”tÎuWPAìs4HDoL\G&Ú³HRêÒ#oÄ6eÁº~1leGœûŽÍu†ÂóS»w©«ªÈÀZÉPÐÇÞ²KøÁý>ÐLq¿;N!öG©®Cvå-&MÑXª"\—NÙ©°žªì…Ws!Í©_ˆ‡l C+±™Ç“"‡O‡<ŠZžó8>µVyÒÈj#ÓœäËÇ“'Ÿ˜£»gÉX˦´v+¾Í7#}Je‚¬ -&vd~q,¾kN$ÖÛnÐ&n:\®¯æ_QaWçúÏx|ûyXéž±Rã¶·r^‰\ÀB‚Ax VÍfŸÚ$Ýć¹úGúXñ“cóã»í©iéÖÜÒ'̵Â6–¹ Ï”ÉÀîi†÷#@7ت1\ÏŸ›!ƒÿj¬QÙlMpz¥0v¥î„Üä¾u”&ä ‹véNߤ­Ohû¸ Ë<ç‚!O¯ö› '–1š¡ÒÞèŸçóJOoaöYj Òo–Ö»_¤"é¼ÓÐÃ<3¦Ùƒ Ì9äFu²{LÌo¨7ÊÖäQð³¼a§èGýÈîQã rs ¬Ð‚{,¹j«$Œ~' ®8¸˜­Þ¬žsÊo¶3ü"ÞÖGhåñ*ÌîEßì6[Äp?—ºŒjø•ß3$Iƒ¨ï…=ÛƒEŽ¿¶'°pèüëüŒ­HœûZûº)ÆEJÒv§Dx ¦ãkÕñC äq>rut|NöåÈK¼=lìö <§W{>êÉ!Rã"ÍÑúØÉÒ‚0.)_”CwY—[azEí[iÛ$cÄÌŒÈàl–®JuéÇ_*`½ïB¶‡ÞdåS‘Æñ\zt7Äœ«ÅóÅ1¹)°sÌÙ2ˆ#hEYJ®]ÖýR¨@‰ †Vj=ʹÇ×08}qŒ}ÎònÆJ"i:®ˆk}¶·5Æ4öÖ"Côý#½;»!—<òì:]òƒ¯dSÂà$3è­b#69‚?æï­,9®)ÉÿØ7˜¾Vé0‚v *†H†÷  ‚g‚c0‚_0‚[ *†H†÷   ‚î0‚ê0 *†H†÷  01É›8GKa¹‚È"F·ï£'Kl[m+6]S[ºQ¦V$U²o¿ëš]j|Š+8êM>yÔӬͪcí–’C%`C†Ó5FU¡'M´:³ÒGÁwïn´¾e¸dûáHdDë"îñøSð1£• ú{ÊÝëñE’$óÃxç×È¿¸~Wá¹ÏÁ‚ÍÌþgŸç¯OÐ`¡$Bì§O½ë×õþ ¢JFØ›ýk`;-3B#é+ˆ{nNÕÛ»¨YØ ìæ>k‡Ç¹i{ˉš¼H燺Á5Z;rj”õ!zUÆ’óp.¡:Ù„/ÞUj-¹Â;é•÷ëXÌ'q”…Ö°Ñ2aï0’À¸ÝžŸÖÕ²b¾t€wRa%Üq„ÿ "å:A®aÒµdmS9·Øuq‹ÏÞ8ëÓ8‡]=p—У™×V ч%O²Mmck’vŽDùVÔ þùÆôønûVªÛ½€¢€ÑlÇ“á…d Ñö¦µüTÑ@ð,ga÷èÊa—„†ÞRÿ&¨d˜x*CéÁ.Þö PÆ6u3_¹’µñRYÔcD%†æž"áYkè/VxßgÛéWqV·Rfþ~Hú©Ký&‰_6í| »I•‰x•§tNPÝ®|˜aF{¯W»•Gî·b¨ë›+Ý7g<ÄP¨3{‡d܃‘:ºÎš$ŒËA‚~YKV±¾Y|Ihχ:/Ùž/sWn#Äv4ø¸@²Ça@Tœì}cpªßþ´5±R›¹kù¶'{Ì}ád3›mYñÀ BÀ[±W¼]åû—eàPÍÞ”Ø öcÐ#sϰΩÁü“:¹ÀxÓÐNW2¤Mp¥ë!ô–ù‘#/Ç?”OV-ØmßûÖŽ½à"S í“ނᛓNxÖ:ß=§¥‡ïš.n°IrofšÒ8 Q7…~ð½{έ?ëƒS·>Ú%Dœ·@›fÀÀçß¿ çÒ€¯säƒ$įëoCAÙ ŸN¿wÑn–Æ|‘¢<&!)áêbMƒ”Ä“\Y‚¡3š ªÔð;§oG¨9 ¼ƒr‡³d¶ýÉÕ¿ŸÄæ*Ú½ÕöÑýÞ1úÃã‡H5_dÌzš.z ñü´Øùœ¨¬ÿ™ÇŠ ½l”ŠÔŒ”TPÔûZéYá w¡ˆý^€Ÿy†öÛ‹ 1ïIí=[a>ò²­ùÎîÒŸ4W¥õ@¨õбŠw‡–›Âl§Ð B™ûªêíä71"ùéÒRûÌ¡öîççàÀ!T>¶DÇuÔ ;S ÊförkE iTáÁ†YQÆF\ûÜ»ZæÞéL­žµ°1Õì+¸áR_NpKù \2ØÒ‹Ò¤ ÕP„Ì~¸êkáa.~‰œ¥7Í~?ÄdüJ­7½ÁHxùÎÉ8Èï²­6ëÍü•ÉBQvd| ŽXÔð°1Óc ¿µ·ôíáÈ8óOÄpz3Ä'¡k&}bô¹X}Îv0â„Ùƒ3Á æjåês»)¾›8NW;*ø„Å’ ½DáÿmÉ8RZ•ñhOL¥Ag÷ðw¨WX¼LAhù¬‚XõÃÉ÷ృ{ X Ï1Z0# *†H†÷  1Ѻ„tó„;ŽŸ «g И03 *†H†÷  1&$Mary's certificate0+00+Ó ñø Sj;êÉ„¢n®OgÐ „èyœwMEventDance-0.2.0/tests/certs/x509-mary.pem000066400000000000000000000021631321356073300201720ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDHTCCAgegAwIBAgIES6jVFTALBgkqhkiG9w0BAQUwITEfMB0GA1UEAxMWRXZl bnREYW5jZSBUTFMgdGVzdCBDQTAeFw0xMDAzMjMxNDQ5NTdaFw0xMTAzMjMxNDQ5 NTdaMA8xDTALBgNVBAMTBE1hcnkwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC AQDCsM4mJ7xjRn3ro2HshPJ/75OanD07FbmVn6GHDy9hv4zt8Il5cbvGTsAGxdmO NrfA6ZPHlOmwt/pGUD2CZGjwAGnpF7YCyDJSuckPmKPP/AVCgnuNe1zMalpakX0D oH1MbHzZxz5Nff7P14okL809TtJHoXwA19YfLVn2vC14eT3+lQaLsHha2GXghcH3 MED9YjBrouUWOGGH45iRXaOs7JkZs/5u7eFmL9x3XmzWjP5axQEEmrHGsQlK07JG fDgixaBb/H1FsQabYhq03BuxDzHI1ocMDECJb+0hh6IgBA82K2+ZZ+kzf7xsCER4 XRgwUltMlgVdVWiXx2Wy4K3nAgMBAAGjdjB0MAwGA1UdEwEB/wQCMAAwEwYDVR0l BAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4EFgQU0bqEdBjz hDuOnxSPCqtnCo3QmIEwHwYDVR0jBBgwFoAUULQ4Y+C1EYvM+yg+S2BFzkWwP5Uw CwYJKoZIhvcNAQEFA4IBAQBef/x/JB9iVZKiP+aiaF65BSK6lNpgnK3lt7daCo9H sIHOtVjpVZqTFR0ZnUaJxBA3Mw9yyUd62VQs9pcnKvzvXGhmTH8T54K7uMKF5sQp OBazzK7Z0CjXwuLeGxxaFmesmZ1/nP1Zn1LGcFTRNypYrR+0955iwoYeSByHJgFh Jlezw9UYxc+m7cHnTLzb99XurMbvbWHFbSYbi3Lf/URur0Z84i7ChGlUS2YAyh5I rUvzyRmwrii7+KMnNgzbezVM2C1P3/SaBOs9We5pW8rxI3iYQcMb9Xz+JW3pPjIA yxV6HYWZfb1YA4OXm3Oyjz4oVTXNEkLNfTn5ZOXcqyiw -----END CERTIFICATE----- EventDance-0.2.0/tests/certs/x509-server-key.pem000066400000000000000000000032131321356073300213130ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwJdopvsdW8cZ5syjndCJaTbwbdKohCK133ygo6EtvW7O0ler i3FVPxraDuHcVAP3PmYKVk2O1HuVGbVflhJzsxoOg7huxeCgFIfDIHS1NhK4OnRf 78bvAf86wIwumQQD0vSW0AltJJ8UfDR0lrigm1GwZEGgpYzY6hzl+4vOKz4G/PS3 qsc+qcn8QF4fO8GUqwjE67P4wMpslZie/GYH2ZHQ+/l1ikDGLE2jlD4oJFvIqCwI i/xHKApBbHy0CIxCkdyR4c9mIRj+NN3TrjyvmhdIDUtgZ1K20p9lvNe3nasl6dUd t096+XrEosalqs9vr/+OHrRTC/oHwCtTzd0YswIDAQABAoIBAFYwLCx2OthEzQml 0R8iISyN4qEzYijoI75lVrqDuiWJflNR2rpnuL+p86YvW9vZfn9VSREAWogq2joM Gj/d+BtR8Obv1dsGQH2PmDphIGuXiwzV4c07mOCq6Q8cbQobat6wiQGHBmoaKx++ leKRs4Zu02K6n/sWgkibMC0Xjud4bS/0ukgASs8YLGvX1kM3Hmosrg9G+3+5QmqD BpDeJ74XpfzuP5izyuHoZ92Gr20+rHac12zUe6cUgiZ8Mq8pahvIE+9pMsaRoopY 3ua7QiRNJYQz3w8ghiBbv1s0N8yh0Yh31xnv48GQk63UZDANjS7BRKX6eLgMWXbv x4oWDtkCgYEAwcSXVmicnxSbnB+LgIyhXQwq0pzH3B2EJJdZ9Weqe6JFU58Qrjei 3AcXkCCy32GlVQnGENAHvn3OdLgQq3oe7aty2smOr3MMrQoRvAz/8+ICMBowCaUP rCN79/vHcjyc2eSiJT6QuHlw7v/C8r+Qpszb0E7ERdyQNM1dLD8OeAUCgYEA/nIW Za1vfk5blb9ka7wpudSpCCx4Zjah2fIHl98lYosvgpqOm6L+qmc9g8MpRYgZes7/ DIUP5MgDmCrowR5grn0BVTMcAiPse6hw+QE5TdbLslibnI+oRfys4UA705YPG3X4 B8ZXl0FAPwoSbIBx4TGSDug8qiwu5tjaZ7+1Q1cCgYAOoFtYH29rJrAUWeE64+Ku VmPJICKKAARgirg0qQ2aZYZcKI6mx/ZYK4Tg0UcPS5yiTamQzVM4UuDJuLcxand5 M07pKvvFTKbxTOhHzKdpbm8B2Ig1wirrsIYEExJcEi042WRP8WgVNL2MnOvHTSSL Ir2Y/MkeCOctxS66T0w1TQKBgQCVmXZZqbMXy/JFBRaB0UunGsWXLK5NSmm2vnqA bI6KddMlMsIN97NLdQ/R3HjzjKWOcw25yrIhjQev++qFQNsPPCgAc+BP0ddEjTB+ 9p0+ir+QyFAVqNEC9SCf/ygtqtiPCLkfuRnesFqZtc5HRO2vvXlmkly4oOmiiHVA 8pIcWQKBgEWl+CzUIKCaVHPhdQRt9u8pTBM0SLvX3isHLFw632Kxm8qBv24sDzi2 yW3VMEpPEVuoATFGfkU6b4XatW3MDhByUhRlrdUG1R3k3YoKpaUoAM1oEFU/Raf6 9Y3PV8y+Wy60CDluHzmMSQjtERfuZEWkqu3b/5jGwajqFk1ozHxB -----END RSA PRIVATE KEY----- EventDance-0.2.0/tests/certs/x509-server.pem000066400000000000000000000023041321356073300205250ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDWTCCAkOgAwIBAgIES6jTpTALBgkqhkiG9w0BAQUwITEfMB0GA1UEAxMWRXZl bnREYW5jZSBUTFMgdGVzdCBDQTAeFw0xMDAzMjMxNDQzNDlaFw0xMTAzMjMxNDQz NDlaMC4xEzARBgNVBAoTCkV2ZW50RGFuY2UxFzAVBgNVBAMTDmV2ZW50ZGFuY2Uu b3JnMIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEAwJdopvsdW8cZ5syjndCJ aTbwbdKohCK133ygo6EtvW7O0leri3FVPxraDuHcVAP3PmYKVk2O1HuVGbVflhJz sxoOg7huxeCgFIfDIHS1NhK4OnRf78bvAf86wIwumQQD0vSW0AltJJ8UfDR0lrig m1GwZEGgpYzY6hzl+4vOKz4G/PS3qsc+qcn8QF4fO8GUqwjE67P4wMpslZie/GYH 2ZHQ+/l1ikDGLE2jlD4oJFvIqCwIi/xHKApBbHy0CIxCkdyR4c9mIRj+NN3Trjyv mhdIDUtgZ1K20p9lvNe3nasl6dUdt096+XrEosalqs9vr/+OHrRTC/oHwCtTzd0Y swIDAQABo4GSMIGPMAwGA1UdEwEB/wQCMAAwGQYDVR0RBBIwEIIOZXZlbnRkYW5j ZS5vcmcwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUDAwegADAdBgNV HQ4EFgQUEtH2qzVUsm1DLGeJIfq0v4+TFnswHwYDVR0jBBgwFoAUULQ4Y+C1EYvM +yg+S2BFzkWwP5UwCwYJKoZIhvcNAQEFA4IBAQAi20Os3+j49CMpiUYgUwyMff6N LsOgJxvPN9eDsgYuef7A21IeA09O7qAFr5OTgp/XAA37HVPoFUd5iKiVLt+VVfI4 t5HtABOoSv1lM5F+cPQ1cMXTEmmYnZ/AV8C+CV+gG/ZkVAHymJWjSW9UW8VD15vF PwNI98xd8YPQvR0b9c8DGODNAA8pDGTyShGO9OQ7uiTxxT0Klq1e6cf/5m5xLCoz M/YRGfiKR6HkRyPF/rs9QSUliCqJrk0qSqVoJ/A4dN+1b8/h+MqM5tRi7jx/ZaUY Wm6fcFVEnioPNhyw5xARdeFe+GKSRwwZDC23M3h+6HdtufbpYm5mymEWpYqG -----END CERTIFICATE----- EventDance-0.2.0/tests/dbus-daemon.conf000066400000000000000000000026301321356073300177400ustar00rootroot00000000000000 unix:tmpdir=/tmp 1000000000 1000000000 1000000000 120000 240000 100000 10000 100000 10000 50000 50000 50000 EventDance-0.2.0/tests/js/000077500000000000000000000000001321356073300153065ustar00rootroot00000000000000EventDance-0.2.0/tests/js/common/000077500000000000000000000000001321356073300165765ustar00rootroot00000000000000EventDance-0.2.0/tests/js/common/assert.js000066400000000000000000000066461321356073300204510ustar00rootroot00000000000000// // Eduardo Lima Mitev // // Specification // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // // Code pieces adapted from // http://github.com/280north/narwhal/blob/master/lib/assert.js // let assert = this; assert.AssertionError = function (options) { if (typeof options == "string") options = {"message": options}; this.name = "AssertionError"; this.message = options.message; this.actual = options.actual; this.expected = options.expected; this.operator = options.operator; }; assert.AssertionError.prototype = { __proto__: Error.prototype, toString: function () { if (this.message) { return [ this.name + ":", this.message ].join (" "); } else { return [ this.name + ":", this.expected, this.operator, this.actual ].join (" "); } }, toSource: function () { return "new AssertionError " + Object.prototype.toSource.call (this) + ""; } }; assert.pass = function () { }; assert.error = function () { }; assert.fail = function (options) { throw (new assert.AssertionError (options)); }; assert.ok = function (value, message) { if (!!!value) (this.fail || assert.fail) ({ "actual": value, "expected": true, "message": message, "operator": "==" }); else (this.pass || assert.pass) (message); }; assert.equal = function (actual, expected, message) { if (actual != expected) (this.fail || assert.fail) ({ "actual": actual, "expected": expected, "message": message, "operator": "==" }); else (this.pass || assert.pass) (message); }; assert.notEqual = function (actual, expected, message) { if (actual == expected) (this.fail || assert.fail) ({ "actual": actual, "expected": expected, "message": message, "operator": "!=" }); else (this.pass || assert.pass) (message); }; assert.strictEqual = function (actual, expected, message) { if (actual !== expected) (this.fail || assert.fail) ({ "actual": actual, "expected": expected, "message": message, "operator": "===" }); else (this.pass || assert.pass) (message); }; assert.notStrictEqual = function (actual, expected, message) { if (actual === expected) (this.fail || assert.fail) ({ "actual": actual, "expected": expected, "message": message, "operator": "!==" }); else (this.pass || assert.pass) (message); }; assert["throws"] = function (block, Error, message) { let threw = false; let exception = null; if (typeof Error == "string") { message = Error; Error = undefined; } try { block (); } catch (e) { threw = true; exception = e; } if (! threw) { (this.fail || assert.fail) ({ "message": message, "operator": "throw" }); } else if (Error) { if (exception instanceof Error) (this.pass || assert.pass) (message); else throw exception; } else { (this.pass || assert.pass) (message); } }; EventDance-0.2.0/tests/js/common/test.js000066400000000000000000000023431321356073300201150ustar00rootroot00000000000000// // Eduardo Lima Mitev // // Specification // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // // Code pieces adapted from // http://github.com/280north/narwhal/blob/master/lib/test.js // const Assert = imports.common.assert; function run (test) { if (typeof test === "string") test = imports[test]; if (! test) throw ("Nothing to run"); let failures = 0; for (let property in test) { if (property.match (/^test/)) { if (typeof test[property] == "function") { if (typeof test.setup === "function") test.setup (); try { test[property] (Assert); failures++; } catch (e) { if (e.name === "AssertionError") { Assert.fail (e); } else { throw (e); } } finally { if (typeof test.teardown === "function") test.teardown (); } } else { failures += run (test[property]); } } } return failures; }; EventDance-0.2.0/tests/js/testResolver.js000066400000000000000000000072231321356073300203510ustar00rootroot00000000000000const MainLoop = imports.mainloop; const Gio = imports.gi.Gio; const Evd = imports.gi.Evd; const Test = imports.common.test; function abort_test_by_timeout () { MainLoop.quit ("test"); Test.Assert.fail ("test timeout"); } function testGetDefault (Assert) { let resolver = Evd.Resolver.get_default (); Assert.ok (resolver); let resolver1 = Evd.Resolver.get_default (); Assert.strictEqual (resolver, resolver1); } function testIPv4NoResolve (Assert) { const ADDR = "127.0.0.1"; const PORT = 80; let timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); let resolver = Evd.Resolver.get_default (); let request = resolver.resolve_with_closure (ADDR + ":" + PORT, function (res, req) { Assert.equal (req.is_active (), false); MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); Assert.strictEqual (resolver, res); Assert.strictEqual (request, req); let addresses = request.get_result (); Assert.ok (addresses); Assert.strictEqual (typeof (addresses), "object"); Assert.strictEqual (addresses.constructor, Array); Assert.strictEqual (addresses.length, 1); let addr = addresses[0]; Assert.strictEqual (addr.constructor, Gio.InetSocketAddress); Assert.strictEqual (addr.get_port (), PORT); let inet_addr = addresses[0].get_address (); Assert.strictEqual (inet_addr.to_string (), ADDR); }); Assert.equal (request.is_active (), true); MainLoop.run ("test"); } function testResolveLocalhost (Assert) { const ADDR = "localhost"; const PORT = 22; const NEW_PORT = 23; let timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); let resolver = Evd.Resolver.get_default (); let request = resolver.resolve_with_closure (ADDR + ":" + PORT, function (res, req) { MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); Assert.strictEqual (resolver, res); Assert.strictEqual (request, req); let addresses = request.get_result (); Assert.ok (addresses); Assert.strictEqual (typeof (addresses), "object"); Assert.strictEqual (addresses.constructor, Array); Assert.ok (addresses.length >= 1); for each (let addr in addresses) { Assert.strictEqual (addr.constructor, Gio.InetSocketAddress); Assert.strictEqual (addr.get_port (), NEW_PORT); if (addr.family == Gio.SocketFamily.IPV4) { let inet_addr = addr.get_address (); Assert.strictEqual (inet_addr.to_string (), "127.0.0.1"); } else if (addr.family == Gio.SocketFamily.IPV6) { let inet_addr = addr.get_address (); Assert.strictEqual (inet_addr.to_string (), "::1"); } } }); request.port = NEW_PORT; MainLoop.run ("test"); } function testCancel (Assert) { const ADDR = "localhost:1024"; let resolver = Evd.Resolver.get_default (); let request = resolver.resolve_with_closure (ADDR, function (res, req) { MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); Assert.fail ("request cancellation failed"); }); request.cancel (); Assert.equal (request.is_active (), false); let timeout_src_id = MainLoop.timeout_add (500, function () { MainLoop.quit ("test"); }); MainLoop.run ("test"); } Test.run (this); EventDance-0.2.0/tests/js/testSocket.js000066400000000000000000000254651321356073300200100ustar00rootroot00000000000000const MainLoop = imports.mainloop; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const Evd = imports.gi.Evd; const Test = imports.common.test; const Assert = imports.common.assert; const TCP_PORT = 7777; const UDP_PORT1 = 7777; const UDP_PORT2 = 7778; let timeout_src_id = 0; function abort_test_by_timeout () { MainLoop.quit ("test"); Assert.fail ("test timeout"); } function testInitialState (Assert) { let socket = new Evd.Socket (); Assert.ok (socket); Assert.equal (socket.socket, null); Assert.equal (socket.family, Gio.SocketFamily.INVALID); Assert.equal (socket.type, Gio.SocketType.INVALID); Assert.equal (socket.protocol, Gio.SocketProtocol.UNKNOWN); Assert.equal (socket.get_on_read (), null); Assert.equal (socket.get_on_write (), null); Assert.equal (socket.read_closure, null); Assert.equal (socket.write_closure, null); Assert.equal (socket.group, null); Assert.equal (socket.priority, 0); Assert.equal (socket.input_stream, null); Assert.equal (socket.output_stream, null); Assert.ok (socket.input_throttle); Assert.ok (socket.get_input_throttle ()); Assert.strictEqual (socket.input_throttle, socket.get_input_throttle ()); Assert.equal (socket.input_throttle.bandwidth, 0); Assert.equal (socket.input_throttle.latency, 0); Assert.ok (socket.output_throttle); Assert.ok (socket.get_output_throttle ()); Assert.strictEqual (socket.output_throttle, socket.get_output_throttle ()); Assert.equal (socket.output_throttle.bandwidth, 0); Assert.equal (socket.output_throttle.latency, 0); Assert.equal (socket.status, Evd.SocketState.CLOSED); Assert.equal (socket.get_socket (), socket.socket); Assert.equal (socket.get_context (), null); Assert.equal (socket.get_family (), socket.family); Assert.equal (socket.get_status (), Evd.SocketState.CLOSED); Assert.equal (socket.get_group (), socket.group); Assert.equal (socket.get_priority (), socket.priority); Assert.equal (socket.get_max_readable (), 0); Assert.equal (socket.get_max_writable (), 0); Assert.equal (socket.can_read (), false); Assert.equal (socket.can_write (), false); Assert.equal (socket.get_remote_address (), null); Assert.equal (socket.get_local_address (), null); Assert.strictEqual (socket.get_input_stream (), socket.input_stream); Assert.strictEqual (socket.get_output_stream (), socket.output_stream); } function testBindWhileActive (Assert) { let socket = new Evd.Socket (); socket.bind ("127.0.0.1:" + TCP_PORT, true); let error = null; try { socket.bind ("127.0.0.1:" + TCP_PORT); } catch (e) { error = e; } Assert.ok (error); socket.close (); } function testListenWhileActive (Assert) { let socket = new Evd.Socket (); socket.bind ("127.0.0.1:" + TCP_PORT, true); let error = null; try { socket.listen ("127.0.0.1:" + TCP_PORT); } catch (e) { error = e; } Assert.ok (error); socket.close (); } function testListenAfterBound (Assert) { let socket = new Evd.Socket (); socket.connect ("state-changed", function (socket, new_state, old_state) { if (new_state == Evd.SocketState.BOUND) { socket.listen (null); } else if (new_state == Evd.SocketState.LISTENING) { socket.close (); MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); } }); socket.bind ("127.0.0.1:" + TCP_PORT, true); timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); MainLoop.run ("test"); } function testConnectWhileActive (Assert) { let socket = new Evd.Socket (); socket.bind ("127.0.0.1:" + TCP_PORT, true); let error = null; try { socket.connect_to ("127.0.0.1:" + TCP_PORT); } catch (e) { error = e; } Assert.ok (error); socket.close (); } function testCancelBind (Assert) { let socket = new Evd.Socket (); socket.connect ("close", function (socket) { MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); }); socket.connect ("state-changed", function (socket, new_state, old_state) { if (new_state == Evd.SocketState.BOUND) Assert.fail ("cancel bind failed"); }); timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); MainLoop.idle_add ( function () { socket.bind ("127.0.0.1:" + TCP_PORT, true); socket.close (); }); MainLoop.run ("test"); } function testCancelListen (Assert) { let socket = new Evd.Socket (); socket.connect ("close", function (socket) { MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); }); socket.connect ("state-changed", function (socket, new_state, old_state) { if (new_state == Evd.SocketState.BOUND) { socket.close (); } else if (new_state == Evd.SocketState.LISTENING) { Assert.fail ("cancel listen failed"); } }); socket.listen ("127.0.0.1:" + TCP_PORT); timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); MainLoop.run ("test"); } // common test greeting functions const GREETING = "Hello world!"; const READ_BLOCK_SIZE = 1024; let socket1, socket2; let sockets_closed; let expected_sockets_closed; let bytes_read; function on_socket_read (socket) { Assert.strictEqual (socket.constructor, Evd.Socket); Assert.ok (socket.can_read ()); let [data, len] = socket.input_stream.read_str (READ_BLOCK_SIZE, null); if (len > 0) { Assert.equal (len, GREETING.length); Assert.equal (data, GREETING); bytes_read += len; if (bytes_read == GREETING.length * 2) { socket1.close (); socket2.close (); } } } function on_socket_write (socket) { Assert.strictEqual (socket.constructor, Evd.Socket); Assert.ok (socket.can_write ()); if (! socket.greeting_sent) { let len = socket.output_stream.write_str (GREETING); Assert.equal (len, GREETING.length); socket.greeting_sent = true; } } function on_socket_close (socket) { Assert.strictEqual (socket.constructor, Evd.Socket); Assert.equal (socket.get_status (), Evd.SocketState.CLOSED); Assert.equal (socket.status, Evd.SocketState.CLOSED); Assert.equal (socket.socket, null); Assert.equal (socket.can_read (), false); Assert.equal (socket.can_write (), false); Assert.equal (socket.get_remote_address (), null); Assert.equal (socket.get_local_address (), null); sockets_closed ++; if (sockets_closed == expected_sockets_closed) { MainLoop.source_remove (timeout_src_id); MainLoop.quit ("test"); } } function on_socket_state_changed (socket, new_state, old_state) { if (new_state == Evd.SocketState.LISTENING) { Assert.equal (socket, socket1); Assert.equal (old_state, Evd.SocketState.BOUND); socket2.connect_addr (socket.get_local_address ()); } else if (new_state == Evd.SocketState.CONNECTED) { Assert.equal (old_state, Evd.SocketState.CONNECTING); } else if (new_state == Evd.SocketState.BOUND) { if (socket.protocol == Gio.SocketProtocol.UDP) { Assert.equal (old_state, Evd.SocketState.CLOSED); socket.connect_to (socket.other_addr); } } } function on_socket_error (socket, code, msg) { Assert.fail ("Socket error " + code + ": " + msg); } function setup_greeting_sockets (socket1, socket2) { Assert.ok (socket1); Assert.ok (socket2); socket1.connect ("state-changed", on_socket_state_changed); socket2.connect ("state-changed", on_socket_state_changed); socket1.connect ("close", on_socket_close); socket2.connect ("close", on_socket_close); socket1.set_on_read (on_socket_read); Assert.ok (socket1.get_on_read ()); Assert.ok (socket1.read_closure); socket1.set_on_write (on_socket_write); Assert.ok (socket1.get_on_write ()); Assert.ok (socket1.write_closure); socket1.connect ("error", on_socket_error); socket2.set_on_read (on_socket_read); socket2.set_on_write (on_socket_write); socket2.connect ("error", on_socket_error); } function on_new_connection (server, client) { Assert.ok (server); Assert.strictEqual (server, socket1); Assert.equal (server.status, Evd.SocketState.LISTENING); Assert.ok (client); Assert.equal (client.status, Evd.SocketState.CONNECTED); client.set_on_read (on_socket_read); client.set_on_write (on_socket_write); client.connect ("close", on_socket_close); } function launchTcpTest (addr) { socket1 = new Evd.Socket (); socket2 = new Evd.Socket (); setup_greeting_sockets (socket1, socket2); socket1.connect ("new-connection", on_new_connection); socket1.listen (addr); Assert.equal (socket1.status, Evd.SocketState.RESOLVING); timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); expected_sockets_closed = 3; sockets_closed = 0; bytes_read = 0; MainLoop.run ("test"); Assert.equal (sockets_closed, 3); Assert.equal (bytes_read, GREETING.length * 2); } function testUnixTcp (Assert) { let ADDR = "/tmp/evd-socket-test-js"; GLib.unlink (ADDR); launchTcpTest (ADDR); } function testInetIpv4Tcp (Assert) { launchTcpTest ("127.0.0.1:" + TCP_PORT); } function testInetIpv6Tcp (Assert) { launchTcpTest ("::1:" + TCP_PORT); } function launchUdpTest (addr1, addr2) { socket1 = new Evd.Socket ({protocol: Gio.SocketProtocol.UDP}); socket2 = new Evd.Socket ({protocol: Gio.SocketProtocol.UDP}); setup_greeting_sockets (socket1, socket2); socket1.bind (addr1, true); socket1.other_addr = addr2; Assert.equal (socket1.status, Evd.SocketState.RESOLVING); socket2.bind (addr2, true); socket2.other_addr = addr1; Assert.equal (socket2.status, Evd.SocketState.RESOLVING); timeout_src_id = MainLoop.timeout_add (1000, abort_test_by_timeout); expected_sockets_closed = 2; sockets_closed = 0; bytes_read = 0; MainLoop.run ("test"); Assert.equal (sockets_closed, 2); Assert.equal (bytes_read, GREETING.length * 2); } function testUnixUdp (Assert) { let ADDR = "/tmp/evd-socket-test-js"; GLib.unlink (ADDR); // Gio Unix socket don't seem to support UDP protocol. Bypassing. // launchUdpTest (ADDR, ADDR); } function testIpv4Udp (Assert) { let ADDR1 = "127.0.0.1:" + UDP_PORT1; let ADDR2 = "127.0.0.1:" + UDP_PORT2; launchUdpTest (ADDR1, ADDR2); } function testIpv6Udp (Assert) { let ADDR1 = "::1:" + UDP_PORT1; let ADDR2 = "::1:" + UDP_PORT2; launchUdpTest (ADDR1, ADDR2); } Test.run (this); EventDance-0.2.0/tests/js/testSocketGroup.js000066400000000000000000000142541321356073300210170ustar00rootroot00000000000000const MainLoop = imports.mainloop; const Lang = imports.lang; const Evd = imports.gi.Evd; const Test = imports.common.test; const Assert = imports.common.assert; function testInitialState (Assert) { let group = new Evd.SocketGroup (); Assert.ok (group); Assert.ok (group instanceof Evd.SocketBase); Assert.strictEqual (group.read_closure, null); Assert.strictEqual (group.get_on_read (), null); Assert.strictEqual (group.write_closure, null); Assert.strictEqual (group.get_on_write (), null); let read_handler = function () {}; let write_handler = function () {}; group.set_on_read (read_handler); Assert.ok (group.get_on_read ()); Assert.ok (group.read_closure); group.set_on_write (write_handler); Assert.ok (group.get_on_write ()); Assert.ok (group.write_closure); } function testAddRemoveSocket (Assert) { let group = new Evd.SocketGroup (); let socket = new Evd.Socket (); // socket can be added to a group by invoking 'add' method of group. group.add (socket); Assert.strictEqual (socket.group, group); Assert.ok (socket.get_on_read ()); Assert.ok (socket.read_closure); Assert.ok (socket.get_on_write ()); Assert.ok (socket.write_closure); // socket can be removed from a group by invoking 'remove' method // of group. group.remove (socket); Assert.strictEqual (socket.group, null); Assert.strictEqual (socket.get_on_read (), null); Assert.strictEqual (socket.read_closure, null); Assert.strictEqual (socket.get_on_write (), null); Assert.strictEqual (socket.write_closure, null); // a socket can be added to a group assigning the 'group' property // to the group. socket.group = group; Assert.strictEqual (socket.group, group); Assert.ok (socket.get_on_read ()); Assert.ok (socket.read_closure); Assert.ok (socket.get_on_write ()); Assert.ok (socket.write_closure); // a socket can be removed from a group assigning 'group' property // to null. socket.group = null; Assert.strictEqual (socket.group, null); Assert.strictEqual (socket.get_on_read (), null); Assert.strictEqual (socket.read_closure, null); Assert.strictEqual (socket.get_on_write (), null); Assert.strictEqual (socket.write_closure, null); // setting the read/write handler of a socket while in a group, // removes the socket from the group. socket.group = group; // @TODO: using 'set_read_handler' here makes JS object leak. Why? // using 'set_on_read' instead. // socket.set_read_handler (function () {}, null); socket.set_on_read (function () {}); Assert.strictEqual (socket.group, null); socket.set_read_handler (null, null); socket.group = group; socket.set_on_write (function () {}); Assert.strictEqual (socket.group, null); socket.set_write_handler (null, null); } function testDataTransfer (Assert) { const CLIENT_SOCKETS = 120; const DATA_SIZE = 1024; const BLOCK_SIZE = 512; const PORT = 6666; /* generate random data */ let data = ""; for (let i=0; i 0) { this.data_written += len; if (this.data_written < DATA_SIZE) return true; } return false; }; group.set_on_write (function (group, socket) { Assert.ok (group instanceof Evd.SocketGroup); Assert.ok (socket instanceof Evd.Socket); Assert.ok (socket.can_write ()); MainLoop.idle_add (Lang.bind (socket, do_write)); }); let do_read = function () { if (this.status != Evd.SocketState.CONNECTED) return false; let [_data, len] = this.input_stream.read_str (BLOCK_SIZE, null); if (len > 0) { this.data_read += len; this.data += _data; if (this.data_read == DATA_SIZE) { Assert.strictEqual (this.data, data); this.close (); } else if (len == BLOCK_SIZE) return true; } return false; }; group.set_on_read (function (group, socket) { Assert.ok (group instanceof Evd.SocketGroup); Assert.ok (socket instanceof Evd.Socket); Assert.ok (socket.can_read ()); MainLoop.idle_add (Lang.bind (socket, do_read)); }); let on_close = function (socket) { active_sockets--; if (active_sockets == 0) MainLoop.quit ("test"); }; let on_error = function (socket, code, msg) { Assert.fail ("Socket error " + code + " :" + msg); }; let setup_socket = function (socket) { socket.connect ("close", on_close); socket.connect ("error", on_error); socket.data_read = 0; socket.data_written = 0; socket.group = group; socket.data = ""; active_sockets++; }; let server = new Evd.Socket (); server.group = group; server.listen ("0.0.0.0:" + PORT); server.connect ("state-changed", function (socket, new_state, old_state) { if (new_state == Evd.SocketState.LISTENING) { /* client sockets */ for (let i=0; i"); Assert.equal (cert.get_expiration_time () * 1000, new Date ("Tue May 12 2015 16:13:11 GMT+0200 (CET)").valueOf ()); Assert.equal (cert.get_activation_time () * 1000, new Date ("Thu May 13 2010 16:13:11 GMT+0200 (CET)").valueOf ()); Assert.equal (cert.verify_validity (), Evd.TlsVerifyState.OK); } Evd.tls_init (); Test.run (this); Evd.tls_deinit (); EventDance-0.2.0/tests/test-all-js.c000066400000000000000000000062131321356073300171770ustar00rootroot00000000000000/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * test-all-js.c * * EventDance project - An event distribution framework (http://eventdance.org) * * Copyright (C) 2009, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include typedef struct { GjsContext *context; } GjsTestJSFixture; gchar *js_test_dir; static void setup (GjsTestJSFixture *fix, gconstpointer test_data) { gchar *search_path[2]; search_path[0] = g_build_filename (js_test_dir, NULL); search_path[1] = NULL; fix->context = gjs_context_new_with_search_path (search_path); g_free (search_path[0]); } static void teardown (GjsTestJSFixture *fix, gconstpointer test_data) { gjs_memory_report ("before destroying context", FALSE); g_object_unref (fix->context); gjs_memory_report ("after destroying context", TRUE); } static void test (GjsTestJSFixture *fix, gconstpointer test_data) { GError *error = NULL; gint code; gjs_context_eval_file (fix->context, (const gchar*) test_data, &code, &error); g_free ((gchar *) test_data); if (error != NULL) { /* TODO: here we should decide if a failing test aborts the process. By now, just log and continue. */ g_error ("%s", error->message); g_assert (error == NULL); } } gint main (gint argc, gchar *argv[]) { gchar *test_dir; const gchar *name; GDir *dir; g_test_init (&argc, &argv, NULL); g_type_init (); test_dir = g_path_get_dirname (argv[0]); js_test_dir = g_build_filename (test_dir, "..", "js", NULL); g_free (test_dir); /* iterate through all 'test*.js' files in 'js_test_dir' */ dir = g_dir_open (js_test_dir, 0, NULL); g_assert (dir != NULL); while ((name = g_dir_read_name (dir)) != NULL) { gchar *test_name; gchar *file_name; if (! (g_str_has_prefix (name, "test") && g_str_has_suffix (name, ".js"))) continue; /* pretty print, drop 'test' prefix and '.js' suffix from test name */ test_name = g_strconcat ("/evd/js/", name + 4, NULL); test_name[strlen (test_name)-3] = '\0'; file_name = g_build_filename (js_test_dir, name, NULL); g_test_add (test_name, GjsTestJSFixture, file_name, setup, test, teardown); g_free (test_name); /* not freeing file_name as it's needed while running the test */ } g_dir_close (dir); return g_test_run (); } EventDance-0.2.0/tests/test-all.c000066400000000000000000000063551321356073300165740ustar00rootroot00000000000000/* * test-all.c * * EventDance project - An event distribution framework (http://eventdance.org) * * Copyright (C) 2009, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #include static gint run_test (gchar *filename, gchar *argv[], GError **error) { gint exit_status; argv[0] = filename; g_spawn_sync (".", argv, NULL, 0, NULL, NULL, NULL, NULL, &exit_status, error); return exit_status; } static gboolean is_a_test (const gchar *filename) { gboolean is_a_test = FALSE; gchar *name; name = g_path_get_basename (filename); if (g_str_has_prefix (name, "test-")) { struct stat stat_buf = {0,}; if (g_stat (filename, &stat_buf) == 0) { if ( ( (stat_buf.st_mode & S_IFREG) != 0) && ( (stat_buf.st_mode & S_IXUSR) != 0) ) is_a_test = TRUE; } } g_free (name); return is_a_test; } gint main (gint argc, gchar *argv[]) { GDir *dir; gchar *current_dir; gchar *test_dir; const gchar *name; gboolean abort = FALSE; gchar *filename; GError *error = NULL; #ifndef GLIB_VERSION_2_36 g_type_init (); #endif if ( (argc > 1) && (g_strcmp0 (argv[1], "--help") == 0 || g_strcmp0 (argv[1], "-?") == 0) ) { g_test_init (&argc, &argv, NULL); return 0; } current_dir = g_path_get_dirname (argv[0]); test_dir = g_build_filename (current_dir, "../", NULL); g_free (current_dir); dir = g_dir_open (test_dir, 0, NULL); g_assert (dir != NULL); while ( (! abort) && (name = g_dir_read_name (dir)) != NULL) { if (g_str_has_prefix (name, "test-all")) continue; filename = g_build_filename (test_dir, name, NULL); if (is_a_test (filename)) { if (run_test (filename, argv, &error) != 0) { /* TODO: decide whether to abort running tests when one fails */ g_error ("%s", error->message); abort = TRUE; g_error_free (error); error = NULL; } } g_free (filename); } g_dir_close(dir); #ifdef HAVE_JS filename = g_build_filename (test_dir, "test-all-js", NULL); if (run_test (filename, argv, &error) != 0) { g_error ("%s", error->message); g_error_free (error); } g_free (filename); #endif g_free (test_dir); return 0; } EventDance-0.2.0/tests/test-dbus-bridge.c000066400000000000000000000435011321356073300202050ustar00rootroot00000000000000/* * test-dbus-bridge.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #include #include #include "evd-dbus-agent.h" #define BASE_NAME "org.eventdance.lib.test" #define BASE_OBJ_PATH "/org/eventdance/lib/test" #define BASE_IFACE_NAME BASE_NAME #define DBUS_ADDR "alias:abstract=" BASE_OBJ_PATH "/dbus-bridge" #define IFACE_XML \ "" \ " " \ " " \ " " \ " " \ " " \ " " \ " " \ "" static gchar *bus_addr; static const gchar *addr_alias = DBUS_ADDR; static gint test_index = -1; static const gchar *self_name; static GOptionEntry entries[] = { { "run-test", 'r', 0, G_OPTION_ARG_INT, &test_index, "Only run specified test case", NULL } }; typedef struct { gchar *test_name; gchar *send[15]; gchar *expect[15]; } TestCase; struct Fixture { EvdDBusBridge *bridge; GObject *obj; gint i; gint j; TestCase *test_case; GMainLoop *main_loop; }; static const TestCase test_cases[] = { { "error/invalid-message", { "", "[]", "[0,0,0,\"\"]", "[0,0,0,0,0]", "[3,1,0,0]", }, { "[1,0,0,0,\"[1]\"]", "[1,0,0,0,\"[1]\"]", "[1,0,0,0,\"[1]\"]", "[1,0,0,0,\"[1]\"]" } }, { "error/invalid-command", { "[0,1,0,0,\"\"]", "[100,16,0,0,\"\"]", }, { "[1,1,0,0,\"[2]\"]", "[1,16,0,0,\"[2]\"]", } }, { "error/invalid-arguments", { "[3,1,1,0,\"\"]", }, { "[1,1,1,0,\"[4]\"]" } }, { "new-connection/error", { "[3,1,0,0,'[\"invalid:address=error\",true]']", }, { "[1,1,0,0,\"[5,\\\"Unknown or unsupported transport \342\200\234invalid\342\200\235 for address \342\200\234invalid:address=error\342\200\235\\\"]\"]" } }, { "new-connection/success", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", "[3,2,0,0,'[\"" DBUS_ADDR "\",false]']", }, { "[2,1,0,0,\"[1]\"]", "[2,2,0,0,\"[2]\"]" } }, { "close-connection/error", { "[4,2,1,0,'[]']" }, { "[1,2,1,0,\"[3,\\\"Object doesn't hold specified connection\\\"]\"]" } }, { "close-connection/success", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", "[4,2,1,0,'[]']" }, { "[2,1,0,0,\"[1]\"]", "[2,2,1,0,\"[]\"]" } }, { "own-name", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,2,1,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name */ NULL, }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,2,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ } }, { "own-name/twice", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,2,1,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name */ NULL, "[6,3,1,1,'[]']", /* unown-name */ "[5,4,1,0,'[\"org.eventdance.lib.tests1\", 0]']", /* own-name again */ NULL, "[6,5,1,2,'[]']", /* unown-name */ "[5,6,1,0,'[\"org.eventdance.lib.tests1\", 0]']", /* own-name again */ NULL, "[6,7,1,3,'[]']", /* unown-name */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,2,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,3,1,1,\"[]\"]", /* unown-name response */ "[2,4,1,0,\"[2]\"]", /* own-name response */ "[7,0,1,2,\"[]\"]", /* name-acquired signal again */ "[2,5,1,2,\"[]\"]", /* unown-name response */ "[2,6,1,0,\"[3]\"]", /* own-name response */ "[7,0,1,3,\"[]\"]", /* name-acquired signal again */ "[2,7,1,3,\"[]\"]", /* unown-name response */ } }, { "own-name/queue", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection 1*/ "[3,2,0,0,'[\"" DBUS_ADDR "\",false]']", /* new-connection 2 */ "[5,3,1,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name, connection 1 */ NULL, "[5,4,2,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name, connection 2 */ NULL, "[6,5,1,1,'[]']", /* unown-name, connection 1 */ NULL, "[6,6,2,2,'[]']", /* unown-name, connection 2 */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection 1 response */ "[2,2,0,0,\"[2]\"]", /* new-connection 2 response */ "[2,3,1,0,\"[1]\"]", /* own-name response, connection 1 */ "[7,0,1,1,\"[]\"]", /* name-acquired signal, connection 1 */ "[2,4,2,0,\"[2]\"]", /* own-name response, connection 2 */ "[8,0,2,2,\"[]\"]", /* name-lost signal, connection 2 */ "[2,5,1,1,\"[]\"]", /* unown-name response, connection 1 */ "[7,0,2,2,\"[]\"]", /* name-acquired signal, connection 2 */ "[2,6,2,2,\"[]\"]", /* unown-name response, connection 2 */ } }, { "own-name/close-connection", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection 1*/ "[3,1,0,0,'[\"" DBUS_ADDR "\",false]']", /* new-connection 2 */ "[5,2,1,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name, connection 1 */ NULL, "[5,2,2,0,'[\"org.eventdance.lib.tests\", 0]']", /* own-name, connection 2 */ NULL, "[4,3,1,0,'[]']", /* close connection 1 */ NULL, "[6,3,2,2,'[]']", /* unown-name, connection 2 */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection 1 response */ "[2,1,0,0,\"[2]\"]", /* new-connection 2 response */ "[2,2,1,0,\"[1]\"]", /* own-name response, connection 1 */ "[7,0,1,1,\"[]\"]", /* name-acquired signal, connection 1 */ "[2,2,2,0,\"[2]\"]", /* own-name response, connection 2 */ "[8,0,2,2,\"[]\"]", /* name-lost signal, connection 2 */ "[7,0,2,2,\"[]\"]", /* name-acquired signal, connection 2 */ "[2,3,1,0,\"[]\"]", /* close-connection 1 response */ "[2,3,2,2,\"[]\"]", /* unown-name response, connection 2 */ } }, { "register-object", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,2,1,0,'[\"org.eventdance.lib.tests.RegisterObject\", 0]']", /* own-name */ NULL, "[9,3,1,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object */ "[10,4,1,1,'[]']", /* unregister-object */ "[6,5,1,1,'[]']", /* unown-name */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,2,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,3,1,0,\"[1]\"]", /* register-object response */ "[2,4,1,1,\"[]\"]", /* unregister-object response */ "[2,5,1,1,\"[]\"]", /* unown-name response */ } }, { "register-object/already-registered", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,2,1,0,'[\"org.eventdance.lib.tests.RegisterObject\", 0]']", /* own-name */ NULL, "[9,3,1,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object */ "[9,4,1,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object */ "[10,5,1,1,'[]']", /* unregister-object */ "[6,6,1,1,'[]']", /* unown-name */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,2,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,3,1,0,\"[1]\"]", /* register-object response */ "[1,4,1,0,\"[6]\"]", /* register-object again - error, already registered */ "[2,5,1,1,\"[]\"]", /* unregister-object response */ "[2,6,1,1,\"[]\"]", /* unown-name response */ } }, { "register-object/two-connections", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection 1 */ "[3,2,0,0,'[\"" DBUS_ADDR "\",false]']", /* new-connection 2 */ "[9,1,1,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object, connection 1 */ "[9,1,2,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object, connection 2 */ "[10,2,1,1,'[]']", /* unregister-object, connection 1 */ "[10,2,2,2,'[]']", /* unregister-object, connection 2 */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection 1 response */ "[2,2,0,0,\"[2]\"]", /* new-connection 2 response */ "[2,1,1,0,\"[1]\"]", /* register-object response, connection 1 */ "[2,1,2,0,\"[2]\"]", /* register-object response, connection 2 */ "[2,2,1,1,\"[]\"]", /* unregister-object response, connection 1 */ "[2,2,2,2,\"[]\"]", /* unregister-object response, connection 2 */ } }, { "register-object/close-connection", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,2,1,0,'[\"org.eventdance.lib.tests.RegisterObject\", 0]']", /* own-name */ NULL, "[9,3,1,0,'[\"/org/eventdance/lib/test/RegisterObject/Object\",\"" IFACE_XML "\"]']", /* register-object */ "[4,4,1,0,'[]']", /* close connection (should unregister object, and lost name) */ "[10,5,1,1,'[]']", /* unregister-object (should return error, invalid subject) */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,2,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,3,1,0,\"[1]\"]", /* register-object response */ "[2,4,1,0,\"[]\"]", /* close-connection response */ "[1,5,1,1,\"[3]\"]", /* error in unregister-object, invalid registered object */ } }, { "new-proxy", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[11,1,1,0,'[\"" BASE_NAME "\",\"" BASE_OBJ_PATH "/NewProxy\",\"" BASE_IFACE_NAME ".TestIface\",0]']", /* new-proxy */ "[12,2,1,1,'[]']", /* close-proxy */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,1,1,0,\"[1]\"]", /* new-proxy response */ "[2,2,1,1,\"[]\"]", /* close-proxy response */ } }, { "new-proxy/close-connection", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[11,1,1,0,'[\"" BASE_NAME "\",\"" BASE_OBJ_PATH "/NewProxy\",\"" BASE_IFACE_NAME ".TestIface\",0]']", /* new-proxy */ "[4,2,1,0,'[]']", /* close connection (should invalidate proxy) */ "[12,3,1,1,'[]']", /* close-proxy */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,1,1,0,\"[1]\"]", /* new-proxy response */ "[2,2,1,0,\"[]\"]", /* close-connection response */ "[1,3,1,1,\"[3]\"]", /* error in close-proxy, invalid proxy */ } }, { "proxy/call-method", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,1,1,0,'[\"" BASE_NAME ".CallProxyMethod\", 0]']", /* own-name */ NULL, "[9,2,1,0,'[\"" BASE_OBJ_PATH "/CallProxyMethod\",\"" IFACE_XML "\"]']", /* register-object */ "[11,3,1,0,'[\"" BASE_NAME ".CallProxyMethod\",\"" BASE_OBJ_PATH "/CallProxyMethod\",\"" BASE_IFACE_NAME ".TestIface\",0]']", /* new-proxy */ "[13,4,1,1,'[\"HelloWorld\",\"[\\\"Hi there\\\"]\",\"(s)\",0,-1]']", /* call-method on proxy */ "[14,1,1,1,'[\"[\\\"hello world!\\\"]\"]']", /* call-method-return from registered object */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,1,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,2,1,0,\"[1]\"]", /* register-object response */ "[2,3,1,0,\"[1]\"]", /* new-proxy response */ "[13,1,1,1,\"[\\\"HelloWorld\\\",\\\"[\\\\\\\"Hi there\\\\\\\"]\\\",\\\"(s)\\\",0,0]\"]", /* call-method to registered object */ "[14,4,1,1,\"[\\\"[\\\\\\\"hello world!\\\\\\\"]\\\"]\"]", /* call-method-return to proxy */ } }, { "proxy/signal", { "[3,1,0,0,'[\"" DBUS_ADDR "\",true]']", /* new-connection */ "[5,1,1,0,'[\"" BASE_NAME ".ProxySignal\", 0]']", /* own-name */ NULL, "[9,2,1,0,'[\"" BASE_OBJ_PATH "/ProxySignal\",\"" IFACE_XML "\"]']", /* register-object */ "[11,3,1,0,'[\"" BASE_NAME ".ProxySignal\",\"" BASE_OBJ_PATH "/ProxySignal\",\"" BASE_IFACE_NAME ".TestIface\",0]']", /* new-proxy */ "[15,4,1,1,'[\"WorldGreets\",\"[\\\"hello world!\\\"]\",\"(s)\"]']", /* emit-signal from registered object */ }, { "[2,1,0,0,\"[1]\"]", /* new-connection response */ "[2,1,1,0,\"[1]\"]", /* own-name response */ "[7,0,1,1,\"[]\"]", /* name-acquired signal */ "[2,2,1,0,\"[1]\"]", /* register-object response */ "[2,3,1,0,\"[1]\"]", /* new-proxy response */ "[15,0,1,1,\"[\\\"WorldGreets\\\",\\\"[\\\\\\\"hello world!\\\\\\\"]\\\",\\\"(s)\\\"]\"]", /* emit-signal received on proxy */ } }, }; static void test_fixture_setup (struct Fixture *f, gconstpointer test_data) { f->bridge = evd_dbus_bridge_new (); f->obj = g_object_new (G_TYPE_OBJECT, NULL); evd_dbus_agent_create_address_alias (f->obj, bus_addr, addr_alias); evd_dbus_bridge_track_object (f->bridge, f->obj); f->main_loop = g_main_loop_new (NULL, FALSE); f->i = 0; f->j = 0; } static void test_fixture_teardown (struct Fixture *f, gconstpointer test_data) { g_object_unref (f->bridge); g_object_unref (f->obj); g_main_loop_unref (f->main_loop); } static gboolean on_send_in_idle (gpointer user_data) { struct Fixture *f = (struct Fixture *) user_data; evd_dbus_bridge_process_msg (f->bridge, f->obj, f->test_case->send[f->i-1], -1); return FALSE; } static void on_bridge_send_msg (EvdDBusBridge *self, GObject *object, const gchar *json, gpointer user_data) { struct Fixture *f = (struct Fixture *) user_data; const gchar *expected_json; JsonParser *parser; GError *error = NULL; expected_json = f->test_case->expect[f->j]; f->j++; f->i++; // g_debug ("%s", json); g_assert_cmpstr (expected_json, ==, json); parser = json_parser_new (); json_parser_load_from_data (parser, json, -1, &error); g_assert_no_error (error); g_object_unref (parser); if (f->test_case->send[f->i-1] != NULL) g_idle_add (on_send_in_idle, f); if (f->test_case->send[f->i] == NULL && f->test_case->expect[f->j] == NULL) { g_main_loop_quit (f->main_loop); } } static void test_func (struct Fixture *f, gconstpointer test_data) { TestCase *test_case = (TestCase *) test_data; f->test_case = test_case; evd_dbus_bridge_set_send_msg_callback (f->bridge, on_bridge_send_msg, f); evd_dbus_bridge_process_msg (f->bridge, f->obj, test_case->send[f->i], -1); f->i++; g_main_loop_run (f->main_loop); } static void spawn_test (gconstpointer test_data) { gint *test_index = (gint *) test_data; gint exit_status; GError *error = NULL; gchar *cmdline; cmdline = g_strdup_printf ("%s -r %d", self_name, *test_index); g_spawn_command_line_sync (cmdline, NULL, NULL, &exit_status, &error); g_assert_cmpint (exit_status, ==, 0); g_assert_no_error (error); g_free (cmdline); g_free (test_index); } gint main (gint argc, gchar *argv[]) { GError *error = NULL; GOptionContext *context; self_name = argv[0]; #ifndef GLIB_VERSION_2_36 g_type_init (); #endif g_test_init (&argc, &argv, NULL); context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, NULL); if (! g_option_context_parse (context, &argc, &argv, &error)) { g_print ("Error in arguments: %s\n", error->message); return 1; } g_option_context_free (context); if (test_index >= 0 && test_index < sizeof (test_cases) / sizeof (TestCase)) { struct Fixture *f; EvdDBusDaemon *dbus_daemon; dbus_daemon = evd_dbus_daemon_new (TESTS_DIR "dbus-daemon.conf", NULL); g_assert (EVD_IS_DBUS_DAEMON (dbus_daemon)); g_object_get (dbus_daemon, "address", &bus_addr, NULL); f = g_slice_new (struct Fixture); test_fixture_setup (f, &test_cases[test_index]); test_func (f, &test_cases[test_index]); test_fixture_teardown (f, &test_cases[test_index]); g_free (bus_addr); g_object_unref (dbus_daemon); g_slice_free (struct Fixture, f); } else { gint i; for (i=0; i * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include typedef struct { EvdIoStreamGroup *group0; EvdIoStreamGroup *group1; EvdSocket *socket0; EvdSocket *socket1; GMainLoop *main_loop; guint count_group_changes; EvdIoStreamGroup *expected_old_group; EvdIoStreamGroup *expected_new_group; guint listen_port; } Fixture; static void fixture_setup (Fixture *f, gconstpointer test_data) { f->group0 = evd_io_stream_group_new (); f->group1 = evd_io_stream_group_new (); f->socket0 = evd_socket_new (); f->socket1 = evd_socket_new (); f->count_group_changes = 0; f->expected_old_group = NULL; f->expected_new_group = NULL; f->main_loop = g_main_loop_new (NULL, FALSE); f->listen_port = g_random_int_range (1025, 65535); } static void fixture_teardown (Fixture *f, gconstpointer test_data) { g_object_unref (f->group0); f->group0 = NULL; g_object_unref (f->group1); f->group1 = NULL; g_object_unref (f->socket0); f->socket0 = NULL; g_object_unref (f->socket1); f->socket1 = NULL; g_main_loop_unref (f->main_loop); f->main_loop = NULL; } static void check_io_stream_is_in_group (EvdIoStream *io_stream, EvdIoStreamGroup *group) { EvdIoStreamGroup *_group; g_assert (evd_io_stream_get_group (io_stream) == group); g_object_get (io_stream, "group", &_group, NULL); g_assert (_group == group); if (_group != NULL) g_object_unref (_group); } static void connection_on_group_changed (EvdIoStream *io_stream, EvdIoStreamGroup *new_group, EvdIoStreamGroup *old_group, gpointer user_data) { Fixture *f = user_data; g_assert (EVD_IS_IO_STREAM (io_stream)); g_assert (new_group == f->expected_new_group); g_assert (old_group == f->expected_old_group); } static void socket_on_new_connection (EvdSocket *socket, EvdConnection *conn, gpointer user_data) { Fixture *f = user_data; g_assert (conn != NULL); g_assert (EVD_IS_CONNECTION (conn)); g_assert (EVD_IS_IO_STREAM (conn)); g_assert (G_IS_IO_STREAM (conn)); g_signal_connect (conn, "group-changed", G_CALLBACK (connection_on_group_changed), f); /* here starts the actual assertions for this test */ /* initially, the connection has no group */ check_io_stream_is_in_group (EVD_IO_STREAM (conn), NULL); /* put connection into one group using method */ f->expected_old_group = NULL; f->expected_new_group = f->group0; evd_io_stream_set_group (EVD_IO_STREAM (conn), f->group0); check_io_stream_is_in_group (EVD_IO_STREAM (conn), f->group0); /* remove connection from group using method */ f->expected_new_group = NULL; f->expected_old_group = f->group0; evd_io_stream_set_group (EVD_IO_STREAM (conn), NULL); check_io_stream_is_in_group (EVD_IO_STREAM (conn), NULL); /* put connection into another group using property */ f->expected_old_group = NULL; f->expected_new_group = f->group1; g_object_set (conn, "group", f->group1, NULL); check_io_stream_is_in_group (EVD_IO_STREAM (conn), f->group1); /* remove connection from group using property */ f->expected_new_group = NULL; f->expected_old_group = f->group1; g_object_set (conn, "group", NULL, NULL); check_io_stream_is_in_group (EVD_IO_STREAM (conn), NULL); /* put connection into another group using API from group */ f->expected_old_group = NULL; f->expected_new_group = f->group0; evd_io_stream_group_add (f->group0, G_IO_STREAM (conn)); check_io_stream_is_in_group (EVD_IO_STREAM (conn), f->group0); /* remove connection from group using API from group */ f->expected_new_group = NULL; f->expected_old_group = f->group0; evd_io_stream_group_remove (f->group0, G_IO_STREAM (conn)); check_io_stream_is_in_group (EVD_IO_STREAM (conn), NULL); g_main_loop_quit (f->main_loop); } static void test_func (Fixture *f, gconstpointer test_data) { gchar *addr; addr = g_strdup_printf ("0.0.0.0:%d", f->listen_port); evd_socket_listen (f->socket0, addr, NULL, NULL, f); g_signal_connect (f->socket0, "new-connection", G_CALLBACK (socket_on_new_connection), f); evd_socket_connect_to (f->socket1, addr, NULL, NULL, NULL); g_free (addr); g_main_loop_run (f->main_loop); } gint main (gint argc, gchar *argv[]) { #ifndef GLIB_VERSION_2_36 g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add ("/evd/io-stream-group/all", Fixture, NULL, fixture_setup, test_func, fixture_teardown); return g_test_run (); } EventDance-0.2.0/tests/test-json-filter.c000066400000000000000000000101721321356073300202500ustar00rootroot00000000000000/* * test-json-filter.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #include #include #include "evd-json-filter.h" static const gchar *evd_json_filter_chunks[] = { " [\"hell", "o world!\", 1, 4, fal", "se, 456, 4, ", "null] {\"foo\":1234} " }; static const gchar *evd_json_filter_packets[] = { "[\"hello world!\", 1, 4, false, 456, 4, null]", "{\"foo\":1234} " }; typedef struct { EvdJsonFilter *filter; gint packet_index; } EvdJsonFilterFixture; void evd_json_filter_fixture_setup (EvdJsonFilterFixture *f, gconstpointer test_data) { f->filter = evd_json_filter_new (); f->packet_index = 0; } void evd_json_filter_fixture_teardown (EvdJsonFilterFixture *f, gconstpointer test_data) { g_object_unref (f->filter); } static void evd_json_filter_test_basic (EvdJsonFilterFixture *f, gconstpointer test_data) { gint i; GError *error = NULL; const gchar *wrong[] = { "null", "true", "false", "1", "\"hello world!\"", "{]", "[}", "}}", "]]", "{foo: 123}", "{\"foo\":]", "{:\"bar\"]", "[\"bar\",]" }; const gchar *good[] = { "{}", "[]", " { } [ ] ", "{\"foo\":123}", "[null,true,false]", "[1, 0.01, 3.12e5, -666.99E+12, -0.23e-5]", "[\"hello world!\", \"foo (\\\"bar') \"]", "{\"obj\":{\"null\": true},\"arr\":[false]}" }; /* wrong */ for (i=0; ifilter, wrong[i], strlen (wrong[i]), &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_error_free (error); error = NULL; } /* good */ for (i=0; ifilter, good[i], &error)); g_assert_no_error (error); } } static void evd_json_filter_test_chunked_on_packet (EvdJsonFilter *filter, const gchar *buffer, gsize size, gpointer user_data) { EvdJsonFilterFixture *f = (EvdJsonFilterFixture *) user_data; g_assert (EVD_IS_JSON_FILTER (filter)); g_assert_cmpint (g_strcmp0 (buffer, evd_json_filter_packets[f->packet_index]), ==, 0); f->packet_index++; } static void evd_json_filter_test_chunked (EvdJsonFilterFixture *f, gconstpointer test_data) { gint i; GError *error = NULL; evd_json_filter_set_packet_handler (f->filter, (EvdJsonFilterOnPacketHandler) evd_json_filter_test_chunked_on_packet, (gpointer) f, NULL); for (i=0; ifilter, evd_json_filter_chunks[i], strlen (evd_json_filter_chunks[i]), &error)); g_assert_no_error (error); } } gint main (gint argc, gchar *argv[]) { #ifndef GLIB_VERSION_2_36 g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add ("/evd/json/filter/basic", EvdJsonFilterFixture, NULL, evd_json_filter_fixture_setup, evd_json_filter_test_basic, evd_json_filter_fixture_teardown); g_test_add ("/evd/json/filter/chunked", EvdJsonFilterFixture, NULL, evd_json_filter_fixture_setup, evd_json_filter_test_chunked, evd_json_filter_fixture_teardown); return g_test_run (); } EventDance-0.2.0/tests/test-pki.c000066400000000000000000000266311321356073300166060ustar00rootroot00000000000000/* * test-pki.c * * EventDance project - An event distribution framework (http://eventdance.org) * * Copyright (C) 2011-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #include #include const gchar *msg = "This is a secret message"; typedef struct { gchar *test_name; gchar *cert_filename; gchar *key_filename; EvdPkiKeyType key_type; gint error_code; GQuark error_domain; } TestCase; typedef struct { EvdTlsCertificate *cert; EvdTlsPrivkey *cert_key; EvdPkiPrivkey *privkey; EvdPkiPubkey *pubkey; GMainLoop *main_loop; gchar *enc_data; gchar *out_data; gchar *signature; gsize sig_size; const TestCase *test_case; } Fixture; const TestCase test_cases[] = { { "X.509/RSA", TESTS_DIR "certs/x509-server.pem", TESTS_DIR "certs/x509-server-key.pem", EVD_PKI_KEY_TYPE_RSA, 0, 0 }, /* @TODO: OpenPGP private key is failing in GnuTLS when exporting RSA params. This is likely caused by the way GnuPG exports private keys, making the secret part unusable */ /* { "OpenPGP/RSA", TESTS_DIR "certs/openpgp-server.asc", TESTS_DIR "certs/openpgp-server-key.asc", EVD_PKI_KEY_TYPE_RSA, 0, 0 } */ }; void fixture_setup (Fixture *f, gconstpointer test_data) { f->test_case = test_data; f->main_loop = g_main_loop_new (NULL, FALSE); f->enc_data = NULL; f->out_data = NULL; } void fixture_teardown (Fixture *f, gconstpointer test_data) { g_free (f->out_data); g_free (f->enc_data); g_main_loop_unref (f->main_loop); if (f->cert != NULL) g_object_unref (f->cert); if (f->cert_key != NULL) g_object_unref (f->cert_key); if (f->privkey != NULL) g_object_unref (f->privkey); if (f->pubkey != NULL) g_object_unref (f->pubkey); g_free (f->signature); } static gboolean compare_strings (const gchar *s1, const gchar *s2, gsize len) { gsize i; for (i = 0; i < len; i++) if (s1[i] != s2[i]) return FALSE; return TRUE; } static gboolean quit (gpointer user_data) { g_main_loop_quit (user_data); return FALSE; } static void test_privkey_basic (Fixture *f, gconstpointer test_data) { EvdPkiKeyType type; f->privkey = evd_pki_privkey_new (); g_assert (EVD_IS_PKI_PRIVKEY (f->privkey)); type = evd_pki_privkey_get_key_type (f->privkey); g_assert_cmpint (type, ==, EVD_PKI_KEY_TYPE_UNKNOWN); g_object_get (f->privkey, "type", &type, NULL); g_assert_cmpint (type, ==, EVD_PKI_KEY_TYPE_UNKNOWN); } static void test_pubkey_basic (Fixture *f, gconstpointer test_data) { EvdPkiKeyType type; f->pubkey = evd_pki_pubkey_new (); g_assert (EVD_IS_PKI_PUBKEY (f->pubkey)); type = evd_pki_pubkey_get_key_type (f->pubkey); g_assert_cmpint (type, ==, EVD_PKI_KEY_TYPE_UNKNOWN); g_object_get (f->pubkey, "type", &type, NULL); g_assert_cmpint (type, ==, EVD_PKI_KEY_TYPE_UNKNOWN); } static gboolean load_cert_and_key (Fixture *f, const gchar *cert_filename, const gchar *key_filename) { gchar *data; GError *error = NULL; gsize len; EvdPkiKeyType key_type; /* load TLS certificate */ g_file_get_contents (cert_filename, &data, &len, &error); g_assert_no_error (error); f->cert = evd_tls_certificate_new (); evd_tls_certificate_import (f->cert, data, len, &error); g_assert_no_error (error); g_free (data); /* load TLS private key */ g_file_get_contents (key_filename, &data, &len, &error); g_assert_no_error (error); f->cert_key = evd_tls_privkey_new (); evd_tls_privkey_import (f->cert_key, data, len, &error); g_assert_no_error (error); g_free (data); /* get PKI public key from certificate */ f->pubkey = evd_tls_certificate_get_pki_key (f->cert, &error); g_assert_no_error (error); g_assert (EVD_IS_PKI_PUBKEY (f->pubkey)); /* get PKI private key from certificate key */ f->privkey = evd_tls_privkey_get_pki_key (f->cert_key, &error); g_assert_no_error (error); g_assert (EVD_IS_PKI_PRIVKEY (f->privkey)); /* validate key type */ key_type = evd_pki_privkey_get_key_type (f->privkey); g_assert_cmpint (key_type, ==, f->test_case->key_type); key_type = evd_pki_pubkey_get_key_type (f->pubkey); g_assert_cmpint (key_type, ==, f->test_case->key_type); return TRUE; } static void privkey_on_decrypt (GObject *obj, GAsyncResult *res, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; gsize size; f->out_data = evd_pki_privkey_decrypt_finish (EVD_PKI_PRIVKEY (obj), res, &size, &error); if (f->test_case->error_code == 0) { g_assert_no_error (error); g_assert (f->out_data != NULL); g_assert_cmpint (size, ==, strlen (msg)); g_assert (compare_strings (f->out_data, msg, size)); } else { g_assert_error (error, f->test_case->error_domain, f->test_case->error_code); } g_idle_add (quit, f->main_loop); } static void pubkey_on_encrypt (GObject *obj, GAsyncResult *res, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; gsize size; f->enc_data = evd_pki_pubkey_encrypt_finish (EVD_PKI_PUBKEY (obj), res, &size, &error); if (f->test_case->error_code == 0) { g_assert_no_error (error); g_assert (f->enc_data != NULL); g_assert_cmpint (size, >, 0); evd_pki_privkey_decrypt (f->privkey, f->enc_data, size, NULL, privkey_on_decrypt, f); } else { g_assert_error (error, f->test_case->error_domain, f->test_case->error_code); g_idle_add (quit, f->main_loop); } } static void test_pubkey_encrypt (Fixture *f, gconstpointer test_data) { load_cert_and_key (f, f->test_case->cert_filename, f->test_case->key_filename); evd_pki_pubkey_encrypt (f->pubkey, msg, strlen (msg), NULL, pubkey_on_encrypt, f); g_main_loop_run (f->main_loop); } static void on_privkey_generated (GObject *obj, GAsyncResult *res, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; gboolean result; g_assert (obj != NULL && EVD_IS_PKI_PRIVKEY (obj)); g_assert (obj == G_OBJECT (f->privkey)); result = evd_pki_privkey_generate_finish (EVD_PKI_PRIVKEY (obj), res, &error); g_assert_no_error (error); g_assert (result); f->pubkey = evd_pki_privkey_get_public_key (f->privkey, &error); g_assert_no_error (error); g_assert (f->pubkey != NULL && EVD_IS_PKI_PUBKEY (f->pubkey)); test_pubkey_encrypt (f, f->test_case); g_idle_add (quit, f->main_loop); } static void test_gen_key_pair (Fixture *f, gconstpointer test_data) { f->privkey = evd_pki_privkey_new (); evd_pki_privkey_generate (f->privkey, f->test_case->key_type, 1024, NULL, on_privkey_generated, f); g_main_loop_run (f->main_loop); } static void pubkey_on_verify (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdPkiPubkey *key; GError *error = NULL; Fixture *f = user_data; gboolean verification; g_assert (EVD_IS_PKI_PUBKEY (obj)); g_assert (G_IS_SIMPLE_ASYNC_RESULT (result)); key = EVD_PKI_PUBKEY (obj); verification = evd_pki_pubkey_verify_data_finish (key, result, &error); g_assert_no_error (error); g_assert (verification == TRUE); g_main_loop_quit (f->main_loop); } static void pubkey_verify (Fixture *f) { evd_pki_pubkey_verify_data (f->pubkey, msg, strlen (msg), f->signature, f->sig_size, NULL, pubkey_on_verify, f); } static void privkey_on_sign (GObject *obj, GAsyncResult *result, gpointer user_data) { EvdPkiPrivkey *key; GError *error = NULL; Fixture *f = user_data; g_assert (EVD_IS_PKI_PRIVKEY (obj)); g_assert (G_IS_SIMPLE_ASYNC_RESULT (result)); key = EVD_PKI_PRIVKEY (obj); f->signature = evd_pki_privkey_sign_data_finish (key, result, &f->sig_size, &error); g_assert_no_error (error); g_assert (f->signature != NULL); pubkey_verify (f); } static void test_privkey_sign (Fixture *f, gconstpointer test_data) { load_cert_and_key (f, f->test_case->cert_filename, f->test_case->key_filename); evd_pki_privkey_sign_data (f->privkey, msg, strlen (msg), NULL, privkey_on_sign, f); g_main_loop_run (f->main_loop); } gint main (gint argc, gchar *argv[]) { gint i; evd_tls_init (NULL); g_test_init (&argc, &argv, NULL); g_test_add ("/evd/pki/private-key/basic", Fixture, NULL, fixture_setup, test_privkey_basic, fixture_teardown); g_test_add ("/evd/pki/public-key/basic", Fixture, NULL, fixture_setup, test_pubkey_basic, fixture_teardown); for (i = 0; i < sizeof (test_cases) / sizeof (TestCase); i++) { gchar *test_name; /* encrypt with public key, decrypt with private */ test_name = g_strdup_printf ("/evd/pki/%s/encrypt-decrypt", test_cases[i].test_name); g_test_add (test_name, Fixture, &test_cases[i], fixture_setup, test_pubkey_encrypt, fixture_teardown); g_free (test_name); /* sign with private key, verify with public */ test_name = g_strdup_printf ("/evd/pki/%s/sign-verify", test_cases[i].test_name); g_test_add (test_name, Fixture, &test_cases[i], fixture_setup, test_privkey_sign, fixture_teardown); g_free (test_name); } /* generate RSA key-pair */ g_test_add ("/evd/pki/generate-key-pair/RSA", Fixture, &test_cases[0], fixture_setup, test_gen_key_pair, fixture_teardown); evd_tls_deinit (); return g_test_run (); } EventDance-0.2.0/tests/test-promise.c000066400000000000000000000241401321356073300174720ustar00rootroot00000000000000/* * test-promise.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #include #include typedef struct { GObject *some_object; GCancellable *cancellable; EvdDeferred *deferred; EvdDeferred *deferred1; GMainLoop *main_loop; guint num_listeners; guint num_callbacks; } Fixture; void some_function_as_tag (void) { } static void fixture_setup (Fixture *f, gconstpointer test_data) { f->some_object = g_object_new (G_TYPE_OBJECT, NULL); f->cancellable = g_cancellable_new (); f->deferred = evd_deferred_new (f->some_object, f->cancellable, some_function_as_tag); f->deferred1 = evd_deferred_new (NULL, NULL, NULL); f->main_loop = g_main_loop_new (NULL, FALSE); f->num_listeners = 0; f->num_callbacks = 0; } static void fixture_teardown (Fixture *f, gconstpointer test_data) { EvdPromise *promise; promise = evd_deferred_get_promise (f->deferred); g_assert_cmpint (G_OBJECT (promise)->ref_count, ==, 1); evd_deferred_unref (f->deferred); promise = evd_deferred_get_promise (f->deferred1); g_assert_cmpint (G_OBJECT (promise)->ref_count, ==, 1); evd_deferred_unref (f->deferred1); g_assert_cmpint (f->some_object->ref_count, ==, 1); g_object_unref (f->some_object); g_assert_cmpint (G_OBJECT (f->cancellable)->ref_count, ==, 1); g_object_unref (f->cancellable); g_main_loop_unref (f->main_loop); } static void test_basic (Fixture *f, gconstpointer test_data) { EvdPromise *promise; g_assert (f->deferred != NULL); promise = evd_deferred_get_promise (f->deferred); g_assert (G_IS_ASYNC_RESULT (promise)); g_assert (EVD_IS_PROMISE (promise)); g_assert (g_async_result_get_source_object (G_ASYNC_RESULT (promise)) == f->some_object); g_assert_cmpint (f->some_object->ref_count, ==, 3); g_object_unref (f->some_object); g_assert (evd_promise_get_cancellable (promise) == f->cancellable); g_assert (g_async_result_get_user_data (G_ASYNC_RESULT (promise)) == NULL); g_assert (g_async_result_is_tagged (G_ASYNC_RESULT (promise), some_function_as_tag)); g_assert (f->deferred1 != NULL); promise = evd_deferred_get_promise (f->deferred1); g_assert (G_IS_ASYNC_RESULT (promise)); g_assert (EVD_IS_PROMISE (promise)); g_assert (evd_promise_get_cancellable (promise) == NULL); g_assert (g_async_result_get_source_object (G_ASYNC_RESULT (promise)) == NULL); g_assert (g_async_result_get_user_data (G_ASYNC_RESULT (promise)) == NULL); g_assert (g_async_result_is_tagged (G_ASYNC_RESULT (promise), NULL)); } static void test_results (Fixture *f, gconstpointer test_data) { EvdPromise *promise; GError *error = NULL; /* result pointer */ promise = evd_deferred_get_promise (f->deferred); g_object_ref (f->some_object); evd_deferred_set_result_pointer (f->deferred, f->some_object, g_object_unref); evd_deferred_complete (f->deferred); g_assert (evd_promise_get_result_pointer (promise) == f->some_object); g_assert (evd_promise_get_result_size (promise) == 0); g_assert (evd_promise_get_result_boolean (promise) == FALSE); g_assert (evd_promise_propagate_error (promise, &error) == FALSE); g_assert_no_error (error); evd_deferred_unref (f->deferred); /* result size */ f->deferred = evd_deferred_new (NULL, NULL, NULL); promise = evd_deferred_get_promise (f->deferred); evd_deferred_set_result_size (f->deferred, -1); evd_deferred_complete (f->deferred); g_assert (evd_promise_get_result_pointer (promise) == NULL); g_assert (evd_promise_get_result_size (promise) == -1); g_assert (evd_promise_get_result_boolean (promise) == FALSE); g_assert (evd_promise_propagate_error (promise, &error) == FALSE); g_assert_no_error (error); evd_deferred_unref (f->deferred); /* result boolean */ f->deferred = evd_deferred_new (NULL, NULL, NULL); promise = evd_deferred_get_promise (f->deferred); evd_deferred_set_result_boolean (f->deferred, TRUE); evd_deferred_complete (f->deferred); g_assert (evd_promise_get_result_pointer (promise) == NULL); g_assert (evd_promise_get_result_size (promise) == 0); g_assert (evd_promise_get_result_boolean (promise) == TRUE); g_assert (evd_promise_propagate_error (promise, &error) == FALSE); g_assert_no_error (error); evd_deferred_unref (f->deferred); /* result error */ f->deferred = evd_deferred_new (NULL, NULL, NULL); promise = evd_deferred_get_promise (f->deferred); evd_deferred_take_result_error (f->deferred, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Some dummy error")); evd_deferred_complete (f->deferred); g_assert (evd_promise_get_result_pointer (promise) == NULL); g_assert (evd_promise_get_result_size (promise) == 0); g_assert (evd_promise_get_result_boolean (promise) == FALSE); g_assert (evd_promise_propagate_error (promise, &error) == TRUE); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_clear_error (&error); evd_deferred_unref (f->deferred); /* result no error */ f->deferred = evd_deferred_new (NULL, NULL, NULL); promise = evd_deferred_get_promise (f->deferred); evd_deferred_complete (f->deferred); g_assert (evd_promise_propagate_error (promise, &error) == FALSE); g_assert_no_error (error); } static void promise_on_resolved (GObject *obj, GAsyncResult *result, gpointer user_data) { GError *error = NULL; EvdPromise *promise; Fixture *f = user_data; g_assert (G_IS_OBJECT (obj)); g_assert (obj == f->some_object); g_assert (EVD_IS_PROMISE (result)); g_assert (f != NULL); g_assert (g_async_result_get_source_object (result) == obj); /* g_async_result_get_source_object() increases reference count */ g_object_unref (obj); g_assert (g_async_result_get_user_data (result) == f); promise = EVD_PROMISE (result); g_assert (! evd_promise_propagate_error (promise, &error)); g_assert_no_error (error); g_assert (evd_promise_get_result_pointer (promise) == f->some_object); g_assert (evd_promise_get_result_size (promise) == 0); g_assert (evd_promise_get_result_boolean (promise) == FALSE); f->num_callbacks++; if (f->num_callbacks == f->num_listeners) g_main_loop_quit (f->main_loop); } static void test_then (Fixture *f, gconstpointer test_data) { EvdPromise *promise; promise = evd_deferred_get_promise (f->deferred); f->num_listeners = 1; evd_promise_then (promise, promise_on_resolved, f); g_object_ref (f->some_object); evd_deferred_set_result_pointer (f->deferred, f->some_object, g_object_unref); evd_deferred_complete_in_idle (f->deferred); g_main_loop_run (f->main_loop); } static void test_many_listeners (Fixture *f, gconstpointer test_data) { EvdPromise *promise; gint i; promise = evd_deferred_get_promise (f->deferred); g_object_ref (f->some_object); evd_deferred_set_result_pointer (f->deferred, f->some_object, g_object_unref); evd_deferred_complete_in_idle (f->deferred); for (i = 0; i < 10; i++) { f->num_listeners++; evd_promise_then (promise, promise_on_resolved, f); } g_main_loop_run (f->main_loop); } static void promise_on_resolved_cancelled (GObject *obj, GAsyncResult *result, gpointer user_data) { GError *error = NULL; EvdPromise *promise; Fixture *f = user_data; g_assert (G_IS_OBJECT (obj)); g_assert (EVD_IS_PROMISE (result)); g_assert (f != NULL); promise = EVD_PROMISE (result); g_assert (evd_promise_propagate_error (promise, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert (evd_promise_get_result_pointer (promise) == NULL); g_assert (evd_promise_get_result_size (promise) == 0); g_assert (evd_promise_get_result_boolean (promise) == FALSE); g_main_loop_quit (f->main_loop); } static void promise_on_cancelled (GCancellable *cancellable, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; g_set_error (&error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled"); evd_deferred_take_result_error (f->deferred, error); evd_deferred_complete_in_idle (f->deferred); } static void test_cancel (Fixture *f, gconstpointer test_data) { EvdPromise *promise; GCancellable *cancellable; promise = evd_deferred_get_promise (f->deferred); cancellable = evd_promise_get_cancellable (promise); g_signal_connect (cancellable, "cancelled", G_CALLBACK (promise_on_cancelled), f); evd_promise_then (promise, promise_on_resolved_cancelled, f); evd_promise_cancel (promise); g_main_loop_run (f->main_loop); } gint main (gint argc, gchar *argv[]) { #ifndef GLIB_VERSION_2_36 g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add ("/evd/promise/basic", Fixture, NULL, fixture_setup, test_basic, fixture_teardown); g_test_add ("/evd/promise/results", Fixture, NULL, fixture_setup, test_results, fixture_teardown); g_test_add ("/evd/promise/then", Fixture, NULL, fixture_setup, test_then, fixture_teardown); g_test_add ("/evd/promise/many-listeners", Fixture, NULL, fixture_setup, test_many_listeners, fixture_teardown); g_test_add ("/evd/promise/cancel", Fixture, NULL, fixture_setup, test_cancel, fixture_teardown); return g_test_run (); } EventDance-0.2.0/tests/test-resolver.c000066400000000000000000000240301321356073300176530ustar00rootroot00000000000000/* * test-resolver.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2009-2014, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #ifdef HAVE_GIO_UNIX #include #endif #include "evd-resolver.h" #define UNIX_ADDR "/this-is-any-unix-addr" #define IPV4_1 "192.168.0.1:1234" #define IPV6_1 "[::1]:4321" #define RESOLVE_GOOD_LOCALHOST "localhost:80" #define CANCEL "172.16.1.1:22" #define RESOLVE_CANCEL "localhost:80" #define NONEXISTANT_1 "127.0.0.0.1" #define NONEXISTANT_2 "nonexistentdomain" typedef struct { GMainLoop *main_loop; EvdResolver *resolver; } Fixture; static void fixture_setup (Fixture *f, gconstpointer test_data) { f->resolver = evd_resolver_get_default (); f->main_loop = g_main_loop_new (NULL, FALSE); } static void fixture_teardown (Fixture *f, gconstpointer test_data) { g_object_unref (f->resolver); g_main_loop_unref (f->main_loop); } static void get_default (Fixture *f, gconstpointer test_data) { EvdResolver *other_resolver; g_assert (EVD_IS_RESOLVER (f->resolver)); g_assert_cmpint (G_OBJECT (f->resolver)->ref_count, ==, 1); other_resolver = evd_resolver_get_default (); g_assert (f->resolver == other_resolver); g_assert_cmpint (G_OBJECT (f->resolver)->ref_count, ==, 2); g_object_unref (other_resolver); } #ifdef HAVE_GIO_UNIX /* unix-addr */ static void unix_addr_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GError *error = NULL; GList *addresses; GSocketAddress *addr; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses != NULL); g_assert_no_error (error); g_assert_cmpint (g_list_length (addresses), ==, 1); addr = G_SOCKET_ADDRESS (addresses->data); g_assert (G_IS_SOCKET_ADDRESS (addr)); g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_UNIX); g_assert_cmpstr (g_unix_socket_address_get_path (G_UNIX_SOCKET_ADDRESS (addr)), ==, UNIX_ADDR); g_main_loop_quit (f->main_loop); g_resolver_free_addresses (addresses); } static void unix_addr (Fixture *f, gconstpointer test_data) { evd_resolver_resolve (f->resolver, UNIX_ADDR, NULL, unix_addr_on_resolve, f); g_main_loop_run (f->main_loop); } #endif /* ipv4 */ static void ipv4_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GError *error = NULL; GList *addresses; GSocketAddress *addr; GInetAddress *inet_addr; gchar *addr_str; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses != NULL); g_assert_no_error (error); g_assert_cmpint (g_list_length (addresses), ==, 1); addr = G_SOCKET_ADDRESS (addresses->data); g_assert (G_IS_SOCKET_ADDRESS (addr)); g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_IPV4); g_assert_cmpint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)), ==, 1234); inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); addr_str = g_inet_address_to_string (inet_addr); g_assert_cmpstr (addr_str, ==, "192.168.0.1"); g_free (addr_str); g_object_unref (inet_addr); g_main_loop_quit (f->main_loop); } static void ipv4 (Fixture *f, gconstpointer test_data) { evd_resolver_resolve (f->resolver, IPV4_1, NULL, ipv4_on_resolve, f); g_main_loop_run (f->main_loop); } /* ipv6 */ static void ipv6_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GError *error = NULL; GList *addresses; GSocketAddress *addr; GInetAddress *inet_addr; gchar *addr_str; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses != NULL); g_assert_no_error (error); g_assert_cmpint (g_list_length (addresses), ==, 1); addr = G_SOCKET_ADDRESS (addresses->data); g_assert (G_IS_SOCKET_ADDRESS (addr)); g_assert_cmpint (g_socket_address_get_family (addr), ==, G_SOCKET_FAMILY_IPV6); g_assert_cmpint (g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)), ==, 4321); inet_addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); addr_str = g_inet_address_to_string (inet_addr); g_assert_cmpstr (addr_str, ==, "::1"); g_free (addr_str); g_object_unref (inet_addr); g_main_loop_quit (f->main_loop); } static void ipv6 (Fixture *f, gconstpointer test_data) { evd_resolver_resolve (f->resolver, IPV6_1, NULL, ipv6_on_resolve, f); g_main_loop_run (f->main_loop); } /* resolve good localhost */ static void resolve_good_localhost_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GError *error = NULL; GList *addresses; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses != NULL); g_assert_no_error (error); g_assert_cmpint (g_list_length (addresses), >=, 1); g_main_loop_quit (f->main_loop); } static void resolve_good_localhost (Fixture *f, gconstpointer test_data) { evd_resolver_resolve (f->resolver, RESOLVE_GOOD_LOCALHOST, NULL, resolve_good_localhost_on_resolve, f); g_main_loop_run (f->main_loop); } /* cancel */ /* static void resolve_cancel_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GError *error = NULL; GList *addresses; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses == NULL); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_error_free (error); g_main_loop_quit (f->main_loop); } */ static void resolve_cancel (Fixture *f, gconstpointer test_data) { /* @TODO: completes this test once EvdResolver supports cancelling */ } /* error */ static void resolve_error_on_resolve (GObject *obj, GAsyncResult *res, gpointer user_data) { EvdResolver *resolver; Fixture *f = (Fixture *) user_data; GList *addresses; GError *error = NULL; g_assert (EVD_IS_RESOLVER (obj)); resolver = EVD_RESOLVER (obj); g_assert (resolver == f->resolver); addresses = evd_resolver_resolve_finish (resolver, res, &error); g_assert (addresses == NULL); g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND); g_error_free (error); g_main_loop_quit (f->main_loop); } static void resolve_error (Fixture *f, gconstpointer test_data) { evd_resolver_resolve (f->resolver, NONEXISTANT_1, NULL, resolve_error_on_resolve, f); g_main_loop_run (f->main_loop); evd_resolver_resolve (f->resolver, NONEXISTANT_2, NULL, resolve_error_on_resolve, f); g_main_loop_run (f->main_loop); } gint main (gint argc, gchar **argv) { #ifndef GLIB_VERSION_2_36 g_type_init (); #endif g_test_init (&argc, &argv, NULL); g_test_add ("/evd/resolver/get-default", Fixture, NULL, fixture_setup, get_default, fixture_teardown); #ifdef HAVE_GIO_UNIX g_test_add ("/evd/resolver/unix", Fixture, NULL, fixture_setup, unix_addr, fixture_teardown); #endif g_test_add ("/evd/resolver/ipv4", Fixture, NULL, fixture_setup, ipv4, fixture_teardown); g_test_add ("/evd/resolver/ipv6", Fixture, NULL, fixture_setup, ipv6, fixture_teardown); g_test_add ("/evd/resolver/resolve", Fixture, NULL, fixture_setup, resolve_good_localhost, fixture_teardown); g_test_add ("/evd/resolver/cancel", Fixture, NULL, fixture_setup, resolve_cancel, fixture_teardown); if (g_test_slow ()) g_test_add ("/evd/resolver/error", Fixture, NULL, fixture_setup, resolve_error, fixture_teardown); return g_test_run (); } EventDance-0.2.0/tests/test-socket-common.c000066400000000000000000000300751321356073300205760ustar00rootroot00000000000000/* * test-socket-common.c * * EventDance project - An event distribution framework (http://eventdance.org) * * Copyright (C) 2009, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef __TEST_SOCKET_COMMON_C__ #define __TEST_SOCKET_COMMON_C__ #include #include #include #include #include #define EVD_SOCKET_TEST_UNREAD_TEXT "Once upon a time " #define EVD_SOCKET_TEST_TEXT1 "in a very remote land... " #define EVD_SOCKET_TEST_TEXT2 "and they lived in joy forever." typedef struct { GMainLoop *main_loop; EvdSocket *socket; EvdSocket *socket1; EvdSocket *socket2; GSocketAddress *socket_addr; gint break_src_id; gboolean bind; gboolean listen; gboolean connect; gboolean new_conn; gsize total_read; gint total_closed; gboolean completed; } EvdSocketFixture; static void evd_socket_fixture_setup (EvdSocketFixture *fixture, gconstpointer test_data) { fixture->main_loop = g_main_loop_new (NULL, FALSE); fixture->break_src_id = 0; fixture->socket = evd_socket_new (); fixture->socket1 = evd_socket_new (); fixture->socket2 = NULL; fixture->socket_addr = NULL; fixture->bind = FALSE; fixture->listen = FALSE; fixture->connect = FALSE; fixture->new_conn = FALSE; fixture->total_read = 0; fixture->total_closed = 0; fixture->completed = FALSE; g_assert (evd_socket_manager_get () == NULL); } static gboolean evd_socket_test_break (gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; if (f->main_loop != NULL) { if (f->break_src_id != 0) g_source_remove (f->break_src_id); g_main_context_wakeup (g_main_loop_get_context (f->main_loop)); g_main_loop_quit (f->main_loop); g_main_loop_unref (f->main_loop); f->main_loop = NULL; } return FALSE; } static void evd_socket_fixture_teardown (EvdSocketFixture *fixture, gconstpointer test_data) { evd_socket_test_break ((gpointer) fixture); g_object_unref (fixture->socket); g_object_unref (fixture->socket1); if (fixture->socket2 != NULL) g_object_unref (fixture->socket2); if (fixture->socket_addr != NULL) g_object_unref (fixture->socket_addr); g_assert (evd_socket_manager_get () == NULL); } static void evd_socket_test_config (EvdSocket *socket, GSocketFamily family, GSocketType type, GSocketProtocol protocol) { GSocketFamily _family; GSocketType _type; GSocketProtocol _protocol; g_object_get (socket, "family", &_family, "protocol", &_protocol, "type", &_type, NULL); g_assert (family == _family); g_assert (type == _type); g_assert (protocol == _protocol); } static void evd_socket_test_on_error (EvdSocket *self, gint code, gchar *message, gpointer user_data) { g_error ("%s", message); g_assert_not_reached (); } static void evd_socket_test_on_close (EvdSocket *self, gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; f->total_closed ++; /* if all socket closed, finish mainloop */ if (f->total_closed == 2) { f->completed = TRUE; evd_socket_test_break ((gpointer) f); } } static void evd_socket_test_on_read (EvdSocket *self, gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; GError *error = NULL; gchar buf[1024] = { 0, }; gssize size; gchar *expected_st; gssize expected_size; GInputStream *input_stream; g_assert (evd_socket_can_read (self)); input_stream = evd_socket_get_input_stream (self); size = g_input_stream_read (input_stream, buf, 1023, NULL, &error); g_assert_no_error (error); if (size == 0) return; /* validate text read */ expected_size = strlen (EVD_SOCKET_TEST_UNREAD_TEXT) + strlen (EVD_SOCKET_TEST_TEXT1) + strlen (EVD_SOCKET_TEST_TEXT2); g_assert_cmpint (size, ==, expected_size); expected_st = g_strconcat (EVD_SOCKET_TEST_UNREAD_TEXT, EVD_SOCKET_TEST_TEXT1, EVD_SOCKET_TEST_TEXT2, NULL); g_assert_cmpstr (buf, ==, expected_st); g_free (expected_st); /* close socket if finished reading */ f->total_read += size; if (f->total_read == (strlen (EVD_SOCKET_TEST_UNREAD_TEXT) + strlen (EVD_SOCKET_TEST_TEXT1) + strlen (EVD_SOCKET_TEST_TEXT2)) * 2) { g_assert (evd_socket_close (self, &error)); g_assert_no_error (error); } } static void evd_socket_test_on_write (EvdSocket *self, gpointer user_data) { GError *error = NULL; gssize size; g_assert (evd_socket_can_write (self)); evd_socket_unread (self, EVD_SOCKET_TEST_UNREAD_TEXT, strlen (EVD_SOCKET_TEST_UNREAD_TEXT), &error); g_assert_no_error (error); /* @TODO: 'evd_socket_can_read' should check if the EvdBufferedInputStream has unread data. Bypassing by now. */ /* g_assert (evd_socket_can_read (self) == TRUE); */ size = evd_socket_write (self, EVD_SOCKET_TEST_TEXT1, strlen (EVD_SOCKET_TEST_TEXT1), &error); g_assert_no_error (error); g_assert_cmpint (size, ==, strlen (EVD_SOCKET_TEST_TEXT1)); size = evd_socket_write (self, EVD_SOCKET_TEST_TEXT2, strlen (EVD_SOCKET_TEST_TEXT2), &error); g_assert_no_error (error); g_assert_cmpint (size, ==, strlen (EVD_SOCKET_TEST_TEXT2)); } static void evd_socket_test_on_new_conn (EvdSocket *self, EvdSocket *client, gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; f->new_conn = TRUE; g_assert (EVD_IS_SOCKET (self)); g_assert (EVD_IS_SOCKET (client)); g_assert_cmpint (evd_socket_get_status (client), ==, EVD_SOCKET_STATE_CONNECTED); g_assert (evd_socket_get_socket (client) != NULL); evd_socket_base_set_read_handler (EVD_SOCKET_BASE (client), G_CALLBACK (evd_socket_test_on_read), f); g_assert (evd_socket_base_get_on_read (EVD_SOCKET_BASE (client)) != NULL); evd_socket_base_set_write_handler (EVD_SOCKET_BASE (client), G_CALLBACK (evd_socket_test_on_write), f); g_assert (evd_socket_base_get_on_write (EVD_SOCKET_BASE (client)) != NULL); f->socket2 = client; g_signal_connect (f->socket2, "error", G_CALLBACK (evd_socket_test_on_error), (gpointer) f); g_signal_connect (f->socket2, "close", G_CALLBACK (evd_socket_test_on_close), (gpointer) f); g_object_ref (f->socket2); } static void evd_socket_test_on_state_changed (EvdSocket *self, EvdSocketState new_state, EvdSocketState old_state, gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; GSocketAddress *address; GError *error = NULL; switch (new_state) { case EVD_SOCKET_STATE_BOUND: { f->bind = TRUE; address = evd_socket_get_local_address (self, &error); g_assert_no_error (error); g_assert (EVD_IS_SOCKET (f->socket)); g_assert_cmpint (evd_socket_get_status (self), ==, EVD_SOCKET_STATE_BOUND); g_assert (evd_socket_get_socket (self) != NULL); g_assert (G_IS_SOCKET_ADDRESS (address)); evd_socket_test_config (self, g_socket_address_get_family ( G_SOCKET_ADDRESS (f->socket_addr)), G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT); break; } case EVD_SOCKET_STATE_LISTENING: { f->listen = TRUE; g_assert (EVD_IS_SOCKET (self)); g_assert_cmpint (evd_socket_get_status (self), ==, EVD_SOCKET_STATE_LISTENING); g_assert (evd_socket_get_socket (self) != NULL); break; } case EVD_SOCKET_STATE_CONNECTED: { f->connect = TRUE; g_assert (EVD_IS_SOCKET (self)); g_assert_cmpint (evd_socket_get_status (self), ==, EVD_SOCKET_STATE_CONNECTED); g_assert (evd_socket_get_socket (self) != NULL); break; } default: break; } } static gboolean evd_socket_launch_test (gpointer user_data) { EvdSocketFixture *f = (EvdSocketFixture *) user_data; GError *error = NULL; g_signal_connect (f->socket, "error", G_CALLBACK (evd_socket_test_on_error), (gpointer) f); g_signal_connect (f->socket1, "error", G_CALLBACK (evd_socket_test_on_error), (gpointer) f); g_signal_connect (f->socket, "close", G_CALLBACK (evd_socket_test_on_close), (gpointer) f); g_signal_connect (f->socket1, "close", G_CALLBACK (evd_socket_test_on_close), (gpointer) f); evd_socket_base_set_read_handler (EVD_SOCKET_BASE (f->socket1), G_CALLBACK (evd_socket_test_on_read), f); g_assert (evd_socket_base_get_on_read (EVD_SOCKET_BASE (f->socket1)) != NULL); evd_socket_base_set_write_handler (EVD_SOCKET_BASE (f->socket1), G_CALLBACK (evd_socket_test_on_write), f); g_assert (evd_socket_base_get_on_write (EVD_SOCKET_BASE (f->socket1)) != NULL); g_signal_connect (f->socket, "state-changed", G_CALLBACK (evd_socket_test_on_state_changed), (gpointer) f); g_signal_connect (f->socket1, "state-changed", G_CALLBACK (evd_socket_test_on_state_changed), (gpointer) f); evd_socket_bind_addr (f->socket, f->socket_addr, TRUE, &error); g_assert_no_error (error); evd_socket_listen_addr (f->socket, NULL, &error); g_assert_no_error (error); /* connect */ g_signal_connect (f->socket, "new-connection", G_CALLBACK (evd_socket_test_on_new_conn), (gpointer) f); evd_socket_connect_addr (f->socket1, f->socket_addr, &error); g_assert_no_error (error); g_assert_cmpint (evd_socket_get_status (f->socket1), ==, EVD_SOCKET_STATE_CONNECTING); return FALSE; } static void evd_socket_test (EvdSocketFixture *f, gconstpointer test_data) { f->break_src_id = g_timeout_add (1000, (GSourceFunc) evd_socket_test_break, (gpointer) f); g_idle_add ((GSourceFunc) evd_socket_launch_test, (gpointer) f); g_main_loop_run (f->main_loop); g_assert (f->bind); g_assert (f->listen); g_assert (f->connect); g_assert (f->new_conn); g_assert (f->completed); } #endif /* __TEST_SOCKET_COMMON_C__ */ EventDance-0.2.0/tests/test-socket-context.c000066400000000000000000000227121321356073300207710ustar00rootroot00000000000000/* * test-socket-context.c * * EventDance project - An event distribution framework (http://eventdance.org) * * Copyright (C) 2009, Igalia S.L. * * Authors: * Eduardo Lima Mitev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #define THREADS 25 #define SOCKETS_PER_THREAD 5 #define DATA_SIZE 65535 #define BLOCK_SIZE 32752 #define TOTAL_DATA_SIZE DATA_SIZE * THREADS * SOCKETS_PER_THREAD #define INET_PORT 5555 GMainLoop *main_loop_server; EvdSocket *server; EvdSocketGroup *group_senders; EvdSocketGroup *group_receivers; static gchar data[DATA_SIZE]; static gsize total_read = 0; static guint clients_done = 0; static guint sockets_closed = 0; static GMainLoop *main_loops[THREADS]; static GThread *threads[THREADS]; G_LOCK_DEFINE_STATIC (sockets_closed); G_LOCK_DEFINE_STATIC (total_read); static void client_on_state_changed (EvdSocket *socket, EvdSocketState new_state, EvdSocketState old_state, gpointer user_data) { g_assert (EVD_IS_SOCKET (socket)); g_assert_cmpint (new_state, !=, old_state); } static void client_on_close (EvdSocket *socket, gpointer user_data) { g_assert (EVD_IS_SOCKET (socket)); g_assert_cmpint (evd_socket_get_status (socket), ==, EVD_SOCKET_STATE_CLOSED); G_LOCK (sockets_closed); sockets_closed ++; if (sockets_closed == THREADS * SOCKETS_PER_THREAD * 2) { gint i; GMainContext *context; for (i=0; i=, 0); G_LOCK (total_read); total_read += size; G_UNLOCK (total_read); if (evd_socket_base_get_total_read (EVD_SOCKET_BASE (socket)) == DATA_SIZE) { g_assert (evd_socket_close (socket, &error)); g_assert_no_error (error); g_assert_cmpint (evd_socket_get_status (socket), ==, EVD_SOCKET_STATE_CLOSING); } if (size == BLOCK_SIZE) return TRUE; else return FALSE; } static void group_socket_on_read (EvdSocketGroup *self, EvdSocket *socket, gpointer user_data) { g_assert (EVD_IS_SOCKET_GROUP (self)); g_assert (self == group_receivers); g_assert (EVD_IS_SOCKET (socket)); g_assert_cmpint (evd_socket_get_status (socket), ==, EVD_SOCKET_STATE_CONNECTED); evd_timeout_add (g_main_context_get_thread_default (), 0, G_PRIORITY_DEFAULT, socket_do_read, socket); } static void group_socket_on_write (EvdSocketGroup *self, EvdSocket *socket, gpointer user_data) { gulong total_sent; g_assert (EVD_IS_SOCKET_GROUP (self)); g_assert (self == group_senders); g_assert (EVD_IS_SOCKET (socket)); total_sent = evd_socket_base_get_total_written (EVD_SOCKET_BASE (socket)); if (total_sent < DATA_SIZE) { GError *error = NULL; g_assert_cmpint (evd_socket_write (socket, (gchar *) (((guintptr) data) + total_sent), DATA_SIZE - total_sent, &error), >=, 0); } } static gpointer thread_handler (gpointer user_data) { GMainContext *main_context; GMainLoop *main_loop; EvdSocket *sockets[SOCKETS_PER_THREAD]; gint *thread_id = (gint *) user_data; gint i; gchar *client_addr; main_context = g_main_context_new (); g_main_context_push_thread_default (main_context); main_loop = g_main_loop_new (main_context, FALSE); G_LOCK (sockets_closed); main_loops[*thread_id] = main_loop; G_UNLOCK (sockets_closed); client_addr = g_strdup_printf ("%s:%d", "127.0.0.1", INET_PORT); /* create client sockets for this context */ for (i=0; i * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 3 as published by the Free Software Foundation. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include #ifdef HAVE_GIO_UNIX #include #endif #include #include #include "test-socket-common.c" /* test initial state */ static void evd_socket_test_initial_state (EvdSocketFixture *f, gconstpointer test_data) { /* EvdStream */ g_assert (EVD_IS_SOCKET_BASE (f->socket)); g_assert (EVD_IS_SOCKET (f->socket)); g_assert (evd_socket_base_get_on_read (EVD_SOCKET_BASE (f->socket)) == NULL); g_assert (evd_socket_base_get_on_write (EVD_SOCKET_BASE (f->socket)) == NULL); g_assert_cmpuint (evd_socket_base_get_total_read (EVD_SOCKET_BASE (f->socket)), ==, 0); g_assert_cmpuint (evd_socket_base_get_total_written (EVD_SOCKET_BASE (f->socket)), ==, 0); /* EvdSocket */ g_assert (evd_socket_get_socket (f->socket) == NULL); g_assert (evd_socket_get_context (f->socket) == NULL); g_assert (evd_socket_get_group (f->socket) == NULL); g_assert_cmpint (evd_socket_get_status (f->socket), ==, EVD_SOCKET_STATE_CLOSED); g_assert_cmpint (evd_socket_get_priority (f->socket), ==, G_PRIORITY_DEFAULT); evd_socket_test_config (f->socket, G_SOCKET_FAMILY_INVALID, G_SOCKET_TYPE_INVALID, G_SOCKET_PROTOCOL_UNKNOWN); g_assert (evd_socket_can_read (f->socket) == FALSE); g_assert (evd_socket_can_write (f->socket) == FALSE); } /* test inet socket */ static void evd_socket_inet_ipv4_fixture_setup (EvdSocketFixture *fixture, gconstpointer test_data) { gint port; GInetAddress *inet_addr; evd_socket_fixture_setup (fixture, test_data); inet_addr = g_inet_address_new_from_string ("127.0.0.1"); port = g_random_int_range (1024, 0xFFFF-1); fixture->socket_addr = g_inet_socket_address_new (inet_addr, port); g_object_unref (inet_addr); } /* test inet socket */ static void evd_socket_inet_ipv6_fixture_setup (EvdSocketFixture *fixture, gconstpointer test_data) { gint port; GInetAddress *inet_addr; evd_socket_fixture_setup (fixture, test_data); inet_addr = g_inet_address_new_from_string ("::1"); port = g_random_int_range (1024, 0xFFFF-1); fixture->socket_addr = g_inet_socket_address_new (inet_addr, port); g_object_unref (inet_addr); } /* test unix socket */ static void evd_socket_unix_fixture_setup (EvdSocketFixture *fixture, gconstpointer test_data) { const gchar *UNIX_FILENAME = "/tmp/evd-test-socket-unix"; evd_socket_fixture_setup (fixture, test_data); g_unlink (UNIX_FILENAME); fixture->socket_addr = G_SOCKET_ADDRESS (g_unix_socket_address_new (UNIX_FILENAME)); } gint main (gint argc, gchar *argv[]) { g_type_init (); g_test_init (&argc, &argv, NULL); g_test_add ("/evd/socket/initial-state", EvdSocketFixture, NULL, evd_socket_fixture_setup, evd_socket_test_initial_state, evd_socket_fixture_teardown); #ifdef HAVE_GIO_UNIX g_test_add ("/evd/socket/unix", EvdSocketFixture, NULL, evd_socket_unix_fixture_setup, evd_socket_test, evd_socket_fixture_teardown); #endif g_test_add ("/evd/socket/inet/ipv4", EvdSocketFixture, NULL, evd_socket_inet_ipv4_fixture_setup, evd_socket_test, evd_socket_fixture_teardown); g_test_add ("/evd/socket/inet/ipv6", EvdSocketFixture, NULL, evd_socket_inet_ipv6_fixture_setup, evd_socket_test, evd_socket_fixture_teardown); g_test_run (); return 0; } EventDance-0.2.0/tests/test-websocket-transport.c000066400000000000000000000165551321356073300220470ustar00rootroot00000000000000/* * test-websocket.c * * EventDance, Peer-to-peer IPC library * * Copyright (C) 2012-2013, Igalia S.L. * * Authors: * Eduardo Lima Mitev */ #include #define LISTEN_ADDR "0.0.0.0:%d" #define WS_ADDR "ws://127.0.0.1:%d/" typedef struct { gchar *test_name; gchar *msg; gssize msg_len; EvdMessageType msg_type; } TestCase; typedef struct { EvdWebsocketClient *ws_client; EvdWebsocketServer *ws_server; const TestCase *test_case; GMainLoop *main_loop; gboolean client_new_peer; guint listen_port; } Fixture; static const TestCase test_cases[] = { { "/text-message", "Hello World!", -1, EVD_MESSAGE_TYPE_TEXT }, { "/binary-message", "Hello\0World!\0", 13, EVD_MESSAGE_TYPE_BINARY } }; static void fixture_setup (Fixture *f, gconstpointer data) { f->ws_client = evd_websocket_client_new (); f->ws_server = evd_websocket_server_new (); f->main_loop = g_main_loop_new (NULL, FALSE); f->client_new_peer = FALSE; f->listen_port = g_random_int_range (1025, 65535); } static void fixture_teardown (Fixture *f, gconstpointer data) { g_object_unref (f->ws_client); g_object_unref (f->ws_server); g_main_loop_unref (f->main_loop); } static void test_new (Fixture *f, gconstpointer data) { g_assert (EVD_IS_WEBSOCKET_CLIENT (f->ws_client)); g_assert (EVD_IS_TRANSPORT (f->ws_client)); g_assert (EVD_IS_WEBSOCKET_SERVER (f->ws_server)); g_assert (EVD_IS_WEB_SERVICE (f->ws_server)); g_assert (EVD_IS_TRANSPORT (f->ws_server)); } static gboolean quit_main_loop (gpointer user_data) { Fixture *f = user_data; g_main_loop_quit (f->main_loop); return FALSE; } static void on_client_open (GObject *obj, GAsyncResult *res, gpointer user_data) { GError *error = NULL; gboolean ok; ok = evd_transport_open_finish (EVD_TRANSPORT (obj), res, &error); g_assert_no_error (error); g_assert (ok); } static void on_server_open (GObject *obj, GAsyncResult *res, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; gboolean ok; gchar *addr; ok = evd_transport_open_finish (EVD_TRANSPORT (obj), res, &error); g_assert_no_error (error); g_assert (ok); /* open client transport */ addr = g_strdup_printf (WS_ADDR, f->listen_port); evd_transport_open (EVD_TRANSPORT (f->ws_client), addr, NULL, on_client_open, f); g_free (addr); } static void on_new_peer (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { Fixture *f = user_data; GError *error = NULL; gboolean ok; g_assert (EVD_IS_TRANSPORT (transport)); g_assert (EVD_IS_PEER (peer)); g_assert (! evd_peer_is_closed (peer)); if (EVD_IS_WEBSOCKET_SERVER (transport)) { if (f->test_case->msg_type == EVD_MESSAGE_TYPE_TEXT) { ok = evd_transport_send_text (transport, peer, f->test_case->msg, &error); } else { ok = evd_transport_send (transport, peer, f->test_case->msg, f->test_case->msg_len, &error); } g_assert_no_error (error); g_assert (ok); } else { f->client_new_peer = TRUE; } } static void on_receive (EvdTransport *transport, EvdPeer *peer, gpointer user_data) { Fixture *f = user_data; const gchar *msg; gsize msg_len; gboolean ok; GError *error = NULL; g_assert (EVD_IS_TRANSPORT (transport)); g_assert (EVD_IS_PEER (peer)); g_assert (! evd_peer_is_closed (peer)); if (f->test_case->msg_type == EVD_MESSAGE_TYPE_TEXT) { msg = evd_transport_receive_text (transport, peer); g_assert_cmpstr (msg, ==, f->test_case->msg); if (EVD_IS_WEBSOCKET_CLIENT (transport)) { ok = evd_peer_send_text (peer, msg, &error); g_assert_no_error (error); g_assert (ok); } } else { msg = evd_transport_receive (transport, peer, &msg_len); g_assert_cmpuint (msg_len, ==, f->test_case->msg_len); if (EVD_IS_WEBSOCKET_CLIENT (transport)) { ok = evd_peer_send (peer, msg, msg_len, &error); g_assert_no_error (error); g_assert (ok); } } if (EVD_IS_WEBSOCKET_CLIENT (transport)) { evd_transport_close_peer (transport, peer, TRUE, &error); g_assert_no_error (error); } } static void on_peer_closed (EvdTransport *transport, EvdPeer *peer, gboolean gracefully, gpointer user_data) { Fixture *f = user_data; g_assert (EVD_IS_TRANSPORT (transport)); g_assert (EVD_IS_PEER (peer)); g_assert (evd_peer_is_closed (peer)); g_assert (gracefully); g_assert (f->client_new_peer); if (EVD_IS_WEBSOCKET_SERVER (transport)) g_timeout_add (1, quit_main_loop, f); } static void test_func (Fixture *f, gconstpointer data) { gchar *addr; f->test_case = (const TestCase *) data; g_signal_connect (f->ws_server, "new-peer", G_CALLBACK (on_new_peer), f); g_signal_connect (f->ws_client, "new-peer", G_CALLBACK (on_new_peer), f); g_signal_connect (f->ws_client, "receive", G_CALLBACK (on_receive), f); g_signal_connect (f->ws_server, "receive", G_CALLBACK (on_receive), f); g_signal_connect (f->ws_server, "peer-closed", G_CALLBACK (on_peer_closed), f); g_signal_connect (f->ws_client, "peer-closed", G_CALLBACK (on_peer_closed), f); evd_websocket_server_set_standalone (f->ws_server, TRUE); /* open server transport */ addr = g_strdup_printf (LISTEN_ADDR, f->listen_port); evd_transport_open (EVD_TRANSPORT (f->ws_server), addr, NULL, on_server_open, f); g_free (addr); g_main_loop_run (f->main_loop); } gint main (gint argc, gchar *argv[]) { gint exit_code; gint i; g_test_init (&argc, &argv, NULL); evd_tls_init (NULL); g_test_add ("/evd/websocket/transport/basic", Fixture, NULL, fixture_setup, test_new, fixture_teardown); for (i=0; i